ozali 0.7.1 → 0.9.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 +12 -0
- package/cli/bin/ozali.mjs +8 -2
- package/cli/lib/commands.mjs +254 -9
- package/cli/lib/detect.mjs +42 -3
- package/cli/lib/util.mjs +55 -0
- package/cli/test/smoke.test.mjs +91 -1
- package/docs/compilado-local.md +182 -0
- package/docs/guia-de-uso.md +50 -0
- package/docs/obsidian-integration.md +93 -0
- package/docs/workspaces.md +19 -5
- package/package.json +1 -1
- package/skill/SKILL.md +22 -0
- package/templates/obsidian-vault/.obsidian/app.json +12 -0
- package/templates/obsidian-vault/.obsidian/core-plugins.json +9 -0
- package/templates/obsidian-vault/MOCs/Bugfixes Recientes.md +9 -0
- package/templates/obsidian-vault/MOCs/Decisiones del Equipo.md +9 -0
- package/templates/obsidian-vault/MOCs/M/303/251tricas y Tokens.md" +9 -0
- package/templates/obsidian-vault/MOCs/Proyectos.md +17 -0
- package/templates/obsidian-vault/MOCs//303/215ndice Global.md" +19 -0
- package/templates/obsidian-vault/README.md +22 -0
- package/templates/ozali-workspace-jarvis.md +57 -13
package/cli/test/smoke.test.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import fs from "node:fs";
|
|
|
6
6
|
import os from "node:os";
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
|
-
import { engramAssetName, pickEngramAsset } from "../lib/util.mjs";
|
|
9
|
+
import { engramAssetName, pickEngramAsset, toPortablePath, fromPortablePath } from "../lib/util.mjs";
|
|
10
10
|
|
|
11
11
|
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
12
12
|
const BIN = path.resolve(HERE, "..", "bin", "ozali.mjs");
|
|
@@ -257,6 +257,8 @@ test("workspace escribe manifiesto + .code-workspace + jarvis y es idempotente",
|
|
|
257
257
|
assert.ok(fs.existsSync(wsFile), ".code-workspace escrito");
|
|
258
258
|
assert.equal(JSON.parse(fs.readFileSync(wsFile, "utf8")).folders.length, 2, "multi-root con 2 folders");
|
|
259
259
|
assert.match(fs.readFileSync(path.join(root, "CLAUDE.md"), "utf8"), /ozali-workspace-jarvis:start/, "bloque jarvis en CLAUDE.md");
|
|
260
|
+
// Track 2: la skill ozali queda instalada en la raíz para calibrar miembros desde el workspace
|
|
261
|
+
assert.ok(fs.existsSync(path.join(root, ".claude", "skills", "ozali", "SKILL.md")), "skill ozali en la raíz");
|
|
260
262
|
|
|
261
263
|
// idempotencia: re-correr no duplica
|
|
262
264
|
run(["workspace", "--yes", "--no-trust"], root);
|
|
@@ -268,6 +270,51 @@ test("workspace escribe manifiesto + .code-workspace + jarvis y es idempotente",
|
|
|
268
270
|
}
|
|
269
271
|
});
|
|
270
272
|
|
|
273
|
+
test("workspace --doctor revisa cada miembro y saca resumen consolidado", () => {
|
|
274
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), "ozali-ws-"));
|
|
275
|
+
try {
|
|
276
|
+
wsRepo(root, "api", { config: true, cdk: true, pkg: { name: "api", version: "1.0.0" } });
|
|
277
|
+
wsRepo(root, "web", { config: true, cdk: true, pkg: { name: "web" } });
|
|
278
|
+
const { stdout } = run(["workspace", "--doctor"], root, true); // exit 1 esperado (repos sin sot/engram)
|
|
279
|
+
assert.match(stdout, /health-check/i, "corre doctor por repo");
|
|
280
|
+
assert.match(stdout, /Resumen del workspace/, "imprime el resumen consolidado");
|
|
281
|
+
assert.match(stdout, /api/, "menciona el repo api en el resumen");
|
|
282
|
+
assert.match(stdout, /web/, "menciona el repo web en el resumen");
|
|
283
|
+
assert.ok(!fs.existsSync(path.join(root, "ozali-workspace.json")), "--doctor no escribe config");
|
|
284
|
+
} finally {
|
|
285
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
test("workspace --update actualiza cada miembro y salta los sin init", () => {
|
|
290
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), "ozali-ws-"));
|
|
291
|
+
try {
|
|
292
|
+
wsRepo(root, "api", { config: true, cdk: true, pkg: { name: "api", version: "1.0.0" } });
|
|
293
|
+
wsRepo(root, "bare", { pkg: { name: "bare" } }); // missing-init → se salta
|
|
294
|
+
const { stdout } = run(["workspace", "--update", "--yes", "--no-jarvis"], root);
|
|
295
|
+
assert.match(stdout, /Resumen del workspace/, "imprime el resumen consolidado");
|
|
296
|
+
assert.match(stdout, /Sin ozali init/, "salta el repo sin init");
|
|
297
|
+
assert.ok(!fs.existsSync(path.join(root, "ozali-workspace.json")), "--update no escribe config");
|
|
298
|
+
} finally {
|
|
299
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
test("workspace no trata subcarpetas de un repo como miembros (solo repos propios)", () => {
|
|
304
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), "ozali-ws-"));
|
|
305
|
+
try {
|
|
306
|
+
execFileSync("git", ["init", "-q"], { cwd: root }); // la raíz ES un repo git
|
|
307
|
+
fs.mkdirSync(path.join(root, "src")); // subcarpeta simple, NO repo propio
|
|
308
|
+
fs.writeFileSync(path.join(root, "src", "index.js"), "//\n");
|
|
309
|
+
wsRepo(root, "pkg", { config: true, cdk: true, pkg: { name: "pkg", version: "1.0.0" } }); // repo propio anidado
|
|
310
|
+
const { stdout } = run(["workspace", "--dry-run"], root);
|
|
311
|
+
assert.match(stdout, /\bpkg\b/, "incluye el repo propio anidado");
|
|
312
|
+
assert.doesNotMatch(stdout, /\bsrc\b/, "no incluye la subcarpeta que no es repo propio");
|
|
313
|
+
} finally {
|
|
314
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
|
|
271
318
|
test("init --dry-run no escribe nada", () => {
|
|
272
319
|
const dir = tmpProject();
|
|
273
320
|
try {
|
|
@@ -336,6 +383,49 @@ test("engramAssetName respeta la convención de release por SO/arch", () => {
|
|
|
336
383
|
assert.equal(engramAssetName("freebsd", "x64", "1.17.0"), null, "SO no soportado → null");
|
|
337
384
|
});
|
|
338
385
|
|
|
386
|
+
test("toPortablePath convierte absolutos a ~ o relativo", () => {
|
|
387
|
+
const home = os.homedir();
|
|
388
|
+
const cwd = "/project";
|
|
389
|
+
assert.equal(toPortablePath(path.join(home, ".ozali", "knowledge"), cwd), "~/.ozali/knowledge");
|
|
390
|
+
assert.equal(toPortablePath("/project/.k", "/project"), ".k");
|
|
391
|
+
assert.equal(toPortablePath("/outside", "/project"), "/outside");
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
test("fromPortablePath expande ~ y resuelve relativos", () => {
|
|
395
|
+
const home = os.homedir();
|
|
396
|
+
const cwd = "/project";
|
|
397
|
+
assert.equal(fromPortablePath("~/.ozali/knowledge", cwd), path.join(home, ".ozali", "knowledge"));
|
|
398
|
+
assert.equal(fromPortablePath(".k", cwd), "/project/.k");
|
|
399
|
+
assert.equal(fromPortablePath("/abs", cwd), "/abs");
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
test("init guarda knowledgeRepo como path portable", () => {
|
|
403
|
+
const dir = tmpProject();
|
|
404
|
+
try {
|
|
405
|
+
run(["init", "--yes", "--no-engram", "--no-trust", "--no-jarvis", "--agent", "claude-code", "--scope", "project", "--knowledge-repo", path.join(dir, ".k")], dir);
|
|
406
|
+
const cfg = JSON.parse(fs.readFileSync(path.join(dir, ".ozali", "config.json"), "utf8"));
|
|
407
|
+
assert.ok(cfg.knowledgeRepo, "config tiene knowledgeRepo");
|
|
408
|
+
assert.ok(!path.isAbsolute(cfg.knowledgeRepo), "knowledgeRepo NO debe ser absoluto");
|
|
409
|
+
assert.equal(cfg.knowledgeRepo, ".k", "knowledgeRepo debe ser relativo al proyecto");
|
|
410
|
+
} finally {
|
|
411
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
test("sync resuelve knowledgeRepo portable correctamente", () => {
|
|
416
|
+
const dir = tmpProject();
|
|
417
|
+
try {
|
|
418
|
+
run(["init", "--yes", "--no-engram", "--no-trust", "--no-jarvis", "--agent", "claude-code", "--scope", "project", "--knowledge-repo", path.join(dir, ".k")], dir);
|
|
419
|
+
// Crear algo en .ozali/docs/ para que sync tenga qué copiar
|
|
420
|
+
fs.mkdirSync(path.join(dir, ".ozali", "docs"), { recursive: true });
|
|
421
|
+
fs.writeFileSync(path.join(dir, ".ozali", "docs", "test.md"), "# test\n");
|
|
422
|
+
const { stdout } = run(["sync", "--yes"], dir);
|
|
423
|
+
assert.match(stdout, /copiados|Docs copiados/, "sync debe encontrar el repo de conocimiento");
|
|
424
|
+
} finally {
|
|
425
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
|
|
339
429
|
test("pickEngramAsset ignora tags no-semver sin binarios (pi-v*) y draft/prerelease", () => {
|
|
340
430
|
// Shape real de la API de GitHub: el 'latest' es pi-v0.1.9 (0 assets); los binarios
|
|
341
431
|
// viven en tags vX.Y.Z. Además metemos un prerelease más nuevo que debe saltarse.
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# Compilado y uso local (para probar el repo clonado)
|
|
2
|
+
|
|
3
|
+
← [README](../README.md)
|
|
4
|
+
|
|
5
|
+
Guía para cuando **clonas este repositorio** y quieres compilarlo/empaquetarlo y **probarlo en local**
|
|
6
|
+
antes de publicarlo o para desarrollar sobre él.
|
|
7
|
+
|
|
8
|
+
> **Aclaración importante — no hay "compilación".** `ozali` es un CLI **Node de cero dependencias y
|
|
9
|
+
> ESM puro** (solo usa módulos `node:*`). **No hay build step**, ni TypeScript que transpilar, ni
|
|
10
|
+
> `dist/`. "Compilar" aquí significa una de dos cosas: (a) **correrlo tal cual** desde el fuente, o
|
|
11
|
+
> (b) **empaquetar el artefacto distribuible** (`npm pack` → un `.tgz`, el "archivo base") para
|
|
12
|
+
> instalarlo y probarlo como lo haría un usuario final.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Requisitos
|
|
17
|
+
|
|
18
|
+
- **Node ≥ 16** (`node --version`). Es lo único imprescindible.
|
|
19
|
+
- **git** (el CLI lee identidad/estado del repo).
|
|
20
|
+
- **pnpm** o **npm** (opcional; solo para `test`, `pack`, `link` o instalación global). No hay `npm
|
|
21
|
+
install` que correr: **el repo no tiene dependencias**.
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
git clone https://github.com/jimmybathrooms/ozali.git
|
|
25
|
+
cd ozali
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 1. Correr desde el fuente (sin instalar nada)
|
|
31
|
+
|
|
32
|
+
Como no hay dependencias ni build, el CLI corre directo:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
node cli/bin/ozali.mjs --version # imprime la versión de package.json (p. ej. 0.8.0)
|
|
36
|
+
node cli/bin/ozali.mjs --help # lista de comandos
|
|
37
|
+
node cli/bin/ozali.mjs doctor # health-check en el cwd
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Esta es la forma **más auditable**: ejecutas exactamente el código que ves, sin capa de instalación.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 2. Correr la batería de pruebas
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm test # (o: pnpm test) → node --test cli/test/*.test.mjs
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Debe salir todo en verde. Los tests son `node:test` puro (sin dependencias) y cubren init, doctor,
|
|
51
|
+
update, workspace, seguridad del paquete y los helpers de release de Engram.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 3. Empaquetar el "archivo base" (tarball distribuible)
|
|
56
|
+
|
|
57
|
+
El artefacto que se publicaría en npm es un tarball `.tgz`. Se genera con:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npm pack # (o: pnpm pack)
|
|
61
|
+
# → crea ozali-<version>.tgz en la raíz del repo (el cwd)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Para tu versión actual produce, por ejemplo, **`ozali-0.8.0.tgz`**. Es **idéntico** a lo que recibiría
|
|
65
|
+
un usuario desde npm: incluye solo lo declarado en `package.json > files` (`cli/`, `skill/`,
|
|
66
|
+
`skill-commit/`, `templates/`, `docs/`, `README.md`, `LICENSE`).
|
|
67
|
+
|
|
68
|
+
Para **ver qué contiene sin generar el archivo**:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npm pack --dry-run # lista archivos, tamaño y nombre del tarball, sin escribir nada
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
> El `.tgz` es un artefacto temporal: **no lo commitees**. Bórralo tras probar (o añádelo a
|
|
75
|
+
> `.gitignore` como `ozali-*.tgz`).
|
|
76
|
+
|
|
77
|
+
### Extraer / inspeccionar el tarball
|
|
78
|
+
|
|
79
|
+
El tarball es un `.tar.gz` estándar. Para extraerlo y revisar el "archivo base" tal como quedaría
|
|
80
|
+
instalado:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
tar -xzf ozali-0.8.0.tgz # crea una carpeta package/ con el contenido publicable
|
|
84
|
+
ls -R package # inspecciona la estructura
|
|
85
|
+
tar -tzf ozali-0.8.0.tgz # o solo LISTA el contenido, sin extraer
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
La carpeta `package/` es exactamente lo que npm coloca en `node_modules/ozali` al instalar.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 4. Usarlo en local para pruebas
|
|
93
|
+
|
|
94
|
+
Tres formas, de la más cómoda para desarrollar a la más fiel al usuario final:
|
|
95
|
+
|
|
96
|
+
### a) `npm link` — bucle de desarrollo (refleja tus cambios al instante)
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
npm link # (o: pnpm link --global)
|
|
100
|
+
# ahora 'ozali' está en tu PATH, apuntando al clon local
|
|
101
|
+
ozali --version
|
|
102
|
+
ozali doctor
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Como es un symlink al repo, cualquier edición que hagas en `cli/` se ve de inmediato (no hace falta
|
|
106
|
+
re-linkear). Para deshacer: `npm unlink -g ozali` (o `npm rm -g ozali`).
|
|
107
|
+
|
|
108
|
+
### b) Instalar el tarball global — prueba el artefacto real
|
|
109
|
+
|
|
110
|
+
Instala el `.tgz` como lo haría un usuario, para validar el paquete exacto que publicarías:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
npm i -g ./ozali-0.8.0.tgz # (o: pnpm add -g ./ozali-0.8.0.tgz)
|
|
114
|
+
ozali --version
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Para desinstalar: `npm rm -g ozali`.
|
|
118
|
+
|
|
119
|
+
### c) Efímero, sin instalar en el PATH — como `npx`/`pnpm dlx`
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
npx ./ozali-0.8.0.tgz init # corre init desde el tarball, sin dejar 'ozali' instalado
|
|
123
|
+
pnpm dlx ./ozali-0.8.0.tgz init # equivalente con pnpm
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
> **Nota de seguridad:** el flag `--ignore-scripts` que recomendamos para instalar desde npm es
|
|
127
|
+
> irrelevante aquí, porque `ozali` **no tiene dependencias ni lifecycle scripts** (nada que ejecutar
|
|
128
|
+
> en install). Detalle en [security.md](security.md).
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## 5. Probar en un proyecto sandbox
|
|
133
|
+
|
|
134
|
+
Crea un repo de juguete y corre el flujo real sin ensuciar nada:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
mkdir /tmp/ozali-sandbox && cd /tmp/ozali-sandbox && git init
|
|
138
|
+
|
|
139
|
+
# opción sin instalar: apunta al bin del clon
|
|
140
|
+
node /ruta/al/clon/ozali/cli/bin/ozali.mjs init --dry-run # muestra el plan, no escribe
|
|
141
|
+
node /ruta/al/clon/ozali/cli/bin/ozali.mjs init --yes --no-engram # init real, sin instalar Engram
|
|
142
|
+
node /ruta/al/clon/ozali/cli/bin/ozali.mjs doctor # verifica el resultado
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Para probar **workspaces multi-repo**, crea una carpeta con varios repos git dentro y corre:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
node /ruta/al/clon/ozali/cli/bin/ozali.mjs workspace --dry-run # inventario + plan, sin escribir
|
|
149
|
+
node /ruta/al/clon/ozali/cli/bin/ozali.mjs workspace --doctor # health-check de todos los miembros
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
(Si hiciste `npm link`, sustituye todo lo anterior por simplemente `ozali <comando>`.)
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## 6. Verificar y limpiar
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
ozali --version # debe coincidir con la "version" de package.json
|
|
160
|
+
npm rm -g ozali # desinstala el global (link o tarball)
|
|
161
|
+
rm -f ozali-*.tgz # borra el tarball de prueba
|
|
162
|
+
rm -rf package/ # borra la extracción del tarball, si la hiciste
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Resumen
|
|
168
|
+
|
|
169
|
+
| Quiero… | Comando |
|
|
170
|
+
|---|---|
|
|
171
|
+
| Correr sin instalar | `node cli/bin/ozali.mjs <cmd>` |
|
|
172
|
+
| Correr los tests | `npm test` |
|
|
173
|
+
| Empaquetar el distribuible | `npm pack` → `ozali-<version>.tgz` |
|
|
174
|
+
| Ver el contenido del paquete | `npm pack --dry-run` |
|
|
175
|
+
| Extraer el tarball | `tar -xzf ozali-<version>.tgz` → `package/` |
|
|
176
|
+
| Desarrollar con `ozali` en PATH | `npm link` |
|
|
177
|
+
| Probar el artefacto real | `npm i -g ./ozali-<version>.tgz` |
|
|
178
|
+
| Probar efímero | `npx ./ozali-<version>.tgz init` |
|
|
179
|
+
|
|
180
|
+
> ¿Buscas **publicar** una versión nueva (no solo probar)? Eso es otro flujo: bump de `version` en
|
|
181
|
+
> `package.json` → commit → tag `vX.Y.Z` → push; el tag dispara la publicación a npm con provenance
|
|
182
|
+
> vía GitHub Actions.
|
package/docs/guia-de-uso.md
CHANGED
|
@@ -81,6 +81,7 @@ Lleva el histórico y la memoria al repo de conocimiento del equipo. Tus compañ
|
|
|
81
81
|
| Comando | Qué hace |
|
|
82
82
|
|---|---|
|
|
83
83
|
| `ozali init` | Prepara el proyecto (lo corres una vez por repo). |
|
|
84
|
+
| `ozali workspace` | Configura varios repos de una carpeta para trabajar en conjunto (lo corres en la carpeta raíz). |
|
|
84
85
|
| `ozali doctor` | Revisa que todo esté bien. Es solo lectura, no cambia nada. |
|
|
85
86
|
| `ozali update` | Actualiza skill ozali + ozali-jarvis + permisos (y guía cómo regenerar cdk). |
|
|
86
87
|
| `ozali sync` | Sube el histórico/memoria al repo de equipo. |
|
|
@@ -171,6 +172,51 @@ Atajos: `ozali audit --tui` abre un navegador interactivo de la memoria; `ozali
|
|
|
171
172
|
busca un término; `ozali audit --general` fuerza el alcance general. Si no tienes Engram, audita el
|
|
172
173
|
histórico local de `.ozali/docs/`.
|
|
173
174
|
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Trabajar con varios repos a la vez (`ozali workspace`)
|
|
178
|
+
|
|
179
|
+
¿Tienes una carpeta que agrupa **varios repositorios que se hablan entre sí** (una API, su front, una
|
|
180
|
+
librería compartida) y quieres que tu agente trabaje con todos juntos? Corre **una vez** en esa
|
|
181
|
+
carpeta raíz:
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
ozali workspace # escanea los repos hijos, prepara los que falten y arma la config conjunta
|
|
185
|
+
ozali workspace --dry-run # solo te muestra el inventario y el plan, sin escribir nada
|
|
186
|
+
ozali workspace --yes # sin preguntas: acepta los defaults y las referencias detectadas
|
|
187
|
+
ozali workspace --depth 2 # busca repos hasta 2 niveles (si están en subcarpetas de grupo)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Qué hace, en orden:
|
|
191
|
+
|
|
192
|
+
1. **Revisa** cada repo y te dice cómo está: `✔ listo`, `⚠ sin calibrar` (le falta correr la skill
|
|
193
|
+
`ozali`) o `✖ sin init` (nunca se preparó).
|
|
194
|
+
2. **Prepara** los que están `✖ sin init` corriendo `ozali init` por ti, y te **guía** para calibrar
|
|
195
|
+
los que están `⚠ sin calibrar` (abrir ese repo y escribir *"diagnostica el proyecto"* — eso lo
|
|
196
|
+
hace tu agente, no el comando).
|
|
197
|
+
3. **Detecta las referencias** entre repos (dependencias npm cruzadas, submódulos, `docker-compose`) y
|
|
198
|
+
te pide confirmarlas.
|
|
199
|
+
4. **Escribe la configuración** para trabajar en conjunto:
|
|
200
|
+
- `ozali-workspace.json` — el mapa de repos, su estado y sus referencias (esto **sí** se commitea).
|
|
201
|
+
- `<carpeta>.code-workspace` — ábrelo en VSCode/Antigravity y verás todos los repos juntos.
|
|
202
|
+
- **ozali-workspace-jarvis** en el `CLAUDE.md`/`AGENTS.md` de la raíz: un orquestador que coordina
|
|
203
|
+
los repos según ese mapa y delega el trabajo de código en el `cdk` de cada uno.
|
|
204
|
+
|
|
205
|
+
**Sin ir a cada proyecto:** desde la carpeta raíz puedes operar todos los repos de una vez:
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
ozali workspace --doctor # revisa la salud de todos los repos y saca un resumen
|
|
209
|
+
ozali workspace --update # actualiza skills, permisos y jarvis de todos los repos
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Y para **calibrar** (generar `cdk`) los que estén `⚠ sin calibrar`, ya **no** abres cada proyecto:
|
|
213
|
+
abre tu agente en la carpeta raíz y pídele *"calibra los repos pendientes"* — el orquestador los
|
|
214
|
+
recorre **uno por uno** (cada uno con su aprobación) sin que cambies de proyecto.
|
|
215
|
+
|
|
216
|
+
Es **idempotente**: vuelve a correrlo cuando agregues un repo o cambien las referencias — no duplica
|
|
217
|
+
nada. Cada repo conserva su autonomía (su propio jarvis, su `cdk` y su memoria); el workspace solo
|
|
218
|
+
los **coordina**. Detalle completo en [workspaces.md](workspaces.md).
|
|
219
|
+
|
|
174
220
|
## Preguntas frecuentes
|
|
175
221
|
|
|
176
222
|
**¿Tengo que usar Engram?**
|
|
@@ -185,6 +231,10 @@ Lo corriste con `dlx`/`npx`, que es temporal. Instálalo global: `pnpm add -g oz
|
|
|
185
231
|
**¿Esto ensucia mi repositorio?**
|
|
186
232
|
No. El histórico y la memoria se aíslan (`.ozali/`, `.engram/` van al `.gitignore`).
|
|
187
233
|
|
|
234
|
+
**Tengo varios repos que dependen entre sí. ¿Los conecto?**
|
|
235
|
+
Sí: abre la carpeta que los agrupa y corre `ozali workspace`. Prepara los que falten y arma una
|
|
236
|
+
config para que tu agente trabaje con todos juntos. Ver [arriba](#trabajar-con-varios-repos-a-la-vez-ozali-workspace).
|
|
237
|
+
|
|
188
238
|
**Veo "this workspace has not been trusted" y mis permisos no aplican.**
|
|
189
239
|
Es el aviso de seguridad de Claude Code: ignora los permisos del proyecto hasta confiar en él.
|
|
190
240
|
Deja que `init` lo marque como confiable, o abre Claude Code en la carpeta y acepta el diálogo una
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Integración con Obsidian
|
|
2
|
+
|
|
3
|
+
← [README](README.md)
|
|
4
|
+
|
|
5
|
+
Ozali puede exportar la memoria de equipo (Engram) y el histórico de hitos a un **vault de Obsidian**: un espacio navegable con wikilinks, grafo de conocimiento y Mapas de Contenido (MOCs).
|
|
6
|
+
|
|
7
|
+
## ¿Qué es el vault?
|
|
8
|
+
|
|
9
|
+
Es una carpeta `obsidian/` dentro de tu **repo de conocimiento** (`ozali-knowledge/` o `~/.ozali/knowledge/`). Contiene:
|
|
10
|
+
|
|
11
|
+
- **README.md** — índice humano del vault
|
|
12
|
+
- **MOCs/** — Mapas de Contenido auto-generados (proyectos, decisiones, bugfixes, métricas)
|
|
13
|
+
- **Memoria/** — exportación de Engram (decisiones, bugfixes, resúmenes técnicos)
|
|
14
|
+
- **.obsidian/** — configuración compartida del equipo (plugins core, hotkeys)
|
|
15
|
+
|
|
16
|
+
## Requisitos
|
|
17
|
+
|
|
18
|
+
- [Obsidian](https://obsidian.md) instalado (opt-in; ozali lo detecta y ofrece descargar)
|
|
19
|
+
- Engram instalado (`engram obsidian-export` es el motor de exportación)
|
|
20
|
+
|
|
21
|
+
## Uso
|
|
22
|
+
|
|
23
|
+
### Generar / actualizar el vault
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Desde cualquier proyecto ozali
|
|
27
|
+
ozali sync --obsidian
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Esto:
|
|
31
|
+
1. Exporta Engram al vault (`engram obsidian-export`)
|
|
32
|
+
2. Actualiza los MOCs dinámicos (lista de proyectos, etc.)
|
|
33
|
+
3. Commit en el repo de conocimiento
|
|
34
|
+
|
|
35
|
+
### Sin el flag `--obsidian`
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
ozali sync
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Si el vault ya existe, ozali pregunta: *"¿Exportar memoria a Obsidian vault?"*. Responde `sí` para actualizar.
|
|
42
|
+
|
|
43
|
+
### Abrir el vault en Obsidian
|
|
44
|
+
|
|
45
|
+
1. Abre Obsidian
|
|
46
|
+
2. Selecciona **"Open folder as vault"**
|
|
47
|
+
3. Navega a tu `ozali-knowledge/obsidian/` (o `~/.ozali/knowledge/obsidian/`)
|
|
48
|
+
|
|
49
|
+
### Estructura recomendada del repo de conocimiento
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
ozali-knowledge/
|
|
53
|
+
.git/
|
|
54
|
+
engram/ ← export chunks de Engram
|
|
55
|
+
projects/
|
|
56
|
+
{proyecto}/
|
|
57
|
+
docs/ ← docs por hito
|
|
58
|
+
obsidian/ ← 🆕 VAULT DE OBSIDIAN
|
|
59
|
+
.obsidian/
|
|
60
|
+
README.md
|
|
61
|
+
MOCs/
|
|
62
|
+
Memoria/
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Configuración compartida
|
|
66
|
+
|
|
67
|
+
La carpeta `.obsidian/` del vault **sí se commitea** en el repo de conocimiento. Contiene:
|
|
68
|
+
- Plugins core habilitados (graph, backlinks, quick switcher)
|
|
69
|
+
- Config de lectura (readable line length)
|
|
70
|
+
|
|
71
|
+
**No se commitean** settings personales: temas, snippets, `workspace.json`, notas `Daily/`.
|
|
72
|
+
|
|
73
|
+
## Flujo de trabajo típico
|
|
74
|
+
|
|
75
|
+
1. Trabajas en un hito con `cdk` → ozali genera docs en `.ozali/docs/`
|
|
76
|
+
2. `ozali sync` → lleva docs + Engram al repo de conocimiento
|
|
77
|
+
3. `ozali sync --obsidian` → regenera el vault con la memoria actualizada
|
|
78
|
+
4. Abres Obsidian → exploras el grafo, wikilinks, decisiones del equipo
|
|
79
|
+
|
|
80
|
+
## Troubleshooting
|
|
81
|
+
|
|
82
|
+
| Síntoma | Causa | Solución |
|
|
83
|
+
|---|---|---|
|
|
84
|
+
| `engram obsidian-export` falla | Vault abierto en Obsidian (lock de archivos) | Cierra el vault en Obsidian y reintenta |
|
|
85
|
+
| MOCs vacíos | Sin proyectos en `knowledgeRepo/projects/` | Corre `ozali sync` primero para crear la estructura |
|
|
86
|
+
| No se detecta Obsidian | Instalado en path no estándar | Ignora la advertencia; abre el vault manualmente |
|
|
87
|
+
|
|
88
|
+
## Cerebro vs Vault
|
|
89
|
+
|
|
90
|
+
- **Cerebro** (`AI.md` + `.ai/`) vive en **cada repo** — es la fuente de verdad.
|
|
91
|
+
- **Vault** (`obsidian/`) vive en el **repo de conocimiento** — es la lectura humana acumulada.
|
|
92
|
+
|
|
93
|
+
No duplicamos el cerebro en el vault. Los MOCs usan **markdown links** para referenciar `AI.md` fuera del vault, y **wikilinks** para todo lo que está dentro (`Memoria/`, `MOCs/`).
|
package/docs/workspaces.md
CHANGED
|
@@ -26,6 +26,18 @@ ozali workspace --no-trust # no marca la raíz como confiable en Claude Code
|
|
|
26
26
|
Un CLI **no puede auto-dispararse** con solo hacer `cd`; por eso es un comando explícito e
|
|
27
27
|
**idempotente**: re-córrelo cuando agregues repos o cambien las referencias.
|
|
28
28
|
|
|
29
|
+
### Operar todos los repos desde la raíz
|
|
30
|
+
|
|
31
|
+
Para no entrar repo por repo, dos modos batch corren un comando sobre **todos los miembros**:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
ozali workspace --doctor # health-check de cada repo miembro + resumen consolidado
|
|
35
|
+
ozali workspace --update # actualiza skills/permisos/jarvis de cada repo + avisa de cdk desfasado
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Ambos son **solo CLI** (no necesitan el agente) y saltan los repos `✖ sin init`. La **calibración**
|
|
39
|
+
(generar `cdk`) sí la hace el agente, pero también **sin cambiar de proyecto** — ver abajo.
|
|
40
|
+
|
|
29
41
|
## Qué hace, paso a paso
|
|
30
42
|
|
|
31
43
|
1. **Escaneo (read-only).** Enumera los repos git hijos y reporta el estado ozali de cada uno:
|
|
@@ -37,10 +49,11 @@ Un CLI **no puede auto-dispararse** con solo hacer `cd`; por eso es un comando e
|
|
|
37
49
|
instala skills, aísla histórico, configura Engram). Hereda `agent`/`scope`/`knowledge-repo` de un
|
|
38
50
|
repo ya inicializado para mantener el equipo consistente.
|
|
39
51
|
|
|
40
|
-
3. **Guía de calibración.** El CLI **no calibra** (eso lo hace el agente)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
52
|
+
3. **Guía de calibración.** El CLI **no calibra** (eso lo hace el agente), pero ya **no** tienes que
|
|
53
|
+
abrir cada repo por separado: `ozali workspace` instala la skill **`ozali`** en la **raíz**, y el
|
|
54
|
+
orquestador `ozali-workspace-jarvis` la usa **en modo target** para calibrar cada repo
|
|
55
|
+
`⚠ sin calibrar` **desde el workspace**, repo por repo y con su propio 🛑 GATE. Abre el agente en la
|
|
56
|
+
raíz y pídele *"calibra los repos pendientes"*.
|
|
44
57
|
|
|
45
58
|
4. **Referencias (auto-detección + confirmación).** Infiere aristas dirigidas `from → to`:
|
|
46
59
|
- `npm-dep` — el `name` de un repo aparece en `dependencies`/`devDependencies`/`peerDependencies` de otro.
|
|
@@ -58,6 +71,7 @@ En la carpeta raíz:
|
|
|
58
71
|
| `ozali-workspace.json` | Manifiesto: miembros (ruta, proyecto, estado, fuente de verdad), referencias, `knowledgeRepo`, cloud. Fuente de verdad del workspace. |
|
|
59
72
|
| `<carpeta>.code-workspace` | Workspace **multi-root** de VSCode/Antigravity: abre todos los repos juntos. Merge idempotente si ya existe. |
|
|
60
73
|
| `CLAUDE.md` / `AGENTS.md` (raíz) | Bloque marcado del orquestador **`ozali-workspace-jarvis`** + subagente / agente de opencode. |
|
|
74
|
+
| `.claude/skills/ozali/` (raíz) | La skill **`ozali`** instalada en la raíz para **calibrar miembros en modo target** sin cambiar de proyecto (solo Claude Code). |
|
|
61
75
|
|
|
62
76
|
Si la raíz es a su vez un repo git, se añade `.claude/`, `.engram/` y `.ozali/` a su `.gitignore`
|
|
63
77
|
(los artefactos locales del agente no se commitean). `ozali-workspace.json` y `.code-workspace` **sí**
|
|
@@ -78,5 +92,5 @@ miembros). Su trabajo:
|
|
|
78
92
|
## Fuera de alcance (por ahora)
|
|
79
93
|
|
|
80
94
|
- Auto-sugerencia al abrir la carpeta (tarea de editor/hook).
|
|
81
|
-
- `ozali
|
|
95
|
+
- `ozali sync` agregado a nivel workspace (`--doctor`/`--update` ya existen; falta `--sync`).
|
|
82
96
|
- Detección de referencias más allá de npm/submódulos/compose (imports TS/py, `go.work`).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ozali",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.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": {
|
package/skill/SKILL.md
CHANGED
|
@@ -26,6 +26,28 @@ produce documentación, la calibración, el plan y los artefactos de la skill `c
|
|
|
26
26
|
|
|
27
27
|
---
|
|
28
28
|
|
|
29
|
+
## Modo workspace — calibrar un repo miembro (target)
|
|
30
|
+
|
|
31
|
+
Normalmente `ozali` opera sobre el **repo actual** (cwd). Pero cuando te invocan desde la **raíz de un
|
|
32
|
+
workspace** (existe `ozali-workspace.json`) para calibrar un **repo miembro**, operas sobre un
|
|
33
|
+
**target de subcarpeta** en vez del cwd. En ese caso:
|
|
34
|
+
|
|
35
|
+
- **Todas las rutas son relativas al `<target>`**, no a la raíz: la fuente de verdad
|
|
36
|
+
(`<target>/AI.md` + `<target>/.ai/`, o su variante `IA.md` + `.ia/`), la skill que generas
|
|
37
|
+
(`<target>/.claude/skills/cdk/`), el histórico (`<target>/.ozali/docs/`) y
|
|
38
|
+
`<target>/.engram/config.json`.
|
|
39
|
+
- **Identidad git del miembro:** usa `git -C <target> …` (no la raíz).
|
|
40
|
+
- **Engram:** guarda con el `project_name` de `<target>/.engram/config.json` (equivale al
|
|
41
|
+
`members[].project` del manifiesto). **No mezcles** memoria entre repos.
|
|
42
|
+
- **Un target por corrida**, secuencial, con su **propio 🛑 GATE**. No calibres varios a la vez sin
|
|
43
|
+
aprobación.
|
|
44
|
+
- Al terminar, sugiere **re-correr `ozali workspace`** en la raíz para refrescar el estado del miembro
|
|
45
|
+
en el manifiesto (`needs-calibration` → `ready`).
|
|
46
|
+
|
|
47
|
+
Si te invocan sin target (uso normal en un solo repo), ignora esta sección y sigue con el cwd.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
29
51
|
## Flujo general (7 fases)
|
|
30
52
|
|
|
31
53
|
```
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"alwaysUpdateLinks": true,
|
|
3
|
+
"newFileLocation": "folder",
|
|
4
|
+
"newFileFolderPath": "Memoria",
|
|
5
|
+
"attachmentFolderPath": "Memoria",
|
|
6
|
+
"readableLineLength": true,
|
|
7
|
+
"strictLineBreaks": false,
|
|
8
|
+
"showLineNumber": false,
|
|
9
|
+
"spellcheck": false,
|
|
10
|
+
"tabSize": 2,
|
|
11
|
+
"useTab": false
|
|
12
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# 🗺️ Bugfixes Recientes
|
|
2
|
+
|
|
3
|
+
Correcciones con impacto en otros módulos o que revelan gotchas del repo.
|
|
4
|
+
|
|
5
|
+
> Este MOC se regenera automáticamente con `ozali sync --obsidian`.
|
|
6
|
+
|
|
7
|
+
<!-- BUGFIXES_START -->
|
|
8
|
+
<!-- Se rellena automáticamente por ozali sync --obsidian desde Engram -->
|
|
9
|
+
<!-- BUGFIXES_END -->
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# 🗺️ Decisiones del Equipo
|
|
2
|
+
|
|
3
|
+
Decisiones de arquitectura, trade-offs y convenciones que afectan a todos.
|
|
4
|
+
|
|
5
|
+
> Este MOC se regenera automáticamente con `ozali sync --obsidian`.
|
|
6
|
+
|
|
7
|
+
<!-- DECISIONS_START -->
|
|
8
|
+
<!-- Se rellena automáticamente por ozali sync --obsidian desde Engram -->
|
|
9
|
+
<!-- DECISIONS_END -->
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# 🗺️ Métricas y Tokens
|
|
2
|
+
|
|
3
|
+
Resumen agregado de uso de tokens y eficiencia del equipo.
|
|
4
|
+
|
|
5
|
+
> Este MOC se regenera automáticamente con `ozali sync --obsidian`.
|
|
6
|
+
|
|
7
|
+
<!-- METRICS_START -->
|
|
8
|
+
<!-- Se rellena automáticamente por ozali sync --obsidian desde Engram -->
|
|
9
|
+
<!-- METRICS_END -->
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# 🗺️ Proyectos
|
|
2
|
+
|
|
3
|
+
Proyectos activos en el workspace ozali.
|
|
4
|
+
|
|
5
|
+
> Este MOC se regenera automáticamente con `ozali sync --obsidian`.
|
|
6
|
+
|
|
7
|
+
## Activos
|
|
8
|
+
|
|
9
|
+
<!-- PROJECTS_START -->
|
|
10
|
+
<!-- Se rellena automáticamente por ozali sync --obsidian -->
|
|
11
|
+
<!-- PROJECTS_END -->
|
|
12
|
+
|
|
13
|
+
## Agregar un nuevo proyecto
|
|
14
|
+
|
|
15
|
+
1. Corre `ozali init` en el repo del proyecto.
|
|
16
|
+
2. Corre `ozali sync --obsidian` desde cualquier proyecto.
|
|
17
|
+
3. El proyecto aparecerá aquí automáticamente.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# 🗺️ Índice Global
|
|
2
|
+
|
|
3
|
+
Bienvenido al vault de ozali. Usa este índice para navegar la memoria del equipo.
|
|
4
|
+
|
|
5
|
+
## Mapas de Contenido (MOCs)
|
|
6
|
+
|
|
7
|
+
- [[MOCs/Proyectos]] — Todos los proyectos del equipo
|
|
8
|
+
- [[MOCs/Decisiones del Equipo]] — Decisiones arquitectónicas y trade-offs
|
|
9
|
+
- [[MOCs/Bugfixes Recientes]] — Correcciones con impacto cruzado
|
|
10
|
+
- [[MOCs/Métricas y Tokens]] — Consumo de tokens y eficiencia
|
|
11
|
+
|
|
12
|
+
## Cómo actualizar este vault
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
# Desde cualquier proyecto ozali
|
|
16
|
+
ozali sync --obsidian
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
> El vault se regenera automáticamente. No edites `Memoria/` a mano.
|