@dforce2055/dai 0.6.0 → 0.7.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/CHANGELOG.md CHANGED
@@ -3,6 +3,19 @@
3
3
  Formato basado en [Keep a Changelog](https://keepachangelog.com/). Versionado semver
4
4
  (ver `VERSION`).
5
5
 
6
+ ## [0.7.0] — 2026-07-14
7
+
8
+ **dai es el distribuidor de skills de cualquier stack, sin opinar sobre su contenido.**
9
+
10
+ ### Agregado
11
+ - `dai skills install` — namespace de skills (`dai install` queda como alias). Con
12
+ `--from <git-url|path>[#ref]` instala **skills externas** por-stack (.NET, Java,
13
+ Rust…) desde un repo git (público o privado por SSH) o un path local, convertidas
14
+ para los 3 asistentes (Claude/Cursor/Copilot).
15
+ Self-service, one-off, sin registro; `dai sync` no las toca ([ADR-0013](docs/adr/0013-skills-externas-install-from.md)).
16
+ Valida el contrato mínimo (`SKILL.md` con `name` + `description`) y saltea con aviso
17
+ las malformadas; molde en [`templates/skill.md`](templates/skill.md). No valida el contenido.
18
+
6
19
  ## [0.6.0] — 2026-07-12
7
20
 
8
21
  Self-update del CLI y blindaje de autoría del repo.
@@ -181,6 +194,7 @@ ClickUp y Jira Cloud.
181
194
  - Tests de las rutas de red (jira/clickup/forge) con `fetch` mockeado. Sin links rotos;
182
195
  `files` de npm sin tests ni secretos.
183
196
 
197
+ [0.7.0]: https://github.com/dforce2055/dai/releases/tag/v0.7.0
184
198
  [0.6.0]: https://github.com/dforce2055/dai/releases/tag/v0.6.0
185
199
  [0.5.0]: https://github.com/dforce2055/dai/releases/tag/v0.5.0
186
200
  [0.4.0]: https://github.com/dforce2055/dai/releases/tag/v0.4.0
package/README.md CHANGED
@@ -49,7 +49,7 @@ deja las skills disponibles:
49
49
 
50
50
  ```bash
51
51
  npm i -g @dforce2055/dai # el CLI
52
- dai install # skills de IA → Claude y Cursor (global por defecto)
52
+ dai skills install # skills de IA → Claude y Cursor (global por defecto)
53
53
  ```
54
54
 
55
55
  Y después, en el chat del asistente, según lo que tengas:
@@ -157,8 +157,10 @@ flowchart TD
157
157
  | Comando | Qué hace |
158
158
  |---|---|
159
159
  | `dai init [<repo>]` | scaffolder interactivo del repo. Flags: `--for claude\|copilot\|both\|cursor\|all` (asistente, default `all`) · `--pm md\|jira\|clickup` (tracker) · `--openspec` |
160
- | `dai install [--global \| --local <repo>] [--force] [--dry-run] [--for claude\|cursor\|all]` | instala/actualiza skills de IA en Claude y/o Cursor (`--for all` por defecto). `--force` re-copia aunque ya existan. Ej: `dai install --local . --for cursor --force` · `dai install --global --for all --force` |
160
+ | `dai skills install [--global \| --local <repo>] [--force] [--dry-run] [--for claude\|cursor\|all]` | instala/actualiza las skills de dai en Claude y/o Cursor (`--for all` por defecto). `--force` re-copia. Alias: **`dai install`**. Ej: `dai skills install --local . --for cursor --force` |
161
+ | `dai skills install --from <git-url\|path>[#ref] [--for …]` | instala **skills externas** (por-stack: .NET, Java, …) desde un repo/dir, **convertidas para los 3 asistentes**. Self-service, one-off, sin registro; `dai sync` no las toca. Colisión con una skill de dai → salta ([ADR-0013](docs/adr/0013-skills-externas-install-from.md)). Ej: `dai skills install --from github.com/mi-org/net-skills` |
161
162
  | `dai sync [--dry-run] [--for <asistentes>]` | **refresca** skills, constitución, templates y PR template a la versión del CLI — **aditivo** (no pisa tu `CLAUDE.md`), no toca el `.env` ni OpenSpec. Detecta los asistentes del repo o pasás `--for`. `--dry-run` muestra qué cambiaría ([ADR-0010](docs/adr/0010-versionado-y-upgrade.md)) |
163
+ | `dai upgrade [--check] [--dry-run]` · alias `dai update` | **actualiza el CLI global** a la última publicada (`npm i -g …@latest`) — self-update. **No toca el repo**: reporta el drift del scaffold pero deja el `dai sync` al mantenedor. `--check` solo informa · `--dry-run` muestra el comando ([ADR-0012](docs/adr/0012-upgrade-self-update-del-cli.md)) |
162
164
  | `dai publish <us.md>` | crea la US en el tracker (Jira/ClickUp/md) desde un `.md` y devuelve el key. Es el fallback del MCP para publicar sin el asistente |
163
165
  | `dai link-us <ID> [--us <md>]` | crea branch + `implements.yaml`; sin `--us` trae la US del tracker |
164
166
  | `dai link-us <ID> --resync` | re-estampa el `ac_hash` contra la US viva (tras un ⚠️ de check) |
@@ -173,12 +175,33 @@ flowchart TD
173
175
  | `dai doctor` · `dai docs <dest>` · `dai version` | diagnóstico del entorno (incluye **version-drift** del scaffold) · copiar la doc · versión (`dai version` avisa si tu repo quedó atrás) |
174
176
 
175
177
  > **🆕 Mantené tu repo al día — `dai sync`.** Las skills, la constitución y los templates son un
176
- > *caché derivable* del CLI. Cuando actualizás `dai` (`npm i -g @dforce2055/dai`), **`dai doctor` y
178
+ > *caché derivable* del CLI. Cuando actualizás `dai` (`dai upgrade`), **`dai doctor` y
177
179
  > `dai version` te avisan solos** si tu scaffold quedó atrás — con color y un `⬆️` —, y **`dai sync`**
178
180
  > lo refresca: **aditivo** (conserva tu `CLAUDE.md` propio), sin tocar el `.env` ni OpenSpec. Probá sin
179
181
  > riesgo con `dai sync --dry-run`. El versionado es semver: patch/minor no rompen nada; solo un major
180
182
  > pediría migración. ([ADR-0010](docs/adr/0010-versionado-y-upgrade.md))
181
183
 
184
+ > **🧩 Skills de cualquier stack — `dai skills install --from`.** Además de las skills de
185
+ > dai, cada equipo suma las suyas (por-stack: .NET, Java, Rust…) desde su propio repo:
186
+ > `dai skills install --from github.com/tu-org/net-skills`. dai las **convierte para los 3
187
+ > asistentes** (Claude/Cursor/Copilot) e instala. **dai es el distribuidor de skills de
188
+ > cualquier stack, sin opinar sobre su contenido** — self-service, sin registro; `dai sync`
189
+ > sigue siendo solo de dai. ([ADR-0013](docs/adr/0013-skills-externas-install-from.md))
190
+ >
191
+ > La fuente puede ser **pública, privada (por SSH) o un path local**: dai no hace auth
192
+ > propia, delega en git — **si podés `git clone` el repo, dai instala desde ahí**. Para
193
+ > privados usá la forma SSH (`git@github.com:tu-org/net-skills.git`), consistente con el
194
+ > modelo de auth de dai ([ADR-0007](docs/adr/0007-modelo-de-autenticacion.md)). Público =
195
+ > cero fricción entre equipos/máquinas.
196
+ >
197
+ > **Cómo armar una skill que dai ingiera.** Cada skill es un directorio con un `SKILL.md`
198
+ > en **formato Agent Skills de Claude**: frontmatter con **`name`** y **`description`**
199
+ > (obligatorios) + el cuerpo con las instrucciones. dai **valida ese contrato**: si a un
200
+ > `SKILL.md` le falta `name`/`description`, o un dir no tiene `SKILL.md`, lo **saltea con
201
+ > un warn** (no instala una skill rota). El **contenido** no lo valida — bajo tu criterio.
202
+ > Molde: [`templates/skill.md`](templates/skill.md) · ejemplos reales: las skills de dai
203
+ > en [`skills/`](skills/).
204
+
182
205
  Skills (se invocan en el asistente): `/doc-to-backlog` · `/grill-intent` · `/grill-epic` · `/grill-user-story` · `/link-us` ·
183
206
  `/tdd` · `/dai-review`. Config del tracker (`md`\|`jira`\|`clickup`) y tokens: en `.env` —
184
207
  ver [`.env.example`](.env.example). Auth (SSH + tokens): [ADR-0007](docs/adr/0007-modelo-de-autenticacion.md).
package/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.0
1
+ 0.7.0
package/cli/dai.mjs CHANGED
@@ -29,7 +29,8 @@ import { composePrBody, prTitle, forgeTool } from "./lib/pr.mjs";
29
29
  import { dirsEqual } from "./lib/fsutil.mjs";
30
30
  import { parseFlags, parseAssistants, isAssistantToken } from "./lib/args.mjs";
31
31
  import { versionDrift, planUpgrade } from "./lib/semver.mjs";
32
- import { skillToPrompt, skillToCursor, constitution, constitutionCursorRule, envFor, mergeEnv, upsertBlock, reconcileGitignore } from "./lib/bootstrap.mjs";
32
+ import { parseSource } from "./lib/skills-source.mjs";
33
+ import { skillToPrompt, skillToCursor, validateSkill, constitution, constitutionCursorRule, envFor, mergeEnv, upsertBlock, reconcileGitignore } from "./lib/bootstrap.mjs";
33
34
 
34
35
  const HERE = dirname(fileURLToPath(import.meta.url));
35
36
 
@@ -416,6 +417,7 @@ async function cmdPr(opts) {
416
417
 
417
418
  // ── install: skills → ~/.claude/skills o <repo>/.claude/skills ────────────────
418
419
  async function cmdInstall(opts) {
420
+ if (opts.from !== undefined) return cmdInstallFrom(opts); // skills externas (ADR-0013)
419
421
  const skillsSrc = join(ROOT, "skills");
420
422
  const skills = readdirSync(skillsSrc).filter((n) => statSync(join(skillsSrc, n)).isDirectory());
421
423
  let want;
@@ -489,6 +491,89 @@ async function cmdInstall(opts) {
489
491
  if (rl) rl.close();
490
492
  }
491
493
 
494
+ // ── skills install --from: skills EXTERNAS (por-stack) desde un repo/dir ───────
495
+ // Self-service, one-off, sin registro ni sync (ADR-0013). Convierte cada skill
496
+ // para los 3 asistentes y la instala en el repo. No pisa las skills built-in de
497
+ // dai (colisión → warn + skip). `dai sync` NO las toca: es solo de dai.
498
+ function cmdInstallFrom(opts) {
499
+ if (typeof opts.from !== "string" || !opts.from.trim())
500
+ fail("--from necesita una fuente: un git URL (github.com/org/skills[#ref]) o un path local", 2);
501
+ let want;
502
+ try { want = parseAssistants(typeof opts.for === "string" ? opts.for : "all"); }
503
+ catch (e) { fail(`--for ${e.message}`); }
504
+
505
+ let src;
506
+ try { src = parseSource(opts.from); } catch (e) { fail(`--from ${e.message}`, 2); }
507
+
508
+ // Resolver la fuente a un directorio local.
509
+ let root, tmp = null;
510
+ if (src.type === "git") {
511
+ tmp = mkdtempSync(join(tmpdir(), "dai-skills-"));
512
+ info(`clonando ${src.location}${src.ref ? " @ " + src.ref : ""} …`);
513
+ const args = ["clone", "--depth", "1"];
514
+ if (src.ref) args.push("--branch", src.ref);
515
+ args.push(src.location, tmp);
516
+ try { git(args); }
517
+ catch (e) { rmSync(tmp, { recursive: true, force: true }); fail(`no pude clonar la fuente: ${String(e.message).split("\n")[0]}`, 1); }
518
+ root = tmp;
519
+ } else {
520
+ root = src.location;
521
+ if (!existsSync(root)) fail(`no existe la fuente: ${root}`, 2);
522
+ }
523
+
524
+ const cleanup = () => { if (tmp) rmSync(tmp, { recursive: true, force: true }); };
525
+
526
+ // El dir de skills: <root>/skills, o <root> si ya contiene <name>/SKILL.md.
527
+ const skillsDir = existsSync(join(root, "skills")) ? join(root, "skills") : root;
528
+ let entries;
529
+ try {
530
+ entries = readdirSync(skillsDir).filter((n) => {
531
+ try { return statSync(join(skillsDir, n)).isDirectory(); } catch { return false; }
532
+ });
533
+ } catch { cleanup(); fail(`no pude leer la fuente: ${skillsDir}`, 2); }
534
+ if (entries.length === 0) { cleanup(); fail("la fuente no tiene directorios de skill", 2); }
535
+
536
+ // Destino: repo local (default cwd), o --local <repo> / --global.
537
+ const repo = opts.global ? homedir() : (typeof opts.local === "string" ? opts.local : process.cwd());
538
+ const builtins = new Set(readdirSync(join(ROOT, "skills")));
539
+ const forStr = ["claude", "copilot", "cursor"].filter((a) => want[a]).join("+");
540
+ info(`skills install --from ${opts.from} · ${entries.length} candidata(s) · asistentes: ${forStr}${opts.dryRun ? " [dry-run]" : ""}`);
541
+
542
+ let installed = 0, skipped = 0;
543
+ for (const name of entries) {
544
+ const srcDir = join(skillsDir, name);
545
+ const skillPath = join(srcDir, "SKILL.md");
546
+ // Validación estructural (contrato mínimo, no contenido — ADR-0013).
547
+ if (!existsSync(skillPath)) { warn(`'${name}' no tiene SKILL.md — salto`); skipped++; continue; }
548
+ if (builtins.has(name)) { warn(`'${name}' choca con una skill de dai — salto (renombrala, p.ej. ${name}-<stack>)`); skipped++; continue; }
549
+ const md = readFileSync(skillPath, "utf8");
550
+ const bad = validateSkill(md);
551
+ if (bad) { warn(`'${name}' inválida: ${bad} — salto (ver templates/skill.md)`); skipped++; continue; }
552
+ if (opts.dryRun) { info(`[dry-run] ${name} → ${forStr}`); continue; }
553
+ if (want.claude) {
554
+ const t = join(repo, ".claude", "skills", name);
555
+ rmSync(t, { recursive: true, force: true }); mkdirSync(dirname(t), { recursive: true }); cpSync(srcDir, t, { recursive: true });
556
+ }
557
+ if (want.cursor) {
558
+ const t = join(repo, ".cursor", "skills", name);
559
+ rmSync(t, { recursive: true, force: true }); cpSync(srcDir, t, { recursive: true }); writeFileSync(join(t, "SKILL.md"), skillToCursor(md));
560
+ }
561
+ if (want.copilot) {
562
+ const pdir = join(repo, ".github", "prompts"); mkdirSync(pdir, { recursive: true });
563
+ writeFileSync(join(pdir, `${name}.prompt.md`), skillToPrompt(md));
564
+ }
565
+ ok(name); installed++;
566
+ }
567
+ cleanup();
568
+ if (opts.dryRun) { info("dry-run: nada escrito."); return; }
569
+ process.stdout.write("\n");
570
+ if (installed === 0) {
571
+ fail(`0 skills instaladas${skipped ? ` (${skipped} salteada(s))` : ""} — revisá el formato: cada skill es un dir con SKILL.md y frontmatter (name + description). Molde en templates/skill.md`, 1);
572
+ }
573
+ ok(`${installed} skill(s) externa(s) instalada(s)${skipped ? `, ${skipped} salteada(s)` : ""} desde ${opts.from}`);
574
+ warn("skills externas — bajo tu criterio: dai las convierte e instala, no las vetea ni las trackea. `dai sync` no las toca.");
575
+ }
576
+
492
577
  // ── helpers de prompt interactivo ─────────────────────────────────────────────
493
578
  async function askMenu(rl, title, choices, def) {
494
579
  process.stdout.write(`\n ${title}\n`);
@@ -935,7 +1020,11 @@ switch (cmd) {
935
1020
  case "pr": cmdPr(opts).catch((e) => fail(String(e.message))); break;
936
1021
  case "done": cmdDone(opts); break;
937
1022
  case "archive": cmdArchive(pos[0], opts); break;
938
- case "install": cmdInstall(opts).catch((e) => fail(String(e.message))); break;
1023
+ case "install": cmdInstall(opts).catch((e) => fail(String(e.message))); break; // alias de `dai skills install`
1024
+ case "skills":
1025
+ if (pos[0] === "install" || pos[0] === undefined) cmdInstall(opts).catch((e) => fail(String(e.message)));
1026
+ else fail(`subcomando de skills desconocido: '${pos[0]}' (por ahora: install)`, 2);
1027
+ break;
939
1028
  case "init": cmdInit(pos[0], opts).catch((e) => fail(String(e.message))); break;
940
1029
  case "sync": cmdSync(pos[0], opts); break;
941
1030
  case "upgrade":
@@ -959,7 +1048,8 @@ switch (cmd) {
959
1048
  " pr [--assignee u] [--base b] [--draft] [--yes] crea TU PR/MR precargada (muestra + confirma)\n" +
960
1049
  " forge comment <ref> --body-file <f> · forge pr <ref> comentar/leer una PR ajena (github/gitlab)\n\n" +
961
1050
  "Instalación:\n" +
962
- " install [--global | --local <repo>] [--force] [--dry-run] [--for <asistentes>] skills Claude/Cursor\n" +
1051
+ " skills install [--global | --local <repo>] [--force] [--dry-run] [--for <asistentes>] instala las skills de dai (alias: `install`)\n" +
1052
+ " skills install --from <git-url|path>[#ref] [--for <asistentes>] instala skills EXTERNAS (por-stack), convertidas para los 3 asistentes (ADR-0013)\n" +
963
1053
  " init [<repo>] scaffolder interactivo del repo (asistente, gestor, OpenSpec)\n" +
964
1054
  " --for <asistentes> claude|copilot|cursor (combinables con coma) · o both|all (default all)\n" +
965
1055
  " ej: --for claude,cursor · --for copilot · --for all\n" +
@@ -14,6 +14,18 @@ export function parseFrontmatter(md) {
14
14
  return { name, description, body: m[2].trim() };
15
15
  }
16
16
 
17
+ // Valida el contrato MÍNIMO de un SKILL.md para que dai lo ingiera y lo convierta a
18
+ // los 3 asistentes (y para que Claude/Cursor/Copilot lo carguen): frontmatter con
19
+ // `name` y `description`. Devuelve null si está OK, o un string con el motivo.
20
+ // NO valida el contenido de la skill — eso es criterio del equipo (ADR-0013).
21
+ export function validateSkill(md) {
22
+ const { name, description } = parseFrontmatter(md);
23
+ if (!name && !description) return "sin frontmatter (falta name y description)";
24
+ if (!name) return "falta 'name' en el frontmatter";
25
+ if (!description) return "falta 'description' en el frontmatter";
26
+ return null;
27
+ }
28
+
17
29
  // Transforma un SKILL.md (Claude) en un prompt file de Copilot (.prompt.md).
18
30
  // Cambia el frontmatter; el cuerpo (la lógica) es el mismo.
19
31
  export function skillToPrompt(md) {
@@ -0,0 +1,26 @@
1
+ // dai · resolución de una fuente de skills externas (`dai skills install --from`).
2
+ // Núcleo puro (sin fs ni red): clasifica el argumento en git URL o path local y
3
+ // separa el ref (`#branch`/`#tag`). Ver ADR-0013.
4
+
5
+ // Devuelve { type: 'git'|'path', location, ref }.
6
+ // git → URL clonable. `host/org/repo` (sin esquema) se normaliza a https://.
7
+ // path → ruta local (relativa o absoluta). El `ref` se ignora aguas abajo.
8
+ // ref → lo que va después de '#', o null.
9
+ export function parseSource(src) {
10
+ const raw = String(src ?? "").trim();
11
+ if (!raw) throw new Error("fuente vacía (pasá un git URL o un path)");
12
+
13
+ // Separar el ref (#branch/tag). El scp de git (git@host:org/repo) no usa '#'.
14
+ let ref = null, loc = raw;
15
+ const hash = raw.lastIndexOf("#");
16
+ if (hash > 0) { ref = raw.slice(hash + 1) || null; loc = raw.slice(0, hash); }
17
+
18
+ // Path local explícito (./ ../ / ~/).
19
+ if (/^(\.\.?\/|\/|~\/)/.test(loc)) return { type: "path", location: loc, ref };
20
+ // git por sintaxis de URL (https, ssh, scp git@host:…).
21
+ if (/^(https?:\/\/|ssh:\/\/|git@|[\w.-]+@)/.test(loc)) return { type: "git", location: loc, ref };
22
+ // host/org/repo (p.ej. github.com/org/skills) → git https.
23
+ if (/^[\w.-]+\.[\w.-]+\/.+/.test(loc)) return { type: "git", location: "https://" + loc, ref };
24
+ // Resto: path local relativo sin ./.
25
+ return { type: "path", location: loc, ref };
26
+ }
@@ -0,0 +1,56 @@
1
+ # ADR-0013 — `dai skills install --from` (skills externas por-stack)
2
+
3
+ - **Estado:** aceptado
4
+ - **Fecha:** 2026-07-14
5
+ - **Decide:** lead / arquitecto de la metodología
6
+
7
+ ## Contexto
8
+
9
+ dai distribuye **sus** skills (las bundleadas: `grill-*`, `link-us`, `tdd`, …) e
10
+ instala/convierte para Claude, Cursor y Copilot. Pero los equipos tienen skills
11
+ **propias de su stack** (.NET, Java, Rust, …) que hoy no tienen lugar: o las copian
12
+ a mano por repo (duplicación, sin conversión a los 3 asistentes), o las meten en el
13
+ paquete dai — que **rompe el ADN**: dai es agnóstico del stack, opina solo en su
14
+ dominio (trazabilidad/distribución), no sobre *qué* dicen tus skills.
15
+
16
+ Falta una forma de que dai **convierta e instale** skills externas, sin volverse
17
+ dueño ni gatekeeper de ellas.
18
+
19
+ ## Decisión
20
+
21
+ Agregamos **`dai skills install --from <git-url|path>[#ref]`**: instala skills
22
+ **externas** desde un repo/dir (con estructura `skills/<nombre>/SKILL.md`, la misma
23
+ de dai), **convertidas para los 3 asistentes** (Claude copia · Cursor `skillToCursor`
24
+ · Copilot `skillToPrompt` → `.github/prompts/`).
25
+
26
+ - **`dai skills` es el namespace canónico** de las operaciones de skills, consistente
27
+ con `dai forge <verb>`. `dai skills install` (sin `--from`) instala las de dai;
28
+ **`dai install` queda como alias** silencioso (backward-compatible).
29
+ - **Self-service, one-off, sin registro.** No hay autorización central ni persistencia:
30
+ no existe `.dai/sources`, dai **no lleva registro** de qué repos usan skills externas.
31
+ El equipo las suma **bajo su propio criterio**.
32
+ - **`dai sync` NO las toca** — sigue siendo **solo** de las skills de dai. Si el equipo
33
+ actualiza sus skills, re-corre `--from`.
34
+ - **Colisión** con una skill built-in de dai → **warn + skip** (no se pisa la
35
+ metodología; renombran, p. ej. `tdd-dotnet`).
36
+
37
+ ## Consecuencias
38
+
39
+ - **Más fácil:** cada equipo suma sus skills por-stack sin tocar dai ni pedir permiso,
40
+ y las escribe **una vez** (dai las sirve a Claude/Cursor/Copilot). El namespace
41
+ `dai skills` deja lugar a crecer (`skills list`, …) sin comandos sueltos.
42
+ - **Se acepta pagar:** es **one-off** (dai no mantiene esas skills al día; re-corrés
43
+ `--from`). **Sin registro** → dai no sabe qué repos tienen skills externas, a
44
+ propósito: cero gatekeeping, bajo riesgo del equipo. Las fuentes remotas dependen de
45
+ git/red, y la **auth se delega en git** (público sin más; privado por SSH o credential
46
+ helper — dai no autentica): *si podés `git clone` la fuente, dai instala desde ahí*.
47
+
48
+ ## Alternativas consideradas
49
+
50
+ - **Persistido (`.dai/sources`) + integrado a `dai sync`** — descartado: obliga a dai a
51
+ **registrar y mantener** fuentes externas (gatekeeping que el equipo no quiere), y
52
+ acopla `dai sync` —que debe ser solo de dai— a repos ajenos.
53
+ - **Meter las skills de stack en el paquete dai** — descartado: rompe el ADN (dai
54
+ agnóstico del stack).
55
+ - **Comando suelto `dai install --from` sin namespace** — descartado: `dai skills <verb>`
56
+ es más claro y consistente con `dai forge <verb>`; `dai install` queda como alias.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dforce2055/dai",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Metodología de desarrollo asistido por IA — CLI de acciones deterministas (trazabilidad QUÉ↔CÓMO).",
5
5
  "repository": { "type": "git", "url": "git+https://github.com/dforce2055/dai.git" },
6
6
  "homepage": "https://dforce2055.github.io/dai/",
@@ -0,0 +1,45 @@
1
+ <!--
2
+ MOLDE DE SKILL · dai
3
+ ─────────────────────────────────────────────────────────────────
4
+ Formato: Agent Skills de Claude (SKILL.md). dai lo INGIERE y lo convierte a los 3
5
+ asistentes (Claude copia · Cursor `skillToCursor` · Copilot `.prompt.md`), con
6
+ `dai skills install --from <repo|path>` (ADR-0013).
7
+
8
+ Estructura del repo/dir fuente:
9
+ skills/
10
+ <nombre-de-la-skill>/
11
+ SKILL.md ← este archivo (renombrado a SKILL.md, en MAYÚSCULAS)
12
+ <recursos...> ← opcional: scripts, plantillas, refs que la skill use
13
+
14
+ CONTRATO MÍNIMO que dai valida (si falta, la salta con un warn):
15
+ - frontmatter con `name` y `description` (ambos obligatorios, en una línea).
16
+ - `name`: slug en kebab-case, IGUAL al nombre del directorio.
17
+ - `description`: una frase — con esto el agente decide CUÁNDO usar la skill.
18
+
19
+ dai NO valida el CONTENIDO (qué dice la skill, qué hacen sus scripts): eso es
20
+ criterio del equipo. Las instalás bajo tu propio riesgo.
21
+ -->
22
+ ---
23
+ name: mi-skill
24
+ description: Qué hace la skill y cuándo conviene invocarla — concreto y orientado al disparador.
25
+ ---
26
+
27
+ # <Título de la skill>
28
+
29
+ Instrucciones para el agente: qué hacer, paso a paso. El **cuerpo es el mismo** para
30
+ Claude, Cursor y Copilot — dai solo ajusta el frontmatter por asistente.
31
+
32
+ ## Cuándo usarla
33
+
34
+ El disparador concreto (qué pide el usuario, o qué situación la activa).
35
+
36
+ ## Pasos
37
+
38
+ 1. …
39
+ 2. …
40
+ 3. …
41
+
42
+ ## Notas
43
+
44
+ Convenciones del stack, ejemplos, o límites de la skill. Si necesita archivos de
45
+ apoyo, ponelos junto al `SKILL.md` y referencialos por ruta relativa.