iron-edge 2.4.4 → 2.4.5

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 CHANGED
@@ -168,12 +168,18 @@ Instalada junto. Depois de `npx iron-edge install`, os dois arquivos abaixo est
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
170
 
171
- # Medir a máquina (Node, git, Claude Code, Iron Edge, chave, plugin, cofre). Com a chave do
171
+ # Medir a máquina (Node, git, Claude Code, Iron Edge, chave, plugin, cofre, atalho). Com a chave do
172
172
  # Iron Office aceita, o resultado acende o checklist da página de Setup do site.
173
173
  npx iron-edge doctor
174
174
 
175
175
  # Instalar já criando um cofre vazio em ~/Iron Cofre (pronto pro Obsidian, nunca sobrescreve)
176
176
  npx iron-edge install --cofre
177
+
178
+ # Ligar o Iron Office: depois do install (ou update), no Mac/Linux, o comando curto é
179
+ ironoffice # = npx iron-edge office (abra um terminal novo na primeira vez)
180
+
181
+ # Instalar sem criar o atalho ironoffice no ~/.zshrc (ou ~/.bashrc)
182
+ npx iron-edge install --sem-atalho
177
183
  ```
178
184
 
179
185
  **Atualizar é um comando. Remover é operação conduzida.**
package/bin/iron-edge.js CHANGED
@@ -18,10 +18,11 @@
18
18
  * Claude Code conversando com o chat do dashboard
19
19
  *
20
20
  * doctor [--json] mede a maquina (Node, git, Claude Code, Iron Edge, chave,
21
- * plugin, cofre) e, com a chave aceita, manda o resultado
21
+ * plugin, cofre, atalho) e, com a chave aceita, manda o resultado
22
22
  * pra pagina de Setup do Iron Office
23
23
  *
24
24
  * install --cofre alem do install, cria ~/Iron Cofre/ (cofre vazio pro Obsidian)
25
+ * install/update --sem-atalho nao cria o alias ironoffice (= office) no ~/.zshrc ou ~/.bashrc
25
26
  *
26
27
  * Nao existe 'uninstall' automatico: remover continua sendo operacao conduzida, com o
27
28
  * manifesto na mao (o README descreve o procedimento).
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * atalho.js — cria o comando curto `ironoffice` (= `npx iron-edge office`) no shell do aluno.
4
+ * Roda no fim do install e do update com sucesso; `--sem-atalho` pula.
5
+ *
6
+ * Regras:
7
+ * - Windows: nao faz nada (o PowerShell nao le .zshrc/.bashrc). O aluno usa `npx iron-edge office`.
8
+ * - escolhe ~/.zshrc se existir, senao ~/.bashrc se existir, senao cria ~/.zshrc (padrao do Mac).
9
+ * - se o arquivo ja tem `alias ironoffice=`, nao mexe: nunca duplica.
10
+ * - so' ACRESCENTA no fim. Nunca remove nem edita linha existente: o arquivo e' do aluno.
11
+ * - escrita atomica: grava ao lado e renomeia por cima. Se o .zshrc e' um link (dotfiles),
12
+ * escreve no arquivo de verdade e o link continua link. Link quebrado: nao mexe.
13
+ * - os bytes do aluno ficam intactos: le e grava em Buffer (um .zshrc em Latin-1 nao e' recodificado).
14
+ * - arquivo que existe e nao da pra ler (permissao, pasta no lugar): nao escreve nada. So' "nao existe"
15
+ * (ENOENT) vale como arquivo novo. Antes, qualquer erro de leitura virava "vazio" e o rename
16
+ * apagava o .zshrc do aluno (gate da onda 9).
17
+ * - dryRun so' diz o que faria.
18
+ *
19
+ * Saida so' em ASCII, como o resto do instalador.
20
+ */
21
+ const fs = require('fs');
22
+ const path = require('path');
23
+ const os = require('os');
24
+
25
+ const LINHA = 'alias ironoffice="npx iron-edge office"';
26
+ const BLOCO = '\n# Iron Edge: atalho do canal do Iron Office\n' + LINHA + '\n';
27
+ const JA_TEM = /^\s*alias\s+ironoffice=/m;
28
+
29
+ function arquivoDoShell(home) {
30
+ for (const nome of ['.zshrc', '.bashrc']) {
31
+ const p = path.join(home, nome);
32
+ if (fs.existsSync(p)) return p;
33
+ }
34
+ return path.join(home, '.zshrc');
35
+ }
36
+
37
+ // Devolve { criado: caminho } | { criaria: caminho }
38
+ // | { pulado: 'windows' | 'ja existe' | 'sem-atalho' | 'sem leitura' | 'link quebrado', arquivo? }
39
+ function criarAtalho(opts) {
40
+ const o = opts || {};
41
+ const platform = o.platform || process.platform;
42
+ if (o.semAtalho) return { pulado: 'sem-atalho' };
43
+ if (platform === 'win32') return { pulado: 'windows' };
44
+ const home = o.HOME || os.homedir();
45
+ const rc = arquivoDoShell(home);
46
+ let link = false;
47
+ try { link = fs.lstatSync(rc).isSymbolicLink(); } catch (_) { /* nao existe */ }
48
+ let real = rc;
49
+ if (link) {
50
+ try { real = fs.realpathSync.native(rc); } catch (_) { return { pulado: 'link quebrado', arquivo: rc }; }
51
+ }
52
+ let atual = Buffer.alloc(0);
53
+ try { atual = fs.readFileSync(real); } catch (e) {
54
+ if (!e || e.code !== 'ENOENT') return { pulado: 'sem leitura', arquivo: rc };
55
+ }
56
+ // latin1 mapeia byte a byte: acha o alias (ASCII) em qualquer codificacao sem estourar
57
+ if (JA_TEM.test(atual.toString('latin1'))) return { pulado: 'ja existe', arquivo: rc };
58
+ if (o.dryRun) return { criaria: rc };
59
+ let modo = 0o644;
60
+ try { modo = fs.statSync(real).mode & 0o777; } catch (_) {}
61
+ const tmp = real + '.iron-tmp-' + process.pid;
62
+ fs.writeFileSync(tmp, Buffer.concat([atual, Buffer.from(BLOCO)]), { mode: modo });
63
+ try { fs.renameSync(tmp, real); } catch (e) { try { fs.unlinkSync(tmp); } catch (_) {} throw e; }
64
+ return { criado: rc };
65
+ }
66
+
67
+ // Linha do relatorio do install/update
68
+ function relatarAtalho(r) {
69
+ if (r.criado) return 'Atalho criado: ironoffice em ' + r.criado + ' (abra um terminal novo)';
70
+ if (r.criaria) return 'criaria o atalho ironoffice em ' + r.criaria;
71
+ if (r.pulado === 'ja existe') return 'Atalho ja existia: ironoffice';
72
+ if (r.pulado === 'windows') return 'Windows: use npx iron-edge office';
73
+ if (r.pulado === 'sem leitura') return 'Atalho nao criado: nao consegui ler ' + r.arquivo + ' (nada foi mexido). Use npx iron-edge office';
74
+ if (r.pulado === 'link quebrado') return 'Atalho nao criado: ' + r.arquivo + ' aponta pra um arquivo que nao existe (nada foi mexido). Use npx iron-edge office';
75
+ return 'Atalho pulado (--sem-atalho): use npx iron-edge office';
76
+ }
77
+
78
+ // Rodado no fim do install/update: nunca derruba a instalacao que ja deu certo.
79
+ function atalhoNoFim(opts) {
80
+ try {
81
+ return relatarAtalho(criarAtalho(opts));
82
+ } catch (e) {
83
+ return 'Atalho nao criado (' + (e.code || e.message) + '): use npx iron-edge office';
84
+ }
85
+ }
86
+
87
+ module.exports = { criarAtalho, relatarAtalho, atalhoNoFim, LINHA };
@@ -4,7 +4,8 @@
4
4
  * pra a pagina de Setup acender cada passo sozinha (o site nao enxerga a maquina).
5
5
  *
6
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).
7
+ * Office (formato + confere com o site), plugin iron-office, cofre (~/Iron Cofre, informativo),
8
+ * atalho ironoffice no .zshrc/.bashrc (informativo).
8
9
  *
9
10
  * Envio: SO' com a chave aceita pelo site (ping = ok) faz POST /api/chat/setup-status. Sem chave,
10
11
  * com chave recusada ou sem rede, o resultado fica so' no terminal. Falhar o envio e' aviso, nao erro.
@@ -90,6 +91,17 @@ function checkPlugin(claudeBin) {
90
91
  return out.includes(PLUGIN) ? item(true, PLUGIN) : item(false, 'falta o plugin: rode npx iron-edge office');
91
92
  }
92
93
 
94
+ // alias ironoffice (installer/atalho.js): no Windows nao existe, entao conta como ok
95
+ function checkAtalho(home) {
96
+ if (IS_WIN) return item(true, 'não se aplica (Windows: npx iron-edge office)');
97
+ for (const nome of ['.zshrc', '.bashrc']) {
98
+ let txt = '';
99
+ try { txt = fs.readFileSync(path.join(home, nome), 'utf8'); } catch (_) { continue; }
100
+ if (/^\s*alias\s+ironoffice=/m.test(txt)) return item(true, 'ironoffice em ~/' + nome);
101
+ }
102
+ return item(false, 'ausente: rode npx iron-edge update');
103
+ }
104
+
93
105
  function checkCofre(home) {
94
106
  const ok = fs.existsSync(path.join(home, 'Iron Cofre', 'CLAUDE.md'));
95
107
  return item(ok, ok ? '~/Iron Cofre' : 'sem cofre pronto (opcional: npx iron-edge install --cofre)');
@@ -111,6 +123,7 @@ async function medir(opts) {
111
123
  key: key.check,
112
124
  plugin: checkPlugin(claude.bin),
113
125
  cofre: checkCofre(home),
126
+ atalho: checkAtalho(home),
114
127
  },
115
128
  ran_at: new Date().toISOString(),
116
129
  };
@@ -138,7 +151,7 @@ async function enviar(result, key, baseUrl) {
138
151
  }
139
152
  }
140
153
 
141
- const ROTULO = { node: 'Node', git: 'git', claude: 'Claude Code', iron_edge: 'Iron Edge', key: 'chave', plugin: 'plugin', cofre: 'cofre' };
154
+ const ROTULO = { node: 'Node', git: 'git', claude: 'Claude Code', iron_edge: 'Iron Edge', key: 'chave', plugin: 'plugin', cofre: 'cofre', atalho: 'atalho' };
142
155
  const OBRIGATORIOS = ['node', 'git', 'claude', 'iron_edge'];
143
156
 
144
157
  async function main(argv, opts) {
@@ -153,7 +166,7 @@ async function main(argv, opts) {
153
166
  }
154
167
  console.log('Iron Edge doctor ' + result.doctor);
155
168
  for (const [k, c] of Object.entries(result.checks)) {
156
- console.log(' ' + (c.ok ? '[ok] ' : (k === 'cofre' ? '[info] ' : '[falta] ')) + ROTULO[k] + ': ' + ascii(c.detail));
169
+ console.log(' ' + (c.ok ? '[ok] ' : (k === 'cofre' || k === 'atalho' ? '[info] ' : '[falta] ')) + ROTULO[k] + ': ' + ascii(c.detail));
157
170
  }
158
171
  console.log('');
159
172
  if (!key && !readKey(o.home || homeDir())) {
@@ -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>] [--cofre]
19
+ * Uso: node install.js [--dry-run] [--source <dir>] [--cofre] [--sem-atalho]
20
20
  */
21
21
 
22
22
  'use strict';
@@ -42,6 +42,7 @@ function abort(msg) {
42
42
  const args = process.argv.slice(2);
43
43
  const DRY_RUN = args.includes('--dry-run');
44
44
  const COFRE = args.includes('--cofre'); // cria ~/Iron Cofre/ no fim (installer/cofre.js)
45
+ const SEM_ATALHO = args.includes('--sem-atalho'); // nao cria o alias ironoffice (installer/atalho.js)
45
46
  const srcIdx = args.indexOf('--source');
46
47
  let SOURCE = srcIdx >= 0 && args[srcIdx + 1] ? path.resolve(args[srcIdx + 1]) : __dirname;
47
48
  // Opera sobre o caminho FISICO: ancestral symlink herdado da maquina (perfil
@@ -337,6 +338,8 @@ if (DRY_RUN) {
337
338
  log('\n--- arquivos que SERAO gerados ---');
338
339
  for (const g of GENERATED) log(' ' + g.dest);
339
340
  if (COFRE) require('./cofre').criarCofre({ home: HOME, dryRun: true });
341
+ log('\n--- atalho ---');
342
+ log(' ' + require('./atalho').atalhoNoFim({ HOME, dryRun: true, semAtalho: SEM_ATALHO }));
340
343
  log('\nDRY-RUN concluido. Nada foi copiado.');
341
344
  process.exit(0);
342
345
  }
@@ -552,6 +555,7 @@ log(`Agentes : ${AGENTS_DEST}`);
552
555
  log(`Skills : ${SKILLS_DEST}`);
553
556
  log(`Nucleo : ${IRON_DIR}`);
554
557
  log(`Manifesto: ${manifestPath}`);
558
+ log(require('./atalho').atalhoNoFim({ HOME, semAtalho: SEM_ATALHO }));
555
559
  log('\n--------------------------------');
556
560
  log(' PROXIMO PASSO');
557
561
  log('--------------------------------');
@@ -37,7 +37,7 @@ function isValidKey(key) {
37
37
  }
38
38
 
39
39
  // Le o arquivo da chave e devolve so' uma chave no formato certo; qualquer outra coisa vira ''.
40
- // O `echo` do passo 4 no Windows pode gravar UTF-16 com BOM: normaliza antes de decidir.
40
+ // O `echo` do passo 2 no Windows pode gravar UTF-16 com BOM: normaliza antes de decidir.
41
41
  function readKey(home) {
42
42
  try {
43
43
  const buf = fs.readFileSync(keyPath(home));
@@ -108,9 +108,9 @@ async function main(argv) {
108
108
  if (!key) {
109
109
  if (keyFileExists()) {
110
110
  console.error('A chave em ' + keyPath() + ' esta invalida (nao tem o formato iok_...).');
111
- console.error('Refaca o passo 4 em ' + ONBOARDING);
111
+ console.error('Refaca o passo 2 (Ligar o Iron) em ' + ONBOARDING);
112
112
  } else {
113
- console.error('Falta a chave do Iron Office. Faca o passo 4 em ' + ONBOARDING);
113
+ console.error('Falta a chave do Iron Office. Faca o passo 2 (Ligar o Iron) em ' + ONBOARDING);
114
114
  }
115
115
  return 1;
116
116
  }
@@ -124,7 +124,7 @@ async function main(argv) {
124
124
  console.log('Iron Office - preparando o canal');
125
125
  const verdict = await ping(key);
126
126
  if (verdict === 'rejected') {
127
- console.error(' [falhou] chave recusada pelo Iron Office: ela foi trocada. Refaca o passo 4 em ' + ONBOARDING);
127
+ console.error(' [falhou] chave recusada pelo Iron Office: ela foi trocada. Refaca o passo 2 (Ligar o Iron) em ' + ONBOARDING);
128
128
  return 1;
129
129
  }
130
130
  if (verdict === 'ok') console.log(' [ok] chave aceita pelo Iron Office');
@@ -28,6 +28,8 @@ const MANIFEST = path.join(IRON_DIR, 'install-manifest.json');
28
28
 
29
29
  const args = process.argv.slice(2);
30
30
  const DRY = args.includes('--dry-run');
31
+ const SEM_ATALHO = args.includes('--sem-atalho'); // nao cria o alias ironoffice (installer/atalho.js)
32
+ const atalho = (dryRun) => require('./atalho').atalhoNoFim({ HOME, dryRun, semAtalho: SEM_ATALHO });
31
33
  const srcIdx = args.indexOf('--source');
32
34
  const SOURCE = srcIdx >= 0 && args[srcIdx + 1] ? path.resolve(args[srcIdx + 1]) : path.join(__dirname, '..');
33
35
 
@@ -292,7 +294,7 @@ log('');
292
294
  relatarConfig();
293
295
 
294
296
 
295
- if (DRY) { log(''); log('--dry-run: nada foi escrito.'); process.exit(0); }
297
+ if (DRY) { log(''); log(atalho(true)); log('--dry-run: nada foi escrito.'); process.exit(0); }
296
298
 
297
299
  // Permissao da pasta de segredos, TAMBEM no update. O chmod entrou no install no ciclo 2 e o
298
300
  // gate mediu a lacuna: quem ja tinha o Iron instalado seguia com `secrets/` em 755, porque o
@@ -385,6 +387,7 @@ const versaoDoPacote = process.env.IRON_EDGE_VERSION ? 'iron-edge-' + process.en
385
387
  const versaoDesatualizada = Boolean(versaoDoPacote) && versaoRegistrada !== versaoDoPacote;
386
388
  if (!plano.trocar.length && !plano.adicionar.length && !plano.restaurar.length && !plano.remover.length && !plano.gerar.length && !plano.preservar.length && !versaoDesatualizada && !metadataReparado) {
387
389
  log(''); log('Nada a fazer: sua instalacao ja esta na versao deste pacote.');
390
+ log(atalho(false));
388
391
  process.exit(0);
389
392
  }
390
393
 
@@ -519,6 +522,7 @@ log('');
519
522
  log('================================');
520
523
  log(' Atualizado com sucesso');
521
524
  log('================================');
525
+ log(atalho(false));
522
526
  log('Confira quando quiser: npx iron-edge verify');
523
527
  log('Reabra o Claude Code para carregar o que mudou.');
524
528
  log('');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iron-edge",
3
- "version": "2.4.4",
3
+ "version": "2.4.5",
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",
@@ -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 && node scripts/test-cofre.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 && node scripts/test-atalho.js",
36
36
  "test:matriz": "node scripts/test-matriz-update.js"
37
37
  },
38
38
  "devDependencies": {