zugzbot-sdd 1.5.30 → 1.5.32
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.
|
@@ -185,15 +185,77 @@ export default tool({
|
|
|
185
185
|
report.push(`⚠️ No se pudo restablecer el lockfile: ${e.message}`);
|
|
186
186
|
}
|
|
187
187
|
}
|
|
188
|
+
// 5.5. Generar reporte dedicado de tokens y costes para este cambio
|
|
189
|
+
const historyPath = path.join(projectRoot, ".openspec/changes", args.changeName, "phase_history.jsonl");
|
|
190
|
+
if (fs.existsSync(historyPath)) {
|
|
191
|
+
try {
|
|
192
|
+
const lines = fs.readFileSync(historyPath, "utf-8").split("\n").filter(Boolean);
|
|
193
|
+
const entries = lines.map(l => JSON.parse(l));
|
|
194
|
+
let totalCost = 0;
|
|
195
|
+
let totalInput = 0;
|
|
196
|
+
let totalOutput = 0;
|
|
197
|
+
const models = new Set();
|
|
198
|
+
entries.forEach((e) => {
|
|
199
|
+
if (e.analytics) {
|
|
200
|
+
totalCost = Math.max(totalCost, e.analytics.cumulative_cost_usd || 0);
|
|
201
|
+
totalInput = Math.max(totalInput, e.analytics.cumulative_tokens_input || 0);
|
|
202
|
+
totalOutput = Math.max(totalOutput, e.analytics.cumulative_tokens_output || 0);
|
|
203
|
+
if (Array.isArray(e.analytics.models_used)) {
|
|
204
|
+
e.analytics.models_used.forEach((m) => models.add(m));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
const tokenUsageMarkdown = `# 💳 Reporte de Consumo del Cambio: ${args.changeName}
|
|
209
|
+
|
|
210
|
+
Este reporte detalla la telemetría de tokens y coste financiero en USD acumulado por el enjambre de agentes durante el desarrollo de esta tarea.
|
|
211
|
+
|
|
212
|
+
## 📊 Métricas de Consumo
|
|
213
|
+
- **Coste Total de la Sesión:** $${totalCost.toFixed(4)} USD
|
|
214
|
+
- **Tokens de Entrada (Prompt):** ${totalInput.toLocaleString()} tokens
|
|
215
|
+
- **Tokens de Salida (Completion):** ${totalOutput.toLocaleString()} tokens
|
|
216
|
+
- **Modelos de IA Participantes:** ${Array.from(models).join(", ") || "Ninguno registrado"}
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
*Reporte autogenerado de forma atómica al cierre de la tarea por sdd_archive_and_commit.*
|
|
220
|
+
`;
|
|
221
|
+
fs.writeFileSync(path.join(projectRoot, ".openspec/changes", args.changeName, "token_usage.md"), tokenUsageMarkdown, "utf-8");
|
|
222
|
+
report.push(`✓ Reporte de telemetría de tokens generado con éxito en token_usage.md`);
|
|
223
|
+
}
|
|
224
|
+
catch (e) {
|
|
225
|
+
report.push(`⚠️ No se pudo generar el reporte de telemetría de tokens: ${e.message}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
188
228
|
// 6. Archivar la carpeta físicamente (ANTES del commit para que quede registrado de forma atómica)
|
|
189
|
-
|
|
229
|
+
// Calcular el siguiente prefijo de secuencia cronológica (ej: 0001_2026-05-30_143015)
|
|
230
|
+
let seqPrefix = "0001";
|
|
231
|
+
const archiveRoot = path.join(projectRoot, ".openspec/changes/archive");
|
|
232
|
+
if (fs.existsSync(archiveRoot)) {
|
|
233
|
+
try {
|
|
234
|
+
const dirs = fs.readdirSync(archiveRoot).filter(f => fs.statSync(path.join(archiveRoot, f)).isDirectory());
|
|
235
|
+
let maxSeq = 0;
|
|
236
|
+
dirs.forEach(d => {
|
|
237
|
+
const match = d.match(/^(\d{4})_/);
|
|
238
|
+
if (match) {
|
|
239
|
+
const seq = parseInt(match[1], 10);
|
|
240
|
+
if (seq > maxSeq)
|
|
241
|
+
maxSeq = seq;
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
seqPrefix = String(maxSeq + 1).padStart(4, "0");
|
|
245
|
+
}
|
|
246
|
+
catch (e) { }
|
|
247
|
+
}
|
|
248
|
+
const now = new Date();
|
|
249
|
+
const timeStr = now.toTimeString().split(" ")[0].replace(/:/g, ""); // e.g. "143015"
|
|
250
|
+
const archiveFolderName = `${seqPrefix}_${dateStr}_${timeStr}-${args.changeName}`;
|
|
251
|
+
const archiveDir = path.join(archiveRoot, archiveFolderName);
|
|
190
252
|
try {
|
|
191
253
|
if (fs.existsSync(archiveDir)) {
|
|
192
254
|
fs.rmSync(archiveDir, { recursive: true, force: true });
|
|
193
255
|
}
|
|
194
256
|
fs.mkdirSync(path.dirname(archiveDir), { recursive: true });
|
|
195
257
|
moveRecursive(changeDir, archiveDir);
|
|
196
|
-
report.push(`✓ Carpeta archivada en: .openspec/changes/archive/${
|
|
258
|
+
report.push(`✓ Carpeta archivada en: .openspec/changes/archive/${archiveFolderName}/`);
|
|
197
259
|
}
|
|
198
260
|
catch (e) {
|
|
199
261
|
return `[SDD Archive Error] Error crítico archivando carpetas: ${e.message}`;
|
package/AGENTS.md
CHANGED
|
@@ -15,6 +15,7 @@ Queda terminantemente prohibido para cualquier agente del swarm (incluyendo al O
|
|
|
15
15
|
- **No Trabajo en Caliente**: Está prohibido proponer código fuente, diseños HTML/CSS o parches técnicos directamente al usuario en el chat principal sin antes haber completado la **Fase 1 (Planificación e Interrogación)** y obtenido su visto bueno explícito.
|
|
16
16
|
- **Rol del Orquestador**: `@zugzbot` debe educar siempre al usuario sobre el flujo de SDD cuando se solicite una nueva característica o cambio. Debe generar un **Roadmap de las 6 Fases de SDD de una línea por fase** y delegar la Fase 1 de inmediato.
|
|
17
17
|
- **Flujo de Trabajo Estricto**: Todo cambio lógico debe iniciarse a través de la delegación estructurada hacia `@sdd-planner`.
|
|
18
|
+
- **Slug Semántico del Cambio (kebab-case) [CRÍTICO]**: Al iniciar un ciclo (transición a Fase 1), el orquestador `@zugzbot` o `@sdd-planner` debe generar obligatoriamente un nombre de cambio (`changeName` o `change_name`) en kebab-case que describa de manera precisa el requerimiento del usuario (ej: `crear-modulo-auth` o `setup-clasp-mocker`). Queda terminantemente prohibido utilizar el valor genérico 'nuevo-cambio' o nombres sin relación semántica.
|
|
18
19
|
|
|
19
20
|
---
|
|
20
21
|
|
package/package.json
CHANGED
|
@@ -184,15 +184,79 @@ export default tool({
|
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
+
// 5.5. Generar reporte dedicado de tokens y costes para este cambio
|
|
188
|
+
const historyPath = path.join(projectRoot, ".openspec/changes", args.changeName, "phase_history.jsonl");
|
|
189
|
+
if (fs.existsSync(historyPath)) {
|
|
190
|
+
try {
|
|
191
|
+
const lines = fs.readFileSync(historyPath, "utf-8").split("\n").filter(Boolean);
|
|
192
|
+
const entries = lines.map(l => JSON.parse(l));
|
|
193
|
+
let totalCost = 0;
|
|
194
|
+
let totalInput = 0;
|
|
195
|
+
let totalOutput = 0;
|
|
196
|
+
const models = new Set<string>();
|
|
197
|
+
|
|
198
|
+
entries.forEach((e: any) => {
|
|
199
|
+
if (e.analytics) {
|
|
200
|
+
totalCost = Math.max(totalCost, e.analytics.cumulative_cost_usd || 0);
|
|
201
|
+
totalInput = Math.max(totalInput, e.analytics.cumulative_tokens_input || 0);
|
|
202
|
+
totalOutput = Math.max(totalOutput, e.analytics.cumulative_tokens_output || 0);
|
|
203
|
+
if (Array.isArray(e.analytics.models_used)) {
|
|
204
|
+
e.analytics.models_used.forEach((m: string) => models.add(m));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
const tokenUsageMarkdown = `# 💳 Reporte de Consumo del Cambio: ${args.changeName}
|
|
210
|
+
|
|
211
|
+
Este reporte detalla la telemetría de tokens y coste financiero en USD acumulado por el enjambre de agentes durante el desarrollo de esta tarea.
|
|
212
|
+
|
|
213
|
+
## 📊 Métricas de Consumo
|
|
214
|
+
- **Coste Total de la Sesión:** $${totalCost.toFixed(4)} USD
|
|
215
|
+
- **Tokens de Entrada (Prompt):** ${totalInput.toLocaleString()} tokens
|
|
216
|
+
- **Tokens de Salida (Completion):** ${totalOutput.toLocaleString()} tokens
|
|
217
|
+
- **Modelos de IA Participantes:** ${Array.from(models).join(", ") || "Ninguno registrado"}
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
*Reporte autogenerado de forma atómica al cierre de la tarea por sdd_archive_and_commit.*
|
|
221
|
+
`;
|
|
222
|
+
fs.writeFileSync(path.join(projectRoot, ".openspec/changes", args.changeName, "token_usage.md"), tokenUsageMarkdown, "utf-8");
|
|
223
|
+
report.push(`✓ Reporte de telemetría de tokens generado con éxito en token_usage.md`);
|
|
224
|
+
} catch (e: any) {
|
|
225
|
+
report.push(`⚠️ No se pudo generar el reporte de telemetría de tokens: ${e.message}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
187
229
|
// 6. Archivar la carpeta físicamente (ANTES del commit para que quede registrado de forma atómica)
|
|
188
|
-
|
|
230
|
+
// Calcular el siguiente prefijo de secuencia cronológica (ej: 0001_2026-05-30_143015)
|
|
231
|
+
let seqPrefix = "0001";
|
|
232
|
+
const archiveRoot = path.join(projectRoot, ".openspec/changes/archive");
|
|
233
|
+
if (fs.existsSync(archiveRoot)) {
|
|
234
|
+
try {
|
|
235
|
+
const dirs = fs.readdirSync(archiveRoot).filter(f => fs.statSync(path.join(archiveRoot, f)).isDirectory());
|
|
236
|
+
let maxSeq = 0;
|
|
237
|
+
dirs.forEach(d => {
|
|
238
|
+
const match = d.match(/^(\d{4})_/);
|
|
239
|
+
if (match) {
|
|
240
|
+
const seq = parseInt(match[1], 10);
|
|
241
|
+
if (seq > maxSeq) maxSeq = seq;
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
seqPrefix = String(maxSeq + 1).padStart(4, "0");
|
|
245
|
+
} catch (e) {}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const now = new Date();
|
|
249
|
+
const timeStr = now.toTimeString().split(" ")[0].replace(/:/g, ""); // e.g. "143015"
|
|
250
|
+
const archiveFolderName = `${seqPrefix}_${dateStr}_${timeStr}-${args.changeName}`;
|
|
251
|
+
const archiveDir = path.join(archiveRoot, archiveFolderName);
|
|
252
|
+
|
|
189
253
|
try {
|
|
190
254
|
if (fs.existsSync(archiveDir)) {
|
|
191
255
|
fs.rmSync(archiveDir, { recursive: true, force: true })
|
|
192
256
|
}
|
|
193
257
|
fs.mkdirSync(path.dirname(archiveDir), { recursive: true })
|
|
194
258
|
moveRecursive(changeDir, archiveDir)
|
|
195
|
-
report.push(`✓ Carpeta archivada en: .openspec/changes/archive/${
|
|
259
|
+
report.push(`✓ Carpeta archivada en: .openspec/changes/archive/${archiveFolderName}/`)
|
|
196
260
|
} catch (e: any) {
|
|
197
261
|
return `[SDD Archive Error] Error crítico archivando carpetas: ${e.message}`
|
|
198
262
|
}
|