@aikdna/kdna-cli 0.16.5 → 0.16.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aikdna/kdna-cli",
3
- "version": "0.16.5",
3
+ "version": "0.16.7",
4
4
  "description": "KDNA CLI — create, validate, install, and manage domain cognition packages for AI agents.",
5
5
  "type": "commonjs",
6
6
  "bin": {
package/src/cli.js CHANGED
@@ -14,6 +14,7 @@ const {
14
14
  cmdUnpack,
15
15
  cmdUnpackEncrypt,
16
16
  cmdInspect,
17
+ cmdCard,
17
18
  } = require('./cmds/domain');
18
19
  const { cmdList, cmdRegistry } = require('./cmds/registry');
19
20
  const {
@@ -85,6 +86,8 @@ Domain Authoring:
85
86
  unpack <file> Unpack .kdna container
86
87
  unpack <file> --license <file> Unpack encrypted .kdnae container
87
88
  inspect <path> Inspect domain or .kdna file
89
+ inspect <path> --locale zh-CN Inspect with localized governance data
90
+ card <path> [--locale zh-CN] Display KDNA Card (governance metadata)
88
91
  publish <path> Pack + sign + publish
89
92
  publish --check <path> Quality gate check only
90
93
  version bump <level> [path] Bump domain version
@@ -107,7 +110,8 @@ Agent Runtime:
107
110
 
108
111
  Testing & Verification:
109
112
  verify <name> 3-layer: structure + trust + judgment
110
- verify <name> --i18n I18N verification: locale dirs, overlays, card completeness
113
+ verify <name> --i18n I18N verification: locales, overlays, card completeness
114
+ verify <name> --governance Governance verification: risk_level, KDNA_CARD, provenance
111
115
  verify <name> --judgment --run-tests Judgment validation with eval cases
112
116
  compare <name> --input "..." With/without KDNA reasoning diff
113
117
  compare <name> --input "..." --report-md Markdown report format
@@ -285,6 +289,14 @@ switch (cmd) {
285
289
  cmdInspect(target, args.includes('--json'), locale);
286
290
  break;
287
291
  }
292
+ case 'card': {
293
+ const target = args.filter((a) => !a.startsWith('--'))[1];
294
+ if (!target) error('Usage: kdna card <path> [--json] [--locale zh-CN]');
295
+ const localeIdx = args.indexOf('--locale');
296
+ const locale = localeIdx >= 0 ? args[localeIdx + 1] : null;
297
+ cmdCard(target, locale);
298
+ break;
299
+ }
288
300
  case 'verify': {
289
301
  const { cmdVerify } = require('./verify');
290
302
  const target = args.filter((a) => !a.startsWith('--'))[1];
@@ -333,7 +333,8 @@ function cmdUnpack(filePath, force) {
333
333
  const outDir = path.join(path.dirname(abs), domainName);
334
334
 
335
335
  if (fs.existsSync(outDir)) {
336
- if (!force) error(`Directory already exists: ${outDir}\nUse --force to overwrite.`, EXIT.INPUT_ERROR);
336
+ if (!force)
337
+ error(`Directory already exists: ${outDir}\nUse --force to overwrite.`, EXIT.INPUT_ERROR);
337
338
  fs.rmSync(outDir, { recursive: true, force: true });
338
339
  }
339
340
 
@@ -645,7 +646,11 @@ function cmdInspect(dir, jsonMode = false, locale = null) {
645
646
  frameworks: (c.frameworks || []).length,
646
647
  core_structures: (c.core_structure || []).length,
647
648
  stances: (c.stances || []).length,
648
- preferred_terms: (pat?.terminology?.preferred_terms || pat?.terminology?.standard_terms || []).length,
649
+ preferred_terms: (
650
+ pat?.terminology?.preferred_terms ||
651
+ pat?.terminology?.standard_terms ||
652
+ []
653
+ ).length,
649
654
  banned_terms: (pat?.terminology?.banned_terms || []).length,
650
655
  misunderstandings: (pat?.misunderstandings || []).length,
651
656
  self_checks: (pat?.self_check || []).length,
@@ -725,7 +730,7 @@ function cmdInspect(dir, jsonMode = false, locale = null) {
725
730
 
726
731
  if (rea) console.log(` Reasoning chains: ${(rea.reasoning_chains || []).length}`);
727
732
 
728
- if (evo) console.log(` Evolution stages: ${(evo.stages || []).length}`);
733
+ if (evo) console.log(` Evolution stages: ${(evo.stages || []).length}`);
729
734
 
730
735
  // Governance metadata (with locale support)
731
736
  let kdnaCard = readJson(path.join(abs, 'KDNA_CARD.json'));
@@ -745,9 +750,18 @@ function cmdInspect(dir, jsonMode = false, locale = null) {
745
750
  if (summary && locale) console.log(` Summary: ${summary}`);
746
751
  console.log(` Risk level: ${kdnaCard.risk_level || '?'}`);
747
752
  console.log(` Review status: ${kdnaCard.review_status || '?'}`);
748
- if (kdnaCard.intended_use?.length) console.log(` Intended use: ${kdnaCard.intended_use[0]}${kdnaCard.intended_use.length > 1 ? ` (+${kdnaCard.intended_use.length - 1} more)` : ''}`);
749
- if (kdnaCard.out_of_scope?.length) console.log(` Out of scope: ${kdnaCard.out_of_scope[0]}${kdnaCard.out_of_scope.length > 1 ? ` (+${kdnaCard.out_of_scope.length - 1} more)` : ''}`);
750
- if (kdnaCard.known_limitations?.length) console.log(` Limitations: ${kdnaCard.known_limitations[0]}${kdnaCard.known_limitations.length > 1 ? ` (+${kdnaCard.known_limitations.length - 1} more)` : ''}`);
753
+ if (kdnaCard.intended_use?.length)
754
+ console.log(
755
+ ` Intended use: ${kdnaCard.intended_use[0]}${kdnaCard.intended_use.length > 1 ? ` (+${kdnaCard.intended_use.length - 1} more)` : ''}`,
756
+ );
757
+ if (kdnaCard.out_of_scope?.length)
758
+ console.log(
759
+ ` Out of scope: ${kdnaCard.out_of_scope[0]}${kdnaCard.out_of_scope.length > 1 ? ` (+${kdnaCard.out_of_scope.length - 1} more)` : ''}`,
760
+ );
761
+ if (kdnaCard.known_limitations?.length)
762
+ console.log(
763
+ ` Limitations: ${kdnaCard.known_limitations[0]}${kdnaCard.known_limitations.length > 1 ? ` (+${kdnaCard.known_limitations.length - 1} more)` : ''}`,
764
+ );
751
765
  if (kdnaCard.requires_expert_review) console.log(` ⚠ Expert review required`);
752
766
  }
753
767
 
@@ -778,7 +792,9 @@ function cmdPackEncrypt(dir, args = []) {
778
792
 
779
793
  let manifest = readJson(path.join(abs, 'kdna.json'));
780
794
  if (!manifest) {
781
- const jsonCount = fs.readdirSync(abs).filter(f => f.endsWith('.json') && f !== 'kdna.json').length;
795
+ const jsonCount = fs
796
+ .readdirSync(abs)
797
+ .filter((f) => f.endsWith('.json') && f !== 'kdna.json').length;
782
798
  manifest = {
783
799
  kdna_spec: '1.0-rc',
784
800
  name: domainName,
@@ -803,7 +819,8 @@ function cmdPackEncrypt(dir, args = []) {
803
819
 
804
820
  if (licenseIdx >= 0) {
805
821
  const licensePath = args[licenseIdx + 1];
806
- if (!licensePath || !fs.existsSync(licensePath)) error('License file not found', EXIT.INPUT_ERROR);
822
+ if (!licensePath || !fs.existsSync(licensePath))
823
+ error('License file not found', EXIT.INPUT_ERROR);
807
824
  const license = JSON.parse(fs.readFileSync(licensePath, 'utf8'));
808
825
  const licenseKey = license.license_id;
809
826
  const fp = machineFingerprint();
@@ -813,7 +830,10 @@ function cmdPackEncrypt(dir, args = []) {
813
830
  if (!rawKey) error('--key requires a value', EXIT.INPUT_ERROR);
814
831
  encKey = deriveKey(rawKey, machineFingerprint());
815
832
  } else {
816
- error('Use --license <license.json> or --key <secret> to provide encryption key', EXIT.INPUT_ERROR);
833
+ error(
834
+ 'Use --license <license.json> or --key <secret> to provide encryption key',
835
+ EXIT.INPUT_ERROR,
836
+ );
817
837
  }
818
838
 
819
839
  const outputDir = args.includes('--output') ? args[args.indexOf('--output') + 1] : null;
@@ -823,7 +843,7 @@ function cmdPackEncrypt(dir, args = []) {
823
843
 
824
844
  // Build encrypted ZIP
825
845
  const zlib = require('zlib');
826
- const files = fs.readdirSync(abs).filter(f => {
846
+ const files = fs.readdirSync(abs).filter((f) => {
827
847
  if (f.startsWith('.')) return false;
828
848
  const ext = path.extname(f);
829
849
  return ext === '.json' || f === 'README.md' || f === 'LICENSE' || f === 'kdna.json';
@@ -897,7 +917,9 @@ function cmdPackEncrypt(dir, args = []) {
897
917
 
898
918
  console.log(`✓ Encrypted pack: ${outPath}`);
899
919
  console.log(` Domain: ${domainName} v${manifest.version}`);
900
- console.log(` Files: ${files.length} (${files.filter(isEncryptable).length} encrypted, ${files.filter(f => !isEncryptable(f)).length} plaintext)`);
920
+ console.log(
921
+ ` Files: ${files.length} (${files.filter(isEncryptable).length} encrypted, ${files.filter((f) => !isEncryptable(f)).length} plaintext)`,
922
+ );
901
923
  console.log(` Container: AES-256-GCM .kdnae`);
902
924
  }
903
925
 
@@ -916,7 +938,8 @@ function cmdUnpackEncrypt(filePath, args = []) {
916
938
 
917
939
  if (licenseIdx >= 0) {
918
940
  const licensePath = args[licenseIdx + 1];
919
- if (!licensePath || !fs.existsSync(licensePath)) error('License file not found', EXIT.INPUT_ERROR);
941
+ if (!licensePath || !fs.existsSync(licensePath))
942
+ error('License file not found', EXIT.INPUT_ERROR);
920
943
  const license = JSON.parse(fs.readFileSync(licensePath, 'utf8'));
921
944
  const licenseKey = license.license_id;
922
945
  const fp = machineFingerprint();
@@ -934,7 +957,8 @@ function cmdUnpackEncrypt(filePath, args = []) {
934
957
  const force = args.includes('--force');
935
958
 
936
959
  if (fs.existsSync(outDir)) {
937
- if (!force) error(`Directory already exists: ${outDir}\nUse --force to overwrite.`, EXIT.INPUT_ERROR);
960
+ if (!force)
961
+ error(`Directory already exists: ${outDir}\nUse --force to overwrite.`, EXIT.INPUT_ERROR);
938
962
  fs.rmSync(outDir, { recursive: true, force: true });
939
963
  }
940
964
  fs.mkdirSync(outDir, { recursive: true });
@@ -954,10 +978,17 @@ zf.close()
954
978
  fs.writeFileSync(tmpPy, script);
955
979
  execSync(`python3 ${tmpPy}`, { stdio: 'pipe' });
956
980
  } catch {
957
- try { execSync(`unzip -q -o "${abs}" -d "${tmpDir}"`, { stdio: 'pipe' }); }
958
- catch { error('Cannot unpack .kdnae container'); }
981
+ try {
982
+ execSync(`unzip -q -o "${abs}" -d "${tmpDir}"`, { stdio: 'pipe' });
983
+ } catch {
984
+ error('Cannot unpack .kdnae container');
985
+ }
959
986
  } finally {
960
- try { fs.unlinkSync(tmpPy); } catch { /* cleanup */ }
987
+ try {
988
+ fs.unlinkSync(tmpPy);
989
+ } catch {
990
+ /* cleanup */
991
+ }
961
992
  }
962
993
 
963
994
  // Copy plaintext files, decrypt KDNA files
@@ -979,13 +1010,93 @@ zf.close()
979
1010
  }
980
1011
  }
981
1012
  } finally {
982
- try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch { /* cleanup */ }
1013
+ try {
1014
+ fs.rmSync(tmpDir, { recursive: true, force: true });
1015
+ } catch {
1016
+ /* cleanup */
1017
+ }
983
1018
  }
984
1019
 
985
1020
  console.log(`✓ Decrypted: ${outDir}`);
986
1021
  const files = fs.readdirSync(outDir);
987
1022
  console.log(` Files: ${files.length}`);
988
- files.forEach(f => console.log(` ${f}`));
1023
+ files.forEach((f) => console.log(` ${f}`));
1024
+ }
1025
+
1026
+ // ─── KDNA Card (locale-aware) ────────────────────────────────────
1027
+
1028
+ function cmdCard(dir, locale = null) {
1029
+ const abs = path.resolve(dir);
1030
+ let card = readJson(path.join(abs, 'KDNA_CARD.json'));
1031
+
1032
+ if (locale) {
1033
+ const localeCard = readJson(path.join(abs, 'locales', locale, 'KDNA_CARD.json'));
1034
+ if (localeCard) {
1035
+ card = { ...card, ...localeCard };
1036
+ }
1037
+ }
1038
+
1039
+ if (!card) {
1040
+ error(
1041
+ `No KDNA_CARD.json found in ${abs}${locale ? ` or locales/${locale}/` : ''}`,
1042
+ EXIT.INPUT_ERROR,
1043
+ );
1044
+ }
1045
+
1046
+ const jsonMode = process.argv.includes('--json');
1047
+
1048
+ if (jsonMode) {
1049
+ console.log(JSON.stringify(card, null, 2));
1050
+ return;
1051
+ }
1052
+
1053
+ console.log('═'.repeat(60));
1054
+ console.log(` KDNA Card${locale ? ` (${locale})` : ''}`);
1055
+ console.log('═'.repeat(60));
1056
+ console.log('');
1057
+ if (card.display_name) console.log(` Display name: ${card.display_name}`);
1058
+ console.log(` Domain: ${card.name || '?'}`);
1059
+ console.log(` Version: ${card.version || '?'}`);
1060
+ console.log(` Risk level: ${card.risk_level || '?'}`);
1061
+ console.log(` Quality: ${card.quality_badge || '?'}`);
1062
+ console.log(` Review: ${card.review_status || '?'}`);
1063
+ console.log(` License: ${card.license || '?'}`);
1064
+ if (card.summary) console.log(` Summary: ${card.summary}`);
1065
+ console.log('');
1066
+ if (card.intended_use?.length) {
1067
+ console.log(' ── Intended Use ──');
1068
+ card.intended_use.forEach((u) => console.log(` ✓ ${u}`));
1069
+ console.log('');
1070
+ }
1071
+ if (card.out_of_scope?.length) {
1072
+ console.log(' ── Out of Scope ──');
1073
+ card.out_of_scope.forEach((o) => console.log(` ✗ ${o}`));
1074
+ console.log('');
1075
+ }
1076
+ if (card.known_limitations?.length) {
1077
+ console.log(' ── Known Limitations ──');
1078
+ card.known_limitations.forEach((l) => console.log(` ⚠ ${l}`));
1079
+ console.log('');
1080
+ }
1081
+ if (card.risk_warnings?.length) {
1082
+ console.log(' ── Risk Warnings ──');
1083
+ card.risk_warnings.forEach((w) => console.log(` ⚡ ${w}`));
1084
+ console.log('');
1085
+ }
1086
+ if (card.author_responsibility) {
1087
+ console.log(` Author: ${card.author_responsibility}`);
1088
+ console.log('');
1089
+ }
1090
+ if (card.provenance) {
1091
+ console.log(' ── Provenance ──');
1092
+ console.log(` Studio: ${card.provenance.studio_core || '?'}`);
1093
+ console.log(` Version: ${card.provenance.studio_core_version || '?'}`);
1094
+ console.log(` Built: ${card.provenance.built_at || '?'}`);
1095
+ if (card.provenance.content_fingerprint)
1096
+ console.log(` Fingerprint: ${card.provenance.content_fingerprint}`);
1097
+ console.log('');
1098
+ }
1099
+ console.log('═'.repeat(60));
989
1100
  }
990
1101
 
991
1102
  module.exports = {
@@ -995,4 +1106,5 @@ module.exports = {
995
1106
  cmdUnpack,
996
1107
  cmdUnpackEncrypt,
997
1108
  cmdInspect,
1109
+ cmdCard,
998
1110
  };
package/src/verify.js CHANGED
@@ -467,6 +467,87 @@ function checkI18n(destDir) {
467
467
  };
468
468
  }
469
469
 
470
+ // ─── Governance layer ───────────────────────────────────────────────
471
+
472
+ function checkGovernance(destDir) {
473
+ const issues = [];
474
+ const passed = [];
475
+ const card = readJson(path.join(destDir, 'KDNA_CARD.json')) || {};
476
+
477
+ if (!readJson(path.join(destDir, 'KDNA_CARD.json'))) {
478
+ issues.push({ severity: 'error', msg: 'governance: KDNA_CARD.json missing — required' });
479
+ return { layer: 'governance', passed: false, issues, results: issues.map((i) => i.msg) };
480
+ }
481
+ passed.push('KDNA_CARD.json present');
482
+
483
+ if (!card.risk_level) {
484
+ issues.push({ severity: 'error', msg: 'governance: risk_level not declared (R0/R1/R2/R3)' });
485
+ } else if (!['R0', 'R1', 'R2', 'R3'].includes(card.risk_level)) {
486
+ issues.push({ severity: 'error', msg: `governance: invalid risk_level "${card.risk_level}"` });
487
+ } else {
488
+ passed.push(`risk_level: ${card.risk_level}`);
489
+ }
490
+
491
+ if (!card.intended_use?.length) {
492
+ issues.push({ severity: 'error', msg: 'governance: intended_use empty' });
493
+ } else {
494
+ passed.push(`intended_use: ${card.intended_use.length} entries`);
495
+ }
496
+
497
+ if (!card.out_of_scope?.length) {
498
+ issues.push({ severity: 'error', msg: 'governance: out_of_scope empty' });
499
+ } else {
500
+ passed.push(`out_of_scope: ${card.out_of_scope.length} entries`);
501
+ }
502
+
503
+ if (!card.known_limitations?.length) {
504
+ issues.push({ severity: 'warn', msg: 'governance: known_limitations empty' });
505
+ } else {
506
+ passed.push(`known_limitations: ${card.known_limitations.length} entries`);
507
+ }
508
+
509
+ if (['R1', 'R2', 'R3'].includes(card.risk_level) && !card.author_responsibility) {
510
+ issues.push({
511
+ severity: 'warn',
512
+ msg: `governance: risk ${card.risk_level} should declare author_responsibility`,
513
+ });
514
+ }
515
+
516
+ if (['R2', 'R3'].includes(card.risk_level)) {
517
+ if (!card.reviewed_by && !card.requires_expert_review) {
518
+ issues.push({
519
+ severity: 'error',
520
+ msg: `governance: risk ${card.risk_level} requires expert_review`,
521
+ });
522
+ }
523
+ if (!card.risk_warnings?.length) {
524
+ issues.push({
525
+ severity: 'error',
526
+ msg: `governance: risk ${card.risk_level} requires risk_warnings`,
527
+ });
528
+ }
529
+ }
530
+
531
+ if (!card.human_lock_summary)
532
+ issues.push({ severity: 'warn', msg: 'governance: human_lock_summary missing' });
533
+ else passed.push('human_lock_summary present');
534
+
535
+ if (!card.provenance) issues.push({ severity: 'warn', msg: 'governance: provenance missing' });
536
+ else passed.push('provenance present');
537
+
538
+ if (!card.quality_badge)
539
+ issues.push({ severity: 'warn', msg: 'governance: quality_badge missing' });
540
+ else passed.push(`quality_badge: ${card.quality_badge}`);
541
+
542
+ return {
543
+ layer: 'governance',
544
+ passed: issues.filter((i) => i.severity === 'error').length === 0,
545
+ issues,
546
+ results: passed.concat(issues.map((i) => i.msg)),
547
+ score: { total: passed.length, max: passed.length + issues.length },
548
+ };
549
+ }
550
+
470
551
  // ─── Main ──────────────────────────────────────────────────────────────
471
552
 
472
553
  function cmdVerify(input, args = []) {
@@ -477,8 +558,9 @@ function cmdVerify(input, args = []) {
477
558
  trust: args.includes('--trust'),
478
559
  judgment: args.includes('--judgment'),
479
560
  i18n: args.includes('--i18n'),
561
+ governance: args.includes('--governance'),
480
562
  };
481
- const all = !want.structure && !want.trust && !want.judgment && !want.i18n;
563
+ const all = !want.structure && !want.trust && !want.judgment && !want.i18n && !want.governance;
482
564
  if (all) want.structure = want.trust = want.judgment = true;
483
565
 
484
566
  // Resolve name → installed path + scope/entry
@@ -532,6 +614,7 @@ function cmdVerify(input, args = []) {
532
614
  if (want.trust) results.push(checkTrust(destDir, scope, entry));
533
615
  if (want.judgment) results.push(checkJudgment(destDir));
534
616
  if (want.i18n) results.push(checkI18n(destDir));
617
+ if (want.governance) results.push(checkGovernance(destDir));
535
618
 
536
619
  // ── JSON output ──────────────────────────────────────────────────────
537
620
  if (jsonMode) {
@@ -548,6 +631,8 @@ function cmdVerify(input, args = []) {
548
631
  const structureResult = results.find((r) => r.layer === 'structure');
549
632
  const trustResult = results.find((r) => r.layer === 'trust');
550
633
  const judgmentResult = results.find((r) => r.layer === 'judgment');
634
+ const i18nResult = results.find((r) => r.layer === 'i18n');
635
+ const governanceResult = results.find((r) => r.layer === 'governance');
551
636
 
552
637
  let exitCode = EXIT.OK;
553
638
  if (structureResult && structureResult.issues.some((i) => i.severity === 'error')) {