@aikdna/kdna-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.
- package/package.json +1 -1
- package/src/cli.js +39 -6
- package/src/cmds/registry.js +1 -1
- package/src/verify.js +218 -13
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -7,7 +7,14 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
const { error, EXIT, setQuiet, setExitCodeOnly } = require('./cmds/_common');
|
|
10
|
-
const {
|
|
10
|
+
const {
|
|
11
|
+
cmdValidate,
|
|
12
|
+
cmdPack,
|
|
13
|
+
cmdPackEncrypt,
|
|
14
|
+
cmdUnpack,
|
|
15
|
+
cmdUnpackEncrypt,
|
|
16
|
+
cmdInspect,
|
|
17
|
+
} = require('./cmds/domain');
|
|
11
18
|
const { cmdList, cmdRegistry } = require('./cmds/registry');
|
|
12
19
|
const {
|
|
13
20
|
cmdCompare,
|
|
@@ -24,12 +31,31 @@ const { cmdIdentity } = require('./cmds/identity');
|
|
|
24
31
|
const { cmdSetup } = require('./cmds/setup');
|
|
25
32
|
const { cmdDoctor } = require('./cmds/doctor');
|
|
26
33
|
const { cmdTrace, cmdHistory } = require('./cmds/trace');
|
|
27
|
-
const {
|
|
34
|
+
const {
|
|
35
|
+
cmdLicenseGenerate,
|
|
36
|
+
cmdLicenseVerify,
|
|
37
|
+
cmdLicenseBind,
|
|
38
|
+
cmdLicenseShow,
|
|
39
|
+
cmdLicenseInstall,
|
|
40
|
+
} = require('./cmds/license');
|
|
28
41
|
const { cmdPreview, cmdProject, cmdEval, cmdExport, cmdDemo } = require('./cmds/legacy');
|
|
29
|
-
const {
|
|
42
|
+
const {
|
|
43
|
+
cmdStudioScaffold,
|
|
44
|
+
cmdCardsValidate,
|
|
45
|
+
cmdLockVerify,
|
|
46
|
+
cmdStudioCompile,
|
|
47
|
+
cmdStudioReadiness,
|
|
48
|
+
} = require('./cmds/studio');
|
|
30
49
|
const { cmdTestRun, cmdTestImport } = require('./cmds/test');
|
|
31
50
|
const { cmdChangelog } = require('./cmds/changelog');
|
|
32
|
-
const {
|
|
51
|
+
const {
|
|
52
|
+
cmdProposalCreate,
|
|
53
|
+
cmdProposalValidate,
|
|
54
|
+
cmdReview,
|
|
55
|
+
cmdLockCard,
|
|
56
|
+
cmdEvolution,
|
|
57
|
+
cmdRegression,
|
|
58
|
+
} = require('./cmds/governance');
|
|
33
59
|
const { cmdBadgeCompute, cmdRegistryAudit, cmdPackage } = require('./cmds/badge');
|
|
34
60
|
|
|
35
61
|
// ─── Main ─────────────────────────────────────────────────────────────
|
|
@@ -81,6 +107,8 @@ Agent Runtime:
|
|
|
81
107
|
|
|
82
108
|
Testing & Verification:
|
|
83
109
|
verify <name> 3-layer: structure + trust + judgment
|
|
110
|
+
verify <name> --i18n I18N verification: locales, overlays, card completeness
|
|
111
|
+
verify <name> --governance Governance verification: risk_level, KDNA_CARD, provenance
|
|
84
112
|
verify <name> --judgment --run-tests Judgment validation with eval cases
|
|
85
113
|
compare <name> --input "..." With/without KDNA reasoning diff
|
|
86
114
|
compare <name> --input "..." --report-md Markdown report format
|
|
@@ -332,7 +360,10 @@ switch (cmd) {
|
|
|
332
360
|
} else if (sub === 'validate') {
|
|
333
361
|
cmdProposalValidate(args);
|
|
334
362
|
} else {
|
|
335
|
-
error(
|
|
363
|
+
error(
|
|
364
|
+
'Usage: kdna proposal create --from-test <run.json> --domain <path>\n kdna proposal validate <proposal.json>',
|
|
365
|
+
EXIT.INPUT_ERROR,
|
|
366
|
+
);
|
|
336
367
|
}
|
|
337
368
|
break;
|
|
338
369
|
}
|
|
@@ -405,7 +436,9 @@ switch (cmd) {
|
|
|
405
436
|
break;
|
|
406
437
|
}
|
|
407
438
|
case 'list': {
|
|
408
|
-
|
|
439
|
+
const localeIdx = args.indexOf('--locale');
|
|
440
|
+
const locale = localeIdx >= 0 ? args[localeIdx + 1] : null;
|
|
441
|
+
cmdList(args.includes('--available'), args.includes('--json'), locale);
|
|
409
442
|
break;
|
|
410
443
|
}
|
|
411
444
|
case 'setup': {
|
package/src/cmds/registry.js
CHANGED
|
@@ -3,7 +3,7 @@ const path = require('path');
|
|
|
3
3
|
const { CANONICAL_REGISTRY_URL, REGISTRY_CACHE, fetchRegistry } = require('../registry');
|
|
4
4
|
const { error, readJson, loadRegistry, INSTALL_DIR, EXIT } = require('./_common');
|
|
5
5
|
|
|
6
|
-
function cmdList(showAvailable, jsonMode = false) {
|
|
6
|
+
function cmdList(showAvailable, jsonMode = false, locale = null) {
|
|
7
7
|
if (showAvailable) {
|
|
8
8
|
const domains = loadRegistry({ allowNetwork: true });
|
|
9
9
|
if (!domains || !domains.length) {
|
package/src/verify.js
CHANGED
|
@@ -240,10 +240,16 @@ function checkJudgment(destDir) {
|
|
|
240
240
|
} else if (hasScope || hasOutOfScope) {
|
|
241
241
|
score.max += 2;
|
|
242
242
|
score.total += 1;
|
|
243
|
-
issues.push({
|
|
243
|
+
issues.push({
|
|
244
|
+
severity: 'warn',
|
|
245
|
+
msg: 'partial: README boundary declaration incomplete (missing Scope or Out-of-Scope section)',
|
|
246
|
+
});
|
|
244
247
|
} else {
|
|
245
248
|
score.max += 2;
|
|
246
|
-
issues.push({
|
|
249
|
+
issues.push({
|
|
250
|
+
severity: 'error',
|
|
251
|
+
msg: 'README missing boundary declaration: require ## Scope + ## Out of Scope (or v2.1 Four Questions)',
|
|
252
|
+
});
|
|
247
253
|
}
|
|
248
254
|
|
|
249
255
|
// 2. v2.1 axiom governance fields
|
|
@@ -321,14 +327,20 @@ function checkJudgment(destDir) {
|
|
|
321
327
|
} else if (files.length > 0) {
|
|
322
328
|
score.max += 2;
|
|
323
329
|
score.total += 1;
|
|
324
|
-
issues.push({
|
|
330
|
+
issues.push({
|
|
331
|
+
severity: 'warn',
|
|
332
|
+
msg: `evals/ has only ${files.length} files (require ≥4: core/boundary/failure/excluded)`,
|
|
333
|
+
});
|
|
325
334
|
} else {
|
|
326
335
|
score.max += 2;
|
|
327
336
|
issues.push({ severity: 'error', msg: 'evals/ directory exists but contains no case files' });
|
|
328
337
|
}
|
|
329
338
|
} else {
|
|
330
339
|
score.max += 2;
|
|
331
|
-
issues.push({
|
|
340
|
+
issues.push({
|
|
341
|
+
severity: 'error',
|
|
342
|
+
msg: 'evals/ directory missing: require ≥4 evaluation cases',
|
|
343
|
+
});
|
|
332
344
|
}
|
|
333
345
|
|
|
334
346
|
// 6. judgment_version manifest field (REQUIRED)
|
|
@@ -367,6 +379,175 @@ function renderLayer(result) {
|
|
|
367
379
|
}
|
|
368
380
|
}
|
|
369
381
|
|
|
382
|
+
// ─── I18N layer ──────────────────────────────────────────────────────
|
|
383
|
+
|
|
384
|
+
function checkI18n(destDir) {
|
|
385
|
+
const issues = [];
|
|
386
|
+
const passed = [];
|
|
387
|
+
const manifest = readJson(path.join(destDir, 'kdna.json')) || {};
|
|
388
|
+
const languages = manifest.languages || [];
|
|
389
|
+
const i18nLevel = manifest.i18n_level || 'L0';
|
|
390
|
+
|
|
391
|
+
if (languages.length === 0) {
|
|
392
|
+
passed.push('i18n: no languages declared (L0 — monolingual)');
|
|
393
|
+
return { layer: 'i18n', passed: true, issues, results: passed };
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
passed.push(`languages declared: ${languages.join(', ')}`);
|
|
397
|
+
passed.push(`i18n level: ${i18nLevel}`);
|
|
398
|
+
|
|
399
|
+
const canonical = manifest.default_language || languages[0] || 'en';
|
|
400
|
+
for (const lang of languages) {
|
|
401
|
+
if (lang === canonical) continue;
|
|
402
|
+
const localeDir = path.join(destDir, 'locales', lang);
|
|
403
|
+
|
|
404
|
+
// L1: card + readme
|
|
405
|
+
if (['L1', 'L2', 'L3', 'L4'].includes(i18nLevel)) {
|
|
406
|
+
if (!fs.existsSync(path.join(localeDir, 'KDNA_CARD.json'))) {
|
|
407
|
+
issues.push({ severity: 'error', msg: `i18n: ${lang} KDNA_CARD.json missing` });
|
|
408
|
+
} else {
|
|
409
|
+
const card = readJson(path.join(localeDir, 'KDNA_CARD.json'));
|
|
410
|
+
if (card) {
|
|
411
|
+
passed.push(`locales/${lang}/KDNA_CARD.json OK`);
|
|
412
|
+
if (!card.display_name)
|
|
413
|
+
issues.push({ severity: 'warn', msg: `i18n: ${lang} card missing display_name` });
|
|
414
|
+
if (!card.intended_use?.length)
|
|
415
|
+
issues.push({ severity: 'warn', msg: `i18n: ${lang} card missing intended_use` });
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
if (!fs.existsSync(path.join(localeDir, 'README.md'))) {
|
|
419
|
+
issues.push({ severity: 'warn', msg: `i18n: ${lang} README.md missing` });
|
|
420
|
+
} else {
|
|
421
|
+
passed.push(`locales/${lang}/README.md OK`);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// L2: overlay files
|
|
426
|
+
if (['L2', 'L3', 'L4'].includes(i18nLevel)) {
|
|
427
|
+
const coreOverlay = path.join(localeDir, 'KDNA_Core.overlay.json');
|
|
428
|
+
if (!fs.existsSync(coreOverlay)) {
|
|
429
|
+
issues.push({ severity: 'error', msg: `i18n: ${lang} KDNA_Core.overlay.json missing` });
|
|
430
|
+
} else {
|
|
431
|
+
const overlay = readJson(coreOverlay);
|
|
432
|
+
if (overlay?.translations) {
|
|
433
|
+
const core = readJson(path.join(destDir, 'KDNA_Core.json'));
|
|
434
|
+
if (core?.axioms) {
|
|
435
|
+
const validIds = new Set(core.axioms.map((a) => a.id));
|
|
436
|
+
for (const key of Object.keys(overlay.translations)) {
|
|
437
|
+
const refId = key.split('.')[0];
|
|
438
|
+
if (!validIds.has(refId)) {
|
|
439
|
+
issues.push({
|
|
440
|
+
severity: 'error',
|
|
441
|
+
msg: `i18n: overlay refs unknown axiom: ${refId}`,
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
passed.push(
|
|
447
|
+
`locales/${lang}/KDNA_Core.overlay.json OK (${Object.keys(overlay.translations).length} translations)`,
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
if (!fs.existsSync(path.join(localeDir, 'KDNA_Patterns.overlay.json'))) {
|
|
452
|
+
issues.push({ severity: 'warn', msg: `i18n: ${lang} KDNA_Patterns.overlay.json missing` });
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (manifest.languages?.length && !manifest.i18n_level) {
|
|
458
|
+
issues.push({ severity: 'warn', msg: 'i18n: languages declared but i18n_level not set' });
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
return {
|
|
462
|
+
layer: 'i18n',
|
|
463
|
+
passed: issues.filter((i) => i.severity === 'error').length === 0,
|
|
464
|
+
issues,
|
|
465
|
+
results: passed.concat(issues.map((i) => i.msg)),
|
|
466
|
+
score: { total: passed.length, max: passed.length + issues.length },
|
|
467
|
+
};
|
|
468
|
+
}
|
|
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
|
+
|
|
370
551
|
// ─── Main ──────────────────────────────────────────────────────────────
|
|
371
552
|
|
|
372
553
|
function cmdVerify(input, args = []) {
|
|
@@ -376,15 +557,23 @@ function cmdVerify(input, args = []) {
|
|
|
376
557
|
structure: args.includes('--structure'),
|
|
377
558
|
trust: args.includes('--trust'),
|
|
378
559
|
judgment: args.includes('--judgment'),
|
|
560
|
+
i18n: args.includes('--i18n'),
|
|
561
|
+
governance: args.includes('--governance'),
|
|
379
562
|
};
|
|
380
|
-
const all = !want.structure && !want.trust && !want.judgment;
|
|
563
|
+
const all = !want.structure && !want.trust && !want.judgment && !want.i18n && !want.governance;
|
|
381
564
|
if (all) want.structure = want.trust = want.judgment = true;
|
|
382
565
|
|
|
383
566
|
// Resolve name → installed path + scope/entry
|
|
384
567
|
const parsed = parseName(input);
|
|
385
568
|
if (!parsed) {
|
|
386
569
|
if (jsonMode) {
|
|
387
|
-
console.log(
|
|
570
|
+
console.log(
|
|
571
|
+
JSON.stringify({
|
|
572
|
+
name: input,
|
|
573
|
+
ok: false,
|
|
574
|
+
error: `Invalid name "${input}". Use @scope/name or bare name.`,
|
|
575
|
+
}),
|
|
576
|
+
);
|
|
388
577
|
} else {
|
|
389
578
|
console.error(`Invalid name "${input}". Use @scope/name or bare name.`);
|
|
390
579
|
}
|
|
@@ -394,7 +583,13 @@ function cmdVerify(input, args = []) {
|
|
|
394
583
|
const destDir = path.join(INSTALL_DIR, parsed.scope, parsed.ident);
|
|
395
584
|
if (!fs.existsSync(destDir)) {
|
|
396
585
|
if (jsonMode) {
|
|
397
|
-
console.log(
|
|
586
|
+
console.log(
|
|
587
|
+
JSON.stringify({
|
|
588
|
+
name: parsed.full,
|
|
589
|
+
ok: false,
|
|
590
|
+
error: `${parsed.full} is not installed. Run: kdna install ${input}`,
|
|
591
|
+
}),
|
|
592
|
+
);
|
|
398
593
|
} else {
|
|
399
594
|
console.error(`${parsed.full} is not installed. Run: kdna install ${input}`);
|
|
400
595
|
}
|
|
@@ -418,6 +613,8 @@ function cmdVerify(input, args = []) {
|
|
|
418
613
|
if (want.structure) results.push(checkStructure(destDir));
|
|
419
614
|
if (want.trust) results.push(checkTrust(destDir, scope, entry));
|
|
420
615
|
if (want.judgment) results.push(checkJudgment(destDir));
|
|
616
|
+
if (want.i18n) results.push(checkI18n(destDir));
|
|
617
|
+
if (want.governance) results.push(checkGovernance(destDir));
|
|
421
618
|
|
|
422
619
|
// ── JSON output ──────────────────────────────────────────────────────
|
|
423
620
|
if (jsonMode) {
|
|
@@ -434,6 +631,8 @@ function cmdVerify(input, args = []) {
|
|
|
434
631
|
const structureResult = results.find((r) => r.layer === 'structure');
|
|
435
632
|
const trustResult = results.find((r) => r.layer === 'trust');
|
|
436
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');
|
|
437
636
|
|
|
438
637
|
let exitCode = EXIT.OK;
|
|
439
638
|
if (structureResult && structureResult.issues.some((i) => i.severity === 'error')) {
|
|
@@ -444,12 +643,18 @@ function cmdVerify(input, args = []) {
|
|
|
444
643
|
exitCode = EXIT.JUDGMENT_QUALITY_FAILED;
|
|
445
644
|
}
|
|
446
645
|
|
|
447
|
-
console.log(
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
646
|
+
console.log(
|
|
647
|
+
JSON.stringify(
|
|
648
|
+
{
|
|
649
|
+
name: parsed.full,
|
|
650
|
+
path: destDir,
|
|
651
|
+
layers,
|
|
652
|
+
ok: exitCode === EXIT.OK,
|
|
653
|
+
},
|
|
654
|
+
null,
|
|
655
|
+
2,
|
|
656
|
+
),
|
|
657
|
+
);
|
|
453
658
|
process.exit(exitCode);
|
|
454
659
|
}
|
|
455
660
|
|