iron-edge 2.4.2 → 2.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -2
- package/agents/iron-auditor.md +31 -0
- package/agents/iron-shopify.md +31 -47
- package/agents/iron-spy.md +22 -0
- package/bin/iron-edge.js +12 -1
- package/installer/cofre/CLAUDE.md +33 -0
- package/installer/cofre/INDICE.md +11 -0
- package/installer/cofre/Projetos/.gitkeep +0 -0
- package/installer/cofre/templates/nota.md +18 -0
- package/installer/cofre.js +94 -0
- package/installer/doctor.js +179 -0
- package/installer/install.js +19 -11
- package/installer/office.js +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -167,6 +167,13 @@ Instalada junto. Depois de `npx iron-edge install`, os dois arquivos abaixo est
|
|
|
167
167
|
```bash
|
|
168
168
|
# Conferir a instalação: o que é seu, o que está diferente sem registro, o que sumiu
|
|
169
169
|
npx iron-edge verify
|
|
170
|
+
|
|
171
|
+
# Medir a máquina (Node, git, Claude Code, Iron Edge, chave, plugin, cofre). Com a chave do
|
|
172
|
+
# Iron Office aceita, o resultado acende o checklist da página de Setup do site.
|
|
173
|
+
npx iron-edge doctor
|
|
174
|
+
|
|
175
|
+
# Instalar já criando um cofre vazio em ~/Iron Cofre (pronto pro Obsidian, nunca sobrescreve)
|
|
176
|
+
npx iron-edge install --cofre
|
|
170
177
|
```
|
|
171
178
|
|
|
172
179
|
**Atualizar é um comando. Remover é operação conduzida.**
|
|
@@ -257,13 +264,13 @@ O Iron Edge instala uma equipe de agentes especializados diretamente no Claude C
|
|
|
257
264
|
Seus dados ficam locais em `~/.iron/`. Sobre o que sai da sua máquina, com precisão:
|
|
258
265
|
|
|
259
266
|
- **Para o Iron Office** (`iron-office.vercel.app`, servidor deste projeto), e só se você criar a chave em `~/.claude/iron-office-api-key`:
|
|
260
|
-
-
|
|
267
|
+
- **0 agentes** leem a chave. Só o plugin `iron-office` fala com o site, e a loja Shopify é consultada pelas ferramentas `shop_status`/`shop_query` dele (desde a 2.4.4; o `iron-auditor` devolve o relatório pra quem chamou desde a 2.4.2). Confira: `grep -rln "iron-office-api-key" ~/.claude/agents/`;
|
|
261
268
|
- **se você rodou `npx iron-edge office`**, o plugin `iron-office` fica instalado e manda pro seu painel (feed e mapa 3D) a atividade **só da sessão aberta pelo `npx iron-edge office`** (desde o plugin 0.3.1; um `claude` aberto em outra pasta não manda nada), e as entregas que o Iron salvar ali pela ferramenta `deliver`. Da atividade vai: nome da ferramenta, caminho do arquivo (nunca o conteúdo), os primeiros 200 caracteres de cada comando de terminal e a descrição dele, buscas, o domínio dos endereços visitados, nome e descrição dos agentes, a pasta e o identificador da sessão. A resposta das ferramentas nunca sai. Padrões conhecidos de segredo nesses campos (chaves de Stripe, Shopify, Meta, GitHub, OpenAI, Supabase e AWS, `Bearer`, `ALGO_KEY=`, `--password`, `-u usuario:senha`…) são apagados antes do envio; é uma lista de padrões, não garantia. Lista completa no README do plugin (github.com/filipecoff21-code/iron-office-channel). Desligar: `claude plugin disable iron-office@iron-office-channel`. Conferir se está ligado: `claude plugin list`.
|
|
262
269
|
- **Para a internet aberta**, através das ferramentas do próprio Claude Code (`WebSearch` / `WebFetch`), vão as buscas de **41 dos 44 agentes** — é como o `iron-research` pesquisa mercado e o `iron-spy` lê a biblioteca de anúncios. Isso passa pelo Claude Code, com a visibilidade e a aprovação que ele te dá; não é canal deste pacote. Confira: `grep -rln "WebSearch\|WebFetch" ~/.claude/agents/`.
|
|
263
270
|
- **A skill `clone-site`** baixa a URL que você mandar clonar. É o trabalho dela.
|
|
264
271
|
|
|
265
272
|
|
|
266
|
-
Sendo exato sobre a força dessa garantia, porque ela não é um sandbox:
|
|
273
|
+
Sendo exato sobre a força dessa garantia, porque ela não é um sandbox: nenhum agente nem skill do pacote lê a chave (a regra R25 do lint barra quem voltar a ler), e se algum bloco novo chamar o Iron Office a regra R20 exige a guarda (`[ -z "$IRON_KEY" ] && exit 0`) antes. Mas esses arquivos são **instruções lidas por um modelo**, não um binário fechado: o que segura de verdade é o modelo seguir o texto. Se o que você quer é garantia dura, a que existe é não criar o arquivo de chave — sem ele, não há para onde enviar, e todo bloco para na guarda (o plugin `iron-office` também: sem a chave, ele não envia nada).
|
|
267
274
|
|
|
268
275
|
---
|
|
269
276
|
|
package/agents/iron-auditor.md
CHANGED
|
@@ -22,6 +22,8 @@ Quando ativado com uma URL:
|
|
|
22
22
|
|
|
23
23
|
### STEP 1 — Captura de screenshots
|
|
24
24
|
|
|
25
|
+
O pedido pode trazer `dono=sim` ou `dono=nao` (o site é do aluno ou de terceiro). Sem o campo, considere `dono=sim`. Guarde: ele decide a ação de cada item do cartão (STEP 4).
|
|
26
|
+
|
|
25
27
|
Execute este script Playwright via Bash:
|
|
26
28
|
|
|
27
29
|
```bash
|
|
@@ -109,6 +111,35 @@ Data: [hoje]
|
|
|
109
111
|
- lp-cinematico: [se LP precisar reconstrução]
|
|
110
112
|
```
|
|
111
113
|
|
|
114
|
+
O relatório SEMPRE termina com o cartão de ação: o título `## Cartão` seguido de um bloco ```json neste formato (é o que o Iron Office mostra no chat; o relatório longo fica guardado como "ver relatório completo"):
|
|
115
|
+
|
|
116
|
+
```json
|
|
117
|
+
{
|
|
118
|
+
"kind": "auditoria",
|
|
119
|
+
"owner": true,
|
|
120
|
+
"score": 6.5,
|
|
121
|
+
"summary": "a frase do diagnóstico, até 160 caracteres",
|
|
122
|
+
"items": [
|
|
123
|
+
{
|
|
124
|
+
"title": "o problema, até 80 caracteres",
|
|
125
|
+
"why": "por que importa, em dinheiro ou cliente perdido, até 160",
|
|
126
|
+
"effort": "tempo pra corrigir, até 40 (ex.: 15 min · 1 h · 1 tarde)",
|
|
127
|
+
"action": "Iron, corrige isso em <domínio>: <o problema, com o que fazer>"
|
|
128
|
+
}
|
|
129
|
+
]
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Regras do cartão:
|
|
134
|
+
- `score` = o Score Geral; `summary` = a frase do diagnóstico.
|
|
135
|
+
- `items` = os 3 issues mais altos, no máximo 3 (🔴 antes de 🟠).
|
|
136
|
+
- `why` sempre em dinheiro ou cliente perdido, nunca "melhora a experiência".
|
|
137
|
+
- `owner` = `true` com `dono=sim`, `false` com `dono=nao`.
|
|
138
|
+
- `action` é o pedido que o aluno manda com um clique, até 300 caracteres. Em site de terceiro ele não pode corrigir, então a ação é a mensagem:
|
|
139
|
+
- `dono=sim` → começa com "Iron, corrige isso em <domínio>:" seguido do problema e do que fazer.
|
|
140
|
+
- `dono=nao` → começa com "Iron, escreve a mensagem pro dono de <domínio> sobre:" seguido do problema e de por que importa.
|
|
141
|
+
- O Iron Office descarta item cuja `action` não começa com um desses dois prefixos.
|
|
142
|
+
|
|
112
143
|
### STEP 5 — Devolver
|
|
113
144
|
|
|
114
145
|
Entregue o relatório completo em markdown na sua resposta final. Quem salva é quem te chamou (no Iron Office, o Iron usa a ferramenta `deliver`).
|
package/agents/iron-shopify.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: iron-shopify
|
|
3
3
|
description: "Iron Edge — controle total de lojas Shopify. Otimiza produtos, SEO, GEO, imagens, banners, tema, páginas de venda, branding e identidade visual. Gerencia múltiplas lojas com memória persistente. Use para qualquer tarefa relacionada a uma loja Shopify. Puxa em: produto da loja, e-commerce, dropshipping."
|
|
4
4
|
model: sonnet
|
|
5
|
-
tools: [Read, Write, Edit, Bash, Glob, Grep, WebSearch, WebFetch, mcp__shopify-dev-mcp__learn_shopify_api, mcp__shopify-dev-mcp__search_docs_chunks, mcp__shopify-dev-mcp__validate_theme, mcp__shopify-dev-mcp__validate_graphql_codeblocks, mcp__shopify-dev-mcp__validate_component_codeblocks]
|
|
5
|
+
tools: [Read, Write, Edit, Bash, Glob, Grep, WebSearch, WebFetch, mcp__shopify-dev-mcp__learn_shopify_api, mcp__shopify-dev-mcp__search_docs_chunks, mcp__shopify-dev-mcp__validate_theme, mcp__shopify-dev-mcp__validate_graphql_codeblocks, mcp__shopify-dev-mcp__validate_component_codeblocks, mcp__plugin_iron-office_iron-office__shop_status, mcp__plugin_iron-office_iron-office__shop_query]
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# iron-shopify — Controle Total de Lojas Shopify
|
|
@@ -15,33 +15,22 @@ Você é um especialista em Shopify com acesso direto às lojas via Shopify CLI
|
|
|
15
15
|
|
|
16
16
|
### Passo 1 — Verificar conexão via Iron Office
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
Chame a ferramenta `shop_status` (plugin `iron-office`, sem argumentos). Você **nunca** lê a chave do Iron Office nem faz requisição HTTP pro site: quem fala com o Iron Office é só o plugin, que já tem a chave.
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
cat ~/.claude/iron-office-api-key 2>/dev/null
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
**Se existir a key** → use o Iron Office como fonte de credenciais (preferencial):
|
|
20
|
+
Se ela devolver `{"connected": true, "shopDomain": "..."}` → loja conectada via Iron Office, use a ferramenta `shop_query` para todas as operações GraphQL (ver seção **USANDO O PROXY IRON OFFICE**).
|
|
25
21
|
|
|
26
|
-
|
|
27
|
-
IRON_KEY=$(cat ~/.claude/iron-office-api-key 2>/dev/null | tr -d '[:space:]')
|
|
28
|
-
# GUARDA: sem chave, nenhuma requisicao sai. Sustenta a promessa do README.
|
|
29
|
-
[ -z "$IRON_KEY" ] && { echo "Sem iron-office-api-key — nada foi enviado."; exit 0; }
|
|
30
|
-
curl -s -H "x-api-key: $IRON_KEY" https://iron-office.vercel.app/api/shopify/graphql
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
Se retornar `{"connected": true, "shopDomain": "..."}` → loja conectada via Iron Office, use o proxy para todas as operações GraphQL (ver seção **USANDO O PROXY IRON OFFICE**).
|
|
34
|
-
|
|
35
|
-
Se retornar `{"connected": false}` → informe:
|
|
22
|
+
Se devolver "Loja não conectada" → informe:
|
|
36
23
|
```
|
|
37
24
|
🛍️ iron-shopify ativo
|
|
38
25
|
|
|
39
26
|
Sua loja ainda não está conectada.
|
|
40
|
-
Acesse https://iron-office.vercel.app/
|
|
27
|
+
Acesse https://iron-office.vercel.app/integrations e conecte sua loja Shopify.
|
|
41
28
|
Depois me chame novamente.
|
|
42
29
|
```
|
|
43
30
|
|
|
44
|
-
**Se
|
|
31
|
+
**Se a ferramenta `shop_status` não existir nesta sessão** e o usuário usa o Iron Office → peça pra ele rodar `npx iron-edge office` uma vez (instala o plugin), abrir uma sessão nova e te chamar de novo. Pare aqui.
|
|
32
|
+
|
|
33
|
+
**Se o usuário não usa o Iron Office** → fallback para arquivo local:
|
|
45
34
|
|
|
46
35
|
Verifique se existe `~/.iron/shopify-stores.yaml`:
|
|
47
36
|
|
|
@@ -170,47 +159,42 @@ Qual é a missão na [apelido]?
|
|
|
170
159
|
|
|
171
160
|
## USANDO O PROXY IRON OFFICE
|
|
172
161
|
|
|
173
|
-
Quando conectado via Iron Office,
|
|
174
|
-
As credenciais ficam no servidor
|
|
162
|
+
Quando conectado via Iron Office, toda chamada GraphQL passa pela ferramenta `shop_query` do plugin `iron-office`.
|
|
163
|
+
As credenciais ficam no servidor e a chave do Iron Office fica com o plugin: você nunca manuseia nenhuma das duas.
|
|
175
164
|
|
|
176
165
|
**Formato de chamada:**
|
|
177
166
|
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
167
|
+
```
|
|
168
|
+
shop_query({ query: "{ shop { name email } }" })
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Com variáveis:
|
|
182
172
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}'
|
|
173
|
+
```
|
|
174
|
+
shop_query({
|
|
175
|
+
query: "query($n: Int!) { products(first: $n) { edges { node { id title } } } }",
|
|
176
|
+
variables: { n: 10 }
|
|
177
|
+
})
|
|
189
178
|
```
|
|
190
179
|
|
|
191
180
|
**Exemplos práticos:**
|
|
192
181
|
|
|
193
|
-
```
|
|
194
|
-
# Cada cerca ```bash e' um bloco INDEPENDENTE: o Claude Code nao guarda estado de shell entre
|
|
195
|
-
# execucoes, entao $IRON_KEY definido num bloco anterior chega VAZIO aqui. A guarda se repete
|
|
196
|
-
# em todo bloco que fala com a rede — sem ela, o curl sairia com a chave em branco.
|
|
197
|
-
IRON_KEY=$(cat ~/.claude/iron-office-api-key 2>/dev/null | tr -d '[:space:]')
|
|
198
|
-
[ -z "$IRON_KEY" ] && { echo "Sem iron-office-api-key — nada foi enviado."; exit 0; }
|
|
199
|
-
|
|
182
|
+
```
|
|
200
183
|
# Listar produtos com SEO
|
|
201
|
-
|
|
202
|
-
-H "Content-Type: application/json" \
|
|
203
|
-
-H "x-api-key: $IRON_KEY" \
|
|
204
|
-
-d '{"query":"{ products(first: 50) { edges { node { id title handle seo { title description } } } } }"}'
|
|
184
|
+
shop_query({ query: "{ products(first: 50) { edges { node { id title handle seo { title description } } } } }" })
|
|
205
185
|
|
|
206
186
|
# Informações da loja
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
187
|
+
shop_query({ query: "{ shop { name email myshopifyDomain plan { displayName } } }" })
|
|
188
|
+
|
|
189
|
+
# Atualizar o SEO de um produto
|
|
190
|
+
shop_query({
|
|
191
|
+
query: "mutation($input: ProductInput!) { productUpdate(input: $input) { product { id } userErrors { field message } } }",
|
|
192
|
+
variables: { input: { id: "gid://shopify/Product/123", seo: { title: "...", description: "..." } } }
|
|
193
|
+
})
|
|
211
194
|
```
|
|
212
195
|
|
|
213
|
-
|
|
196
|
+
A ferramenta devolve o JSON da Shopify como texto; resposta muito grande vem cortada com aviso (peça menos itens por vez, com `first:` menor).
|
|
197
|
+
|
|
214
198
|
|
|
215
199
|
---
|
|
216
200
|
|
package/agents/iron-spy.md
CHANGED
|
@@ -259,6 +259,28 @@ Após a execução:
|
|
|
259
259
|
- Perguntar se o usuário quer ajustar as palavras-chave e rodar novamente
|
|
260
260
|
- Oferecer análise mais profunda de algum anúncio específico
|
|
261
261
|
|
|
262
|
+
Quando quem te chamou é o Iron do Iron Office, não cite o caminho `file://` do dashboard: ele não abre no site. A resposta final leva a tabela dos 10 anúncios mais validados (anunciante · dias no ar · ângulo · gancho) e a análise dos ângulos em markdown, e termina com o `## Cartão`.
|
|
263
|
+
|
|
264
|
+
Além do dashboard, a resposta final termina com o cartão de ação: o título `## Cartão` seguido de um bloco ```json (é o que o Iron Office mostra no chat):
|
|
265
|
+
|
|
266
|
+
```json
|
|
267
|
+
{
|
|
268
|
+
"kind": "spy",
|
|
269
|
+
"score": null,
|
|
270
|
+
"summary": "o que o nicho mais usa, até 160 caracteres",
|
|
271
|
+
"items": [
|
|
272
|
+
{
|
|
273
|
+
"title": "o ângulo, até 80 caracteres",
|
|
274
|
+
"why": "% dos anúncios que usam e dias no ar do mais antigo, até 160",
|
|
275
|
+
"effort": "1 copy",
|
|
276
|
+
"action": "Iron, escreve uma copy nesse ângulo: <ângulo>, pro meu produto"
|
|
277
|
+
}
|
|
278
|
+
]
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Regras do cartão: `items` = os 3 ângulos mais validados, no máximo 3; `action` cabe em 300 caracteres, é o pedido que o aluno manda com um clique e sempre começa com "Iron, escreve uma copy nesse ângulo" (o Iron Office descarta item com outro começo).
|
|
283
|
+
|
|
262
284
|
## Palavras-chave por nicho
|
|
263
285
|
|
|
264
286
|
Ao gerar as keywords para o scraper, use estas estratégias:
|
package/bin/iron-edge.js
CHANGED
|
@@ -17,6 +17,12 @@
|
|
|
17
17
|
* office [--safe] liga o Iron Office: instala o plugin de canal e abre o
|
|
18
18
|
* Claude Code conversando com o chat do dashboard
|
|
19
19
|
*
|
|
20
|
+
* doctor [--json] mede a maquina (Node, git, Claude Code, Iron Edge, chave,
|
|
21
|
+
* plugin, cofre) e, com a chave aceita, manda o resultado
|
|
22
|
+
* pra pagina de Setup do Iron Office
|
|
23
|
+
*
|
|
24
|
+
* install --cofre alem do install, cria ~/Iron Cofre/ (cofre vazio pro Obsidian)
|
|
25
|
+
*
|
|
20
26
|
* Nao existe 'uninstall' automatico: remover continua sendo operacao conduzida, com o
|
|
21
27
|
* manifesto na mao (o README descreve o procedimento).
|
|
22
28
|
*/
|
|
@@ -155,7 +161,12 @@ if (cmd === 'install') {
|
|
|
155
161
|
const r = spawnSync(process.execPath, [path.join(__dirname, '..', 'installer', 'office.js'), ...rest], { stdio: 'inherit' });
|
|
156
162
|
process.exit(r.status ?? 1);
|
|
157
163
|
|
|
164
|
+
} else if (cmd === 'doctor') {
|
|
165
|
+
// mede a maquina e acende o checklist do Setup no site (installer/doctor.js)
|
|
166
|
+
const r = spawnSync(process.execPath, [path.join(__dirname, '..', 'installer', 'doctor.js'), ...rest], { stdio: 'inherit' });
|
|
167
|
+
process.exit(r.status ?? 1);
|
|
168
|
+
|
|
158
169
|
} else {
|
|
159
|
-
console.log('uso: iron-edge <install|update|verify|office> [--dry-run] [--source <dir>] [--safe]');
|
|
170
|
+
console.log('uso: iron-edge <install|update|verify|office|doctor> [--dry-run] [--source <dir>] [--cofre] [--safe] [--json]');
|
|
160
171
|
process.exit(cmd && cmd !== '--help' && cmd !== '-h' ? 1 : 0);
|
|
161
172
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# CLAUDE.md — regras do cofre
|
|
2
|
+
|
|
3
|
+
Este cofre é a memória do Iron. São arquivos `.md` no seu computador: nada sai daqui.
|
|
4
|
+
Você pode abrir a pasta no Obsidian ou em qualquer editor de texto.
|
|
5
|
+
|
|
6
|
+
## Como o cofre é organizado
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
Iron Cofre/
|
|
10
|
+
CLAUDE.md ← estas regras
|
|
11
|
+
INDICE.md ← o mapa: um link pra cada projeto
|
|
12
|
+
memoria/ ← o diário das sessões (o Iron cria no primeiro "salvar")
|
|
13
|
+
Projetos/ ← uma pasta por projeto
|
|
14
|
+
templates/nota.md ← o modelo de nota
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
- **Cada projeto** vira uma pasta: `Projetos/<Nome>/PROJETO.md` é a nota principal (o hub), com o estado, as decisões e os próximos passos.
|
|
18
|
+
- **Projeto com várias partes** ganha uma nota por parte (as folhas) na mesma pasta. As folhas apontam só pro hub; o hub aponta pras folhas e pro `INDICE.md`.
|
|
19
|
+
- Toda nota começa com o cabeçalho do `templates/nota.md`: `projeto`, `tipo`, `status` (ativo, pausado ou concluído) e `atualizado` (a data).
|
|
20
|
+
|
|
21
|
+
## As regras
|
|
22
|
+
|
|
23
|
+
1. Antes de trabalhar num projeto, leia a nota dele em `Projetos/` pra saber em que pé ele está. Não assuma o estado pela memória da conversa.
|
|
24
|
+
2. Quando o usuário disser "salvar" ou "encerrar", registre a sessão sem perguntar nada.
|
|
25
|
+
3. Diário e nota de projeto são coisas diferentes. O diário responde "o que fizemos hoje"; a nota responde "como o projeto está hoje". Conversa, ideia sem decisão e teste que não concluiu ficam só no diário.
|
|
26
|
+
4. Data sempre absoluta (AAAA-MM-DD). Nunca "ontem", "semana passada" nem "amanhã": daqui a dois meses isso não significa mais nada.
|
|
27
|
+
5. Nunca invente número, nome, versão ou caminho. O que faltar entra como "(a preencher)".
|
|
28
|
+
6. Projeto de cliente pode ficar aqui (o que foi feito, o que foi decidido, o que falta). O que não fica: material confidencial de cliente ou empregador (contrato, planilha interna, dado de cliente), senha, chave nem dado pessoal de quem não autorizou. Se aparecer na conversa, não registre e avise que não registrou.
|
|
29
|
+
|
|
30
|
+
## O que salvar e o que não salvar
|
|
31
|
+
|
|
32
|
+
- **Salvar:** o que foi construído e onde está, decisões importantes, status, próximos passos, o que está esperando outra pessoa.
|
|
33
|
+
- **Não salvar:** detalhe técnico que já está no código, conversa genérica, teste que não foi pra frente.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Índice do cofre
|
|
2
|
+
|
|
3
|
+
O mapa da sua memória. Cada projeto novo ganha uma linha aqui.
|
|
4
|
+
|
|
5
|
+
## Projetos
|
|
6
|
+
|
|
7
|
+
| Projeto | Status | Nota |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| (nenhum ainda) | | |
|
|
10
|
+
|
|
11
|
+
Quando começar um projeto, conte ao Iron o que é: ele oferece criar a pasta do projeto e a linha aqui. No fim da conversa, diga "salvar".
|
|
File without changes
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* cofre.js — `npx iron-edge install --cofre`: cria um cofre vazio em ~/Iron Cofre/, pronto pro
|
|
4
|
+
* Obsidian. Roda no fim do install com sucesso (ou sozinho, pra teste).
|
|
5
|
+
*
|
|
6
|
+
* Regras:
|
|
7
|
+
* - copia os 4 itens de installer/cofre/ SO' se nao existirem. Nunca sobrescreve: o cofre e' do
|
|
8
|
+
* aluno, e um segundo `install --cofre` nao pode apagar o que ele ja escreveu.
|
|
9
|
+
* - fica FORA do manifesto do pacote (o verify e o update nao encostam nele).
|
|
10
|
+
* - grava o caminho no `vault_path` do ~/.iron/config.yaml so' quando nenhum cofre esta apontado. Sem isso
|
|
11
|
+
* o Iron nao enxerga o cofre e oferece criar outro. Caminho com barra normal (regra da skill
|
|
12
|
+
* salvar, secao 0) e lido de volta antes de declarar.
|
|
13
|
+
* - --dry-run so' lista.
|
|
14
|
+
*
|
|
15
|
+
* Saida so' em ASCII, como o resto do instalador.
|
|
16
|
+
*/
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const path = require('path');
|
|
19
|
+
const os = require('os');
|
|
20
|
+
|
|
21
|
+
const MODELO = path.join(__dirname, 'cofre');
|
|
22
|
+
const ITENS = ['CLAUDE.md', 'INDICE.md', path.join('templates', 'nota.md'), path.join('Projetos', '.gitkeep')];
|
|
23
|
+
const NOME = 'Iron Cofre';
|
|
24
|
+
|
|
25
|
+
function log(msg) { try { fs.writeSync(1, msg + '\n'); } catch (_) { process.stdout.write(msg + '\n'); } }
|
|
26
|
+
|
|
27
|
+
function homeReal(home) {
|
|
28
|
+
let h = home || os.homedir();
|
|
29
|
+
try { h = fs.realpathSync.native(h); } catch (_) {}
|
|
30
|
+
return h;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function barraNormal(p) { return p.split(path.sep).join('/'); }
|
|
34
|
+
|
|
35
|
+
// Grava vault_path no config SO' se ninguem apontou um cofre ainda. Devolve
|
|
36
|
+
// 'gravado' | 'ja-tinha' | 'sem-config' | 'falhou'.
|
|
37
|
+
// "Ninguem apontou" = vault_path ausente, vazio, null ou ~ E sem obsidian_vault_path preenchido
|
|
38
|
+
// (o nome antigo do campo, que o Iron tambem le). Ausente: acrescenta a linha no fim.
|
|
39
|
+
function gravarVaultPath(home, cofre) {
|
|
40
|
+
const cfg = path.join(home, '.iron', 'config.yaml');
|
|
41
|
+
let txt;
|
|
42
|
+
try { txt = fs.readFileSync(cfg, 'utf8'); } catch (_) { return 'sem-config'; }
|
|
43
|
+
const VAZIO = /^\s*(""|''|null|~)?\s*(#.*)?$/;
|
|
44
|
+
const campo = (nome) => {
|
|
45
|
+
const m = txt.match(new RegExp('^' + nome + ':(.*)$', 'm'));
|
|
46
|
+
return m ? { linha: m[0], valor: m[1] } : null;
|
|
47
|
+
};
|
|
48
|
+
const antigo = campo('obsidian_vault_path');
|
|
49
|
+
if (antigo && !VAZIO.test(antigo.valor)) return 'ja-tinha';
|
|
50
|
+
const atual = campo('vault_path');
|
|
51
|
+
if (atual && !VAZIO.test(atual.valor)) return 'ja-tinha';
|
|
52
|
+
const linha = 'vault_path: "' + barraNormal(cofre) + '"';
|
|
53
|
+
const novo = atual
|
|
54
|
+
? txt.replace(atual.linha, linha)
|
|
55
|
+
: txt + (txt === '' || txt.endsWith('\n') ? '' : '\n') + linha + '\n';
|
|
56
|
+
fs.writeFileSync(cfg, novo);
|
|
57
|
+
const relido = fs.readFileSync(cfg, 'utf8').split(/\r?\n/).find(l => l.startsWith('vault_path:'));
|
|
58
|
+
return relido === linha ? 'gravado' : 'falhou';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function criarCofre(opts) {
|
|
62
|
+
const o = opts || {};
|
|
63
|
+
const home = homeReal(o.home);
|
|
64
|
+
const destino = path.join(home, NOME);
|
|
65
|
+
const criados = [], pulados = [];
|
|
66
|
+
for (const rel of ITENS) {
|
|
67
|
+
const dest = path.join(destino, rel);
|
|
68
|
+
if (fs.existsSync(dest)) { pulados.push(dest); continue; }
|
|
69
|
+
criados.push(dest);
|
|
70
|
+
if (o.dryRun) continue;
|
|
71
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
72
|
+
fs.copyFileSync(path.join(MODELO, rel), dest);
|
|
73
|
+
}
|
|
74
|
+
log('');
|
|
75
|
+
log('--- cofre (' + destino + ') ---');
|
|
76
|
+
for (const c of criados) log((o.dryRun ? ' criaria: ' : ' criado: ') + c);
|
|
77
|
+
for (const p of pulados) log(' ja existe, nao mexi: ' + p);
|
|
78
|
+
let vault = 'dry-run';
|
|
79
|
+
if (!o.dryRun) {
|
|
80
|
+
vault = gravarVaultPath(home, destino);
|
|
81
|
+
if (vault === 'gravado') log(' vault_path gravado no ~/.iron/config.yaml: o Iron ja sabe onde o cofre esta');
|
|
82
|
+
else if (vault === 'ja-tinha') log(' o ~/.iron/config.yaml ja aponta pra um cofre: nao troquei. Pra usar este, diga ao Iron: "usa o Iron Cofre como meu cofre"');
|
|
83
|
+
else if (vault === 'sem-config') log(' sem ~/.iron/config.yaml: diga ao Iron "usa o Iron Cofre como meu cofre"');
|
|
84
|
+
else log(' ATENCAO: o vault_path nao ficou gravado certo. Diga ao Iron: "conserta meu vault_path"');
|
|
85
|
+
log(' Abra a pasta no Obsidian: "Open folder as vault" > ' + destino);
|
|
86
|
+
}
|
|
87
|
+
return { destino, criados, pulados, vault };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
module.exports = { criarCofre, ITENS, NOME };
|
|
91
|
+
|
|
92
|
+
if (require.main === module) {
|
|
93
|
+
criarCofre({ dryRun: process.argv.includes('--dry-run') });
|
|
94
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* doctor.js — `npx iron-edge doctor`: mede a maquina do aluno e manda o resultado pro Iron Office,
|
|
4
|
+
* pra a pagina de Setup acender cada passo sozinha (o site nao enxerga a maquina).
|
|
5
|
+
*
|
|
6
|
+
* Mede: node (>= 20), git, Claude Code, Iron Edge instalado pelo npm (manifesto), chave do Iron
|
|
7
|
+
* Office (formato + confere com o site), plugin iron-office, cofre (~/Iron Cofre, informativo).
|
|
8
|
+
*
|
|
9
|
+
* Envio: SO' com a chave aceita pelo site (ping = ok) faz POST /api/chat/setup-status. Sem chave,
|
|
10
|
+
* com chave recusada ou sem rede, o resultado fica so' no terminal. Falhar o envio e' aviso, nao erro.
|
|
11
|
+
* Nao grava nada em disco.
|
|
12
|
+
*
|
|
13
|
+
* Saida: uma linha por item, sem acento (console do Windows antigo). --json imprime o JSON cru
|
|
14
|
+
* (o envio acontece igual).
|
|
15
|
+
* Exit 0 so' se node, git, claude e iron_edge estao ok (chave e plugin sao passos do site).
|
|
16
|
+
*/
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const path = require('path');
|
|
19
|
+
const os = require('os');
|
|
20
|
+
const { spawnSync } = require('child_process');
|
|
21
|
+
const { readKey, keyFileExists, ping, resolveClaude, PLUGIN } = require('./office.js');
|
|
22
|
+
|
|
23
|
+
const OFFICE_URL = (process.env.IRON_OFFICE_URL || 'https://iron-office.vercel.app').replace(/\/+$/, '');
|
|
24
|
+
const IS_WIN = process.platform === 'win32';
|
|
25
|
+
const POST_TIMEOUT_MS = 15000;
|
|
26
|
+
let VERSION = '';
|
|
27
|
+
try { VERSION = require('../package.json').version || ''; } catch (_) {}
|
|
28
|
+
|
|
29
|
+
function cut(s, n) { s = String(s == null ? '' : s).replace(/\s+/g, ' ').trim(); return s.length > n ? s.slice(0, n - 1) + '~' : s; }
|
|
30
|
+
function item(ok, detail) { return { ok: !!ok, detail: cut(detail, 120) }; }
|
|
31
|
+
// O detalhe vai COM acento pro site (o aluno le' la'); no terminal sai sem acento (console do
|
|
32
|
+
// Windows antigo mostra lixo em acento). Uma fonte so', duas saidas.
|
|
33
|
+
function ascii(s) { return String(s).normalize('NFD').replace(/[\u0300-\u036f]/g, ''); }
|
|
34
|
+
|
|
35
|
+
function run(bin, args) {
|
|
36
|
+
return spawnSync(bin, args, { encoding: 'utf8', shell: IS_WIN, timeout: 20000 });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function homeDir() {
|
|
40
|
+
let h = os.homedir();
|
|
41
|
+
try { h = fs.realpathSync.native(h); } catch (_) {}
|
|
42
|
+
return h;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function checkNode() {
|
|
46
|
+
const major = Number(process.versions.node.split('.')[0]);
|
|
47
|
+
return item(major >= 20, major >= 20 ? process.version : process.version + ': precisa da 20 ou maior (nodejs.org)');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function checkGit() {
|
|
51
|
+
const r = run('git', ['--version']);
|
|
52
|
+
if (r.error || r.status !== 0) return item(false, 'git não encontrado: instale em git-scm.com');
|
|
53
|
+
return item(true, (r.stdout || '').trim());
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function checkClaude() {
|
|
57
|
+
const bin = resolveClaude();
|
|
58
|
+
if (!bin) return { bin: null, check: item(false, 'Claude Code não encontrado: instale em claude.com/claude-code') };
|
|
59
|
+
const r = run(bin, ['--version']);
|
|
60
|
+
return { bin, check: item(true, ((r.stdout || '').trim().split('\n')[0]) || 'instalado') };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function checkIronEdge(home) {
|
|
64
|
+
const man = path.join(home, '.iron', 'install-manifest.json');
|
|
65
|
+
if (!fs.existsSync(man)) return item(false, 'não instalado pelo npm: rode npx iron-edge install');
|
|
66
|
+
try {
|
|
67
|
+
const m = JSON.parse(fs.readFileSync(man, 'utf8'));
|
|
68
|
+
return item(true, m.version || m.label || 'instalado');
|
|
69
|
+
} catch (_) {
|
|
70
|
+
return item(false, 'manifesto ilegível: rode npx iron-edge install');
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function checkKey(home, baseUrl) {
|
|
75
|
+
const key = readKey(home);
|
|
76
|
+
if (!key) {
|
|
77
|
+
return { key: '', check: item(false, keyFileExists(home) ? 'formato errado: refaça o passo 2 no site' : 'sem chave ainda: faça o passo 2 no site') };
|
|
78
|
+
}
|
|
79
|
+
const v = await ping(key, baseUrl);
|
|
80
|
+
if (v === 'ok') return { key, check: item(true, 'aceita pelo Iron Office') };
|
|
81
|
+
if (v === 'rejected') return { key: '', check: item(false, 'recusada: a chave foi trocada, refaça o passo 2') };
|
|
82
|
+
return { key: '', check: item(false, 'sem rede: não deu pra conferir com o site') };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function checkPlugin(claudeBin) {
|
|
86
|
+
if (!claudeBin) return item(false, 'sem Claude Code');
|
|
87
|
+
const r = run(claudeBin, ['plugin', 'list']);
|
|
88
|
+
const out = (r.stdout || '') + (r.stderr || '');
|
|
89
|
+
if (r.error || r.status !== 0) return item(false, 'não deu pra listar os plugins');
|
|
90
|
+
return out.includes(PLUGIN) ? item(true, PLUGIN) : item(false, 'falta o plugin: rode npx iron-edge office');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function checkCofre(home) {
|
|
94
|
+
const ok = fs.existsSync(path.join(home, 'Iron Cofre', 'CLAUDE.md'));
|
|
95
|
+
return item(ok, ok ? '~/Iron Cofre' : 'sem cofre pronto (opcional: npx iron-edge install --cofre)');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function medir(opts) {
|
|
99
|
+
const o = opts || {};
|
|
100
|
+
const home = o.home || homeDir();
|
|
101
|
+
const claude = checkClaude();
|
|
102
|
+
const key = await checkKey(home, o.baseUrl);
|
|
103
|
+
const result = {
|
|
104
|
+
os: cut(process.platform + '-' + process.arch, 20),
|
|
105
|
+
doctor: cut(VERSION, 20),
|
|
106
|
+
checks: {
|
|
107
|
+
node: checkNode(),
|
|
108
|
+
git: checkGit(),
|
|
109
|
+
claude: claude.check,
|
|
110
|
+
iron_edge: checkIronEdge(home),
|
|
111
|
+
key: key.check,
|
|
112
|
+
plugin: checkPlugin(claude.bin),
|
|
113
|
+
cofre: checkCofre(home),
|
|
114
|
+
},
|
|
115
|
+
ran_at: new Date().toISOString(),
|
|
116
|
+
};
|
|
117
|
+
return { result, key: key.key };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Devolve 'enviado' | 'recusado' (o site respondeu e nao gravou) | 'falhou' (rede).
|
|
121
|
+
async function enviar(result, key, baseUrl) {
|
|
122
|
+
const url = (baseUrl || OFFICE_URL).replace(/\/+$/, '') + '/api/chat/setup-status';
|
|
123
|
+
if (typeof fetch !== 'function') return 'falhou';
|
|
124
|
+
const ctl = new AbortController();
|
|
125
|
+
const timer = setTimeout(() => ctl.abort(), POST_TIMEOUT_MS);
|
|
126
|
+
try {
|
|
127
|
+
const r = await fetch(url, {
|
|
128
|
+
method: 'POST',
|
|
129
|
+
headers: { 'content-type': 'application/json', 'x-api-key': key },
|
|
130
|
+
body: JSON.stringify(result),
|
|
131
|
+
signal: ctl.signal,
|
|
132
|
+
});
|
|
133
|
+
return r.ok ? 'enviado' : 'recusado';
|
|
134
|
+
} catch (_) {
|
|
135
|
+
return 'falhou';
|
|
136
|
+
} finally {
|
|
137
|
+
clearTimeout(timer);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const ROTULO = { node: 'Node', git: 'git', claude: 'Claude Code', iron_edge: 'Iron Edge', key: 'chave', plugin: 'plugin', cofre: 'cofre' };
|
|
142
|
+
const OBRIGATORIOS = ['node', 'git', 'claude', 'iron_edge'];
|
|
143
|
+
|
|
144
|
+
async function main(argv, opts) {
|
|
145
|
+
const o = opts || {};
|
|
146
|
+
const { result, key } = await medir(o);
|
|
147
|
+
const exit = OBRIGATORIOS.every(k => result.checks[k].ok) ? 0 : 1;
|
|
148
|
+
if (argv.includes('--json')) {
|
|
149
|
+
// mesmo envio, saida so' o JSON (pra teste e pra script)
|
|
150
|
+
if (key) await enviar(result, key, o.baseUrl);
|
|
151
|
+
console.log(JSON.stringify(result, null, 2));
|
|
152
|
+
return exit;
|
|
153
|
+
}
|
|
154
|
+
console.log('Iron Edge doctor ' + result.doctor);
|
|
155
|
+
for (const [k, c] of Object.entries(result.checks)) {
|
|
156
|
+
console.log(' ' + (c.ok ? '[ok] ' : (k === 'cofre' ? '[info] ' : '[falta] ')) + ROTULO[k] + ': ' + ascii(c.detail));
|
|
157
|
+
}
|
|
158
|
+
console.log('');
|
|
159
|
+
if (!key && !readKey(o.home || homeDir())) {
|
|
160
|
+
console.log("sem chave ainda: o resultado fica so' aqui no terminal; faca o passo 2 no site.");
|
|
161
|
+
} else if (!key) {
|
|
162
|
+
console.log("o resultado fica so' aqui no terminal (chave: " + ascii(result.checks.key.detail) + ').');
|
|
163
|
+
} else {
|
|
164
|
+
const env = await enviar(result, key, o.baseUrl);
|
|
165
|
+
if (env === 'enviado') console.log('resultado enviado: confira em ' + OFFICE_URL + '/onboarding');
|
|
166
|
+
else if (env === 'recusado') console.log('[aviso] o site recusou o resultado (chave trocada?). O que esta acima vale; refaca o passo 2 e rode de novo.');
|
|
167
|
+
else console.log('[aviso] nao deu pra mandar o resultado pro site (sem rede?). O que esta acima vale.');
|
|
168
|
+
}
|
|
169
|
+
return exit;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
module.exports = { medir, enviar, main };
|
|
173
|
+
|
|
174
|
+
if (require.main === module) {
|
|
175
|
+
main(process.argv.slice(2)).then(code => process.exit(code), err => {
|
|
176
|
+
console.error('Erro inesperado: ' + (err && err.message ? err.message : err));
|
|
177
|
+
process.exit(1);
|
|
178
|
+
});
|
|
179
|
+
}
|
package/installer/install.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* - Manifesto com SHA-256 (gerados marcados generated:true).
|
|
17
17
|
* - shopify.yaml nasce desativado. config em branco (sem PII).
|
|
18
18
|
*
|
|
19
|
-
* Uso: node install.js [--dry-run] [--source <dir>]
|
|
19
|
+
* Uso: node install.js [--dry-run] [--source <dir>] [--cofre]
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
'use strict';
|
|
@@ -41,6 +41,7 @@ function abort(msg) {
|
|
|
41
41
|
|
|
42
42
|
const args = process.argv.slice(2);
|
|
43
43
|
const DRY_RUN = args.includes('--dry-run');
|
|
44
|
+
const COFRE = args.includes('--cofre'); // cria ~/Iron Cofre/ no fim (installer/cofre.js)
|
|
44
45
|
const srcIdx = args.indexOf('--source');
|
|
45
46
|
let SOURCE = srcIdx >= 0 && args[srcIdx + 1] ? path.resolve(args[srcIdx + 1]) : __dirname;
|
|
46
47
|
// Opera sobre o caminho FISICO: ancestral symlink herdado da maquina (perfil
|
|
@@ -335,6 +336,7 @@ if (DRY_RUN) {
|
|
|
335
336
|
if (payloadPlan.length > 10) log(` ... e mais ${payloadPlan.length - 10} arquivos`);
|
|
336
337
|
log('\n--- arquivos que SERAO gerados ---');
|
|
337
338
|
for (const g of GENERATED) log(' ' + g.dest);
|
|
339
|
+
if (COFRE) require('./cofre').criarCofre({ home: HOME, dryRun: true });
|
|
338
340
|
log('\nDRY-RUN concluido. Nada foi copiado.');
|
|
339
341
|
process.exit(0);
|
|
340
342
|
}
|
|
@@ -569,13 +571,19 @@ log(' Os outros 43 agentes se chamam PEDINDO ("usa o iron-copy").');
|
|
|
569
571
|
const nSkills = (() => { try { return fs.readdirSync(path.join(SOURCE, 'skills')).filter((d) => !d.startsWith('.')).length; } catch (_) { return null; } })();
|
|
570
572
|
log(` As ${nSkills || ''} SKILLS sao de barra: /gate /salvar /brandkit /escada ...`.replace(' ', ' '));
|
|
571
573
|
log('');
|
|
572
|
-
|
|
573
|
-
log('');
|
|
574
|
-
|
|
575
|
-
log('');
|
|
576
|
-
|
|
577
|
-
log('
|
|
578
|
-
log('
|
|
579
|
-
log('
|
|
580
|
-
log('
|
|
581
|
-
log('');
|
|
574
|
+
if (COFRE) {
|
|
575
|
+
log('3. Memoria: o cofre (detalhes abaixo).');
|
|
576
|
+
require('./cofre').criarCofre({ home: HOME });
|
|
577
|
+
log('');
|
|
578
|
+
} else {
|
|
579
|
+
log('3. Ligue a memoria. Diga a ele:');
|
|
580
|
+
log('');
|
|
581
|
+
log(' cria meu cofre');
|
|
582
|
+
log('');
|
|
583
|
+
log(' O cofre e uma pasta de arquivos .md na SUA maquina, onde o Iron guarda quem');
|
|
584
|
+
log(' voce e, seus projetos e o que ficou pendente. SEM ele, cada conversa comeca');
|
|
585
|
+
log(' do zero. Com ele, o Iron abre a sessao ja sabendo em que pe as coisas estao.');
|
|
586
|
+
log(' Nada e enviado pra lugar nenhum: e arquivo seu, no seu disco.');
|
|
587
|
+
log(' Se preferir fazer depois, e so pedir a qualquer momento.');
|
|
588
|
+
log('');
|
|
589
|
+
}
|
package/installer/office.js
CHANGED
|
@@ -163,7 +163,7 @@ async function main(argv) {
|
|
|
163
163
|
return r.status ?? 1;
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
-
module.exports = { buildClaudeArgs, keyPath, readKey, isValidKey, ping, CHANNEL, PLUGIN, MARKETPLACE_REPO, KEY_RE };
|
|
166
|
+
module.exports = { buildClaudeArgs, keyPath, readKey, keyFileExists, isValidKey, ping, resolveClaude, CHANNEL, PLUGIN, MARKETPLACE_REPO, KEY_RE };
|
|
167
167
|
|
|
168
168
|
if (require.main === module) {
|
|
169
169
|
main(process.argv.slice(2)).then(code => process.exit(code), err => {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iron-edge",
|
|
3
|
-
"version": "2.4.
|
|
4
|
-
"description": "Ecossistema de agentes para Claude Code: 44 agentes de topo, 18 squads e 14 skills, com quality gate, memoria, instalador que nunca sobrescreve
|
|
3
|
+
"version": "2.4.4",
|
|
4
|
+
"description": "Ecossistema de agentes para Claude Code: 44 agentes de topo, 18 squads e 14 skills, com quality gate, memoria, instalador que nunca sobrescreve o comando office (chat do Iron Office no dashboard; confere a chave com o site antes de abrir o Claude) e o doctor (mede a maquina e acende o Setup do site).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude",
|
|
7
7
|
"claude-code",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"node": ">=18.0.0"
|
|
33
33
|
},
|
|
34
34
|
"scripts": {
|
|
35
|
-
"test": "node scripts/lint.js && node scripts/test-verify.js && node scripts/test-update.js && node scripts/test-office.js",
|
|
35
|
+
"test": "node scripts/lint.js && node scripts/test-verify.js && node scripts/test-update.js && node scripts/test-office.js && node scripts/test-cofre.js",
|
|
36
36
|
"test:matriz": "node scripts/test-matriz-update.js"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|