ozali 0.5.0 → 0.6.1

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 CHANGED
@@ -59,9 +59,9 @@ git clone <repo> && node ozali/cli/bin/ozali.mjs init
59
59
  ### Uso del CLI
60
60
 
61
61
  ```bash
62
- ozali init # detecta agente, instala la skill, aísla el histórico, instala+configura Engram
63
- ozali doctor # health-check read-only (fuente de verdad, Engram, Cloud, Strict TDD, runner…)
64
- ozali update # actualiza la instalación (skill ozali + ozali-jarvis + permisos) al paquete
62
+ ozali init # detecta agente, instala skills ozali + ozali-commit, aísla histórico, configura Engram
63
+ ozali doctor # health-check read-only (fuente de verdad, Engram, Cloud, versión de cdk, Strict TDD…)
64
+ ozali update # actualiza skills ozali + ozali-commit + ozali-jarvis + permisos; avisa si cdk quedó atrás
65
65
  ozali sync # lleva el histórico (docs + Engram) al repo de conocimiento de equipo
66
66
  ozali audit # navega/audita la memoria de Engram del proyecto (o general)
67
67
  ```
@@ -78,10 +78,17 @@ pnpm add -g ozali@<versión> # actualiza el CLI global (pinea la versión por
78
78
  ozali update # en cada repo: refresca skill ozali + ozali-jarvis + permisos
79
79
  ```
80
80
 
81
- `ozali update` también **crea ozali-jarvis** en repos inicializados con versiones anteriores. La skill
82
- `cdk` la **regenera tu agente** (no el CLI): tras `ozali update`, abre el agente y vuelve a correr la
83
- skill `ozali` para regenerar `cdk` con el contrato nuevo tus docs por hito y el plan congelado se
84
- conservan.
81
+ `ozali update` también **crea ozali-jarvis** y **instala la skill `ozali-commit`** en repos
82
+ inicializados con versiones anteriores. La skill `cdk` la **regenera/migra tu agente** (no el CLI):
83
+ `ozali update` **detecta la versión de contrato** de `cdk` y, si quedó atrás (o aún referencia
84
+ `copsis-commit` de versiones viejas), te avisa y te da los pasos manuales. Tras `ozali update`, abre
85
+ el agente y vuelve a correr la skill `ozali`: su **pre-flight** detecta el `cdk` existente, lo **migra
86
+ automáticamente** al contrato vigente (eliminando `copsis-commit` y cableando `ozali-commit`) y
87
+ estampa la versión — tus docs por hito y el plan congelado se conservan.
88
+
89
+ > **Commit del hito (`ozali-commit`):** `init`/`update` instalan la skill `ozali-commit`
90
+ > (`.claude/skills/ozali-commit/`). `cdk` la invoca al cerrar cada hito
91
+ > para generar el commit convencional (feat/fix/hotfix/refactor + scope) tras tu aprobación.
85
92
 
86
93
  `init` también escribe un **perfil base de permisos** (`.claude/settings.json` para Claude Code,
87
94
  `opencode.json` para opencode) para reducir confirmaciones: deja libres comandos seguros y bloquea
@@ -125,6 +132,7 @@ Claude Code y opencode (perfiles de permisos para ambos en
125
132
  | Desplegar Engram Cloud (Google Cloud) | [docs/deploy-cloud-gcloud.md](docs/deploy-cloud-gcloud.md) |
126
133
  | Skill bootstrap | [skill/SKILL.md](skill/SKILL.md) |
127
134
  | Calibración de testing + TDD | [skill/references/calibration-blueprint.md](skill/references/calibration-blueprint.md) |
135
+ | Contrato y versión de `cdk` | [skill/references/cdk-contract.md](skill/references/cdk-contract.md) |
128
136
  | Blueprint de agentes | [skill/references/agents-blueprint.md](skill/references/agents-blueprint.md) |
129
137
  | Memoria híbrida (docs + Engram) | [skill/references/engram-convention.md](skill/references/engram-convention.md) |
130
138
 
@@ -137,7 +145,7 @@ Claude Code y opencode (perfiles de permisos para ambos en
137
145
 
138
146
  > Siguiente paso natural: crear el repo en GitHub y publicar la primera versión (`pnpm publish` vía tag), más probar el bootstrap en un repo real del equipo.
139
147
 
140
- > Linaje: `ozali` evoluciona la skill `copsis-doctor`, nutrida con conceptos de
148
+ > Linaje: `ozali` se nutre de conceptos de
141
149
  > [gentle-ai](https://github.com/Gentleman-Programming/gentle-ai) (Engram, calibración SDD/TDD,
142
150
  > distribución plug-n-play).
143
151
 
package/cli/bin/ozali.mjs CHANGED
@@ -12,10 +12,11 @@ ${c.bold("Uso:")}
12
12
  ozali <comando> [opciones]
13
13
 
14
14
  ${c.bold("Comandos:")}
15
- init Detecta el agente (Claude Code/opencode), instala la skill ozali,
16
- aísla el histórico, configura Engram y el repo de conocimiento.
17
- doctor Health-check read-only del proyecto (fuente de verdad, Engram, TDD…).
18
- update Actualiza la instalación (skill ozali + ozali-jarvis + permisos) al paquete.
15
+ init Detecta el agente (Claude Code/opencode), instala las skills ozali y
16
+ ozali-commit, aísla el histórico, configura Engram y el repo de conocimiento.
17
+ doctor Health-check read-only del proyecto (fuente de verdad, Engram, versión de cdk, TDD…).
18
+ update Actualiza la instalación (skills ozali + ozali-commit + ozali-jarvis + permisos)
19
+ al paquete y avisa si la skill cdk quedó desactualizada.
19
20
  sync Sincroniza el histórico (docs + Engram) con el repo de conocimiento.
20
21
  audit Navega/audita la memoria de Engram del proyecto (o general).
21
22
  cloud Gestiona Engram Cloud: status, upgrade, repair, dashboard, config.
@@ -4,7 +4,7 @@ import path from "node:path";
4
4
  import os from "node:os";
5
5
  import {
6
6
  c, ok, warn, err, info, step,
7
- SKILL_SRC, TEMPLATES_SRC, exists, ensureDir, copyDir, readJSON, writeJSON,
7
+ SKILL_SRC, COMMIT_SKILL_SRC, TEMPLATES_SRC, exists, ensureDir, copyDir, readJSON, writeJSON,
8
8
  ensureGitignore, tryExec, spawnCmd, which, engramAssetName,
9
9
  projectName, pkgVersion, DEFAULT_KNOWLEDGE, HOME, openURL,
10
10
  } from "./util.mjs";
@@ -23,6 +23,11 @@ function skillTarget(cwd, scope) {
23
23
  return path.join(base, "skills", "ozali");
24
24
  }
25
25
 
26
+ function commitSkillTarget(cwd, scope) {
27
+ const base = scope === "global" ? path.join(process.env.HOME || "", ".claude") : path.join(cwd, ".claude");
28
+ return path.join(base, "skills", "ozali-commit");
29
+ }
30
+
26
31
  function readTeamCloud(cwd) {
27
32
  return readJSON(TEAM_CLOUD_PATH(cwd));
28
33
  }
@@ -166,11 +171,14 @@ export async function init(cwd, opts) {
166
171
 
167
172
  // --- acciones ---
168
173
  step("Aplicando");
169
- // 1) copiar skill
174
+ // 1) copiar skill ozali (bootstrap) + ozali-commit (commit convencional)
170
175
  const target = skillTarget(cwd, scope);
171
176
  ensureDir(path.dirname(target));
172
177
  copyDir(SKILL_SRC, target);
173
178
  ok(`Skill instalada en ${c.bold(path.relative(cwd, target) || target)}.`);
179
+ const commitTarget = commitSkillTarget(cwd, scope);
180
+ copyDir(COMMIT_SKILL_SRC, commitTarget);
181
+ ok(`Skill ${c.bold("ozali-commit")} instalada en ${c.bold(path.relative(cwd, commitTarget) || commitTarget)}.`);
174
182
 
175
183
  // 2) perfiles base de permisos (idempotentes, merge mínimo) por agente
176
184
  if (agent === "claude-code" || agent === "both") {
@@ -761,6 +769,19 @@ export function doctor(cwd) {
761
769
  add("Node ≥ 16", env.node.ok, env.node.version);
762
770
  add("Fuente de verdad", env.sot.found, env.sot.found ? `${env.sot.doc} + ${env.sot.dir}/` : "ausente (corre la skill 'ozali')");
763
771
  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)");
772
+ // Skill cdk (la genera el agente): versión de contrato vs. la vigente del paquete.
773
+ const cdkInfo = detectCdk(cwd);
774
+ const cdkN = cdkCanonicalVersion();
775
+ if (!cdkInfo.installed) {
776
+ add("Skill cdk", true, "no generada aún (corre la skill 'ozali' en tu agente)");
777
+ } else if (cdkInfo.version != null && cdkInfo.version >= cdkN && !cdkInfo.hasCopsis) {
778
+ add("Skill cdk", true, `contrato v${cdkInfo.version} (al día)`);
779
+ } else {
780
+ const reason = cdkInfo.hasCopsis ? "contiene copsis-commit → migra con la skill 'ozali'"
781
+ : cdkInfo.version == null ? "sin versión de contrato (legado) → migra con la skill 'ozali'"
782
+ : `contrato v${cdkInfo.version} < v${cdkN} → migra con la skill 'ozali'`;
783
+ add("Skill cdk", false, reason);
784
+ }
764
785
  add("Engram", env.engram.available, env.engram.available ? env.engram.bin : "no instalado → modo docs");
765
786
  if (env.engram.available) {
766
787
  const online = tryExec("engram", ["doctor"], { cwd }) !== null;
@@ -899,10 +920,15 @@ export function update(cwd, opts = {}) {
899
920
  }
900
921
 
901
922
  // 1) Skill ozali (incluye las references: la base desde la que el agente regenera cdk)
923
+ // + ozali-commit (commit convencional) como skill hermana — la crea en repos previos.
902
924
  if (env.skill.installed) {
903
925
  for (const p of env.skill.paths) {
904
926
  copyDir(SKILL_SRC, p);
905
927
  ok(`Skill ozali actualizada: ${path.relative(cwd, p) || p} → v${pkgVersion()}`);
928
+ const commitDir = path.join(path.dirname(p), "ozali-commit");
929
+ const freshCommit = !exists(commitDir);
930
+ copyDir(COMMIT_SKILL_SRC, commitDir);
931
+ ok(`Skill ozali-commit ${freshCommit ? "instalada" : "actualizada"}: ${path.relative(cwd, commitDir) || commitDir}`);
906
932
  }
907
933
  } else {
908
934
  warn("Skill ozali no instalada en esta ruta (corre " + c.bold("ozali init") + " para instalarla).");
@@ -926,13 +952,27 @@ export function update(cwd, opts = {}) {
926
952
  if (agent === "opencode" || agent === "both") ensureJarvisOpencode(cwd);
927
953
  }
928
954
 
929
- // 4) Skill cdk: la genera el AGENTE (Fase 6), el CLI no la regenera.
955
+ // 4) Skill cdk: la genera/migra el AGENTE (Fase 0.5/6); el CLI solo detecta versión y guía.
930
956
  const cdk = detectCdk(cwd);
957
+ const cdkN = cdkCanonicalVersion();
931
958
  if (cdk.installed) {
932
959
  step("Skill cdk (generada por el agente)");
933
- warn("cdk no se actualiza desde el CLI: la regenera tu agente con el contrato nuevo.");
934
- info("Abre tu agente y vuelve a correr la skill " + c.bold("ozali") + " para regenerar cdk (ozali-jarvis, recall-first §7, telemetría).");
935
- info("Tus docs por hito (" + c.bold(".ozali/docs/cdk/") + ") y el plan congelado se conservan.");
960
+ const upToDate = cdk.version != null && cdk.version >= cdkN && !cdk.hasCopsis;
961
+ if (upToDate) {
962
+ ok(`cdk al día (contrato v${cdk.version}).`);
963
+ } else {
964
+ const reason = cdk.version == null ? "sin versión de contrato (cdk legado)"
965
+ : cdk.version < cdkN ? `contrato v${cdk.version} < v${cdkN} (desactualizado)`
966
+ : "contiene referencias a copsis-commit";
967
+ warn(`cdk desactualizada: ${reason}.`);
968
+ if (cdk.hasCopsis) warn("Detectadas referencias a " + c.bold("copsis-commit") + " (heredadas de versiones anteriores).");
969
+ info("El CLI no regenera cdk. Actualízala manualmente desde tu agente:");
970
+ info(" 1. Abre tu agente en este proyecto.");
971
+ info(" 2. Corre la skill " + c.bold("ozali") + " (escribe " + c.bold('"ozali"') + "): el pre-flight migra cdk al contrato " + c.bold("v" + cdkN) + ", elimina copsis-commit y cablea ozali-commit.");
972
+ info("Tus docs por hito (" + c.bold(".ozali/docs/cdk/") + ") y el plan congelado se conservan.");
973
+ }
974
+ } else {
975
+ info("cdk aún no generada en este repo. Corre la skill " + c.bold("ozali") + " en tu agente para crearla.");
936
976
  }
937
977
 
938
978
  // 5) versión del config
@@ -941,13 +981,39 @@ export function update(cwd, opts = {}) {
941
981
  return 0;
942
982
  }
943
983
 
944
- /** ¿Existe la skill cdk (generada por el agente)? */
984
+ /**
985
+ * ¿Existe la skill cdk (generada por el agente)? Devuelve además la versión de contrato
986
+ * estampada en su frontmatter (`cdk_contract_version`) y si aún referencia `copsis-commit`.
987
+ * version === null ⇒ cdk legado (sin marcador de versión).
988
+ */
945
989
  function detectCdk(cwd) {
946
990
  const paths = [
947
991
  path.join(cwd, ".claude", "skills", "cdk", "SKILL.md"),
948
992
  path.join(HOME, ".claude", "skills", "cdk", "SKILL.md"),
949
993
  ].filter(exists);
950
- return { installed: paths.length > 0, paths };
994
+ if (paths.length === 0) return { installed: false, paths: [], version: null, hasCopsis: false };
995
+ let version = null;
996
+ let hasCopsis = false;
997
+ for (const f of paths) {
998
+ const txt = fs.readFileSync(f, "utf8");
999
+ if (version === null) {
1000
+ const m = txt.match(/cdk_contract_version:\s*(\d+)/i);
1001
+ if (m) version = parseInt(m[1], 10);
1002
+ }
1003
+ if (/copsis-commit/i.test(txt)) hasCopsis = true;
1004
+ }
1005
+ return { installed: true, paths, version, hasCopsis };
1006
+ }
1007
+
1008
+ /** Versión de contrato vigente de cdk (fuente única: skill/references/cdk-contract.md del paquete). */
1009
+ function cdkCanonicalVersion() {
1010
+ try {
1011
+ const txt = fs.readFileSync(path.join(SKILL_SRC, "references", "cdk-contract.md"), "utf8");
1012
+ const m = txt.match(/CDK_CONTRACT_VERSION:\s*(\d+)/i);
1013
+ return m ? parseInt(m[1], 10) : 1;
1014
+ } catch {
1015
+ return 1;
1016
+ }
951
1017
  }
952
1018
 
953
1019
  // ============================================================= sync ===========
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 COMMIT_SKILL_SRC = path.join(PKG_ROOT, "skill-commit");
13
14
  export const TEMPLATES_SRC = path.join(PKG_ROOT, "templates");
14
15
 
15
16
  export function pkgVersion() {
@@ -133,6 +133,80 @@ test("update agrega ozali-jarvis en un repo que no lo tenía", () => {
133
133
  }
134
134
  });
135
135
 
136
+ function initRepo(dir) {
137
+ run(["init", "--yes", "--no-engram", "--no-trust", "--no-jarvis", "--agent", "claude-code", "--scope", "project", "--knowledge-repo", path.join(dir, ".k")], dir);
138
+ }
139
+
140
+ function writeCdkStub(dir, body) {
141
+ const f = path.join(dir, ".claude", "skills", "cdk", "SKILL.md");
142
+ fs.mkdirSync(path.dirname(f), { recursive: true });
143
+ fs.writeFileSync(f, body);
144
+ return f;
145
+ }
146
+
147
+ test("init instala la skill ozali-commit", () => {
148
+ const dir = tmpProject();
149
+ try {
150
+ initRepo(dir);
151
+ assert.ok(fs.existsSync(path.join(dir, ".claude", "skills", "ozali-commit", "SKILL.md")), "ozali-commit SKILL.md instalada");
152
+ } finally {
153
+ fs.rmSync(dir, { recursive: true, force: true });
154
+ }
155
+ });
156
+
157
+ test("doctor marca cdk al día cuando la versión de contrato coincide", () => {
158
+ const dir = tmpProject();
159
+ try {
160
+ initRepo(dir);
161
+ writeCdkStub(dir, "---\nname: cdk\ncdk_contract_version: 1\n---\n# cdk\n");
162
+ const { stdout } = run(["doctor"], dir, true);
163
+ assert.match(stdout, /Skill cdk/, "doctor reporta la fila Skill cdk");
164
+ assert.match(stdout, /contrato v1 \(al día\)/, "doctor marca cdk al día");
165
+ } finally {
166
+ fs.rmSync(dir, { recursive: true, force: true });
167
+ }
168
+ });
169
+
170
+ test("doctor marca cdk desactualizada si referencia copsis-commit", () => {
171
+ const dir = tmpProject();
172
+ try {
173
+ initRepo(dir);
174
+ writeCdkStub(dir, "---\nname: cdk\ncdk_contract_version: 1\n---\n# cdk\ninvoca copsis-commit al cierre del hito\n");
175
+ const { stdout } = run(["doctor"], dir, true);
176
+ assert.match(stdout, /copsis-commit/, "doctor avisa de copsis-commit");
177
+ } finally {
178
+ fs.rmSync(dir, { recursive: true, force: true });
179
+ }
180
+ });
181
+
182
+ test("update avisa y da pasos manuales si cdk está desactualizada (legado)", () => {
183
+ const dir = tmpProject();
184
+ try {
185
+ initRepo(dir);
186
+ writeCdkStub(dir, "---\nname: cdk\n---\n# cdk legado que invoca copsis-commit\n");
187
+ const { stdout } = run(["update"], dir);
188
+ assert.match(stdout, /cdk desactualizada/, "update avisa de cdk desactualizada");
189
+ assert.match(stdout, /copsis-commit/, "update menciona la migración de copsis-commit");
190
+ assert.match(stdout, /skill ozali|pre-flight|migra/i, "update da pasos manuales");
191
+ } finally {
192
+ fs.rmSync(dir, { recursive: true, force: true });
193
+ }
194
+ });
195
+
196
+ test("update también instala ozali-commit en repos previos", () => {
197
+ const dir = tmpProject();
198
+ try {
199
+ initRepo(dir);
200
+ // Simula instalación previa sin ozali-commit.
201
+ fs.rmSync(path.join(dir, ".claude", "skills", "ozali-commit"), { recursive: true, force: true });
202
+ assert.ok(!fs.existsSync(path.join(dir, ".claude", "skills", "ozali-commit", "SKILL.md")), "precondición: sin ozali-commit");
203
+ run(["update"], dir);
204
+ assert.ok(fs.existsSync(path.join(dir, ".claude", "skills", "ozali-commit", "SKILL.md")), "update instala ozali-commit");
205
+ } finally {
206
+ fs.rmSync(dir, { recursive: true, force: true });
207
+ }
208
+ });
209
+
136
210
  test("init --dry-run no escribe nada", () => {
137
211
  const dir = tmpProject();
138
212
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ozali",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
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
+ "skill-commit/",
15
16
  "templates/",
16
17
  "docs/",
17
18
  "README.md",
package/skill/SKILL.md CHANGED
@@ -30,6 +30,7 @@ produce documentación, la calibración, el plan y los artefactos de la skill `c
30
30
 
31
31
  ```
32
32
  Fase 0 Identidad y registro → quién corre ozali (git/sesión)
33
+ Fase 0.5 Pre-flight de cdk → ¿existe cdk? ¿versión de contrato vigente? migrar si toca
33
34
  Fase 1 Detección fuente de verdad → ¿existe AI.md/.ai o IA.md/.ia?
34
35
  Fase 2 Generación conocimiento → si falta, evaluar el proyecto y generar AI.md + .ai/
35
36
  Fase 3 Validación + ajustes → ¿coincide con la estructura real? ajustes mínimos
@@ -69,6 +70,50 @@ Guarda estos valores; se reutilizan en Fase 6 y en la documentación.
69
70
 
70
71
  ---
71
72
 
73
+ ## Fase 0.5 — Pre-flight de `cdk` (versión de contrato + migración)
74
+
75
+ > Esta fase resuelve el caso de **un repo que ya tiene `cdk` instalada**: en vez de omitirla,
76
+ > `ozali` decide si está **al día**, **desactualizada** o **ausente**, y actúa en consecuencia.
77
+ > La **versión de contrato vigente** y las reglas de migración están en
78
+ > [`references/cdk-contract.md`](references/cdk-contract.md) — es la **fuente de verdad única**.
79
+
80
+ 1. **Lee la versión de contrato vigente `N`** del marcador `CDK_CONTRACT_VERSION` en
81
+ [`references/cdk-contract.md`](references/cdk-contract.md).
82
+ 2. **Busca el `cdk` instalado**: `.claude/skills/cdk/SKILL.md` (proyecto), luego
83
+ `~/.claude/skills/cdk/SKILL.md` (global). Si existe, lee `cdk_contract_version` de su frontmatter.
84
+ 3. **Decide y registra** la decisión (se usa en las Fases 5 y 6):
85
+
86
+ | Estado del `cdk` instalado | Decisión |
87
+ |---|---|
88
+ | **No existe** | `GENERAR` — flujo normal: continúa el bootstrap hasta el GATE (Fase 5) y genera `cdk` en la Fase 6, estampando `cdk_contract_version: N`. |
89
+ | **Existe sin `cdk_contract_version`** (cdk legado) o con valor **`< N`** | `MIGRAR` — **migración automática** (sin GATE), ver abajo. |
90
+ | **Existe con `cdk_contract_version == N`** | `AL_DÍA` — no regenerar. Infórmalo y continúa el bootstrap solo para refrescar la calibración/documentación si el usuario lo pide. |
91
+
92
+ 4. **Migración automática (`MIGRAR`)** — aplica los deltas del changelog de
93
+ [`references/cdk-contract.md`](references/cdk-contract.md) sobre el `cdk` instalado, **sin** pasar
94
+ por el GATE, porque preserva el `cdk` del usuario y solo actualiza el contrato:
95
+ - **Elimina toda referencia a la nomenclatura heredada** (`copsis-commit`, `copsis-doctor`) y
96
+ reemplázala por la de `ozali`: `copsis-commit` → **`ozali-commit`** (instalada en
97
+ `.claude/skills/ozali-commit/`); refs a plantillas o a la skill `copsis-doctor` → sus
98
+ equivalentes de `ozali`. No debe quedar ninguna mención a `copsis-*`.
99
+ - **Migra el histórico legado**: si existe `.copsis/docs/cdk/` (la ruta de `copsis-doctor`),
100
+ **mueve** su contenido a `.ozali/docs/cdk/` (fusiona **por hito**, **sin sobrescribir** lo ya
101
+ existente) y **borra** la carpeta `.copsis/` una vez vacía.
102
+ - Aplica el resto de deltas del changelog (recall-first, telemetría, etc. según la versión).
103
+ - **Estampa** `cdk_contract_version: N` en el frontmatter del `cdk`.
104
+ - **Preserva** intactos los docs ya en `.ozali/docs/cdk/` (histórico por hito) y los planes
105
+ congelados (`02-plan-aprobado.md`).
106
+ - **Reporta** al usuario, en texto, qué cambió (referencias migradas, docs reubicados de `.copsis/`
107
+ a `.ozali/`, secciones actualizadas, versión nueva).
108
+ - **Excepción → GATE:** si un delta exige **regeneración estructural** (reescribir subagentes,
109
+ cambiar el harness, rehacer el wiring de TDD), eso **no** es migración automática: preséntalo en
110
+ el plan del GATE (Fase 5) y regénéralo en la Fase 6.
111
+
112
+ > Sin `cdk` previo, esta fase no hace nada salvo registrar `GENERAR`. La calibración (Fase 3.5) y el
113
+ > resto del flujo continúan igual.
114
+
115
+ ---
116
+
72
117
  ## Fase 1 — Detección de la fuente de verdad
73
118
 
74
119
  Busca en la raíz del repositorio, aceptando **ambas nomenclaturas**:
@@ -217,6 +262,9 @@ Solo tras la aprobación de la Fase 5. Genera:
217
262
  - declarar en el frontmatter una **description con triggers ricos** (verbos y sustantivos
218
263
  que el usuario usaría: "crea", "genera", "corrige", "refactoriza", método, clase,
219
264
  endpoint, componente…) para mejorar la auto-invocación de la skill;
265
+ - **estampar en el frontmatter** `cdk_contract_version: N`, con `N` = la versión de contrato
266
+ vigente de [`references/cdk-contract.md`](references/cdk-contract.md). Esto permite que el
267
+ pre-flight (Fase 0.5) y `ozali doctor`/`ozali update` sepan si el `cdk` está al día;
220
268
  - declarar que ayuda a **generar código** (nuevo componente, fix de bug, análisis de impacto)
221
269
  respetando la fuente de verdad y los estándares del proyecto;
222
270
  - orquestar los 8 subagentes según la fase del trabajo;
@@ -249,9 +297,11 @@ Solo tras la aprobación de la Fase 5. Genera:
249
297
  resumen técnico, `state`) con naming determinista, según
250
298
  [`references/engram-convention.md`](references/engram-convention.md), y al **iniciar** un
251
299
  hito hacer `mem_search` del proyecto/hito para recuperar contexto previo;
252
- - en el **cierre del hito**, invocar la skill **`ozali-commit`** para generar el commit
253
- summary (feature→`feat`, bugfix→`fix`, hotfix→`hotfix`, refactor→`refactor`; scope = módulo
254
- del hito). El GATE del mensaje es independiente del GATE del plan.
300
+ - en el **cierre del hito**, invocar la skill **`ozali-commit`** (instalada por el CLI en
301
+ `.claude/skills/ozali-commit/`; **nunca** `copsis-commit`, nombre heredado de versiones
302
+ anteriores) para generar el commit summary (feature→`feat`, bugfix→`fix`, hotfix→`hotfix`,
303
+ refactor→`refactor`; scope = módulo del hito). El GATE del mensaje es independiente del GATE
304
+ del plan.
255
305
  2. `.claude/agents/<rol>.md` — un subagente real por cada uno de los 8 roles, con su system
256
306
  prompt y herramientas, según [`references/agents-blueprint.md`](references/agents-blueprint.md).
257
307
  3. `.claude/skills/cdk/verify-structure.mjs` — **harness del analista**: script Node sin
@@ -0,0 +1,81 @@
1
+ <!-- CDK_CONTRACT_VERSION: 1 -->
2
+
3
+ # Contrato de la skill `cdk` — versión y migración
4
+
5
+ Esta es la **fuente de verdad única** de la versión del contrato con el que `ozali` genera (y
6
+ mantiene) la skill `cdk`. La lee el **agente** (en la Fase 0.5 y la Fase 6 de
7
+ [`../SKILL.md`](../SKILL.md)) y la parsea el **CLI** (`ozali doctor`/`ozali update`) del paquete.
8
+ Para subir el contrato: incrementa el número del marcador `CDK_CONTRACT_VERSION` de arriba y la
9
+ prosa de abajo, y agrega una entrada al changelog.
10
+
11
+ > **Versión de contrato vigente: `1`**
12
+
13
+ El número vive en **un solo lugar** (el marcador HTML de la primera línea, formato
14
+ `CDK_CONTRACT_VERSION: <entero>`). No lo dupliques en otros archivos.
15
+
16
+ ---
17
+
18
+ ## Cómo se usa la versión
19
+
20
+ - El `cdk` generado **debe estampar** su versión de contrato en el frontmatter de su `SKILL.md`:
21
+
22
+ ```yaml
23
+ ---
24
+ name: cdk
25
+ description: <triggers ricos…>
26
+ cdk_contract_version: 1
27
+ ---
28
+ ```
29
+
30
+ - En el **pre-flight** (Fase 0.5 de `ozali`), el agente compara la versión estampada en el `cdk`
31
+ instalado contra esta versión vigente **N**:
32
+
33
+ | Estado del `cdk` instalado | Acción |
34
+ |---|---|
35
+ | No existe | Generar `cdk` por el flujo normal (GATE Fase 5 → Fase 6), estampando `cdk_contract_version: N`. |
36
+ | Existe **sin** `cdk_contract_version`, o con un valor **< N** | **Desactualizado → migración automática** (sin GATE): aplica los deltas del changelog, estampa `N` y reporta los cambios. Si el delta exige **regeneración estructural** de subagentes, eso sí pasa por el GATE de la Fase 5. |
37
+ | Existe con `cdk_contract_version == N` | Al día → no regenerar; solo informarlo. |
38
+
39
+ > Un `cdk` **sin** el campo `cdk_contract_version` es un `cdk` **legado** (generado por una versión
40
+ > anterior de `ozali`). Trátalo como versión `0`: siempre desactualizado.
41
+
42
+ ---
43
+
44
+ ## Regla de migración (todas las versiones)
45
+
46
+ Al migrar un `cdk` legado o desactualizado, **siempre**:
47
+
48
+ 1. **Elimina toda referencia a la nomenclatura heredada** (`copsis-commit`, `copsis-doctor` — nombres
49
+ de versiones anteriores del ecosistema, predecesoras de `ozali`) y **reemplázala por la de
50
+ `ozali`**:
51
+ - `copsis-commit` → **`ozali-commit`** (la skill de commit vigente que `ozali init`/`ozali update`
52
+ instalan en `.claude/skills/ozali-commit/`).
53
+ - Refs a plantillas o a la skill **`copsis-doctor`** → sus equivalentes de `ozali` (esta skill y
54
+ sus `references/`). No debe quedar ninguna mención a `copsis-*` en el `cdk` migrado.
55
+ 2. **Migra el histórico legado de docs**: si existe `.copsis/docs/cdk/` (la ruta que usaba
56
+ `copsis-doctor`), **mueve** su contenido a `.ozali/docs/cdk/` — fusionando **por hito** y **sin
57
+ sobrescribir** lo que ya exista en el destino — y **elimina** la carpeta `.copsis/` una vez vacía.
58
+ Si `.copsis/` contiene otras subrutas legadas (p. ej. `.copsis/metrics/`), reubícalas bajo
59
+ `.ozali/` de forma equivalente antes de borrar `.copsis/`.
60
+ 3. **Preserva** el histórico por hito ya en `.ozali/docs/cdk/` y los planes congelados
61
+ (`02-plan-aprobado.md`): la migración del contrato **nunca** los borra ni reescribe.
62
+ 4. **Estampa** `cdk_contract_version: N` en el frontmatter del `cdk` migrado.
63
+ 5. **Reporta** al usuario, en texto, qué cambió (referencias migradas, docs reubicados de `.copsis/`
64
+ a `.ozali/`, secciones actualizadas, versión nueva).
65
+
66
+ ---
67
+
68
+ ## Changelog del contrato
69
+
70
+ ### v1 — contrato base
71
+ Un `cdk` conforme a la v1 debe:
72
+
73
+ - Estampar `cdk_contract_version: 1` en su frontmatter.
74
+ - En el **cierre de hito**, invocar la skill **`ozali-commit`** (nunca `copsis-commit`) para
75
+ generar el commit summary convencional (feature→`feat`, bugfix→`fix`, hotfix→`hotfix`,
76
+ refactor→`refactor`; scope = módulo del hito).
77
+ - Operar **recall-first** (reusar memoria de Engram antes de releer/re-analizar) según
78
+ [`engram-convention.md`](engram-convention.md) §7.
79
+ - Espejar a Engram los artefactos clave del hito y escribir la telemetría de tokens
80
+ (`.ozali/metrics/token-metrics.json`, que lee `ozali doctor`).
81
+ - Respetar la calibración de Strict TDD (Fase 3.5) y el 🛑 GATE del plan.
@@ -0,0 +1,81 @@
1
+ ---
2
+ name: ozali-commit
3
+ description: Genera un "commit summary" convencional (feat/fix/hotfix/refactor) con prefijo, scope y descripción a partir de los cambios EN STAGE y commitea tras tu aprobación. Úsala cuando el usuario quiera "commitea", "haz el commit", "genera el mensaje de commit", "cierra el hito con commit", o cuando otra skill (p. ej. cdk) cierre un hito.
4
+ ---
5
+
6
+ # Skill: ozali-commit
7
+
8
+ `ozali-commit` produce el **mensaje de commit** de un cambio siguiendo
9
+ [Conventional Commits](https://www.conventionalcommits.org/) y lo **commitea tras tu aprobación**.
10
+ Es la skill de commit vigente del ecosistema `ozali`.
11
+
12
+ `cdk` la invoca en el **cierre de cada hito**, pero también puedes usarla suelta en cualquier repo.
13
+
14
+ > **Regla de oro:** nunca commitea sin tu aprobación explícita (🛑 **GATE de mensaje**). El GATE del
15
+ > mensaje es **independiente** del GATE del plan de `cdk`.
16
+
17
+ > **Cero dependencias:** usa `git` directo (ya habilitado en el perfil base de permisos de ozali:
18
+ > `Bash(git add *)`, `Bash(git commit *)`, `Bash(git status)`, `Bash(git diff *)`,
19
+ > `Bash(git log *)`). No requiere herramientas externas.
20
+
21
+ ---
22
+
23
+ ## Flujo
24
+
25
+ ### 1. Inspecciona el stage
26
+ - `git status --short` — qué hay staged vs sin stage.
27
+ - `git diff --cached` — el contenido **EN STAGE** (la fuente de verdad del mensaje).
28
+ - Si **no hay nada en stage**: dilo y pregunta si quieres que haga `git add` de archivos concretos
29
+ (o `git add -A`). **No** hagas `add -A` por tu cuenta sin confirmación.
30
+ - Lee el commit anterior con `git log -1 --pretty=%s` para mantener el estilo del repo.
31
+
32
+ ### 2. Determina el tipo y el scope
33
+ A partir del diff EN STAGE, clasifica el cambio:
34
+
35
+ | Tipo | Cuándo |
36
+ |---|---|
37
+ | `feat` | nueva funcionalidad / capacidad |
38
+ | `fix` | corrección de bug |
39
+ | `hotfix` | corrección urgente en caliente |
40
+ | `refactor` | reestructura sin cambiar comportamiento |
41
+ | `docs` | solo documentación |
42
+ | `test` | solo pruebas |
43
+ | `chore` | mantenimiento (deps, config, build) |
44
+
45
+ - **Scope** = módulo/área tocada del repo (derívalo de las rutas en stage; ej. `cobranza`, `cli`,
46
+ `auth`). Si abarca varias áreas, usa la más representativa o un scope general.
47
+ - Si la skill que invoca aporta tipo/scope (p. ej. `cdk` mapea feature→`feat`, bugfix→`fix`,
48
+ hotfix→`hotfix`, refactor→`refactor`), **respétalos**.
49
+
50
+ ### 3. Redacta el mensaje
51
+ Formato: `tipo(scope): descripción en imperativo, ≤ 72 chars`. Cuerpo opcional (qué/por qué, no el
52
+ cómo). No inventes cambios que no estén en el diff. Idioma: el del repo (mira `git log`).
53
+
54
+ ```
55
+ feat(cobranza): agrega validación de RFC en alta de cotización
56
+
57
+ - valida formato y dígito verificador antes de persistir
58
+ - cubre el caso de RFC genérico (XAXX010101000)
59
+ ```
60
+
61
+ ### 4. 🛑 GATE de mensaje
62
+ Presenta el mensaje completo **como texto visible** y espera aprobación. Si el usuario pide ajustes,
63
+ itera. **No commitees** hasta el "ok".
64
+
65
+ ### 5. Commitea
66
+ Tras la aprobación:
67
+ ```bash
68
+ git commit -m "tipo(scope): descripción" [-m "cuerpo…"]
69
+ ```
70
+ - Commitea **solo lo que está en stage** (no añadas archivos no aprobados).
71
+ - **No** hagas `git push` (es destructivo/saliente y está denegado por defecto): si el usuario lo
72
+ quiere, recuérdaselo para que lo haga él.
73
+ - Reporta el hash corto resultante (`git rev-parse --short HEAD`).
74
+
75
+ ---
76
+
77
+ ## Notas
78
+ - Si el repo usa una convención propia (gitmoji, prefijos de ticket), detéctala desde `git log` y
79
+ síguela.
80
+ - Si el cambio es demasiado grande/heterogéneo para un solo mensaje coherente, **sugiere** dividir
81
+ en varios commits (pero no lo hagas sin aprobación).