ozali 0.3.0 → 0.4.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 +15 -2
- package/cli/bin/ozali.mjs +11 -1
- package/cli/lib/commands.mjs +210 -1
- package/cli/lib/util.mjs +1 -0
- package/cli/test/smoke.test.mjs +44 -2
- package/docs/guia-de-uso.md +29 -0
- package/package.json +2 -1
- package/skill/SKILL.md +17 -4
- package/skill/references/agents-blueprint.md +15 -8
- package/skill/references/doc-templates.md +27 -0
- package/skill/references/engram-convention.md +47 -0
- package/templates/ozali-jarvis-plugin.js +27 -0
- package/templates/ozali-jarvis.md +45 -0
package/README.md
CHANGED
|
@@ -61,8 +61,14 @@ ozali init # detecta agente, instala la skill, aísla el histórico, instal
|
|
|
61
61
|
ozali doctor # health-check read-only (fuente de verdad, Engram, Cloud, Strict TDD, runner…)
|
|
62
62
|
ozali update # actualiza la skill instalada a la versión del paquete
|
|
63
63
|
ozali sync # lleva el histórico (docs + Engram) al repo de conocimiento de equipo
|
|
64
|
+
ozali audit # navega/audita la memoria de Engram del proyecto (o general)
|
|
64
65
|
```
|
|
65
66
|
|
|
67
|
+
`ozali audit` recorre lo que el equipo ha acumulado en Engram: dentro de un repo propone auditar
|
|
68
|
+
**ese proyecto** o **general** (todos los proyectos); fuera de un repo va directo a general. Usa
|
|
69
|
+
`--tui` para el navegador interactivo, `--search "<texto>"` para buscar y `--general` para forzar el
|
|
70
|
+
alcance. Sin Engram, audita el histórico local de `.ozali/docs/`.
|
|
71
|
+
|
|
66
72
|
`init` también escribe un **perfil base de permisos** (`.claude/settings.json` para Claude Code,
|
|
67
73
|
`opencode.json` para opencode) para reducir confirmaciones: deja libres comandos seguros y bloquea
|
|
68
74
|
los destructivos. Es un template — tus reglas se conservan al re-correr `init`. Como Claude Code
|
|
@@ -75,9 +81,16 @@ macOS/Linux, `go install` en Windows, con binario precompilado como fallback. Si
|
|
|
75
81
|
instalarlo, pasa `--no-engram` (arranca en modo `docs`). Opcionalmente habilita **Engram Cloud**
|
|
76
82
|
(réplica de equipo opt-in) además del git-sync.
|
|
77
83
|
|
|
84
|
+
`init` también crea **ozali-jarvis**, un **orquestador always-on**: persona en `CLAUDE.md`/`AGENTS.md`
|
|
85
|
+
+ subagente + hooks de recordatorio que hace que el agente, **en toda sesión y sin necesidad de
|
|
86
|
+
`/cdk`**, recupere contexto de Engram, registre el trabajo del equipo (memoria en contexto) y delegue
|
|
87
|
+
la ejecución disciplinada en `cdk`. Fija el proyecto en `.engram/config.json` para memoria
|
|
88
|
+
determinista. Con Engram en línea, jarvis y `cdk` operan **recall-first** (reusan memoria en vez de
|
|
89
|
+
releer) para gastar menos tokens/contexto. Omítelo con `--no-jarvis`.
|
|
90
|
+
|
|
78
91
|
Flags útiles: `--yes` (no interactivo), `--dry-run` (init sin escribir), `--no-engram`, `--no-trust`,
|
|
79
|
-
`--agent`, `--scope`, `--knowledge-repo`, `--import`/`--push`/`--cloud` (sync). Modelo
|
|
80
|
-
en [docs/intended-usage.md](docs/intended-usage.md).
|
|
92
|
+
`--no-jarvis`, `--agent`, `--scope`, `--knowledge-repo`, `--import`/`--push`/`--cloud` (sync). Modelo
|
|
93
|
+
mental completo en [docs/intended-usage.md](docs/intended-usage.md).
|
|
81
94
|
|
|
82
95
|
## Agentes soportados
|
|
83
96
|
|
package/cli/bin/ozali.mjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Seguridad: sin dependencias ni scripts de instalación. Ejecuta seguro con
|
|
4
4
|
// `pnpm dlx ozali` o `npx --ignore-scripts ozali`. Ver docs/security.md.
|
|
5
5
|
import { c, err, pkgVersion } from "../lib/util.mjs";
|
|
6
|
-
import { init, doctor, update, sync } from "../lib/commands.mjs";
|
|
6
|
+
import { init, doctor, update, sync, audit } from "../lib/commands.mjs";
|
|
7
7
|
|
|
8
8
|
const HELP = `
|
|
9
9
|
${c.bold("ozali")} ${c.dim("v" + pkgVersion())} — bootstrap de IA por equipo (TDD/SDD + memoria Engram)
|
|
@@ -17,6 +17,7 @@ ${c.bold("Comandos:")}
|
|
|
17
17
|
doctor Health-check read-only del proyecto (fuente de verdad, Engram, TDD…).
|
|
18
18
|
update Actualiza la skill ozali instalada a la versión de este paquete.
|
|
19
19
|
sync Sincroniza el histórico (docs + Engram) con el repo de conocimiento.
|
|
20
|
+
audit Navega/audita la memoria de Engram del proyecto (o general).
|
|
20
21
|
|
|
21
22
|
${c.bold("Opciones comunes:")}
|
|
22
23
|
--yes, -y No interactivo: usa defaults.
|
|
@@ -26,9 +27,13 @@ ${c.bold("Opciones comunes:")}
|
|
|
26
27
|
--knowledge-repo <p> (init) Ruta del repo de conocimiento.
|
|
27
28
|
--no-engram (init) No usar Engram; arranca en modo docs.
|
|
28
29
|
--no-trust (init) No marcar el workspace como confiable en Claude Code.
|
|
30
|
+
--no-jarvis (init) No crear el orquestador ozali-jarvis.
|
|
29
31
|
--import (sync) Importa del repo de conocimiento a local.
|
|
30
32
|
--push (sync) Hace push al remoto del repo de conocimiento.
|
|
31
33
|
--cloud (sync) Replica también a Engram Cloud (si está habilitado).
|
|
34
|
+
--general (audit) Auditoría general (todos los proyectos en Engram).
|
|
35
|
+
--tui (audit) Abre el navegador interactivo de Engram.
|
|
36
|
+
--search <q> (audit) Busca <q> en la memoria.
|
|
32
37
|
-h, --help Esta ayuda. -v, --version Versión.
|
|
33
38
|
|
|
34
39
|
${c.bold("Instalación segura:")}
|
|
@@ -48,6 +53,10 @@ function parseArgs(argv) {
|
|
|
48
53
|
else if (a === "--cloud") opts.cloud = true;
|
|
49
54
|
else if (a === "--no-engram") opts.noEngram = true;
|
|
50
55
|
else if (a === "--no-trust") opts.noTrust = true;
|
|
56
|
+
else if (a === "--no-jarvis") opts.noJarvis = true;
|
|
57
|
+
else if (a === "--general") opts.general = true;
|
|
58
|
+
else if (a === "--tui") opts.tui = true;
|
|
59
|
+
else if (a === "--search") opts.search = argv[++i];
|
|
51
60
|
else if (a === "--agent") opts.agent = argv[++i];
|
|
52
61
|
else if (a === "--scope") opts.scope = argv[++i];
|
|
53
62
|
else if (a === "--knowledge-repo") opts.knowledgeRepo = argv[++i];
|
|
@@ -71,6 +80,7 @@ async function main() {
|
|
|
71
80
|
case "doctor": return doctor(cwd);
|
|
72
81
|
case "update": return update(cwd);
|
|
73
82
|
case "sync": return sync(cwd, opts);
|
|
83
|
+
case "audit": return await audit(cwd, opts);
|
|
74
84
|
default:
|
|
75
85
|
err(`comando desconocido "${cmd}". Usa ${c.bold("ozali --help")}.`);
|
|
76
86
|
return 1;
|
package/cli/lib/commands.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import fs from "node:fs";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import {
|
|
5
5
|
c, ok, warn, err, info, step,
|
|
6
|
-
SKILL_SRC, exists, ensureDir, copyDir, readJSON, writeJSON,
|
|
6
|
+
SKILL_SRC, TEMPLATES_SRC, exists, ensureDir, copyDir, readJSON, writeJSON,
|
|
7
7
|
ensureGitignore, tryExec, spawnCmd, which,
|
|
8
8
|
projectName, pkgVersion, DEFAULT_KNOWLEDGE, HOME,
|
|
9
9
|
} from "./util.mjs";
|
|
@@ -111,6 +111,16 @@ export async function init(cwd, opts) {
|
|
|
111
111
|
ensureOpencodeProfile(cwd);
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
// 2.5) ozali-jarvis: orquestador always-on (memoria Engram + puente a cdk).
|
|
115
|
+
if (!opts.noJarvis) {
|
|
116
|
+
const proj = projectName(cwd);
|
|
117
|
+
// Fija el proyecto para escrituras deterministas de memoria (se deriva igual por cada miembro).
|
|
118
|
+
writeJSON(path.join(cwd, ".engram", "config.json"), { project_name: proj });
|
|
119
|
+
ok(`Proyecto de memoria fijado en ${c.bold(".engram/config.json")} (${proj}).`);
|
|
120
|
+
if (agent === "claude-code" || agent === "both") ensureJarvisClaudeCode(cwd);
|
|
121
|
+
if (agent === "opencode" || agent === "both") ensureJarvisOpencode(cwd);
|
|
122
|
+
}
|
|
123
|
+
|
|
114
124
|
// 3) gitignore del histórico aislado
|
|
115
125
|
if (env.git.isRepo) {
|
|
116
126
|
const { added } = ensureGitignore(cwd, [".ozali/", ".engram/"]);
|
|
@@ -286,6 +296,102 @@ function ensureClaudeCodeProfile(cwd, scope) {
|
|
|
286
296
|
}
|
|
287
297
|
}
|
|
288
298
|
|
|
299
|
+
// ----------------------------------------------------------------- ozali-jarvis
|
|
300
|
+
const JARVIS_BEGIN = "<!-- ozali-jarvis:start -->";
|
|
301
|
+
const JARVIS_END = "<!-- ozali-jarvis:end -->";
|
|
302
|
+
|
|
303
|
+
/** Cuerpo del bloque jarvis para CLAUDE.md / AGENTS.md (sin el frontmatter del template). */
|
|
304
|
+
function jarvisPersonaBody() {
|
|
305
|
+
const tpl = fs.readFileSync(path.join(TEMPLATES_SRC, "ozali-jarvis.md"), "utf8");
|
|
306
|
+
// Quita el frontmatter YAML (--- ... ---) y deja el cuerpo markdown.
|
|
307
|
+
return tpl.replace(/^---[\s\S]*?---\s*/, "").trim();
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/** Inserta/actualiza un bloque marcado en un archivo markdown (idempotente). */
|
|
311
|
+
function upsertMarkedBlock(file, body) {
|
|
312
|
+
const block = `${JARVIS_BEGIN}\n${body}\n${JARVIS_END}`;
|
|
313
|
+
let txt = exists(file) ? fs.readFileSync(file, "utf8") : "";
|
|
314
|
+
const re = new RegExp(`${JARVIS_BEGIN}[\\s\\S]*?${JARVIS_END}`);
|
|
315
|
+
let changed;
|
|
316
|
+
if (re.test(txt)) {
|
|
317
|
+
const next = txt.replace(re, block);
|
|
318
|
+
changed = next !== txt; txt = next;
|
|
319
|
+
} else {
|
|
320
|
+
txt = (txt.trim() ? txt.replace(/\s*$/, "") + "\n\n" : "") + block + "\n";
|
|
321
|
+
changed = true;
|
|
322
|
+
}
|
|
323
|
+
fs.writeFileSync(file, txt);
|
|
324
|
+
return changed;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function ensureJarvisClaudeCode(cwd) {
|
|
328
|
+
// 1) persona en CLAUDE.md (always-on)
|
|
329
|
+
const claudeMd = path.join(cwd, "CLAUDE.md");
|
|
330
|
+
const ch = upsertMarkedBlock(claudeMd, jarvisPersonaBody());
|
|
331
|
+
ok(`ozali-jarvis ${ch ? "escrito" : "ya presente"} en ${c.bold("CLAUDE.md")} (orquestador por defecto).`);
|
|
332
|
+
// 2) subagente
|
|
333
|
+
const agentFile = path.join(cwd, ".claude", "agents", "ozali-jarvis.md");
|
|
334
|
+
ensureDir(path.dirname(agentFile));
|
|
335
|
+
fs.copyFileSync(path.join(TEMPLATES_SRC, "ozali-jarvis.md"), agentFile);
|
|
336
|
+
ok(`Subagente ${c.bold(".claude/agents/ozali-jarvis.md")} instalado.`);
|
|
337
|
+
// 3) hooks de recordatorio (idempotentes)
|
|
338
|
+
ensureJarvisHooks(cwd);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function ensureJarvisHooks(cwd) {
|
|
342
|
+
const p = path.join(cwd, ".claude", "settings.json");
|
|
343
|
+
const cfg = readJSON(p, {});
|
|
344
|
+
cfg.hooks = cfg.hooks || {};
|
|
345
|
+
const reminder = (msg) => ({ hooks: [{ type: "command", command: `echo '[ozali-jarvis] ${msg}'` }] });
|
|
346
|
+
const want = {
|
|
347
|
+
SessionStart: reminder("recall-first: confirma proyecto (mem_current_project) y recupera contexto de Engram (mem_context) antes de actuar."),
|
|
348
|
+
PreCompact: reminder("antes de compactar: persiste el state recuperable en Engram (engram-convention §4)."),
|
|
349
|
+
SessionEnd: reminder("cierre: registra lo trabajado en Engram (scope project, español) y haz mem_session_summary."),
|
|
350
|
+
};
|
|
351
|
+
let added = 0;
|
|
352
|
+
for (const [evt, val] of Object.entries(want)) {
|
|
353
|
+
const arr = cfg.hooks[evt] || (cfg.hooks[evt] = []);
|
|
354
|
+
// idempotencia: no duplicar el recordatorio ozali-jarvis para ese evento
|
|
355
|
+
const has = JSON.stringify(arr).includes("[ozali-jarvis]");
|
|
356
|
+
if (!has) { arr.push(val); added++; }
|
|
357
|
+
}
|
|
358
|
+
writeJSON(p, cfg);
|
|
359
|
+
if (added) ok(`Hooks de recordatorio de ozali-jarvis añadidos a ${c.bold(".claude/settings.json")} (${added}).`);
|
|
360
|
+
else info("Hooks de ozali-jarvis ya presentes en Claude Code.");
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function ensureJarvisOpencode(cwd) {
|
|
364
|
+
// 1) persona en AGENTS.md
|
|
365
|
+
const agentsMd = path.join(cwd, "AGENTS.md");
|
|
366
|
+
const ch = upsertMarkedBlock(agentsMd, jarvisPersonaBody());
|
|
367
|
+
ok(`ozali-jarvis ${ch ? "escrito" : "ya presente"} en ${c.bold("AGENTS.md")} (orquestador por defecto).`);
|
|
368
|
+
// 2) agente en opencode.json
|
|
369
|
+
const p = path.join(cwd, "opencode.json");
|
|
370
|
+
const cfg = readJSON(p, {});
|
|
371
|
+
cfg.$schema = cfg.$schema || "https://opencode.ai/config.json";
|
|
372
|
+
cfg.agent = cfg.agent || {};
|
|
373
|
+
if (!cfg.agent["ozali-jarvis"]) {
|
|
374
|
+
cfg.agent["ozali-jarvis"] = {
|
|
375
|
+
mode: "primary",
|
|
376
|
+
description: "Orquestador del proyecto: memoria Engram + puente a la skill cdk.",
|
|
377
|
+
prompt: "{file:./AGENTS.md}",
|
|
378
|
+
};
|
|
379
|
+
writeJSON(p, cfg);
|
|
380
|
+
ok(`Agente ${c.bold("ozali-jarvis")} (primary) añadido a ${c.bold("opencode.json")}.`);
|
|
381
|
+
} else {
|
|
382
|
+
info("Agente ozali-jarvis ya presente en opencode.json.");
|
|
383
|
+
}
|
|
384
|
+
// 3) plugin de recordatorio
|
|
385
|
+
const plugin = path.join(cwd, ".opencode", "plugins", "ozali-jarvis.js");
|
|
386
|
+
if (!exists(plugin)) {
|
|
387
|
+
ensureDir(path.dirname(plugin));
|
|
388
|
+
fs.copyFileSync(path.join(TEMPLATES_SRC, "ozali-jarvis-plugin.js"), plugin);
|
|
389
|
+
ok(`Plugin ${c.bold(".opencode/plugins/ozali-jarvis.js")} instalado.`);
|
|
390
|
+
} else {
|
|
391
|
+
info("Plugin de ozali-jarvis ya presente en opencode.");
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
289
395
|
// Marca el proyecto como confiable en Claude Code (~/.claude.json). Sin esto, Claude Code
|
|
290
396
|
// ignora los permisos de un .claude/settings.json de proyecto ("workspace not trusted").
|
|
291
397
|
async function ensureClaudeWorkspaceTrust(cwd, opts) {
|
|
@@ -342,6 +448,13 @@ export function doctor(cwd) {
|
|
|
342
448
|
add("Fuente de verdad", env.sot.found, env.sot.found ? `${env.sot.doc} + ${env.sot.dir}/` : "ausente (corre la skill 'ozali')");
|
|
343
449
|
add("Skill ozali instalada", env.skill.installed, env.skill.installed ? env.skill.paths.map((p) => path.relative(cwd, p) || p).join(", ") : "no instalada (ozali init)");
|
|
344
450
|
add("Engram", env.engram.available, env.engram.available ? env.engram.bin : "no instalado → modo docs");
|
|
451
|
+
if (env.engram.available) {
|
|
452
|
+
const online = tryExec("engram", ["doctor"], { cwd }) !== null;
|
|
453
|
+
add("Engram en línea", online, online ? "engram doctor OK" : "engram doctor no responde");
|
|
454
|
+
}
|
|
455
|
+
const jarvis = detectJarvis(cwd);
|
|
456
|
+
// jarvis es opt-in (--no-jarvis): informativo, no cuenta como fallo.
|
|
457
|
+
add("ozali-jarvis", true, jarvis.present ? `configurado (${jarvis.where.join(", ")})` : "no configurado (--no-jarvis)");
|
|
345
458
|
const cloudOn = !!(cfg && cfg.cloud && cfg.cloud.enabled);
|
|
346
459
|
// Cloud es opt-in: "off" es un estado válido (no cuenta como fallo).
|
|
347
460
|
add("Engram Cloud", true, cloudOn ? `enrolado → ${cfg.cloud.server || "server por defecto"}` : "off (opt-in, git-sync activo)");
|
|
@@ -381,9 +494,31 @@ export function doctor(cwd) {
|
|
|
381
494
|
}
|
|
382
495
|
}
|
|
383
496
|
|
|
497
|
+
// Tendencia de uso de tokens (la escribe cdk en cada hito; informativo).
|
|
498
|
+
const metrics = readJSON(path.join(cwd, ".ozali", "metrics", "token-metrics.json"));
|
|
499
|
+
if (metrics && Array.isArray(metrics.hits) && metrics.hits.length) {
|
|
500
|
+
step("Tendencia de tokens (últimos hitos)");
|
|
501
|
+
for (const h of metrics.hits.slice(-3)) {
|
|
502
|
+
const saved = h.savedByRecall ? ` ${c.green("(ahorro recall: " + h.savedByRecall + ")")}` : "";
|
|
503
|
+
console.log(` ${c.dim(h.hito || "?")}: total ${h.total ?? "N/A"}${saved}`);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
384
507
|
return bad === 0 ? 0 : 1;
|
|
385
508
|
}
|
|
386
509
|
|
|
510
|
+
/** Detecta si ozali-jarvis está configurado en el proyecto y dónde. */
|
|
511
|
+
function detectJarvis(cwd) {
|
|
512
|
+
const where = [];
|
|
513
|
+
const hasBlock = (f) => exists(f) && fs.readFileSync(f, "utf8").includes(JARVIS_BEGIN);
|
|
514
|
+
if (hasBlock(path.join(cwd, "CLAUDE.md"))) where.push("CLAUDE.md");
|
|
515
|
+
if (hasBlock(path.join(cwd, "AGENTS.md"))) where.push("AGENTS.md");
|
|
516
|
+
if (exists(path.join(cwd, ".claude", "agents", "ozali-jarvis.md"))) where.push("subagente");
|
|
517
|
+
const oc = readJSON(path.join(cwd, "opencode.json"));
|
|
518
|
+
if (oc && oc.agent && oc.agent["ozali-jarvis"]) where.push("opencode");
|
|
519
|
+
return { present: where.length > 0, where };
|
|
520
|
+
}
|
|
521
|
+
|
|
387
522
|
function readStrictTdd(cwd, sot) {
|
|
388
523
|
const f = path.join(cwd, sot.dir, "context", "tech-stack.md");
|
|
389
524
|
if (!exists(f)) return { found: false };
|
|
@@ -496,3 +631,77 @@ export function sync(cwd, opts) {
|
|
|
496
631
|
}
|
|
497
632
|
return 0;
|
|
498
633
|
}
|
|
634
|
+
|
|
635
|
+
// ============================================================ audit ===========
|
|
636
|
+
// Navega/audita la memoria de Engram: del proyecto actual o general (todos los
|
|
637
|
+
// proyectos). Sin contexto de proyecto en la ruta → general. Sin Engram → audita
|
|
638
|
+
// el histórico local de documentos.
|
|
639
|
+
export async function audit(cwd, opts) {
|
|
640
|
+
step("ozali audit — auditoría de memoria (Engram)");
|
|
641
|
+
const env = detectAll(cwd);
|
|
642
|
+
const cfg = readJSON(CONFIG_PATH(cwd));
|
|
643
|
+
const engramCfg = readJSON(path.join(cwd, ".engram", "config.json"));
|
|
644
|
+
const project = (engramCfg && engramCfg.project_name) || (cfg && cfg.project) || projectName(cwd);
|
|
645
|
+
const hasProjectContext = env.git.isRepo || !!cfg || !!engramCfg;
|
|
646
|
+
|
|
647
|
+
// Resolver alcance: --general fuerza general; sin contexto → general; en repo se propone elegir.
|
|
648
|
+
let scope = (opts.general || !hasProjectContext) ? "general" : "project";
|
|
649
|
+
if (!hasProjectContext) info("Sin contexto de proyecto en esta ruta → auditoría " + c.bold("general") + ".");
|
|
650
|
+
else if (scope === "project" && !opts.general && !opts.yes) {
|
|
651
|
+
scope = await select("¿Qué auditoría quieres?", [
|
|
652
|
+
{ value: "project", label: `Proyecto (${c.bold(project)})` },
|
|
653
|
+
{ value: "general", label: "General (todos los proyectos en Engram)" },
|
|
654
|
+
], 0);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// Sin Engram → auditar el histórico local de documentos.
|
|
658
|
+
if (!env.engram.available) {
|
|
659
|
+
warn("Engram no está instalado → auditoría desde documentos locales.");
|
|
660
|
+
return auditFromDocs(cwd, project);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// Navegador interactivo.
|
|
664
|
+
if (opts.tui) {
|
|
665
|
+
info("Abriendo el navegador interactivo de Engram (engram tui)…");
|
|
666
|
+
return spawnCmd("engram", ["tui"], { cwd });
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
if (scope === "general") {
|
|
670
|
+
step("Proyectos en Engram");
|
|
671
|
+
spawnCmd("engram", ["projects", "list"], { cwd });
|
|
672
|
+
step("Estadísticas (global)");
|
|
673
|
+
spawnCmd("engram", ["stats"], { cwd });
|
|
674
|
+
step("Contexto reciente");
|
|
675
|
+
spawnCmd("engram", ["context"], { cwd });
|
|
676
|
+
} else {
|
|
677
|
+
step(`Contexto reciente — ${project}`);
|
|
678
|
+
spawnCmd("engram", ["context", project], { cwd });
|
|
679
|
+
step("Estadísticas (global)");
|
|
680
|
+
spawnCmd("engram", ["stats"], { cwd });
|
|
681
|
+
}
|
|
682
|
+
if (opts.search) {
|
|
683
|
+
step(`Búsqueda: "${opts.search}"`);
|
|
684
|
+
spawnCmd("engram", ["search", opts.search], { cwd });
|
|
685
|
+
}
|
|
686
|
+
info("Navegación interactiva: " + c.bold("ozali audit --tui") + " · búsqueda: " + c.bold('ozali audit --search "<texto>"'));
|
|
687
|
+
return 0;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
function auditFromDocs(cwd, project) {
|
|
691
|
+
const docsDir = path.join(cwd, ".ozali", "docs", "cdk");
|
|
692
|
+
if (exists(docsDir)) {
|
|
693
|
+
step(`Hitos documentados localmente — ${project}`);
|
|
694
|
+
const hitos = fs.readdirSync(docsDir, { withFileTypes: true }).filter((e) => e.isDirectory());
|
|
695
|
+
if (hitos.length) for (const e of hitos) console.log(" • " + e.name);
|
|
696
|
+
else info("Aún no hay hitos en .ozali/docs/cdk/.");
|
|
697
|
+
} else {
|
|
698
|
+
info("No hay histórico local en .ozali/docs/cdk/ todavía.");
|
|
699
|
+
}
|
|
700
|
+
const metrics = readJSON(path.join(cwd, ".ozali", "metrics", "token-metrics.json"));
|
|
701
|
+
if (metrics && Array.isArray(metrics.hits) && metrics.hits.length) {
|
|
702
|
+
step("Uso de tokens (últimos hitos)");
|
|
703
|
+
for (const h of metrics.hits.slice(-5)) console.log(` ${c.dim(h.hito || "?")}: total ${h.total ?? "N/A"}`);
|
|
704
|
+
}
|
|
705
|
+
info("Instala Engram para auditoría buscable/acumulativa (" + c.bold("ozali doctor") + ").");
|
|
706
|
+
return 0;
|
|
707
|
+
}
|
package/cli/lib/util.mjs
CHANGED
|
@@ -10,6 +10,7 @@ import { execFileSync } from "node:child_process";
|
|
|
10
10
|
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
11
11
|
export const PKG_ROOT = path.resolve(HERE, "..", "..");
|
|
12
12
|
export const SKILL_SRC = path.join(PKG_ROOT, "skill");
|
|
13
|
+
export const TEMPLATES_SRC = path.join(PKG_ROOT, "templates");
|
|
13
14
|
|
|
14
15
|
export function pkgVersion() {
|
|
15
16
|
try {
|
package/cli/test/smoke.test.mjs
CHANGED
|
@@ -33,9 +33,20 @@ test("--version imprime la versión del package.json", () => {
|
|
|
33
33
|
assert.equal(stdout.trim(), pkg.version);
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
-
test("--help menciona los
|
|
36
|
+
test("--help menciona los comandos", () => {
|
|
37
37
|
const { stdout } = run(["--help"]);
|
|
38
|
-
for (const cmd of ["init", "doctor", "update", "sync"]) assert.match(stdout, new RegExp(cmd));
|
|
38
|
+
for (const cmd of ["init", "doctor", "update", "sync", "audit"]) assert.match(stdout, new RegExp(cmd));
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("audit imprime cabecera y no rompe (general)", () => {
|
|
42
|
+
const dir = tmpProject();
|
|
43
|
+
try {
|
|
44
|
+
// Sin Engram → fallback a docs; con Engram → comandos read-only. Ambos exit 0.
|
|
45
|
+
const { stdout } = run(["audit", "--general", "--yes"], dir);
|
|
46
|
+
assert.match(stdout, /auditoría de memoria/);
|
|
47
|
+
} finally {
|
|
48
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
49
|
+
}
|
|
39
50
|
});
|
|
40
51
|
|
|
41
52
|
test("comando desconocido sale con código 1", () => {
|
|
@@ -71,6 +82,37 @@ test("init --yes instala la skill, aísla el histórico y escribe config", () =>
|
|
|
71
82
|
const settings = JSON.parse(fs.readFileSync(settingsPath, "utf8"));
|
|
72
83
|
assert.ok(Array.isArray(settings.permissions.allow) && settings.permissions.allow.length > 0, "allow no vacío");
|
|
73
84
|
assert.ok(settings.permissions.deny.includes("Bash(rm -rf *)"), "deny bloquea rm -rf");
|
|
85
|
+
// ozali-jarvis (Claude Code)
|
|
86
|
+
assert.match(fs.readFileSync(path.join(dir, "CLAUDE.md"), "utf8"), /ozali-jarvis:start/, "bloque jarvis en CLAUDE.md");
|
|
87
|
+
assert.ok(fs.existsSync(path.join(dir, ".claude", "agents", "ozali-jarvis.md")), "subagente jarvis");
|
|
88
|
+
assert.ok(settings.hooks && settings.hooks.SessionStart, "hooks de recordatorio jarvis");
|
|
89
|
+
const eng = JSON.parse(fs.readFileSync(path.join(dir, ".engram", "config.json"), "utf8"));
|
|
90
|
+
assert.ok(eng.project_name, ".engram/config.json con project_name");
|
|
91
|
+
} finally {
|
|
92
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("init --no-jarvis no crea el orquestador", () => {
|
|
97
|
+
const dir = tmpProject();
|
|
98
|
+
try {
|
|
99
|
+
run(["init", "--yes", "--no-engram", "--no-trust", "--no-jarvis", "--agent", "claude-code", "--scope", "project", "--knowledge-repo", path.join(dir, ".k")], dir);
|
|
100
|
+
assert.ok(!fs.existsSync(path.join(dir, "CLAUDE.md")), "sin CLAUDE.md");
|
|
101
|
+
assert.ok(!fs.existsSync(path.join(dir, ".claude", "agents", "ozali-jarvis.md")), "sin subagente jarvis");
|
|
102
|
+
assert.ok(!fs.existsSync(path.join(dir, ".engram", "config.json")), "sin .engram/config.json");
|
|
103
|
+
} finally {
|
|
104
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("init opencode crea jarvis (AGENTS.md + agente + plugin)", () => {
|
|
109
|
+
const dir = tmpProject();
|
|
110
|
+
try {
|
|
111
|
+
run(["init", "--yes", "--no-engram", "--no-trust", "--agent", "opencode", "--scope", "project", "--knowledge-repo", path.join(dir, ".k")], dir);
|
|
112
|
+
assert.match(fs.readFileSync(path.join(dir, "AGENTS.md"), "utf8"), /ozali-jarvis:start/, "bloque jarvis en AGENTS.md");
|
|
113
|
+
const oc = JSON.parse(fs.readFileSync(path.join(dir, "opencode.json"), "utf8"));
|
|
114
|
+
assert.equal(oc.agent["ozali-jarvis"].mode, "primary", "agente jarvis primary en opencode.json");
|
|
115
|
+
assert.ok(fs.existsSync(path.join(dir, ".opencode", "plugins", "ozali-jarvis.js")), "plugin jarvis opencode");
|
|
74
116
|
} finally {
|
|
75
117
|
fs.rmSync(dir, { recursive: true, force: true });
|
|
76
118
|
}
|
package/docs/guia-de-uso.md
CHANGED
|
@@ -85,6 +85,7 @@ Lleva el histórico y la memoria al repo de conocimiento del equipo. Tus compañ
|
|
|
85
85
|
| `ozali update` | Actualiza la skill a la última versión. |
|
|
86
86
|
| `ozali sync` | Sube el histórico/memoria al repo de equipo. |
|
|
87
87
|
| `ozali sync --import` | Baja lo que el equipo ya guardó. |
|
|
88
|
+
| `ozali audit` | Navega/audita la memoria de Engram (qué se ha hecho). |
|
|
88
89
|
|
|
89
90
|
¿Algo no funciona? Corre **`ozali doctor`** primero: te dice qué falta y cómo arreglarlo.
|
|
90
91
|
|
|
@@ -119,6 +120,23 @@ Ejemplo — permitir Docker además de lo de fábrica:
|
|
|
119
120
|
|
|
120
121
|
---
|
|
121
122
|
|
|
123
|
+
## ozali-jarvis: el orquestador (no necesitas /cdk)
|
|
124
|
+
|
|
125
|
+
`init` crea **ozali-jarvis**, el "cerebro" por defecto de tu proyecto. A partir de ahí, **en cada
|
|
126
|
+
sesión y sin escribir `/cdk`**, tu agente:
|
|
127
|
+
|
|
128
|
+
- **recuerda** lo que el equipo trabajó antes (recupera contexto de Engram al iniciar),
|
|
129
|
+
- **registra** lo que van haciendo (decisiones, acciones, aprendizajes) para que quede acumulado,
|
|
130
|
+
- y cuando toca **escribir código con disciplina**, delega en la skill `cdk` (plan, aprobación, TDD).
|
|
131
|
+
|
|
132
|
+
Sigues pudiendo invocar `/cdk` cuando quieras; jarvis simplemente lo usa como referencia y mantiene
|
|
133
|
+
todo en contexto. ¿No lo quieres? `init --no-jarvis`.
|
|
134
|
+
|
|
135
|
+
### Menos tokens, más contexto
|
|
136
|
+
Con Engram en línea, jarvis y `cdk` trabajan **recall-first**: antes de releer archivos o re-analizar,
|
|
137
|
+
reutilizan el análisis/resumen que ya está en memoria (si el código no cambió). Eso gasta **menos
|
|
138
|
+
tokens** y mantiene el contexto enfocado. Puedes ver la tendencia de consumo con `ozali doctor`.
|
|
139
|
+
|
|
122
140
|
## Engram: la memoria del equipo (opcional)
|
|
123
141
|
|
|
124
142
|
Durante `init`, ozali **instala Engram por ti**: en modo interactivo te pregunta (basta con aceptar),
|
|
@@ -142,6 +160,17 @@ Más detalle técnico en [engram-convention.md](../skill/references/engram-conve
|
|
|
142
160
|
|
|
143
161
|
---
|
|
144
162
|
|
|
163
|
+
## Auditar lo que se ha hecho (`ozali audit`)
|
|
164
|
+
|
|
165
|
+
¿Quieres ver qué ha registrado el equipo? `ozali audit`:
|
|
166
|
+
|
|
167
|
+
- **Dentro de un repo:** te propone auditar **ese proyecto** o **todos** (general).
|
|
168
|
+
- **Fuera de un repo** (una ruta cualquiera): va directo a la auditoría **general**.
|
|
169
|
+
|
|
170
|
+
Atajos: `ozali audit --tui` abre un navegador interactivo de la memoria; `ozali audit --search "rfc"`
|
|
171
|
+
busca un término; `ozali audit --general` fuerza el alcance general. Si no tienes Engram, audita el
|
|
172
|
+
histórico local de `.ozali/docs/`.
|
|
173
|
+
|
|
145
174
|
## Preguntas frecuentes
|
|
146
175
|
|
|
147
176
|
**¿Tengo que usar Engram?**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ozali",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Bootstrap interactivo de IA por equipo: calibra el proyecto, genera la skill de ejecución (cdk) con TDD/SDD, y mantiene memoria de equipo (Engram) con histórico aislado.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
"files": [
|
|
13
13
|
"cli/",
|
|
14
14
|
"skill/",
|
|
15
|
+
"templates/",
|
|
15
16
|
"docs/",
|
|
16
17
|
"README.md",
|
|
17
18
|
"LICENSE"
|
package/skill/SKILL.md
CHANGED
|
@@ -308,12 +308,25 @@ recuperable, acumulativo). Así la herramienta **aprende de lo que el equipo va
|
|
|
308
308
|
completo (naming, llamadas inline, degradación) en
|
|
309
309
|
[`references/engram-convention.md`](references/engram-convention.md).
|
|
310
310
|
|
|
311
|
+
> **Orquestador por defecto (`ozali-jarvis`):** `ozali init` crea el agente **ozali-jarvis**
|
|
312
|
+
> (persona en CLAUDE.md/AGENTS.md + subagente + hooks) que opera **memoria-aware en toda sesión, sin
|
|
313
|
+
> necesidad de `/cdk`**: recupera contexto de Engram al iniciar, registra el trabajo del equipo
|
|
314
|
+
> conforme avanza, y **delega la ejecución disciplinada en `cdk`**. `cdk` y jarvis comparten el mismo
|
|
315
|
+
> contrato de memoria (esta convención).
|
|
316
|
+
|
|
311
317
|
**Mínimos que `cdk` debe cablear** (resumen; el detalle y los `mem_*` exactos están en la convención):
|
|
312
318
|
|
|
313
|
-
- **Al iniciar un hito** →
|
|
314
|
-
`
|
|
315
|
-
|
|
316
|
-
|
|
319
|
+
- **Al iniciar un hito** → handshake "Engram en línea" (`mem_current_project` + `mem_context`) +
|
|
320
|
+
`mem_save_prompt` (captura el prompt real una vez) + `mem_search` de `cdk/{hito}/state`,
|
|
321
|
+
`cdk/_project/testing-capabilities`, `cdk/_project/token-metrics` y `cdk/{hito}/*` →
|
|
322
|
+
`mem_get_observation` **selectivo** de lo relevante. Si hay `state` con pendientes, **reanuda**; con
|
|
323
|
+
`testing-capabilities` resuelve el modo TDD.
|
|
324
|
+
- **Recall-first (ahorro de tokens/contexto)** → antes de releer/re-analizar, reusar el
|
|
325
|
+
`analisis`/`resumen-tecnico` previo si su `ultimo_commit` sigue vigente (guard de staleness); si el
|
|
326
|
+
código cambió, re-analizar solo el delta. Recuperación selectiva y restauración tras compactación
|
|
327
|
+
desde `state`+`bitacora`. Ver convención §7.
|
|
328
|
+
- **Telemetría** → espejar `uso-tokens` a `cdk/{hito}/uso-tokens`, agregar en
|
|
329
|
+
`cdk/_project/token-metrics` y escribir `.ozali/metrics/token-metrics.json` (lo lee `ozali doctor`).
|
|
317
330
|
- **Al aprobar el GATE** → `mem_save` de `cdk/{hito}/plan-aprobado` **una sola vez** (inmutable).
|
|
318
331
|
- **Durante el ciclo** → `mem_update` (upsert) de `cdk/{hito}/bitacora` y `cdk/{hito}/state` tras
|
|
319
332
|
cada transición de fase (el archivo `05` sigue siendo append-only como fuente de verdad).
|
|
@@ -55,13 +55,15 @@ Para cada subagente, `cdk` debe generar un `.claude/agents/<rol>.md` con frontma
|
|
|
55
55
|
### 3. project-analyzer
|
|
56
56
|
- **Misión:** entender el código existente y el impacto del cambio. *No se planea lo que
|
|
57
57
|
no se ha entendido: primero el terreno, después el plano.*
|
|
58
|
-
- **Responsabilidades:** **primero
|
|
59
|
-
`
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
**
|
|
58
|
+
- **Responsabilidades:** **recall antes de recomputar** — primero `mem_search` del `analisis`/
|
|
59
|
+
`resumen-tecnico` previo del área; si existe con `ultimo_commit` vigente, **reúsalo** (vía
|
|
60
|
+
`mem_get_observation` selectivo) en vez de releer archivos grandes; si el código cambió, analiza
|
|
61
|
+
**solo el delta** (convención §7). Ejecuta **siempre** el harness
|
|
62
|
+
`node .claude/skills/cdk/verify-structure.mjs --grep <palabra-clave>` (estructural, barato) como
|
|
63
|
+
**base factual** (estructura real, clases clave, discrepancias doc↔código, candidatos a
|
|
64
|
+
**reutilización antes de crear**). Luego mapea dependencias, identifica **hitos de importancia** y
|
|
65
|
+
riesgos, superficies afectadas y contratos en riesgo. Marca los riesgos **🔴 BLOQUEANTES** que
|
|
66
|
+
detienen el flujo hasta resolverse. Es el autor del **Documento 1 (análisis)**.
|
|
65
67
|
- **Entrada:** tarea. **Salida:** informe de impacto + hitos (+ preguntas abiertas al usuario).
|
|
66
68
|
- **Herramientas:** solo lectura (Read, Grep, Glob) + ejecución del harness (Bash/PowerShell
|
|
67
69
|
acotado a `node verify-structure.mjs`).
|
|
@@ -70,7 +72,10 @@ Para cada subagente, `cdk` debe generar un `.claude/agents/<rol>.md` con frontma
|
|
|
70
72
|
- **Misión:** enrutar el trabajo entre subagentes e integrar resultados.
|
|
71
73
|
- **Responsabilidades:** decide qué subagente actúa y en qué orden; gestiona iteraciones
|
|
72
74
|
executioners ⇄ tester; consolida la salida final. **Alimenta al `project-documenter`** con
|
|
73
|
-
los eventos del ciclo (dudas, decisiones, desvíos) para la bitácora `05`.
|
|
75
|
+
los eventos del ciclo (dudas, decisiones, desvíos) para la bitácora `05`. **Hace cumplir el
|
|
76
|
+
recall-first** (convención §7): recuperación selectiva (no volcar toda la memoria), restauración
|
|
77
|
+
desde `state`+`bitacora` tras compactación, y consulta `cdk/_project/token-metrics` al iniciar para
|
|
78
|
+
ajustar la agresividad del recall.
|
|
74
79
|
- **Modo `--auto`:** si el hito se invocó con `--auto`, recorre el hito de punta a punta
|
|
75
80
|
**sin prompts** —incluida la **lectura de rutas no contempladas** durante el análisis y la
|
|
76
81
|
ejecución post-GATE— con el **único alto en el 🛑 GATE del plan**. Si algo cae **fuera de
|
|
@@ -117,6 +122,8 @@ Para cada subagente, `cdk` debe generar un `.claude/agents/<rol>.md` con frontma
|
|
|
117
122
|
- `03-resumen-tecnico.md`, `04-resumen-usuario.md` y `06-uso-tokens.md` se escriben **al
|
|
118
123
|
cierre**. El `06` solo rellena métricas si el proveedor es **Claude/Anthropic** (fuente
|
|
119
124
|
`/cost`); con otro proveedor deja la estructura + nombre del proveedor y métricas `N/A`.
|
|
125
|
+
Además **espeja** `06` a Engram (`cdk/{hito}/uso-tokens`), actualiza el agregado
|
|
126
|
+
`cdk/_project/token-metrics` y escribe `.ozali/metrics/token-metrics.json` (lo lee `ozali doctor`).
|
|
120
127
|
- **Herramientas:** escritura de documentación (Read, Write).
|
|
121
128
|
|
|
122
129
|
### 8. tester
|
|
@@ -291,6 +291,13 @@ usuario, en:
|
|
|
291
291
|
| Total de tiempo de sesión | <n / N/A> |
|
|
292
292
|
| Costo estimado | <$ / N/A> |
|
|
293
293
|
|
|
294
|
+
## Ahorro por recall (Engram)
|
|
295
|
+
| Métrica | Valor |
|
|
296
|
+
| :-- | :-- |
|
|
297
|
+
| ¿Se reusó memoria? (recall-first) | sí / no |
|
|
298
|
+
| Artefactos reusados | <p. ej. cdk/<hito>/analisis @ <commit>> |
|
|
299
|
+
| Relectura evitada (estimada) | <n tokens / N/A> |
|
|
300
|
+
|
|
294
301
|
## Notas
|
|
295
302
|
- <observaciones sobre el consumo del hito; deja vacío o "N/A" si no puedes encontrar información del proveedor>
|
|
296
303
|
```
|
|
@@ -299,3 +306,23 @@ usuario, en:
|
|
|
299
306
|
> derivar ni inventar cifras de otro sistema: solo registra el nombre del proveedor y deja
|
|
300
307
|
> el resto en `N/A`. El objetivo es **mantener la estructura presente** para no perder la
|
|
301
308
|
> trazabilidad del hito.
|
|
309
|
+
|
|
310
|
+
### Espejo en Engram + agregado (telemetría)
|
|
311
|
+
|
|
312
|
+
Al cerrar, además del doc, `cdk` **espeja** las métricas a Engram y mantiene un agregado por
|
|
313
|
+
proyecto (ver [`engram-convention.md`](engram-convention.md) §7):
|
|
314
|
+
|
|
315
|
+
- `cdk/{hito}/uso-tokens` — métricas del hito (mismo contenido que el doc).
|
|
316
|
+
- `cdk/_project/token-metrics` — agregado de los últimos hitos (para ajustar el recall-first).
|
|
317
|
+
- `.ozali/metrics/token-metrics.json` — copia **local** que lee `ozali doctor` para la tendencia:
|
|
318
|
+
|
|
319
|
+
```json
|
|
320
|
+
{
|
|
321
|
+
"hits": [
|
|
322
|
+
{ "hito": "<slug>", "input": 0, "output": 0, "total": 0, "savedByRecall": 0, "at": "<ISO-8601>" }
|
|
323
|
+
]
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
> `savedByRecall` = estimación de tokens de relectura evitados por reusar memoria. Con proveedor sin
|
|
328
|
+
> métricas, deja `0`/`N/A` pero mantén la estructura.
|
|
@@ -222,3 +222,50 @@ Internamente (Fase C lo implementa):
|
|
|
222
222
|
|
|
223
223
|
> Trazabilidad: cada doc lleva `ultimo_commit`/`Commit:` del repo principal en su encabezado, así
|
|
224
224
|
> el histórico aislado siempre apunta al código exacto que documenta.
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## 7. Rendimiento: recall-first + telemetría de tokens
|
|
229
|
+
|
|
230
|
+
El objetivo de esta sección es **gastar menos tokens y contexto** reutilizando lo que el equipo ya
|
|
231
|
+
guardó en Engram, en vez de recomputarlo. Aplica tanto al uso **ambiental** (orquestador
|
|
232
|
+
`ozali-jarvis`, sin `/cdk`) como por **hito** (`cdk`).
|
|
233
|
+
|
|
234
|
+
### 7.1 Handshake "Engram en línea"
|
|
235
|
+
Al iniciar sesión/hito: `mem_current_project` (confirma que coincide con `.engram/config.json`) +
|
|
236
|
+
`mem_context`. Si responde → **modo recall-first activo**. Si no responde → degrada a `docs` (§1);
|
|
237
|
+
la ausencia de Engram nunca bloquea.
|
|
238
|
+
|
|
239
|
+
### 7.2 Recall-first con guard de staleness
|
|
240
|
+
**Antes** de releer archivos o re-analizar, `mem_search` del `analisis` / `plan-aprobado` /
|
|
241
|
+
`resumen-tecnico` del área. Cada artefacto lleva `ultimo_commit` (§6):
|
|
242
|
+
|
|
243
|
+
- Si el **SHA actual coincide** (o los archivos del área no cambiaron) → **reusar** el resumen
|
|
244
|
+
conciso vía `mem_get_observation`, **sin releer el código**. Ahorra tokens de entrada.
|
|
245
|
+
- Si **cambió** → re-analizar **solo el delta** (los archivos tocados desde `ultimo_commit`), no todo.
|
|
246
|
+
|
|
247
|
+
> El harness `verify-structure.mjs` (estructural, barato) puede seguir corriendo; lo que se evita es
|
|
248
|
+
> **releer archivos grandes** que ya tienen un resumen vigente en memoria.
|
|
249
|
+
|
|
250
|
+
### 7.3 Recuperación selectiva
|
|
251
|
+
`mem_search` devuelve **previews truncados**. Haz `mem_get_observation` **solo** de los pocos
|
|
252
|
+
artefactos que realmente necesitas. **Nunca** vuelques toda la memoria al contexto.
|
|
253
|
+
|
|
254
|
+
### 7.4 Restauración tras compactación
|
|
255
|
+
Tras una compactación (o reabrir sesión), reconstruye desde `state` (§4) + `bitacora` —ambos
|
|
256
|
+
concisos— en vez de re-derivar todo el contexto releyendo conversación y archivos.
|
|
257
|
+
|
|
258
|
+
### 7.5 Guardar conciso para recall barato
|
|
259
|
+
Guarda resúmenes **estructurados y deduplicados** (no volcados crudos). Mientras más conciso el
|
|
260
|
+
artefacto, menos cuesta recuperarlo después.
|
|
261
|
+
|
|
262
|
+
### 7.6 Telemetría de tokens (medir → mejorar)
|
|
263
|
+
- Espeja el uso de tokens del hito a `cdk/{hito}/uso-tokens` y mantén un agregado por proyecto en
|
|
264
|
+
`cdk/_project/token-metrics`.
|
|
265
|
+
- También escribe un resumen **local** en `.ozali/metrics/token-metrics.json` (últimos N hitos:
|
|
266
|
+
`{ hito, input, output, total, savedByRecall, at }`) para que `ozali doctor` muestre la tendencia
|
|
267
|
+
sin consultar el MCP.
|
|
268
|
+
- **Al iniciar**, recupera `cdk/_project/token-metrics`: si hitos similares fueron pesados, sé más
|
|
269
|
+
agresivo con recall-first (resume antes, evita relecturas grandes).
|
|
270
|
+
|
|
271
|
+
> `savedByRecall` = estimación de relectura evitada por reusar memoria (ver plantilla `06-uso-tokens`).
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// ozali-jarvis-plugin — recordatorios de memoria (Engram) para opencode.
|
|
2
|
+
// Generado por `ozali init`. NO es la fuente de verdad del comportamiento:
|
|
3
|
+
// la persona ozali-jarvis vive en AGENTS.md + el agente de opencode.json.
|
|
4
|
+
// Este plugin solo refuerza, en eventos de sesión, el ciclo recall → capturar → resumir.
|
|
5
|
+
// Marca de idempotencia: ozali-jarvis (no reescribir si ya existe esta marca).
|
|
6
|
+
|
|
7
|
+
/** @type {import("@opencode-ai/plugin").Plugin} */
|
|
8
|
+
export const OzaliJarvis = async () => {
|
|
9
|
+
const note = (m) => { try { console.log(`[ozali-jarvis] ${m}`); } catch { /* noop */ } };
|
|
10
|
+
return {
|
|
11
|
+
event: async ({ event }) => {
|
|
12
|
+
switch (event && event.type) {
|
|
13
|
+
case "session.created":
|
|
14
|
+
note("recall-first: confirma proyecto (mem_current_project) y recupera contexto de Engram (mem_context) antes de actuar.");
|
|
15
|
+
break;
|
|
16
|
+
case "experimental.session.compacting":
|
|
17
|
+
note("antes de compactar: persiste el `state` recuperable en Engram (ver engram-convention §4).");
|
|
18
|
+
break;
|
|
19
|
+
case "session.idle":
|
|
20
|
+
note("cierre: registra lo trabajado en Engram (scope: project, español) y haz mem_session_summary.");
|
|
21
|
+
break;
|
|
22
|
+
default:
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ozali-jarvis
|
|
3
|
+
description: Orquestador principal del proyecto. Memoria-aware por defecto (Engram) y puente hacia la skill cdk. Úsalo/actúa como jarvis para CUALQUIER trabajo en el repo — recupera contexto del equipo antes de actuar, registra decisiones y aprendizajes conforme avanzas (aunque no se invoque /cdk), y delega la ejecución disciplinada de código en cdk.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ozali-jarvis — orquestador del proyecto
|
|
7
|
+
|
|
8
|
+
Eres **ozali-jarvis**, el orquestador por defecto de este repositorio. Tu trabajo es mantener al
|
|
9
|
+
equipo **en contexto** usando la memoria de Engram y delegar la ejecución de código a la skill
|
|
10
|
+
`cdk`. Operas así en **toda** sesión, sin necesidad de que el usuario escriba `/cdk`.
|
|
11
|
+
|
|
12
|
+
> Engram ya inyecta su **Memory Protocol** en la superficie de instrucciones (vía `engram setup`).
|
|
13
|
+
> Esta persona lo **complementa**: no dupliques sus reglas; añade la orquestación y el wiring a `cdk`.
|
|
14
|
+
|
|
15
|
+
## 1. Al iniciar (recall-first)
|
|
16
|
+
- Confirma el proyecto con `mem_current_project` (debe coincidir con `.engram/config.json`).
|
|
17
|
+
- Recupera contexto reciente del equipo: `mem_context` + `mem_search` de lo relacionado a la tarea.
|
|
18
|
+
- **Recuperación selectiva:** `mem_search` devuelve previews truncados; haz `mem_get_observation`
|
|
19
|
+
**solo** de lo que necesitas. Nunca vuelques toda la memoria al contexto.
|
|
20
|
+
- **No recomputes lo ya sabido:** si existe un `analisis`/`resumen-tecnico`/`plan-aprobado` del área
|
|
21
|
+
con `ultimo_commit` vigente, reúsalo en vez de releer el código (ver
|
|
22
|
+
`references/engram-convention.md` §7, *recall-first con guard de staleness*).
|
|
23
|
+
|
|
24
|
+
## 2. Durante el trabajo (captura ambiental, en español, scope: project)
|
|
25
|
+
- Registra en Engram las **decisiones, acciones y aprendizajes** del equipo conforme ocurren —
|
|
26
|
+
**aunque no se haya invocado `/cdk`**. Así el trabajo queda acumulado y buscable.
|
|
27
|
+
- Guarda **conciso y estructurado** (no volcados crudos) para que el recall futuro cueste menos.
|
|
28
|
+
- Idioma y scope según `references/engram-convention.md` §1.5: memoria compartida en **español**,
|
|
29
|
+
`scope: project`; lo puramente personal va en `scope: personal`.
|
|
30
|
+
|
|
31
|
+
## 3. Para ejecutar cambios de código → delega en `cdk`
|
|
32
|
+
- Cuando el trabajo implique escribir/modificar código con disciplina (plan, GATE, TDD, docs por
|
|
33
|
+
hito), **usa la skill `cdk`**. jarvis prepara el contexto; `cdk` ejecuta.
|
|
34
|
+
- Si `cdk` aún no existe en el proyecto, sugiere generarla corriendo la skill **`ozali`**
|
|
35
|
+
("diagnostica el proyecto").
|
|
36
|
+
|
|
37
|
+
## 4. Al cerrar
|
|
38
|
+
- Cierra la sesión con `mem_session_summary` (solo el agente top-level, nunca un subagente) — qué se
|
|
39
|
+
logró, decisiones clave y pendientes.
|
|
40
|
+
- Antes de una compactación de contexto, persiste el `state` recuperable (ver convención §4).
|
|
41
|
+
|
|
42
|
+
## Reglas duras
|
|
43
|
+
- La memoria **nunca bloquea**: si Engram no responde, sigue en modo `docs` y anótalo.
|
|
44
|
+
- **Persistir antes de responder** cuando guardes en Engram dentro de un subagente.
|
|
45
|
+
- No inventes contenido de memoria; si no hay contexto, no fabriques.
|