iron-edge 2.0.2 → 2.2.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/README.md +39 -8
- package/bin/iron-edge.js +15 -3
- package/installer/gerados.js +106 -0
- package/installer/install.js +21 -55
- package/installer/update.js +343 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,7 +40,7 @@ Existem **duas coisas diferentes** aqui, e elas se chamam de jeitos diferentes.
|
|
|
40
40
|
(As outras: `anotar`, `faxina`, `conectar-mcp`, `roteiro-video`, `lp-cinematico`, `clone-site`,
|
|
41
41
|
`clone-inventory`, `clone-distill`, `clone-organize`, `clone-transplant`.)
|
|
42
42
|
|
|
43
|
-
**Agentes — esses
|
|
43
|
+
**Agentes — esses você chama pedindo.** São 44:
|
|
44
44
|
|
|
45
45
|
```
|
|
46
46
|
"usa o iron-ceo" ← comece por aqui, ele direciona
|
|
@@ -48,14 +48,27 @@ Existem **duas coisas diferentes** aqui, e elas se chamam de jeitos diferentes.
|
|
|
48
48
|
"quero o iron-shopify nessa loja"
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
+
**E tem um atalho, que é como se usa no dia a dia.** A instalação cria o `iron`, e você o chama
|
|
52
|
+
escrevendo só a palavra:
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
iron ← ativa e pergunta o que você precisa
|
|
56
|
+
iron preciso da copy desse anúncio ← já entra com o pedido
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Sem barra e sem traço.** Se você preferir a barra, `/iron` faz exatamente o mesmo — ela é o
|
|
60
|
+
atalho explícito da interface, não uma exigência. As três formas (`iron`, `/iron`,
|
|
61
|
+
`"usa o iron-ceo"`) chegam no mesmo lugar.
|
|
62
|
+
|
|
63
|
+
Os outros 43 agentes continuam sendo chamados pedindo. O pacote instala **um** atalho, não 44.
|
|
64
|
+
|
|
51
65
|
Digitar `/iron-ceo` **não funciona** — barra é para skill, e agente não é skill. Se quiser a
|
|
52
66
|
menção explícita, a forma que resolve é **`@agent-iron-ceo`** (com o prefixo `agent-`; o
|
|
53
67
|
`@iron-ceo` sozinho não é documentado). O Claude Code também escolhe o agente sozinho quando o
|
|
54
68
|
pedido é claro; nomear é só para ter certeza de qual.
|
|
55
69
|
|
|
56
|
-
> Se você viu em algum lugar a instrução de digitar `/iron-ceo`, ela está desatualizada
|
|
57
|
-
>
|
|
58
|
-
> Para conferir que está tudo no lugar: `npx iron-edge verify`.
|
|
70
|
+
> Se você viu em algum lugar a instrução de digitar `/iron-ceo`, ela está desatualizada: o atalho
|
|
71
|
+
> se chama **`/iron`**. Sua instalação não tem defeito. Para conferir: `npx iron-edge verify`.
|
|
59
72
|
|
|
60
73
|
**Squads — o terceiro nível, e você não chama direto.** São 18 equipes que vivem em `~/.iron/`.
|
|
61
74
|
Cada uma tem um agente de topo, e é por ele que se entra:
|
|
@@ -152,11 +165,29 @@ Instalada junto. Depois de `npx iron-edge install`, os dois arquivos abaixo est
|
|
|
152
165
|
npx iron-edge verify
|
|
153
166
|
```
|
|
154
167
|
|
|
155
|
-
**
|
|
168
|
+
**Atualizar é um comando. Remover é operação conduzida.**
|
|
169
|
+
|
|
170
|
+
- **Atualizar:**
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
npx iron-edge@latest update
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Ele lê o manifesto e confere **arquivo por arquivo, por hash**. Só troca o que estiver
|
|
177
|
+
byte a byte idêntico ao pacote — ou seja, o que ninguém encostou. Numa instalação típica
|
|
178
|
+
isso é mais de 2.100 dos 2.126 arquivos.
|
|
179
|
+
|
|
180
|
+
**O que ele nunca toca:** o seu `config.yaml`, o seu cofre, agentes e skills que você
|
|
181
|
+
criou, e **qualquer arquivo do pacote que você tenha editado** — esses ficam marcados no
|
|
182
|
+
manifesto e continuam preservados nas atualizações seguintes. Ele lista um por um antes de
|
|
183
|
+
agir. Se quiser ver o plano sem escrever nada: `npx iron-edge update --dry-run`.
|
|
156
184
|
|
|
157
|
-
|
|
185
|
+
Se algo falhar no meio, nada fica pela metade: ele escreve tudo ao lado primeiro e só
|
|
186
|
+
depois coloca no lugar. Falhou antes disso, sua instalação continua exatamente como estava.
|
|
158
187
|
|
|
159
|
-
|
|
188
|
+
> Instalação anterior a 24/08/2026, feita por zip, não tem manifesto — sem ele não dá para
|
|
189
|
+
> separar o que é seu por lista. Nesse caso rode `npx iron-edge install` e siga o **CASO B**
|
|
190
|
+
> da mensagem que aparece.
|
|
160
191
|
- **Remover — nesta ordem, porque a lista do que remover mora dentro da pasta que some:**
|
|
161
192
|
|
|
162
193
|
1. **Primeiro** tire uma cópia do manifesto para fora:
|
|
@@ -164,7 +195,7 @@ O instalador **nunca sobrescreve** o que já está em `~/.claude/` e em `~/.iron
|
|
|
164
195
|
cp ~/.iron/install-manifest.json ./iron-manifest.json
|
|
165
196
|
```
|
|
166
197
|
(Guarda na pasta em que você está. `~/Desktop` nem sempre existe — em servidor e container, não existe.)
|
|
167
|
-
2. Abra essa cópia e olhe o campo `files` — é a lista exata do que o pacote escreveu em `~/.claude/agents/` e `~/.claude/
|
|
198
|
+
2. Abra essa cópia e olhe o campo `files` — é a lista exata do que o pacote escreveu em `~/.claude/agents/`, `~/.claude/skills/` e `~/.claude/commands/`.
|
|
168
199
|
3. Apague **só** esses arquivos. O que você criou não está na lista e fica.
|
|
169
200
|
4. **Por último**, apague a pasta `~/.iron/` inteira.
|
|
170
201
|
|
package/bin/iron-edge.js
CHANGED
|
@@ -10,8 +10,12 @@
|
|
|
10
10
|
* imprime versao, edicao, commit e os
|
|
11
11
|
* arquivos que o usuario alterou
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* update [--dry-run] [--source <dir>] atualiza uma instalacao existente: troca SO os
|
|
14
|
+
* arquivos cujo hash bate com o manifesto — os que
|
|
15
|
+
* ninguem alterou — e preserva o resto
|
|
16
|
+
*
|
|
17
|
+
* Nao existe 'uninstall' automatico: remover continua sendo operacao conduzida, com o
|
|
18
|
+
* manifesto na mao (o README descreve o procedimento).
|
|
15
19
|
*/
|
|
16
20
|
const fs = require('fs');
|
|
17
21
|
const path = require('path');
|
|
@@ -35,6 +39,14 @@ if (cmd === 'install') {
|
|
|
35
39
|
const r = spawnSync(process.execPath, args, { stdio: 'inherit' });
|
|
36
40
|
process.exit(r.status ?? 1);
|
|
37
41
|
|
|
42
|
+
} else if (cmd === 'update') {
|
|
43
|
+
// atualiza uma instalacao existente: troca so' o que esta identico ao manifesto.
|
|
44
|
+
const pkgRoot = path.join(__dirname, '..');
|
|
45
|
+
const args = [path.join(pkgRoot, 'installer', 'update.js'), ...rest];
|
|
46
|
+
if (!rest.includes('--source')) args.push('--source', pkgRoot);
|
|
47
|
+
const r = spawnSync(process.execPath, args, { stdio: 'inherit' });
|
|
48
|
+
process.exit(r.status ?? 1);
|
|
49
|
+
|
|
38
50
|
} else if (cmd === 'verify') {
|
|
39
51
|
const manPath = path.join(HOME, '.iron', 'install-manifest.json');
|
|
40
52
|
if (!fs.existsSync(manPath)) {
|
|
@@ -69,6 +81,6 @@ if (cmd === 'install') {
|
|
|
69
81
|
process.exit(alterados.length + sumidos.length === 0 ? 0 : 1);
|
|
70
82
|
|
|
71
83
|
} else {
|
|
72
|
-
console.log('uso: iron-edge <install|verify> [--dry-run] [--source <dir>]');
|
|
84
|
+
console.log('uso: iron-edge <install|update|verify> [--dry-run] [--source <dir>]');
|
|
73
85
|
process.exit(cmd ? 1 : 0);
|
|
74
86
|
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Os arquivos que o Iron GERA na maquina (nao vem do payload): config, atalho, integracoes.
|
|
3
|
+
*
|
|
4
|
+
* Por que este arquivo existe: o `install` criava esses arquivos e o `update` nao sabia deles.
|
|
5
|
+
* Resultado medido: quem atualizasse do 2.0.2 pro 2.1.0 NAO ganhava o atalho `/iron`, porque
|
|
6
|
+
* ele e' gerado, nao copiado. A correcao obvia — repetir a geracao dentro do update — seria o
|
|
7
|
+
* defeito de propagacao que este pacote passou um ciclo inteiro cacando: duas copias que
|
|
8
|
+
* divergem no dia em que alguem editar so' uma.
|
|
9
|
+
*
|
|
10
|
+
* Entao a lista mora aqui, e os dois scripts leem daqui.
|
|
11
|
+
*
|
|
12
|
+
* `sobrescrever`:
|
|
13
|
+
* false → e' do usuario depois de criado. Nunca se toca (config.yaml, shopify.yaml).
|
|
14
|
+
* true → e' do pacote. O update troca, MAS so' se o hash bater com o manifesto; se a
|
|
15
|
+
* pessoa editou, ela ganha aviso e o arquivo dela fica.
|
|
16
|
+
*/
|
|
17
|
+
'use strict';
|
|
18
|
+
const path = require('path');
|
|
19
|
+
|
|
20
|
+
function arquivosGerados({ IRON_DIR, CLAUDE_DIR }) {
|
|
21
|
+
const COMMANDS = path.join(CLAUDE_DIR, 'commands');
|
|
22
|
+
return [
|
|
23
|
+
{
|
|
24
|
+
dest: path.join(IRON_DIR, 'config.yaml'),
|
|
25
|
+
oQueE: 'a config inicial',
|
|
26
|
+
sobrescrever: false,
|
|
27
|
+
conteudo: [
|
|
28
|
+
'# Iron Edge - configuracao. Tudo aqui e opcional, mas os dois primeiros blocos mudam',
|
|
29
|
+
'# muito a qualidade da resposta. Edite a mao, ou peca ao Iron: "configura o Iron pra mim".',
|
|
30
|
+
'',
|
|
31
|
+
'# QUEM E VOCE. Sem isto, tudo que o Iron responder sai generico.',
|
|
32
|
+
'owner: "" # seu nome',
|
|
33
|
+
'businesses: [] # ex: ["loja de roupa", "consultoria de trafego"]',
|
|
34
|
+
'',
|
|
35
|
+
'# O COFRE - a memoria do Iron entre uma conversa e outra.',
|
|
36
|
+
'#',
|
|
37
|
+
'# SEM cofre, cada conversa comeca do zero: o Iron nao lembra dos seus projetos, do que',
|
|
38
|
+
'# ficou pendente, nem do que voce decidiu ontem. COM cofre, ele le tudo isso antes de',
|
|
39
|
+
'# trabalhar. E uma pasta de arquivos .md comuns, na SUA maquina - abre no Obsidian, no',
|
|
40
|
+
'# VS Code ou em qualquer editor. Nada e enviado pra lugar nenhum.',
|
|
41
|
+
'#',
|
|
42
|
+
'# Ainda nao tem? Peca ao Iron: "cria meu cofre". Ele monta a estrutura no lugar que',
|
|
43
|
+
'# voce escolher e preenche este campo sozinho. Leva um minuto.',
|
|
44
|
+
'vault_path: "" # ex: ~/Documents/Cofre',
|
|
45
|
+
'',
|
|
46
|
+
'# focus: dev # prioriza o iron-eng (engenharia de software) no roteamento.',
|
|
47
|
+
'# # descomente se o seu uso principal for programar.',
|
|
48
|
+
'',
|
|
49
|
+
].join('\n'),
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
dest: path.join(COMMANDS, 'iron.md'),
|
|
53
|
+
oQueE: 'o atalho iron',
|
|
54
|
+
sobrescrever: true,
|
|
55
|
+
conteudo: [
|
|
56
|
+
'---',
|
|
57
|
+
'description: Iron Edge - chama o orquestrador master. Ele entende o pedido, decide qual equipe resolve e aciona os especialistas certos.',
|
|
58
|
+
'---',
|
|
59
|
+
'',
|
|
60
|
+
'Leia o arquivo `~/.claude/agents/iron-ceo.md` por inteiro e assuma aquele papel agora,',
|
|
61
|
+
'seguindo a sequencia de ativacao descrita la (ler o config, ler o cofre se houver, ler o',
|
|
62
|
+
'mapa de roteamento, e so entao saudar).',
|
|
63
|
+
'',
|
|
64
|
+
'Nao resuma o arquivo nem trabalhe de memoria: leia o conteudo real, porque ele muda quando',
|
|
65
|
+
'o pacote e atualizado.',
|
|
66
|
+
'',
|
|
67
|
+
'Pedido do usuario: $ARGUMENTS',
|
|
68
|
+
'',
|
|
69
|
+
'(Sem pedido depois do comando, apenas ative e pergunte qual e a missao.)',
|
|
70
|
+
'',
|
|
71
|
+
'Este atalho atende tanto por `/iron` quanto pela palavra `iron` sozinha.',
|
|
72
|
+
'',
|
|
73
|
+
].join('\n'),
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
dest: path.join(IRON_DIR, 'secrets', 'LEIA-ME.md'),
|
|
77
|
+
oQueE: 'o LEIA-ME de secrets',
|
|
78
|
+
sobrescrever: true,
|
|
79
|
+
conteudo: [
|
|
80
|
+
'# secrets/',
|
|
81
|
+
'',
|
|
82
|
+
'Pasta para as SUAS credenciais, se um dia voce conectar alguma integracao.',
|
|
83
|
+
'**Ela nasce vazia e o Iron nao escreve nada aqui sozinho.**',
|
|
84
|
+
'',
|
|
85
|
+
'Nada nesta pasta e enviado para lugar nenhum: e arquivo no seu disco, como',
|
|
86
|
+
'qualquer outro. Se voce nunca conectar integracao, ela fica vazia para sempre —',
|
|
87
|
+
'e pode apagar sem quebrar nada.',
|
|
88
|
+
'',
|
|
89
|
+
].join('\n'),
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
dest: path.join(IRON_DIR, 'integrations', 'LEIA-ME.md'),
|
|
93
|
+
oQueE: 'o LEIA-ME de integrations',
|
|
94
|
+
sobrescrever: true,
|
|
95
|
+
conteudo: [
|
|
96
|
+
'# integrations/',
|
|
97
|
+
'',
|
|
98
|
+
'Configuracao das integracoes opcionais (por enquanto, so o Shopify via Iron Office).',
|
|
99
|
+
'O `shopify.yaml` nasce com `enabled: false`: nada e ligado sem voce ligar.',
|
|
100
|
+
'',
|
|
101
|
+
].join('\n'),
|
|
102
|
+
},
|
|
103
|
+
];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
module.exports = { arquivosGerados };
|
package/installer/install.js
CHANGED
|
@@ -58,8 +58,9 @@ const IRON_DIR = path.join(HOME, '.iron');
|
|
|
58
58
|
const CLAUDE_DIR = path.join(HOME, '.claude');
|
|
59
59
|
const AGENTS_DEST = path.join(CLAUDE_DIR, 'agents');
|
|
60
60
|
const SKILLS_DEST = path.join(CLAUDE_DIR, 'skills');
|
|
61
|
+
const COMMANDS_DEST = path.join(CLAUDE_DIR, 'commands');
|
|
61
62
|
|
|
62
|
-
const ALLOWED_ROOTS = [IRON_DIR, AGENTS_DEST, SKILLS_DEST];
|
|
63
|
+
const ALLOWED_ROOTS = [IRON_DIR, AGENTS_DEST, SKILLS_DEST, COMMANDS_DEST];
|
|
63
64
|
|
|
64
65
|
const WIN_RESERVED = new Set([
|
|
65
66
|
'CON','PRN','AUX','NUL',
|
|
@@ -289,7 +290,7 @@ Da pra separar o que e seu por lista:
|
|
|
289
290
|
O que NAO estiver la foi voce que criou.
|
|
290
291
|
3. Guarde o que for seu (agente proprio, skill propria, o que voce editou).
|
|
291
292
|
"npx iron-edge verify" mostra o que voce alterou em relacao ao manifesto.
|
|
292
|
-
4. Apague em ~/.claude/agents e ~/.claude/
|
|
293
|
+
4. Apague em ~/.claude/agents, ~/.claude/skills e ~/.claude/commands SO os que estao na
|
|
293
294
|
lista. Depois, POR ULTIMO, apague a pasta ~/.iron inteira.
|
|
294
295
|
5. Rode a instalacao de novo.
|
|
295
296
|
|
|
@@ -446,28 +447,6 @@ log(` ${manifest.length} arquivos copiados.`);
|
|
|
446
447
|
|
|
447
448
|
for (const d of [path.join(IRON_DIR, 'integrations'), path.join(IRON_DIR, 'secrets')]) {
|
|
448
449
|
criarPasta(d, 'as pastas de estado');
|
|
449
|
-
|
|
450
|
-
// As duas pastas nascem vazias e com nome que gera duvida ("secrets" assusta quem vai por
|
|
451
|
-
// dado de negocio). Um LEIAME em cada uma explica pra que servem — pasta vazia sem
|
|
452
|
-
// explicacao foi achado de um leitor sem contexto nenhum.
|
|
453
|
-
escrever(path.join(IRON_DIR, 'secrets', 'LEIA-ME.md'), [
|
|
454
|
-
'# secrets/',
|
|
455
|
-
'',
|
|
456
|
-
'Pasta para as SUAS credenciais, se um dia voce conectar alguma integracao.',
|
|
457
|
-
'**Ela nasce vazia e o Iron nao escreve nada aqui sozinho.**',
|
|
458
|
-
'',
|
|
459
|
-
'Nada nesta pasta e enviado para lugar nenhum: e arquivo no seu disco, como',
|
|
460
|
-
'qualquer outro. Se voce nunca conectar integracao, ela fica vazia para sempre —',
|
|
461
|
-
'e pode apagar sem quebrar nada.',
|
|
462
|
-
''
|
|
463
|
-
].join('\n'), 'o LEIA-ME de secrets');
|
|
464
|
-
escrever(path.join(IRON_DIR, 'integrations', 'LEIA-ME.md'), [
|
|
465
|
-
'# integrations/',
|
|
466
|
-
'',
|
|
467
|
-
'Configuracao das integracoes opcionais (por enquanto, so o Shopify via Iron Office).',
|
|
468
|
-
'O `shopify.yaml` nasce com `enabled: false`: nada e ligado sem voce ligar.',
|
|
469
|
-
''
|
|
470
|
-
].join('\n'), 'o LEIA-ME de integrations');
|
|
471
450
|
}
|
|
472
451
|
|
|
473
452
|
// memoria por cargo do squad-engineering: pasta vazia, FORA do manifesto de propósito.
|
|
@@ -478,31 +457,6 @@ criarPasta(path.join(IRON_DIR, 'memory', 'squad-engineering'), 'a pasta de memor
|
|
|
478
457
|
const shopifyYaml = path.join(IRON_DIR, 'integrations', 'shopify.yaml');
|
|
479
458
|
escrever(shopifyYaml, 'enabled: false\nendpoint: ""\napi_key_file: ""\n', 'a config da integracao');
|
|
480
459
|
|
|
481
|
-
const configYaml = path.join(IRON_DIR, 'config.yaml');
|
|
482
|
-
escrever(configYaml, [
|
|
483
|
-
'# Iron Edge - configuracao. Tudo aqui e opcional, mas os dois primeiros blocos mudam',
|
|
484
|
-
'# muito a qualidade da resposta. Edite a mao, ou peca ao Iron: "configura o Iron pra mim".',
|
|
485
|
-
'',
|
|
486
|
-
'# QUEM E VOCE. Sem isto, tudo que o Iron responder sai generico.',
|
|
487
|
-
'owner: "" # seu nome',
|
|
488
|
-
'businesses: [] # ex: ["loja de roupa", "consultoria de trafego"]',
|
|
489
|
-
'',
|
|
490
|
-
'# O COFRE - a memoria do Iron entre uma conversa e outra.',
|
|
491
|
-
'#',
|
|
492
|
-
'# SEM cofre, cada conversa comeca do zero: o Iron nao lembra dos seus projetos, do que',
|
|
493
|
-
'# ficou pendente, nem do que voce decidiu ontem. COM cofre, ele le tudo isso antes de',
|
|
494
|
-
'# trabalhar. E uma pasta de arquivos .md comuns, na SUA maquina - abre no Obsidian, no',
|
|
495
|
-
'# VS Code ou em qualquer editor. Nada e enviado pra lugar nenhum.',
|
|
496
|
-
'#',
|
|
497
|
-
'# Ainda nao tem? Peca ao Iron: "cria meu cofre". Ele monta a estrutura no lugar que',
|
|
498
|
-
'# voce escolher e preenche este campo sozinho. Leva um minuto.',
|
|
499
|
-
'vault_path: "" # ex: ~/Documents/Cofre',
|
|
500
|
-
'',
|
|
501
|
-
'# focus: dev # prioriza o iron-eng (engenharia de software) no roteamento.',
|
|
502
|
-
'# # descomente se o seu uso principal for programar.',
|
|
503
|
-
''
|
|
504
|
-
].join('\n'), 'a config inicial');
|
|
505
|
-
|
|
506
460
|
const installedAt = new Date().toISOString();
|
|
507
461
|
|
|
508
462
|
// PATCH 7: gerados entram no manifesto com generated:true
|
|
@@ -518,13 +472,21 @@ const installManifest = {
|
|
|
518
472
|
platform: process.platform,
|
|
519
473
|
node: process.version,
|
|
520
474
|
home: HOME,
|
|
521
|
-
scopes: { iron_root: IRON_DIR, claude_agents: AGENTS_DEST, claude_skills: SKILLS_DEST },
|
|
475
|
+
scopes: { iron_root: IRON_DIR, claude_agents: AGENTS_DEST, claude_skills: SKILLS_DEST, claude_commands: COMMANDS_DEST },
|
|
522
476
|
files: manifest // sera reescrito abaixo ja com os gerados
|
|
523
477
|
};
|
|
524
478
|
|
|
525
|
-
//
|
|
479
|
+
// Os arquivos GERADOS (config, atalho, LEIA-ME) vivem em installer/gerados.js — um lugar so',
|
|
480
|
+
// lido tambem pelo `update`. Antes ficavam escritos aqui dentro, e o update nao sabia deles:
|
|
481
|
+
// quem atualizasse nao ganhava o atalho /iron. Duas copias divergem; uma lista nao.
|
|
482
|
+
const { arquivosGerados } = require('./gerados');
|
|
483
|
+
let configYaml = null;
|
|
484
|
+
for (const g of arquivosGerados({ IRON_DIR, CLAUDE_DIR })) {
|
|
485
|
+
escrever(g.dest, g.conteudo, g.oQueE);
|
|
486
|
+
addGenerated(g.dest);
|
|
487
|
+
if (g.dest.endsWith('config.yaml')) configYaml = g.dest;
|
|
488
|
+
}
|
|
526
489
|
addGenerated(shopifyYaml);
|
|
527
|
-
addGenerated(configYaml);
|
|
528
490
|
|
|
529
491
|
const metadataPath = path.join(IRON_DIR, 'metadata.json');
|
|
530
492
|
escrever(metadataPath,
|
|
@@ -579,11 +541,15 @@ log(' PROXIMO PASSO');
|
|
|
579
541
|
log('--------------------------------');
|
|
580
542
|
log('1. Reabra o Claude Code. Agente novo so aparece em sessao nova.');
|
|
581
543
|
log('');
|
|
582
|
-
log('2. Chame o orquestrador
|
|
544
|
+
log('2. Chame o orquestrador. Escreva so isto:');
|
|
545
|
+
log('');
|
|
546
|
+
log(' iron');
|
|
583
547
|
log('');
|
|
584
|
-
log('
|
|
548
|
+
log(' So a palavra, sem barra e sem traco. Se preferir a barra, /iron faz o mesmo,');
|
|
549
|
+
log(' e da pra ja mandar o pedido junto: iron preciso da copy desse anuncio');
|
|
585
550
|
log('');
|
|
586
|
-
log('
|
|
551
|
+
log(' Os outros 43 agentes se chamam PEDINDO ("usa o iron-copy").');
|
|
552
|
+
log(' As 14 SKILLS sao de barra: /gate /salvar /brandkit /escada ...');
|
|
587
553
|
log('');
|
|
588
554
|
log('3. Ligue a memoria. Diga a ele:');
|
|
589
555
|
log('');
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* iron-edge update — atualiza uma instalacao existente SEM refazer tudo.
|
|
4
|
+
*
|
|
5
|
+
* Por que isto existe: ate a versao 2.1.0 nao havia caminho de atualizacao. O instalador
|
|
6
|
+
* abortava em qualquer conflito e mandava a pessoa mover tres pastas, reinstalar e preencher
|
|
7
|
+
* o config de novo. O argumento era "nunca sobrescrever, pra nao destruir o que e' seu" — e
|
|
8
|
+
* era bom, ENQUANTO nao havia manifesto. Com manifesto e hash, da' pra separar exatamente o
|
|
9
|
+
* que veio do pacote do que e' da pessoa: numa instalacao tipica, 2120 dos 2123 arquivos estao
|
|
10
|
+
* byte a byte identicos ao pacote. Trocar aqueles e' matematicamente seguro.
|
|
11
|
+
*
|
|
12
|
+
* A REGRA DESTE SCRIPT, em uma frase: so' se troca arquivo cujo hash bate com o manifesto.
|
|
13
|
+
* Hash diferente = a pessoa mexeu = nao se toca, e avisa.
|
|
14
|
+
*
|
|
15
|
+
* Seguranca em duas fases, pra nunca deixar a instalacao pela metade:
|
|
16
|
+
* Fase 1 — escreve tudo como `<destino>.iron-new`. Nada que esta' em uso muda.
|
|
17
|
+
* Fase 2 — renomeia para o lugar. Se falhar no meio, desfaz os que ja' foram.
|
|
18
|
+
*/
|
|
19
|
+
const fs = require('fs');
|
|
20
|
+
const path = require('path');
|
|
21
|
+
const os = require('os');
|
|
22
|
+
const crypto = require('crypto');
|
|
23
|
+
|
|
24
|
+
const HOME = os.homedir();
|
|
25
|
+
const IRON_DIR = path.join(HOME, '.iron');
|
|
26
|
+
const CLAUDE_DIR = path.join(HOME, '.claude');
|
|
27
|
+
const MANIFEST = path.join(IRON_DIR, 'install-manifest.json');
|
|
28
|
+
|
|
29
|
+
const args = process.argv.slice(2);
|
|
30
|
+
const DRY = args.includes('--dry-run');
|
|
31
|
+
const srcIdx = args.indexOf('--source');
|
|
32
|
+
const SOURCE = srcIdx >= 0 && args[srcIdx + 1] ? path.resolve(args[srcIdx + 1]) : path.join(__dirname, '..');
|
|
33
|
+
|
|
34
|
+
const log = (m) => console.log(m);
|
|
35
|
+
function abort(msg) {
|
|
36
|
+
console.error('\n[ABORTADO] ' + msg);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function sha256(file) {
|
|
41
|
+
return crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Compara caminho por caminho REAL, nao por string. `path.resolve` nao desfaz symlink, e o
|
|
45
|
+
// manifesto grava o caminho ja resolvido: numa maquina onde o HOME esta atras de um link
|
|
46
|
+
// (macOS com /tmp, perfil corporativo redirecionado) NADA casaria, e o update concluiria que
|
|
47
|
+
// precisa adicionar os 2120 arquivos e remover os 2119 antigos. O --dry-run pegou isso antes
|
|
48
|
+
// de qualquer escrita — que e' exatamente pra isso que ele existe.
|
|
49
|
+
function norm(p) {
|
|
50
|
+
let alvo = path.resolve(p);
|
|
51
|
+
let resto = '';
|
|
52
|
+
for (;;) {
|
|
53
|
+
try { return path.join(fs.realpathSync(alvo), resto); }
|
|
54
|
+
catch (e) {
|
|
55
|
+
const pai = path.dirname(alvo);
|
|
56
|
+
if (pai === alvo) return path.resolve(p);
|
|
57
|
+
resto = path.join(path.basename(alvo), resto);
|
|
58
|
+
alvo = pai;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ---------------------------------------------------------------- payload novo
|
|
64
|
+
// Mesmo mapeamento do install.js: o que vai pra ~/.iron e o que vai pra ~/.claude.
|
|
65
|
+
function destinoDe(rel) {
|
|
66
|
+
const norm = rel.split(path.sep).join('/');
|
|
67
|
+
if (norm.startsWith('agents/')) return path.join(CLAUDE_DIR, 'agents', norm.slice('agents/'.length));
|
|
68
|
+
if (norm.startsWith('skills/')) return path.join(CLAUDE_DIR, 'skills', norm.slice('skills/'.length));
|
|
69
|
+
if (norm === 'iron-routing-map.md') return path.join(IRON_DIR, 'iron-routing-map.md');
|
|
70
|
+
if (norm.startsWith('squads/')) return path.join(IRON_DIR, norm.slice('squads/'.length));
|
|
71
|
+
if (norm.startsWith('docs/')) return path.join(IRON_DIR, 'docs', norm.slice('docs/'.length));
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function varrerPayload(dir, base = '') {
|
|
76
|
+
const out = [];
|
|
77
|
+
for (const e of fs.readdirSync(path.join(SOURCE, dir), { withFileTypes: true })) {
|
|
78
|
+
const rel = base ? `${base}/${e.name}` : `${dir}/${e.name}`;
|
|
79
|
+
if (e.isSymbolicLink()) continue;
|
|
80
|
+
if (e.isDirectory()) out.push(...varrerPayload(path.join(dir, e.name), rel));
|
|
81
|
+
else out.push(rel);
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ---------------------------------------------------------------- pre-voo
|
|
87
|
+
log('================================');
|
|
88
|
+
log(' Iron Edge - Atualizador');
|
|
89
|
+
log('================================');
|
|
90
|
+
log('HOME : ' + HOME);
|
|
91
|
+
log('Origem : ' + SOURCE);
|
|
92
|
+
if (DRY) log('MODO : --dry-run (nada sera escrito)');
|
|
93
|
+
log('');
|
|
94
|
+
|
|
95
|
+
if (!fs.existsSync(MANIFEST)) {
|
|
96
|
+
console.error(`Nao ha manifesto em ${MANIFEST}.`);
|
|
97
|
+
console.error('');
|
|
98
|
+
console.error('Isto significa que esta instalacao e ANTERIOR ao manifesto (veio de zip), e nao');
|
|
99
|
+
console.error('da pra separar por lista o que e do pacote do que e seu. O caminho, nesse caso, e');
|
|
100
|
+
console.error('o manual: rode "npx iron-edge install" e siga o CASO B da mensagem que aparece.');
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
let man;
|
|
105
|
+
try { man = JSON.parse(fs.readFileSync(MANIFEST, 'utf8')); }
|
|
106
|
+
catch (e) { abort(`o manifesto existe mas nao e JSON valido: ${e.message}`); }
|
|
107
|
+
|
|
108
|
+
const antigos = new Map(); // dest -> entrada do manifesto
|
|
109
|
+
for (const f of man.files || []) {
|
|
110
|
+
const dest = f.dest || f.path;
|
|
111
|
+
if (dest) antigos.set(norm(dest), f);
|
|
112
|
+
}
|
|
113
|
+
log(`Instalacao encontrada: ${antigos.size} arquivos no manifesto`);
|
|
114
|
+
if (man.label) log(` edicao: ${man.label}`);
|
|
115
|
+
if (man.installed_at) log(` instalada em: ${man.installed_at}`);
|
|
116
|
+
|
|
117
|
+
for (const must of ['agents', 'iron-routing-map.md']) {
|
|
118
|
+
if (!fs.existsSync(path.join(SOURCE, must))) abort(`origem invalida: faltou "${must}" em ${SOURCE}`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// payload novo, mapeado por destino
|
|
122
|
+
const novos = new Map(); // dest -> caminho de origem
|
|
123
|
+
for (const dir of ['agents', 'skills', 'squads', 'docs']) {
|
|
124
|
+
if (!fs.existsSync(path.join(SOURCE, dir))) continue;
|
|
125
|
+
for (const rel of varrerPayload(dir)) {
|
|
126
|
+
const d = destinoDe(rel);
|
|
127
|
+
if (d) novos.set(norm(d), path.join(SOURCE, rel));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
{
|
|
131
|
+
const d = destinoDe('iron-routing-map.md');
|
|
132
|
+
novos.set(norm(d), path.join(SOURCE, 'iron-routing-map.md'));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ---------------------------------------------------------------- classificar
|
|
136
|
+
const plano = { trocar: [], adicionar: [], restaurar: [], preservar: [], remover: [], manter: [], gerar: [] };
|
|
137
|
+
|
|
138
|
+
for (const [dest, origem] of novos) {
|
|
139
|
+
const ent = antigos.get(dest);
|
|
140
|
+
if (!ent) { plano.adicionar.push({ dest, origem }); continue; }
|
|
141
|
+
if (ent.generated) { plano.manter.push({ dest, motivo: 'gerado na instalacao (config, manifesto)' }); continue; }
|
|
142
|
+
if (!fs.existsSync(dest)) { plano.restaurar.push({ dest, origem }); continue; }
|
|
143
|
+
const atual = sha256(dest);
|
|
144
|
+
// Arquivo que a pessoa editou fica marcado no manifesto. Sem essa marca, o update anterior
|
|
145
|
+
// gravava o hash NOVO (o dela) e o update SEGUINTE via hash batendo, concluia "intacto do
|
|
146
|
+
// pacote" e sobrescrevia: a edicao sobrevivia a uma atualizacao e morria calada na proxima.
|
|
147
|
+
// Medido em teste, nao deduzido. A marca so' sai quando o arquivo volta a ser identico ao
|
|
148
|
+
// pacote — ou seja, quando a propria pessoa desfez a edicao.
|
|
149
|
+
if (ent.user_modified) {
|
|
150
|
+
if (sha256(origem) === atual) { plano.manter.push({ dest, motivo: 'voltou a ser igual ao pacote' }); continue; }
|
|
151
|
+
plano.preservar.push({ dest, marcado: true });
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (atual !== ent.sha256) { plano.preservar.push({ dest }); continue; }
|
|
155
|
+
if (sha256(origem) === atual) { plano.manter.push({ dest, motivo: 'ja esta na versao nova' }); continue; }
|
|
156
|
+
plano.trocar.push({ dest, origem });
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
for (const [dest, ent] of antigos) {
|
|
160
|
+
if (novos.has(dest)) continue;
|
|
161
|
+
if (ent.generated) continue;
|
|
162
|
+
if (!fs.existsSync(dest)) continue;
|
|
163
|
+
if (sha256(dest) === ent.sha256) plano.remover.push({ dest });
|
|
164
|
+
else plano.preservar.push({ dest, obsoleto: true });
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Os arquivos GERADOS (config, atalho /iron, LEIA-ME) nao vem do payload — o instalador os
|
|
168
|
+
// escreve. Sem este bloco, quem ATUALIZAVA nao ganhava capacidade nova gerada: foi medido no
|
|
169
|
+
// 2.0.2 -> 2.1.0, em que o /iron simplesmente nao aparecia. A lista vem de gerados.js, a
|
|
170
|
+
// mesma que o install le, pra nao existirem duas versoes do mesmo conteudo.
|
|
171
|
+
const { arquivosGerados } = require('./gerados');
|
|
172
|
+
const GERADOS = arquivosGerados({ IRON_DIR, CLAUDE_DIR });
|
|
173
|
+
for (const g of GERADOS) {
|
|
174
|
+
const dest = norm(g.dest);
|
|
175
|
+
const ent = antigos.get(dest);
|
|
176
|
+
const existe = fs.existsSync(g.dest);
|
|
177
|
+
if (!existe) { plano.gerar.push({ ...g, motivo: 'novo nesta versao' }); continue; }
|
|
178
|
+
if (!g.sobrescrever) { plano.manter.push({ dest: g.dest, motivo: 'seu (config/integracao)' }); continue; }
|
|
179
|
+
const atual = sha256(g.dest);
|
|
180
|
+
if (ent && atual !== ent.sha256) { plano.preservar.push({ dest: g.dest }); continue; }
|
|
181
|
+
if (atual === crypto.createHash('sha256').update(g.conteudo).digest('hex')) {
|
|
182
|
+
plano.manter.push({ dest: g.dest, motivo: 'ja esta na versao nova' });
|
|
183
|
+
} else {
|
|
184
|
+
plano.gerar.push({ ...g, motivo: 'mudou nesta versao' });
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ---------------------------------------------------------------- mostrar
|
|
189
|
+
log('');
|
|
190
|
+
log('--- PLANO ---');
|
|
191
|
+
log(` trocar : ${plano.trocar.length} (identicos ao pacote antigo, mudaram na versao nova)`);
|
|
192
|
+
log(` adicionar : ${plano.adicionar.length} (nao existiam)`);
|
|
193
|
+
log(` restaurar : ${plano.restaurar.length} (estavam no manifesto e sumiram do disco)`);
|
|
194
|
+
log(` remover : ${plano.remover.length} (a versao nova nao traz mais, e estavam intactos)`);
|
|
195
|
+
log(` preservar : ${plano.preservar.length} (VOCE alterou — nao sao tocados)`);
|
|
196
|
+
log(` manter : ${plano.manter.length} (ja atualizados, ou gerados na sua instalacao)`);
|
|
197
|
+
log(` gerar : ${plano.gerar.length} (config, atalho /iron, LEIA-ME — criados pelo instalador)`);
|
|
198
|
+
for (const g of plano.gerar) log(` + ${g.dest} (${g.motivo})`);
|
|
199
|
+
|
|
200
|
+
if (plano.preservar.length) {
|
|
201
|
+
log('');
|
|
202
|
+
log(' Estes arquivos NAO serao tocados, porque o hash mostra que voce mexeu neles:');
|
|
203
|
+
for (const p of plano.preservar.slice(0, 30))
|
|
204
|
+
log(` ~ ${p.dest}${p.obsoleto ? ' (a versao nova nem traz mais este arquivo)' : ''}${p.marcado ? ' (ja marcado como seu)' : ''}`);
|
|
205
|
+
if (plano.preservar.length > 30) log(` ... e mais ${plano.preservar.length - 30}`);
|
|
206
|
+
log('');
|
|
207
|
+
log(' Se algum deles for edicao sua que voce quer manter, esta tudo certo: fica como esta.');
|
|
208
|
+
log(' Se for edicao que voce ja nao quer, apague o arquivo e rode o update de novo — ele');
|
|
209
|
+
log(' entra em "restaurar" e volta na versao nova.');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
log('');
|
|
213
|
+
log(' Seu config.yaml, seus agentes e skills proprios, e seu cofre NAO entram neste plano.');
|
|
214
|
+
log(' Config e manifesto sao regravados no fim; o resto nem e olhado.');
|
|
215
|
+
|
|
216
|
+
if (DRY) { log(''); log('--dry-run: nada foi escrito.'); process.exit(0); }
|
|
217
|
+
if (!plano.trocar.length && !plano.adicionar.length && !plano.restaurar.length && !plano.remover.length && !plano.gerar.length) {
|
|
218
|
+
log(''); log('Nada a fazer: sua instalacao ja esta na versao deste pacote.');
|
|
219
|
+
process.exit(0);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// ---------------------------------------------------------------- FASE 1: escrever ao lado
|
|
223
|
+
log('');
|
|
224
|
+
log('--- FASE 1: escrevendo ao lado (nada em uso muda ainda) ---');
|
|
225
|
+
const temporarios = [];
|
|
226
|
+
try {
|
|
227
|
+
for (const it of [...plano.trocar, ...plano.adicionar, ...plano.restaurar]) {
|
|
228
|
+
const tmp = it.dest + '.iron-new';
|
|
229
|
+
fs.mkdirSync(path.dirname(it.dest), { recursive: true });
|
|
230
|
+
fs.copyFileSync(it.origem, tmp);
|
|
231
|
+
if (sha256(tmp) !== sha256(it.origem)) throw new Error(`copia divergiu do original: ${it.dest}`);
|
|
232
|
+
temporarios.push({ tmp, dest: it.dest, novo: !fs.existsSync(it.dest) });
|
|
233
|
+
}
|
|
234
|
+
} catch (e) {
|
|
235
|
+
for (const t of temporarios) { try { fs.unlinkSync(t.tmp); } catch (_) {} }
|
|
236
|
+
abort(`falhou ao preparar os arquivos (${e.message}). NADA foi alterado na sua instalacao.`);
|
|
237
|
+
}
|
|
238
|
+
log(` ${temporarios.length} arquivos preparados e conferidos por hash.`);
|
|
239
|
+
|
|
240
|
+
// ---------------------------------------------------------------- FASE 2: trocar de lugar
|
|
241
|
+
log('');
|
|
242
|
+
log('--- FASE 2: colocando no lugar ---');
|
|
243
|
+
const feitos = [];
|
|
244
|
+
try {
|
|
245
|
+
for (const t of temporarios) {
|
|
246
|
+
if (!t.novo) {
|
|
247
|
+
const bak = t.dest + '.iron-old';
|
|
248
|
+
fs.renameSync(t.dest, bak);
|
|
249
|
+
t.bak = bak;
|
|
250
|
+
}
|
|
251
|
+
fs.renameSync(t.tmp, t.dest);
|
|
252
|
+
feitos.push(t);
|
|
253
|
+
}
|
|
254
|
+
} catch (e) {
|
|
255
|
+
log(' ERRO no meio da troca. Desfazendo...');
|
|
256
|
+
for (const f of feitos.reverse()) {
|
|
257
|
+
try {
|
|
258
|
+
fs.unlinkSync(f.dest);
|
|
259
|
+
if (f.bak) fs.renameSync(f.bak, f.dest);
|
|
260
|
+
} catch (_) {}
|
|
261
|
+
}
|
|
262
|
+
for (const t of temporarios) { try { fs.unlinkSync(t.tmp); } catch (_) {} }
|
|
263
|
+
abort(`falhou ao trocar os arquivos (${e.message}). A instalacao foi devolvida ao estado anterior.`);
|
|
264
|
+
}
|
|
265
|
+
for (const f of feitos) { if (f.bak) { try { fs.unlinkSync(f.bak); } catch (_) {} } }
|
|
266
|
+
log(` ${feitos.length} arquivos atualizados.`);
|
|
267
|
+
|
|
268
|
+
let removidos = 0;
|
|
269
|
+
for (const r of plano.remover) { try { fs.unlinkSync(r.dest); removidos++; } catch (_) {} }
|
|
270
|
+
if (removidos) log(` ${removidos} arquivos que a versao nova nao traz mais foram removidos.`);
|
|
271
|
+
|
|
272
|
+
// gerados: escritos direto (sao pequenos e nao ha versao "em uso" a proteger)
|
|
273
|
+
let geradosOk = 0;
|
|
274
|
+
for (const g of plano.gerar) {
|
|
275
|
+
try {
|
|
276
|
+
fs.mkdirSync(path.dirname(g.dest), { recursive: true });
|
|
277
|
+
fs.writeFileSync(g.dest, g.conteudo);
|
|
278
|
+
geradosOk++;
|
|
279
|
+
} catch (e) { log(` aviso: nao consegui gravar ${g.dest} (${e.code || e.message})`); }
|
|
280
|
+
}
|
|
281
|
+
if (geradosOk) log(` ${geradosOk} arquivo(s) gerado(s) desta versao criados (ex: o atalho /iron).`);
|
|
282
|
+
|
|
283
|
+
// ---------------------------------------------------------------- FASE 3: manifesto e config
|
|
284
|
+
log('');
|
|
285
|
+
log('--- FASE 3: manifesto e config ---');
|
|
286
|
+
|
|
287
|
+
const arquivos = [];
|
|
288
|
+
const editados = new Set(plano.preservar.map((x) => norm(x.dest)));
|
|
289
|
+
for (const [dest, origem] of novos) {
|
|
290
|
+
if (!fs.existsSync(dest)) continue;
|
|
291
|
+
const e = {
|
|
292
|
+
rel: path.relative(SOURCE, origem).split(path.sep).join('/'),
|
|
293
|
+
dest,
|
|
294
|
+
sha256: sha256(dest),
|
|
295
|
+
};
|
|
296
|
+
// a marca que impede a proxima atualizacao de comer a edicao da pessoa
|
|
297
|
+
if (editados.has(dest)) e.user_modified = true;
|
|
298
|
+
arquivos.push(e);
|
|
299
|
+
}
|
|
300
|
+
for (const [dest, ent] of antigos) {
|
|
301
|
+
if (novos.has(dest)) continue;
|
|
302
|
+
if (!fs.existsSync(dest)) continue;
|
|
303
|
+
if (GERADOS.some((g) => norm(g.dest) === dest)) continue; // entram abaixo, com hash fresco
|
|
304
|
+
arquivos.push({ ...ent, sha256: sha256(dest), generated: ent.generated || undefined });
|
|
305
|
+
}
|
|
306
|
+
for (const g of GERADOS) {
|
|
307
|
+
if (!fs.existsSync(g.dest)) continue;
|
|
308
|
+
arquivos.push({ rel: path.relative(IRON_DIR, g.dest).split(path.sep).join('/'), dest: g.dest, sha256: sha256(g.dest), generated: true });
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const novoMan = {
|
|
312
|
+
...man,
|
|
313
|
+
updated_at: new Date().toISOString(),
|
|
314
|
+
previous_installed_at: man.installed_at,
|
|
315
|
+
files: arquivos,
|
|
316
|
+
};
|
|
317
|
+
fs.writeFileSync(MANIFEST, JSON.stringify(novoMan, null, 2));
|
|
318
|
+
log(` manifesto regravado: ${arquivos.length} arquivos`);
|
|
319
|
+
|
|
320
|
+
// config: NUNCA sobrescrito. Mas se a versao nova tem campo que o seu nao tem, avisa.
|
|
321
|
+
const cfg = path.join(IRON_DIR, 'config.yaml');
|
|
322
|
+
if (fs.existsSync(cfg)) {
|
|
323
|
+
const seu = fs.readFileSync(cfg, 'utf8');
|
|
324
|
+
const campos = ['owner', 'businesses', 'vault_path'];
|
|
325
|
+
const faltando = campos.filter((c) => !new RegExp(`^\\s*${c}\\s*:`, 'm').test(seu));
|
|
326
|
+
log(' config.yaml: preservado como estava (nao foi tocado).');
|
|
327
|
+
if (faltando.length) {
|
|
328
|
+
log('');
|
|
329
|
+
log(' ATENCAO: a versao nova usa campos que o seu config nao tem:');
|
|
330
|
+
for (const f of faltando) log(` - ${f}`);
|
|
331
|
+
log(' Acrescente a mao, ou peca ao Iron: "atualiza meu config".');
|
|
332
|
+
if (faltando.includes('vault_path') && /obsidian_vault_path/.test(seu))
|
|
333
|
+
log(' (Voce tem "obsidian_vault_path": e o mesmo cofre. O Iron entende os dois nomes.)');
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
log('');
|
|
338
|
+
log('================================');
|
|
339
|
+
log(' Atualizado com sucesso');
|
|
340
|
+
log('================================');
|
|
341
|
+
log('Confira quando quiser: npx iron-edge verify');
|
|
342
|
+
log('Reabra o Claude Code para carregar o que mudou.');
|
|
343
|
+
log('');
|
package/package.json
CHANGED