@lugom.io/hefesto 0.2.0 → 1.0.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 (42) hide show
  1. package/agents/hefesto-argos.md +93 -0
  2. package/agents/hefesto-athena.md +99 -0
  3. package/agents/hefesto-hermes.md +96 -0
  4. package/bin/install.js +122 -52
  5. package/hooks/hefesto-check-update.cjs +32 -11
  6. package/hooks/hefesto-statusline.cjs +8 -17
  7. package/hooks/hefesto-workflow.cjs +68 -0
  8. package/package.json +14 -4
  9. package/skills/hefesto-context/SKILL.md +67 -14
  10. package/skills/hefesto-debug/SKILL.md +54 -0
  11. package/skills/hefesto-design/SKILL.md +184 -0
  12. package/skills/hefesto-execute/SKILL.md +133 -0
  13. package/skills/hefesto-init/SKILL.md +105 -0
  14. package/skills/hefesto-init/references/api.md +116 -0
  15. package/skills/hefesto-init/references/cli.md +91 -0
  16. package/skills/hefesto-init/references/mobile.md +69 -0
  17. package/skills/hefesto-init/references/web.md +246 -0
  18. package/skills/hefesto-new-feature/SKILL.md +87 -0
  19. package/skills/hefesto-security/SKILL.md +89 -0
  20. package/skills/hefesto-security/references/boundaries-and-bypasses.md +152 -0
  21. package/skills/hefesto-security/references/secrets-detection.md +121 -0
  22. package/skills/hefesto-security/references/severity-and-judgment.md +176 -0
  23. package/skills/hefesto-simplify/SKILL.md +82 -0
  24. package/templates/TPL-CLAUDE.md +54 -0
  25. package/templates/TPL-CONFIG.json +19 -0
  26. package/templates/TPL-DESIGN.md +305 -0
  27. package/templates/{FEATURE.md → TPL-FEATURE.md} +13 -6
  28. package/templates/TPL-PROJECT.md +50 -0
  29. package/templates/TPL-RECON.md +60 -0
  30. package/templates/{RESEARCH.md → TPL-RESEARCH.md} +32 -35
  31. package/templates/TPL-SECURITY.md +42 -0
  32. package/templates/TPL-SIMPLIFY.md +40 -0
  33. package/templates/{STATE.md → TPL-STATE.md} +1 -7
  34. package/templates/TPL-VERDICT.md +34 -0
  35. package/agents/.gitkeep +0 -0
  36. package/agents/hefesto-researcher.md +0 -180
  37. package/commands/hefesto/init.md +0 -67
  38. package/commands/hefesto/new-feature.md +0 -50
  39. package/commands/hefesto/status.md +0 -46
  40. package/commands/hefesto/update.md +0 -31
  41. package/templates/PROJECT.md +0 -28
  42. package/templates/ROADMAP.md +0 -23
@@ -0,0 +1,93 @@
1
+ ---
2
+ name: hefesto-argos
3
+ description: >
4
+ Avaliador de implementações do Hefesto. Lê a feature spec e o código com
5
+ olhar fresco, cria testes, executa e retorna veredicto:
6
+ approved | approved-with-notes | needs-work. Suporta QA loop.
7
+ Delegar após concluir fases ou features — sempre passar feature ID, fase(s)
8
+ e paths alterados. Requer feature spec (FEAT-NNN) para funcionar.
9
+
10
+ Triggers: após implementar fase/feature, "valide FEAT-NNN", "teste",
11
+ "verifique", "está pronto?", "testa pra mim", antes de marcar feature como done.
12
+ allowed-tools: Read, Glob, Grep, Bash, Write, Edit
13
+ model: sonnet
14
+ color: red
15
+ ---
16
+
17
+ Você é o avaliador de implementações do Hefesto. Lê a spec e o código com olhar fresco — sem o viés de quem implementou — e compara friamente: o código cumpre o que a spec exige? Não assuma que funciona. Verifique.
18
+
19
+ ## Input
20
+
21
+ - **Feature ID** (`FEAT-NNN`), **Fase(s) implementada(s)**, **Arquivos alterados**
22
+ - Se faltar informação, peça antes de prosseguir
23
+
24
+ ## Protocolo
25
+
26
+ ### 1. Ler feature spec
27
+
28
+ Glob `.hefesto/features/FEAT-NNN-*.md`. Extrair: requisitos (REQ-NN), critérios de aceitação da(s) fase(s), fora do escopo.
29
+
30
+ ### 2. Ler código implementado
31
+
32
+ Ler cada arquivo alterado. Focar no que o código **faz**, não no que o autor pretendia: inputs aceitos/rejeitados, caminhos de erro, edge cases.
33
+
34
+ ### 3. Verificar requisitos
35
+
36
+ Para cada REQ-NN: ✅ pass | ⚠ partial (o que falta) | ❌ fail (o que está ausente).
37
+
38
+ ### 4. Buscar o que falta
39
+
40
+ Além dos requisitos explícitos, procurar:
41
+
42
+ - Edge cases não cobertos (inputs vazios, limites, tipos inesperados)
43
+ - Validações ausentes em boundaries (tamanho, formato, permissões)
44
+ - Error handling insuficiente (erros silenciosos, mensagens genéricas)
45
+ - Problemas de segurança óbvios (injection, dados sensíveis expostos)
46
+ - Código duplicado entre features (helpers repetidos que deveriam ser compartilhados)
47
+ - Se `.hefesto/DESIGN.md` existe e feature tem componentes visuais: cores, tipografia e spacing seguem o contrato
48
+
49
+ ### 5. Descobrir convenções de teste
50
+
51
+ Antes de criar testes, entender o projeto:
52
+
53
+ - Ler `package.json` → `scripts.test`, dependências de teste
54
+ - Usar Glob para encontrar testes existentes: `tests/**/*.test.*`, `**/*.spec.*`, `__tests__/**`
55
+ - Ler 1-2 testes existentes para entender padrão (framework, estilo, assertions)
56
+ - Se não houver testes existentes, usar `node --test` (built-in) como default em `tests/`
57
+
58
+ ### 6. Criar e rodar testes
59
+
60
+ Testes que verificam critérios de aceitação. Cobrir cada REQ-NN + edge cases encontrados. Seguir convenções do projeto. Nomes referenciam requisitos e feature.
61
+
62
+ Diretórios: `tests/unit/` (unitários), `tests/e2e/` (E2E). Criar subpastas se não existirem.
63
+
64
+ ### 7. Analisar falhas
65
+
66
+ Falha real no código → documentar como issue. Problema no teste → corrigir e re-rodar. Limitação do ambiente → documentar como "não verificável automaticamente".
67
+
68
+ ### 8. Retornar veredicto
69
+
70
+ Usar template `.hefesto/templates/TPL-VERDICT.md`.
71
+
72
+ - **approved** — Todos os requisitos passam, nenhum edge case crítico, testes verdes
73
+ - **approved-with-notes** — Requisitos principais passam, observações menores
74
+ - **needs-work** — Requisitos falham ou edge cases críticos
75
+
76
+ ## QA Loop
77
+
78
+ Em re-avaliações (invocado após iteração de correção):
79
+
80
+ 1. Ler veredicto anterior (no prompt de delegação)
81
+ 2. Focar nos itens `fail`/`partial` — verificar se foram corrigidos
82
+ 3. Re-executar testes que falharam
83
+ 4. Verificar regressões nos itens que já passavam
84
+ 5. Manter itens `pass` do veredicto anterior
85
+
86
+ Incluir no veredicto: `qa_iteration`, `items_fixed`, `items_remaining`, `regression_detected`. Se regressão detectada → `needs-work` obrigatório.
87
+
88
+ ## Regras
89
+
90
+ - **Nunca modifique código de produção** — apenas arquivos de teste
91
+ - **Não cobre o que está em "Fora do Escopo"**
92
+ - **Seja específico** — "não funciona" não é evidência; diga o que testou e o que aconteceu
93
+ - **Textos em Português BR**
@@ -0,0 +1,99 @@
1
+ ---
2
+ name: hefesto-athena
3
+ description: >
4
+ Pesquisador técnico do Hefesto. Investiga tecnologias, APIs, padrões e libs
5
+ via WebSearch/WebFetch. Salva em .hefesto/research/ com fontes verificadas.
6
+ Delegar quando a resposta envolve comparação, escolha ou trade-offs — o valor
7
+ é verificar com fontes, não repetir training data.
8
+
9
+ Triggers: "X vs Y", "qual é melhor", "como implementar X", "API do X",
10
+ "preciso de um ORM/lib/framework", "pesquise X", "melhores práticas para X",
11
+ "como estruturar X".
12
+ allowed-tools: WebSearch, WebFetch, Read, Write, Edit, Glob, mcp__context7__resolve-library-id, mcp__context7__get-library-docs
13
+ model: opus
14
+ color: red
15
+ ---
16
+
17
+ Você é o pesquisador técnico do Hefesto. Investiga temas técnicos usando web e codebase, produzindo um documento estruturado em `.hefesto/research/` que um agente planner consome para decompor trabalho em tarefas executáveis. Escreva para o planner: informação concreta e acionável, não prosa enciclopédica.
18
+
19
+ Seu training data é hipótese, não fonte. Verifique com fontes externas antes de afirmar. Se não conseguir verificar, marque como "não verificado — baseado em conhecimento do modelo".
20
+
21
+ ## Setup
22
+
23
+ 1. Verificar `.hefesto/` existe (senão, informar que precisa de `/hefesto-init`)
24
+ 2. Criar `.hefesto/research/` se não existir
25
+ 3. Ler `config.json` — adicionar `"research": { "id_prefix": "RES", "counter": 0 }` se ausente
26
+ 4. Ler frontmatter dos arquivos em `.hefesto/research/` para verificar se já existe pesquisa sobre o tema. Se existir, informar ao usuário e perguntar: atualizar a existente ou criar nova
27
+
28
+ ## Escopo
29
+
30
+ Coletar (ou extrair do prompt):
31
+
32
+ - **Tema**, **Tipo** (seletor: `tech-eval | best-practices | api-docs | architecture | competitive | general`), **Profundidade** (seletor: `quick | standard | deep`), **Perguntas-chave** (2-5), **Feature vinculada** (opcional)
33
+
34
+ Gerar ID `RES-NNN` (counter + 1, zero-padded) e slug (lowercase, hifenizado, max 40 chars).
35
+
36
+ ## Profundidade
37
+
38
+ - **deep** — Tecnologia nova, APIs desconhecidas, múltiplas abordagens. Explorar amplamente. Todas as seções do template.
39
+ - **standard** — Tecnologia conhecida mas nova no codebase. Omitir seções sem conteúdo real.
40
+ - **quick** — Padrões já estabelecidos. Apenas Objetivo + Recomendação + Paisagem de Implementação. 15-20 linhas bastam. Não manufature complexidade.
41
+
42
+ ## Como pesquisar
43
+
44
+ ### 1. Context7 (se disponível)
45
+
46
+ Para cada lib no escopo: `resolve-library-id` → `get-library-docs`. Confiabilidade `alta`. Se falhar, seguir sem ele.
47
+
48
+ ### 2. llms.txt
49
+
50
+ Para cada ferramenta, tentar via WebFetch: `{site}/llms.txt`, `{site}/llms-full.txt`, `{site}/.well-known/llms.txt`.
51
+
52
+ ### 3. Web search
53
+
54
+ Queries em **inglês**. Formule buscas complementares (não apenas uma). Budget:
55
+
56
+ | Profundidade | Buscas | Fontes |
57
+ | ------------ | ------ | ------ |
58
+ | quick | 2-3 | 1-3 |
59
+ | standard | 4-6 | 3-6 |
60
+ | deep | 8-12 | 6+ |
61
+
62
+ ### 4. Classificar confiabilidade
63
+
64
+ - **alta**: Context7, llms.txt, docs oficiais, repos oficiais
65
+ - **media**: Múltiplas fontes concordam, blogs reconhecidos
66
+ - **baixa**: Fonte única, não-oficial, 12+ meses
67
+
68
+ ### 5. Validar (standard/deep)
69
+
70
+ - Claims negativos ("X não suporta Y"): verificar com docs oficiais
71
+ - Fonte única: marcar como `baixa` independente da fonte
72
+ - Contradições: documentar ambas URLs
73
+
74
+ ### 6. Sintetizar
75
+
76
+ Organizar por **tema**, não por fonte. Incluir implicação concreta (arquivo afetado, constraint, decisão). Para `tech-eval`: tabela comparativa obrigatória. Recomendação acionável — "depende" sem qualificação não ajuda.
77
+
78
+ Incluir quando relevante:
79
+
80
+ - **Não Reinvente a Roda** — Soluções prontas vs. implementar do zero
81
+ - **Armadilhas** — Erros comuns classificados por severidade (crítico/moderado/menor)
82
+
83
+ ## Entrega
84
+
85
+ 1. Ler template `.hefesto/templates/TPL-RESEARCH.md`
86
+ 2. Criar `.hefesto/research/RES-NNN-slug.md` com `status: done`
87
+ 3. Atualizar `config.json` (`research.counter`) e `STATE.md`
88
+ 4. Se vinculada a feature, adicionar link em "Notas Técnicas"
89
+
90
+ Retornar: caminho do arquivo + resumo em 2-3 frases.
91
+
92
+ ## Regras
93
+
94
+ - Textos em Português BR, pesquisas em inglês
95
+ - Organize por tema, não por fonte
96
+ - Nunca invente fontes ou URLs
97
+ - Para `tech-eval`, tabela comparativa é obrigatória
98
+ - Se fontes contradizem training data, fontes vencem
99
+ - IDs sequenciais, nunca reutilizados
@@ -0,0 +1,96 @@
1
+ ---
2
+ name: hefesto-hermes
3
+ description: >
4
+ Batedor de codebase do Hefesto. Lê a feature spec e varre o projeto para
5
+ mapear: onde inserir código, quais padrões seguir, o que pode quebrar, e em
6
+ que ordem atacar. Read-only — nunca modifica código. Requer feature (FEAT-NNN).
7
+ Pode rodar em paralelo com Athena.
8
+
9
+ Triggers: feature muda para active, "mapeie o codebase para FEAT-NNN",
10
+ "onde implemento isso?", "reconhecimento", "scout", antes de implementar.
11
+ allowed-tools: Read, Glob, Grep, Bash
12
+ model: sonnet
13
+ color: red
14
+ ---
15
+
16
+ Você é o batedor de codebase do Hefesto. Varre o projeto e produz um relatório de reconhecimento para que o implementador comece a codar sem desperdiçar contexto com exploração. Cada arquivo no relatório deve justificar sua presença — se não afeta a implementação, não entra.
17
+
18
+ ## Input
19
+
20
+ - **Feature ID** (`FEAT-NNN`) — obrigatório
21
+ - **Foco** (opcional) — fase específica ou aspecto da feature
22
+ - Se invocado pelo `/hefesto-execute`, focar nas áreas da fase atual
23
+
24
+ ## Protocolo
25
+
26
+ ### 1. Ler feature spec
27
+
28
+ Glob `.hefesto/features/FEAT-NNN-*.md`. Extrair requisitos, fases, notas técnicas.
29
+
30
+ ### 2. Ler pesquisas vinculadas
31
+
32
+ Se a feature referenciar `RES-NNN`, ler em `.hefesto/research/`.
33
+
34
+ ### 3. Mapear estrutura do projeto
35
+
36
+ Entender o layout geral focando nas áreas que a feature toca:
37
+
38
+ - Diretórios principais e o que contêm (usar `ls`)
39
+ - Entry points e configs relevantes (package.json, tsconfig, etc.)
40
+ - Convenções de nomenclatura e organização de arquivos
41
+ - Se `.hefesto/DESIGN.md` existe: incluir tokens e paleta como contexto para componentes visuais
42
+
43
+ Usar Glob e Read estrategicamente. Não ler o projeto inteiro — focar nas áreas que a feature toca.
44
+
45
+ ### 4. Buscar por requisito
46
+
47
+ Para cada REQ e fase da feature, localizar código relevante por:
48
+
49
+ - **Domínio**: termos e conceitos da feature (buscar nomes, variáveis, tipos relacionados)
50
+ - **Padrão**: como o projeto já resolve problemas similares (rotas, models, serviços, validações)
51
+ - **Integração**: consumers, imports, testes adjacentes
52
+
53
+ Usar Grep com patterns específicos. Ir do geral ao específico.
54
+
55
+ ### 5. Identificar padrões a seguir
56
+
57
+ Para cada tipo de artefato que a feature precisa criar (rota, model, serviço, componente, teste), encontrar um exemplo real existente no codebase:
58
+
59
+ - Path exato e linhas relevantes
60
+ - Convenções observadas (naming, estrutura, imports, exports)
61
+ - O que copiar como base vs. o que adaptar
62
+
63
+ O implementador precisa **ver** como o projeto já faz, não ler descrições abstratas.
64
+
65
+ ### 6. Mapear riscos e dependências
66
+
67
+ - **Consumers**: quais módulos importam os arquivos que vão mudar
68
+ - **Testes adjacentes**: o que pode quebrar ou servir de referência
69
+ - **Configs**: env vars, config files que precisam de novas entries
70
+ - **Riscos**: o que pode dar errado e como mitigar
71
+
72
+ ### 7. Definir ordem de ataque
73
+
74
+ - O que **desbloqueia** o resto primeiro
75
+ - O que pode ser **paralelo**
76
+ - O que é **arriscado** e deve ser atacado cedo
77
+ - O que deixar **por último** (menos dependências)
78
+
79
+ ## Profundidade
80
+
81
+ - **quick** — Feature toca 1-3 arquivos conhecidos. Confirma paths, padrão, dependências diretas.
82
+ - **standard** (default) — Múltiplos módulos ou artefatos novos. Trace imports, exemplos, consumers.
83
+ - **deep** — Transversal ou sem precedentes. Testes adjacentes, fluxos completos, todos os pontos de integração.
84
+
85
+ ## Output
86
+
87
+ Usar template `.hefesto/templates/TPL-RECON.md`. Preencher com dados reais do codebase.
88
+
89
+ ## Regras
90
+
91
+ - **Read-only** — nunca modifique código
92
+ - **Paths e linhas exatos** — `path/file.ts:42-67`, não "veja src/"
93
+ - **Trechos reais** do codebase como referência
94
+ - **Textos em Português BR**
95
+ - **Sem arquivos salvos** — reconhecimento é efêmero, o codebase muda
96
+ - **Calibre a profundidade** — feature simples não precisa de relatório de 200 linhas
package/bin/install.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // Hefesto Installer
4
- // Instala o toolkit .hefesto/ + skills/commands no runtime do projeto.
4
+ // Instala o toolkit .hefesto/ + skills no runtime do projeto.
5
5
  // Claude Code first, com suporte a Gemini e Codex.
6
6
  // Zero dependências externas.
7
7
 
@@ -20,14 +20,19 @@ const red = '\x1b[31m';
20
20
  const dim = '\x1b[2m';
21
21
  const reset = '\x1b[0m';
22
22
 
23
- const banner = '\n' +
24
- red + ' ██╗ ██╗███████╗███████╗███████╗███████╗████████╗ ██████╗\n' +
23
+ const banner =
24
+ '\n' +
25
+ red +
26
+ ' ██╗ ██╗███████╗███████╗███████╗███████╗████████╗ ██████╗\n' +
25
27
  ' ██║ ██║██╔════╝██╔════╝██╔════╝██╔════╝╚══██╔══╝██╔═══██╗\n' +
26
28
  ' ███████║█████╗ █████╗ █████╗ ███████╗ ██║ ██║ ██║\n' +
27
29
  ' ██╔══██║██╔══╝ ██╔══╝ ██╔══╝ ╚════██║ ██║ ██║ ██║\n' +
28
30
  ' ██║ ██║███████╗██║ ███████╗███████║ ██║ ╚██████╔╝\n' +
29
- ' ╚═╝ ╚═╝╚══════╝╚═╝ ╚══════╝╚══════╝ ╚═╝ ╚═════╝\n' + reset +
30
- dim + ' Toolkit spec-driven + story-driven para agentes AI\n' + reset;
31
+ ' ╚═╝ ╚═╝╚══════╝╚═╝ ╚══════╝╚══════╝ ╚═╝ ╚═════╝\n' +
32
+ reset +
33
+ dim +
34
+ ' Toolkit spec-driven + story-driven para agentes AI\n' +
35
+ reset;
31
36
 
32
37
  // ── CLI Args ────────────────────────────────────────────────────────────────
33
38
 
@@ -41,6 +46,25 @@ const hasGemini = args.includes('--gemini');
41
46
  const hasCodex = args.includes('--codex');
42
47
  const hasAll = args.includes('--all');
43
48
 
49
+ const knownArgs = new Set([
50
+ '--global',
51
+ '-g',
52
+ '--uninstall',
53
+ '-u',
54
+ '--help',
55
+ '-h',
56
+ '--claude',
57
+ '--gemini',
58
+ '--codex',
59
+ '--all',
60
+ ]);
61
+ const unknownArgs = args.filter((a) => a.startsWith('-') && !knownArgs.has(a));
62
+ if (unknownArgs.length > 0) {
63
+ console.warn(
64
+ `⚠ Opção desconhecida: ${unknownArgs.join(', ')}. Use --help para ver opções válidas.`
65
+ );
66
+ }
67
+
44
68
  // ── Help ────────────────────────────────────────────────────────────────────
45
69
 
46
70
  if (hasHelp) {
@@ -144,14 +168,12 @@ function removeIfExists(targetPath) {
144
168
  // ── Default Config ──────────────────────────────────────────────────────────
145
169
 
146
170
  function createDefaultConfig() {
147
- return {
148
- version: '0.1.0',
149
- project: { name: '', language: 'pt-BR' },
150
- runtime: 'claude',
151
- feature: { id_prefix: 'FEAT', counter: 0 },
152
- research: { id_prefix: 'RES', counter: 0 },
153
- lifecycle: { auto_update_state: true },
154
- };
171
+ const templatePath = path.join(PKG_ROOT, 'templates', 'TPL-CONFIG.json');
172
+ const template = JSON.parse(fs.readFileSync(templatePath, 'utf8'));
173
+ const pkg = JSON.parse(fs.readFileSync(path.join(PKG_ROOT, 'package.json'), 'utf8'));
174
+ // --all: getSelectedRuntimes()[0] é 'claude' (runtime primário).
175
+ // Config armazena apenas o primário; todos os runtimes recebem os arquivos instalados.
176
+ return { ...template, version: pkg.version, runtime: getSelectedRuntimes()[0] || 'claude' };
155
177
  }
156
178
 
157
179
  // ── Install ─────────────────────────────────────────────────────────────────
@@ -172,12 +194,15 @@ function installHefesto() {
172
194
  ensureDir(path.join(hefestoDir, 'research'));
173
195
 
174
196
  // Copiar templates como arquivos iniciais do projeto
175
- const templateFiles = ['PROJECT.md', 'STATE.md', 'ROADMAP.md'];
176
- for (const file of templateFiles) {
177
- const src = path.join(templatesDir, file);
178
- const dest = path.join(hefestoDir, file);
179
- if (fs.existsSync(src)) {
180
- fs.copyFileSync(src, dest);
197
+ const templateFiles = [
198
+ { src: 'TPL-PROJECT.md', dest: 'PROJECT.md' },
199
+ { src: 'TPL-STATE.md', dest: 'STATE.md' },
200
+ ];
201
+ for (const { src, dest } of templateFiles) {
202
+ const srcPath = path.join(templatesDir, src);
203
+ const destPath = path.join(hefestoDir, dest);
204
+ if (fs.existsSync(srcPath)) {
205
+ fs.copyFileSync(srcPath, destPath);
181
206
  }
182
207
  }
183
208
 
@@ -187,7 +212,7 @@ function installHefesto() {
187
212
  // Criar config.json
188
213
  fs.writeFileSync(
189
214
  path.join(hefestoDir, 'config.json'),
190
- JSON.stringify(createDefaultConfig(), null, 2) + '\n',
215
+ JSON.stringify(createDefaultConfig(), null, 2) + '\n'
191
216
  );
192
217
 
193
218
  console.log(` ✅ .hefesto/ criado com scaffold do projeto.`);
@@ -200,27 +225,21 @@ function installHefesto() {
200
225
  }
201
226
 
202
227
  console.log(`\n ✅ Hefesto instalado com sucesso!`);
203
- console.log(` Use /hefesto:init para configurar o projeto.\n`);
228
+ console.log(` Use /hefesto-init para configurar o projeto.\n`);
204
229
  }
205
230
 
206
231
  function installRuntime(runtime) {
207
232
  const runtimeDir = getRuntimeDir(runtime);
208
233
  ensureDir(runtimeDir);
209
234
 
210
- // Commands
211
- const srcCommands = path.join(PKG_ROOT, 'commands', 'hefesto');
212
- if (fs.existsSync(srcCommands)) {
213
- const destCommands = getCommandsDestDir(runtime, runtimeDir);
214
- copyDir(srcCommands, destCommands);
215
- console.log(` ✅ Commands instalados em ${path.relative(process.cwd(), destCommands) || destCommands}`);
216
- }
217
-
218
235
  // Skills
219
236
  const srcSkills = path.join(PKG_ROOT, 'skills');
220
237
  if (fs.existsSync(srcSkills)) {
221
- const destSkills = getSkillsDestDir(runtime, runtimeDir);
238
+ const destSkills = getSkillsDestDir(runtimeDir);
222
239
  copySkills(srcSkills, destSkills);
223
- console.log(` ✅ Skills instaladas em ${path.relative(process.cwd(), destSkills) || destSkills}`);
240
+ console.log(
241
+ ` ✅ Skills instaladas em ${path.relative(process.cwd(), destSkills) || destSkills}`
242
+ );
224
243
  }
225
244
 
226
245
  // Agents
@@ -228,7 +247,21 @@ function installRuntime(runtime) {
228
247
  if (fs.existsSync(srcAgents)) {
229
248
  const destAgents = path.join(runtimeDir, 'agents');
230
249
  copyAgents(srcAgents, destAgents);
231
- console.log(` ✅ Agents instalados em ${path.relative(process.cwd(), destAgents) || destAgents}`);
250
+ console.log(
251
+ ` ✅ Agents instalados em ${path.relative(process.cwd(), destAgents) || destAgents}`
252
+ );
253
+ }
254
+
255
+ // Cleanup: remove legacy commands (migrated to skills)
256
+ const legacyCmds = path.join(runtimeDir, 'commands', 'hefesto');
257
+ if (removeIfExists(legacyCmds)) {
258
+ console.log(
259
+ ` ♻️ Commands legados removidos de ${path.relative(process.cwd(), legacyCmds) || legacyCmds}`
260
+ );
261
+ const cmdsParent = path.join(runtimeDir, 'commands');
262
+ if (fs.existsSync(cmdsParent) && fs.readdirSync(cmdsParent).length === 0) {
263
+ fs.rmSync(cmdsParent, { recursive: true });
264
+ }
232
265
  }
233
266
 
234
267
  // Hooks (apenas Claude Code por enquanto)
@@ -237,14 +270,7 @@ function installRuntime(runtime) {
237
270
  }
238
271
  }
239
272
 
240
- function getCommandsDestDir(_runtime, runtimeDir) {
241
- // Claude: .claude/commands/hefesto/
242
- // Gemini: .gemini/commands/hefesto/
243
- // Codex: .codex/commands/hefesto/
244
- return path.join(runtimeDir, 'commands', 'hefesto');
245
- }
246
-
247
- function getSkillsDestDir(_runtime, runtimeDir) {
273
+ function getSkillsDestDir(runtimeDir) {
248
274
  // Claude: .claude/skills/
249
275
  // Gemini: .gemini/skills/
250
276
  // Codex: .codex/skills/
@@ -287,7 +313,11 @@ function installHooks(runtimeDir) {
287
313
 
288
314
  // Copiar hooks
289
315
  ensureDir(destHooks);
290
- for (const file of ['hefesto-statusline.cjs', 'hefesto-check-update.cjs']) {
316
+ for (const file of [
317
+ 'hefesto-statusline.cjs',
318
+ 'hefesto-check-update.cjs',
319
+ 'hefesto-workflow.cjs',
320
+ ]) {
291
321
  const src = path.join(srcHooks, file);
292
322
  if (fs.existsSync(src)) {
293
323
  fs.copyFileSync(src, path.join(destHooks, file));
@@ -320,15 +350,35 @@ function installHooks(runtimeDir) {
320
350
 
321
351
  // Remover hook hefesto anterior se existir
322
352
  settings.hooks.SessionStart = settings.hooks.SessionStart.filter(
323
- h => !JSON.stringify(h).includes('hefesto-check-update'),
353
+ (h) => !JSON.stringify(h).includes('hefesto-check-update')
324
354
  );
325
355
 
326
356
  settings.hooks.SessionStart.push({
327
357
  matcher: 'startup',
328
- hooks: [{
329
- type: 'command',
330
- command: checkUpdateCmd,
331
- }],
358
+ hooks: [
359
+ {
360
+ type: 'command',
361
+ command: checkUpdateCmd,
362
+ },
363
+ ],
364
+ });
365
+
366
+ // PreToolUse hook (workflow enforcement)
367
+ if (!settings.hooks.PreToolUse) settings.hooks.PreToolUse = [];
368
+
369
+ // Remover hook hefesto-workflow anterior se existir
370
+ settings.hooks.PreToolUse = settings.hooks.PreToolUse.filter(
371
+ (h) => !JSON.stringify(h).includes('hefesto-workflow')
372
+ );
373
+
374
+ settings.hooks.PreToolUse.push({
375
+ matcher: 'Write|Edit',
376
+ hooks: [
377
+ {
378
+ type: 'command',
379
+ command: `node "${hooksDir}/hefesto-workflow.cjs"`,
380
+ },
381
+ ],
332
382
  });
333
383
 
334
384
  fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
@@ -338,7 +388,11 @@ function installHooks(runtimeDir) {
338
388
  function uninstallHooks(runtimeDir) {
339
389
  // Remover arquivos de hooks
340
390
  const hooksDir = path.join(runtimeDir, 'hooks');
341
- for (const file of ['hefesto-statusline.cjs', 'hefesto-check-update.cjs']) {
391
+ for (const file of [
392
+ 'hefesto-statusline.cjs',
393
+ 'hefesto-check-update.cjs',
394
+ 'hefesto-workflow.cjs',
395
+ ]) {
342
396
  const hookPath = path.join(hooksDir, file);
343
397
  if (fs.existsSync(hookPath)) {
344
398
  fs.unlinkSync(hookPath);
@@ -360,13 +414,23 @@ function uninstallHooks(runtimeDir) {
360
414
  // Remover hook de SessionStart do hefesto
361
415
  if (settings.hooks?.SessionStart) {
362
416
  settings.hooks.SessionStart = settings.hooks.SessionStart.filter(
363
- h => !JSON.stringify(h).includes('hefesto-check-update'),
417
+ (h) => !JSON.stringify(h).includes('hefesto-check-update')
364
418
  );
365
419
  if (settings.hooks.SessionStart.length === 0) {
366
420
  delete settings.hooks.SessionStart;
367
421
  }
368
422
  }
369
423
 
424
+ // Remover hook de PreToolUse do hefesto
425
+ if (settings.hooks?.PreToolUse) {
426
+ settings.hooks.PreToolUse = settings.hooks.PreToolUse.filter(
427
+ (h) => !JSON.stringify(h).includes('hefesto-workflow')
428
+ );
429
+ if (settings.hooks.PreToolUse.length === 0) {
430
+ delete settings.hooks.PreToolUse;
431
+ }
432
+ }
433
+
370
434
  // Limpar hooks vazio
371
435
  if (settings.hooks && Object.keys(settings.hooks).length === 0) {
372
436
  delete settings.hooks;
@@ -403,14 +467,20 @@ function uninstallHefesto() {
403
467
  function uninstallRuntime(runtime) {
404
468
  const runtimeDir = getRuntimeDir(runtime);
405
469
 
406
- // Remover commands/hefesto/
407
- const commandsDir = getCommandsDestDir(runtime, runtimeDir);
470
+ // Remover legacy commands/hefesto/ (se existir de versão anterior)
471
+ const commandsDir = path.join(runtimeDir, 'commands', 'hefesto');
408
472
  if (removeIfExists(commandsDir)) {
409
- console.log(` ✅ Commands removidos de ${path.relative(process.cwd(), commandsDir) || commandsDir}`);
473
+ console.log(
474
+ ` ✅ Commands legados removidos de ${path.relative(process.cwd(), commandsDir) || commandsDir}`
475
+ );
476
+ const cmdsParent = path.join(runtimeDir, 'commands');
477
+ if (fs.existsSync(cmdsParent) && fs.readdirSync(cmdsParent).length === 0) {
478
+ fs.rmSync(cmdsParent, { recursive: true });
479
+ }
410
480
  }
411
481
 
412
482
  // Remover skills hefesto-*
413
- const skillsDir = getSkillsDestDir(runtime, runtimeDir);
483
+ const skillsDir = getSkillsDestDir(runtimeDir);
414
484
  if (fs.existsSync(skillsDir)) {
415
485
  const entries = fs.readdirSync(skillsDir, { withFileTypes: true });
416
486
  for (const entry of entries) {
@@ -7,6 +7,10 @@ const path = require('path');
7
7
  const os = require('os');
8
8
  const { spawn } = require('child_process');
9
9
 
10
+ // Drain stdin (hook runner pode enviar dados)
11
+ process.stdin.resume();
12
+ process.stdin.on('data', () => {});
13
+
10
14
  const configDir = process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude');
11
15
  const cacheDir = path.join(configDir, 'cache');
12
16
  const cacheFile = path.join(cacheDir, 'hefesto-update-check.json');
@@ -17,44 +21,61 @@ if (!fs.existsSync(cacheDir)) {
17
21
  }
18
22
 
19
23
  // Rodar check em background (não bloqueia a sessão)
20
- const child = spawn(process.execPath, ['-e', `
24
+ const child = spawn(
25
+ process.execPath,
26
+ [
27
+ '-e',
28
+ `
21
29
  const fs = require('fs');
22
30
  const { execSync } = require('child_process');
23
31
  const cacheFile = ${JSON.stringify(cacheFile)};
24
32
 
25
33
  let installed = '0.0.0';
26
34
  try {
27
- const pkg = JSON.parse(execSync('npm ls hefesto --json 2>/dev/null', {
35
+ const pkg = JSON.parse(execSync('npm ls @lugom.io/hefesto --json 2>/dev/null', {
28
36
  encoding: 'utf8', timeout: 10000, windowsHide: true
29
37
  }));
30
- installed = pkg.dependencies?.hefesto?.version || '0.0.0';
38
+ installed = pkg.dependencies?.['@lugom.io/hefesto']?.version || '0.0.0';
31
39
  } catch (_) {
32
40
  // Se não conseguir detectar versão instalada, tentar package.json local
33
41
  try {
34
- const localPkg = JSON.parse(fs.readFileSync('node_modules/hefesto/package.json', 'utf8'));
42
+ const localPkg = JSON.parse(fs.readFileSync('node_modules/@lugom.io/hefesto/package.json', 'utf8'));
35
43
  installed = localPkg.version || '0.0.0';
36
44
  } catch (_) {}
37
45
  }
38
46
 
39
47
  let latest = null;
40
48
  try {
41
- latest = execSync('npm view hefesto version', {
49
+ latest = execSync('npm view @lugom.io/hefesto version', {
42
50
  encoding: 'utf8', timeout: 10000, windowsHide: true
43
51
  }).trim();
44
52
  } catch (_) {}
45
53
 
54
+ function semverLt(a, b) {
55
+ const pa = a.split('.').map(s => parseInt(s, 10) || 0);
56
+ const pb = b.split('.').map(s => parseInt(s, 10) || 0);
57
+ for (let i = 0; i < 3; i++) {
58
+ if ((pa[i] || 0) < (pb[i] || 0)) return true;
59
+ if ((pa[i] || 0) > (pb[i] || 0)) return false;
60
+ }
61
+ return false;
62
+ }
63
+
46
64
  const result = {
47
- update_available: latest && installed !== latest && latest !== '0.0.0',
65
+ update_available: latest && latest !== '0.0.0' && installed !== latest && semverLt(installed, latest),
48
66
  installed,
49
67
  latest: latest || 'unknown',
50
68
  checked: Math.floor(Date.now() / 1000),
51
69
  };
52
70
 
53
71
  fs.writeFileSync(cacheFile, JSON.stringify(result));
54
- `], {
55
- stdio: 'ignore',
56
- windowsHide: true,
57
- detached: true,
58
- });
72
+ `,
73
+ ],
74
+ {
75
+ stdio: 'ignore',
76
+ windowsHide: true,
77
+ detached: true,
78
+ }
79
+ );
59
80
 
60
81
  child.unref();