adi_dev_workflow 1.3.0 → 1.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.
Files changed (75) hide show
  1. package/frameworks/commands/ministack/generate-tasks.md +212 -212
  2. package/frameworks/commands/sdd/generate-task-plan.md +4 -4
  3. package/frameworks/commands/sdd/generate-tech-direction.md +2 -2
  4. package/frameworks/commands/sdd/generate-tech-spec.md +4 -4
  5. package/frameworks/commands/sdd/generate-tests.md +171 -39
  6. package/frameworks/config/ai-framework-config.yaml +2 -2
  7. package/frameworks/skills/ministack-tasks-expert/SKILL.md +115 -104
  8. package/frameworks/skills/ministack-tech-direction-expert/SKILL.md +21 -6
  9. package/frameworks/skills/prompt-engineer-expert/SKILL.md +10 -7
  10. package/frameworks/skills/sdd-tech-direction-expert/SKILL.md +21 -6
  11. package/frameworks/skills/sdd-tech-spec-expert/SKILL.md +23 -7
  12. package/package.json +1 -1
  13. package/src/cli.js +27 -2
  14. package/src/installer.js +185 -106
  15. package/frameworks/agents/qa-validation-expert.md +0 -458
  16. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/benchmark.json +0 -99
  17. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/benchmark.md +0 -64
  18. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-1-happy-path/eval_metadata.json +0 -12
  19. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-1-happy-path/with_skill/grading.json +0 -32
  20. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-1-happy-path/with_skill/outputs/response.md +0 -134
  21. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-1-happy-path/with_skill/outputs/transcript.md +0 -68
  22. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-1-happy-path/with_skill/timing.json +0 -5
  23. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-1-happy-path/without_skill/grading.json +0 -32
  24. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-1-happy-path/without_skill/outputs/response.md +0 -525
  25. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-1-happy-path/without_skill/outputs/transcript.md +0 -30
  26. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-1-happy-path/without_skill/timing.json +0 -5
  27. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-2-spec-simples/eval_metadata.json +0 -12
  28. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-2-spec-simples/with_skill/grading.json +0 -32
  29. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-2-spec-simples/with_skill/outputs/response.md +0 -1126
  30. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-2-spec-simples/with_skill/outputs/transcript.md +0 -131
  31. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-2-spec-simples/with_skill/timing.json +0 -5
  32. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-2-spec-simples/without_skill/grading.json +0 -32
  33. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-2-spec-simples/without_skill/outputs/response.md +0 -452
  34. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-2-spec-simples/without_skill/outputs/transcript.md +0 -78
  35. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-2-spec-simples/without_skill/timing.json +0 -5
  36. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-3-sem-user-stories/eval_metadata.json +0 -12
  37. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-3-sem-user-stories/with_skill/grading.json +0 -32
  38. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-3-sem-user-stories/with_skill/outputs/response.md +0 -101
  39. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-3-sem-user-stories/with_skill/outputs/transcript.md +0 -133
  40. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-3-sem-user-stories/with_skill/timing.json +0 -5
  41. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-3-sem-user-stories/without_skill/grading.json +0 -32
  42. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-3-sem-user-stories/without_skill/outputs/response.md +0 -248
  43. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-3-sem-user-stories/without_skill/outputs/transcript.md +0 -49
  44. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/eval-3-sem-user-stories/without_skill/timing.json +0 -5
  45. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-1/review.html +0 -1325
  46. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/benchmark.json +0 -94
  47. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/benchmark.md +0 -67
  48. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-1-happy-path/eval_metadata.json +0 -12
  49. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-1-happy-path/with_skill/grading.json +0 -32
  50. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-1-happy-path/with_skill/outputs/response.md +0 -117
  51. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-1-happy-path/with_skill/outputs/transcript.md +0 -91
  52. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-1-happy-path/with_skill/timing.json +0 -1
  53. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-1-happy-path/without_skill/grading.json +0 -32
  54. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-1-happy-path/without_skill/outputs/response.md +0 -694
  55. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-1-happy-path/without_skill/outputs/transcript.md +0 -45
  56. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-1-happy-path/without_skill/timing.json +0 -1
  57. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-2-spec-simples/eval_metadata.json +0 -12
  58. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-2-spec-simples/with_skill/grading.json +0 -32
  59. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-2-spec-simples/with_skill/outputs/response.md +0 -1087
  60. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-2-spec-simples/with_skill/outputs/transcript.md +0 -124
  61. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-2-spec-simples/with_skill/timing.json +0 -1
  62. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-2-spec-simples/without_skill/grading.json +0 -32
  63. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-2-spec-simples/without_skill/outputs/response.md +0 -458
  64. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-2-spec-simples/without_skill/outputs/transcript.md +0 -84
  65. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-2-spec-simples/without_skill/timing.json +0 -1
  66. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-3-sem-user-stories/eval_metadata.json +0 -12
  67. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-3-sem-user-stories/with_skill/grading.json +0 -32
  68. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-3-sem-user-stories/with_skill/outputs/response.md +0 -70
  69. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-3-sem-user-stories/with_skill/outputs/transcript.md +0 -148
  70. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-3-sem-user-stories/with_skill/timing.json +0 -1
  71. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-3-sem-user-stories/without_skill/grading.json +0 -32
  72. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-3-sem-user-stories/without_skill/outputs/response.md +0 -249
  73. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-3-sem-user-stories/without_skill/outputs/transcript.md +0 -80
  74. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/eval-3-sem-user-stories/without_skill/timing.json +0 -1
  75. package/frameworks/skills/sdd-task-plan-expert-workspace/iteration-2/review.html +0 -1325
@@ -20,6 +20,20 @@ Estilo: Objetivo. Contextualizado. Perguntas curtas com opções baseadas no cod
20
20
 
21
21
  ---
22
22
 
23
+ # Paths dos Artefatos
24
+
25
+ > **OBRIGATÓRIO**: Antes de salvar qualquer artefato, leia `.claude/config/ai-framework-config.yaml` seção `sdd` e use os paths ali definidos.
26
+
27
+ Paths definidos no config (para referência):
28
+
29
+ | Artefato | Path |
30
+ |----------|------|
31
+ | tech_direction | `docs/specs/{feature}/{version}/tech_direction.md` |
32
+
33
+ Substitua `{feature}` pelo nome da feature em kebab-case e `{version}` pela versão (ex: `v1`) identificados a partir do path do PRD recebido como argumento.
34
+
35
+ ---
36
+
23
37
  # Regra de Acentuação
24
38
 
25
39
  Todo artefato gerado por esta skill é um documento em português brasileiro. Todo conteúdo textual (títulos, descrições, instruções, regras, mensagens) deve usar acentuação correta do pt-BR:
@@ -188,7 +202,7 @@ Estas regras sao **absolutas** e nao podem ser violadas em nenhuma circunstancia
188
202
 
189
203
  O Tech Direction e salvo **na mesma pasta** do PRD aprovado. O versionamento ja foi definido pelo PRD:
190
204
 
191
- - Se o PRD esta em `docs/feature-x/v1/prd.md`, o tech_direction vai em `docs/feature-x/v1/tech_direction.md`
205
+ - Se o PRD esta em `docs/specs/feature-x/v1/prd.md`, o tech_direction vai em `docs/specs/feature-x/v1/tech_direction.md`
192
206
  - **NAO crie nova versao** — use a mesma pasta do PRD fornecido como argumento
193
207
 
194
208
  ---
@@ -197,10 +211,11 @@ O Tech Direction e salvo **na mesma pasta** do PRD aprovado. O versionamento ja
197
211
 
198
212
  **ANTES de apresentar o Tech Direction ao usuario**, voce DEVE:
199
213
 
200
- 1. **Identificar o diretorio** do PRD fornecido (ex: `docs/feature-x/v1/`)
201
- 2. **Remover todos os comentarios `<!-- LLM-ONLY: ... -->`** do conteudo antes de salvar
202
- 3. **Salvar o arquivo fisico** em: `docs/[nome-feature]/vN/tech_direction.md` (mesmo diretorio do PRD)
203
- 4. **Confirmar** que o arquivo foi criado com sucesso
214
+ 1. **Ler o config** `.claude/config/ai-framework-config.yaml` secao `sdd.tech_direction` para obter o path
215
+ 2. **Identificar o diretorio** do PRD fornecido (ex: `docs/specs/feature-x/v1/`)
216
+ 3. **Remover todos os comentarios `<!-- LLM-ONLY: ... -->`** do conteudo antes de salvar
217
+ 4. **Salvar o arquivo fisico** em: `docs/specs/[nome-feature]/[versao]/tech_direction.md` (mesmo diretorio do PRD)
218
+ 5. **Confirmar** que o arquivo foi criado com sucesso
204
219
 
205
220
  ---
206
221
 
@@ -209,7 +224,7 @@ O Tech Direction e salvo **na mesma pasta** do PRD aprovado. O versionamento ja
209
224
  Apos salvar o arquivo fisico, apresente **apenas um resumo compacto**. NAO exiba o tech_direction completo no terminal.
210
225
 
211
226
  ```
212
- Arquivo salvo em: docs/[nome-feature]/vN/tech_direction.md
227
+ Arquivo salvo em: docs/specs/[nome-feature]/[versao]/tech_direction.md
213
228
 
214
229
  ## Resumo do Tech Direction
215
230
  - **Decisoes:** [lista curta]
@@ -8,6 +8,22 @@ Responsabilidade: Transformar PRDs aprovados em especificações técnicas compl
8
8
 
9
9
  ---
10
10
 
11
+ # Paths dos Artefatos
12
+
13
+ > **OBRIGATÓRIO**: Antes de salvar qualquer artefato, leia `.claude/config/ai-framework-config.yaml` seção `sdd` e use os paths ali definidos.
14
+
15
+ Paths definidos no config (para referência):
16
+
17
+ | Artefato | Path |
18
+ |----------|------|
19
+ | prd (leitura) | `docs/specs/{feature}/{version}/prd.md` |
20
+ | tech_direction (leitura, opcional) | `docs/specs/{feature}/{version}/tech_direction.md` |
21
+ | spec_tech (escrita) | `docs/specs/{feature}/{version}/spec_tech.md` |
22
+
23
+ Substitua `{feature}` pelo nome da feature em kebab-case e `{version}` pela versão (ex: `v1`) identificados a partir do path do PRD recebido como argumento.
24
+
25
+ ---
26
+
11
27
  # Regra de Acentuação
12
28
 
13
29
  Todo artefato gerado por esta skill é um documento em português brasileiro. Todo conteúdo textual (títulos, descrições, instruções, regras, mensagens) deve usar acentuação correta do pt-BR:
@@ -57,7 +73,7 @@ O SPEC_TECH responde: **COMO a solução será implementada tecnicamente?**
57
73
  **ANTES de definir o SPEC_TECH**, você DEVE obrigatoriamente:
58
74
 
59
75
  ### 1. Verificar Tech Direction (opcional)
60
- - Buscar em: `docs/[nome-feature]/vN/tech_direction.md` (onde vN é a versão mais recente)
76
+ - Buscar em: `docs/specs/[nome-feature]/[versão]/tech_direction.md` (onde vN é a versão mais recente)
61
77
  - Se existir, **use como ponto de partida** para decisões técnicas
62
78
  - O tech_direction contém decisões já tomadas, tecnologias sugeridas, padrões preferidos e restrições
63
79
  - Você pode **complementar, ajustar ou questionar** qualquer item — não é uma ordem, é um direcionamento
@@ -107,7 +123,7 @@ O usuário pode criar um arquivo **`tech_direction.md`** na pasta da feature **a
107
123
 
108
124
  ### O que é
109
125
 
110
- É um arquivo estruturado localizado em `docs/[nome-feature]/vN/tech_direction.md` com as seguintes seções:
126
+ É um arquivo estruturado localizado em `docs/specs/[nome-feature]/[versão]/tech_direction.md` com as seguintes seções:
111
127
 
112
128
  | Seção | O que contém |
113
129
  |-------|-------------|
@@ -124,7 +140,7 @@ O template completo está em: [tech_direction-template.md](templates/tech_direct
124
140
  **ANTES de iniciar as perguntas**, você DEVE buscar o arquivo:
125
141
 
126
142
  ```
127
- docs/[nome-feature]/vN/tech_direction.md
143
+ docs/specs/[nome-feature]/[versão]/tech_direction.md
128
144
  ```
129
145
 
130
146
  - **Se existir**: use como ponto de partida para decisões técnicas
@@ -223,7 +239,7 @@ A seção 14 é preenchida pelo **comando orquestrador** (`/sdd:generate-spec-te
223
239
  Após coletar todas as decisões:
224
240
  1. Gere o SPEC_TECH completo (todas as 16 seções) usando as respostas coletadas
225
241
  2. Preencha a seção 15 (Arquivos Envolvidos) baseado na pesquisa do codebase
226
- 3. **Salve o arquivo** em `docs/[nome-feature]/vN/spec_tech.md`
242
+ 3. **Salve o arquivo** em `docs/specs/[nome-feature]/[versão]/spec_tech.md`
227
243
  4. Apresente ao usuário para validação final
228
244
 
229
245
  ### Regras do Processo
@@ -288,9 +304,9 @@ O template completo está em: [spec_tech_template.md](templates/spec_tech_templa
288
304
  **ANTES de apresentar o SPEC_TECH ao usuário**, você DEVE:
289
305
 
290
306
  1. **Identificar o nome da feature** a partir do PRD (kebab-case, letras minúsculas, sem espaços)
291
- 2. **Criar diretório** se não existir: `docs/[nome-feature]/vN/`
307
+ 2. **Criar diretório** se não existir: `docs/specs/[nome-feature]/[versão]/`
292
308
  3. **Remover todos os comentários `<!-- LLM-ONLY: ... -->`** do conteúdo antes de salvar — são instruções internas do template e NÃO devem aparecer no arquivo gerado
293
- 4. **Salvar o arquivo físico** em: `docs/[nome-feature]/vN/spec_tech.md`
309
+ 4. **Salvar o arquivo físico** em: `docs/specs/[nome-feature]/[versão]/spec_tech.md`
294
310
  5. **Confirmar** que o arquivo foi criado com sucesso
295
311
 
296
312
  ---
@@ -300,7 +316,7 @@ O template completo está em: [spec_tech_template.md](templates/spec_tech_templa
300
316
  Após salvar o arquivo físico:
301
317
 
302
318
  ```
303
- Arquivo salvo em: docs/[nome-feature]/vN/spec_tech.md
319
+ Arquivo salvo em: docs/specs/[nome-feature]/[versão]/spec_tech.md
304
320
 
305
321
  Essa especificação técnica está aprovada? (sim/não)
306
322
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adi_dev_workflow",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Install SDD, miniStack and TaskCard development frameworks for Claude Code and Cursor",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.js CHANGED
@@ -92,10 +92,35 @@ export async function run(argv) {
92
92
 
93
93
  console.log(`\nInstalling to ${ideLabel} ...\n`);
94
94
 
95
- const summary = await install({ cwd, target, frameworks });
95
+ const summary = await install({
96
+ cwd,
97
+ target,
98
+ frameworks,
99
+ confirmOverwrite: async (labels) => {
100
+ console.log(` Os seguintes ${labels.length} itens ja existem em ${ideLabel} e serao SOBRESCRITOS:\n`);
101
+ for (const l of labels) console.log(` - ${l}`);
102
+ console.log('');
103
+ const res = await prompts({
104
+ type: 'confirm',
105
+ name: 'ok',
106
+ message: 'Deseja continuar e sobrescrever?',
107
+ initial: true,
108
+ });
109
+ return res.ok === true;
110
+ },
111
+ });
112
+
113
+ if (summary.cancelled) {
114
+ console.log('\n Install cancelled.\n');
115
+ return;
116
+ }
96
117
 
97
118
  console.log(` Done!\n`);
98
- console.log(` ${summary.skills} skills + ${summary.commands} commands + ${summary.templates} templates + ${summary.agents} agents + ${summary.config} config installed.\n`);
119
+ console.log(` ${summary.skills} skills + ${summary.commands} commands + ${summary.templates} templates + ${summary.agents} agents + ${summary.config} config installed.`);
120
+ if (summary.overwritten > 0) {
121
+ console.log(` ${summary.overwritten} item(s) sobrescritos.`);
122
+ }
123
+ console.log('');
99
124
 
100
125
  // Usage hints
101
126
  const hints = [];
package/src/installer.js CHANGED
@@ -6,64 +6,176 @@ import { transformContent } from './transformer.js';
6
6
  const __dirname = dirname(fileURLToPath(import.meta.url));
7
7
  const FRAMEWORKS_DIR = join(__dirname, '..', 'frameworks');
8
8
 
9
- const FRAMEWORK_MAP = {
10
- sdd: {
11
- commands: 'sdd',
12
- skills: ['sdd-prd-expert', 'sdd-spec-tech-expert', 'sdd-task-plan-expert', 'sdd-qa-expert'],
13
- },
14
- ministack: {
15
- commands: 'ministack',
16
- skills: ['ministack-intent-expert', 'ministack-scope-expert', 'ministack-expert', 'ministack-qa-expert'],
17
- },
18
- taskcard: {
19
- commands: 'taskcard',
20
- skills: ['taskcard-expert', 'taskcard-qa-expert'],
21
- },
22
- shared: {
23
- commands: null, // root-level commands
24
- skills: [],
25
- },
26
- };
27
-
28
9
  const IDE_DIRS = {
29
10
  claude: '.claude',
30
11
  cursor: '.cursor',
31
12
  };
32
13
 
33
- /**
34
- * Copy a directory recursively, applying transformation.
35
- */
14
+ // Prefixos usados para rotear skills e commands ao framework correto.
15
+ // Qualquer skill sem prefixo conhecido vai para 'shared'.
16
+ const FRAMEWORK_PREFIXES = {
17
+ sdd: 'sdd',
18
+ ministack: 'ministack',
19
+ taskcard: 'taskcard',
20
+ };
21
+
22
+ async function exists(path) {
23
+ try {
24
+ await stat(path);
25
+ return true;
26
+ } catch {
27
+ return false;
28
+ }
29
+ }
30
+
31
+ async function listDirs(dir) {
32
+ if (!(await exists(dir))) return [];
33
+ const entries = await readdir(dir, { withFileTypes: true });
34
+ return entries.filter((e) => e.isDirectory()).map((e) => e.name);
35
+ }
36
+
37
+ async function listFiles(dir) {
38
+ if (!(await exists(dir))) return [];
39
+ const entries = await readdir(dir, { withFileTypes: true });
40
+ return entries.filter((e) => e.isFile()).map((e) => e.name);
41
+ }
42
+
43
+ function skillFramework(skillName) {
44
+ for (const [fw, prefix] of Object.entries(FRAMEWORK_PREFIXES)) {
45
+ if (skillName === prefix || skillName.startsWith(prefix + '-')) return fw;
46
+ }
47
+ return 'shared';
48
+ }
49
+
50
+ async function copyFile(src, dest, target) {
51
+ await mkdir(dirname(dest), { recursive: true });
52
+ if (src.endsWith('.md')) {
53
+ const content = await readFile(src, 'utf-8');
54
+ const transformed = transformContent(content, target);
55
+ await writeFile(dest, transformed, 'utf-8');
56
+ } else {
57
+ const content = await readFile(src);
58
+ await writeFile(dest, content);
59
+ }
60
+ }
61
+
36
62
  async function copyDir(src, dest, target) {
37
63
  await mkdir(dest, { recursive: true });
38
64
  const entries = await readdir(src, { withFileTypes: true });
39
-
40
65
  for (const entry of entries) {
41
66
  const srcPath = join(src, entry.name);
42
67
  const destPath = join(dest, entry.name);
43
-
44
68
  if (entry.isDirectory()) {
45
69
  await copyDir(srcPath, destPath, target);
46
- } else if (entry.name.endsWith('.md')) {
47
- const content = await readFile(srcPath, 'utf-8');
48
- const transformed = transformContent(content, target);
49
- await writeFile(destPath, transformed, 'utf-8');
50
70
  } else {
51
- const content = await readFile(srcPath);
52
- await writeFile(destPath, content);
71
+ await copyFile(srcPath, destPath, target);
53
72
  }
54
73
  }
55
74
  }
56
75
 
76
+ async function countMarkdownRecursive(dir) {
77
+ let count = 0;
78
+ const entries = await readdir(dir, { withFileTypes: true });
79
+ for (const entry of entries) {
80
+ const full = join(dir, entry.name);
81
+ if (entry.isDirectory()) count += await countMarkdownRecursive(full);
82
+ else if (entry.name.endsWith('.md')) count++;
83
+ }
84
+ return count;
85
+ }
86
+
57
87
  /**
58
- * Check if a path exists.
88
+ * Constroi o plano de instalacao varrendo frameworks/ e mapeando cada item
89
+ * (skill, command, agent, template, config) para seu destino no IDE.
90
+ * Cada item e marcado com `overwrite: true` se o destino ja existir.
59
91
  */
60
- async function exists(path) {
61
- try {
62
- await stat(path);
63
- return true;
64
- } catch {
65
- return false;
92
+ async function buildPlan({ cwd, target, frameworks }) {
93
+ const ideDir = join(cwd, IDE_DIRS[target]);
94
+ const selected = new Set(frameworks);
95
+ const items = [];
96
+
97
+ // Skills: detectadas automaticamente por prefixo
98
+ const skillNames = await listDirs(join(FRAMEWORKS_DIR, 'skills'));
99
+ for (const name of skillNames) {
100
+ const fw = skillFramework(name);
101
+ if (!selected.has(fw)) continue;
102
+ items.push({
103
+ category: 'skills',
104
+ kind: 'dir',
105
+ src: join(FRAMEWORKS_DIR, 'skills', name),
106
+ dest: join(ideDir, 'skills', name),
107
+ label: `skills/${name}`,
108
+ });
109
+ }
110
+
111
+ // Commands: subdir por framework + arquivos root-level para 'shared'
112
+ const commandDirs = await listDirs(join(FRAMEWORKS_DIR, 'commands'));
113
+ for (const name of commandDirs) {
114
+ if (!FRAMEWORK_PREFIXES[name] || !selected.has(name)) continue;
115
+ items.push({
116
+ category: 'commands',
117
+ kind: 'dir',
118
+ src: join(FRAMEWORKS_DIR, 'commands', name),
119
+ dest: join(ideDir, 'commands', name),
120
+ label: `commands/${name}/`,
121
+ });
122
+ }
123
+ if (selected.has('shared')) {
124
+ const rootCmds = await listFiles(join(FRAMEWORKS_DIR, 'commands'));
125
+ for (const name of rootCmds) {
126
+ if (!name.endsWith('.md')) continue;
127
+ items.push({
128
+ category: 'commands',
129
+ kind: 'file',
130
+ src: join(FRAMEWORKS_DIR, 'commands', name),
131
+ dest: join(ideDir, 'commands', name),
132
+ label: `commands/${name}`,
133
+ });
134
+ }
135
+ }
136
+
137
+ // Agents, templates, config: pertencem a 'shared'
138
+ if (selected.has('shared')) {
139
+ for (const name of await listFiles(join(FRAMEWORKS_DIR, 'agents'))) {
140
+ if (!name.endsWith('.md')) continue;
141
+ items.push({
142
+ category: 'agents',
143
+ kind: 'file',
144
+ src: join(FRAMEWORKS_DIR, 'agents', name),
145
+ dest: join(ideDir, 'agents', name),
146
+ label: `agents/${name}`,
147
+ });
148
+ }
149
+ for (const name of await listFiles(join(FRAMEWORKS_DIR, 'templates'))) {
150
+ if (!name.endsWith('.md')) continue;
151
+ items.push({
152
+ category: 'templates',
153
+ kind: 'file',
154
+ src: join(FRAMEWORKS_DIR, 'templates', name),
155
+ dest: join(ideDir, 'templates', name),
156
+ label: `templates/${name}`,
157
+ });
158
+ }
159
+ for (const name of await listFiles(join(FRAMEWORKS_DIR, 'config'))) {
160
+ items.push({
161
+ category: 'config',
162
+ kind: 'file',
163
+ src: join(FRAMEWORKS_DIR, 'config', name),
164
+ dest: join(ideDir, 'config', name),
165
+ label: `config/${name}`,
166
+ });
167
+ }
168
+ }
169
+
170
+ for (const item of items) {
171
+ item.overwrite = await exists(item.dest);
66
172
  }
173
+
174
+ return { ideDir, items };
175
+ }
176
+
177
+ export async function planInstall(options) {
178
+ return buildPlan(options);
67
179
  }
68
180
 
69
181
  /**
@@ -73,81 +185,48 @@ async function exists(path) {
73
185
  * @param {string} options.cwd - Current working directory
74
186
  * @param {'claude'|'cursor'} options.target - Target IDE
75
187
  * @param {string[]} options.frameworks - Frameworks to install ('sdd', 'ministack', 'taskcard', 'shared')
76
- * @returns {Object} Summary of installed items
188
+ * @param {(labels: string[]) => Promise<boolean>} [options.confirmOverwrite] - Called when items will be overwritten
189
+ * @returns {Object} Summary of installed items (or { cancelled: true })
77
190
  */
78
- export async function install({ cwd, target, frameworks }) {
79
- const ideDir = join(cwd, IDE_DIRS[target]);
80
- const commandsDir = join(ideDir, 'commands');
81
- const skillsDir = join(ideDir, 'skills');
82
- const templatesDir = join(ideDir, 'templates');
83
- const agentsDir = join(ideDir, 'agents');
84
-
85
- const summary = { commands: 0, skills: 0, templates: 0, agents: 0, config: 0 };
86
-
87
- for (const fw of frameworks) {
88
- const config = FRAMEWORK_MAP[fw];
89
- if (!config) continue;
90
-
91
- // Copy commands
92
- if (fw === 'shared') {
93
- // Root-level commands (generate-prompt.md, sync-tasks-to-linear.md)
94
- await mkdir(commandsDir, { recursive: true });
95
- const srcCmds = join(FRAMEWORKS_DIR, 'commands');
96
- const entries = await readdir(srcCmds, { withFileTypes: true });
97
-
98
- for (const entry of entries) {
99
- if (entry.isFile() && entry.name.endsWith('.md')) {
100
- const content = await readFile(join(srcCmds, entry.name), 'utf-8');
101
- const transformed = transformContent(content, target);
102
- await writeFile(join(commandsDir, entry.name), transformed, 'utf-8');
103
- summary.commands++;
104
- }
105
- }
191
+ export async function install({ cwd, target, frameworks, confirmOverwrite }) {
192
+ const plan = await buildPlan({ cwd, target, frameworks });
193
+ const overwrites = plan.items.filter((i) => i.overwrite);
106
194
 
107
- // Shared templates
108
- const srcTemplates = join(FRAMEWORKS_DIR, 'templates');
109
- if (await exists(srcTemplates)) {
110
- await copyDir(srcTemplates, templatesDir, target);
111
- const tplEntries = await readdir(srcTemplates);
112
- summary.templates += tplEntries.filter((f) => f.endsWith('.md')).length;
113
- }
114
-
115
- // Shared agents
116
- const srcAgents = join(FRAMEWORKS_DIR, 'agents');
117
- if (await exists(srcAgents)) {
118
- await copyDir(srcAgents, agentsDir, target);
119
- const agentEntries = await readdir(srcAgents);
120
- summary.agents += agentEntries.filter((f) => f.endsWith('.md')).length;
121
- }
195
+ if (overwrites.length > 0 && typeof confirmOverwrite === 'function') {
196
+ const ok = await confirmOverwrite(overwrites.map((i) => i.label));
197
+ if (!ok) return { cancelled: true };
198
+ }
122
199
 
123
- // Config file
124
- const srcConfig = join(FRAMEWORKS_DIR, 'config', 'ai-framework-config.yaml');
125
- if (await exists(srcConfig)) {
126
- const configDestDir = join(ideDir, 'config');
127
- await mkdir(configDestDir, { recursive: true });
128
- const content = await readFile(srcConfig);
129
- await writeFile(join(configDestDir, 'ai-framework-config.yaml'), content);
130
- summary.config = 1;
131
- }
200
+ const summary = {
201
+ skills: 0,
202
+ commands: 0,
203
+ templates: 0,
204
+ agents: 0,
205
+ config: 0,
206
+ overwritten: overwrites.length,
207
+ };
208
+
209
+ for (const item of plan.items) {
210
+ if (item.kind === 'dir') {
211
+ await copyDir(item.src, item.dest, target);
132
212
  } else {
133
- // Framework-specific commands
134
- if (config.commands) {
135
- const srcCmdDir = join(FRAMEWORKS_DIR, 'commands', config.commands);
136
- const destCmdDir = join(commandsDir, config.commands);
137
- await copyDir(srcCmdDir, destCmdDir, target);
138
- const entries = await readdir(srcCmdDir);
139
- summary.commands += entries.filter((f) => f.endsWith('.md')).length;
140
- }
213
+ await copyFile(item.src, item.dest, target);
214
+ }
141
215
 
142
- // Framework-specific skills
143
- for (const skillName of config.skills) {
144
- const srcSkillDir = join(FRAMEWORKS_DIR, 'skills', skillName);
145
- const destSkillDir = join(skillsDir, skillName);
146
- if (await exists(srcSkillDir)) {
147
- await copyDir(srcSkillDir, destSkillDir, target);
148
- summary.skills++;
149
- }
216
+ if (item.category === 'skills') {
217
+ summary.skills++;
218
+ } else if (item.category === 'commands') {
219
+ if (item.kind === 'dir') {
220
+ summary.commands += await countMarkdownRecursive(item.src);
221
+ } else {
222
+ summary.commands++;
150
223
  }
224
+ } else if (item.category === 'agents') {
225
+ summary.agents++;
226
+ } else if (item.category === 'templates') {
227
+ summary.templates++;
228
+ } else if (item.category === 'config') {
229
+ summary.config++;
151
230
  }
152
231
  }
153
232