auroq-os 2.6.3 → 2.6.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.
@@ -1,7 +1,7 @@
1
1
  project:
2
2
  name: "Auroq OS"
3
3
  type: greenfield
4
- version: 2.6.3
4
+ version: 2.6.4
5
5
  installedAt: '2026-03-16'
6
6
 
7
7
  ide:
package/CHANGELOG.md CHANGED
@@ -1,3 +1,16 @@
1
+ ## 2.6.4 — 2026-09-29
2
+
3
+ ### Companion com o nome do aluno nao trava mais a atualizacao
4
+
5
+ - Quem deu nome ao Companion no Bootstrap (ex.: "Atlas" → `/atlas-companion`) podia ficar preso em "Sync de skills do Codex falhou" → "arquivo faltando: Codex Companion (.agents/skills/companion/SKILL.md)" → `AUROQ_RESULTADO status=erro`. Tres alunos em 29/09.
6
+ - Causa: o sync do Codex morria inteiro na primeira pasta de skill que nao reconhecia — pasta do negocio movida, copiada ou vinda de outra maquina (marcador com outro caminho), `companion.md` antigo colidindo com `auroq-companion.md`, ou skill feita a mao pelo agente. Como o `--clean` ja tinha apagado a skill `companion` quando o comando base sumiu, ela nunca voltava e a verificacao final derrubava a atualizacao.
7
+ - O sync nao para mais por isso: pasta de skill com o nosso marcador dentro do proprio negocio e reconhecida mesmo depois de mover a pasta; colisao de nome usa o comando oficial e segue; skill sem marcador feita pelo aluno fica como esta, com aviso. A pasta global `~/.agents/skills` continua recusando o que e de outro projeto.
8
+ - `$companion` sempre existe no Codex: sem o comando base, vira atalho do comando com o nome do aluno. `$atlas-companion` continua.
9
+ - A verificacao de integridade aceita o comando com o nome do aluno como o Companion (so ele; os outros arquivos seguem exigidos pelo caminho exato). Quando o sync falha, a manutencao mostra o motivo.
10
+ - Ops (auroq-core 2.3.20): a FASE 10 cria o comando com o nome e deixa `/auroq-companion` no lugar (antes mandava apagar um `companion.md` que nao existe mais).
11
+ - Quem esta travado: "atualiza o sistema" termina `status=ok` e mantem o nome, o comando e o conteudo do Companion.
12
+ - Mantem `arcane-mcp@0.7.0`. Instaladores do repositorio preparados para 2.6.4.
13
+
1
14
  ## 2.6.3 — 2026-09-29
2
15
 
3
16
  ### Codex no Windows volta a ler e gravar na pasta (EMB-063)
package/README.md CHANGED
@@ -241,13 +241,13 @@ agents/ → Seu exercito (companion, workers, minds, squads)
241
241
 
242
242
  ## Observabilidade e integridade do motor
243
243
 
244
- Apos publicacao, `npx -y auroq-os@2.6.3 doctor` mostra a fila local segura e os comandos pinados de
245
- manutencao/diagnostico. `npx -y auroq-os@2.6.3 manutencao` preserva o caminho existente
244
+ Apos publicacao, `npx -y auroq-os@2.6.4 doctor` mostra a fila local segura e os comandos pinados de
245
+ manutencao/diagnostico. `npx -y auroq-os@2.6.4 manutencao` preserva o caminho existente
246
246
  de consentimento legado, studentFiles, customizacoes e dados do negocio. Nao ha
247
247
  migracao forcada. O registro MCP aponta para `arcane-mcp@0.7.0` — o pin mora no campo
248
248
  `arcaneMcpPin` do package.json (fonte unica; o servidor le o mesmo campo no npm).
249
249
  `npm run release:verify` recusa publicar com um pin que o npm nao resolva.
250
- Os instaladores do repositorio ficam preparados para 2.6.3; as copias publicas so recebem
250
+ Os instaladores do repositorio ficam preparados para 2.6.4; as copias publicas so recebem
251
251
  a versao apos os gates nativos e a verificacao do tarball publicado.
252
252
  O pacote inclui `lib/runtime-integrity.json`: inventario SHA-256 gerado depois da montagem da casca, verificavel com `npm run integrity:verify` dentro do pacote extraido. Ele comprova consistencia do conteudo; nao e uma assinatura de autoria.
253
253
 
package/bin/auroq-os.js CHANGED
@@ -398,6 +398,32 @@ const HEALTH_CHECKS = [
398
398
  ['scripts/validate-hybrid.mjs', 'Validador hibrido'],
399
399
  ];
400
400
 
401
+ /**
402
+ * O aluno da um nome ao Companion no bootstrap (FASE 10 do Ops): `/atlas-companion`. Um
403
+ * comando com esse nome que ainda aponta pro Companion (recurso `companion` do MCP ou a
404
+ * pasta agents/companion/) vale como a casca dele — um clone do negocio que so tem o
405
+ * comando com o nome do aluno nao pode ser acusado de "arquivo faltando". So o Companion
406
+ * tem essa folga: os outros arquivos continuam exigidos pelo caminho exato.
407
+ */
408
+ async function companionComNomeDoAluno(targetDir) {
409
+ const dir = path.join(targetDir, '.claude', 'commands');
410
+ let nomes = [];
411
+ try { nomes = await fs.readdir(dir); } catch { return null; }
412
+ for (const nome of nomes.filter(n => /^[a-z0-9-]+-companion\.md$/.test(n)).sort()) {
413
+ try {
414
+ const texto = await fs.readFile(path.join(dir, nome), 'utf8');
415
+ if (/recurso_id\s*`?companion`?/.test(texto) || /agents\/companion\//.test(texto)) return `.claude/commands/${nome}`;
416
+ } catch { /* ilegivel: nao conta */ }
417
+ }
418
+ return null;
419
+ }
420
+
421
+ async function checagemPresente(targetDir, file) {
422
+ if (await fs.pathExists(path.join(targetDir, file))) return true;
423
+ if (file === '.claude/commands/auroq-companion.md') return Boolean(await companionComNomeDoAluno(targetDir));
424
+ return false;
425
+ }
426
+
401
427
  /**
402
428
  * Registro pro suporte (PRD "Embarque em 2 passos" §5.7).
403
429
  *
@@ -494,7 +520,7 @@ async function runHealthChecks(targetDir) {
494
520
  let passed = 0;
495
521
  const missing = [];
496
522
  for (const [file, label] of HEALTH_CHECKS) {
497
- if (await fs.pathExists(path.join(targetDir, file))) {
523
+ if (await checagemPresente(targetDir, file)) {
498
524
  passed++;
499
525
  } else {
500
526
  missing.push(`${label} (${file})`);
@@ -531,7 +557,7 @@ async function runDoctor(targetDir) {
531
557
  // 1. arquivos de autoload
532
558
  let faltando = 0;
533
559
  for (const [file] of HEALTH_CHECKS) {
534
- if (!(await fs.pathExists(path.join(targetDir, file)))) faltando++;
560
+ if (!(await checagemPresente(targetDir, file))) faltando++;
535
561
  }
536
562
  if (faltando === 0) {
537
563
  success(`Arquivos do sistema: ${HEALTH_CHECKS.length}/${HEALTH_CHECKS.length} no lugar`);
@@ -1968,8 +1994,11 @@ async function init({ pasta = null, maintenance = false, modoAgente = false, mig
1968
1994
  execSync('node scripts/sync-codex-skills.mjs --clean', { cwd: targetDir, stdio: 'pipe' });
1969
1995
  execSync('node scripts/sync-codex-skills.mjs --check', { cwd: targetDir, stdio: 'pipe' });
1970
1996
  success('Codex CLI pronto — skills locais verificadas ($companion, $ops, etc.)');
1971
- } catch {
1997
+ } catch (e) {
1972
1998
  warn('Sync de skills do Codex falhou — rode "npx auroq-os sync-codex" para diagnosticar');
1999
+ // Sem o motivo, o suporte so via o efeito ("Codex Companion faltando") e nunca a causa.
2000
+ const motivo = String(e && (e.stderr || e.message) || '').split(/\r?\n/).find(l => /Error:|Skill |Drift|Ownership|Recusando/.test(l));
2001
+ if (motivo) warn(` ${motivo.trim().slice(0, 300)}`);
1973
2002
  }
1974
2003
 
1975
2004
  // ─── Fase 5: npm install
@@ -589,7 +589,7 @@ function Invoke-AuroqLogin {
589
589
  # sucesso. Usado como pergunta de sim/nao, ele dizia "voce ja estava logado"
590
590
  # pra quem nunca logou, pulava o portao e deixava a conta sem acesso chegar ate
591
591
  # a criacao da pasta.
592
- & (Get-AuroqNpx) -y auroq-os@2.6.3 check-access
592
+ & (Get-AuroqNpx) -y auroq-os@2.6.4 check-access
593
593
  if ($LASTEXITCODE -ne 0) {
594
594
  Write-AuroqLog 'login' 'recusado'
595
595
  Write-Host ''
@@ -677,7 +677,7 @@ function Invoke-AuroqInit {
677
677
  if ($env:AUROQ_INSTALL_DRY_RUN) { Write-AuroqOk 'Sistema - seria montado aqui'; return }
678
678
 
679
679
  Set-Location $script:AuroqPastaAlvo
680
- & (Get-AuroqNpx) -y auroq-os@2.6.3 instalar
680
+ & (Get-AuroqNpx) -y auroq-os@2.6.4 instalar
681
681
  if ($LASTEXITCODE -ne 0) {
682
682
  Write-AuroqLog 'init' 'falhou'
683
683
  Write-AuroqErro 'A montagem do sistema parou no meio.' 'Rode o mesmo comando de novo. Se repetir, mande pro suporte o arquivo install.log da pasta .auroq.'
@@ -219,9 +219,9 @@ auroq_tem_terminal() {
219
219
  # recebem pedaco do script no lugar da resposta dele.
220
220
  auroq_npx_aluno() {
221
221
  if [ -t 0 ]; then
222
- npx -y auroq-os@2.6.3 "$@"
222
+ npx -y auroq-os@2.6.4 "$@"
223
223
  else
224
- npx -y auroq-os@2.6.3 "$@" < /dev/tty
224
+ npx -y auroq-os@2.6.4 "$@" < /dev/tty
225
225
  fi
226
226
  }
227
227
 
@@ -2,7 +2,7 @@
2
2
  "schema": 1,
3
3
  "algorithm": "sha256",
4
4
  "scope": "packaged-runtime-and-cascas",
5
- "version": "2.6.3",
5
+ "version": "2.6.4",
6
6
  "files": [
7
7
  {
8
8
  "path": ".auroq-core/constitution.md",
@@ -10,7 +10,7 @@
10
10
  },
11
11
  {
12
12
  "path": ".auroq-core/core-config.yaml",
13
- "sha256": "791b732e0e56651111feeec49d53bf4399bc2590c0c51d7ee2e8f3fd2ac54e24"
13
+ "sha256": "3052aab9e21a7b36788f5141a7d34ce6baa87c482ac18762c306b84b24ffc5c9"
14
14
  },
15
15
  {
16
16
  "path": ".auroq-core/core/synapse/context/context-builder.js",
@@ -206,7 +206,7 @@
206
206
  },
207
207
  {
208
208
  "path": "bin/auroq-os.js",
209
- "sha256": "167c9c92e3e247cfc61f8b9fcde69deec85debd8938181708d980142b45cef95"
209
+ "sha256": "7a9fd719e1ddc2fb18000b9f237ec2fdb0b605ed111c6e58dccef890f2d68cb1"
210
210
  },
211
211
  {
212
212
  "path": "lib/ahi/client.mjs",
@@ -258,7 +258,7 @@
258
258
  },
259
259
  {
260
260
  "path": "package.json",
261
- "sha256": "d26aeecec853f39b5b0a1c6a78a6a3bafe5ce025e310c6f0fbd3f05e4a7567bf"
261
+ "sha256": "9c2cafc77bfc679534da059fbcb7a847ca284da5d9074e138308d329aa6aef76"
262
262
  },
263
263
  {
264
264
  "path": "scripts/build-pack.mjs",
@@ -282,7 +282,7 @@
282
282
  },
283
283
  {
284
284
  "path": "scripts/sync-codex-skills.mjs",
285
- "sha256": "36e703e8d298a5fc4cff3a85828b93a5123ab9563eeb2808c30f23f3eefd58f8"
285
+ "sha256": "889394bcbf80aefeba49adf2f19202b47382324f3b1357063ca04b87ce46ddb2"
286
286
  },
287
287
  {
288
288
  "path": "scripts/validate-hybrid.mjs",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "auroq-os",
3
- "version": "2.6.3",
3
+ "version": "2.6.4",
4
4
  "arcaneMcpPin": "0.7.0",
5
5
  "description": "Auroq OS — Sistema Operacional de IA para Experts",
6
6
  "bin": {
@@ -61,12 +61,48 @@ function summaryFromMarkdown(file) {
61
61
  return output.join(' ').replace(/\s+/g, ' ').replaceAll('"', "'") || 'Agente do Auroq OS.';
62
62
  }
63
63
 
64
+ const avisos = [];
65
+
66
+ // Comando oficial = `auroq-<nome>.md` ou o Ops. Numa colisao de nome ele vence; o
67
+ // outro comando continua valendo no Claude/Grok e so fica sem skill no Codex.
68
+ const oficial = (file) => /^auroq-/.test(path.basename(file)) || relative(file).startsWith('.claude/commands/AuroqOS/');
69
+
64
70
  function registerSkill(name, commandFile) {
65
- if (!/^[a-z0-9-]+$/.test(name)) throw new Error(`Nome de skill invalido: ${name}`);
66
- if (skills.has(name)) throw new Error(`Colisao de nome '${name}': ${relative(skills.get(name))} e ${relative(commandFile)}`);
71
+ if (!/^[a-z0-9-]+$/.test(name)) {
72
+ avisos.push(`Comando ${relative(commandFile)} ignorado no Codex: nome de skill invalido '${name}'.`);
73
+ return;
74
+ }
75
+ if (skills.has(name)) {
76
+ // Uma colisao (ex: companion.md de antes do prefixo auroq- ao lado de auroq-companion.md)
77
+ // derrubava o sync inteiro — e junto a skill $companion, que a manutencao exige.
78
+ const atual = skills.get(name);
79
+ const [fica, sai] = oficial(commandFile) && !oficial(atual) ? [commandFile, atual] : [atual, commandFile];
80
+ skills.set(name, fica);
81
+ avisos.push(`Colisao de nome '${name}': usando ${relative(fica)}; ${relative(sai)} continua valendo no Claude, sem skill propria no Codex.`);
82
+ return;
83
+ }
67
84
  skills.set(name, commandFile);
68
85
  }
69
86
 
87
+ // O aluno da um nome ao Companion no bootstrap (FASE 10 do Ops: /atlas-companion).
88
+ // Se o comando base sumiu, $companion continua existindo, apontando pro comando dele.
89
+ function comandoDoCompanion(file) {
90
+ try {
91
+ const texto = fs.readFileSync(file, 'utf8');
92
+ return /recurso_id\s*`?companion`?/.test(texto) || /agents\/companion\//.test(texto);
93
+ } catch {
94
+ return false;
95
+ }
96
+ }
97
+
98
+ function garantirAliasCompanion() {
99
+ if (skills.has('companion')) return;
100
+ const candidatos = [...skills.entries()]
101
+ .filter(([name, file]) => name.endsWith('-companion') && comandoDoCompanion(file))
102
+ .sort(([a], [b]) => a.localeCompare(b));
103
+ if (candidatos.length) skills.set('companion', candidatos[0][1]);
104
+ }
105
+
70
106
  function collectSkills() {
71
107
  const commandRoot = path.join(root, '.claude', 'commands');
72
108
  if (!fs.existsSync(commandRoot)) return;
@@ -88,6 +124,7 @@ function collectSkills() {
88
124
  registerSkill(kebab(file.replace(/\.md$/, '')), path.join(coreAgents, file));
89
125
  }
90
126
  }
127
+ garantirAliasCompanion();
91
128
  }
92
129
 
93
130
  function skillContent(name, commandFile, outRoot) {
@@ -139,13 +176,20 @@ function readOwner(dir) {
139
176
  // Mesma pasta de projeto = mesmo dono, mesmo que o name do package tenha
140
177
  // mudado depois (ex: marcador gerado antes do package.json existir).
141
178
  if (data.sourceRoot && path.resolve(data.sourceRoot) === root) return { kind: 'owned', data };
179
+ // .agents/skills mora DENTRO do projeto: marcador do nosso gerador com outro caminho
180
+ // e a mesma pasta de negocio movida, copiada ou vinda de outra maquina. Tratar como
181
+ // estranho travava o sync inteiro (e com ele a skill $companion) pra sempre.
182
+ if (path.dirname(dir) === localOut && data.schema === 2 && data.projectId) return { kind: 'owned', data, movido: true };
142
183
  return { kind: 'foreign', data };
143
184
  } catch {
144
185
  return { kind: 'foreign', data: { invalidMarker: true } };
145
186
  }
146
187
  }
147
188
 
148
- function assertNoCollisions(outRoot) {
189
+ // Local: pasta de skill sem o nosso marcador e do aluno — fica como esta, com aviso,
190
+ // e o resto do sync segue. Global (~/.agents/skills, dividido entre projetos): recusa tudo.
191
+ function collisions(outRoot) {
192
+ const preservadas = new Set();
149
193
  for (const [name, commandFile] of skills) {
150
194
  const dir = path.join(outRoot, name);
151
195
  if (!fs.existsSync(dir)) continue;
@@ -155,8 +199,15 @@ function assertNoCollisions(outRoot) {
155
199
  if (adoptLegacy && owner.kind === 'foreign' && owner.data?.projectName === projectName) continue;
156
200
  const existing = path.join(dir, 'SKILL.md');
157
201
  if (owner.kind === 'legacy' && fs.existsSync(existing) && fs.readFileSync(existing, 'utf8') === skillContent(name, commandFile, outRoot)) continue;
202
+ if (outRoot === localOut && owner.kind === 'legacy') continue;
203
+ if (outRoot === localOut) {
204
+ preservadas.add(name);
205
+ continue;
206
+ }
158
207
  throw new Error(`Recusando sobrescrever ${dir}; ownership: ${owner.kind}.`);
159
208
  }
209
+ for (const name of preservadas) avisos.push(`Skill ${name} em ${relative(path.join(outRoot, name))} nao foi gerada por este sync: mantida como esta.`);
210
+ return preservadas;
160
211
  }
161
212
 
162
213
  function inspect(outRoot) {
@@ -168,7 +219,9 @@ function inspect(outRoot) {
168
219
  issues.push(`Skill ausente: ${name}`);
169
220
  continue;
170
221
  }
171
- if (readOwner(dir).kind !== 'owned') issues.push(`Ownership invalido: ${name}`);
222
+ const owner = readOwner(dir);
223
+ if (outRoot === localOut && owner.kind === 'unmanaged') continue; // skill do aluno, preservada
224
+ if (owner.kind !== 'owned' || owner.movido) issues.push(`Ownership invalido: ${name}`);
172
225
  const file = path.join(dir, 'SKILL.md');
173
226
  if (!fs.existsSync(file) || fs.readFileSync(file, 'utf8') !== skillContent(name, commandFile, outRoot)) {
174
227
  issues.push(`Drift de conteudo: ${name}`);
@@ -183,16 +236,18 @@ function inspect(outRoot) {
183
236
 
184
237
  function sync(outRoot) {
185
238
  ensureDir(outRoot);
186
- assertNoCollisions(outRoot);
239
+ const preservadas = collisions(outRoot);
187
240
  const stage = fs.mkdtempSync(path.join(outRoot, '.auroq-stage-'));
188
241
  try {
189
242
  for (const [name, commandFile] of skills) {
243
+ if (preservadas.has(name)) continue;
190
244
  const dir = path.join(stage, name);
191
245
  ensureDir(dir);
192
246
  fs.writeFileSync(path.join(dir, markerFile), marker, 'utf8');
193
247
  fs.writeFileSync(path.join(dir, 'SKILL.md'), skillContent(name, commandFile, outRoot), 'utf8');
194
248
  }
195
249
  for (const name of skills.keys()) {
250
+ if (preservadas.has(name)) continue;
196
251
  const target = path.join(outRoot, name);
197
252
  fs.rmSync(target, { recursive: true, force: true });
198
253
  fs.renameSync(path.join(stage, name), target);
@@ -237,6 +292,11 @@ Padrao gera skills locais em .agents/skills (recomendado)
237
292
  collectSkills();
238
293
  if (targets.includes(localOut)) cleanLegacyLocalLocation();
239
294
 
295
+ const mostrarAvisos = () => {
296
+ for (const aviso of new Set(avisos)) console.error(`Aviso: ${aviso}`);
297
+ avisos.length = 0;
298
+ };
299
+
240
300
  for (const target of targets) {
241
301
  if (check) {
242
302
  const issues = inspect(target);
@@ -247,10 +307,11 @@ for (const target of targets) {
247
307
  console.log(`Auroq: ${skills.size} skills verificadas em ${target}`);
248
308
  }
249
309
  } else if (dryRun) {
250
- if (fs.existsSync(target)) assertNoCollisions(target);
310
+ if (fs.existsSync(target)) collisions(target);
251
311
  console.log(`Auroq: ${skills.size} skills validadas para ${target}`);
252
312
  } else {
253
313
  sync(target);
254
314
  console.log(`Auroq: ${skills.size} skills sincronizadas em ${target}`);
255
315
  }
316
+ mostrarAvisos();
256
317
  }