agentic-kdd 3.0.1 → 3.0.3
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/bin/akdd.js +61 -26
- package/collab-manager.cjs +403 -0
- package/package.json +1 -1
- package/src/init.js +9 -0
- package/src/mcp-setup.js +265 -0
package/bin/akdd.js
CHANGED
|
@@ -6,6 +6,7 @@ const { update } = require('../src/update');
|
|
|
6
6
|
const { graph } = require('../src/graph');
|
|
7
7
|
const { dashboard } = require('../src/dashboard');
|
|
8
8
|
const { analyze } = require('../src/analyze');
|
|
9
|
+
const { mcpSetup, mcpStatus } = require('../src/mcp-setup');
|
|
9
10
|
const pkg = require('../package.json');
|
|
10
11
|
const path = require('path');
|
|
11
12
|
const fs = require('fs');
|
|
@@ -61,6 +62,13 @@ const HELP = `
|
|
|
61
62
|
akdd trail <ciclo_id> Full trail of a specific cycle
|
|
62
63
|
akdd trail why <f> Why does this file/entity exist?
|
|
63
64
|
|
|
65
|
+
Collaborative Mode (Legion):
|
|
66
|
+
akdd collab init Activate collaborative mode — creates shared DB automatically
|
|
67
|
+
akdd collab join <url> <token> Join an existing team's shared memory
|
|
68
|
+
akdd collab push Push your learnings to the team
|
|
69
|
+
akdd collab pull Pull team's latest learnings
|
|
70
|
+
akdd collab status Check collaborative sync status
|
|
71
|
+
|
|
64
72
|
Intelligence v2.2:
|
|
65
73
|
akdd git-context Analyze git diff + risk assessment
|
|
66
74
|
akdd predict Predictive risk patterns from episodic memory
|
|
@@ -73,9 +81,10 @@ const HELP = `
|
|
|
73
81
|
Dashboard:
|
|
74
82
|
akdd dashboard Open visual dashboard in browser
|
|
75
83
|
|
|
76
|
-
MCP
|
|
77
|
-
|
|
78
|
-
|
|
84
|
+
MCP Setup (Cursor / Claude Code / VS Code):
|
|
85
|
+
akdd mcp Auto-configure MCP in all IDEs (recommended)
|
|
86
|
+
akdd mcp --global Configure MCP globally for all projects
|
|
87
|
+
akdd mcp status Check MCP configuration status
|
|
79
88
|
|
|
80
89
|
akdd --version / akdd --help
|
|
81
90
|
`;
|
|
@@ -96,11 +105,7 @@ function runGrafo(cmd, extra) {
|
|
|
96
105
|
function runModule(name, cmd, extra) {
|
|
97
106
|
const p = path.join(process.cwd(), '.agentic', 'grafo', name);
|
|
98
107
|
if (!fs.existsSync(p)) { console.log(`\n ${name} not found. Run: akdd update\n`); process.exit(1); }
|
|
99
|
-
const fullCmd = [
|
|
100
|
-
`node "${p}"`,
|
|
101
|
-
cmd || '',
|
|
102
|
-
extra || ''
|
|
103
|
-
].join(' ').trim().replace(/\s+/g,' ');
|
|
108
|
+
const fullCmd = [`node "${p}"`, cmd || '', extra || ''].join(' ').trim().replace(/\s+/g,' ');
|
|
104
109
|
try { execSync(fullCmd, { stdio: 'inherit', cwd: process.cwd() }); }
|
|
105
110
|
catch(e) { process.exit(e.status || 1); }
|
|
106
111
|
}
|
|
@@ -111,14 +116,14 @@ switch (command) {
|
|
|
111
116
|
case 'update': update(); break;
|
|
112
117
|
case 'analyze': analyze(); break;
|
|
113
118
|
|
|
114
|
-
// ── v3.0: Health
|
|
119
|
+
// ── v3.0: Health ──────────────────────────────────────────────────────
|
|
115
120
|
case 'health': {
|
|
116
121
|
const fixFlag = args.includes('--fix') ? '--fix' : '';
|
|
117
122
|
runModule('health-check.cjs', fixFlag);
|
|
118
123
|
break;
|
|
119
124
|
}
|
|
120
125
|
|
|
121
|
-
// ── Core memory
|
|
126
|
+
// ── Core memory ───────────────────────────────────────────────────────
|
|
122
127
|
case 'sync': runGrafo('sync'); break;
|
|
123
128
|
case 'graph': graph(); break;
|
|
124
129
|
case 'stats': runGrafo('stats'); break;
|
|
@@ -136,7 +141,7 @@ switch (command) {
|
|
|
136
141
|
runGrafo('impacto', `"${arg1}"`);
|
|
137
142
|
break;
|
|
138
143
|
|
|
139
|
-
// ── v3.0: Memory Audit
|
|
144
|
+
// ── v3.0: Memory Audit ────────────────────────────────────────────────
|
|
140
145
|
case 'audit': runModule('memory-audit.cjs', 'report'); break;
|
|
141
146
|
|
|
142
147
|
case 'forget': {
|
|
@@ -147,11 +152,11 @@ switch (command) {
|
|
|
147
152
|
break;
|
|
148
153
|
}
|
|
149
154
|
|
|
150
|
-
// ── v3.0: AST
|
|
155
|
+
// ── v3.0: AST ─────────────────────────────────────────────────────────
|
|
151
156
|
case 'ast': {
|
|
152
157
|
const sub = arg1 || 'index';
|
|
153
158
|
const tgt = arg2 || '';
|
|
154
|
-
if (sub === 'stats')
|
|
159
|
+
if (sub === 'stats') runModule('ast-indexer.cjs', 'stats');
|
|
155
160
|
else if (sub === 'symbols') {
|
|
156
161
|
if (!tgt) { console.log('\n Uso: akdd ast symbols <archivo>\n'); break; }
|
|
157
162
|
runModule('ast-indexer.cjs', 'symbols', `"${tgt}"`);
|
|
@@ -171,17 +176,17 @@ switch (command) {
|
|
|
171
176
|
runModule('decision-trail.cjs', 'why', `"${arg1}"`);
|
|
172
177
|
break;
|
|
173
178
|
|
|
174
|
-
// ── v3.0: Specs
|
|
179
|
+
// ── v3.0: Specs ───────────────────────────────────────────────────────
|
|
175
180
|
case 'spec': {
|
|
176
181
|
const sub = arg1;
|
|
177
182
|
const mod = arg2;
|
|
178
|
-
if (!sub || sub === 'list')
|
|
183
|
+
if (!sub || sub === 'list') runModule('spec-manager.cjs', 'list');
|
|
179
184
|
else if (sub === 'create') {
|
|
180
185
|
if (!mod) { console.log('\n Uso: akdd spec create <módulo> [--bugfix]\n'); break; }
|
|
181
186
|
runModule('spec-manager.cjs', 'create', `"${mod}"${args.includes('--bugfix') ? ' --bugfix' : ''}`);
|
|
182
187
|
}
|
|
183
|
-
else if (sub === 'waves')
|
|
184
|
-
else if (sub === 'validate')
|
|
188
|
+
else if (sub === 'waves') { if (!mod) { console.log('\n Uso: akdd spec waves <módulo>\n'); break; } runModule('spec-manager.cjs', 'waves', `"${mod}"`); }
|
|
189
|
+
else if (sub === 'validate') { if (!mod) { console.log('\n Uso: akdd spec validate <módulo>\n'); break; } runModule('spec-manager.cjs', 'validate', `"${mod}"`); }
|
|
185
190
|
else runModule('spec-manager.cjs', 'status', `"${sub}"`);
|
|
186
191
|
break;
|
|
187
192
|
}
|
|
@@ -191,7 +196,7 @@ switch (command) {
|
|
|
191
196
|
runModule('spec-manager.cjs', 'create', `"${arg1}"${args.includes('--bugfix') ? ' --bugfix' : ''}`);
|
|
192
197
|
break;
|
|
193
198
|
|
|
194
|
-
// ── v3.0: Knowledge
|
|
199
|
+
// ── v3.0: Knowledge ───────────────────────────────────────────────────
|
|
195
200
|
case 'adr':
|
|
196
201
|
runModule('adr-ingestor.cjs', 'ingest', arg1 || 'docs/adr');
|
|
197
202
|
break;
|
|
@@ -200,24 +205,45 @@ switch (command) {
|
|
|
200
205
|
runModule('knowledge-ingestor.cjs', 'ingest', arg1 || '');
|
|
201
206
|
break;
|
|
202
207
|
|
|
203
|
-
// ── v3.0: Metrics
|
|
208
|
+
// ── v3.0: Metrics ─────────────────────────────────────────────────────
|
|
204
209
|
case 'metrics':
|
|
205
210
|
runModule('metrics.cjs', arg1 || 'summary');
|
|
206
211
|
break;
|
|
207
212
|
|
|
208
|
-
// ── v3.0: Decision Trail
|
|
213
|
+
// ── v3.0: Decision Trail ──────────────────────────────────────────────
|
|
209
214
|
case 'trail': {
|
|
210
|
-
if (!arg1)
|
|
211
|
-
else if (arg1 === 'why')
|
|
212
|
-
else if (arg1 === 'timeline'){ if (!arg2) { console.log('\n Uso: akdd trail timeline <módulo>\n'); break; } runModule('decision-trail.cjs', 'timeline', `"${arg2}"`); }
|
|
213
|
-
else
|
|
215
|
+
if (!arg1) runModule('decision-trail.cjs', 'recent', '5');
|
|
216
|
+
else if (arg1 === 'why') { if (!arg2) { console.log('\n Uso: akdd trail why <entidad>\n'); break; } runModule('decision-trail.cjs', 'why', `"${arg2}"`); }
|
|
217
|
+
else if (arg1 === 'timeline') { if (!arg2) { console.log('\n Uso: akdd trail timeline <módulo>\n'); break; } runModule('decision-trail.cjs', 'timeline', `"${arg2}"`); }
|
|
218
|
+
else runModule('decision-trail.cjs', 'ciclo', `"${arg1}"`);
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// ── v3.0: Collaborative Mode (Legion) ────────────────────────────────
|
|
223
|
+
case 'collab': {
|
|
224
|
+
const sub = arg1 || 'status';
|
|
225
|
+
if (sub === 'init') {
|
|
226
|
+
runModule('collab-manager.cjs', 'init');
|
|
227
|
+
} else if (sub === 'join') {
|
|
228
|
+
if (!arg2 || !args[3]) {
|
|
229
|
+
console.log('\n Uso: akdd collab join <url> <token>\n');
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
runModule('collab-manager.cjs', 'join', `"${arg2}" "${args[3]}"`);
|
|
233
|
+
} else if (sub === 'push') {
|
|
234
|
+
runModule('collab-manager.cjs', 'push');
|
|
235
|
+
} else if (sub === 'pull') {
|
|
236
|
+
runModule('collab-manager.cjs', 'pull');
|
|
237
|
+
} else {
|
|
238
|
+
runModule('collab-manager.cjs', 'status');
|
|
239
|
+
}
|
|
214
240
|
break;
|
|
215
241
|
}
|
|
216
242
|
|
|
217
|
-
// ── Dashboard
|
|
243
|
+
// ── Dashboard ─────────────────────────────────────────────────────────
|
|
218
244
|
case 'dashboard': dashboard(); break;
|
|
219
245
|
|
|
220
|
-
// ── v2.2: Intelligence
|
|
246
|
+
// ── v2.2: Intelligence ────────────────────────────────────────────────
|
|
221
247
|
case 'git-context': runGrafo('git-context', args.includes('--install-hook') ? '--install-hook' : ''); break;
|
|
222
248
|
case 'predict': runGrafo('predict'); break;
|
|
223
249
|
case 'embed-status': runGrafo('embed-status'); break;
|
|
@@ -242,6 +268,15 @@ switch (command) {
|
|
|
242
268
|
break;
|
|
243
269
|
}
|
|
244
270
|
|
|
271
|
+
// ── v3.0: MCP Setup ───────────────────────────────────────────────────
|
|
272
|
+
case 'mcp': {
|
|
273
|
+
const sub = arg1;
|
|
274
|
+
const opts = { global: args.includes('--global') };
|
|
275
|
+
if (sub === 'status') mcpStatus(process.cwd());
|
|
276
|
+
else mcpSetup(process.cwd(), opts);
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
279
|
+
|
|
245
280
|
case '--version': case '-v':
|
|
246
281
|
console.log(pkg.version); break;
|
|
247
282
|
|
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agentic KDD — Collab Manager v2.0
|
|
3
|
+
* Modo colaborativo automático via Turso + Cloudflare Worker.
|
|
4
|
+
*
|
|
5
|
+
* El usuario solo corre: akdd collab init
|
|
6
|
+
* El sistema crea la DB en Turso automáticamente.
|
|
7
|
+
* El usuario nunca sabe que Turso existe.
|
|
8
|
+
*
|
|
9
|
+
* Uso:
|
|
10
|
+
* node .agentic/grafo/collab-manager.cjs init
|
|
11
|
+
* node .agentic/grafo/collab-manager.cjs push
|
|
12
|
+
* node .agentic/grafo/collab-manager.cjs pull
|
|
13
|
+
* node .agentic/grafo/collab-manager.cjs status
|
|
14
|
+
* node .agentic/grafo/collab-manager.cjs join <url> <token>
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
'use strict';
|
|
18
|
+
|
|
19
|
+
const fs = require('fs');
|
|
20
|
+
const path = require('path');
|
|
21
|
+
const os = require('os');
|
|
22
|
+
const { execSync } = require('child_process');
|
|
23
|
+
|
|
24
|
+
// URL de tu Cloudflare Worker — actualizar después de deploy
|
|
25
|
+
const PROVISIONER_URL = 'https://agentic-collab.adrianlpz-game.workers.dev';
|
|
26
|
+
|
|
27
|
+
const COLLAB_CONFIG_PATH = '.agentic/collab.json';
|
|
28
|
+
|
|
29
|
+
const SYNC_TABLES = [
|
|
30
|
+
'nodos',
|
|
31
|
+
'relaciones_semanticas',
|
|
32
|
+
'episodios',
|
|
33
|
+
'knowledge_docs',
|
|
34
|
+
'ast_symbols',
|
|
35
|
+
'ast_edges',
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
// ─── DB LOCAL ──────────────────────────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
function openLocalDB(projectRoot) {
|
|
41
|
+
const dbPath = path.join(projectRoot, '.agentic/memoria.db');
|
|
42
|
+
try { return new (require('better-sqlite3'))(dbPath); } catch {}
|
|
43
|
+
try { const { DatabaseSync } = require('node:sqlite'); return new DatabaseSync(dbPath); } catch {}
|
|
44
|
+
throw new Error('No SQLite driver disponible');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ─── CONFIG ───────────────────────────────────────────────────────────────────
|
|
48
|
+
|
|
49
|
+
function loadConfig(projectRoot) {
|
|
50
|
+
const configPath = path.join(projectRoot, COLLAB_CONFIG_PATH);
|
|
51
|
+
if (!fs.existsSync(configPath)) return null;
|
|
52
|
+
try { return JSON.parse(fs.readFileSync(configPath, 'utf8')); } catch { return null; }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function saveConfig(projectRoot, config) {
|
|
56
|
+
const configPath = path.join(projectRoot, COLLAB_CONFIG_PATH);
|
|
57
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
58
|
+
|
|
59
|
+
// Asegurar que collab.json está en .gitignore
|
|
60
|
+
const gitignorePath = path.join(projectRoot, '.gitignore');
|
|
61
|
+
if (fs.existsSync(gitignorePath)) {
|
|
62
|
+
const gi = fs.readFileSync(gitignorePath, 'utf8');
|
|
63
|
+
if (!gi.includes('collab.json')) {
|
|
64
|
+
fs.appendFileSync(gitignorePath, '\n# Agentic KDD collab credentials\n.agentic/collab.json\n');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ─── GENERAR PROJECT ID ───────────────────────────────────────────────────────
|
|
70
|
+
|
|
71
|
+
function getProjectId(projectRoot) {
|
|
72
|
+
// Usar el nombre del directorio + un hash corto para que sea único y legible
|
|
73
|
+
const dirName = path.basename(projectRoot).toLowerCase().replace(/[^a-z0-9]/g, '-');
|
|
74
|
+
const hash = require('crypto')
|
|
75
|
+
.createHash('md5')
|
|
76
|
+
.update(projectRoot)
|
|
77
|
+
.digest('hex')
|
|
78
|
+
.substring(0, 6);
|
|
79
|
+
return `${dirName}-${hash}`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ─── INIT — PROVISIONAR DB AUTOMÁTICAMENTE ───────────────────────────────────
|
|
83
|
+
|
|
84
|
+
async function collabInit(projectRoot) {
|
|
85
|
+
console.log('\n[COLLAB] Activando modo colaborativo...\n');
|
|
86
|
+
|
|
87
|
+
// Verificar si ya está configurado
|
|
88
|
+
const existing = loadConfig(projectRoot);
|
|
89
|
+
if (existing?.enabled && existing?.url) {
|
|
90
|
+
console.log('[COLLAB] Ya configurado. URL:', existing.url);
|
|
91
|
+
console.log('[COLLAB] Para re-configurar: borrar .agentic/collab.json y correr de nuevo.');
|
|
92
|
+
return existing;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const projectId = getProjectId(projectRoot);
|
|
96
|
+
console.log(`[COLLAB] Project ID: ${projectId}`);
|
|
97
|
+
console.log('[COLLAB] Creando base de datos compartida...');
|
|
98
|
+
|
|
99
|
+
// Llamar al Cloudflare Worker para provisionar la DB
|
|
100
|
+
let provisionResult;
|
|
101
|
+
try {
|
|
102
|
+
const response = await fetch(PROVISIONER_URL, {
|
|
103
|
+
method: 'POST',
|
|
104
|
+
headers: { 'Content-Type': 'application/json' },
|
|
105
|
+
body: JSON.stringify({ projectId }),
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
if (!response.ok) {
|
|
109
|
+
const err = await response.text();
|
|
110
|
+
throw new Error(`Provisioner error: ${err}`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
provisionResult = await response.json();
|
|
114
|
+
} catch (e) {
|
|
115
|
+
console.error('[COLLAB] Error conectando con el servidor de provisión:', e.message);
|
|
116
|
+
console.error('[COLLAB] Verificar que el Cloudflare Worker está activo.');
|
|
117
|
+
process.exit(1);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (!provisionResult.ok) {
|
|
121
|
+
console.error('[COLLAB] Error creando DB:', provisionResult.error);
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Guardar credenciales localmente
|
|
126
|
+
const config = {
|
|
127
|
+
enabled: true,
|
|
128
|
+
url: provisionResult.url,
|
|
129
|
+
token: provisionResult.token,
|
|
130
|
+
db: provisionResult.db,
|
|
131
|
+
project_id: projectId,
|
|
132
|
+
member_id: os.userInfo().username,
|
|
133
|
+
sync_on_cycle_end: true,
|
|
134
|
+
last_sync: null,
|
|
135
|
+
created_at: new Date().toISOString(),
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
saveConfig(projectRoot, config);
|
|
139
|
+
|
|
140
|
+
console.log('\n[COLLAB] ✅ Modo colaborativo activado.');
|
|
141
|
+
console.log(`[COLLAB] DB: ${provisionResult.db}`);
|
|
142
|
+
console.log('[COLLAB] Credenciales guardadas en .agentic/collab.json (gitignored)');
|
|
143
|
+
console.log('\n[COLLAB] Para que otros miembros del equipo se unan:');
|
|
144
|
+
console.log(` Compárteles: URL y token de .agentic/collab.json`);
|
|
145
|
+
console.log(` Ellos corren: akdd collab join <url> <token>\n`);
|
|
146
|
+
|
|
147
|
+
// Primer push
|
|
148
|
+
console.log('[COLLAB] Sincronizando memoria local → compartida...');
|
|
149
|
+
await syncUp(projectRoot);
|
|
150
|
+
|
|
151
|
+
return config;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ─── JOIN — PARA EL DEV QUE SE UNE AL EQUIPO ─────────────────────────────────
|
|
155
|
+
|
|
156
|
+
async function collabJoin(projectRoot, url, token) {
|
|
157
|
+
console.log('\n[COLLAB] Uniéndose al equipo...\n');
|
|
158
|
+
|
|
159
|
+
const config = {
|
|
160
|
+
enabled: true,
|
|
161
|
+
url,
|
|
162
|
+
token,
|
|
163
|
+
member_id: os.userInfo().username,
|
|
164
|
+
sync_on_cycle_end: true,
|
|
165
|
+
last_sync: null,
|
|
166
|
+
joined_at: new Date().toISOString(),
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
saveConfig(projectRoot, config);
|
|
170
|
+
|
|
171
|
+
console.log('[COLLAB] ✅ Conectado al equipo.');
|
|
172
|
+
console.log('[COLLAB] Descargando memoria del equipo...');
|
|
173
|
+
|
|
174
|
+
await syncDown(projectRoot);
|
|
175
|
+
|
|
176
|
+
console.log('[COLLAB] ✅ Listo. Ya tienes toda la memoria del equipo.\n');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// ─── SYNC UP (local → Turso) ──────────────────────────────────────────────────
|
|
180
|
+
|
|
181
|
+
async function syncUp(projectRoot) {
|
|
182
|
+
const config = loadConfig(projectRoot);
|
|
183
|
+
if (!config?.enabled) {
|
|
184
|
+
console.log('[COLLAB] Modo colaborativo no activado. Correr: akdd collab init');
|
|
185
|
+
return { synced: false };
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
let client;
|
|
189
|
+
try {
|
|
190
|
+
const { createClient } = require('@libsql/client');
|
|
191
|
+
client = createClient({ url: config.url, authToken: config.token });
|
|
192
|
+
} catch {
|
|
193
|
+
console.log('[COLLAB] @libsql/client no instalado. Correr: npm install @libsql/client');
|
|
194
|
+
return { synced: false };
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const localDB = openLocalDB(projectRoot);
|
|
198
|
+
const lastSync = config.last_sync || '1970-01-01T00:00:00.000Z';
|
|
199
|
+
let totalRows = 0;
|
|
200
|
+
|
|
201
|
+
// Schema push — crear tablas en remoto si no existen
|
|
202
|
+
await pushSchema(client, localDB);
|
|
203
|
+
|
|
204
|
+
for (const table of SYNC_TABLES) {
|
|
205
|
+
try {
|
|
206
|
+
// Solo filas nuevas/modificadas desde el último sync
|
|
207
|
+
const dateField = getDateField(table);
|
|
208
|
+
const rows = localDB.prepare(
|
|
209
|
+
`SELECT * FROM ${table} WHERE ${dateField} > ? LIMIT 500`
|
|
210
|
+
).all(lastSync);
|
|
211
|
+
|
|
212
|
+
if (rows.length === 0) continue;
|
|
213
|
+
|
|
214
|
+
console.log(`[COLLAB] ${table}: ${rows.length} filas a sincronizar`);
|
|
215
|
+
|
|
216
|
+
for (const row of rows) {
|
|
217
|
+
const keys = Object.keys(row);
|
|
218
|
+
const vals = Object.values(row);
|
|
219
|
+
const placeholders = keys.map(() => '?').join(', ');
|
|
220
|
+
try {
|
|
221
|
+
await client.execute({
|
|
222
|
+
sql: `INSERT OR REPLACE INTO ${table} (${keys.join(', ')}) VALUES (${placeholders})`,
|
|
223
|
+
args: vals,
|
|
224
|
+
});
|
|
225
|
+
totalRows++;
|
|
226
|
+
} catch {}
|
|
227
|
+
}
|
|
228
|
+
} catch {}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Actualizar last_sync
|
|
232
|
+
config.last_sync = new Date().toISOString();
|
|
233
|
+
saveConfig(projectRoot, config);
|
|
234
|
+
|
|
235
|
+
console.log(`[COLLAB] ✅ Sync up: ${totalRows} filas enviadas.`);
|
|
236
|
+
return { synced: true, rows: totalRows };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// ─── SYNC DOWN (Turso → local) ────────────────────────────────────────────────
|
|
240
|
+
|
|
241
|
+
async function syncDown(projectRoot) {
|
|
242
|
+
const config = loadConfig(projectRoot);
|
|
243
|
+
if (!config?.enabled) return { synced: false };
|
|
244
|
+
|
|
245
|
+
let client;
|
|
246
|
+
try {
|
|
247
|
+
const { createClient } = require('@libsql/client');
|
|
248
|
+
client = createClient({ url: config.url, authToken: config.token });
|
|
249
|
+
} catch {
|
|
250
|
+
console.log('[COLLAB] @libsql/client no instalado. Correr: npm install @libsql/client');
|
|
251
|
+
return { synced: false };
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const localDB = openLocalDB(projectRoot);
|
|
255
|
+
let totalRows = 0;
|
|
256
|
+
|
|
257
|
+
for (const table of SYNC_TABLES) {
|
|
258
|
+
try {
|
|
259
|
+
const dateField = getDateField(table);
|
|
260
|
+
const lastSync = config.last_sync || '1970-01-01T00:00:00.000Z';
|
|
261
|
+
|
|
262
|
+
const rs = await client.execute(
|
|
263
|
+
`SELECT * FROM ${table} WHERE ${dateField} > ? LIMIT 1000`,
|
|
264
|
+
[lastSync]
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
if (!rs.rows?.length) continue;
|
|
268
|
+
|
|
269
|
+
console.log(`[COLLAB] ${table}: recibiendo ${rs.rows.length} filas`);
|
|
270
|
+
|
|
271
|
+
for (const row of rs.rows) {
|
|
272
|
+
const keys = Object.keys(row);
|
|
273
|
+
const vals = keys.map(k => row[k]);
|
|
274
|
+
const placeholders = keys.map(() => '?').join(', ');
|
|
275
|
+
|
|
276
|
+
// Estrategia por tabla:
|
|
277
|
+
// episodios → INSERT OR IGNORE (son aditivos, no sobrescribir locales)
|
|
278
|
+
// resto → INSERT OR REPLACE (toma la versión más reciente)
|
|
279
|
+
const strategy = table === 'episodios' ? 'OR IGNORE' : 'OR REPLACE';
|
|
280
|
+
|
|
281
|
+
try {
|
|
282
|
+
localDB.prepare(
|
|
283
|
+
`INSERT ${strategy} INTO ${table} (${keys.join(', ')}) VALUES (${placeholders})`
|
|
284
|
+
).run(vals);
|
|
285
|
+
totalRows++;
|
|
286
|
+
} catch {}
|
|
287
|
+
}
|
|
288
|
+
} catch {}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Actualizar last_sync
|
|
292
|
+
config.last_sync = new Date().toISOString();
|
|
293
|
+
saveConfig(projectRoot, config);
|
|
294
|
+
|
|
295
|
+
console.log(`[COLLAB] ✅ Sync down: ${totalRows} filas recibidas.`);
|
|
296
|
+
return { synced: true, rows: totalRows };
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// ─── SCHEMA PUSH ──────────────────────────────────────────────────────────────
|
|
300
|
+
// Crea las tablas en la DB remota si no existen (primera vez)
|
|
301
|
+
|
|
302
|
+
async function pushSchema(client, localDB) {
|
|
303
|
+
const tables = localDB.prepare(
|
|
304
|
+
"SELECT name, sql FROM sqlite_master WHERE type='table' AND sql IS NOT NULL"
|
|
305
|
+
).all();
|
|
306
|
+
|
|
307
|
+
for (const table of tables) {
|
|
308
|
+
if (!SYNC_TABLES.includes(table.name)) continue;
|
|
309
|
+
try {
|
|
310
|
+
await client.execute(
|
|
311
|
+
table.sql.replace('CREATE TABLE', 'CREATE TABLE IF NOT EXISTS')
|
|
312
|
+
);
|
|
313
|
+
} catch {}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// ─── STATUS ───────────────────────────────────────────────────────────────────
|
|
318
|
+
|
|
319
|
+
async function status(projectRoot) {
|
|
320
|
+
const config = loadConfig(projectRoot);
|
|
321
|
+
|
|
322
|
+
console.log('\n[COLLAB] Estado del modo colaborativo:\n');
|
|
323
|
+
|
|
324
|
+
if (!config?.enabled) {
|
|
325
|
+
console.log(' ❌ No activado.');
|
|
326
|
+
console.log(' Correr: akdd collab init\n');
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
console.log(` ✅ Activado`);
|
|
331
|
+
console.log(` DB: ${config.db || config.url}`);
|
|
332
|
+
console.log(` Miembro: ${config.member_id}`);
|
|
333
|
+
console.log(` Último sync: ${config.last_sync || 'nunca'}`);
|
|
334
|
+
console.log(` Auto-sync: ${config.sync_on_cycle_end ? 'sí (al final de cada aa:)' : 'no'}`);
|
|
335
|
+
|
|
336
|
+
// Test de conexión
|
|
337
|
+
try {
|
|
338
|
+
const { createClient } = require('@libsql/client');
|
|
339
|
+
const client = createClient({ url: config.url, authToken: config.token });
|
|
340
|
+
await client.execute('SELECT 1');
|
|
341
|
+
console.log(' Conexión: ✅ OK\n');
|
|
342
|
+
} catch {
|
|
343
|
+
console.log(' Conexión: ❌ Error — verificar credenciales\n');
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// ─── HELPER ───────────────────────────────────────────────────────────────────
|
|
348
|
+
|
|
349
|
+
function getDateField(table) {
|
|
350
|
+
const fields = {
|
|
351
|
+
nodos: 'fecha_update',
|
|
352
|
+
episodios: 'fecha',
|
|
353
|
+
relaciones_semanticas: 'valid_at',
|
|
354
|
+
knowledge_docs: 'last_indexed',
|
|
355
|
+
ast_symbols: 'last_indexed',
|
|
356
|
+
ast_edges: 'last_indexed',
|
|
357
|
+
};
|
|
358
|
+
return fields[table] || 'rowid';
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// ─── CLI ──────────────────────────────────────────────────────────────────────
|
|
362
|
+
|
|
363
|
+
if (require.main === module) {
|
|
364
|
+
const [,, cmd, arg1, arg2] = process.argv;
|
|
365
|
+
const projectRoot = process.cwd();
|
|
366
|
+
|
|
367
|
+
switch (cmd) {
|
|
368
|
+
case 'init':
|
|
369
|
+
collabInit(projectRoot).catch(console.error);
|
|
370
|
+
break;
|
|
371
|
+
case 'join':
|
|
372
|
+
if (!arg1 || !arg2) {
|
|
373
|
+
console.error('Uso: collab-manager.cjs join <url> <token>');
|
|
374
|
+
process.exit(1);
|
|
375
|
+
}
|
|
376
|
+
collabJoin(projectRoot, arg1, arg2).catch(console.error);
|
|
377
|
+
break;
|
|
378
|
+
case 'push':
|
|
379
|
+
syncUp(projectRoot).then(r => {
|
|
380
|
+
process.exit(r.synced ? 0 : 1);
|
|
381
|
+
}).catch(console.error);
|
|
382
|
+
break;
|
|
383
|
+
case 'pull':
|
|
384
|
+
syncDown(projectRoot).then(r => {
|
|
385
|
+
process.exit(r.synced ? 0 : 1);
|
|
386
|
+
}).catch(console.error);
|
|
387
|
+
break;
|
|
388
|
+
case 'status':
|
|
389
|
+
status(projectRoot).catch(console.error);
|
|
390
|
+
break;
|
|
391
|
+
default:
|
|
392
|
+
console.log('Uso: node collab-manager.cjs [init | join <url> <token> | push | pull | status]');
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
module.exports = {
|
|
397
|
+
collabInit,
|
|
398
|
+
collabJoin,
|
|
399
|
+
syncUp,
|
|
400
|
+
syncDown,
|
|
401
|
+
status,
|
|
402
|
+
loadConfig,
|
|
403
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentic-kdd",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.3",
|
|
4
4
|
"description": "Autonomous development pipeline — aa: · ag: · audit: · AST graph · Harness · Specs · Impact analysis · Decision trail · Metrics · MCP server. Works with Cursor and Claude Code.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
package/src/init.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
+
const { mcpSetup } = require('./mcp-setup');
|
|
2
3
|
|
|
3
4
|
const fs = require('fs-extra');
|
|
4
5
|
const path = require('path');
|
|
@@ -278,6 +279,14 @@ Estado: Pendiente aa: configurar
|
|
|
278
279
|
console.log(chalk.gray(' CLAUDE.md — activa aa: / ag: / audit:'));
|
|
279
280
|
console.log(chalk.gray(' .cursorrules — reglas para Cursor'));
|
|
280
281
|
|
|
282
|
+
// ── CONFIGURAR MCP AUTOMÁTICAMENTE ─────────────────────────────────────────
|
|
283
|
+
console.log(chalk.bold(" Configurando MCP server..."));
|
|
284
|
+
try {
|
|
285
|
+
await mcpSetup(projectPath, { silent: false });
|
|
286
|
+
} catch(e) {
|
|
287
|
+
console.log(chalk.gray(" (MCP: ejecuta akdd mcp para configurarlo manualmente)"));
|
|
288
|
+
}
|
|
289
|
+
|
|
281
290
|
// Instrucción final
|
|
282
291
|
console.log('\n' + chalk.dim(' ─────────────────────────────────────────────'));
|
|
283
292
|
console.log(chalk.bold(' Último paso — abre este proyecto en'));
|
package/src/mcp-setup.js
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const { execSync } = require('child_process');
|
|
7
|
+
const chalk = require('chalk');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* akdd mcp — Configura el MCP server automáticamente.
|
|
11
|
+
*
|
|
12
|
+
* Hace todo lo posible sin intervención del usuario:
|
|
13
|
+
* 1. Escribe .cursor/mcp.json en el proyecto (Cursor lo lee automáticamente)
|
|
14
|
+
* 2. Intenta ejecutar `claude mcp add` si el CLI está disponible
|
|
15
|
+
* 3. Imprime el JSON EXACTO (con ruta real del sistema, no placeholder)
|
|
16
|
+
* para los casos que requieran paso manual
|
|
17
|
+
*
|
|
18
|
+
* El usuario NUNCA tiene que adivinar la ruta — este comando la resuelve por él.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
async function mcpSetup(projectPath, opts = {}) {
|
|
22
|
+
projectPath = projectPath || process.cwd();
|
|
23
|
+
|
|
24
|
+
// ── Verificar que Agentic está instalado ───────────────────────────────────
|
|
25
|
+
const serverFile = path.join(projectPath, '.agentic', 'grafo', 'mcp-server.cjs');
|
|
26
|
+
if (!fs.existsSync(serverFile)) {
|
|
27
|
+
console.log(chalk.yellow('\n mcp-server.cjs no encontrado.'));
|
|
28
|
+
console.log(chalk.gray(' Ejecuta: akdd update\n'));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ── Resolver ruta absoluta real (sin placeholders, sin adivinar) ───────────
|
|
33
|
+
// path.resolve() → ruta exacta del sistema actual, con nombre de usuario correcto
|
|
34
|
+
const serverPath = path.resolve(serverFile);
|
|
35
|
+
const serverPathJson = serverPath.replace(/\\/g, '\\\\'); // escaping para JSON en Windows
|
|
36
|
+
|
|
37
|
+
console.log('\n' + chalk.bold.hex('#8b5cf6')(' Agentic KDD — MCP Setup'));
|
|
38
|
+
console.log(chalk.gray(` Ruta del servidor: ${serverPath}\n`));
|
|
39
|
+
|
|
40
|
+
const results = {
|
|
41
|
+
cursor_project: false,
|
|
42
|
+
cursor_global: false,
|
|
43
|
+
claude_code: false,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// ══ PASO 1: Cursor — proyecto (automático, siempre funciona) ═══════════════
|
|
47
|
+
const cursorMcpDir = path.join(projectPath, '.cursor');
|
|
48
|
+
const cursorMcpFile = path.join(cursorMcpDir, 'mcp.json');
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
fs.ensureDirSync(cursorMcpDir);
|
|
52
|
+
|
|
53
|
+
let cursorConfig = {};
|
|
54
|
+
if (fs.existsSync(cursorMcpFile)) {
|
|
55
|
+
try { cursorConfig = JSON.parse(fs.readFileSync(cursorMcpFile, 'utf8')); } catch {}
|
|
56
|
+
}
|
|
57
|
+
if (!cursorConfig.mcpServers) cursorConfig.mcpServers = {};
|
|
58
|
+
|
|
59
|
+
cursorConfig.mcpServers['agentic-kdd'] = {
|
|
60
|
+
command: 'node',
|
|
61
|
+
args: [serverPath],
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
fs.writeFileSync(cursorMcpFile, JSON.stringify(cursorConfig, null, 2));
|
|
65
|
+
results.cursor_project = true;
|
|
66
|
+
console.log(chalk.green(' ✓ Cursor (proyecto) → .cursor/mcp.json actualizado'));
|
|
67
|
+
console.log(chalk.gray(' Reinicia Cursor o recarga la ventana (Ctrl+Shift+P → "Reload Window")'));
|
|
68
|
+
} catch (e) {
|
|
69
|
+
console.log(chalk.yellow(` ⚠ Cursor (proyecto) → Error: ${e.message}`));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ══ PASO 2: Claude Code CLI (automático si está instalado) ═════════════════
|
|
73
|
+
const claudeCliAvailable = isCLIAvailable('claude');
|
|
74
|
+
if (claudeCliAvailable) {
|
|
75
|
+
try {
|
|
76
|
+
// claude mcp add agentic-kdd -- node "/ruta/exacta/mcp-server.cjs"
|
|
77
|
+
execSync(`claude mcp add agentic-kdd -- node "${serverPath}"`, {
|
|
78
|
+
stdio: 'pipe',
|
|
79
|
+
cwd: projectPath,
|
|
80
|
+
});
|
|
81
|
+
results.claude_code = true;
|
|
82
|
+
console.log(chalk.green(' ✓ Claude Code → registrado via "claude mcp add"'));
|
|
83
|
+
} catch (e) {
|
|
84
|
+
// Puede fallar si ya existe — intentar actualizar
|
|
85
|
+
try {
|
|
86
|
+
execSync(`claude mcp remove agentic-kdd`, { stdio: 'pipe', cwd: projectPath });
|
|
87
|
+
execSync(`claude mcp add agentic-kdd -- node "${serverPath}"`, {
|
|
88
|
+
stdio: 'pipe', cwd: projectPath,
|
|
89
|
+
});
|
|
90
|
+
results.claude_code = true;
|
|
91
|
+
console.log(chalk.green(' ✓ Claude Code → actualizado'));
|
|
92
|
+
} catch {
|
|
93
|
+
console.log(chalk.gray(' ~ Claude Code → CLI no disponible o ya configurado'));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
} else {
|
|
97
|
+
console.log(chalk.gray(' ~ Claude Code → CLI no detectado (config manual abajo)'));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ══ PASO 3: Cursor global (opcional, solo si --global) ════════════════════
|
|
101
|
+
if (opts.global) {
|
|
102
|
+
const globalCursorConfig = getGlobalCursorConfigPath();
|
|
103
|
+
if (globalCursorConfig) {
|
|
104
|
+
try {
|
|
105
|
+
fs.ensureDirSync(path.dirname(globalCursorConfig));
|
|
106
|
+
let globalConfig = {};
|
|
107
|
+
if (fs.existsSync(globalCursorConfig)) {
|
|
108
|
+
try { globalConfig = JSON.parse(fs.readFileSync(globalCursorConfig, 'utf8')); } catch {}
|
|
109
|
+
}
|
|
110
|
+
if (!globalConfig.mcpServers) globalConfig.mcpServers = {};
|
|
111
|
+
globalConfig.mcpServers['agentic-kdd'] = { command: 'node', args: [serverPath] };
|
|
112
|
+
fs.writeFileSync(globalCursorConfig, JSON.stringify(globalConfig, null, 2));
|
|
113
|
+
results.cursor_global = true;
|
|
114
|
+
console.log(chalk.green(` ✓ Cursor (global) → ${globalCursorConfig}`));
|
|
115
|
+
} catch (e) {
|
|
116
|
+
console.log(chalk.yellow(` ⚠ Cursor (global) → ${e.message}`));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ══ PASO 4: Imprimir configs manuales con ruta EXACTA ═════════════════════
|
|
122
|
+
console.log('\n' + chalk.bold(' ── Config manual (si necesitas hacerlo tú mismo) ──────────────────'));
|
|
123
|
+
|
|
124
|
+
// Cursor manual
|
|
125
|
+
const cursorJson = JSON.stringify({
|
|
126
|
+
mcpServers: {
|
|
127
|
+
'agentic-kdd': {
|
|
128
|
+
command: 'node',
|
|
129
|
+
args: [serverPath],
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}, null, 2);
|
|
133
|
+
|
|
134
|
+
console.log('\n' + chalk.cyan(' Cursor → .cursor/mcp.json'));
|
|
135
|
+
console.log(chalk.gray(' (Abre Cursor → Settings → MCP → Add → pega esto:)\n'));
|
|
136
|
+
console.log(chalk.white(cursorJson.split('\n').map(l => ' ' + l).join('\n')));
|
|
137
|
+
|
|
138
|
+
// Claude Code manual
|
|
139
|
+
console.log('\n' + chalk.cyan(' Claude Code → terminal'));
|
|
140
|
+
console.log(chalk.white(` claude mcp add agentic-kdd -- node "${serverPath}"`));
|
|
141
|
+
|
|
142
|
+
// VS Code manual
|
|
143
|
+
console.log('\n' + chalk.cyan(' VS Code → .vscode/settings.json'));
|
|
144
|
+
const vscodeJson = JSON.stringify({
|
|
145
|
+
'mcp.servers': {
|
|
146
|
+
'agentic-kdd': {
|
|
147
|
+
command: 'node',
|
|
148
|
+
args: [serverPath],
|
|
149
|
+
type: 'stdio',
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}, null, 2);
|
|
153
|
+
console.log(chalk.white(vscodeJson.split('\n').map(l => ' ' + l).join('\n')));
|
|
154
|
+
|
|
155
|
+
// ══ RESUMEN ════════════════════════════════════════════════════════════════
|
|
156
|
+
console.log('\n' + chalk.dim(' ──────────────────────────────────────────────────────'));
|
|
157
|
+
const autoCount = Object.values(results).filter(Boolean).length;
|
|
158
|
+
if (autoCount > 0) {
|
|
159
|
+
console.log(chalk.bold.green(` ${autoCount} configuración(es) automática(s) completada(s).`));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (results.cursor_project) {
|
|
163
|
+
console.log(chalk.green(' Cursor listo:') + chalk.gray(' Reload Window → las tools aparecen automáticamente.'));
|
|
164
|
+
}
|
|
165
|
+
if (results.claude_code) {
|
|
166
|
+
console.log(chalk.green(' Claude Code listo:') + chalk.gray(' cierra y abre el proyecto.'));
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
console.log('\n' + chalk.gray(' Para configurar globalmente (todos tus proyectos):'));
|
|
170
|
+
console.log(chalk.gray(' akdd mcp --global\n'));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ─── HELPERS ──────────────────────────────────────────────────────────────────
|
|
174
|
+
|
|
175
|
+
function isCLIAvailable(cmd) {
|
|
176
|
+
try {
|
|
177
|
+
execSync(`${cmd} --version`, { stdio: 'pipe', timeout: 5000 });
|
|
178
|
+
return true;
|
|
179
|
+
} catch { return false; }
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function getGlobalCursorConfigPath() {
|
|
183
|
+
const platform = os.platform();
|
|
184
|
+
const home = os.homedir();
|
|
185
|
+
|
|
186
|
+
if (platform === 'win32') {
|
|
187
|
+
// Windows: %APPDATA%\Cursor\User\globalStorage\mcp.json
|
|
188
|
+
const appData = process.env.APPDATA || path.join(home, 'AppData', 'Roaming');
|
|
189
|
+
return path.join(appData, 'Cursor', 'User', 'globalStorage', 'mcp.json');
|
|
190
|
+
}
|
|
191
|
+
if (platform === 'darwin') {
|
|
192
|
+
// macOS: ~/Library/Application Support/Cursor/User/globalStorage/mcp.json
|
|
193
|
+
return path.join(home, 'Library', 'Application Support', 'Cursor', 'User', 'globalStorage', 'mcp.json');
|
|
194
|
+
}
|
|
195
|
+
// Linux: ~/.config/Cursor/User/globalStorage/mcp.json
|
|
196
|
+
return path.join(home, '.config', 'Cursor', 'User', 'globalStorage', 'mcp.json');
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Verifica y muestra el estado actual de la config MCP.
|
|
201
|
+
* akdd mcp status
|
|
202
|
+
*/
|
|
203
|
+
function mcpStatus(projectPath) {
|
|
204
|
+
projectPath = projectPath || process.cwd();
|
|
205
|
+
|
|
206
|
+
const serverFile = path.join(projectPath, '.agentic', 'grafo', 'mcp-server.cjs');
|
|
207
|
+
const cursorProject = path.join(projectPath, '.cursor', 'mcp.json');
|
|
208
|
+
const globalCursor = getGlobalCursorConfigPath();
|
|
209
|
+
|
|
210
|
+
console.log('\n' + chalk.bold(' Agentic KDD — MCP Status\n'));
|
|
211
|
+
|
|
212
|
+
// Server file
|
|
213
|
+
const hasServer = fs.existsSync(serverFile);
|
|
214
|
+
console.log(hasServer
|
|
215
|
+
? chalk.green(' ✓ mcp-server.cjs encontrado')
|
|
216
|
+
: chalk.red(' ✗ mcp-server.cjs NO encontrado — ejecutar: akdd update'));
|
|
217
|
+
|
|
218
|
+
// Cursor project config
|
|
219
|
+
let cursorProjectOk = false;
|
|
220
|
+
if (fs.existsSync(cursorProject)) {
|
|
221
|
+
try {
|
|
222
|
+
const config = JSON.parse(fs.readFileSync(cursorProject, 'utf8'));
|
|
223
|
+
cursorProjectOk = !!(config?.mcpServers?.['agentic-kdd']);
|
|
224
|
+
} catch {}
|
|
225
|
+
}
|
|
226
|
+
console.log(cursorProjectOk
|
|
227
|
+
? chalk.green(' ✓ Cursor (proyecto) .cursor/mcp.json configurado')
|
|
228
|
+
: chalk.yellow(' ~ Cursor (proyecto) No configurado — ejecutar: akdd mcp'));
|
|
229
|
+
|
|
230
|
+
// Cursor global config
|
|
231
|
+
let cursorGlobalOk = false;
|
|
232
|
+
if (globalCursor && fs.existsSync(globalCursor)) {
|
|
233
|
+
try {
|
|
234
|
+
const config = JSON.parse(fs.readFileSync(globalCursor, 'utf8'));
|
|
235
|
+
cursorGlobalOk = !!(config?.mcpServers?.['agentic-kdd']);
|
|
236
|
+
} catch {}
|
|
237
|
+
}
|
|
238
|
+
console.log(cursorGlobalOk
|
|
239
|
+
? chalk.green(' ✓ Cursor (global) configurado')
|
|
240
|
+
: chalk.gray(' ~ Cursor (global) No configurado — opcional: akdd mcp --global'));
|
|
241
|
+
|
|
242
|
+
// Claude Code
|
|
243
|
+
const claudeAvailable = isCLIAvailable('claude');
|
|
244
|
+
if (claudeAvailable) {
|
|
245
|
+
try {
|
|
246
|
+
const mcpList = execSync('claude mcp list', { stdio: 'pipe' }).toString();
|
|
247
|
+
const hasAgentic = mcpList.includes('agentic-kdd');
|
|
248
|
+
console.log(hasAgentic
|
|
249
|
+
? chalk.green(' ✓ Claude Code registrado')
|
|
250
|
+
: chalk.yellow(' ~ Claude Code No registrado — ejecutar: akdd mcp'));
|
|
251
|
+
} catch {
|
|
252
|
+
console.log(chalk.gray(' ~ Claude Code CLI disponible pero sin listar MCPs'));
|
|
253
|
+
}
|
|
254
|
+
} else {
|
|
255
|
+
console.log(chalk.gray(' ~ Claude Code CLI No instalado'));
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (hasServer && !cursorProjectOk) {
|
|
259
|
+
console.log('\n' + chalk.bold(' → Ejecuta: akdd mcp\n'));
|
|
260
|
+
} else if (hasServer && cursorProjectOk) {
|
|
261
|
+
console.log('\n' + chalk.green(' Todo configurado. Recarga la ventana en Cursor si es necesario.\n'));
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
module.exports = { mcpSetup, mcpStatus };
|