@aikdna/kdna-cli 0.17.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.
@@ -0,0 +1,119 @@
1
+ const { error, EXIT } = require('./_common');
2
+ const { parseName } = require('../registry');
3
+ const { getInstalled, readContainer } = require('../package-store');
4
+
5
+ function cmdExplain(args) {
6
+ const target = args.filter((a) => !a.startsWith('--'))[1];
7
+ if (!target) {
8
+ error(
9
+ 'Usage: kdna explain <domain> [--locale zh-CN]\n\n' +
10
+ ' Produces a natural language explanation of what a domain covers,\n' +
11
+ ' its key axioms, applicable scenarios, and intended model types.',
12
+ EXIT.INPUT_ERROR,
13
+ );
14
+ }
15
+
16
+ const parsed = parseName(target);
17
+ if (!parsed) {
18
+ error(`Invalid domain name: ${target}`, EXIT.INPUT_ERROR);
19
+ }
20
+
21
+ const installed = getInstalled(parsed.full);
22
+ if (!installed) {
23
+ error(`${parsed.full} is not installed.\nRun: kdna install ${target}`, EXIT.INPUT_ERROR);
24
+ }
25
+
26
+ const { core, patterns, scenarios } = readContainer(installed.asset_path);
27
+
28
+ if (!core) {
29
+ error(`Failed to load KDNA_Core.json from ${installed.asset_path}`, EXIT.VALIDATION_FAILED);
30
+ }
31
+
32
+ const m = core.meta || {};
33
+ const purpose = m.purpose || '(not specified)';
34
+ const domain = m.domain || parsed.ident;
35
+ const version = m.version || 'unknown';
36
+ const axioms = core.axioms || [];
37
+ const bannedTerms = patterns?.terminology?.banned_terms || [];
38
+ const selfChecks = patterns?.self_checks || patterns?.self_check || [];
39
+ const standardTerms = patterns?.terminology?.standard_terms || [];
40
+ const misunderstandings = patterns?.misunderstandings || [];
41
+
42
+ console.log('');
43
+ console.log(`╔══════════════════════════════════════════════════════════════╗`);
44
+ console.log(`║ KDNA Domain: ${domain.padEnd(46)}║`);
45
+ console.log(`║ Package: ${parsed.full.padEnd(46)}║`);
46
+ console.log(`║ Version: ${version.padEnd(46)}║`);
47
+ console.log(`╚══════════════════════════════════════════════════════════════╝`);
48
+ console.log('');
49
+
50
+ console.log('── Purpose ──');
51
+ console.log(` ${purpose}`);
52
+ console.log('');
53
+
54
+ console.log(`── Axioms (${axioms.length} core principles) ──`);
55
+ for (const ax of axioms) {
56
+ console.log(` • ${ax.one_sentence || ax.id}`);
57
+ const applies = (ax.applies_when || []).join('; ');
58
+ if (applies) console.log(` Applies when: ${applies.slice(0, 120)}`);
59
+ const notApply = (ax.does_not_apply_when || []).join('; ');
60
+ if (notApply) console.log(` NOT when: ${notApply.slice(0, 120)}`);
61
+ console.log('');
62
+ }
63
+
64
+ if (standardTerms.length) {
65
+ console.log(`── Standard Terms (${standardTerms.length}) ──`);
66
+ for (const t of standardTerms.slice(0, 8)) {
67
+ const def = t.definition ? `: ${t.definition.slice(0, 80)}` : '';
68
+ console.log(` • ${t.term}${def}`);
69
+ }
70
+ console.log('');
71
+ }
72
+
73
+ if (misunderstandings.length) {
74
+ console.log(`── Common Misunderstandings (${misunderstandings.length}) ──`);
75
+ for (const mm of misunderstandings.slice(0, 5)) {
76
+ console.log(` ✗ "${mm.mistake || mm.id}"`);
77
+ }
78
+ console.log('');
79
+ }
80
+
81
+ if (bannedTerms.length) {
82
+ console.log(`── Banned Terms (${bannedTerms.length} — do not use) ──`);
83
+ for (const b of bannedTerms.slice(0, 6)) {
84
+ const why = b.why ? ` → ${b.why.slice(0, 60)}` : '';
85
+ console.log(` ✗ "${b.term}"${why}`);
86
+ }
87
+ console.log('');
88
+ }
89
+
90
+ if (selfChecks.length) {
91
+ console.log(`── Self-Checks (${selfChecks.length} — verify before responding) ──`);
92
+ for (const sc of selfChecks.slice(0, 8)) {
93
+ console.log(` ✓ ${sc.question || sc.id || sc}`);
94
+ }
95
+ console.log('');
96
+ }
97
+
98
+ if (scenarios && scenarios.scenarios && scenarios.scenarios.length) {
99
+ console.log(`── Scenarios (${scenarios.scenarios.length} — strategy shifts) ──`);
100
+ for (const s of scenarios.scenarios.slice(0, 6)) {
101
+ const desc = s.description ? s.description.slice(0, 80) : '';
102
+ console.log(` ▶ ${s.signal || s.id}: ${desc}`);
103
+ }
104
+ console.log('');
105
+ }
106
+
107
+ console.log('── Model Compatibility ──');
108
+ console.log(' Works with any LLM/agent that loads context before reasoning.');
109
+ console.log(' Tested: Claude, GPT, Gemini, Qwen, MiniMax');
110
+ console.log('');
111
+
112
+ console.log('── Quick Start ──');
113
+ console.log(` kdna verify ${target} --judgment`);
114
+ console.log(` kdna compare ${target} --input "<your task>"`);
115
+ console.log(` kdna trace --domain ${target.slice(0, 40)}`);
116
+ console.log('');
117
+ }
118
+
119
+ module.exports = { cmdExplain };
@@ -36,7 +36,8 @@ function cmdProposalCreate(args = []) {
36
36
  if (!fs.existsSync(absTest)) error(`Test run file not found: ${absTest}`, EXIT.INPUT_ERROR);
37
37
 
38
38
  const runData = readJson(absTest);
39
- if (!runData || !runData.test_id) error(`Not a valid test run file: ${absTest}`, EXIT.INPUT_ERROR);
39
+ if (!runData || !runData.test_id)
40
+ error(`Not a valid test run file: ${absTest}`, EXIT.INPUT_ERROR);
40
41
 
41
42
  const absDomain = path.resolve(domainPath);
42
43
 
@@ -63,7 +64,10 @@ function cmdProposalCreate(args = []) {
63
64
  };
64
65
 
65
66
  // Auto-detect suggested changes from test result gaps
66
- if (runData.expected?.classification && runData.expected.classification !== runData.results?.classification) {
67
+ if (
68
+ runData.expected?.classification &&
69
+ runData.expected.classification !== runData.results?.classification
70
+ ) {
67
71
  proposal.suggested_changes.push({
68
72
  what: 'axiom',
69
73
  field: 'applies_when',
@@ -80,7 +84,17 @@ function cmdProposalCreate(args = []) {
80
84
  writeJson(outFile, proposal);
81
85
 
82
86
  if (jsonMode) {
83
- console.log(JSON.stringify({ proposal_id: proposal.proposal_id, saved: outFile, suggested_changes: proposal.suggested_changes.length }, null, 2));
87
+ console.log(
88
+ JSON.stringify(
89
+ {
90
+ proposal_id: proposal.proposal_id,
91
+ saved: outFile,
92
+ suggested_changes: proposal.suggested_changes.length,
93
+ },
94
+ null,
95
+ 2,
96
+ ),
97
+ );
84
98
  return;
85
99
  }
86
100
 
@@ -98,7 +112,8 @@ function cmdProposalCreate(args = []) {
98
112
 
99
113
  function cmdProposalValidate(args = []) {
100
114
  const jsonMode = args.includes('--json');
101
- const target = args.filter((a) => !a.startsWith('--'))[2] || args.filter((a) => !a.startsWith('--'))[1];
115
+ const target =
116
+ args.filter((a) => !a.startsWith('--'))[2] || args.filter((a) => !a.startsWith('--'))[1];
102
117
  if (!target) error('Usage: kdna proposal validate <proposal.json>', EXIT.INPUT_ERROR);
103
118
 
104
119
  const abs = path.resolve(target);
@@ -113,7 +128,8 @@ function cmdProposalValidate(args = []) {
113
128
  if (!proposal.source) issues.push('missing source');
114
129
  if (!proposal.domain) issues.push('missing domain');
115
130
  if (!proposal.trigger?.test_id) issues.push('missing trigger.test_id');
116
- if (!proposal.reasoning || proposal.reasoning.length < 10) issues.push('reasoning too short (min 10 chars)');
131
+ if (!proposal.reasoning || proposal.reasoning.length < 10)
132
+ issues.push('reasoning too short (min 10 chars)');
117
133
  if (!proposal.suggested_changes || proposal.suggested_changes.length === 0) {
118
134
  issues.push('no suggested changes');
119
135
  } else {
@@ -124,11 +140,17 @@ function cmdProposalValidate(args = []) {
124
140
  }
125
141
 
126
142
  if (jsonMode) {
127
- console.log(JSON.stringify({
128
- proposal_id: proposal.proposal_id,
129
- valid: issues.length === 0,
130
- issues,
131
- }, null, 2));
143
+ console.log(
144
+ JSON.stringify(
145
+ {
146
+ proposal_id: proposal.proposal_id,
147
+ valid: issues.length === 0,
148
+ issues,
149
+ },
150
+ null,
151
+ 2,
152
+ ),
153
+ );
132
154
  process.exit(issues.length ? EXIT.VALIDATION_FAILED : EXIT.OK);
133
155
  }
134
156
 
@@ -184,12 +206,18 @@ function cmdReview(args = []) {
184
206
  writeJson(abs, proposal);
185
207
 
186
208
  if (jsonMode) {
187
- console.log(JSON.stringify({
188
- proposal_id: proposal.proposal_id,
189
- decision: sub,
190
- by,
191
- reason,
192
- }, null, 2));
209
+ console.log(
210
+ JSON.stringify(
211
+ {
212
+ proposal_id: proposal.proposal_id,
213
+ decision: sub,
214
+ by,
215
+ reason,
216
+ },
217
+ null,
218
+ 2,
219
+ ),
220
+ );
193
221
  process.exit(sub === 'reject' ? EXIT.POLICY_VIOLATION : EXIT.OK);
194
222
  }
195
223
 
@@ -217,7 +245,10 @@ function cmdLockCard(args = []) {
217
245
  // Lock card in the current studio project (finds studio.project.json)
218
246
  const projectPath = path.resolve('studio.project.json');
219
247
  if (!fs.existsSync(projectPath)) {
220
- error('No studio.project.json found in current directory. Run: kdna studio scaffold', EXIT.INPUT_ERROR);
248
+ error(
249
+ 'No studio.project.json found in current directory. Run: kdna studio scaffold',
250
+ EXIT.INPUT_ERROR,
251
+ );
221
252
  }
222
253
 
223
254
  const project = readJson(projectPath);
@@ -240,11 +271,17 @@ function cmdLockCard(args = []) {
240
271
  found = true;
241
272
 
242
273
  if (jsonMode) {
243
- console.log(JSON.stringify({
244
- card: `${type}.${id}`,
245
- locked: true,
246
- lock: card.human_lock,
247
- }, null, 2));
274
+ console.log(
275
+ JSON.stringify(
276
+ {
277
+ card: `${type}.${id}`,
278
+ locked: true,
279
+ lock: card.human_lock,
280
+ },
281
+ null,
282
+ 2,
283
+ ),
284
+ );
248
285
  } else {
249
286
  console.log(`✓ Locked: ${type}.${id}`);
250
287
  console.log(` By: ${by}`);
@@ -254,7 +291,10 @@ function cmdLockCard(args = []) {
254
291
  }
255
292
 
256
293
  if (!found) {
257
- error(`Card not found: ${cardId}. Check the card ID and that a studio project exists.`, EXIT.INPUT_ERROR);
294
+ error(
295
+ `Card not found: ${cardId}. Check the card ID and that a studio project exists.`,
296
+ EXIT.INPUT_ERROR,
297
+ );
258
298
  }
259
299
  }
260
300
 
@@ -320,17 +360,23 @@ function cmdEvolutionReport(domainPath, jsonMode) {
320
360
  const pending = evolution.pending || [];
321
361
 
322
362
  if (jsonMode) {
323
- console.log(JSON.stringify({
324
- domain: path.basename(abs),
325
- total_stages: stages.length,
326
- pending_changes: pending.length,
327
- stages: stages.map((s) => ({
328
- stage: s.stage,
329
- version: s.version,
330
- date: s.date,
331
- changes: s.changes?.length || 0,
332
- })),
333
- }, null, 2));
363
+ console.log(
364
+ JSON.stringify(
365
+ {
366
+ domain: path.basename(abs),
367
+ total_stages: stages.length,
368
+ pending_changes: pending.length,
369
+ stages: stages.map((s) => ({
370
+ stage: s.stage,
371
+ version: s.version,
372
+ date: s.date,
373
+ changes: s.changes?.length || 0,
374
+ })),
375
+ },
376
+ null,
377
+ 2,
378
+ ),
379
+ );
334
380
  return;
335
381
  }
336
382
 
@@ -362,7 +408,10 @@ function cmdRegression(args = []) {
362
408
  const evalsDir = evalsIdx >= 0 ? args[evalsIdx + 1] : null;
363
409
 
364
410
  if (!oldPath || !newPath) {
365
- error('Usage: kdna regression <old-domain> <new-domain> --evals <dir> [--json]', EXIT.INPUT_ERROR);
411
+ error(
412
+ 'Usage: kdna regression <old-domain> <new-domain> --evals <dir> [--json]',
413
+ EXIT.INPUT_ERROR,
414
+ );
366
415
  }
367
416
 
368
417
  const absOld = path.resolve(oldPath);
@@ -421,8 +470,18 @@ function cmdRegression(args = []) {
421
470
 
422
471
  const result = {
423
472
  domain: path.basename(absOld),
424
- old: { axioms: oldAxiomCount, misunderstandings: oldMisCount, self_checks: oldSelfCheckCount, governance_coverage: oldGov },
425
- new: { axioms: newAxiomCount, misunderstandings: newMisCount, self_checks: newSelfCheckCount, governance_coverage: newGov },
473
+ old: {
474
+ axioms: oldAxiomCount,
475
+ misunderstandings: oldMisCount,
476
+ self_checks: oldSelfCheckCount,
477
+ governance_coverage: oldGov,
478
+ },
479
+ new: {
480
+ axioms: newAxiomCount,
481
+ misunderstandings: newMisCount,
482
+ self_checks: newSelfCheckCount,
483
+ governance_coverage: newGov,
484
+ },
426
485
  delta: {
427
486
  axioms: newAxiomCount - oldAxiomCount,
428
487
  misunderstandings: newMisCount - oldMisCount,
@@ -442,12 +501,22 @@ function cmdRegression(args = []) {
442
501
 
443
502
  console.log(`Regression check: ${path.basename(absOld)} → ${path.basename(absNew)}`);
444
503
  console.log('');
445
- console.log(` Axioms: ${oldAxiomCount} → ${newAxiomCount} (${result.delta.axioms >= 0 ? '+' : ''}${result.delta.axioms})`);
446
- console.log(` Misunderstandings: ${oldMisCount} → ${newMisCount} (${result.delta.misunderstandings >= 0 ? '+' : ''}${result.delta.misunderstandings})`);
447
- console.log(` Self-checks: ${oldSelfCheckCount} → ${newSelfCheckCount} (${result.delta.self_checks >= 0 ? '+' : ''}${result.delta.self_checks})`);
448
- console.log(` Governance coverage: ${oldGov}% → ${newGov}% (${result.delta.governance_coverage >= 0 ? '+' : ''}${result.delta.governance_coverage}%)`);
504
+ console.log(
505
+ ` Axioms: ${oldAxiomCount} → ${newAxiomCount} (${result.delta.axioms >= 0 ? '+' : ''}${result.delta.axioms})`,
506
+ );
507
+ console.log(
508
+ ` Misunderstandings: ${oldMisCount} → ${newMisCount} (${result.delta.misunderstandings >= 0 ? '+' : ''}${result.delta.misunderstandings})`,
509
+ );
510
+ console.log(
511
+ ` Self-checks: ${oldSelfCheckCount} → ${newSelfCheckCount} (${result.delta.self_checks >= 0 ? '+' : ''}${result.delta.self_checks})`,
512
+ );
513
+ console.log(
514
+ ` Governance coverage: ${oldGov}% → ${newGov}% (${result.delta.governance_coverage >= 0 ? '+' : ''}${result.delta.governance_coverage}%)`,
515
+ );
449
516
  if (totalEvals) {
450
- console.log(` Evals: ${passedEvals} passed, ${failedEvals} failed out of ${totalEvals}`);
517
+ console.log(
518
+ ` Evals: ${passedEvals} passed, ${failedEvals} failed out of ${totalEvals}`,
519
+ );
451
520
  }
452
521
  console.log('');
453
522
  const mark = result.safe ? '✓' : '✗';
@@ -2,10 +2,9 @@ const { error } = require('./_common');
2
2
 
3
3
  function cmdPreview() {
4
4
  // Removed in v0.9 — no real user scenario for browser preview.
5
- // To inspect a .kdna file, use: kdna inspect <path>
5
+ // To inspect a .kdna file, use: kdna inspect <file.kdna>
6
6
  error(
7
- 'kdna preview was removed in v0.9.\n' +
8
- 'Use: kdna inspect <path> to view a .kdna file or domain directory.',
7
+ 'kdna preview was removed in v0.9.\n' + 'Use: kdna inspect <file.kdna> to view a .kdna asset.',
9
8
  );
10
9
  }
11
10
 
@@ -18,8 +17,8 @@ function cmdProject() {
18
17
  'kdna project was removed in v0.9. The .kdna/config.json file is no\n' +
19
18
  'longer read by the kdna-loader skill — it would have forced KDNA\n' +
20
19
  'loading on tasks where the user did not ask for it.\n\n' +
21
- 'The agent now discovers KDNA on demand by reading ~/.kdna/domains/\n' +
22
- 'and matching the task against v2.1 applies_when fields.\n\n' +
20
+ 'The agent now discovers KDNA on demand through kdna available/load,\n' +
21
+ 'which read installed .kdna assets from the package index.\n\n' +
23
22
  'If you have stale .kdna/config.json files in your projects, you\n' +
24
23
  'can delete them — nothing reads them anymore.',
25
24
  );
@@ -32,7 +31,7 @@ function cmdEval() {
32
31
  error(
33
32
  'kdna eval was removed in v0.9.\n' +
34
33
  'To compare with/without KDNA reasoning, use:\n' +
35
- ' kdna compare <name> --input "<task>"\n' +
34
+ ' kdna compare <name|file.kdna> --input "<task>"\n' +
36
35
  'To inspect a domain, use:\n' +
37
36
  ' kdna info <name>',
38
37
  );
@@ -53,17 +52,17 @@ function cmdSelect() {
53
52
  }
54
53
 
55
54
  function cmdExport() {
56
- // Removed in v0.9 — was an alias for `kdna pack`.
55
+ // Removed in v0.9 — was an alias for the old top-level pack command.
57
56
  error(
58
57
  'kdna export was removed in v0.9 (it was an alias for pack).\n' +
59
- 'Use: kdna pack <path> [--output <dir>]',
58
+ 'Use: kdna dev pack <source-dir> [--output <dir>]',
60
59
  );
61
60
  }
62
61
 
63
62
  function cmdDemo() {
64
63
  // Removed in v0.9 — internal demo, not a user feature. To see
65
64
  // before/after on a real input, use:
66
- // kdna compare <name> --input "<task>" (requires LLM API key)
65
+ // kdna compare <name|file.kdna> --input "<task>" (requires LLM API key)
67
66
  error(
68
67
  'kdna demo was removed in v0.9.\n' +
69
68
  'To see KDNA before/after on a real input, use:\n' +