agentic-kdd 3.0.2 → 3.0.4
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 +54 -31
- package/collab-manager.cjs +480 -0
- package/package.json +3 -2
package/bin/akdd.js
CHANGED
|
@@ -62,6 +62,14 @@ const HELP = `
|
|
|
62
62
|
akdd trail <ciclo_id> Full trail of a specific cycle
|
|
63
63
|
akdd trail why <f> Why does this file/entity exist?
|
|
64
64
|
|
|
65
|
+
Collaborative Mode (Legion):
|
|
66
|
+
akdd collab init Activate collaborative mode — creates shared DB automatically
|
|
67
|
+
akdd collab invite Generate a 6-char invite code for a team member (24h, one-use)
|
|
68
|
+
akdd collab join <code> Join the team with an invite code (e.g. LUMO-X7K2P4)
|
|
69
|
+
akdd collab push Push your learnings to the team
|
|
70
|
+
akdd collab pull Pull team's latest learnings
|
|
71
|
+
akdd collab status Check collaborative sync status
|
|
72
|
+
|
|
65
73
|
Intelligence v2.2:
|
|
66
74
|
akdd git-context Analyze git diff + risk assessment
|
|
67
75
|
akdd predict Predictive risk patterns from episodic memory
|
|
@@ -78,7 +86,6 @@ const HELP = `
|
|
|
78
86
|
akdd mcp Auto-configure MCP in all IDEs (recommended)
|
|
79
87
|
akdd mcp --global Configure MCP globally for all projects
|
|
80
88
|
akdd mcp status Check MCP configuration status
|
|
81
|
-
(manual fallback: imprime el JSON exacto con tu ruta real)
|
|
82
89
|
|
|
83
90
|
akdd --version / akdd --help
|
|
84
91
|
`;
|
|
@@ -99,11 +106,7 @@ function runGrafo(cmd, extra) {
|
|
|
99
106
|
function runModule(name, cmd, extra) {
|
|
100
107
|
const p = path.join(process.cwd(), '.agentic', 'grafo', name);
|
|
101
108
|
if (!fs.existsSync(p)) { console.log(`\n ${name} not found. Run: akdd update\n`); process.exit(1); }
|
|
102
|
-
const fullCmd = [
|
|
103
|
-
`node "${p}"`,
|
|
104
|
-
cmd || '',
|
|
105
|
-
extra || ''
|
|
106
|
-
].join(' ').trim().replace(/\s+/g,' ');
|
|
109
|
+
const fullCmd = [`node "${p}"`, cmd || '', extra || ''].join(' ').trim().replace(/\s+/g,' ');
|
|
107
110
|
try { execSync(fullCmd, { stdio: 'inherit', cwd: process.cwd() }); }
|
|
108
111
|
catch(e) { process.exit(e.status || 1); }
|
|
109
112
|
}
|
|
@@ -114,14 +117,14 @@ switch (command) {
|
|
|
114
117
|
case 'update': update(); break;
|
|
115
118
|
case 'analyze': analyze(); break;
|
|
116
119
|
|
|
117
|
-
// ── v3.0: Health
|
|
120
|
+
// ── v3.0: Health ──────────────────────────────────────────────────────
|
|
118
121
|
case 'health': {
|
|
119
122
|
const fixFlag = args.includes('--fix') ? '--fix' : '';
|
|
120
123
|
runModule('health-check.cjs', fixFlag);
|
|
121
124
|
break;
|
|
122
125
|
}
|
|
123
126
|
|
|
124
|
-
// ── Core memory
|
|
127
|
+
// ── Core memory ───────────────────────────────────────────────────────
|
|
125
128
|
case 'sync': runGrafo('sync'); break;
|
|
126
129
|
case 'graph': graph(); break;
|
|
127
130
|
case 'stats': runGrafo('stats'); break;
|
|
@@ -139,7 +142,7 @@ switch (command) {
|
|
|
139
142
|
runGrafo('impacto', `"${arg1}"`);
|
|
140
143
|
break;
|
|
141
144
|
|
|
142
|
-
// ── v3.0: Memory Audit
|
|
145
|
+
// ── v3.0: Memory Audit ────────────────────────────────────────────────
|
|
143
146
|
case 'audit': runModule('memory-audit.cjs', 'report'); break;
|
|
144
147
|
|
|
145
148
|
case 'forget': {
|
|
@@ -150,11 +153,11 @@ switch (command) {
|
|
|
150
153
|
break;
|
|
151
154
|
}
|
|
152
155
|
|
|
153
|
-
// ── v3.0: AST
|
|
156
|
+
// ── v3.0: AST ─────────────────────────────────────────────────────────
|
|
154
157
|
case 'ast': {
|
|
155
158
|
const sub = arg1 || 'index';
|
|
156
159
|
const tgt = arg2 || '';
|
|
157
|
-
if (sub === 'stats')
|
|
160
|
+
if (sub === 'stats') runModule('ast-indexer.cjs', 'stats');
|
|
158
161
|
else if (sub === 'symbols') {
|
|
159
162
|
if (!tgt) { console.log('\n Uso: akdd ast symbols <archivo>\n'); break; }
|
|
160
163
|
runModule('ast-indexer.cjs', 'symbols', `"${tgt}"`);
|
|
@@ -174,17 +177,17 @@ switch (command) {
|
|
|
174
177
|
runModule('decision-trail.cjs', 'why', `"${arg1}"`);
|
|
175
178
|
break;
|
|
176
179
|
|
|
177
|
-
// ── v3.0: Specs
|
|
180
|
+
// ── v3.0: Specs ───────────────────────────────────────────────────────
|
|
178
181
|
case 'spec': {
|
|
179
182
|
const sub = arg1;
|
|
180
183
|
const mod = arg2;
|
|
181
|
-
if (!sub || sub === 'list')
|
|
184
|
+
if (!sub || sub === 'list') runModule('spec-manager.cjs', 'list');
|
|
182
185
|
else if (sub === 'create') {
|
|
183
186
|
if (!mod) { console.log('\n Uso: akdd spec create <módulo> [--bugfix]\n'); break; }
|
|
184
187
|
runModule('spec-manager.cjs', 'create', `"${mod}"${args.includes('--bugfix') ? ' --bugfix' : ''}`);
|
|
185
188
|
}
|
|
186
|
-
else if (sub === 'waves')
|
|
187
|
-
else if (sub === 'validate')
|
|
189
|
+
else if (sub === 'waves') { if (!mod) { console.log('\n Uso: akdd spec waves <módulo>\n'); break; } runModule('spec-manager.cjs', 'waves', `"${mod}"`); }
|
|
190
|
+
else if (sub === 'validate') { if (!mod) { console.log('\n Uso: akdd spec validate <módulo>\n'); break; } runModule('spec-manager.cjs', 'validate', `"${mod}"`); }
|
|
188
191
|
else runModule('spec-manager.cjs', 'status', `"${sub}"`);
|
|
189
192
|
break;
|
|
190
193
|
}
|
|
@@ -194,7 +197,7 @@ switch (command) {
|
|
|
194
197
|
runModule('spec-manager.cjs', 'create', `"${arg1}"${args.includes('--bugfix') ? ' --bugfix' : ''}`);
|
|
195
198
|
break;
|
|
196
199
|
|
|
197
|
-
// ── v3.0: Knowledge
|
|
200
|
+
// ── v3.0: Knowledge ───────────────────────────────────────────────────
|
|
198
201
|
case 'adr':
|
|
199
202
|
runModule('adr-ingestor.cjs', 'ingest', arg1 || 'docs/adr');
|
|
200
203
|
break;
|
|
@@ -203,24 +206,48 @@ switch (command) {
|
|
|
203
206
|
runModule('knowledge-ingestor.cjs', 'ingest', arg1 || '');
|
|
204
207
|
break;
|
|
205
208
|
|
|
206
|
-
// ── v3.0: Metrics
|
|
209
|
+
// ── v3.0: Metrics ─────────────────────────────────────────────────────
|
|
207
210
|
case 'metrics':
|
|
208
211
|
runModule('metrics.cjs', arg1 || 'summary');
|
|
209
212
|
break;
|
|
210
213
|
|
|
211
|
-
// ── v3.0: Decision Trail
|
|
214
|
+
// ── v3.0: Decision Trail ──────────────────────────────────────────────
|
|
212
215
|
case 'trail': {
|
|
213
|
-
if (!arg1)
|
|
214
|
-
else if (arg1 === 'why')
|
|
215
|
-
else if (arg1 === 'timeline'){ if (!arg2) { console.log('\n Uso: akdd trail timeline <módulo>\n'); break; } runModule('decision-trail.cjs', 'timeline', `"${arg2}"`); }
|
|
216
|
-
else
|
|
216
|
+
if (!arg1) runModule('decision-trail.cjs', 'recent', '5');
|
|
217
|
+
else if (arg1 === 'why') { if (!arg2) { console.log('\n Uso: akdd trail why <entidad>\n'); break; } runModule('decision-trail.cjs', 'why', `"${arg2}"`); }
|
|
218
|
+
else if (arg1 === 'timeline') { if (!arg2) { console.log('\n Uso: akdd trail timeline <módulo>\n'); break; } runModule('decision-trail.cjs', 'timeline', `"${arg2}"`); }
|
|
219
|
+
else runModule('decision-trail.cjs', 'ciclo', `"${arg1}"`);
|
|
217
220
|
break;
|
|
218
221
|
}
|
|
219
222
|
|
|
220
|
-
// ──
|
|
223
|
+
// ── v3.0: Collaborative Mode (Legion) ────────────────────────────────
|
|
224
|
+
case 'collab': {
|
|
225
|
+
const sub = arg1 || 'status';
|
|
226
|
+
if (sub === 'init') {
|
|
227
|
+
runModule('collab-manager.cjs', 'init');
|
|
228
|
+
} else if (sub === 'invite') {
|
|
229
|
+
runModule('collab-manager.cjs', 'invite');
|
|
230
|
+
} else if (sub === 'join') {
|
|
231
|
+
if (!arg2) {
|
|
232
|
+
console.log('\n Uso: akdd collab join <código>\n');
|
|
233
|
+
console.log(' El código lo genera el jefe con: akdd collab invite\n');
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
runModule('collab-manager.cjs', 'join', `"${arg2}"`);
|
|
237
|
+
} else if (sub === 'push') {
|
|
238
|
+
runModule('collab-manager.cjs', 'push');
|
|
239
|
+
} else if (sub === 'pull') {
|
|
240
|
+
runModule('collab-manager.cjs', 'pull');
|
|
241
|
+
} else {
|
|
242
|
+
runModule('collab-manager.cjs', 'status');
|
|
243
|
+
}
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// ── Dashboard ─────────────────────────────────────────────────────────
|
|
221
248
|
case 'dashboard': dashboard(); break;
|
|
222
249
|
|
|
223
|
-
// ── v2.2: Intelligence
|
|
250
|
+
// ── v2.2: Intelligence ────────────────────────────────────────────────
|
|
224
251
|
case 'git-context': runGrafo('git-context', args.includes('--install-hook') ? '--install-hook' : ''); break;
|
|
225
252
|
case 'predict': runGrafo('predict'); break;
|
|
226
253
|
case 'embed-status': runGrafo('embed-status'); break;
|
|
@@ -245,16 +272,12 @@ switch (command) {
|
|
|
245
272
|
break;
|
|
246
273
|
}
|
|
247
274
|
|
|
248
|
-
|
|
249
|
-
// ── v3.0: MCP Setup ────────────────────────────────────────────────────
|
|
275
|
+
// ── v3.0: MCP Setup ───────────────────────────────────────────────────
|
|
250
276
|
case 'mcp': {
|
|
251
277
|
const sub = arg1;
|
|
252
278
|
const opts = { global: args.includes('--global') };
|
|
253
|
-
if (sub === 'status')
|
|
254
|
-
|
|
255
|
-
} else {
|
|
256
|
-
mcpSetup(process.cwd(), opts);
|
|
257
|
-
}
|
|
279
|
+
if (sub === 'status') mcpStatus(process.cwd());
|
|
280
|
+
else mcpSetup(process.cwd(), opts);
|
|
258
281
|
break;
|
|
259
282
|
}
|
|
260
283
|
|
|
@@ -0,0 +1,480 @@
|
|
|
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, urlOrCode, token) {
|
|
157
|
+
console.log('\n[COLLAB] Uniéndose al equipo...\n');
|
|
158
|
+
|
|
159
|
+
let finalUrl = urlOrCode;
|
|
160
|
+
let finalToken = token;
|
|
161
|
+
|
|
162
|
+
// Detectar si es un código de invitación (formato: PREFIX-XXXXXX)
|
|
163
|
+
const isInviteCode = !urlOrCode?.startsWith('libsql://') && !urlOrCode?.startsWith('http');
|
|
164
|
+
|
|
165
|
+
if (isInviteCode) {
|
|
166
|
+
console.log(`[COLLAB] Resolviendo código: ${urlOrCode}`);
|
|
167
|
+
try {
|
|
168
|
+
const response = await fetch(`${PROVISIONER_URL}/join`, {
|
|
169
|
+
method: 'POST',
|
|
170
|
+
headers: { 'Content-Type': 'application/json' },
|
|
171
|
+
body: JSON.stringify({ code: urlOrCode }),
|
|
172
|
+
});
|
|
173
|
+
const result = await response.json();
|
|
174
|
+
|
|
175
|
+
if (!result.ok) {
|
|
176
|
+
console.error(`[COLLAB] ❌ ${result.error}`);
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
finalUrl = result.url;
|
|
181
|
+
finalToken = result.token;
|
|
182
|
+
console.log(`[COLLAB] ✅ Código válido. Conectando a: ${result.db}`);
|
|
183
|
+
} catch (e) {
|
|
184
|
+
console.error('[COLLAB] Error resolviendo código:', e.message);
|
|
185
|
+
process.exit(1);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const config = {
|
|
190
|
+
enabled: true,
|
|
191
|
+
url: finalUrl,
|
|
192
|
+
token: finalToken,
|
|
193
|
+
member_id: os.userInfo().username,
|
|
194
|
+
sync_on_cycle_end: true,
|
|
195
|
+
last_sync: null,
|
|
196
|
+
joined_at: new Date().toISOString(),
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
saveConfig(projectRoot, config);
|
|
200
|
+
|
|
201
|
+
console.log('[COLLAB] ✅ Conectado al equipo.');
|
|
202
|
+
console.log('[COLLAB] Descargando memoria del equipo...');
|
|
203
|
+
|
|
204
|
+
await syncDown(projectRoot);
|
|
205
|
+
|
|
206
|
+
console.log('[COLLAB] ✅ Listo. Ya tienes toda la memoria del equipo.\n');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// ─── INVITE ───────────────────────────────────────────────────────────────────
|
|
210
|
+
|
|
211
|
+
async function collabInvite(projectRoot) {
|
|
212
|
+
const config = loadConfig(projectRoot);
|
|
213
|
+
if (!config?.enabled) {
|
|
214
|
+
console.log('\n[COLLAB] Modo colaborativo no activado. Correr: akdd collab init\n');
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const projectId = config.project_id || getProjectId(projectRoot);
|
|
219
|
+
console.log('\n[COLLAB] Generando código de invitación...');
|
|
220
|
+
|
|
221
|
+
let result;
|
|
222
|
+
try {
|
|
223
|
+
const response = await fetch(`${PROVISIONER_URL}/invite`, {
|
|
224
|
+
method: 'POST',
|
|
225
|
+
headers: { 'Content-Type': 'application/json' },
|
|
226
|
+
body: JSON.stringify({ projectId }),
|
|
227
|
+
});
|
|
228
|
+
result = await response.json();
|
|
229
|
+
} catch (e) {
|
|
230
|
+
console.error('[COLLAB] Error:', e.message);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (!result.ok) {
|
|
235
|
+
console.error('[COLLAB] Error generando código:', result.error);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
console.log('\n' + '═'.repeat(50));
|
|
240
|
+
console.log(' 🔑 Código de invitación Agentic KDD');
|
|
241
|
+
console.log('═'.repeat(50));
|
|
242
|
+
console.log(`\n Código: ${result.code}`);
|
|
243
|
+
console.log(` Expira: ${result.expires_in}`);
|
|
244
|
+
console.log(` Un solo uso — expira al usarse o en 24h`);
|
|
245
|
+
console.log('\n Comparte este código por Slack/WhatsApp.');
|
|
246
|
+
console.log(' El miembro del equipo corre:');
|
|
247
|
+
console.log(`\n akdd collab join ${result.code}\n`);
|
|
248
|
+
console.log('═'.repeat(50) + '\n');
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// ─── SYNC UP (local → Turso) ──────────────────────────────────────────────────
|
|
252
|
+
|
|
253
|
+
async function syncUp(projectRoot) {
|
|
254
|
+
const config = loadConfig(projectRoot);
|
|
255
|
+
if (!config?.enabled) {
|
|
256
|
+
console.log('[COLLAB] Modo colaborativo no activado. Correr: akdd collab init');
|
|
257
|
+
return { synced: false };
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
let client;
|
|
261
|
+
try {
|
|
262
|
+
const { createClient } = require('@libsql/client');
|
|
263
|
+
client = createClient({ url: config.url, authToken: config.token });
|
|
264
|
+
} catch {
|
|
265
|
+
console.log('[COLLAB] @libsql/client no instalado. Correr: npm install @libsql/client');
|
|
266
|
+
return { synced: false };
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const localDB = openLocalDB(projectRoot);
|
|
270
|
+
const lastSync = config.last_sync || '1970-01-01T00:00:00.000Z';
|
|
271
|
+
let totalRows = 0;
|
|
272
|
+
|
|
273
|
+
// Schema push — crear tablas en remoto si no existen
|
|
274
|
+
await pushSchema(client, localDB);
|
|
275
|
+
|
|
276
|
+
for (const table of SYNC_TABLES) {
|
|
277
|
+
try {
|
|
278
|
+
// Solo filas nuevas/modificadas desde el último sync
|
|
279
|
+
const dateField = getDateField(table);
|
|
280
|
+
const rows = localDB.prepare(
|
|
281
|
+
`SELECT * FROM ${table} WHERE ${dateField} > ? LIMIT 500`
|
|
282
|
+
).all(lastSync);
|
|
283
|
+
|
|
284
|
+
if (rows.length === 0) continue;
|
|
285
|
+
|
|
286
|
+
console.log(`[COLLAB] ${table}: ${rows.length} filas a sincronizar`);
|
|
287
|
+
|
|
288
|
+
for (const row of rows) {
|
|
289
|
+
const keys = Object.keys(row);
|
|
290
|
+
const vals = Object.values(row);
|
|
291
|
+
const placeholders = keys.map(() => '?').join(', ');
|
|
292
|
+
try {
|
|
293
|
+
await client.execute({
|
|
294
|
+
sql: `INSERT OR REPLACE INTO ${table} (${keys.join(', ')}) VALUES (${placeholders})`,
|
|
295
|
+
args: vals,
|
|
296
|
+
});
|
|
297
|
+
totalRows++;
|
|
298
|
+
} catch {}
|
|
299
|
+
}
|
|
300
|
+
} catch {}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Actualizar last_sync
|
|
304
|
+
config.last_sync = new Date().toISOString();
|
|
305
|
+
saveConfig(projectRoot, config);
|
|
306
|
+
|
|
307
|
+
console.log(`[COLLAB] ✅ Sync up: ${totalRows} filas enviadas.`);
|
|
308
|
+
return { synced: true, rows: totalRows };
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// ─── SYNC DOWN (Turso → local) ────────────────────────────────────────────────
|
|
312
|
+
|
|
313
|
+
async function syncDown(projectRoot) {
|
|
314
|
+
const config = loadConfig(projectRoot);
|
|
315
|
+
if (!config?.enabled) return { synced: false };
|
|
316
|
+
|
|
317
|
+
let client;
|
|
318
|
+
try {
|
|
319
|
+
const { createClient } = require('@libsql/client');
|
|
320
|
+
client = createClient({ url: config.url, authToken: config.token });
|
|
321
|
+
} catch {
|
|
322
|
+
console.log('[COLLAB] @libsql/client no instalado. Correr: npm install @libsql/client');
|
|
323
|
+
return { synced: false };
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const localDB = openLocalDB(projectRoot);
|
|
327
|
+
let totalRows = 0;
|
|
328
|
+
|
|
329
|
+
for (const table of SYNC_TABLES) {
|
|
330
|
+
try {
|
|
331
|
+
const dateField = getDateField(table);
|
|
332
|
+
const lastSync = config.last_sync || '1970-01-01T00:00:00.000Z';
|
|
333
|
+
|
|
334
|
+
const rs = await client.execute(
|
|
335
|
+
`SELECT * FROM ${table} WHERE ${dateField} > ? LIMIT 1000`,
|
|
336
|
+
[lastSync]
|
|
337
|
+
);
|
|
338
|
+
|
|
339
|
+
if (!rs.rows?.length) continue;
|
|
340
|
+
|
|
341
|
+
console.log(`[COLLAB] ${table}: recibiendo ${rs.rows.length} filas`);
|
|
342
|
+
|
|
343
|
+
for (const row of rs.rows) {
|
|
344
|
+
const keys = Object.keys(row);
|
|
345
|
+
const vals = keys.map(k => row[k]);
|
|
346
|
+
const placeholders = keys.map(() => '?').join(', ');
|
|
347
|
+
|
|
348
|
+
// Estrategia por tabla:
|
|
349
|
+
// episodios → INSERT OR IGNORE (son aditivos, no sobrescribir locales)
|
|
350
|
+
// resto → INSERT OR REPLACE (toma la versión más reciente)
|
|
351
|
+
const strategy = table === 'episodios' ? 'OR IGNORE' : 'OR REPLACE';
|
|
352
|
+
|
|
353
|
+
try {
|
|
354
|
+
localDB.prepare(
|
|
355
|
+
`INSERT ${strategy} INTO ${table} (${keys.join(', ')}) VALUES (${placeholders})`
|
|
356
|
+
).run(vals);
|
|
357
|
+
totalRows++;
|
|
358
|
+
} catch {}
|
|
359
|
+
}
|
|
360
|
+
} catch {}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Actualizar last_sync
|
|
364
|
+
config.last_sync = new Date().toISOString();
|
|
365
|
+
saveConfig(projectRoot, config);
|
|
366
|
+
|
|
367
|
+
console.log(`[COLLAB] ✅ Sync down: ${totalRows} filas recibidas.`);
|
|
368
|
+
return { synced: true, rows: totalRows };
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// ─── SCHEMA PUSH ──────────────────────────────────────────────────────────────
|
|
372
|
+
// Crea las tablas en la DB remota si no existen (primera vez)
|
|
373
|
+
|
|
374
|
+
async function pushSchema(client, localDB) {
|
|
375
|
+
const tables = localDB.prepare(
|
|
376
|
+
"SELECT name, sql FROM sqlite_master WHERE type='table' AND sql IS NOT NULL"
|
|
377
|
+
).all();
|
|
378
|
+
|
|
379
|
+
for (const table of tables) {
|
|
380
|
+
if (!SYNC_TABLES.includes(table.name)) continue;
|
|
381
|
+
try {
|
|
382
|
+
await client.execute(
|
|
383
|
+
table.sql.replace('CREATE TABLE', 'CREATE TABLE IF NOT EXISTS')
|
|
384
|
+
);
|
|
385
|
+
} catch {}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// ─── STATUS ───────────────────────────────────────────────────────────────────
|
|
390
|
+
|
|
391
|
+
async function status(projectRoot) {
|
|
392
|
+
const config = loadConfig(projectRoot);
|
|
393
|
+
|
|
394
|
+
console.log('\n[COLLAB] Estado del modo colaborativo:\n');
|
|
395
|
+
|
|
396
|
+
if (!config?.enabled) {
|
|
397
|
+
console.log(' ❌ No activado.');
|
|
398
|
+
console.log(' Correr: akdd collab init\n');
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
console.log(` ✅ Activado`);
|
|
403
|
+
console.log(` DB: ${config.db || config.url}`);
|
|
404
|
+
console.log(` Miembro: ${config.member_id}`);
|
|
405
|
+
console.log(` Último sync: ${config.last_sync || 'nunca'}`);
|
|
406
|
+
console.log(` Auto-sync: ${config.sync_on_cycle_end ? 'sí (al final de cada aa:)' : 'no'}`);
|
|
407
|
+
|
|
408
|
+
// Test de conexión
|
|
409
|
+
try {
|
|
410
|
+
const { createClient } = require('@libsql/client');
|
|
411
|
+
const client = createClient({ url: config.url, authToken: config.token });
|
|
412
|
+
await client.execute('SELECT 1');
|
|
413
|
+
console.log(' Conexión: ✅ OK\n');
|
|
414
|
+
} catch {
|
|
415
|
+
console.log(' Conexión: ❌ Error — verificar credenciales\n');
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// ─── HELPER ───────────────────────────────────────────────────────────────────
|
|
420
|
+
|
|
421
|
+
function getDateField(table) {
|
|
422
|
+
const fields = {
|
|
423
|
+
nodos: 'fecha_update',
|
|
424
|
+
episodios: 'fecha',
|
|
425
|
+
relaciones_semanticas: 'valid_at',
|
|
426
|
+
knowledge_docs: 'last_indexed',
|
|
427
|
+
ast_symbols: 'last_indexed',
|
|
428
|
+
ast_edges: 'last_indexed',
|
|
429
|
+
};
|
|
430
|
+
return fields[table] || 'rowid';
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// ─── CLI ──────────────────────────────────────────────────────────────────────
|
|
434
|
+
|
|
435
|
+
if (require.main === module) {
|
|
436
|
+
const [,, cmd, arg1, arg2] = process.argv;
|
|
437
|
+
const projectRoot = process.cwd();
|
|
438
|
+
|
|
439
|
+
switch (cmd) {
|
|
440
|
+
case 'init':
|
|
441
|
+
collabInit(projectRoot).catch(console.error);
|
|
442
|
+
break;
|
|
443
|
+
case 'invite':
|
|
444
|
+
collabInvite(projectRoot).catch(console.error);
|
|
445
|
+
break;
|
|
446
|
+
case 'join':
|
|
447
|
+
if (!arg1) {
|
|
448
|
+
console.error('Uso: collab-manager.cjs join <código>');
|
|
449
|
+
console.error(' o: collab-manager.cjs join <url> <token>');
|
|
450
|
+
process.exit(1);
|
|
451
|
+
}
|
|
452
|
+
collabJoin(projectRoot, arg1, arg2).catch(console.error);
|
|
453
|
+
break;
|
|
454
|
+
case 'push':
|
|
455
|
+
syncUp(projectRoot).then(r => {
|
|
456
|
+
process.exit(r.synced ? 0 : 1);
|
|
457
|
+
}).catch(console.error);
|
|
458
|
+
break;
|
|
459
|
+
case 'pull':
|
|
460
|
+
syncDown(projectRoot).then(r => {
|
|
461
|
+
process.exit(r.synced ? 0 : 1);
|
|
462
|
+
}).catch(console.error);
|
|
463
|
+
break;
|
|
464
|
+
case 'status':
|
|
465
|
+
status(projectRoot).catch(console.error);
|
|
466
|
+
break;
|
|
467
|
+
default:
|
|
468
|
+
console.log('Uso: node collab-manager.cjs [init | join <url> <token> | push | pull | status]');
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
module.exports = {
|
|
473
|
+
collabInit,
|
|
474
|
+
collabJoin,
|
|
475
|
+
collabInvite,
|
|
476
|
+
syncUp,
|
|
477
|
+
syncDown,
|
|
478
|
+
status,
|
|
479
|
+
loadConfig,
|
|
480
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentic-kdd",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.4",
|
|
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": {
|
|
@@ -44,7 +44,8 @@
|
|
|
44
44
|
"chalk": "^4.1.2",
|
|
45
45
|
"ora": "^5.4.1",
|
|
46
46
|
"inquirer": "^8.2.6",
|
|
47
|
-
"sql.js": "^1.10.3"
|
|
47
|
+
"sql.js": "^1.10.3",
|
|
48
|
+
"@libsql/client": "^0.14.0"
|
|
48
49
|
},
|
|
49
50
|
"optionalDependencies": {
|
|
50
51
|
"better-sqlite3": "^9.4.3"
|