@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.
- package/README.md +120 -101
- package/SECURITY.md +1 -1
- package/package.json +6 -4
- package/skills/kdna-loader/SKILL.md +23 -22
- package/src/agent.js +290 -159
- package/src/cli.js +117 -67
- package/src/cmds/_common.js +40 -18
- package/src/cmds/badge.js +14 -9
- package/src/cmds/changelog.js +32 -12
- package/src/cmds/cluster.js +80 -85
- package/src/cmds/doctor.js +10 -27
- package/src/cmds/domain.js +114 -427
- package/src/cmds/explain.js +119 -0
- package/src/cmds/governance.js +111 -42
- package/src/cmds/legacy.js +8 -9
- package/src/cmds/license.js +491 -26
- package/src/cmds/quality.js +10 -3
- package/src/cmds/registry.js +15 -67
- package/src/cmds/studio.js +99 -47
- package/src/cmds/test.js +9 -6
- package/src/cmds/trace.js +11 -7
- package/src/compare.js +41 -22
- package/src/diff.js +38 -24
- package/src/identity.js +9 -7
- package/src/init.js +2 -2
- package/src/install.js +147 -459
- package/src/loader.js +10 -10
- package/src/package-store.js +232 -0
- package/src/paths.js +44 -0
- package/src/publish.js +150 -51
- package/src/registry.js +81 -9
- package/src/setup.js +19 -20
- package/src/verify.js +293 -140
- 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 +7 -3
- package/validators/kdna-lint.js +45 -11
- package/src/cmds/encrypt.js +0 -199
package/src/cmds/cluster.js
CHANGED
|
@@ -8,6 +8,16 @@ const {
|
|
|
8
8
|
detectDomainConflicts,
|
|
9
9
|
generateClusterTrace,
|
|
10
10
|
} = require('@aikdna/kdna-core');
|
|
11
|
+
const { getInstalled, readContainer } = require('../package-store');
|
|
12
|
+
|
|
13
|
+
function loadInstalledDomain(domainId) {
|
|
14
|
+
const full = domainId.startsWith('@') ? domainId : `@aikdna/${domainId}`;
|
|
15
|
+
const installed = getInstalled(full);
|
|
16
|
+
if (!installed) return null;
|
|
17
|
+
const { core, patterns } = readContainer(installed.asset_path);
|
|
18
|
+
if (!core || !patterns) return null;
|
|
19
|
+
return { core, patterns };
|
|
20
|
+
}
|
|
11
21
|
|
|
12
22
|
function cmdCluster(args) {
|
|
13
23
|
const { cmdClusterLint } = require('../cluster');
|
|
@@ -30,7 +40,10 @@ function cmdCluster(args) {
|
|
|
30
40
|
if (!target) error('Usage: kdna cluster match <cluster.json> --input "<task>"');
|
|
31
41
|
cmdClusterMatch(target, args);
|
|
32
42
|
} else if (sub === 'compose') {
|
|
33
|
-
if (!target)
|
|
43
|
+
if (!target)
|
|
44
|
+
error(
|
|
45
|
+
'Usage: kdna cluster compose <cluster.json> --input "<task>" [--profile=compact] [--json]',
|
|
46
|
+
);
|
|
34
47
|
cmdClusterCompose(target, args);
|
|
35
48
|
} else if (sub === 'conflicts') {
|
|
36
49
|
if (!target) error('Usage: kdna cluster conflicts <cluster.json> --input "<task>" [--json]');
|
|
@@ -101,7 +114,7 @@ function cmdClusterInfo(target, _format = 'human') {
|
|
|
101
114
|
}
|
|
102
115
|
|
|
103
116
|
/**
|
|
104
|
-
* Load a cluster: resolve domains from installed
|
|
117
|
+
* Load a cluster: resolve domains from installed .kdna assets,
|
|
105
118
|
* classify input signals, compose context with attribution, detect
|
|
106
119
|
* conflicts, and emit the composed context.
|
|
107
120
|
*/
|
|
@@ -116,23 +129,7 @@ function cmdClusterLoad(target, args = []) {
|
|
|
116
129
|
const manifest = readJson(abs);
|
|
117
130
|
if (!manifest || !manifest.cluster_id) error('Not a valid cluster manifest');
|
|
118
131
|
|
|
119
|
-
const
|
|
120
|
-
process.env.HOME || process.env.USERPROFILE || '.',
|
|
121
|
-
'.kdna',
|
|
122
|
-
'domains',
|
|
123
|
-
);
|
|
124
|
-
|
|
125
|
-
// Domain loader: resolve from installed ~/.kdna/domains/
|
|
126
|
-
const domainLoader = (domainId) => {
|
|
127
|
-
const [scope, ident] = domainId.startsWith('@')
|
|
128
|
-
? [domainId.slice(0, domainId.indexOf('/')), domainId.slice(domainId.indexOf('/') + 1)]
|
|
129
|
-
: ['@aikdna', domainId];
|
|
130
|
-
const dir = path.join(INSTALL_DIR, scope, ident);
|
|
131
|
-
const core = readJson(path.join(dir, 'KDNA_Core.json'));
|
|
132
|
-
const pat = readJson(path.join(dir, 'KDNA_Patterns.json'));
|
|
133
|
-
if (!core || !pat) return null;
|
|
134
|
-
return { core, patterns: pat };
|
|
135
|
-
};
|
|
132
|
+
const domainLoader = loadInstalledDomain;
|
|
136
133
|
|
|
137
134
|
const result = loadCluster(abs, domainLoader);
|
|
138
135
|
if (result.errors.length) {
|
|
@@ -208,22 +205,7 @@ function cmdClusterMatch(target, args = []) {
|
|
|
208
205
|
const manifest = readJson(abs);
|
|
209
206
|
if (!manifest || !manifest.cluster_id) error('Not a valid cluster manifest');
|
|
210
207
|
|
|
211
|
-
const
|
|
212
|
-
process.env.HOME || process.env.USERPROFILE || '.',
|
|
213
|
-
'.kdna',
|
|
214
|
-
'domains',
|
|
215
|
-
);
|
|
216
|
-
|
|
217
|
-
const domainLoader = (domainId) => {
|
|
218
|
-
const [scope, ident] = domainId.startsWith('@')
|
|
219
|
-
? [domainId.slice(0, domainId.indexOf('/')), domainId.slice(domainId.indexOf('/') + 1)]
|
|
220
|
-
: ['@aikdna', domainId];
|
|
221
|
-
const dir = path.join(INSTALL_DIR, scope, ident);
|
|
222
|
-
const core = readJson(path.join(dir, 'KDNA_Core.json'));
|
|
223
|
-
const pat = readJson(path.join(dir, 'KDNA_Patterns.json'));
|
|
224
|
-
if (!core || !pat) return null;
|
|
225
|
-
return { core, patterns: pat };
|
|
226
|
-
};
|
|
208
|
+
const domainLoader = loadInstalledDomain;
|
|
227
209
|
|
|
228
210
|
const result = loadCluster(abs, domainLoader);
|
|
229
211
|
const classification = classifySignalsAcrossDomains(input, result.domains);
|
|
@@ -273,20 +255,26 @@ function cmdClusterCompose(target, args = []) {
|
|
|
273
255
|
const { context } = composeContextWithAttribution(classification.selected);
|
|
274
256
|
|
|
275
257
|
if (jsonMode) {
|
|
276
|
-
console.log(
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
258
|
+
console.log(
|
|
259
|
+
JSON.stringify(
|
|
260
|
+
{
|
|
261
|
+
cluster: manifest.cluster_id,
|
|
262
|
+
input: input.slice(0, 200),
|
|
263
|
+
selected: classification.selected.map((d) => ({
|
|
264
|
+
id: d.id,
|
|
265
|
+
role: d.role,
|
|
266
|
+
reason: d.reason,
|
|
267
|
+
})),
|
|
268
|
+
excluded: classification.excluded.map((d) => ({
|
|
269
|
+
id: d.id,
|
|
270
|
+
reason: d.reason,
|
|
271
|
+
})),
|
|
272
|
+
context,
|
|
273
|
+
},
|
|
274
|
+
null,
|
|
275
|
+
2,
|
|
276
|
+
),
|
|
277
|
+
);
|
|
290
278
|
return;
|
|
291
279
|
}
|
|
292
280
|
|
|
@@ -294,7 +282,9 @@ function cmdClusterCompose(target, args = []) {
|
|
|
294
282
|
console.log(`Profile: ${profile}`);
|
|
295
283
|
console.log(`Input: ${input.slice(0, 100)}${input.length > 100 ? '...' : ''}`);
|
|
296
284
|
console.log('');
|
|
297
|
-
console.log(
|
|
285
|
+
console.log(
|
|
286
|
+
`Selected: ${classification.selected.length} | Excluded: ${classification.excluded.length}`,
|
|
287
|
+
);
|
|
298
288
|
console.log('');
|
|
299
289
|
console.log('─'.repeat(64));
|
|
300
290
|
console.log(context);
|
|
@@ -321,25 +311,33 @@ function cmdClusterConflicts(target, args = []) {
|
|
|
321
311
|
const conflicts = detectDomainConflicts(classification.selected);
|
|
322
312
|
|
|
323
313
|
if (jsonMode) {
|
|
324
|
-
console.log(
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
314
|
+
console.log(
|
|
315
|
+
JSON.stringify(
|
|
316
|
+
{
|
|
317
|
+
cluster: manifest.cluster_id,
|
|
318
|
+
input: input.slice(0, 200),
|
|
319
|
+
selected: classification.selected.map((d) => ({ id: d.id, role: d.role })),
|
|
320
|
+
conflicts: conflicts.map((c) => ({
|
|
321
|
+
type: c.type,
|
|
322
|
+
domains: c.domains,
|
|
323
|
+
description: c.description,
|
|
324
|
+
severity: c.severity || 'warn',
|
|
325
|
+
})),
|
|
326
|
+
conflict_count: conflicts.length,
|
|
327
|
+
safe: conflicts.length === 0,
|
|
328
|
+
},
|
|
329
|
+
null,
|
|
330
|
+
2,
|
|
331
|
+
),
|
|
332
|
+
);
|
|
337
333
|
process.exit(conflicts.length ? EXIT.HUMAN_LOCK_REQUIRED : EXIT.OK);
|
|
338
334
|
}
|
|
339
335
|
|
|
340
336
|
console.log(`Cluster: ${manifest.cluster_id}`);
|
|
341
337
|
console.log(`Input: ${input.slice(0, 100)}${input.length > 100 ? '...' : ''}`);
|
|
342
|
-
console.log(
|
|
338
|
+
console.log(
|
|
339
|
+
`Selected: ${classification.selected.length} domains | Conflicts: ${conflicts.length}`,
|
|
340
|
+
);
|
|
343
341
|
console.log('');
|
|
344
342
|
|
|
345
343
|
if (!conflicts.length) {
|
|
@@ -403,15 +401,21 @@ function cmdClusterGraph(target, args = []) {
|
|
|
403
401
|
for (const d of manifest.domains || []) {
|
|
404
402
|
const shape = d.role === 'primary' ? 'box' : d.role === 'critic' ? 'diamond' : 'ellipse';
|
|
405
403
|
const required = d.required !== false ? ',style=filled,fillcolor="#e8f0fe"' : ',style=dashed';
|
|
406
|
-
console.log(
|
|
404
|
+
console.log(
|
|
405
|
+
` "${d.id || d.role}" [label="${d.id || d.role}\\n[${d.role}]",shape=${shape}${required}];`,
|
|
406
|
+
);
|
|
407
407
|
}
|
|
408
408
|
|
|
409
409
|
// Edges
|
|
410
410
|
if (manifest.relationships) {
|
|
411
411
|
console.log('');
|
|
412
412
|
for (const r of manifest.relationships) {
|
|
413
|
-
const style =
|
|
414
|
-
|
|
413
|
+
const style =
|
|
414
|
+
r.type === 'conflicts'
|
|
415
|
+
? ',style=dashed,color=red'
|
|
416
|
+
: r.type === 'extends'
|
|
417
|
+
? ',style=bold'
|
|
418
|
+
: '';
|
|
415
419
|
console.log(` "${r.from}" -> "${r.to}" [label="${r.type}"${style}];`);
|
|
416
420
|
}
|
|
417
421
|
}
|
|
@@ -423,24 +427,15 @@ function cmdClusterGraph(target, args = []) {
|
|
|
423
427
|
* Shared domain loader for cluster commands.
|
|
424
428
|
*/
|
|
425
429
|
function loadClusterDomains(manifest) {
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
const [scope, ident] = domainId.startsWith('@')
|
|
436
|
-
? [domainId.slice(0, domainId.indexOf('/')), domainId.slice(domainId.indexOf('/') + 1)]
|
|
437
|
-
: ['@aikdna', domainId];
|
|
438
|
-
const dir = path.join(INSTALL_DIR, scope, ident);
|
|
439
|
-
const core = readJson(path.join(dir, 'KDNA_Core.json'));
|
|
440
|
-
const pat = readJson(path.join(dir, 'KDNA_Patterns.json'));
|
|
441
|
-
if (!core || !pat) return null;
|
|
442
|
-
return { id: domainId, role: d.role, required: d.required !== false, core, patterns: pat };
|
|
443
|
-
}).filter(Boolean);
|
|
430
|
+
return (manifest.domains || [])
|
|
431
|
+
.map((d) => {
|
|
432
|
+
const domainId = d.id;
|
|
433
|
+
if (!domainId) return null;
|
|
434
|
+
const loaded = loadInstalledDomain(domainId);
|
|
435
|
+
if (!loaded) return null;
|
|
436
|
+
return { id: domainId, role: d.role, required: d.required !== false, ...loaded };
|
|
437
|
+
})
|
|
438
|
+
.filter(Boolean);
|
|
444
439
|
}
|
|
445
440
|
|
|
446
441
|
module.exports = { cmdCluster };
|
package/src/cmds/doctor.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
-
const { EXIT
|
|
3
|
+
const { EXIT } = require('./_common');
|
|
4
4
|
const USER_KDNA_DIR = path.join(process.env.HOME || process.env.USERPROFILE || '.', '.kdna');
|
|
5
|
-
const
|
|
5
|
+
const PATHS = require('../paths');
|
|
6
|
+
const { listInstalled } = require('../package-store');
|
|
6
7
|
|
|
7
8
|
const AGENTS = [
|
|
8
9
|
{ name: 'OpenCode', dir: path.join(process.env.HOME || '', '.agents'), skillsDir: 'skills' },
|
|
@@ -17,11 +18,6 @@ const AGENTS = [
|
|
|
17
18
|
];
|
|
18
19
|
|
|
19
20
|
const V2_1_MARKER = 'kdna available';
|
|
20
|
-
|
|
21
|
-
function detectAgents() {
|
|
22
|
-
return AGENTS.filter((a) => fs.existsSync(a.dir));
|
|
23
|
-
}
|
|
24
|
-
|
|
25
21
|
function checkAgentSkill(agent) {
|
|
26
22
|
const skillPath = path.join(agent.dir, agent.skillsDir, 'kdna-loader', 'SKILL.md');
|
|
27
23
|
if (!fs.existsSync(skillPath)) return { installed: false, version: null, path: skillPath };
|
|
@@ -74,38 +70,25 @@ function cmdDoctor(args) {
|
|
|
74
70
|
checks.push({ name: 'KDNA data directory', status: 'warn', detail: '~/.kdna/ not found' });
|
|
75
71
|
}
|
|
76
72
|
|
|
77
|
-
// 4. ~/.kdna/
|
|
78
|
-
if (fs.existsSync(
|
|
79
|
-
const domains =
|
|
80
|
-
.readdirSync(INSTALL_DIR, { withFileTypes: true })
|
|
81
|
-
.filter((d) => d.isDirectory())
|
|
82
|
-
.reduce((acc, scopeDir) => {
|
|
83
|
-
if (scopeDir.name.startsWith('@')) {
|
|
84
|
-
try {
|
|
85
|
-
return acc + fs.readdirSync(path.join(INSTALL_DIR, scopeDir.name)).length;
|
|
86
|
-
} catch {
|
|
87
|
-
return acc;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
return acc + 1;
|
|
91
|
-
}, 0);
|
|
73
|
+
// 4. ~/.kdna/packages/ exists and has .kdna assets
|
|
74
|
+
if (fs.existsSync(PATHS.packages)) {
|
|
75
|
+
const domains = listInstalled().length;
|
|
92
76
|
checks.push({
|
|
93
|
-
name: 'Installed
|
|
77
|
+
name: 'Installed assets',
|
|
94
78
|
status: domains > 0 ? 'ok' : 'warn',
|
|
95
|
-
detail: `${domains}
|
|
79
|
+
detail: `${domains} .kdna asset${domains !== 1 ? 's' : ''} installed`,
|
|
96
80
|
});
|
|
97
81
|
} else {
|
|
98
82
|
checks.push({
|
|
99
|
-
name: '
|
|
83
|
+
name: 'Package asset store',
|
|
100
84
|
status: 'warn',
|
|
101
|
-
detail: '~/.kdna/
|
|
85
|
+
detail: '~/.kdna/packages/ not found',
|
|
102
86
|
});
|
|
103
87
|
}
|
|
104
88
|
}
|
|
105
89
|
|
|
106
90
|
if (!domainsOnly) {
|
|
107
91
|
// 5. Agent integration check
|
|
108
|
-
const detected = detectAgents();
|
|
109
92
|
for (const agent of AGENTS) {
|
|
110
93
|
const agentDirExists = fs.existsSync(agent.dir);
|
|
111
94
|
const skill = agentDirExists
|