@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.
@@ -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) error('Usage: kdna cluster compose <cluster.json> --input "<task>" [--profile=compact] [--json]');
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 ~/.kdna/domains/,
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 INSTALL_DIR = path.join(
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 INSTALL_DIR = path.join(
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(JSON.stringify({
277
- cluster: manifest.cluster_id,
278
- input: input.slice(0, 200),
279
- selected: classification.selected.map((d) => ({
280
- id: d.id,
281
- role: d.role,
282
- reason: d.reason,
283
- })),
284
- excluded: classification.excluded.map((d) => ({
285
- id: d.id,
286
- reason: d.reason,
287
- })),
288
- context,
289
- }, null, 2));
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(`Selected: ${classification.selected.length} | Excluded: ${classification.excluded.length}`);
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(JSON.stringify({
325
- cluster: manifest.cluster_id,
326
- input: input.slice(0, 200),
327
- selected: classification.selected.map((d) => ({ id: d.id, role: d.role })),
328
- conflicts: conflicts.map((c) => ({
329
- type: c.type,
330
- domains: c.domains,
331
- description: c.description,
332
- severity: c.severity || 'warn',
333
- })),
334
- conflict_count: conflicts.length,
335
- safe: conflicts.length === 0,
336
- }, null, 2));
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(`Selected: ${classification.selected.length} domains | Conflicts: ${conflicts.length}`);
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(` "${d.id || d.role}" [label="${d.id || d.role}\\n[${d.role}]",shape=${shape}${required}];`);
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 = r.type === 'conflicts' ? ',style=dashed,color=red' :
414
- r.type === 'extends' ? ',style=bold' : '';
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
- const INSTALL_DIR = path.join(
427
- process.env.HOME || process.env.USERPROFILE || '.',
428
- '.kdna',
429
- 'domains',
430
- );
431
-
432
- return (manifest.domains || []).map((d) => {
433
- const domainId = d.id;
434
- if (!domainId) return null;
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 };
@@ -1,8 +1,9 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
- const { EXIT, error } = require('./_common');
3
+ const { EXIT } = require('./_common');
4
4
  const USER_KDNA_DIR = path.join(process.env.HOME || process.env.USERPROFILE || '.', '.kdna');
5
- const INSTALL_DIR = path.join(USER_KDNA_DIR, 'domains');
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/domains/ exists and has domains
78
- if (fs.existsSync(INSTALL_DIR)) {
79
- const domains = fs
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 domains',
77
+ name: 'Installed assets',
94
78
  status: domains > 0 ? 'ok' : 'warn',
95
- detail: `${domains} domain${domains !== 1 ? 's' : ''} installed`,
79
+ detail: `${domains} .kdna asset${domains !== 1 ? 's' : ''} installed`,
96
80
  });
97
81
  } else {
98
82
  checks.push({
99
- name: 'Domains directory',
83
+ name: 'Package asset store',
100
84
  status: 'warn',
101
- detail: '~/.kdna/domains/ not found',
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