@aikdna/kdna-studio-cli 0.8.17 → 0.9.1
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/README.md +15 -18
- package/bin/kdna-studio.js +61 -251
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Distillation-first authoring is domain-first: declare the target domain, owner s
|
|
|
8
8
|
|
|
9
9
|
This package provides the `kdna-studio` command. It creates Studio projects,
|
|
10
10
|
imports evidence, manages judgment cards, records optional review/provenance
|
|
11
|
-
signals, compiles
|
|
11
|
+
signals, compiles project content, and exports canonical `.kdna`
|
|
12
12
|
assets with build reports.
|
|
13
13
|
|
|
14
14
|
It is intentionally separate from `@aikdna/kdna-cli`:
|
|
@@ -37,7 +37,7 @@ kdna-studio target declare my_domain \
|
|
|
37
37
|
--task "longform article review" \
|
|
38
38
|
--include "argument structure,tone,revision" \
|
|
39
39
|
--exclude "life habits,food preference"
|
|
40
|
-
kdna-studio source
|
|
40
|
+
kdna-studio source my_domain
|
|
41
41
|
kdna-studio distill my_domain --candidates candidates.json
|
|
42
42
|
kdna-studio candidate list my_domain
|
|
43
43
|
kdna-studio candidate accept my_domain <candidate-id>
|
|
@@ -51,16 +51,16 @@ kdna-studio card add my_domain axiom \
|
|
|
51
51
|
--field failure_risk="generic advice" \
|
|
52
52
|
--field confidence='high' \
|
|
53
53
|
--field evidence_type='practice'
|
|
54
|
+
# Optional: record review provenance
|
|
54
55
|
kdna-studio card approve my_domain --all --by expert --statement "I confirm this judgment."
|
|
55
56
|
kdna-studio export my_domain --out dist/my_domain.kdna
|
|
56
57
|
```
|
|
57
58
|
|
|
58
59
|
Candidate promotion is scope-gated: only candidates with `status == accepted` and `scope_fit == true` are promoted to cards by default. Use `kdna-studio candidate override <project> <candidate-id>` only when a human intentionally overrides the scope gate.
|
|
59
60
|
|
|
60
|
-
The
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
requirements.
|
|
61
|
+
The Studio CLI exports complete, non-deprecated cards. Human Lock and other
|
|
62
|
+
provenance records are optional review evidence, not creation permission or
|
|
63
|
+
format-validity requirements.
|
|
64
64
|
|
|
65
65
|
After export, use the runtime CLI:
|
|
66
66
|
|
|
@@ -79,14 +79,11 @@ loading existing `.kdna` assets.
|
|
|
79
79
|
If you just installed and want to ship a KDNA asset without an LLM:
|
|
80
80
|
|
|
81
81
|
```bash
|
|
82
|
-
# 1.
|
|
83
|
-
kdna-studio identity init --name "Your Name"
|
|
84
|
-
|
|
85
|
-
# 2. Create a project
|
|
82
|
+
# 1. Create a project
|
|
86
83
|
kdna-studio create my_domain --name @yourscope/my_domain
|
|
87
84
|
|
|
88
|
-
#
|
|
89
|
-
kdna-studio card add
|
|
85
|
+
# 2. Add at least one judgment card
|
|
86
|
+
kdna-studio card add my_domain axiom \
|
|
90
87
|
--field one_sentence='specific evidence outranks broad claims' \
|
|
91
88
|
--field full_statement='Always cite the specific source that supports a judgment; broad claims without evidence are the most common cause of bad agent advice.' \
|
|
92
89
|
--field why='because vague advice fails in production' \
|
|
@@ -96,13 +93,13 @@ kdna-studio card add . axiom \
|
|
|
96
93
|
--field confidence='high' \
|
|
97
94
|
--field evidence_type='practice'
|
|
98
95
|
|
|
99
|
-
#
|
|
100
|
-
kdna-studio card approve
|
|
96
|
+
# 3. Optional: approve and record Human Lock provenance
|
|
97
|
+
kdna-studio card approve my_domain --all --by me --statement "i confirm"
|
|
101
98
|
|
|
102
|
-
#
|
|
103
|
-
kdna-studio export
|
|
99
|
+
# 4. Export the asset
|
|
100
|
+
kdna-studio export my_domain --out my_domain.kdna
|
|
104
101
|
|
|
105
|
-
#
|
|
102
|
+
# 5. Verify with the runtime CLI
|
|
106
103
|
kdna load my_domain.kdna --profile=compact
|
|
107
104
|
```
|
|
108
105
|
|
|
@@ -157,7 +154,7 @@ kdna-studio identity show
|
|
|
157
154
|
## Import from existing KDNA or legacy folders
|
|
158
155
|
|
|
159
156
|
```bash
|
|
160
|
-
# Fork an existing .kdna asset (cards imported as draft
|
|
157
|
+
# Fork an existing canonical .kdna asset (cards are imported as draft)
|
|
161
158
|
kdna-studio create forked --from-kdna ./parent.kdna --name @scope/forked
|
|
162
159
|
|
|
163
160
|
# Migrate a legacy JSON source folder
|
package/bin/kdna-studio.js
CHANGED
|
@@ -410,10 +410,14 @@ function resetImportedIds() { _importedIds.clear(); }
|
|
|
410
410
|
function decodePayload(bytes, manifest = {}) {
|
|
411
411
|
const raw = Buffer.isBuffer(bytes) ? bytes : Buffer.from(bytes);
|
|
412
412
|
const encoding = manifest.payload?.encoding || manifest.container?.payload_encoding || null;
|
|
413
|
-
if (encoding
|
|
414
|
-
|
|
413
|
+
if (encoding && encoding !== 'cbor') {
|
|
414
|
+
throw new Error(`unsupported payload encoding "${encoding}"; current .kdna assets require CBOR`);
|
|
415
|
+
}
|
|
416
|
+
try {
|
|
417
|
+
return cbor.decode(raw);
|
|
418
|
+
} catch (error) {
|
|
419
|
+
throw new Error(`payload.kdnab is not valid CBOR: ${error.message}`);
|
|
415
420
|
}
|
|
416
|
-
return cbor.decode(raw);
|
|
417
421
|
}
|
|
418
422
|
|
|
419
423
|
// Runtime path: trust the producer (export-runtime) to have already shaped
|
|
@@ -561,97 +565,11 @@ function cardsFromRuntimePayload(payload) {
|
|
|
561
565
|
return cards;
|
|
562
566
|
}
|
|
563
567
|
|
|
564
|
-
function cardsFromLegacyPayload(payload) {
|
|
565
|
-
resetImportedIds();
|
|
566
|
-
const cards = [];
|
|
567
|
-
const judgment = payload.judgment || {};
|
|
568
|
-
const core = judgment.core || {};
|
|
569
|
-
const patterns = judgment.patterns || {};
|
|
570
|
-
|
|
571
|
-
for (const ax of (Array.isArray(core.axioms) ? core.axioms : [])) {
|
|
572
|
-
pushImportedCard(cards, 'axiom', {
|
|
573
|
-
one_sentence: ax.one_sentence || '',
|
|
574
|
-
full_statement: ax.full_statement || '',
|
|
575
|
-
why: ax.why || '',
|
|
576
|
-
applies_when: ax.applies_when || [],
|
|
577
|
-
does_not_apply_when: ax.does_not_apply_when || [],
|
|
578
|
-
failure_risk: ax.failure_risk || '',
|
|
579
|
-
}, ax.id || null);
|
|
580
|
-
}
|
|
581
|
-
for (const ont of (Array.isArray(core.ontology) ? core.ontology : [])) {
|
|
582
|
-
pushImportedCard(cards, 'ontology', {
|
|
583
|
-
one_sentence: ont.one_sentence || ont.essence || '',
|
|
584
|
-
essence: ont.essence || '',
|
|
585
|
-
boundary: ont.boundary || '',
|
|
586
|
-
trigger_signal: ont.trigger_signal || '',
|
|
587
|
-
}, ont.id || null);
|
|
588
|
-
}
|
|
589
|
-
for (const boundary of (Array.isArray(core.boundaries) ? core.boundaries : [])) {
|
|
590
|
-
pushImportedCard(cards, 'boundary', {
|
|
591
|
-
scope: boundary.scope || '',
|
|
592
|
-
out_of_scope: boundary.out_of_scope || '',
|
|
593
|
-
acceptable_exceptions: boundary.acceptable_exceptions || [],
|
|
594
|
-
}, boundary.id || null);
|
|
595
|
-
}
|
|
596
|
-
for (const risk of (Array.isArray(core.risks) ? core.risks : [])) {
|
|
597
|
-
pushImportedCard(cards, 'risk', risk, risk.id || null);
|
|
598
|
-
}
|
|
599
|
-
// Bug (#63): prior version skipped `core.stances` and
|
|
600
|
-
// `core.frameworks`, so a legacy asset that authored either type
|
|
601
|
-
// round-tripped out as zero cards. The fix adds both loops.
|
|
602
|
-
for (const stance of (Array.isArray(core.stances) ? core.stances : [])) {
|
|
603
|
-
pushImportedCard(cards, 'stance', stance, stance.id || null);
|
|
604
|
-
}
|
|
605
|
-
for (const fw of (Array.isArray(core.frameworks) ? core.frameworks : [])) {
|
|
606
|
-
pushImportedCard(cards, 'framework', fw, fw.id || null);
|
|
607
|
-
}
|
|
608
|
-
for (const term of (Array.isArray(patterns.terminology?.standard_terms) ? patterns.terminology?.standard_terms : [])) {
|
|
609
|
-
pushImportedCard(cards, 'term', term, term.id || null);
|
|
610
|
-
}
|
|
611
|
-
for (const banned of (Array.isArray(patterns.terminology?.banned_terms) ? patterns.terminology?.banned_terms : [])) {
|
|
612
|
-
pushImportedCard(cards, 'banned_term', banned, banned.id || null);
|
|
613
|
-
}
|
|
614
|
-
for (const ms of (Array.isArray(patterns.misunderstandings) ? patterns.misunderstandings : [])) {
|
|
615
|
-
pushImportedCard(cards, 'misunderstanding', {
|
|
616
|
-
wrong: ms.wrong || '',
|
|
617
|
-
correct: ms.correct || '',
|
|
618
|
-
key_distinction: ms.key_distinction || '',
|
|
619
|
-
why: ms.why || '',
|
|
620
|
-
applies_when: ms.applies_when || [],
|
|
621
|
-
does_not_apply_when: ms.does_not_apply_when || [],
|
|
622
|
-
failure_risk: ms.failure_risk || '',
|
|
623
|
-
}, ms.id || null);
|
|
624
|
-
}
|
|
625
|
-
for (const sc of (Array.isArray(patterns.self_check) ? patterns.self_check : [])) {
|
|
626
|
-
const question = typeof sc === 'string' ? sc : sc.question || '';
|
|
627
|
-
pushImportedCard(cards, 'self_check', { question }, sc.id || null);
|
|
628
|
-
}
|
|
629
|
-
for (const aesthetic of (Array.isArray(patterns.aesthetics) ? patterns.aesthetics : [])) {
|
|
630
|
-
pushImportedCard(cards, 'aesthetic', aesthetic, aesthetic.id || null);
|
|
631
|
-
}
|
|
632
|
-
for (const scene of (Array.isArray(judgment.scenarios?.scenes) ? judgment.scenarios?.scenes : [])) {
|
|
633
|
-
pushImportedCard(cards, 'scenario', scene, scene.id || null);
|
|
634
|
-
}
|
|
635
|
-
for (const item of (Array.isArray(judgment.cases?.cases) ? judgment.cases?.cases : [])) {
|
|
636
|
-
pushImportedCard(cards, 'case', item, item.id || null);
|
|
637
|
-
}
|
|
638
|
-
for (const chain of (Array.isArray(judgment.reasoning?.reasoning_chains) ? judgment.reasoning?.reasoning_chains : [])) {
|
|
639
|
-
pushImportedCard(cards, 'reasoning', chain, chain.id || null);
|
|
640
|
-
}
|
|
641
|
-
for (const stage of (Array.isArray(judgment.evolution?.stages) ? judgment.evolution?.stages : [])) {
|
|
642
|
-
pushImportedCard(cards, 'evolution_stage', stage, stage.id || null);
|
|
643
|
-
}
|
|
644
|
-
return cards;
|
|
645
|
-
}
|
|
646
|
-
|
|
647
568
|
function cardsFromPayload(payload) {
|
|
648
569
|
if (!payload || typeof payload !== 'object') return [];
|
|
649
570
|
if (payload.profile === 'judgment-profile-v1' || payload.core || payload.source_cards) {
|
|
650
571
|
return cardsFromRuntimePayload(payload);
|
|
651
572
|
}
|
|
652
|
-
if (payload.kind === 'kdna.payload' || payload.judgment) {
|
|
653
|
-
return cardsFromLegacyPayload(payload);
|
|
654
|
-
}
|
|
655
573
|
return [];
|
|
656
574
|
}
|
|
657
575
|
|
|
@@ -675,20 +593,20 @@ function buildManifest(project, name) {
|
|
|
675
593
|
return {
|
|
676
594
|
kdna_version: '1.0',
|
|
677
595
|
asset_id: assetId,
|
|
678
|
-
asset_uid: project.asset_uid || 'urn:uuid:' + crypto.randomUUID(),
|
|
596
|
+
asset_uid: project.asset_uid || source.asset_uid || 'urn:uuid:' + crypto.randomUUID(),
|
|
679
597
|
asset_type: project.type === 'cluster' ? 'cluster' : 'domain',
|
|
680
598
|
source_asset_type: source.asset_type || null,
|
|
681
599
|
title: titleFromName(source.name || name || project.name, assetId),
|
|
682
600
|
version: semverValue(project.release?.version || source.version, '1.0.0'),
|
|
683
601
|
judgment_version: semverValue(project.release?.judgment_version || source.judgment_version || source.version, '1.0.0'),
|
|
684
|
-
created_at: isoDateTime(source.created || project.created),
|
|
685
|
-
updated_at: isoDateTime(source.updated || project.updated),
|
|
602
|
+
created_at: isoDateTime(source.created_at || source.created || project.created),
|
|
603
|
+
updated_at: isoDateTime(source.updated_at || source.updated || project.updated),
|
|
686
604
|
creator: {
|
|
687
605
|
name: author.name || project.author?.name || 'Studio Export',
|
|
688
606
|
id: author.id || project.author?.id || undefined,
|
|
689
607
|
},
|
|
690
608
|
compatibility: { min_loader_version: '1.0.0', profile: 'judgment-profile-v1' },
|
|
691
|
-
payload: { path: 'payload.kdnab', encoding: '
|
|
609
|
+
payload: { path: 'payload.kdnab', encoding: 'cbor', encrypted: false },
|
|
692
610
|
summary: source.core_insight || project.release?.description || source.description || '',
|
|
693
611
|
description: source.description || project.release?.description || '',
|
|
694
612
|
language: source.default_language || project.default_language || (Array.isArray(source.languages) ? source.languages[0] : undefined),
|
|
@@ -821,6 +739,14 @@ function importFromKdna(kdnaPath) {
|
|
|
821
739
|
if (stats.size > 50 * 1024 * 1024) {
|
|
822
740
|
fail(`KDNA file too large (${(stats.size / (1024 * 1024)).toFixed(1)} MiB, max 50 MiB): ${absKdna}`);
|
|
823
741
|
}
|
|
742
|
+
let runtimeCore;
|
|
743
|
+
try { runtimeCore = require('@aikdna/kdna-core'); } catch {
|
|
744
|
+
fail('@aikdna/kdna-core is required to validate --from-kdna imports.');
|
|
745
|
+
}
|
|
746
|
+
const validation = runtimeCore.validate(absKdna);
|
|
747
|
+
if (!validation.overall_valid) {
|
|
748
|
+
fail(`Not a current .kdna asset: ${(validation.problems || []).join('; ')}`);
|
|
749
|
+
}
|
|
824
750
|
const zipBuf = fs.readFileSync(absKdna);
|
|
825
751
|
const entries = readZipEntries(zipBuf);
|
|
826
752
|
if (!entries.has('kdna.json')) fail('Not a valid .kdna asset: missing kdna.json');
|
|
@@ -834,122 +760,21 @@ function importFromKdna(kdnaPath) {
|
|
|
834
760
|
parent_asset_digest: manifest.content_digest || manifest.asset_digest || null,
|
|
835
761
|
};
|
|
836
762
|
|
|
837
|
-
//
|
|
838
|
-
//
|
|
839
|
-
// across KDNA_*.json files. Prefer payload.kdnab when present — it
|
|
840
|
-
// covers all 8+ card types (axiom / boundary / risk / aesthetic /
|
|
841
|
-
// ontology / misunderstanding / self_check / scenario / case /
|
|
842
|
-
// reasoning / evolution_stage / stance / framework / term / banned_term)
|
|
843
|
-
// via cardsFromRuntimePayload / cardsFromLegacyPayload.
|
|
763
|
+
// Current assets carry strict-CBOR `payload.kdnab`. Legacy source folders
|
|
764
|
+
// are migrated explicitly with --from-folder; they are not runtime assets.
|
|
844
765
|
const importedCards = [];
|
|
845
|
-
if (entries.has('payload.kdnab')) {
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
// Source-tree fallback — used when an imported source has no payload.kdnab
|
|
854
|
-
// (i.e. the asset predates the 0.7 split). Bug (#67): prior
|
|
855
|
-
// version only handled axiom / ontology / boundary / misunderstanding
|
|
856
|
-
// / self_check — 5 of the 14 card types — so every other type
|
|
857
|
-
// (risk / stance / framework / term / banned_term / aesthetic /
|
|
858
|
-
// scenario / case / reasoning / evolution_stage) round-tripped
|
|
859
|
-
// out as zero cards. The fix threads every type this CLI can
|
|
860
|
-
// import through, and prefers the same field-shape rules as
|
|
861
|
-
// cardsFromLegacyPayload so the round-trip is symmetric.
|
|
862
|
-
if (entries.has('KDNA_Core.json')) {
|
|
863
|
-
const core = JSON.parse(entries.get('KDNA_Core.json').toString());
|
|
864
|
-
for (const ax of (Array.isArray(core.axioms) ? core.axioms : [])) {
|
|
865
|
-
const fields = {};
|
|
866
|
-
for (const k of ['one_sentence','full_statement','why','applies_when','does_not_apply_when','failure_risk','confidence','evidence_type']) {
|
|
867
|
-
if (k in ax) fields[k] = ax[k];
|
|
868
|
-
}
|
|
869
|
-
importedCards.push(cardApi.createCard('axiom', fields));
|
|
870
|
-
}
|
|
871
|
-
for (const ont of (Array.isArray(core.ontology) ? core.ontology : [])) {
|
|
872
|
-
importedCards.push(cardApi.createCard('ontology', {
|
|
873
|
-
one_sentence: ont.one_sentence || ont.essence || '',
|
|
874
|
-
essence: ont.essence || '', boundary: ont.boundary || '',
|
|
875
|
-
trigger_signal: ont.trigger_signal || '',
|
|
876
|
-
}));
|
|
877
|
-
}
|
|
878
|
-
for (const b of (Array.isArray(core.boundaries) ? core.boundaries : [])) {
|
|
879
|
-
importedCards.push(cardApi.createCard('boundary', {
|
|
880
|
-
scope: b.scope || '', out_of_scope: b.out_of_scope || '',
|
|
881
|
-
acceptable_exceptions: b.acceptable_exceptions || [],
|
|
882
|
-
}));
|
|
883
|
-
}
|
|
884
|
-
// Accept risks as a top-level array (`risks`) or wrapped in `risk_model`
|
|
885
|
-
// (either as `{risks: [...]}` or as an array itself). Uses the same
|
|
886
|
-
// Array.isArray ladder pattern as the safe path at lines 1033-1040.
|
|
887
|
-
let risksSource = core.risks;
|
|
888
|
-
if (!Array.isArray(risksSource)) {
|
|
889
|
-
if (Array.isArray(core.risk_model)) risksSource = core.risk_model;
|
|
890
|
-
else if (Array.isArray(core.risk_model?.risks)) risksSource = core.risk_model.risks;
|
|
891
|
-
else risksSource = [];
|
|
892
|
-
}
|
|
893
|
-
for (const risk of risksSource) {
|
|
894
|
-
importedCards.push(cardApi.createCard('risk', risk.fields || risk));
|
|
895
|
-
}
|
|
896
|
-
for (const stance of (Array.isArray(core.stances) ? core.stances : [])) {
|
|
897
|
-
importedCards.push(cardApi.createCard('stance', stance.fields || stance));
|
|
898
|
-
}
|
|
899
|
-
for (const fw of (Array.isArray(core.frameworks) ? core.frameworks : [])) {
|
|
900
|
-
importedCards.push(cardApi.createCard('framework', fw.fields || fw));
|
|
901
|
-
}
|
|
902
|
-
}
|
|
903
|
-
if (entries.has('KDNA_Patterns.json')) {
|
|
904
|
-
const pat = JSON.parse(entries.get('KDNA_Patterns.json').toString());
|
|
905
|
-
for (const ms of (Array.isArray(pat.misunderstandings) ? pat.misunderstandings : [])) {
|
|
906
|
-
importedCards.push(cardApi.createCard('misunderstanding', {
|
|
907
|
-
wrong: ms.wrong || '', correct: ms.correct || '',
|
|
908
|
-
key_distinction: ms.key_distinction || '', why: ms.why || '',
|
|
909
|
-
}));
|
|
910
|
-
}
|
|
911
|
-
for (const sc of (Array.isArray(pat.self_check) ? pat.self_check : [])) {
|
|
912
|
-
const q = typeof sc === 'string' ? sc : sc.question || '';
|
|
913
|
-
importedCards.push(cardApi.createCard('self_check', { question: q }));
|
|
914
|
-
}
|
|
915
|
-
for (const aesthetic of (Array.isArray(pat.aesthetics) ? pat.aesthetics : [])) {
|
|
916
|
-
importedCards.push(cardApi.createCard('aesthetic', aesthetic.fields || aesthetic));
|
|
917
|
-
}
|
|
918
|
-
for (const term of (Array.isArray(pat.terminology?.standard_terms) ? pat.terminology?.standard_terms : [])) {
|
|
919
|
-
importedCards.push(cardApi.createCard('term', term));
|
|
920
|
-
}
|
|
921
|
-
for (const banned of (Array.isArray(pat.terminology?.banned_terms) ? pat.terminology?.banned_terms : [])) {
|
|
922
|
-
importedCards.push(cardApi.createCard('banned_term', banned));
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
if (entries.has('KDNA_Scenarios.json')) {
|
|
926
|
-
const scen = JSON.parse(entries.get('KDNA_Scenarios.json').toString());
|
|
927
|
-
for (const s of (Array.isArray(scen.scenes) ? scen.scenes : [])) {
|
|
928
|
-
importedCards.push(cardApi.createCard('scenario', s));
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
if (entries.has('KDNA_Cases.json')) {
|
|
932
|
-
const cases = JSON.parse(entries.get('KDNA_Cases.json').toString());
|
|
933
|
-
for (const c of (Array.isArray(cases.cases) ? cases.cases : [])) {
|
|
934
|
-
importedCards.push(cardApi.createCard('case', c));
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
if (entries.has('KDNA_Reasoning.json')) {
|
|
938
|
-
const reasoning = JSON.parse(entries.get('KDNA_Reasoning.json').toString());
|
|
939
|
-
for (const chain of (Array.isArray(reasoning.reasoning_chains) ? reasoning.reasoning_chains : [])) {
|
|
940
|
-
importedCards.push(cardApi.createCard('reasoning', chain));
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
if (entries.has('KDNA_Evolution.json')) {
|
|
944
|
-
const evolution = JSON.parse(entries.get('KDNA_Evolution.json').toString());
|
|
945
|
-
for (const stage of (Array.isArray(evolution.stages) ? evolution.stages : [])) {
|
|
946
|
-
importedCards.push(cardApi.createCard('evolution_stage', stage));
|
|
947
|
-
}
|
|
948
|
-
}
|
|
766
|
+
if (!entries.has('payload.kdnab')) {
|
|
767
|
+
fail('Not a current .kdna asset: missing payload.kdnab. Use --from-folder for legacy JSON source migration.');
|
|
768
|
+
}
|
|
769
|
+
try {
|
|
770
|
+
const payload = decodePayload(entries.get('payload.kdnab'), manifest);
|
|
771
|
+
importedCards.push(...cardsFromPayload(payload));
|
|
772
|
+
} catch (e) {
|
|
773
|
+
fail(`Could not import cards from payload.kdnab: ${e.message}`);
|
|
949
774
|
}
|
|
950
775
|
|
|
951
776
|
if (importedCards.length === 0) {
|
|
952
|
-
fail(`No cards could be extracted from ${absKdna}
|
|
777
|
+
fail(`No current judgment cards could be extracted from ${absKdna}.`);
|
|
953
778
|
}
|
|
954
779
|
|
|
955
780
|
// Bug #15 / #28 follow-up: surface the source's KDNA_Patterns /
|
|
@@ -958,23 +783,13 @@ function importFromKdna(kdnaPath) {
|
|
|
958
783
|
// compileDomain via exportRuntimeAsset, which preserves
|
|
959
784
|
// changelog / version_notes / reasoning_chains / banned_terms
|
|
960
785
|
// through the round-trip.
|
|
961
|
-
const sourcePatterns = entries.has('KDNA_Patterns.json')
|
|
962
|
-
? JSON.parse(entries.get('KDNA_Patterns.json').toString())
|
|
963
|
-
: null;
|
|
964
|
-
const sourceReasoning = entries.has('KDNA_Reasoning.json')
|
|
965
|
-
? JSON.parse(entries.get('KDNA_Reasoning.json').toString())
|
|
966
|
-
: null;
|
|
967
|
-
const sourceEvolution = entries.has('KDNA_Evolution.json')
|
|
968
|
-
? JSON.parse(entries.get('KDNA_Evolution.json').toString())
|
|
969
|
-
: null;
|
|
970
|
-
|
|
971
786
|
return {
|
|
972
787
|
lineage,
|
|
973
788
|
cards: importedCards,
|
|
974
789
|
source_manifest: manifest,
|
|
975
|
-
source_patterns:
|
|
976
|
-
source_reasoning:
|
|
977
|
-
source_evolution:
|
|
790
|
+
source_patterns: null,
|
|
791
|
+
source_reasoning: null,
|
|
792
|
+
source_evolution: null,
|
|
978
793
|
};
|
|
979
794
|
}
|
|
980
795
|
|
|
@@ -1006,6 +821,7 @@ function importFromFolder(sourceDir, projectDir, projectName, creatorIdentity, o
|
|
|
1006
821
|
if (manifest) {
|
|
1007
822
|
manifestData.raw = manifest;
|
|
1008
823
|
if (manifest.version) manifestData.version = manifest.version;
|
|
824
|
+
if (manifest.asset_uid) manifestData.asset_uid = manifest.asset_uid;
|
|
1009
825
|
if (manifest.judgment_version) manifestData.judgment_version = manifest.judgment_version;
|
|
1010
826
|
if (manifest.languages) manifestData.languages = manifest.languages;
|
|
1011
827
|
if (manifest.default_language) manifestData.default_language = manifest.default_language;
|
|
@@ -1016,8 +832,8 @@ function importFromFolder(sourceDir, projectDir, projectName, creatorIdentity, o
|
|
|
1016
832
|
if (manifest.author) manifestData.author = manifest.author;
|
|
1017
833
|
if (manifest.asset_type) manifestData.asset_type = manifest.asset_type;
|
|
1018
834
|
if (manifest.core_insight) manifestData.core_insight = manifest.core_insight;
|
|
1019
|
-
if (manifest.created) manifestData.created = manifest.created;
|
|
1020
|
-
if (manifest.updated) manifestData.updated = manifest.updated;
|
|
835
|
+
if (manifest.created_at || manifest.created) manifestData.created = manifest.created_at || manifest.created;
|
|
836
|
+
if (manifest.updated_at || manifest.updated) manifestData.updated = manifest.updated_at || manifest.updated;
|
|
1021
837
|
if (manifest.risk_level) manifestData.risk_level = manifest.risk_level;
|
|
1022
838
|
if (manifest.fitness_for_purpose) manifestData.fitness_for_purpose = manifest.fitness_for_purpose;
|
|
1023
839
|
}
|
|
@@ -1054,20 +870,20 @@ function importFromFolder(sourceDir, projectDir, projectName, creatorIdentity, o
|
|
|
1054
870
|
if (key in ax) fields[key] = ax[key];
|
|
1055
871
|
else audit.missingFields.push(`axiom.${ax.id || '?'}.${key}`);
|
|
1056
872
|
}
|
|
1057
|
-
importedCards.push(cardApi.createCard('axiom', fields));
|
|
873
|
+
importedCards.push(cardApi.createCard('axiom', fields, ax.id || null));
|
|
1058
874
|
}
|
|
1059
875
|
for (const ont of (Array.isArray(core.ontology) ? core.ontology : [])) {
|
|
1060
876
|
importedCards.push(cardApi.createCard('ontology', {
|
|
1061
877
|
one_sentence: ont.one_sentence || ont.essence || '',
|
|
1062
878
|
essence: ont.essence || '', boundary: ont.boundary || '',
|
|
1063
879
|
trigger_signal: ont.trigger_signal || '',
|
|
1064
|
-
}));
|
|
880
|
+
}, ont.id || null));
|
|
1065
881
|
}
|
|
1066
882
|
for (const b of (Array.isArray(core.boundaries) ? core.boundaries : [])) {
|
|
1067
883
|
importedCards.push(cardApi.createCard('boundary', {
|
|
1068
884
|
scope: b.scope || '', out_of_scope: b.out_of_scope || '',
|
|
1069
885
|
acceptable_exceptions: b.acceptable_exceptions || [],
|
|
1070
|
-
}));
|
|
886
|
+
}, b.id || null));
|
|
1071
887
|
}
|
|
1072
888
|
// Accept risks as a top-level array (`risks`) or wrapped in `risk_model`
|
|
1073
889
|
// (either as `{risks: [...]}` or as an array itself).
|
|
@@ -1078,7 +894,7 @@ function importFromFolder(sourceDir, projectDir, projectName, creatorIdentity, o
|
|
|
1078
894
|
else risksSource = [];
|
|
1079
895
|
}
|
|
1080
896
|
for (const r of risksSource) {
|
|
1081
|
-
importedCards.push(cardApi.createCard('risk', r.fields || r));
|
|
897
|
+
importedCards.push(cardApi.createCard('risk', r.fields || r, r.id || null));
|
|
1082
898
|
}
|
|
1083
899
|
// NEW: import stances
|
|
1084
900
|
for (const s of (Array.isArray(core.stances) ? core.stances : [])) {
|
|
@@ -1087,14 +903,14 @@ function importFromFolder(sourceDir, projectDir, projectName, creatorIdentity, o
|
|
|
1087
903
|
statement: text,
|
|
1088
904
|
applies_when: s.applies_when || [],
|
|
1089
905
|
does_not_apply_when: s.does_not_apply_when || [],
|
|
1090
|
-
}));
|
|
906
|
+
}, typeof s === 'object' ? s.id || null : null));
|
|
1091
907
|
}
|
|
1092
908
|
// NEW: import frameworks
|
|
1093
909
|
for (const fw of (Array.isArray(core.frameworks) ? core.frameworks : [])) {
|
|
1094
910
|
importedCards.push(cardApi.createCard('framework', {
|
|
1095
911
|
name: fw.name || '', when_to_use: fw.when_to_use || '',
|
|
1096
912
|
steps: fw.steps || [],
|
|
1097
|
-
}));
|
|
913
|
+
}, fw.id || null));
|
|
1098
914
|
}
|
|
1099
915
|
}
|
|
1100
916
|
|
|
@@ -1104,23 +920,27 @@ function importFromFolder(sourceDir, projectDir, projectName, creatorIdentity, o
|
|
|
1104
920
|
for (const t of (Array.isArray(patterns.terminology.standard_terms) ? patterns.terminology.standard_terms : [])) {
|
|
1105
921
|
importedCards.push(cardApi.createCard('term', {
|
|
1106
922
|
term: t.term || '', definition: t.definition || '',
|
|
1107
|
-
}));
|
|
923
|
+
}, t.id || null));
|
|
1108
924
|
}
|
|
1109
925
|
for (const bt of (Array.isArray(patterns.terminology.banned_terms) ? patterns.terminology.banned_terms : [])) {
|
|
1110
926
|
importedCards.push(cardApi.createCard('banned_term', {
|
|
1111
927
|
term: bt.term || '', why: bt.why || '', replace_with: bt.replace_with || '',
|
|
1112
|
-
}));
|
|
928
|
+
}, bt.id || null));
|
|
1113
929
|
}
|
|
1114
930
|
}
|
|
1115
931
|
for (const ms of (Array.isArray(patterns.misunderstandings) ? patterns.misunderstandings : [])) {
|
|
1116
932
|
importedCards.push(cardApi.createCard('misunderstanding', {
|
|
1117
933
|
wrong: ms.wrong || '', correct: ms.correct || '',
|
|
1118
934
|
key_distinction: ms.key_distinction || '', why: ms.why || '',
|
|
1119
|
-
}));
|
|
935
|
+
}, ms.id || null));
|
|
1120
936
|
}
|
|
1121
937
|
for (const sc of (Array.isArray(patterns.self_check) ? patterns.self_check : [])) {
|
|
1122
938
|
const q = typeof sc === 'string' ? sc : sc.question || '';
|
|
1123
|
-
importedCards.push(cardApi.createCard(
|
|
939
|
+
importedCards.push(cardApi.createCard(
|
|
940
|
+
'self_check',
|
|
941
|
+
{ question: q },
|
|
942
|
+
typeof sc === 'object' ? sc.id || null : null,
|
|
943
|
+
));
|
|
1124
944
|
}
|
|
1125
945
|
// Bug (aesthetic round-trip): prior version dropped
|
|
1126
946
|
// `patterns.aesthetics` on import. `cardsFromRuntimePayload` and
|
|
@@ -1129,7 +949,11 @@ function importFromFolder(sourceDir, projectDir, projectName, creatorIdentity, o
|
|
|
1129
949
|
// them when imported through `create --from-folder`. The fix
|
|
1130
950
|
// adds the missing loop here.
|
|
1131
951
|
for (const aesthetic of (Array.isArray(patterns.aesthetics) ? patterns.aesthetics : [])) {
|
|
1132
|
-
importedCards.push(cardApi.createCard(
|
|
952
|
+
importedCards.push(cardApi.createCard(
|
|
953
|
+
'aesthetic',
|
|
954
|
+
aesthetic.fields || aesthetic,
|
|
955
|
+
aesthetic.id || null,
|
|
956
|
+
));
|
|
1133
957
|
}
|
|
1134
958
|
}
|
|
1135
959
|
|
|
@@ -1139,7 +963,7 @@ function importFromFolder(sourceDir, projectDir, projectName, creatorIdentity, o
|
|
|
1139
963
|
importedCards.push(cardApi.createCard('scenario', {
|
|
1140
964
|
name: scene.name || scene.id || '',
|
|
1141
965
|
trigger: scene.trigger || '', action: scene.action || '',
|
|
1142
|
-
}));
|
|
966
|
+
}, scene.id || null));
|
|
1143
967
|
}
|
|
1144
968
|
}
|
|
1145
969
|
|
|
@@ -1149,7 +973,7 @@ function importFromFolder(sourceDir, projectDir, projectName, creatorIdentity, o
|
|
|
1149
973
|
importedCards.push(cardApi.createCard('case', {
|
|
1150
974
|
title: c.title || c.id || '',
|
|
1151
975
|
scenario: c.scenario || '', expected: c.expected || '',
|
|
1152
|
-
}));
|
|
976
|
+
}, c.id || null));
|
|
1153
977
|
}
|
|
1154
978
|
}
|
|
1155
979
|
|
|
@@ -1170,7 +994,7 @@ function importFromFolder(sourceDir, projectDir, projectName, creatorIdentity, o
|
|
|
1170
994
|
concrete_action: rc.concrete_action || rc.so_what || '',
|
|
1171
995
|
so_what: rc.so_what || rc.concrete_action || '',
|
|
1172
996
|
conclusion: rc.conclusion || '',
|
|
1173
|
-
}));
|
|
997
|
+
}, rc.id || null));
|
|
1174
998
|
}
|
|
1175
999
|
}
|
|
1176
1000
|
|
|
@@ -1194,6 +1018,7 @@ function importFromFolder(sourceDir, projectDir, projectName, creatorIdentity, o
|
|
|
1194
1018
|
lineage: { type: 'migrated', parent_name: manifestData.name || null, parent_asset_uid: null, parent_version: manifestData.version || null, parent_asset_digest: null },
|
|
1195
1019
|
});
|
|
1196
1020
|
// Preserve manifest metadata
|
|
1021
|
+
if (manifestData.asset_uid) project.asset_uid = manifestData.asset_uid;
|
|
1197
1022
|
if (manifestData.version) {
|
|
1198
1023
|
if (!project.release) project.release = {};
|
|
1199
1024
|
project.release.version = manifestData.version;
|
|
@@ -1992,17 +1817,7 @@ function exportProject(project, name, outPath, opts = {}) {
|
|
|
1992
1817
|
);
|
|
1993
1818
|
}
|
|
1994
1819
|
}
|
|
1995
|
-
const
|
|
1996
|
-
if (gate.blocked) {
|
|
1997
|
-
if (opts.allowIncomplete) {
|
|
1998
|
-
console.warn('Human Lock Gate bypassed (--allow-incomplete):');
|
|
1999
|
-
for (const issue of gate.issues) console.warn(` - ${issue.cardId}: ${issue.reason}`);
|
|
2000
|
-
} else {
|
|
2001
|
-
const reasons = gate.issues.map((i) => `${i.cardId}: ${i.reason}`).join('\n - ');
|
|
2002
|
-
fail(`Human Lock Gate blocked export:\n - ${reasons}\nUse --allow-incomplete to bypass.`, EXIT.HUMAN_LOCK_REQUIRED);
|
|
2003
|
-
}
|
|
2004
|
-
}
|
|
2005
|
-
const lockedCards = (project.cards || []).filter(c => c.locked);
|
|
1820
|
+
const exportedCards = (project.cards || []).filter(c => c.status !== 'deprecated');
|
|
2006
1821
|
const runtimeDir = trackTempDir(fs.mkdtempSync(path.join(os.tmpdir(), 'kdna-runtime-')));
|
|
2007
1822
|
try {
|
|
2008
1823
|
const manifestDefaults = buildManifest(project, name);
|
|
@@ -2021,7 +1836,7 @@ function exportProject(project, name, outPath, opts = {}) {
|
|
|
2021
1836
|
const vr = core.validate(outPath);
|
|
2022
1837
|
if (!vr.overall_valid) fail('runtime export validation failed: ' + (vr.problems || []).join('; '));
|
|
2023
1838
|
console.log('Exported: ' + outPath);
|
|
2024
|
-
console.log(' Name: ' + name + ' Cards: ' +
|
|
1839
|
+
console.log(' Name: ' + name + ' Cards: ' + exportedCards.length + ' Validated: runtime contract passed');
|
|
2025
1840
|
} finally {
|
|
2026
1841
|
try { fs.rmSync(runtimeDir, { recursive: true }); } catch { /* cleanup */ }
|
|
2027
1842
|
}
|
|
@@ -2029,11 +1844,6 @@ function exportProject(project, name, outPath, opts = {}) {
|
|
|
2029
1844
|
|
|
2030
1845
|
function compileProject(projectInput) {
|
|
2031
1846
|
const { project } = readProject(projectInput);
|
|
2032
|
-
const gate = projectApi.checkHumanLockGate(project);
|
|
2033
|
-
if (gate.blocked) {
|
|
2034
|
-
const reasons = gate.issues.map((i) => `${i.cardId}: ${i.reason}`).join('\n - ');
|
|
2035
|
-
fail(`Human Lock Gate blocked compile:\n - ${reasons}`, EXIT.HUMAN_LOCK_REQUIRED);
|
|
2036
|
-
}
|
|
2037
1847
|
return { project, result: compileApi.compileDomain(project) };
|
|
2038
1848
|
}
|
|
2039
1849
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aikdna/kdna-studio-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "Official KDNA Studio command-line authoring entry for creating, reviewing, compiling, and exporting .kdna judgment assets through the KDNA toolchain.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"bin": {
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
"node": ">=18"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@aikdna/kdna-core": "0.
|
|
45
|
-
"@aikdna/kdna-studio-core": "1.
|
|
44
|
+
"@aikdna/kdna-core": "0.16.0",
|
|
45
|
+
"@aikdna/kdna-studio-core": "1.8.0",
|
|
46
46
|
"ajv": "^8.20.0",
|
|
47
47
|
"ajv-formats": "^3.0.1",
|
|
48
48
|
"cbor-x": "^1.6.4"
|