@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.
- package/agents/hefesto-argos.md +93 -0
- package/agents/hefesto-athena.md +99 -0
- package/agents/hefesto-hermes.md +96 -0
- package/bin/install.js +122 -52
- package/hooks/hefesto-check-update.cjs +32 -11
- package/hooks/hefesto-statusline.cjs +8 -17
- package/hooks/hefesto-workflow.cjs +68 -0
- package/package.json +14 -4
- package/skills/hefesto-context/SKILL.md +67 -14
- package/skills/hefesto-debug/SKILL.md +54 -0
- package/skills/hefesto-design/SKILL.md +184 -0
- package/skills/hefesto-execute/SKILL.md +133 -0
- package/skills/hefesto-init/SKILL.md +105 -0
- package/skills/hefesto-init/references/api.md +116 -0
- package/skills/hefesto-init/references/cli.md +91 -0
- package/skills/hefesto-init/references/mobile.md +69 -0
- package/skills/hefesto-init/references/web.md +246 -0
- package/skills/hefesto-new-feature/SKILL.md +87 -0
- package/skills/hefesto-security/SKILL.md +89 -0
- package/skills/hefesto-security/references/boundaries-and-bypasses.md +152 -0
- package/skills/hefesto-security/references/secrets-detection.md +121 -0
- package/skills/hefesto-security/references/severity-and-judgment.md +176 -0
- package/skills/hefesto-simplify/SKILL.md +82 -0
- package/templates/TPL-CLAUDE.md +54 -0
- package/templates/TPL-CONFIG.json +19 -0
- package/templates/TPL-DESIGN.md +305 -0
- package/templates/{FEATURE.md → TPL-FEATURE.md} +13 -6
- package/templates/TPL-PROJECT.md +50 -0
- package/templates/TPL-RECON.md +60 -0
- package/templates/{RESEARCH.md → TPL-RESEARCH.md} +32 -35
- package/templates/TPL-SECURITY.md +42 -0
- package/templates/TPL-SIMPLIFY.md +40 -0
- package/templates/{STATE.md → TPL-STATE.md} +1 -7
- package/templates/TPL-VERDICT.md +34 -0
- package/agents/.gitkeep +0 -0
- package/agents/hefesto-researcher.md +0 -180
- package/commands/hefesto/init.md +0 -67
- package/commands/hefesto/new-feature.md +0 -50
- package/commands/hefesto/status.md +0 -46
- package/commands/hefesto/update.md +0 -31
- package/templates/PROJECT.md +0 -28
- 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
|
|
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 =
|
|
24
|
-
|
|
23
|
+
const banner =
|
|
24
|
+
'\n' +
|
|
25
|
+
red +
|
|
26
|
+
' ██╗ ██╗███████╗███████╗███████╗███████╗████████╗ ██████╗\n' +
|
|
25
27
|
' ██║ ██║██╔════╝██╔════╝██╔════╝██╔════╝╚══██╔══╝██╔═══██╗\n' +
|
|
26
28
|
' ███████║█████╗ █████╗ █████╗ ███████╗ ██║ ██║ ██║\n' +
|
|
27
29
|
' ██╔══██║██╔══╝ ██╔══╝ ██╔══╝ ╚════██║ ██║ ██║ ██║\n' +
|
|
28
30
|
' ██║ ██║███████╗██║ ███████╗███████║ ██║ ╚██████╔╝\n' +
|
|
29
|
-
' ╚═╝ ╚═╝╚══════╝╚═╝ ╚══════╝╚══════╝ ╚═╝ ╚═════╝\n' +
|
|
30
|
-
|
|
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
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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 = [
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
|
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(
|
|
238
|
+
const destSkills = getSkillsDestDir(runtimeDir);
|
|
222
239
|
copySkills(srcSkills, destSkills);
|
|
223
|
-
console.log(
|
|
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(
|
|
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
|
|
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 [
|
|
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
|
-
|
|
330
|
-
|
|
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 [
|
|
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 =
|
|
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(
|
|
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(
|
|
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(
|
|
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 &&
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
72
|
+
`,
|
|
73
|
+
],
|
|
74
|
+
{
|
|
75
|
+
stdio: 'ignore',
|
|
76
|
+
windowsHide: true,
|
|
77
|
+
detached: true,
|
|
78
|
+
}
|
|
79
|
+
);
|
|
59
80
|
|
|
60
81
|
child.unref();
|