@aikdna/kdna-cli 0.18.0 → 0.19.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 +101 -101
- package/SECURITY.md +1 -1
- package/package.json +3 -2
- package/skills/kdna-loader/SKILL.md +18 -16
- package/src/agent.js +152 -57
- package/src/cli.js +17 -11
- package/src/cmds/_common.js +9 -3
- package/src/cmds/badge.js +8 -3
- package/src/cmds/changelog.js +31 -11
- package/src/cmds/cluster.js +67 -40
- package/src/cmds/domain.js +37 -27
- package/src/cmds/explain.js +1 -4
- package/src/cmds/governance.js +111 -42
- package/src/cmds/legacy.js +1 -2
- package/src/cmds/license.js +16 -8
- package/src/cmds/quality.js +9 -2
- package/src/cmds/registry.js +11 -5
- package/src/cmds/studio.js +95 -42
- package/src/cmds/test.js +5 -2
- package/src/compare.js +15 -2
- package/src/diff.js +27 -11
- package/src/identity.js +9 -7
- package/src/install.js +17 -7
- package/src/package-store.js +4 -1
- package/src/paths.js +2 -2
- package/src/publish.js +90 -42
- package/src/registry.js +13 -8
- package/src/search.js +4 -5
- package/src/verify.js +61 -17
- package/src/version.js +15 -7
- package/templates/minimal-domain/kdna.json +7 -7
- package/templates/standard-domain/README.md +10 -10
- package/templates/standard-domain/kdna.json +6 -3
- package/validators/kdna-lint.js +45 -11
package/src/cmds/registry.js
CHANGED
|
@@ -13,8 +13,10 @@ function cmdList(showAvailable, jsonMode = false, _locale = null) {
|
|
|
13
13
|
error('No registry found.');
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
const installableDomains = domains.filter((d) => d.yanked !== true);
|
|
17
|
+
|
|
16
18
|
if (jsonMode) {
|
|
17
|
-
const result =
|
|
19
|
+
const result = installableDomains.map((d) => ({
|
|
18
20
|
name: d.name || d.id || null,
|
|
19
21
|
version: d.version || null,
|
|
20
22
|
type: d.type || 'domain',
|
|
@@ -27,16 +29,20 @@ function cmdList(showAvailable, jsonMode = false, _locale = null) {
|
|
|
27
29
|
process.exit(EXIT.OK);
|
|
28
30
|
}
|
|
29
31
|
|
|
30
|
-
console.log('Available KDNA domains:');
|
|
32
|
+
console.log('Available installable KDNA domains:');
|
|
31
33
|
console.log(`Registry: ${REGISTRY_CACHE}`);
|
|
32
34
|
console.log('');
|
|
33
|
-
|
|
35
|
+
if (!installableDomains.length) {
|
|
36
|
+
console.log(' No non-yanked registry entries are currently installable.');
|
|
37
|
+
console.log('');
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
for (const d of installableDomains) {
|
|
34
41
|
const name = d.name || d.id || '?';
|
|
35
42
|
const installed = listInstalled().some((entry) => entry.full === name) ? '[installed]' : '';
|
|
36
|
-
const yanked = d.yanked ? '[yanked] ' : '';
|
|
37
43
|
const dep = d.deprecated ? '[deprecated] ' : '';
|
|
38
44
|
console.log(
|
|
39
|
-
` ${name.padEnd(36)} ${(d.version || '?').padEnd(8)} ${(d.type || 'domain').padEnd(8)} ${(d.status || '').padEnd(14)} ${
|
|
45
|
+
` ${name.padEnd(36)} ${(d.version || '?').padEnd(8)} ${(d.type || 'domain').padEnd(8)} ${(d.status || '').padEnd(14)} ${dep}${installed}`,
|
|
40
46
|
);
|
|
41
47
|
if (d.description) console.log(` ${d.description}`);
|
|
42
48
|
console.log('');
|
package/src/cmds/studio.js
CHANGED
|
@@ -79,9 +79,16 @@ const CARD_TEMPLATES = {
|
|
|
79
79
|
};
|
|
80
80
|
|
|
81
81
|
function cmdStudioScaffold(name, args = []) {
|
|
82
|
-
if (!name)
|
|
82
|
+
if (!name)
|
|
83
|
+
error(
|
|
84
|
+
'Usage: kdna studio scaffold <name> [--type=domain|cluster] [--minimal]',
|
|
85
|
+
EXIT.INPUT_ERROR,
|
|
86
|
+
);
|
|
83
87
|
if (!/^[a-z][a-z0-9_-]*$/.test(name)) {
|
|
84
|
-
error(
|
|
88
|
+
error(
|
|
89
|
+
`Invalid name "${name}". Use lowercase letters, numbers, hyphens, underscores. Start with letter.`,
|
|
90
|
+
EXIT.INPUT_ERROR,
|
|
91
|
+
);
|
|
85
92
|
}
|
|
86
93
|
|
|
87
94
|
const type = args.includes('--type=cluster') ? 'cluster' : 'domain';
|
|
@@ -154,15 +161,22 @@ function cmdCardsValidate(projectPath, args = []) {
|
|
|
154
161
|
|
|
155
162
|
if (!fs.existsSync(abs)) error(`Project file not found: ${abs}`, EXIT.INPUT_ERROR);
|
|
156
163
|
const project = readJson(abs);
|
|
157
|
-
if (!project || !project.kdna_studio)
|
|
164
|
+
if (!project || !project.kdna_studio)
|
|
165
|
+
error(`Not a KDNA Studio project: ${abs}`, EXIT.INPUT_ERROR);
|
|
158
166
|
|
|
159
167
|
const errors = [];
|
|
160
168
|
const warnings = [];
|
|
161
169
|
const passed = [];
|
|
162
170
|
|
|
163
|
-
function fail(msg) {
|
|
164
|
-
|
|
165
|
-
|
|
171
|
+
function fail(msg) {
|
|
172
|
+
errors.push(msg);
|
|
173
|
+
}
|
|
174
|
+
function warn(msg) {
|
|
175
|
+
warnings.push(msg);
|
|
176
|
+
}
|
|
177
|
+
function ok(msg) {
|
|
178
|
+
passed.push(msg);
|
|
179
|
+
}
|
|
166
180
|
|
|
167
181
|
// Validate each card set
|
|
168
182
|
for (const [cardType, cardFile] of Object.entries(project.cards || {})) {
|
|
@@ -192,7 +206,11 @@ function cmdCardsValidate(projectPath, args = []) {
|
|
|
192
206
|
} else {
|
|
193
207
|
ok(`axiom ${label}: applies_when has ${ax.applies_when.length} entries`);
|
|
194
208
|
}
|
|
195
|
-
if (
|
|
209
|
+
if (
|
|
210
|
+
!ax.does_not_apply_when ||
|
|
211
|
+
!Array.isArray(ax.does_not_apply_when) ||
|
|
212
|
+
ax.does_not_apply_when.length === 0
|
|
213
|
+
) {
|
|
196
214
|
fail(`axiom ${label}: missing does_not_apply_when`);
|
|
197
215
|
} else {
|
|
198
216
|
ok(`axiom ${label}: does_not_apply_when has ${ax.does_not_apply_when.length} entries`);
|
|
@@ -208,9 +226,11 @@ function cmdCardsValidate(projectPath, args = []) {
|
|
|
208
226
|
case 'misunderstandings':
|
|
209
227
|
for (const ms of cards) {
|
|
210
228
|
const label = ms.id || '?';
|
|
211
|
-
if (!ms.wrong || ms.wrong.includes('[TODO]'))
|
|
229
|
+
if (!ms.wrong || ms.wrong.includes('[TODO]'))
|
|
230
|
+
warn(`misunderstanding ${label}: wrong is placeholder`);
|
|
212
231
|
else ok(`misunderstanding ${label}: wrong OK`);
|
|
213
|
-
if (!ms.correct || ms.correct.includes('[TODO]'))
|
|
232
|
+
if (!ms.correct || ms.correct.includes('[TODO]'))
|
|
233
|
+
warn(`misunderstanding ${label}: correct is placeholder`);
|
|
214
234
|
else ok(`misunderstanding ${label}: correct OK`);
|
|
215
235
|
if (!ms.key_distinction || ms.key_distinction.length < 15) {
|
|
216
236
|
fail(`misunderstanding ${label}: key_distinction missing or too short`);
|
|
@@ -223,14 +243,18 @@ function cmdCardsValidate(projectPath, args = []) {
|
|
|
223
243
|
case 'boundaries':
|
|
224
244
|
for (const bd of cards) {
|
|
225
245
|
const label = bd.id || '?';
|
|
226
|
-
if (!bd.scope || bd.scope.includes('[TODO]'))
|
|
246
|
+
if (!bd.scope || bd.scope.includes('[TODO]'))
|
|
247
|
+
warn(`boundary ${label}: scope is placeholder`);
|
|
227
248
|
else ok(`boundary ${label}: scope OK`);
|
|
228
|
-
if (!bd.out_of_scope || bd.out_of_scope.includes('[TODO]'))
|
|
249
|
+
if (!bd.out_of_scope || bd.out_of_scope.includes('[TODO]'))
|
|
250
|
+
warn(`boundary ${label}: out_of_scope is placeholder`);
|
|
229
251
|
else ok(`boundary ${label}: out_of_scope OK`);
|
|
230
252
|
if (!bd.acceptable_exceptions || !Array.isArray(bd.acceptable_exceptions)) {
|
|
231
253
|
warn(`boundary ${label}: acceptable_exceptions not declared`);
|
|
232
254
|
} else {
|
|
233
|
-
ok(
|
|
255
|
+
ok(
|
|
256
|
+
`boundary ${label}: acceptable_exceptions has ${bd.acceptable_exceptions.length} entries`,
|
|
257
|
+
);
|
|
234
258
|
}
|
|
235
259
|
}
|
|
236
260
|
break;
|
|
@@ -251,11 +275,14 @@ function cmdCardsValidate(projectPath, args = []) {
|
|
|
251
275
|
case 'ontology':
|
|
252
276
|
for (const ont of cards) {
|
|
253
277
|
const label = ont.id || '?';
|
|
254
|
-
if (!ont.essence || ont.essence.includes('[TODO]'))
|
|
278
|
+
if (!ont.essence || ont.essence.includes('[TODO]'))
|
|
279
|
+
warn(`ontology ${label}: essence is placeholder`);
|
|
255
280
|
else ok(`ontology ${label}: essence OK`);
|
|
256
|
-
if (!ont.boundary || ont.boundary.includes('[TODO]'))
|
|
281
|
+
if (!ont.boundary || ont.boundary.includes('[TODO]'))
|
|
282
|
+
warn(`ontology ${label}: boundary is placeholder`);
|
|
257
283
|
else ok(`ontology ${label}: boundary OK`);
|
|
258
|
-
if (!ont.trigger_signal || ont.trigger_signal.includes('[TODO]'))
|
|
284
|
+
if (!ont.trigger_signal || ont.trigger_signal.includes('[TODO]'))
|
|
285
|
+
warn(`ontology ${label}: trigger_signal is placeholder`);
|
|
259
286
|
else ok(`ontology ${label}: trigger_signal OK`);
|
|
260
287
|
}
|
|
261
288
|
break;
|
|
@@ -263,14 +290,20 @@ function cmdCardsValidate(projectPath, args = []) {
|
|
|
263
290
|
}
|
|
264
291
|
|
|
265
292
|
if (jsonMode) {
|
|
266
|
-
console.log(
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
293
|
+
console.log(
|
|
294
|
+
JSON.stringify(
|
|
295
|
+
{
|
|
296
|
+
project: path.basename(abs),
|
|
297
|
+
valid: errors.length === 0,
|
|
298
|
+
errors,
|
|
299
|
+
warnings,
|
|
300
|
+
passed: passed.length,
|
|
301
|
+
total_checks: errors.length + warnings.length + passed.length,
|
|
302
|
+
},
|
|
303
|
+
null,
|
|
304
|
+
2,
|
|
305
|
+
),
|
|
306
|
+
);
|
|
274
307
|
process.exit(errors.length ? EXIT.VALIDATION_FAILED : EXIT.OK);
|
|
275
308
|
}
|
|
276
309
|
|
|
@@ -298,7 +331,8 @@ function cmdLockVerify(projectPath, args = []) {
|
|
|
298
331
|
|
|
299
332
|
if (!fs.existsSync(abs)) error(`Project file not found: ${abs}`, EXIT.INPUT_ERROR);
|
|
300
333
|
const project = readJson(abs);
|
|
301
|
-
if (!project || !project.kdna_studio)
|
|
334
|
+
if (!project || !project.kdna_studio)
|
|
335
|
+
error(`Not a KDNA Studio project: ${abs}`, EXIT.INPUT_ERROR);
|
|
302
336
|
|
|
303
337
|
const locked = [];
|
|
304
338
|
const unlocked = [];
|
|
@@ -324,7 +358,10 @@ function cmdLockVerify(projectPath, args = []) {
|
|
|
324
358
|
locked.push(label);
|
|
325
359
|
} else {
|
|
326
360
|
// Check Feynman restatement for axioms and misunderstandings
|
|
327
|
-
if (
|
|
361
|
+
if (
|
|
362
|
+
(cardType === 'axioms' || cardType === 'misunderstandings') &&
|
|
363
|
+
!card.feynman_restatement
|
|
364
|
+
) {
|
|
328
365
|
unlocked.push(label);
|
|
329
366
|
blocking.push(`${label} missing Feynman restatement`);
|
|
330
367
|
} else {
|
|
@@ -341,15 +378,21 @@ function cmdLockVerify(projectPath, args = []) {
|
|
|
341
378
|
const publishable = blocking.length === 0 && locked.length > 0;
|
|
342
379
|
|
|
343
380
|
if (jsonMode) {
|
|
344
|
-
console.log(
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
381
|
+
console.log(
|
|
382
|
+
JSON.stringify(
|
|
383
|
+
{
|
|
384
|
+
project: path.basename(abs),
|
|
385
|
+
locked_cards: locked.length,
|
|
386
|
+
unlocked_cards: unlocked.length,
|
|
387
|
+
publishable,
|
|
388
|
+
blocking,
|
|
389
|
+
locked: locked.sort(),
|
|
390
|
+
unlocked: unlocked.sort(),
|
|
391
|
+
},
|
|
392
|
+
null,
|
|
393
|
+
2,
|
|
394
|
+
),
|
|
395
|
+
);
|
|
353
396
|
process.exit(publishable ? EXIT.OK : EXIT.HUMAN_LOCK_REQUIRED);
|
|
354
397
|
}
|
|
355
398
|
|
|
@@ -381,13 +424,15 @@ function cmdStudioCompile(projectPath, args = []) {
|
|
|
381
424
|
|
|
382
425
|
if (!fs.existsSync(abs)) error(`Project file not found: ${abs}`, EXIT.INPUT_ERROR);
|
|
383
426
|
const project = readJson(abs);
|
|
384
|
-
if (!project || !project.kdna_studio)
|
|
427
|
+
if (!project || !project.kdna_studio)
|
|
428
|
+
error(`Not a KDNA Studio project: ${abs}`, EXIT.INPUT_ERROR);
|
|
385
429
|
|
|
386
430
|
// Determine output directory
|
|
387
431
|
const outIdx = args.indexOf('--out');
|
|
388
|
-
const outDir =
|
|
389
|
-
|
|
390
|
-
|
|
432
|
+
const outDir =
|
|
433
|
+
outIdx >= 0
|
|
434
|
+
? path.resolve(args[outIdx + 1])
|
|
435
|
+
: path.join(path.dirname(abs), project.exports?.dir || 'exports');
|
|
391
436
|
|
|
392
437
|
fs.mkdirSync(outDir, { recursive: true });
|
|
393
438
|
|
|
@@ -459,12 +504,17 @@ function cmdStudioCompile(projectPath, args = []) {
|
|
|
459
504
|
|
|
460
505
|
// Compile manifest → kdna.json
|
|
461
506
|
const manifest = {
|
|
462
|
-
|
|
507
|
+
format: 'kdna',
|
|
508
|
+
format_version: '1.0',
|
|
509
|
+
spec_version: '1.0-rc',
|
|
463
510
|
name: `@aikdna/${project.name}`,
|
|
464
511
|
version: '0.1.0',
|
|
512
|
+
judgment_version: '0.1.0',
|
|
465
513
|
status: 'experimental',
|
|
514
|
+
quality_badge: 'untested',
|
|
466
515
|
access: 'open',
|
|
467
|
-
|
|
516
|
+
languages: ['en'],
|
|
517
|
+
default_language: 'en',
|
|
468
518
|
author: { name: '', id: '' },
|
|
469
519
|
license: { type: 'CC-BY-4.0' },
|
|
470
520
|
description: '',
|
|
@@ -519,7 +569,8 @@ function cmdStudioReadiness(projectPath, args = []) {
|
|
|
519
569
|
|
|
520
570
|
if (!fs.existsSync(abs)) error(`Project file not found: ${abs}`, EXIT.INPUT_ERROR);
|
|
521
571
|
const project = readJson(abs);
|
|
522
|
-
if (!project || !project.kdna_studio)
|
|
572
|
+
if (!project || !project.kdna_studio)
|
|
573
|
+
error(`Not a KDNA Studio project: ${abs}`, EXIT.INPUT_ERROR);
|
|
523
574
|
|
|
524
575
|
const readiness = {
|
|
525
576
|
axioms: loadCardStats(project, path.dirname(abs), 'axioms'),
|
|
@@ -533,7 +584,9 @@ function cmdStudioReadiness(projectPath, args = []) {
|
|
|
533
584
|
};
|
|
534
585
|
|
|
535
586
|
// Determine publishability
|
|
536
|
-
const allTypes = Object.values(readiness).filter(
|
|
587
|
+
const allTypes = Object.values(readiness).filter(
|
|
588
|
+
(v) => v && typeof v === 'object' && 'total' in v,
|
|
589
|
+
);
|
|
537
590
|
let allLocked = allTypes.every((t) => t.total > 0 && t.total === t.locked);
|
|
538
591
|
if (allTypes.length === 0) allLocked = false;
|
|
539
592
|
readiness.publishable = allLocked;
|
package/src/cmds/test.js
CHANGED
|
@@ -115,8 +115,11 @@ function cmdTestRun(args = []) {
|
|
|
115
115
|
if (!jsonMode) {
|
|
116
116
|
console.log(`Test run recorded: ${result.test_id}`);
|
|
117
117
|
console.log(` Domain: ${result.domain}`);
|
|
118
|
-
console.log(
|
|
119
|
-
|
|
118
|
+
console.log(
|
|
119
|
+
` Input: ${result.input.slice(0, 100)}${result.input.length > 100 ? '...' : ''}`,
|
|
120
|
+
);
|
|
121
|
+
if (result.expected.classification)
|
|
122
|
+
console.log(` Expected classification: ${result.expected.classification}`);
|
|
120
123
|
}
|
|
121
124
|
}
|
|
122
125
|
|
package/src/compare.js
CHANGED
|
@@ -493,7 +493,11 @@ async function cmdCompare(input, args = []) {
|
|
|
493
493
|
const userInput = args[idxInput + 1];
|
|
494
494
|
|
|
495
495
|
const asset = resolveAsset(input);
|
|
496
|
-
if (!asset)
|
|
496
|
+
if (!asset)
|
|
497
|
+
error(
|
|
498
|
+
`KDNA asset not found: ${input}. Use an installed name or a .kdna file.`,
|
|
499
|
+
EXIT.INPUT_ERROR,
|
|
500
|
+
);
|
|
497
501
|
const parsed = asset.parsed || parseName(asset.name || '');
|
|
498
502
|
const label = parsed?.full || asset.name || input;
|
|
499
503
|
|
|
@@ -552,7 +556,16 @@ async function cmdCompare(input, args = []) {
|
|
|
552
556
|
});
|
|
553
557
|
|
|
554
558
|
if (reportMd) {
|
|
555
|
-
const report = emitMarkdownReport(
|
|
559
|
+
const report = emitMarkdownReport(
|
|
560
|
+
parsed || { full: label },
|
|
561
|
+
manifest,
|
|
562
|
+
core,
|
|
563
|
+
pat,
|
|
564
|
+
responseA,
|
|
565
|
+
responseB,
|
|
566
|
+
diff,
|
|
567
|
+
llm,
|
|
568
|
+
);
|
|
556
569
|
if (outputFile) {
|
|
557
570
|
fs.writeFileSync(outputFile, report);
|
|
558
571
|
console.log(`Report saved to ${outputFile}`);
|
package/src/diff.js
CHANGED
|
@@ -80,7 +80,10 @@ function downloadAndExtract(url, destDir) {
|
|
|
80
80
|
stdio: 'pipe',
|
|
81
81
|
});
|
|
82
82
|
} catch (e) {
|
|
83
|
-
error(
|
|
83
|
+
error(
|
|
84
|
+
`Failed to download ${url}: ${e.stderr?.toString().trim() || e.message}`,
|
|
85
|
+
EXIT.PROVIDER_ERROR,
|
|
86
|
+
);
|
|
84
87
|
}
|
|
85
88
|
|
|
86
89
|
fs.mkdirSync(destDir, { recursive: true });
|
|
@@ -243,7 +246,9 @@ async function cmdDiff(a, b, args = []) {
|
|
|
243
246
|
newEntry = entryA;
|
|
244
247
|
if (oldVersion === newVersion) {
|
|
245
248
|
if (jsonMode) {
|
|
246
|
-
console.log(
|
|
249
|
+
console.log(
|
|
250
|
+
JSON.stringify({ error: `${aParsed.full}@${oldVersion}: only one version found.` }),
|
|
251
|
+
);
|
|
247
252
|
process.exit(EXIT.OK);
|
|
248
253
|
}
|
|
249
254
|
console.log(
|
|
@@ -283,7 +288,13 @@ async function cmdDiff(a, b, args = []) {
|
|
|
283
288
|
);
|
|
284
289
|
}
|
|
285
290
|
|
|
286
|
-
const axiomsDiff = diffMaps(
|
|
291
|
+
const axiomsDiff = diffMaps(
|
|
292
|
+
'axioms',
|
|
293
|
+
oldJ.axioms,
|
|
294
|
+
newJ.axioms,
|
|
295
|
+
(a) => a.one_sentence || a.id,
|
|
296
|
+
jsonMode,
|
|
297
|
+
);
|
|
287
298
|
diffMaps('ontology', oldJ.ontology, newJ.ontology, (o) => o.one_sentence || o.id, jsonMode);
|
|
288
299
|
const misunderstandingsDiff = diffMaps(
|
|
289
300
|
'misunderstandings',
|
|
@@ -292,7 +303,13 @@ async function cmdDiff(a, b, args = []) {
|
|
|
292
303
|
(m) => m.wrong || m.id,
|
|
293
304
|
jsonMode,
|
|
294
305
|
);
|
|
295
|
-
const bannedDiff = diffMaps(
|
|
306
|
+
const bannedDiff = diffMaps(
|
|
307
|
+
'banned_terms',
|
|
308
|
+
oldJ.banned_terms,
|
|
309
|
+
newJ.banned_terms,
|
|
310
|
+
(t) => t.term || '',
|
|
311
|
+
jsonMode,
|
|
312
|
+
);
|
|
296
313
|
const stancesDiff = diffStanceList(oldJ.stances, newJ.stances, jsonMode);
|
|
297
314
|
|
|
298
315
|
// Cleanup
|
|
@@ -332,11 +349,7 @@ async function cmdDiff(a, b, args = []) {
|
|
|
332
349
|
}));
|
|
333
350
|
|
|
334
351
|
const affectedScenarios = axiomsDiff.changedDetails
|
|
335
|
-
.filter(
|
|
336
|
-
(d) =>
|
|
337
|
-
d.boundary_changes.applies_when ||
|
|
338
|
-
d.boundary_changes.does_not_apply_when,
|
|
339
|
-
)
|
|
352
|
+
.filter((d) => d.boundary_changes.applies_when || d.boundary_changes.does_not_apply_when)
|
|
340
353
|
.map((d) => ({
|
|
341
354
|
axiom_id: d.id,
|
|
342
355
|
applies_when: d.boundary_changes.applies_when || null,
|
|
@@ -350,7 +363,8 @@ async function cmdDiff(a, b, args = []) {
|
|
|
350
363
|
let recommendedVersionBump = 'none';
|
|
351
364
|
if (hasRemoved) recommendedVersionBump = 'major';
|
|
352
365
|
else if (hasAdded || hasChanged) recommendedVersionBump = 'minor';
|
|
353
|
-
else if (stancesDiff.added.length > 0 || stancesDiff.removed.length > 0)
|
|
366
|
+
else if (stancesDiff.added.length > 0 || stancesDiff.removed.length > 0)
|
|
367
|
+
recommendedVersionBump = 'patch';
|
|
354
368
|
|
|
355
369
|
if (jsonMode) {
|
|
356
370
|
const result = {
|
|
@@ -376,7 +390,9 @@ async function cmdDiff(a, b, args = []) {
|
|
|
376
390
|
const drift = Object.keys(newJ.axioms).length - Object.keys(oldJ.axioms).length;
|
|
377
391
|
const note = drift !== 0 ? ` (axiom count drift: ${drift > 0 ? '+' : ''}${drift})` : '';
|
|
378
392
|
console.log(` Judgment surface change: ${oldVersion} → ${newVersion}${note}`);
|
|
379
|
-
console.log(
|
|
393
|
+
console.log(
|
|
394
|
+
` Agent loading the new version may classify, diagnose, or recommend differently.`,
|
|
395
|
+
);
|
|
380
396
|
console.log('═'.repeat(64));
|
|
381
397
|
}
|
|
382
398
|
}
|
package/src/identity.js
CHANGED
|
@@ -90,13 +90,15 @@ function cmdIdentityShow(jsonMode = false) {
|
|
|
90
90
|
const fp = fingerprint(pub);
|
|
91
91
|
|
|
92
92
|
if (jsonMode) {
|
|
93
|
-
console.log(
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
93
|
+
console.log(
|
|
94
|
+
JSON.stringify({
|
|
95
|
+
pubkey: pub.trim(),
|
|
96
|
+
buyer_id: id,
|
|
97
|
+
fingerprint: fp,
|
|
98
|
+
public_key_path: PUBLIC_KEY_PATH,
|
|
99
|
+
private_key_exists: fs.existsSync(PRIVATE_KEY_PATH),
|
|
100
|
+
}),
|
|
101
|
+
);
|
|
100
102
|
process.exit(EXIT.OK);
|
|
101
103
|
}
|
|
102
104
|
|
package/src/install.js
CHANGED
|
@@ -159,7 +159,10 @@ function parseSource(input) {
|
|
|
159
159
|
// Local file (.kdna)
|
|
160
160
|
if (
|
|
161
161
|
input.endsWith('.kdna') &&
|
|
162
|
-
(input.startsWith('./') ||
|
|
162
|
+
(input.startsWith('./') ||
|
|
163
|
+
input.startsWith('/') ||
|
|
164
|
+
input.startsWith('~/') ||
|
|
165
|
+
fs.existsSync(input))
|
|
163
166
|
) {
|
|
164
167
|
const resolved = path.resolve(input.replace(/^~/, process.env.HOME || ''));
|
|
165
168
|
if (!fs.existsSync(resolved)) error(`Local file not found: ${resolved}`);
|
|
@@ -183,7 +186,7 @@ function parseSource(input) {
|
|
|
183
186
|
const parsed = parseName(input);
|
|
184
187
|
if (!parsed) {
|
|
185
188
|
error(
|
|
186
|
-
|
|
189
|
+
`Cannot parse "${input}". Use:\n` +
|
|
187
190
|
` kdna install <name> # @aikdna/<name>\n` +
|
|
188
191
|
` kdna install @scope/name # any scope\n` +
|
|
189
192
|
` kdna install ./file.kdna # local .kdna file`,
|
|
@@ -247,7 +250,10 @@ function verifySignature({ assetPath, scope, entry, lenient = true }) {
|
|
|
247
250
|
|
|
248
251
|
// Full Ed25519 verify (requires public_key_pem embedded in the package)
|
|
249
252
|
if (!manifest.author?.public_key_pem) {
|
|
250
|
-
error(
|
|
253
|
+
error(
|
|
254
|
+
`${entry.name}: manifest author.public_key_pem is required for Ed25519 verification.`,
|
|
255
|
+
EXIT.TRUST_FAILED,
|
|
256
|
+
);
|
|
251
257
|
}
|
|
252
258
|
|
|
253
259
|
const result = verifyAsset(assetPath, { requireSignature: true });
|
|
@@ -397,7 +403,9 @@ function installSingleFromUrl({ entry, scope }, jsonMode = false) {
|
|
|
397
403
|
} catch {
|
|
398
404
|
/* ignore */
|
|
399
405
|
}
|
|
400
|
-
error(
|
|
406
|
+
error(
|
|
407
|
+
`asset digest mismatch for ${entry.name}: expected ${expectedDigest}, got ${actualDigest}`,
|
|
408
|
+
);
|
|
401
409
|
}
|
|
402
410
|
if (!jsonMode) console.log(` ✓ asset digest verified`);
|
|
403
411
|
|
|
@@ -634,13 +642,15 @@ function cmdInfo(input, jsonMode = false) {
|
|
|
634
642
|
if (manifest?.author?.public_key_pem) {
|
|
635
643
|
console.log(` Embedded PEM: yes (full Ed25519 verify available)`);
|
|
636
644
|
} else {
|
|
637
|
-
console.log(` Embedded PEM: no
|
|
645
|
+
console.log(` Embedded PEM: no`);
|
|
638
646
|
}
|
|
639
647
|
if (source.asset_url) {
|
|
640
648
|
console.log(` Source URL: ${source.asset_url}`);
|
|
641
649
|
}
|
|
642
|
-
if (installed.asset_digest)
|
|
643
|
-
|
|
650
|
+
if (installed.asset_digest)
|
|
651
|
+
console.log(` Asset digest: ${installed.asset_digest.slice(0, 39)}…`);
|
|
652
|
+
if (installed.content_digest)
|
|
653
|
+
console.log(` Content digest: ${installed.content_digest.slice(0, 39)}…`);
|
|
644
654
|
if (installed.receipt_path) console.log(` Receipt: ${installed.receipt_path}`);
|
|
645
655
|
console.log(` Installed: ${installed.installed_at || '?'}`);
|
|
646
656
|
console.log(` Asset: ${installed.asset_path}`);
|
package/src/package-store.js
CHANGED
|
@@ -171,7 +171,10 @@ function resolveAsset(input) {
|
|
|
171
171
|
const expanded = input.replace(/^~/, process.env.HOME || '');
|
|
172
172
|
const looksLikeFile =
|
|
173
173
|
input.endsWith('.kdna') &&
|
|
174
|
-
(input.startsWith('./') ||
|
|
174
|
+
(input.startsWith('./') ||
|
|
175
|
+
input.startsWith('/') ||
|
|
176
|
+
input.startsWith('~/') ||
|
|
177
|
+
fs.existsSync(expanded));
|
|
175
178
|
if (looksLikeFile) {
|
|
176
179
|
const abs = path.resolve(expanded);
|
|
177
180
|
if (!fs.existsSync(abs) || !fs.statSync(abs).isFile()) return null;
|
package/src/paths.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
|
|
6
|
-
const KDNA_HOME =
|
|
7
|
-
|| path.join(process.env.HOME || process.env.USERPROFILE || '.', '.kdna');
|
|
6
|
+
const KDNA_HOME =
|
|
7
|
+
process.env.KDNA_HOME || path.join(process.env.HOME || process.env.USERPROFILE || '.', '.kdna');
|
|
8
8
|
|
|
9
9
|
const PATHS = {
|
|
10
10
|
root: KDNA_HOME,
|