auroq-os 2.6.9 → 2.6.10
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/.auroq-core/core-config.yaml +1 -1
- package/.claude/rules/titulo-da-barra.md +2 -2
- package/CHANGELOG.md +12 -0
- package/README.md +3 -3
- package/bin/auroq-os.js +10 -3
- package/installer/install.ps1 +2 -2
- package/installer/install.sh +2 -2
- package/lib/barra/barra.cjs +52 -18
- package/lib/barra/ccstatusline-grok.json +98 -0
- package/lib/barra/index.js +158 -32
- package/lib/runtime-integrity.json +10 -6
- package/package.json +1 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Título da conversa na barra de status
|
|
2
2
|
|
|
3
|
-
A barra de status
|
|
3
|
+
A barra de status (embaixo da caixa de mensagem) mostra, em amarelo, o título da conversa. No Claude Code e no Grok, quem escreve esse título é você.
|
|
4
4
|
|
|
5
5
|
- Na primeira resposta de cada conversa, grave em silêncio, uma única vez, um título de 3 a 5 palavras em português sobre o tema central:
|
|
6
6
|
`node ~/.auroq/barra/barra.cjs titulo "Título em português"`
|
|
7
7
|
- Não avise o expert, não comente o comando e não troque o título por causa de tarefa lateral (o primeiro fica).
|
|
8
8
|
- Se o comando falhar ou o arquivo não existir, ignore e siga a conversa normalmente.
|
|
9
|
-
- Vale
|
|
9
|
+
- Vale no Claude Code e no Grok. No Codex não rode: ele mostra o título da conversa sozinho.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## 2.6.10 — 2026-10-16
|
|
2
|
+
|
|
3
|
+
### A barra de status também no Codex e no Grok
|
|
4
|
+
|
|
5
|
+
- Pedido do Euriler: a barra tem que aparecer também no terminal com Codex ou Grok, não só no Claude Code.
|
|
6
|
+
- **Grok** (aceita comando na barra): `instalar`/`manutencao`/`clone` acrescentam `[ui.status_line]` no `~/.grok/config.toml` rodando a mesma barra (`node ~/.auroq/barra/barra.cjs grok`, `refresh_interval = 10`). Só quando o Grok existe no computador (`~/.grok` ou `GROK_HOME`); o resto do arquivo volta byte a byte. Layout próprio de 3 linhas (`ccstatusline-grok.json`): modelo, esforço, título e tempo; pasta e branch; contexto e data. Sem "Claude: ok", cache e uso de 5 h/semana, que o Grok não manda. A barra traduz o contexto do Grok (`context_tokens`) para o formato que o ccstatusline lê e usa a largura que o Grok passa em `COLUMNS`.
|
|
7
|
+
- **Codex** (não aceita comando, só itens prontos): liga a barra nativa com os mesmos itens da do Euriler no `[tui] status_line` do `~/.codex/config.toml` (`CODEX_HOME` respeitado): modelo e raciocínio, título da conversa, pasta, branch, alterações, contexto, uso de 5 h e da semana, com cores. O app Auroq OS espelha esse arquivo nas contas do Codex dele.
|
|
8
|
+
- Mesmas regras da 2.6.8 nos dois: nunca troca barra que o aluno já tinha (seção, chave dentro de `[ui]`/`[tui]` ou chave pontilhada), não põe de volta se o aluno tirou, `~/.auroq/sem-barra` desliga tudo. Falha em um não afeta os outros.
|
|
9
|
+
- Título: a regra `titulo-da-barra.md` passa a valer no Grok também (ele lê `.claude/rules` do projeto); a barra grava pelo `GROK_SESSION_ID` e, sem título, mostra o nome da sessão que o Grok dá. Grok aberto de dentro do Claude Code (ou o contrário) herda os dois ids: o título vai para os dois. O Codex mostra o título dele sozinho.
|
|
10
|
+
- Provado com os programas de verdade num terminal: Codex 0.158 mostra a barra com esses itens; Grok 1.0.41 mostra as 3 linhas com o título que ele mesmo gravou (configuração do Grok do Euriler ligada só durante o teste e devolvida idêntica). Testes novos em `tests/barra.test.mjs` (21 no total) e prova do Grok no Windows em `barra-windows.yml`.
|
|
11
|
+
- Mantem `arcane-mcp@0.7.0`. Instaladores do repositorio preparados para 2.6.10.
|
|
12
|
+
|
|
1
13
|
## 2.6.9 — 2026-10-16
|
|
2
14
|
|
|
3
15
|
### Historico das sessoes: texto no modelo "atualizamos os termos"
|
package/README.md
CHANGED
|
@@ -254,13 +254,13 @@ agents/ → Seu exercito (companion, workers, minds, squads)
|
|
|
254
254
|
|
|
255
255
|
## Observabilidade e integridade do motor
|
|
256
256
|
|
|
257
|
-
Apos publicacao, `npx -y auroq-os@2.6.
|
|
258
|
-
manutencao/diagnostico. `npx -y auroq-os@2.6.
|
|
257
|
+
Apos publicacao, `npx -y auroq-os@2.6.10 doctor` mostra a fila local segura e os comandos pinados de
|
|
258
|
+
manutencao/diagnostico. `npx -y auroq-os@2.6.10 manutencao` preserva o caminho existente
|
|
259
259
|
de consentimento legado, studentFiles, customizacoes e dados do negocio. Nao ha
|
|
260
260
|
migracao forcada. O registro MCP aponta para `arcane-mcp@0.7.0` — o pin mora no campo
|
|
261
261
|
`arcaneMcpPin` do package.json (fonte unica; o servidor le o mesmo campo no npm).
|
|
262
262
|
`npm run release:verify` recusa publicar com um pin que o npm nao resolva.
|
|
263
|
-
Os instaladores do repositorio ficam preparados para 2.6.
|
|
263
|
+
Os instaladores do repositorio ficam preparados para 2.6.10; as copias publicas so recebem
|
|
264
264
|
a versao apos os gates nativos e a verificacao do tarball publicado.
|
|
265
265
|
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.
|
|
266
266
|
|
package/bin/auroq-os.js
CHANGED
|
@@ -1549,14 +1549,14 @@ function opcoesDeLoginDeTeste() {
|
|
|
1549
1549
|
}
|
|
1550
1550
|
|
|
1551
1551
|
// ─── INSTALACAO ──────────────────────────────────────────
|
|
1552
|
-
// Barra de status
|
|
1552
|
+
// Barra de status (2.6.8; Codex e Grok desde 2.6.10): a mesma do Euriler, no HOME do aluno. Nunca derruba a
|
|
1553
1553
|
// instalacao: sem a barra o sistema funciona igual, entao qualquer falha vira aviso.
|
|
1554
1554
|
function ligarBarraDeStatus() {
|
|
1555
|
-
step('Ligando a barra de status
|
|
1555
|
+
step('Ligando a barra de status (Claude Code, Codex e Grok)...');
|
|
1556
1556
|
try {
|
|
1557
1557
|
const r = require('../lib/barra').instalarBarra();
|
|
1558
1558
|
const textos = {
|
|
1559
|
-
'ligada': 'Barra de status ligada (vale em todo terminal
|
|
1559
|
+
'ligada': 'Barra de status ligada no Claude Code (vale em todo terminal)',
|
|
1560
1560
|
'atualizada': 'Barra de status em dia',
|
|
1561
1561
|
'em-dia': 'Barra de status em dia',
|
|
1562
1562
|
'barra-do-aluno': 'Voce ja tem uma barra de status propria no Claude Code; ficou como estava',
|
|
@@ -1565,6 +1565,13 @@ function ligarBarraDeStatus() {
|
|
|
1565
1565
|
};
|
|
1566
1566
|
if (textos[r.estado]) success(textos[r.estado]);
|
|
1567
1567
|
else warn(`Barra de status nao ligada (${r.estado}${r.detalhe ? `: ${r.detalhe}` : ''})`);
|
|
1568
|
+
// 2.6.10: a mesma barra no Grok (se instalado) e a barra nativa do Codex com os mesmos itens.
|
|
1569
|
+
const outras = { ligada: 'ligada', atualizada: 'em dia', 'em-dia': 'em dia', 'barra-do-aluno': 'a sua ficou como estava', 'tirada-pelo-aluno': 'desligada por voce' };
|
|
1570
|
+
for (const [nome, estado] of [['Codex', r.codex], ['Grok', r.grok]]) {
|
|
1571
|
+
if (!estado || estado === 'sem-grok') continue;
|
|
1572
|
+
if (outras[estado]) success(`Barra de status no ${nome}: ${outras[estado]}`);
|
|
1573
|
+
else warn(`Barra de status no ${nome} nao ligada (${estado})`);
|
|
1574
|
+
}
|
|
1568
1575
|
return r;
|
|
1569
1576
|
} catch (e) {
|
|
1570
1577
|
warn(`Barra de status nao ligada (${String(e && e.message || e).split(/\r?\n/)[0].slice(0, 200)}). O resto funciona normal.`);
|
package/installer/install.ps1
CHANGED
|
@@ -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.
|
|
592
|
+
& (Get-AuroqNpx) -y auroq-os@2.6.10 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.
|
|
680
|
+
& (Get-AuroqNpx) -y auroq-os@2.6.10 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.'
|
package/installer/install.sh
CHANGED
|
@@ -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.
|
|
222
|
+
npx -y auroq-os@2.6.10 "$@"
|
|
223
223
|
else
|
|
224
|
-
npx -y auroq-os@2.6.
|
|
224
|
+
npx -y auroq-os@2.6.10 "$@" < /dev/tty
|
|
225
225
|
fi
|
|
226
226
|
}
|
|
227
227
|
|
package/lib/barra/barra.cjs
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
|
-
// Barra de status do
|
|
4
|
-
// `statusLine.command` do ~/.claude/settings.json
|
|
3
|
+
// Barra de status do aluno (2.6.8; Grok desde 2.6.10). Mora em ~/.auroq/barra/barra.cjs e e o
|
|
4
|
+
// `statusLine.command` do ~/.claude/settings.json e o `[ui.status_line]` do ~/.grok/config.toml:
|
|
5
|
+
// vale em qualquer terminal e no app Auroq OS. O Codex usa a barra nativa dele (ver index.js).
|
|
5
6
|
//
|
|
6
7
|
// node barra.cjs desenha a barra (o Claude Code manda o JSON da sessao na entrada)
|
|
8
|
+
// node barra.cjs grok desenha a barra no Grok (mesmo JSON, com os nomes do Grok)
|
|
7
9
|
// node barra.cjs titulo "Texto" grava o titulo da conversa (uma vez por sessao; a IA chama)
|
|
8
10
|
// node barra.cjs topico pedaco da barra: o titulo desta sessao
|
|
9
11
|
// node barra.cjs data pedaco da barra: "sex 16/10 11:39"
|
|
@@ -19,6 +21,7 @@ const PASTA = __dirname;
|
|
|
19
21
|
const TITULOS = path.join(PASTA, 'titulos');
|
|
20
22
|
const MOTOR = path.join(PASTA, 'ccstatusline', 'dist', 'ccstatusline.js');
|
|
21
23
|
const LAYOUT = path.join(PASTA, 'ccstatusline.json');
|
|
24
|
+
const LAYOUT_GROK = path.join(PASTA, 'ccstatusline-grok.json');
|
|
22
25
|
const SESSAO_VALIDA = /^[A-Za-z0-9._-]{1,128}$/;
|
|
23
26
|
const SETE_DIAS_MS = 7 * 24 * 60 * 60 * 1000;
|
|
24
27
|
|
|
@@ -46,16 +49,19 @@ function arquivoDoTitulo(sessao) {
|
|
|
46
49
|
}
|
|
47
50
|
|
|
48
51
|
function gravarTitulo(texto) {
|
|
49
|
-
|
|
52
|
+
// Claude Code e Grok poem o id da sessao no terminal da IA. Um aberto dentro do outro herda os
|
|
53
|
+
// dois, entao grava nos dois: cada barra le o seu.
|
|
54
|
+
const sessoes = [process.env.CLAUDE_CODE_SESSION_ID, process.env.GROK_SESSION_ID].filter(s => s && SESSAO_VALIDA.test(s));
|
|
50
55
|
const titulo = String(texto || '').replace(/[\r\n\t]+/g, ' ').replace(/[\u0000-\u001f\u007f]/g, '').trim().slice(0, 60);
|
|
51
|
-
if (!titulo || !
|
|
56
|
+
if (!titulo || !sessoes.length) return;
|
|
52
57
|
fs.mkdirSync(TITULOS, { recursive: true });
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
for (const sessao of sessoes) {
|
|
59
|
+
// Uma vez por sessao: o primeiro titulo fica, como o do Euriler.
|
|
60
|
+
try {
|
|
61
|
+
fs.writeFileSync(arquivoDoTitulo(sessao), `${titulo}\n`, { flag: 'wx' });
|
|
62
|
+
} catch (e) {
|
|
63
|
+
if (e.code !== 'EEXIST') throw e;
|
|
64
|
+
}
|
|
59
65
|
}
|
|
60
66
|
limparTitulosVelhos();
|
|
61
67
|
}
|
|
@@ -72,12 +78,34 @@ function limparTitulosVelhos() {
|
|
|
72
78
|
|
|
73
79
|
function mostrarTitulo(entrada) {
|
|
74
80
|
const sessao = sessaoDoJson(entrada);
|
|
75
|
-
|
|
81
|
+
let titulo = '';
|
|
82
|
+
if (sessao) {
|
|
83
|
+
try {
|
|
84
|
+
titulo = fs.readFileSync(arquivoDoTitulo(sessao), 'utf8').split(/\r?\n/)[0] || '';
|
|
85
|
+
} catch {}
|
|
86
|
+
}
|
|
87
|
+
// Sem titulo gravado, o nome da sessao que o Grok (ou o /rename do Claude Code) da.
|
|
88
|
+
if (!titulo) {
|
|
89
|
+
try {
|
|
90
|
+
const nome = JSON.parse(entrada).session_name;
|
|
91
|
+
if (typeof nome === 'string') titulo = nome.replace(/[\u0000-\u001f\u007f]/g, '').trim();
|
|
92
|
+
} catch {}
|
|
93
|
+
}
|
|
94
|
+
return [...titulo].slice(0, 40).join('');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// O Grok segue a convencao do Claude Code, mas chama o contexto atual de `context_tokens`.
|
|
98
|
+
function jsonDoGrok(entrada) {
|
|
76
99
|
try {
|
|
77
|
-
const
|
|
78
|
-
|
|
100
|
+
const dados = JSON.parse(entrada);
|
|
101
|
+
const janela = dados.context_window;
|
|
102
|
+
if (janela && typeof janela.context_tokens === 'number' && !janela.current_usage) {
|
|
103
|
+
janela.current_usage = { input_tokens: janela.context_tokens, output_tokens: 0, cache_creation_input_tokens: 0, cache_read_input_tokens: 0 };
|
|
104
|
+
if (typeof janela.total_input_tokens !== 'number') janela.total_input_tokens = janela.context_tokens;
|
|
105
|
+
}
|
|
106
|
+
return JSON.stringify(dados);
|
|
79
107
|
} catch {
|
|
80
|
-
return
|
|
108
|
+
return entrada;
|
|
81
109
|
}
|
|
82
110
|
}
|
|
83
111
|
|
|
@@ -116,12 +144,12 @@ function repassarAoApp(entrada) {
|
|
|
116
144
|
} catch {}
|
|
117
145
|
}
|
|
118
146
|
|
|
119
|
-
function desenhar(entrada) {
|
|
147
|
+
function desenhar(entrada, { layout = LAYOUT, env = process.env } = {}) {
|
|
120
148
|
return new Promise(resolve => {
|
|
121
|
-
if (!fs.existsSync(MOTOR)) return resolve();
|
|
149
|
+
if (!fs.existsSync(MOTOR) || !fs.existsSync(layout)) return resolve();
|
|
122
150
|
let filho;
|
|
123
151
|
try {
|
|
124
|
-
filho = spawn(process.execPath, [MOTOR, '--config',
|
|
152
|
+
filho = spawn(process.execPath, [MOTOR, '--config', layout], { stdio: ['pipe', 'inherit', 'ignore'], windowsHide: true, env });
|
|
125
153
|
} catch {
|
|
126
154
|
return resolve();
|
|
127
155
|
}
|
|
@@ -138,6 +166,12 @@ async function principal() {
|
|
|
138
166
|
if (modo === 'data') return process.stdout.write(dataCurta());
|
|
139
167
|
const entrada = await lerEntrada();
|
|
140
168
|
if (modo === 'topico') return process.stdout.write(mostrarTitulo(entrada));
|
|
169
|
+
if (modo === 'grok') {
|
|
170
|
+
// O Grok passa a largura da linha em COLUMNS; o ccstatusline usa para alinhar a direita.
|
|
171
|
+
const env = { ...process.env };
|
|
172
|
+
if (/^\d+$/.test(env.COLUMNS || '') && !env.CCSTATUSLINE_WIDTH) env.CCSTATUSLINE_WIDTH = env.COLUMNS;
|
|
173
|
+
return desenhar(jsonDoGrok(entrada), { layout: LAYOUT_GROK, env });
|
|
174
|
+
}
|
|
141
175
|
repassarAoApp(entrada);
|
|
142
176
|
await desenhar(entrada);
|
|
143
177
|
}
|
|
@@ -146,4 +180,4 @@ if (require.main === module) {
|
|
|
146
180
|
principal().catch(() => {}).finally(() => { process.exitCode = 0; });
|
|
147
181
|
}
|
|
148
182
|
|
|
149
|
-
module.exports = { dataCurta, sessaoDoJson, mostrarTitulo, gravarTitulo, scriptDoApp };
|
|
183
|
+
module.exports = { dataCurta, sessaoDoJson, mostrarTitulo, gravarTitulo, scriptDoApp, jsonDoGrok };
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 4,
|
|
3
|
+
"lines": [
|
|
4
|
+
[
|
|
5
|
+
{
|
|
6
|
+
"id": "l1-model",
|
|
7
|
+
"type": "model",
|
|
8
|
+
"color": "brightCyan",
|
|
9
|
+
"bold": true,
|
|
10
|
+
"rawValue": true
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"id": "l1-effort",
|
|
14
|
+
"type": "thinking-effort",
|
|
15
|
+
"color": "magenta"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"id": "l1-skill",
|
|
19
|
+
"type": "skills",
|
|
20
|
+
"color": "brightBlack",
|
|
21
|
+
"metadata": {
|
|
22
|
+
"hide": "empty"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "l1-topic",
|
|
27
|
+
"type": "custom-command",
|
|
28
|
+
"color": "yellow",
|
|
29
|
+
"commandPath": "{{BARRA}} topico",
|
|
30
|
+
"timeout": 1000
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"id": "l1-flex",
|
|
34
|
+
"type": "flex-separator"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"id": "l1-clock-ico",
|
|
38
|
+
"type": "custom-text",
|
|
39
|
+
"customText": "⏱",
|
|
40
|
+
"color": "brightYellow",
|
|
41
|
+
"merge": "no-padding"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"id": "l1-clock",
|
|
45
|
+
"type": "session-clock",
|
|
46
|
+
"color": "brightYellow",
|
|
47
|
+
"rawValue": true
|
|
48
|
+
}
|
|
49
|
+
],
|
|
50
|
+
[
|
|
51
|
+
{
|
|
52
|
+
"id": "l2-cwd",
|
|
53
|
+
"type": "current-working-dir",
|
|
54
|
+
"color": "blue",
|
|
55
|
+
"metadata": {
|
|
56
|
+
"abbreviateHome": "true"
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"id": "l2-branch",
|
|
61
|
+
"type": "git-branch",
|
|
62
|
+
"color": "green",
|
|
63
|
+
"metadata": {
|
|
64
|
+
"hide": "no-git"
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"id": "l2-changes",
|
|
69
|
+
"type": "git-changes",
|
|
70
|
+
"color": "yellow",
|
|
71
|
+
"metadata": {
|
|
72
|
+
"hide": "zero"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
],
|
|
76
|
+
[
|
|
77
|
+
{
|
|
78
|
+
"id": "l3-ctx",
|
|
79
|
+
"type": "context-bar",
|
|
80
|
+
"color": "brightMagenta"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"id": "l3-flex",
|
|
84
|
+
"type": "flex-separator"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"id": "l4-date",
|
|
88
|
+
"type": "custom-command",
|
|
89
|
+
"color": "brightBlack",
|
|
90
|
+
"commandPath": "{{BARRA}} data",
|
|
91
|
+
"timeout": 1000
|
|
92
|
+
}
|
|
93
|
+
]
|
|
94
|
+
],
|
|
95
|
+
"flexMode": "full",
|
|
96
|
+
"colorLevel": 3,
|
|
97
|
+
"defaultPadding": " "
|
|
98
|
+
}
|
package/lib/barra/index.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
// Barra de status do
|
|
3
|
-
// titulo da conversa, pasta, branch, contexto, cache, uso de 5 h e da semana, data), em
|
|
4
|
-
// terminal do Mac ou do Windows e dentro do app Auroq OS.
|
|
2
|
+
// Barra de status do aluno (2.6.8; Codex e Grok desde 2.6.10): a mesma barra do Euriler (modelo,
|
|
3
|
+
// esforco, titulo da conversa, pasta, branch, contexto, cache, uso de 5 h e da semana, data), em
|
|
4
|
+
// qualquer terminal do Mac ou do Windows e dentro do app Auroq OS.
|
|
5
|
+
// - Claude Code: `statusLine` no ~/.claude/settings.json roda barra.cjs (ccstatusline).
|
|
6
|
+
// - Grok: `[ui.status_line]` no ~/.grok/config.toml roda `barra.cjs grok` (so se o Grok existe).
|
|
7
|
+
// - Codex: nao aceita comando na barra; liga a barra nativa com os mesmos itens no
|
|
8
|
+
// `[tui] status_line` do ~/.codex/config.toml (o app Auroq OS espelha esse arquivo nas contas).
|
|
5
9
|
//
|
|
6
10
|
// O que faz, sempre no HOME do aluno (nunca na pasta do negocio):
|
|
7
11
|
// - copia o ccstatusline travado (MIT, sem dependencias) para ~/.auroq/barra/ccstatusline;
|
|
@@ -19,6 +23,9 @@ const VERSAO_CCSTATUSLINE = '2.2.30';
|
|
|
19
23
|
const AQUI = __dirname;
|
|
20
24
|
|
|
21
25
|
const ehNossa = comando => /[\\/]\.auroq[\\/]barra[\\/]barra\.cjs/.test(comando);
|
|
26
|
+
// Mesmos itens da barra do Euriler no Codex (~/.codex/config.toml dele, 24/09/2026).
|
|
27
|
+
const CODEX_ITENS = ['model-with-reasoning', 'thread-title', 'current-dir', 'git-branch', 'branch-changes', 'context-used', 'five-hour-limit', 'weekly-limit'];
|
|
28
|
+
const CODEX_LINHA = `status_line = [${CODEX_ITENS.map(i => `"${i}"`).join(', ')}]`;
|
|
22
29
|
const ehDoApp = comando => /agent-hooks[\\/]+claude-statusline/.test(comando);
|
|
23
30
|
const sha = texto => crypto.createHash('sha256').update(texto).digest('hex');
|
|
24
31
|
|
|
@@ -67,39 +74,106 @@ function copiarMotor(origem, destino) {
|
|
|
67
74
|
return true;
|
|
68
75
|
}
|
|
69
76
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
} = {}) {
|
|
75
|
-
if (fs.existsSync(path.join(home, '.auroq', 'sem-barra'))) return { estado: 'desligada' };
|
|
77
|
+
// ─── TOML sem biblioteca: so acha a secao pelo cabecalho e mexe nas linhas dela. O resto do
|
|
78
|
+
// arquivo (comentarios, ordem, outras secoes) volta byte a byte como estava.
|
|
79
|
+
const CABECALHO = /^\s*\[\s*([^\[\]]+?)\s*\]\s*(#.*)?$/;
|
|
80
|
+
const CABECALHO_QUALQUER = /^\s*\[/;
|
|
76
81
|
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
+
function lerToml(arquivo) {
|
|
83
|
+
const texto = fs.existsSync(arquivo) ? fs.readFileSync(arquivo, 'utf8') : '';
|
|
84
|
+
const quebra = texto.includes('\r\n') ? '\r\n' : '\n';
|
|
85
|
+
return { texto, quebra, linhas: texto === '' ? [] : texto.split(/\r?\n/) };
|
|
86
|
+
}
|
|
82
87
|
|
|
83
|
-
|
|
84
|
-
const
|
|
85
|
-
|
|
88
|
+
function secao(linhas, nome) {
|
|
89
|
+
const inicio = linhas.findIndex(l => (l.match(CABECALHO) || [])[1] === nome);
|
|
90
|
+
if (inicio === -1) return null;
|
|
91
|
+
let fim = linhas.length;
|
|
92
|
+
for (let i = inicio + 1; i < linhas.length; i++) {
|
|
93
|
+
if (CABECALHO_QUALQUER.test(linhas[i])) { fim = i; break; }
|
|
94
|
+
}
|
|
95
|
+
return { inicio, fim };
|
|
96
|
+
}
|
|
86
97
|
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
98
|
+
function chaveNaSecao(linhas, s, chave) {
|
|
99
|
+
const re = new RegExp(`^\\s*${chave}\\s*=`);
|
|
100
|
+
for (let i = s.inicio + 1; i < s.fim; i++) if (re.test(linhas[i])) return i;
|
|
101
|
+
return -1;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function gravarToml(arquivo, { linhas, quebra }) {
|
|
105
|
+
fs.mkdirSync(path.dirname(arquivo), { recursive: true });
|
|
106
|
+
let texto = linhas.join(quebra);
|
|
107
|
+
if (!texto.endsWith(quebra)) texto += quebra;
|
|
108
|
+
gravarAtomico(arquivo, texto);
|
|
109
|
+
}
|
|
95
110
|
|
|
111
|
+
function acrescentarSecao(toml, novas) {
|
|
112
|
+
const linhas = [...toml.linhas];
|
|
113
|
+
while (linhas.length && linhas[linhas.length - 1].trim() === '') linhas.pop();
|
|
114
|
+
if (linhas.length) linhas.push('');
|
|
115
|
+
return { ...toml, linhas: [...linhas, ...novas] };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Codex: barra nativa com os mesmos itens. So ocupa `[tui] status_line` quando ninguem pos.
|
|
119
|
+
function ligarCodex({ codexDir, registro }) {
|
|
120
|
+
const arquivo = path.join(codexDir, 'config.toml');
|
|
121
|
+
const toml = lerToml(arquivo);
|
|
122
|
+
if (/^\s*tui\s*\.\s*status_line\s*=/m.test(toml.texto)) return 'barra-do-aluno';
|
|
123
|
+
const tui = secao(toml.linhas, 'tui');
|
|
124
|
+
if (tui) {
|
|
125
|
+
const i = chaveNaSecao(toml.linhas, tui, 'status_line');
|
|
126
|
+
if (i !== -1) return toml.linhas[i].replace(/\s+/g, '') === CODEX_LINHA.replace(/\s+/g, '') ? 'em-dia' : 'barra-do-aluno';
|
|
127
|
+
if (registro.codexLigadaEm) return 'tirada-pelo-aluno';
|
|
128
|
+
const novas = [CODEX_LINHA];
|
|
129
|
+
if (chaveNaSecao(toml.linhas, tui, 'status_line_use_colors') === -1) novas.push('status_line_use_colors = true');
|
|
130
|
+
const linhas = [...toml.linhas];
|
|
131
|
+
linhas.splice(tui.inicio + 1, 0, ...novas);
|
|
132
|
+
gravarToml(arquivo, { ...toml, linhas });
|
|
133
|
+
return 'ligada';
|
|
134
|
+
}
|
|
135
|
+
if (registro.codexLigadaEm) return 'tirada-pelo-aluno';
|
|
136
|
+
gravarToml(arquivo, acrescentarSecao(toml, ['[tui]', CODEX_LINHA, 'status_line_use_colors = true']));
|
|
137
|
+
return 'ligada';
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Grok: `[ui.status_line]` roda a mesma barra. So mexe se o Grok existe neste computador.
|
|
141
|
+
function ligarGrok({ grokDir, registro, script }) {
|
|
142
|
+
if (!fs.existsSync(grokDir)) return 'sem-grok';
|
|
143
|
+
if (script.includes("'")) return 'caminho-nao-suportado';
|
|
144
|
+
const arquivo = path.join(grokDir, 'config.toml');
|
|
145
|
+
const toml = lerToml(arquivo);
|
|
146
|
+
// String literal do TOML (aspas simples): as aspas duplas do caminho passam sem escape.
|
|
147
|
+
const desejadas = ['[ui.status_line]', 'type = "command"', `command = 'node "${script}" grok'`, 'refresh_interval = 10'];
|
|
148
|
+
const atual = secao(toml.linhas, 'ui.status_line');
|
|
149
|
+
if (atual) {
|
|
150
|
+
const corpo = toml.linhas.slice(atual.inicio, atual.fim);
|
|
151
|
+
if (!corpo.some(l => /^\s*command\s*=/.test(l) && ehNossa(l))) return 'barra-do-aluno';
|
|
152
|
+
const sobra = corpo.slice(1).filter(l => l.trim() !== '' && !/^\s*(type|command|refresh_interval)\s*=/.test(l));
|
|
153
|
+
const novas = [...desejadas, ...sobra];
|
|
154
|
+
const limpas = corpo.filter(l => l.trim() !== '');
|
|
155
|
+
if (JSON.stringify(limpas) === JSON.stringify(novas)) return 'em-dia';
|
|
156
|
+
const linhas = [...toml.linhas];
|
|
157
|
+
const vazias = corpo.length - limpas.length;
|
|
158
|
+
linhas.splice(atual.inicio, corpo.length, ...novas, ...Array(vazias).fill(''));
|
|
159
|
+
gravarToml(arquivo, { ...toml, linhas });
|
|
160
|
+
return 'atualizada';
|
|
161
|
+
}
|
|
162
|
+
const ui = secao(toml.linhas, 'ui');
|
|
163
|
+
if (ui && chaveNaSecao(toml.linhas, ui, 'status_line') !== -1) return 'barra-do-aluno';
|
|
164
|
+
if (/^\s*ui\s*\.\s*status_line\s*[.=]/m.test(toml.texto)) return 'barra-do-aluno';
|
|
165
|
+
if (registro.grokLigadaEm) return 'tirada-pelo-aluno';
|
|
166
|
+
gravarToml(arquivo, acrescentarSecao(toml, desejadas));
|
|
167
|
+
return 'ligada';
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Claude Code: so a chave `statusLine` do ~/.claude/settings.json.
|
|
171
|
+
function ligarClaude({ claudeDir, registro, comando }) {
|
|
96
172
|
const settingsPath = path.join(claudeDir, 'settings.json');
|
|
97
173
|
let settings = {};
|
|
98
174
|
if (fs.existsSync(settingsPath)) {
|
|
99
175
|
settings = lerJson(settingsPath);
|
|
100
|
-
if (!settings || typeof settings !== 'object' || Array.isArray(settings))
|
|
101
|
-
return { estado: 'settings-ilegivel', detalhe: settingsPath };
|
|
102
|
-
}
|
|
176
|
+
if (!settings || typeof settings !== 'object' || Array.isArray(settings)) return 'settings-ilegivel';
|
|
103
177
|
}
|
|
104
178
|
const atual = settings.statusLine && typeof settings.statusLine.command === 'string' ? settings.statusLine.command : '';
|
|
105
179
|
let estado;
|
|
@@ -114,14 +188,66 @@ function instalarBarra({
|
|
|
114
188
|
gravarAtomico(settingsPath, `${JSON.stringify({ ...settings, statusLine: { ...(estado === 'em-dia' ? settings.statusLine : {}), ...desejada } }, null, 2)}\n`);
|
|
115
189
|
if (estado === 'em-dia') estado = 'atualizada';
|
|
116
190
|
}
|
|
191
|
+
return estado;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function seguro(fn) {
|
|
195
|
+
try {
|
|
196
|
+
return fn();
|
|
197
|
+
} catch (e) {
|
|
198
|
+
return `erro: ${String(e && e.message || e).split(/\r?\n/)[0].slice(0, 160)}`;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function instalarBarra({
|
|
203
|
+
home = os.homedir(),
|
|
204
|
+
claudeDir = process.env.CLAUDE_CONFIG_DIR || path.join(home, '.claude'),
|
|
205
|
+
codexDir = process.env.CODEX_HOME || path.join(home, '.codex'),
|
|
206
|
+
grokDir = process.env.GROK_HOME || path.join(home, '.grok'),
|
|
207
|
+
origem = null,
|
|
208
|
+
} = {}) {
|
|
209
|
+
if (fs.existsSync(path.join(home, '.auroq', 'sem-barra'))) return { estado: 'desligada' };
|
|
210
|
+
|
|
211
|
+
const pasta = path.join(home, '.auroq', 'barra');
|
|
212
|
+
const script = path.join(pasta, 'barra.cjs').split(path.sep).join('/');
|
|
213
|
+
// Aspas servem ao cmd.exe e aos shells POSIX; caminho com aspas, $, crase ou quebra de linha fica de fora.
|
|
214
|
+
if (/["\r\n`$%]/.test(script)) return { estado: 'caminho-nao-suportado', detalhe: script };
|
|
215
|
+
const comando = `node "${script}"`;
|
|
216
|
+
|
|
217
|
+
fs.mkdirSync(pasta, { recursive: true });
|
|
218
|
+
const motorNovo = copiarMotor(origem || origemPadrao(), path.join(pasta, 'ccstatusline'));
|
|
219
|
+
gravarAtomico(path.join(pasta, 'barra.cjs'), fs.readFileSync(path.join(AQUI, 'barra.cjs')));
|
|
220
|
+
|
|
221
|
+
// Layout: o nosso, a menos que o aluno tenha mexido no que gravamos da ultima vez.
|
|
222
|
+
const registroPath = path.join(pasta, 'instalacao.json');
|
|
223
|
+
const registro = lerJson(registroPath) || {};
|
|
224
|
+
const gravarLayout = (nome, shaAnterior) => {
|
|
225
|
+
const destino = path.join(pasta, nome);
|
|
226
|
+
const novo = fs.readFileSync(path.join(AQUI, nome), 'utf8').split('{{BARRA}}').join(comando.replace(/"/g, '\\"'));
|
|
227
|
+
const atual = fs.existsSync(destino) ? fs.readFileSync(destino, 'utf8') : null;
|
|
228
|
+
const doAluno = Boolean(atual !== null && shaAnterior && sha(atual) !== shaAnterior && sha(atual) !== sha(novo));
|
|
229
|
+
if (!doAluno && atual !== novo) gravarAtomico(destino, novo);
|
|
230
|
+
return { doAluno, sha: doAluno ? shaAnterior : sha(novo) };
|
|
231
|
+
};
|
|
232
|
+
const layoutClaude = gravarLayout('ccstatusline.json', registro.layoutSha);
|
|
233
|
+
const layoutGrok = gravarLayout('ccstatusline-grok.json', registro.layoutGrokSha);
|
|
234
|
+
const layoutDoAluno = layoutClaude.doAluno;
|
|
235
|
+
|
|
236
|
+
const estado = ligarClaude({ claudeDir, registro, comando });
|
|
117
237
|
|
|
238
|
+
const codex = seguro(() => ligarCodex({ codexDir, registro }));
|
|
239
|
+
const grok = seguro(() => ligarGrok({ grokDir, registro, script }));
|
|
240
|
+
const agora = new Date().toISOString();
|
|
118
241
|
const novoRegistro = {
|
|
119
242
|
versaoCcstatusline: VERSAO_CCSTATUSLINE,
|
|
120
|
-
layoutSha:
|
|
121
|
-
|
|
243
|
+
layoutSha: layoutClaude.sha,
|
|
244
|
+
layoutGrokSha: layoutGrok.sha,
|
|
245
|
+
ligadaEm: registro.ligadaEm || (estado === 'ligada' ? agora : undefined),
|
|
246
|
+
codexLigadaEm: registro.codexLigadaEm || (codex === 'ligada' ? agora : undefined),
|
|
247
|
+
grokLigadaEm: registro.grokLigadaEm || (grok === 'ligada' ? agora : undefined),
|
|
122
248
|
};
|
|
123
249
|
gravarAtomico(registroPath, `${JSON.stringify(novoRegistro, null, 2)}\n`);
|
|
124
|
-
return { estado, comando, motorNovo, layoutDoAluno
|
|
250
|
+
return { estado, comando, motorNovo, layoutDoAluno, codex, grok };
|
|
125
251
|
}
|
|
126
252
|
|
|
127
|
-
module.exports = { instalarBarra, ehNossa, ehDoApp, VERSAO_CCSTATUSLINE };
|
|
253
|
+
module.exports = { instalarBarra, ehNossa, ehDoApp, VERSAO_CCSTATUSLINE, CODEX_ITENS };
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schema": 1,
|
|
3
3
|
"algorithm": "sha256",
|
|
4
4
|
"scope": "packaged-runtime-and-cascas",
|
|
5
|
-
"version": "2.6.
|
|
5
|
+
"version": "2.6.10",
|
|
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": "
|
|
13
|
+
"sha256": "3a052bb9ae63bb567962ae6b06d3d65368174c1aa305d5c110c2473ac7db610f"
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
16
|
"path": ".auroq-core/core/synapse/context/context-builder.js",
|
|
@@ -210,7 +210,7 @@
|
|
|
210
210
|
},
|
|
211
211
|
{
|
|
212
212
|
"path": "bin/auroq-os.js",
|
|
213
|
-
"sha256": "
|
|
213
|
+
"sha256": "65da747dd205bdd0d5aef71167a174936d516f201b63276fd27caa6af55f3628"
|
|
214
214
|
},
|
|
215
215
|
{
|
|
216
216
|
"path": "lib/ahi/client.mjs",
|
|
@@ -230,7 +230,11 @@
|
|
|
230
230
|
},
|
|
231
231
|
{
|
|
232
232
|
"path": "lib/barra/barra.cjs",
|
|
233
|
-
"sha256": "
|
|
233
|
+
"sha256": "fcffa577918fba0c7dff103c6b55f424a071a84a74d5117f486ddd70e984113e"
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
"path": "lib/barra/ccstatusline-grok.json",
|
|
237
|
+
"sha256": "1a1449a7b06d2ec7e61c6d6403937b6bc29c487418dec07fa3669991ee236d76"
|
|
234
238
|
},
|
|
235
239
|
{
|
|
236
240
|
"path": "lib/barra/ccstatusline.json",
|
|
@@ -238,7 +242,7 @@
|
|
|
238
242
|
},
|
|
239
243
|
{
|
|
240
244
|
"path": "lib/barra/index.js",
|
|
241
|
-
"sha256": "
|
|
245
|
+
"sha256": "d5219d2a9f28f44633af56ec63e79a3f5f1ce2b4440f2daa8226c9f2868b5382"
|
|
242
246
|
},
|
|
243
247
|
{
|
|
244
248
|
"path": "lib/codex-windows.cjs",
|
|
@@ -274,7 +278,7 @@
|
|
|
274
278
|
},
|
|
275
279
|
{
|
|
276
280
|
"path": "package.json",
|
|
277
|
-
"sha256": "
|
|
281
|
+
"sha256": "b953fc664535e2336ccc89db3a07d391ac3a31c2ea8452996b9a83adac4612a4"
|
|
278
282
|
},
|
|
279
283
|
{
|
|
280
284
|
"path": "scripts/build-pack.mjs",
|