refacil-sdd-ai 3.0.3 → 4.0.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 +402 -392
- package/bin/cli.js +81 -1217
- package/lib/commands/bus.js +499 -0
- package/lib/commands/compact.js +92 -0
- package/lib/hooks.js +107 -0
- package/lib/ignore-files.js +88 -0
- package/lib/installer.js +265 -0
- package/package.json +6 -2
- package/refacil-bus-diagrams.md +314 -0
- package/skills/archive/SKILL.md +191 -167
- package/skills/guide/SKILL.md +4 -2
- package/skills/setup/SKILL.md +123 -91
- package/skills/update/SKILL.md +81 -0
- package/templates/methodology-guide.md +52 -45
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
const BASE_ENTRIES = [
|
|
7
|
+
'# Dependencias',
|
|
8
|
+
'node_modules/',
|
|
9
|
+
'',
|
|
10
|
+
'# Build/dist',
|
|
11
|
+
'dist/',
|
|
12
|
+
'build/',
|
|
13
|
+
'.next/',
|
|
14
|
+
'out/',
|
|
15
|
+
'',
|
|
16
|
+
'# Logs',
|
|
17
|
+
'*.log',
|
|
18
|
+
'logs/',
|
|
19
|
+
'npm-debug.log*',
|
|
20
|
+
'',
|
|
21
|
+
'# Cache y temporales',
|
|
22
|
+
'.cache/',
|
|
23
|
+
'tmp/',
|
|
24
|
+
'temp/',
|
|
25
|
+
'.parcel-cache/',
|
|
26
|
+
'.turbo/',
|
|
27
|
+
'',
|
|
28
|
+
'# Binarios y medios pesados',
|
|
29
|
+
'*.png',
|
|
30
|
+
'*.jpg',
|
|
31
|
+
'*.jpeg',
|
|
32
|
+
'*.gif',
|
|
33
|
+
'*.svg',
|
|
34
|
+
'*.pdf',
|
|
35
|
+
'*.zip',
|
|
36
|
+
'*.tar.gz',
|
|
37
|
+
'*.mp4',
|
|
38
|
+
'*.mp3',
|
|
39
|
+
'',
|
|
40
|
+
'# Secretos y credenciales',
|
|
41
|
+
'.env',
|
|
42
|
+
'.env.*',
|
|
43
|
+
'*.pem',
|
|
44
|
+
'*.key',
|
|
45
|
+
'credentials.json',
|
|
46
|
+
'secrets.json',
|
|
47
|
+
'',
|
|
48
|
+
'# Cobertura y reportes',
|
|
49
|
+
'coverage/',
|
|
50
|
+
'.nyc_output/',
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
function isSignificant(line) {
|
|
54
|
+
const trimmed = line.trim();
|
|
55
|
+
return trimmed.length > 0 && !trimmed.startsWith('#');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function syncIgnoreFile(filePath) {
|
|
59
|
+
const significantBase = BASE_ENTRIES.filter(isSignificant);
|
|
60
|
+
|
|
61
|
+
if (!fs.existsSync(filePath)) {
|
|
62
|
+
fs.writeFileSync(filePath, BASE_ENTRIES.join('\n') + '\n');
|
|
63
|
+
return { status: 'created', added: significantBase.length };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const existing = fs.readFileSync(filePath, 'utf8');
|
|
67
|
+
const existingLines = existing.split('\n').map((l) => l.trim());
|
|
68
|
+
|
|
69
|
+
const missing = BASE_ENTRIES.filter(
|
|
70
|
+
(entry) => isSignificant(entry) && !existingLines.includes(entry.trim()),
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
if (missing.length === 0) {
|
|
74
|
+
return { status: 'noop', added: 0 };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const separator = existing.endsWith('\n') ? '' : '\n';
|
|
78
|
+
fs.writeFileSync(filePath, existing + separator + missing.join('\n') + '\n');
|
|
79
|
+
return { status: 'updated', added: missing.length };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function syncIgnoreFiles(projectRoot) {
|
|
83
|
+
const claude = syncIgnoreFile(path.join(projectRoot, '.claudeignore'));
|
|
84
|
+
const cursor = syncIgnoreFile(path.join(projectRoot, '.cursorignore'));
|
|
85
|
+
return { claude, cursor };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
module.exports = { syncIgnoreFiles, BASE_ENTRIES };
|
package/lib/installer.js
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
const SKILLS = [
|
|
7
|
+
'setup',
|
|
8
|
+
'prereqs',
|
|
9
|
+
'guide',
|
|
10
|
+
'explore',
|
|
11
|
+
'propose',
|
|
12
|
+
'apply',
|
|
13
|
+
'test',
|
|
14
|
+
'verify',
|
|
15
|
+
'review',
|
|
16
|
+
'archive',
|
|
17
|
+
'bug',
|
|
18
|
+
'up-code',
|
|
19
|
+
'join',
|
|
20
|
+
'say',
|
|
21
|
+
'ask',
|
|
22
|
+
'reply',
|
|
23
|
+
'inbox',
|
|
24
|
+
'attend',
|
|
25
|
+
'update',
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
const AGENTS = [
|
|
29
|
+
'auditor',
|
|
30
|
+
'investigator',
|
|
31
|
+
'validator',
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
const REPO_VERSION_FILES = ['.claude/.sdd-version', '.cursor/.sdd-version'];
|
|
35
|
+
|
|
36
|
+
function copyDir(src, dest) {
|
|
37
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
38
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
39
|
+
for (const entry of entries) {
|
|
40
|
+
const srcPath = path.join(src, entry.name);
|
|
41
|
+
const destPath = path.join(dest, entry.name);
|
|
42
|
+
if (entry.isDirectory()) {
|
|
43
|
+
copyDir(srcPath, destPath);
|
|
44
|
+
} else {
|
|
45
|
+
fs.copyFileSync(srcPath, destPath);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function installSkills(packageRoot, projectRoot) {
|
|
51
|
+
let installed = 0;
|
|
52
|
+
for (const skill of SKILLS) {
|
|
53
|
+
const srcDir = path.join(packageRoot, 'skills', skill);
|
|
54
|
+
if (!fs.existsSync(srcDir)) continue;
|
|
55
|
+
|
|
56
|
+
const claudeDest = path.join(projectRoot, '.claude', 'skills', `refacil-${skill}`);
|
|
57
|
+
copyDir(srcDir, claudeDest);
|
|
58
|
+
|
|
59
|
+
const cursorDest = path.join(projectRoot, '.cursor', 'skills', `refacil-${skill}`);
|
|
60
|
+
copyDir(srcDir, cursorDest);
|
|
61
|
+
|
|
62
|
+
installed++;
|
|
63
|
+
}
|
|
64
|
+
return installed;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Claude Code: tools allowlist granular, model: sonnet|opus|haiku
|
|
68
|
+
// Cursor: readonly: true|false (booleano), model: inherit (default)
|
|
69
|
+
function transformFrontmatterForCursor(content) {
|
|
70
|
+
const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
71
|
+
if (!match) return content;
|
|
72
|
+
|
|
73
|
+
const [, frontmatterRaw, body] = match;
|
|
74
|
+
const lines = frontmatterRaw.split('\n');
|
|
75
|
+
const out = [];
|
|
76
|
+
let toolsLine = null;
|
|
77
|
+
let hasReadonly = false;
|
|
78
|
+
|
|
79
|
+
for (const line of lines) {
|
|
80
|
+
if (line.startsWith('tools:')) {
|
|
81
|
+
toolsLine = line;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (line.startsWith('readonly:')) {
|
|
85
|
+
hasReadonly = true;
|
|
86
|
+
out.push(line);
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (line.startsWith('model:')) {
|
|
90
|
+
const value = line.slice('model:'.length).trim();
|
|
91
|
+
if (value === 'sonnet' || value === 'opus' || value === 'haiku') {
|
|
92
|
+
out.push('model: inherit');
|
|
93
|
+
} else {
|
|
94
|
+
out.push(line);
|
|
95
|
+
}
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
out.push(line);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (toolsLine && !hasReadonly) {
|
|
102
|
+
const toolsList = toolsLine.slice('tools:'.length).trim();
|
|
103
|
+
const canWrite = /\b(Edit|Write|NotebookEdit)\b/.test(toolsList);
|
|
104
|
+
out.push(`readonly: ${canWrite ? 'false' : 'true'}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return `---\n${out.join('\n')}\n---\n${body}`;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function installAgents(packageRoot, projectRoot) {
|
|
111
|
+
let installed = 0;
|
|
112
|
+
|
|
113
|
+
const claudeDir = path.join(projectRoot, '.claude', 'agents');
|
|
114
|
+
const cursorDir = path.join(projectRoot, '.cursor', 'agents');
|
|
115
|
+
fs.mkdirSync(claudeDir, { recursive: true });
|
|
116
|
+
fs.mkdirSync(cursorDir, { recursive: true });
|
|
117
|
+
|
|
118
|
+
for (const agent of AGENTS) {
|
|
119
|
+
const srcFile = path.join(packageRoot, 'agents', `${agent}.md`);
|
|
120
|
+
if (!fs.existsSync(srcFile)) continue;
|
|
121
|
+
|
|
122
|
+
const content = fs.readFileSync(srcFile, 'utf8');
|
|
123
|
+
|
|
124
|
+
fs.writeFileSync(path.join(claudeDir, `refacil-${agent}.md`), content);
|
|
125
|
+
fs.writeFileSync(
|
|
126
|
+
path.join(cursorDir, `refacil-${agent}.md`),
|
|
127
|
+
transformFrontmatterForCursor(content),
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
installed++;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return installed;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function writeGuideFile(destPath, header, label) {
|
|
137
|
+
const content =
|
|
138
|
+
`# ${header}\n\n` +
|
|
139
|
+
'Contexto completo del proyecto: ver `AGENTS.md`.\n' +
|
|
140
|
+
'Si no existe, ejecuta `/refacil:setup`.\n';
|
|
141
|
+
fs.writeFileSync(destPath, content);
|
|
142
|
+
console.log(` ${label} generado.`);
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function createClaudeMd(packageRoot, projectRoot) {
|
|
147
|
+
return writeGuideFile(
|
|
148
|
+
path.join(projectRoot, 'CLAUDE.md'),
|
|
149
|
+
'CLAUDE.md',
|
|
150
|
+
'CLAUDE.md',
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function createCursorRules(packageRoot, projectRoot) {
|
|
155
|
+
return writeGuideFile(
|
|
156
|
+
path.join(projectRoot, '.cursorrules'),
|
|
157
|
+
'Cursor Rules',
|
|
158
|
+
'.cursorrules',
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function readRepoVersion(rootDir) {
|
|
163
|
+
for (const rel of REPO_VERSION_FILES) {
|
|
164
|
+
const p = path.join(rootDir, rel);
|
|
165
|
+
try {
|
|
166
|
+
const raw = fs.readFileSync(p, 'utf8').trim();
|
|
167
|
+
if (raw) return raw;
|
|
168
|
+
} catch (_) {
|
|
169
|
+
// siguiente
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function writeRepoVersion(rootDir, version) {
|
|
176
|
+
for (const rel of REPO_VERSION_FILES) {
|
|
177
|
+
const p = path.join(rootDir, rel);
|
|
178
|
+
const parent = path.dirname(p);
|
|
179
|
+
if (!fs.existsSync(parent)) continue;
|
|
180
|
+
try {
|
|
181
|
+
fs.writeFileSync(p, String(version) + '\n');
|
|
182
|
+
} catch (_) {
|
|
183
|
+
// tolerante
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function getPackageVersion(packageRoot) {
|
|
189
|
+
try {
|
|
190
|
+
return require(path.join(packageRoot, 'package.json')).version;
|
|
191
|
+
} catch (_) {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function removeSkills(projectRoot) {
|
|
197
|
+
let removed = 0;
|
|
198
|
+
for (const skill of SKILLS) {
|
|
199
|
+
const claudeDir = path.join(projectRoot, '.claude', 'skills', `refacil-${skill}`);
|
|
200
|
+
const cursorDir = path.join(projectRoot, '.cursor', 'skills', `refacil-${skill}`);
|
|
201
|
+
|
|
202
|
+
if (fs.existsSync(claudeDir)) {
|
|
203
|
+
fs.rmSync(claudeDir, { recursive: true });
|
|
204
|
+
removed++;
|
|
205
|
+
}
|
|
206
|
+
if (fs.existsSync(cursorDir)) {
|
|
207
|
+
fs.rmSync(cursorDir, { recursive: true });
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return removed;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function checkClaudeCodeVersion() {
|
|
214
|
+
const { execSync } = require('child_process');
|
|
215
|
+
try {
|
|
216
|
+
const output = execSync('claude --version 2>&1', {
|
|
217
|
+
encoding: 'utf8',
|
|
218
|
+
timeout: 5000,
|
|
219
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
220
|
+
}).trim();
|
|
221
|
+
const match = output.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
222
|
+
if (!match) return { ok: null, version: null };
|
|
223
|
+
const maj = Number(match[1]);
|
|
224
|
+
const min = Number(match[2]);
|
|
225
|
+
const patch = Number(match[3]);
|
|
226
|
+
const ok =
|
|
227
|
+
maj > 2 ||
|
|
228
|
+
(maj === 2 && min > 1) ||
|
|
229
|
+
(maj === 2 && min === 1 && patch >= 89);
|
|
230
|
+
return { ok, version: `${maj}.${min}.${patch}` };
|
|
231
|
+
} catch (_) {
|
|
232
|
+
return { ok: null, version: null };
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function checkNodeVersion() {
|
|
237
|
+
const version = process.version;
|
|
238
|
+
const major = parseInt(version.split('.')[0].replace('v', ''));
|
|
239
|
+
const minor = parseInt(version.split('.')[1]);
|
|
240
|
+
|
|
241
|
+
if (major < 20 || (major === 20 && minor < 19)) {
|
|
242
|
+
console.log(`\n ADVERTENCIA: Node.js ${version} detectado.`);
|
|
243
|
+
console.log(' OpenSpec requiere Node.js >= 20.19.0.');
|
|
244
|
+
console.log(' Las skills se instalaran pero /refacil:setup podria fallar al instalar OpenSpec.\n');
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
module.exports = {
|
|
251
|
+
SKILLS,
|
|
252
|
+
AGENTS,
|
|
253
|
+
copyDir,
|
|
254
|
+
installSkills,
|
|
255
|
+
transformFrontmatterForCursor,
|
|
256
|
+
installAgents,
|
|
257
|
+
createClaudeMd,
|
|
258
|
+
createCursorRules,
|
|
259
|
+
readRepoVersion,
|
|
260
|
+
writeRepoVersion,
|
|
261
|
+
getPackageVersion,
|
|
262
|
+
removeSkills,
|
|
263
|
+
checkClaudeCodeVersion,
|
|
264
|
+
checkNodeVersion,
|
|
265
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "refacil-sdd-ai",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "SDD-AI: Specification-Driven Development with AI — metodologia de desarrollo con IA usando OpenSpec, Claude Code y Cursor",
|
|
5
5
|
"bin": {
|
|
6
6
|
"refacil-sdd-ai": "./bin/cli.js"
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
"agents/",
|
|
13
13
|
"templates/",
|
|
14
14
|
"config/",
|
|
15
|
-
"README.md"
|
|
15
|
+
"README.md",
|
|
16
|
+
"refacil-bus-diagrams.md"
|
|
16
17
|
],
|
|
17
18
|
"keywords": [
|
|
18
19
|
"ai",
|
|
@@ -35,6 +36,9 @@
|
|
|
35
36
|
"engines": {
|
|
36
37
|
"node": ">=20.19.0"
|
|
37
38
|
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"test": "node --test test/hooks.test.js test/installer.test.js test/ignore-files.test.js"
|
|
41
|
+
},
|
|
38
42
|
"dependencies": {
|
|
39
43
|
"ws": "^8.18.0"
|
|
40
44
|
}
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
# refacil-bus — Diagramas y explicación
|
|
2
|
+
|
|
3
|
+
Documento para presentar la feature al equipo. Usa Mermaid (se renderiza automático en GitHub, GitLab, Notion, Confluence, Teams, VS Code preview). Para exportar a imagen: https://mermaid.live
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. El problema que resolvemos
|
|
8
|
+
|
|
9
|
+
Un único dev trabaja con varios repos abiertos al mismo tiempo — **una ventana de Claude Code o Cursor por repo** (microservicios + frontend + utilidades). Cada ventana tiene SU propio agente del LLM que solo conoce ese repo. Cuando el dev está trabajando en un repo y necesita contexto de otro, hoy tiene que:
|
|
10
|
+
|
|
11
|
+
- pausar lo que está haciendo
|
|
12
|
+
- **saltar a la ventana del otro repo**
|
|
13
|
+
- leer código él mismo, o preguntarle al LLM de esa otra ventana
|
|
14
|
+
- copiar la respuesta manualmente
|
|
15
|
+
- volver a la ventana original y pegar/explicar al primer agente
|
|
16
|
+
|
|
17
|
+
El resultado: **el dev queda de mensajero entre sus propios agentes**. Contexto se pierde, se transcribe mal, y los bugs cruzados aparecen en QA.
|
|
18
|
+
|
|
19
|
+
> **Clave del caso de uso**: las "sesiones" en el bus NO son devs distintos — son los **agentes de LLM** de cada repo que el mismo dev tiene abiertos. El bus es para que esos agentes se hablen entre sí sin que el dev sea el intermediario.
|
|
20
|
+
|
|
21
|
+
### Antes de refacil-bus
|
|
22
|
+
|
|
23
|
+
```mermaid
|
|
24
|
+
flowchart LR
|
|
25
|
+
D[Dev en ventana<br/>de payments-api] --> A[Pide al agente:<br/>crea endpoint<br/>POST /refund]
|
|
26
|
+
A --> B{¿El agente sabe cómo<br/>lo consume el front?}
|
|
27
|
+
B -->|No| C[Dev salta a la<br/>ventana del frontend]
|
|
28
|
+
C --> E[Lee código del front<br/>o pregunta al agente<br/>de esa ventana]
|
|
29
|
+
E --> F[Copia la respuesta<br/>mentalmente o literal]
|
|
30
|
+
F --> G[Vuelve a la ventana<br/>de payments-api]
|
|
31
|
+
G --> H[Pega/explica al<br/>agente de payments]
|
|
32
|
+
H --> I[Agente implementa<br/>con info transcrita]
|
|
33
|
+
B -->|Arriesga| J[Agente implementa<br/>según suposición]
|
|
34
|
+
J --> K[Bug en QA —<br/>campo mal nombrado]
|
|
35
|
+
|
|
36
|
+
classDef dev fill:#e3f2fd,stroke:#1976d2,color:#333
|
|
37
|
+
classDef manual fill:#ffe5b3,stroke:#e17055,color:#333
|
|
38
|
+
classDef pain fill:#ffe5e5,stroke:#d63031,color:#333
|
|
39
|
+
class D dev
|
|
40
|
+
class C,E,F,G,H manual
|
|
41
|
+
class B,J,K pain
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
El dev **termina siendo el bus humano** entre sus propios agentes.
|
|
45
|
+
|
|
46
|
+
### Después de refacil-bus — Escenario A: el otro agente está en `/refacil:attend`
|
|
47
|
+
|
|
48
|
+
El caso ideal. El dev dejó el agente del otro repo en modo escucha antes de empezar a trabajar en el primero.
|
|
49
|
+
|
|
50
|
+
```mermaid
|
|
51
|
+
flowchart LR
|
|
52
|
+
D[Dev en ventana<br/>de payments-api] --> A[Pide al agente:<br/>crea /refund,<br/>pregúntale al bus<br/>si necesitás algo]
|
|
53
|
+
A --> B[Agente payments ejecuta<br/>/refacil:ask @frontend<br/>"..." --wait 180]
|
|
54
|
+
B --> C[Agente frontend<br/>en /refacil:attend<br/>recibe la pregunta]
|
|
55
|
+
C --> E[Agente frontend lee<br/>SUS propios archivos<br/>y responde con<br/>/refacil:reply]
|
|
56
|
+
E --> F[Agente payments recibe<br/>la respuesta automática<br/>y sigue implementando]
|
|
57
|
+
F --> G[Endpoint correcto<br/>al primer intento]
|
|
58
|
+
|
|
59
|
+
classDef dev fill:#e3f2fd,stroke:#1976d2,color:#333
|
|
60
|
+
classDef auto fill:#d4edda,stroke:#28a745,color:#333
|
|
61
|
+
class D dev
|
|
62
|
+
class A,B,C,E,F,G auto
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**El dev nunca cambió de ventana**. Los dos agentes se hablaron entre sí; cada uno consultó su código real. El dev se enteró solo cuando el primer agente le dijo "listo, endpoint implementado con estos campos".
|
|
66
|
+
|
|
67
|
+
### Después de refacil-bus — Escenario B: el otro agente NO está en attend
|
|
68
|
+
|
|
69
|
+
Aún hay ganancia, pero el dev sí tendrá que ir a la otra ventana al menos una vez — aunque con menos esfuerzo que antes.
|
|
70
|
+
|
|
71
|
+
```mermaid
|
|
72
|
+
flowchart LR
|
|
73
|
+
D[Dev en ventana<br/>de payments-api] --> A[Pide al agente:<br/>pregúntale al front...]
|
|
74
|
+
A --> B[Agente payments ejecuta<br/>/refacil:ask @frontend "..."]
|
|
75
|
+
B --> C[Mensaje queda en inbox.jsonl<br/>de la sala]
|
|
76
|
+
C --> E[Dev, cuando pueda,<br/>va a la ventana frontend]
|
|
77
|
+
E --> F[Dice al agente frontend:<br/>"ejecutá /refacil:inbox<br/>y respondé lo que haya"]
|
|
78
|
+
F --> G[Agente frontend lee SUS<br/>archivos y responde con<br/>/refacil:reply]
|
|
79
|
+
G --> H[Dev vuelve a payments;<br/>agente payments hace<br/>/refacil:inbox y continúa]
|
|
80
|
+
|
|
81
|
+
classDef dev fill:#e3f2fd,stroke:#1976d2,color:#333
|
|
82
|
+
classDef semi fill:#fff3cd,stroke:#ffc107,color:#333
|
|
83
|
+
class D,E dev
|
|
84
|
+
class A,B,C,F,G,H semi
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Ganancia aún sin attend**: el dev sigue saltando, **pero NO hace de transcriptor**. El agente de cada repo responde desde su código real; el dev solo "despacha" el trabajo entre agentes. Mucho menos fricción que copiar respuestas a mano.
|
|
88
|
+
|
|
89
|
+
> **Conclusión práctica**: la magia del "cero saltos de ventana" aparece cuando el dev, antes de meterse en una tarea profunda, pone los agentes de los otros repos que podría necesitar consultar en `/refacil:attend`. Sin eso, el bus sigue siendo útil — evita la transcripción manual — pero el dev sí visita las otras ventanas para "activar" al agente.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 2. Arquitectura — qué corre dónde
|
|
94
|
+
|
|
95
|
+
El broker es un proceso local mínimo que solo enruta texto plano entre sesiones. **No transfiere archivos, contexto ni tokens entre repos**. Todas las ventanas son del mismo dev — el broker conecta a sus agentes entre sí.
|
|
96
|
+
|
|
97
|
+
```mermaid
|
|
98
|
+
flowchart TB
|
|
99
|
+
subgraph DEV[Máquina del Dev]
|
|
100
|
+
subgraph V1[Ventana IDE 1]
|
|
101
|
+
IDE1[Claude Code / Cursor<br/>en repo payments-api<br/>agente: payments-api]
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
subgraph V2[Ventana IDE 2]
|
|
105
|
+
IDE2[Claude Code / Cursor<br/>en repo frontend<br/>agente: frontend]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
subgraph V3[Ventana IDE 3 · opcional]
|
|
109
|
+
IDE3[Claude Code / Cursor<br/>en repo reports<br/>agente: reports]
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
subgraph T4[Terminal · opcional]
|
|
113
|
+
WATCH[refacil-sdd-ai bus view<br/>o bus watch<br/>0 tokens]
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
subgraph BROKER[Broker local · 127.0.0.1:7821]
|
|
117
|
+
WS[HTTP + WebSocket<br/>40 MB RAM · 0% CPU idle]
|
|
118
|
+
DISK[~/.refacil-sdd-ai/bus/<sala>/<br/>inbox.jsonl<br/>rotación 7 días]
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
IDE1 <-->|ws| WS
|
|
123
|
+
IDE2 <-->|ws| WS
|
|
124
|
+
IDE3 <-->|ws| WS
|
|
125
|
+
WATCH -.->|http + ws read-only| WS
|
|
126
|
+
WS <--> DISK
|
|
127
|
+
|
|
128
|
+
classDef local fill:#e3f2fd,stroke:#1976d2,color:#333
|
|
129
|
+
classDef ide fill:#f3e5f5,stroke:#7b1fa2,color:#333
|
|
130
|
+
class WS,DISK local
|
|
131
|
+
class IDE1,IDE2,IDE3,WATCH ide
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Propiedades importantes
|
|
135
|
+
|
|
136
|
+
- **100% local**: nada sale de `127.0.0.1`. Sin internet, sin cuentas, sin servidor compartido.
|
|
137
|
+
- **Zero config**: el broker se auto-arranca la primera vez que cualquier skill del bus lo necesita.
|
|
138
|
+
- **Zero costo perceptible**: 40 MB RAM, 0% CPU cuando no hay tráfico.
|
|
139
|
+
- **Persistente**: sobrevive reinicio del broker; mensajes quedan 7 días en disco.
|
|
140
|
+
- **Portable**: las mismas skills funcionan idéntico en Claude Code y Cursor (no usa hooks específicos de un IDE).
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## 3. Flujo automático agente ↔ agente (happy path)
|
|
145
|
+
|
|
146
|
+
Caso de uso estrella: el dev está trabajando en un repo y sus otros agentes (en otras ventanas) responden solos cuando reciben preguntas vía el bus.
|
|
147
|
+
|
|
148
|
+
> **Pre-requisito**: antes de arrancar la tarea, el dev fue a la ventana del otro repo y dijo *"atiende el bus"*. Eso pone al agente de esa ventana en `/refacil:attend`. Sin eso, el flujo cae al Escenario B (sección 3.2).
|
|
149
|
+
|
|
150
|
+
### 3.1 Con attend activo — cero saltos de ventana
|
|
151
|
+
|
|
152
|
+
```mermaid
|
|
153
|
+
sequenceDiagram
|
|
154
|
+
autonumber
|
|
155
|
+
participant D as Dev<br/>(único)
|
|
156
|
+
participant AP as Agente<br/>payments
|
|
157
|
+
participant BR as Broker
|
|
158
|
+
participant AF as Agente<br/>frontend
|
|
159
|
+
|
|
160
|
+
Note over D,AF: Setup previo (1 vez):<br/>Dev fue a la ventana frontend y dijo "atiende el bus"
|
|
161
|
+
D->>AF: "atiende el bus<br/>mientras trabajo en otro repo"
|
|
162
|
+
AF->>BR: /refacil:attend (bloqueado)
|
|
163
|
+
Note over AF: Escuchando preguntas
|
|
164
|
+
|
|
165
|
+
Note over D,AP: Dev vuelve a la ventana payments y arranca la tarea
|
|
166
|
+
D->>AP: "crea /refund,<br/>si necesitás algo del front<br/>preguntale al bus"
|
|
167
|
+
AP->>BR: /refacil:ask @frontend<br/>"..." --wait 180
|
|
168
|
+
Note over AP: Bloqueado<br/>esperando respuesta
|
|
169
|
+
|
|
170
|
+
BR->>AF: Pregunta recibida<br/>(correlationId X)
|
|
171
|
+
AF->>AF: Read/Grep src/api/pay.ts
|
|
172
|
+
AF->>BR: /refacil:reply<br/>"cartId, amountRefunded,<br/>timestamp (camelCase)"
|
|
173
|
+
BR->>AP: Respuesta con<br/>correlationId X
|
|
174
|
+
|
|
175
|
+
AP->>D: Endpoint implementado<br/>con los campos correctos
|
|
176
|
+
AF->>BR: /refacil:attend<br/>(vuelve a escuchar)
|
|
177
|
+
Note over AF: Listo para la<br/>siguiente pregunta
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**Leyendo el diagrama**: el Dev solo participa en los pasos 1, 4 y 12. Entre medio (pasos 5 al 11) los dos agentes se hablaron entre sí — el dev no cambió de ventana, no copió nada, no transcribió respuestas.
|
|
181
|
+
|
|
182
|
+
### 3.2 Sin attend — el dev despacha manualmente pero NO transcribe
|
|
183
|
+
|
|
184
|
+
Si el dev no dejó attend activo, la pregunta queda en inbox y él mismo tiene que ir a la otra ventana a "despachar" el trabajo al otro agente.
|
|
185
|
+
|
|
186
|
+
```mermaid
|
|
187
|
+
sequenceDiagram
|
|
188
|
+
autonumber
|
|
189
|
+
participant D as Dev<br/>(único)
|
|
190
|
+
participant AP as Agente<br/>payments
|
|
191
|
+
participant BR as Broker
|
|
192
|
+
participant AF as Agente<br/>frontend
|
|
193
|
+
|
|
194
|
+
D->>AP: "pregúntale al front<br/>qué formato espera"
|
|
195
|
+
AP->>BR: /refacil:ask @frontend "..."
|
|
196
|
+
Note over AP: Pregunta enviada,<br/>sin bloqueo
|
|
197
|
+
|
|
198
|
+
Note over D: Dev salta a la<br/>ventana frontend
|
|
199
|
+
|
|
200
|
+
D->>AF: "ejecutá /refacil:inbox<br/>y respondé lo que haya"
|
|
201
|
+
AF->>BR: /refacil:inbox
|
|
202
|
+
BR->>AF: Hay 1 ask dirigido a vos
|
|
203
|
+
AF->>AF: Read/Grep src/api/pay.ts
|
|
204
|
+
AF->>BR: /refacil:reply "..."
|
|
205
|
+
|
|
206
|
+
Note over D: Dev vuelve a la<br/>ventana payments
|
|
207
|
+
|
|
208
|
+
D->>AP: "ejecutá /refacil:inbox<br/>y seguí con la tarea"
|
|
209
|
+
AP->>BR: /refacil:inbox
|
|
210
|
+
BR->>AP: Respuesta del frontend
|
|
211
|
+
AP->>D: Endpoint implementado
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**Diferencia clave vs 3.1**: el Dev visita la ventana frontend una vez (pasos 3-4). **Pero no lee código, no copia, no transcribe** — solo dice "ejecutá inbox y respondé". El agente del repo hace el trabajo real. Aún así el attend previo es el patrón ideal.
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## 4. Guía rápida: ¿qué skill uso?
|
|
219
|
+
|
|
220
|
+
Para que el equipo sepa qué invocar en cada caso:
|
|
221
|
+
|
|
222
|
+
```mermaid
|
|
223
|
+
flowchart TD
|
|
224
|
+
START[Necesito algo<br/>del bus] --> Q1{¿Primera vez<br/>en la sala hoy?}
|
|
225
|
+
Q1 -->|Sí| JOIN[/refacil:join sala<br/>escribe el bloque en<br/>AGENTS.md si falta/]
|
|
226
|
+
Q1 -->|No| Q2{¿Qué quiero<br/>hacer?}
|
|
227
|
+
|
|
228
|
+
JOIN --> Q2
|
|
229
|
+
|
|
230
|
+
Q2 -->|Preguntar a alguien<br/>y esperar respuesta<br/>para continuar| ASKW[/refacil:ask @X<br/>"..." --wait 180/]
|
|
231
|
+
Q2 -->|Preguntar sin<br/>bloquear| ASK[/refacil:ask @X<br/>"..."/]
|
|
232
|
+
Q2 -->|Anunciar algo<br/>a toda la sala| SAY[/refacil:say "..."/]
|
|
233
|
+
Q2 -->|Responder<br/>pregunta que me hicieron| REPLY[/refacil:reply "..."/]
|
|
234
|
+
Q2 -->|Atender el bus<br/>un rato| ATTEND[/refacil:attend/]
|
|
235
|
+
Q2 -->|Ver mensajes<br/>que llegaron offline| INBOX[/refacil:inbox/]
|
|
236
|
+
|
|
237
|
+
classDef start fill:#fff3cd,stroke:#856404,color:#333
|
|
238
|
+
classDef skill fill:#cce5ff,stroke:#004085,color:#333
|
|
239
|
+
class START start
|
|
240
|
+
class JOIN,ASKW,ASK,SAY,REPLY,ATTEND,INBOX skill
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## 5. Impacto esperado
|
|
246
|
+
|
|
247
|
+
Beneficios cualitativos para el dev que trabaja con múltiples repos. El impacto depende de si el agente del otro repo está o no en `/refacil:attend`:
|
|
248
|
+
|
|
249
|
+
| Aspecto | Antes | Con bus · otro agente SIN attend | Con bus · otro agente EN attend |
|
|
250
|
+
|---|---|---|---|
|
|
251
|
+
| **Consulta cruzada** | Dev salta de ventana, lee código, copia al otro agente | Dev salta 1 vez y dice "ejecutá inbox y respondé" | Agente pregunta solo; otro agente responde solo |
|
|
252
|
+
| **Calidad del contexto** | Transcripción manual del dev — se pierde precisión | Agente del repo dueño responde desde código real | Agente del repo dueño responde desde código real |
|
|
253
|
+
| **Saltos de ventana del dev** | Muchos: salir, leer, copiar, volver, pegar | Uno solo por consulta, corto (despachar al agente) | Cero: el dev se entera al final del resultado |
|
|
254
|
+
| **Ruptura del foco** | Alta: cada consulta interrumpe el flujo principal | Media: una interrupción liviana | Casi nula: todo ocurre en background |
|
|
255
|
+
| **Trazabilidad** | Nada queda registrado | `inbox.jsonl` 7 días auditable | `inbox.jsonl` 7 días auditable |
|
|
256
|
+
| **Bugs por transcripción** | Frecuentes (tipeo, campos mal copiados) | Eliminados — el agente copia literal | Eliminados |
|
|
257
|
+
| **Revisión post-mortem** | Hay que reconstruir de memoria | Historial del bus muestra la conversación real | Historial del bus muestra la conversación real |
|
|
258
|
+
|
|
259
|
+
**Lectura de la tabla**: con el bus siempre hay ganancia respecto al estado actual. La automatización **total** (columna derecha) requiere la disciplina de dejar en `/refacil:attend` los agentes de los repos que uno sabe que va a consultar antes de meterse en una tarea larga.
|
|
260
|
+
|
|
261
|
+
### Impacto cuantitativo (estimar por equipo)
|
|
262
|
+
|
|
263
|
+
Estos valores se deben **medir en producción del equipo** durante las primeras 2-4 semanas:
|
|
264
|
+
|
|
265
|
+
- Número de preguntas/respuestas cruzadas por día (proxy: líneas en `inbox.jsonl`)
|
|
266
|
+
- Reducción de reuniones técnicas ad-hoc para alinear contratos de APIs
|
|
267
|
+
- Tiempo promedio hasta resolver una consulta cruzada (antes medido vs ahora)
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## 6. Cómo empezar (5 minutos)
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
# 1. Actualiza refacil-sdd-ai a la última versión (automático si tienes Claude Code)
|
|
275
|
+
npm update -g refacil-sdd-ai
|
|
276
|
+
|
|
277
|
+
# 2. En cada repo donde quieras usar el bus:
|
|
278
|
+
refacil-sdd-ai update # re-copia skills (el hook lo hace solo en Claude Code)
|
|
279
|
+
|
|
280
|
+
# 3. Reinicia la sesión de Claude Code o Cursor
|
|
281
|
+
|
|
282
|
+
# 4. En el chat del LLM:
|
|
283
|
+
/refacil:join refacil-main
|
|
284
|
+
# La primera vez el LLM escribe un bloque de presentación en tu AGENTS.md
|
|
285
|
+
# y te une a la sala
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
No requiere configurar puertos, servidores ni credenciales. El broker se levanta solo.
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## 7. Puntos clave para el pitch al equipo
|
|
293
|
+
|
|
294
|
+
- **Las sesiones son agentes de repo, no devs**: el caso principal es un dev con varias ventanas abiertas (una por repo), donde los agentes del LLM se hablan entre sí para que el dev no haga de transcriptor.
|
|
295
|
+
- **El dev sigue siendo el "PM"** de la operación — decide qué tarea arrancar, a qué agentes poner en attend, y valida el resultado final. El bus solo quita el trabajo mecánico entre ventanas.
|
|
296
|
+
- **No transfiere código ni secretos** entre repos: solo texto plano que el dev controla.
|
|
297
|
+
- **Costo infraestructural cero**: local, sin servicio compartido, sin cuentas nuevas, 40 MB de RAM.
|
|
298
|
+
- **Funciona en Claude Code y Cursor**: sin diferencias ni configuración adicional.
|
|
299
|
+
- **Persistencia con expiración automática** (7 días) — no se llena el disco.
|
|
300
|
+
- **Patrón óptimo**: antes de empezar una tarea que puede requerir consultar otros repos, el dev abre rápido las otras ventanas y dice *"atiende el bus"*. A partir de ahí, todo ocurre en background mientras trabaja.
|
|
301
|
+
- **Fallback natural sin attend**: aunque el dev olvide el setup, el bus sigue sirviendo como canal asíncrono: el dev salta 1 vez, despacha al agente (*"ejecutá inbox y respondé"*) y vuelve. Mucho mejor que transcribir código a mano.
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## Apéndice: comandos útiles para el dev
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
refacil-sdd-ai bus status # ¿está activo el broker?
|
|
309
|
+
refacil-sdd-ai bus rooms # salas activas + miembros
|
|
310
|
+
refacil-sdd-ai bus watch <session> # panel en vivo (sin tokens)
|
|
311
|
+
refacil-sdd-ai bus history --n 50 # últimos 50 mensajes de la sala actual
|
|
312
|
+
refacil-sdd-ai bus leave # salir de la sala
|
|
313
|
+
refacil-sdd-ai bus stop # bajar el broker (raro necesitarlo)
|
|
314
|
+
```
|