auroq-os 2.6.10 → 2.8.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.
Files changed (42) hide show
  1. package/.auroq-core/como-funciona.md +162 -0
  2. package/.auroq-core/core/synapse/output/formatter.js +22 -2
  3. package/.auroq-core/core-config.yaml +1 -1
  4. package/.claude/CLAUDE.md +12 -4
  5. package/.claude/commands/auroq-companion.md +1 -1
  6. package/.claude/hooks/auroq-sync.cjs +80 -11
  7. package/.claude/hooks/precompact-session-digest.cjs +165 -29
  8. package/.claude/output-styles/direto.md +8 -3
  9. package/.claude/rules/agent-authority.md +5 -5
  10. package/.claude/rules/dna-operacional.md +1 -1
  11. package/.claude/rules/memoria-inteligente.md +71 -7
  12. package/.claude/rules/natural-language-first.md +3 -3
  13. package/.claude/rules/operacao-autonoma.md +20 -23
  14. package/.claude/rules/puxar-e-entregar.md +7 -7
  15. package/.claude/rules/titulo-da-barra.md +1 -1
  16. package/.claude/settings.local.json +11 -1
  17. package/.synapse/agent-companion +2 -1
  18. package/.synapse/agent-ops +4 -4
  19. package/.synapse/agent-organizer +2 -2
  20. package/.synapse/context +3 -3
  21. package/.synapse/global +1 -0
  22. package/.synapse/squad-clone-forge +1 -1
  23. package/.synapse/squad-etlmaker +2 -2
  24. package/.synapse/squad-squad-forge +1 -1
  25. package/AGENTS.md +149 -12
  26. package/CHANGELOG.md +46 -0
  27. package/README.md +5 -5
  28. package/agents/organizer/agents/organizer.md +8 -8
  29. package/agents/organizer/tasks/start.md +1 -1
  30. package/bin/auroq-os.js +76 -19
  31. package/installer/install.ps1 +2 -2
  32. package/installer/install.sh +2 -2
  33. package/lib/legacy-shims.json +17 -0
  34. package/lib/runtime-integrity.json +13 -13
  35. package/lib/synapse-hashes.json +2 -1
  36. package/package.json +5 -4
  37. package/scripts/runtime-integrity.mjs +1 -1
  38. package/scripts/sync-codex-skills.mjs +2 -1
  39. package/seeds/agents/companion/data/contexto-dinamico.md +1 -1
  40. package/seeds/agents/companion/data/padroes-observados.md +1 -1
  41. package/seeds/business/cockpit.md +2 -2
  42. package/seeds/business/templates/tracker-template.md +1 -1
@@ -0,0 +1,162 @@
1
+ # Como o Auroq OS funciona por dentro
2
+
3
+ > Arquivo de consulta. Não carrega em toda conversa: leia quando o aluno perguntar "como funciona",
4
+ > quando for explicar o sistema ou quando a tarefa precisar desse mapa. As regras de trabalho estão
5
+ > no `AGENTS.md` da raiz da pasta e em `.claude/rules/`; em conflito, elas valem sobre este texto.
6
+ > Este arquivo é do sistema: a atualização troca.
7
+
8
+ ## A ideia
9
+
10
+ O Auroq OS transforma o Claude Code (ou o Codex) num centro de comando do negócio do aluno.
11
+
12
+ - **Pensar com IA:** decisões, planos e estratégia, com a IA como parceira.
13
+ - **Fazer com IA:** o aluno pede, a equipe de IA executa, o aluno confere e aprova.
14
+ - **Lembrar com IA:** o que foi decidido e aprendido fica salvo na pasta e cresce com o uso.
15
+
16
+ Fórmula: repertório + IA = resultado. O aluno domina o **o quê** (o que fazer e se ficou bom);
17
+ a IA domina o **como**.
18
+
19
+ ## Quem é quem na equipe
20
+
21
+ | Tipo | O que é | Exemplo |
22
+ |------|---------|---------|
23
+ | **Companion** | Chefe da equipe e parceiro do aluno. Situa, lembra, pensa junto e chama quem faz | `/auroq-companion` |
24
+ | **Mentes** | Pensam e julgam: consultam, mentoram, avaliam | Clone, mente sintética, consultor |
25
+ | **Workers** | Executam uma função (um cargo) | um worker de atendimento |
26
+ | **Squads** | Vários agentes com etapas e conferência de qualidade | Squad de conteúdo, de anúncios |
27
+ | **Ops** | O técnico: instala, atualiza, conserta, conecta | `/AuroqOS:agents:ops` |
28
+ | **Forjas** | Criam agentes novos para o negócio do aluno | Squad Forge, Mind Forge, Worker Forge, Clone Forge, ETLmaker |
29
+
30
+ Subtipos de mente: **Clone** (replica uma pessoa real), **Mente sintética** (junta vários
31
+ especialistas num agente só) e **Consultor** (empacota o conhecimento de um assunto).
32
+
33
+ Cada agente pode servir de **mentor** ("o que eu faço?"), **juiz** ("tá bom?") ou **executor** ("faz").
34
+ Num squad os papéis ficam separados: quem coordena não executa, quem executa não aprova o próprio
35
+ trabalho, quem julga não cria.
36
+
37
+ **Como isso aparece para o aluno:** ele só fala com o Companion. O Companion escolhe e aciona o
38
+ squad, a skill ou o agente certo sozinho, confere e entrega. O aluno não precisa saber o nome de ninguém.
39
+
40
+ ## Onde mora cada coisa
41
+
42
+ ```
43
+ pasta-do-negocio/
44
+ ├── AGENTS.md # regras de base (Claude, Codex, Grok) — do sistema
45
+ ├── .claude/
46
+ │ ├── CLAUDE.md # porta de entrada do Claude Code (importa o AGENTS.md)
47
+ │ ├── rules/ # regras do sistema + custom-do-aluno.md (do aluno)
48
+ │ ├── commands/ # atalhos dos agentes (/auroq-companion, /AuroqOS:agents:ops...)
49
+ │ ├── output-styles/ # estilo de resposta Direto
50
+ │ └── hooks/ # automações (Synapse, sincronização, histórico)
51
+ ├── .auroq-core/ # núcleo do sistema (constitution, configuração) — não mexer
52
+ ├── .synapse/ # lembretes curtos que o sistema injeta em cada conversa
53
+ ├── agents/companion/data/ # memória: onde paramos, decisões, padrões, pendências
54
+ ├── business/ # o negócio: cockpit, campanhas, processos, vault (fora do git)
55
+ └── docs/knowledge/ # conhecimento do aluno
56
+ ├── expert-mind/ # quem o aluno é: propósito, identidade, valores, tom de voz, história
57
+ └── expert-business/ # o que ele faz: posicionamento, público, método, produtos
58
+ ```
59
+
60
+ Os nomes `expert-mind/` e `expert-business/` ficaram assim por compatibilidade; o conteúdo é do aluno.
61
+ A equipe da Arcane (Companion interno, Consultor, forjas, Pack) não fica no disco: chega ao vivo pelo
62
+ MCP `arcane`.
63
+
64
+ ## Constitution (resumo)
65
+
66
+ Texto completo em `.auroq-core/constitution.md`.
67
+
68
+ | Artigo | Princípio |
69
+ |--------|-----------|
70
+ | I | O terminal é o centro de comando: o negócio passa pela pasta, ferramentas externas são braços |
71
+ | II | Cada um faz o seu: o técnico (push forçado, MCP, infraestrutura, atualização) é do Ops |
72
+ | III | Documentar é investir: o que não está escrito se perde |
73
+ | IV | Não inventar: fato, número e prova precisam de fonte |
74
+ | V | Qualidade com julgamento: o aluno aprova as entregas importantes |
75
+ | VI | Evolução incremental: reusar antes de adaptar, adaptar antes de criar |
76
+
77
+ ## Jeito de trabalhar de todo agente
78
+
79
+ - **Plano antes de execução** em trabalho grande: o plano é a coleira.
80
+ - **Documento de trabalho atualizado** a cada etapa: sobrevive à compactação da conversa e serve
81
+ de passagem de bastão para qualquer agente.
82
+ - **Sem viagem:** faz o pedido, não inventa melhoria; mudança de rumo só com o aluno.
83
+ - **Sem bagunça:** tarefas com entrada e saída claras, documentos valem mais que conversa,
84
+ conferência antes de aceitar a entrega.
85
+
86
+ Premissas: faça primeiro e documente depois; ninguém cuida do negócio como o dono; resolva a dor de
87
+ agora; a certeza vem do uso; nada volta para o zero.
88
+
89
+ ## Ciclos
90
+
91
+ **Dia a dia:** situar ("onde estou?") → priorizar ("o que dói mais?") → executar → documentar →
92
+ refinar (melhorar agentes e processos). Cada volta deixa o sistema mais forte.
93
+
94
+ **Conversa:** o Companion carrega onde paramos, mostra o que importa, o aluno escolhe o foco, a
95
+ equipe trabalha, no fim de cada tarefa a memória é salva e, quando o aluno pede, o trabalho é salvo
96
+ e entregue no GitHub.
97
+
98
+ **Semana:** sem revisão há 7 dias ou mais, o Companion propõe uma revisão de 20 minutos (cockpit,
99
+ trackers, pendências, padrões).
100
+
101
+ **Projeto:** nasce no cockpit com tracker e fases → os agentes trabalham e atualizam o tracker →
102
+ termina com uma retrospectiva de 3 perguntas e vai para o arquivo, abrindo vaga para o próximo.
103
+
104
+ ## Níveis de uso de IA
105
+
106
+ | Nível | Como é |
107
+ |-------|--------|
108
+ | N0 | Sem IA, na força bruta |
109
+ | N1 | IA crua: cada conversa começa do zero |
110
+ | N2 | IA com repertório: skills, mentes e contexto que fica |
111
+ | N3 | Sistema operacional de IA: equipe coordenada, memória e Companion |
112
+
113
+ O Auroq OS leva o aluno ao N3.
114
+
115
+ ## Conhecimento, skills e agentes
116
+
117
+ - **Base de conhecimento:** conhecimento bruto tratado (extraído, limpo, organizado). É o ingrediente.
118
+ - **Skill:** IA + base de conhecimento + persona + regras. É o prato.
119
+ - Caminho: conhecimento bruto → tratamento (o ETLmaker faz) → base pronta → skill → agente.
120
+ Conhecimento primeiro, execução depois.
121
+
122
+ Três camadas de contexto que a equipe usa:
123
+
124
+ | Pergunta | De onde vem |
125
+ |----------|-------------|
126
+ | Para quem trabalho? | `docs/knowledge/expert-mind/` |
127
+ | O que sei fazer? | `docs/knowledge/expert-business/` e o conhecimento que o aluno tratou |
128
+ | O que está rolando agora? | `business/campanhas/` e o cockpit |
129
+
130
+ Tipos de skill: de **identidade** (do aluno, únicas), **técnicas** (copy, tráfego, lançamento) e de
131
+ **projeto** (de uma campanha, temporárias).
132
+
133
+ ## Memória
134
+
135
+ | Camada | Onde | Dura |
136
+ |--------|------|------|
137
+ | Conversa | o chat | até a compactação |
138
+ | Operacional | `agents/companion/data/` | entre conversas |
139
+ | Permanente | `docs/knowledge/` e `business/` | para sempre |
140
+
141
+ Quando salvar: decisão tomada, projeto avançou, conhecimento criado, padrão percebido, tarefa
142
+ concluída e conversa encerrando. Regra completa: `.claude/rules/memoria-inteligente.md`.
143
+
144
+ ## Projetos
145
+
146
+ - Ideias soltas viram pendência; o que vai ser feito entra no **cockpit** (`business/cockpit.md`),
147
+ com no máximo 3 projetos ativos, cada um com a próxima ação definida.
148
+ - Receitas que se repetem viram **processos** em `business/processos/`.
149
+ - Cada projeto tem um **tracker** (`business/campanhas/{projeto}/tracker.md`): todo agente lê antes de
150
+ trabalhar e atualiza depois (`.claude/rules/project-tracker.md`).
151
+
152
+ ## Salvar é o botão do negócio
153
+
154
+ Commit aqui não é coisa de programador: é o ponto de restauração do negócio. A mensagem conta o que
155
+ aconteceu (`progresso:`, `decisao:`, `processo:`, `agente:`, `conhecimento:`, `campanha:`, `fix:`,
156
+ `setup:`), não o que mudou nos arquivos. Entregar (push) manda a cópia para o GitHub privado do aluno.
157
+ Ritual em `.claude/rules/puxar-e-entregar.md`.
158
+
159
+ ## Volante
160
+
161
+ Conhecimento cresce → agentes melhoram → o negócio do aluno anda → o aluno aprende mais → o
162
+ conhecimento cresce de novo. Cada uso acelera o próximo.
@@ -23,6 +23,18 @@ const { estimateTokens } = require('../utils/tokens');
23
23
  * Truncation removes from the END of this array first (SUMMARY, then
24
24
  * KEYWORD, then SQUAD, etc.) to preserve highest-priority sections.
25
25
  */
26
+ /**
27
+ * Regras que mais escapam em conversa longa (2.7.0). O resto já está no AGENTS.md e nas rules;
28
+ * aqui fica só o que costuma sumir depois de muitas mensagens.
29
+ */
30
+ const LEMBRETE_CONVERSA_LONGA = [
31
+ '[LEMBRETE: CONVERSA LONGA]',
32
+ ' 1. Resposta que fez trabalho fecha com **Resumo:** e **Onde parou:**.',
33
+ ' 2. Travas do AGENTS.md (dinheiro, mensagem ou post para outra pessoa, apagar, force push, senha): só com ordem do aluno para aquela ação.',
34
+ ' 3. Tarefa concluída: salve onde parou e as decisões na memória (rule memoria-inteligente) antes de seguir.',
35
+ ' 4. Companion aciona sozinho squads, skills e agentes, revisa e entrega; não manda o aluno chamar ninguém.',
36
+ ].join('\n');
37
+
26
38
  const SECTION_ORDER = [
27
39
  'CONTEXT_BRACKET',
28
40
  'CONSTITUTION',
@@ -400,7 +412,7 @@ function enforceTokenBudget(sections, sectionIds, tokenBudget) {
400
412
  }
401
413
 
402
414
  // Sections that should never be removed
403
- const PROTECTED = new Set(['CONTEXT_BRACKET', 'CONSTITUTION', 'AGENT']);
415
+ const PROTECTED = new Set(['CONTEXT_BRACKET', 'CONSTITUTION', 'AGENT', 'CONVERSA_LONGA']);
404
416
 
405
417
  // Truncation priority: remove from end first
406
418
  const TRUNCATION_ORDER = [
@@ -531,9 +543,16 @@ function formatSynapseRules(results, bracket, contextPercent, session, devmode,
531
543
  }
532
544
  }
533
545
 
546
+ // Conversa longa (2.7.0): do MODERATE em diante, as regras que mais escapam quando a conversa
547
+ // enche voltam a cada mensagem. Acréscimo: nada do que já era injetado sai.
548
+ if (bracket && bracket !== 'FRESH') {
549
+ sections.push(LEMBRETE_CONVERSA_LONGA);
550
+ sectionIds.push('CONVERSA_LONGA');
551
+ }
552
+
534
553
  // Handoff warning (CRITICAL bracket)
535
554
  if (showHandoffWarning) {
536
- sections.push('[HANDOFF WARNING]\n Context is nearly exhausted. Consider starting a new session to preserve quality.');
555
+ sections.push('[HANDOFF WARNING]\n A conversa está quase cheia. Termine o passo atual, salve a memória e sugira ao aluno abrir uma conversa nova (ou /compact).');
537
556
  sectionIds.push('HANDOFF_WARNING');
538
557
  }
539
558
 
@@ -563,4 +582,5 @@ module.exports = {
563
582
  estimateTokens,
564
583
  SECTION_ORDER,
565
584
  LAYER_TO_SECTION,
585
+ LEMBRETE_CONVERSA_LONGA,
566
586
  };
@@ -1,7 +1,7 @@
1
1
  project:
2
2
  name: "Auroq OS"
3
3
  type: greenfield
4
- version: 2.6.10
4
+ version: 2.8.0
5
5
  installedAt: '2026-03-16'
6
6
 
7
7
  ide:
package/.claude/CLAUDE.md CHANGED
@@ -1,9 +1,17 @@
1
1
  # Auroq OS
2
2
 
3
- Sistema operacional de IA para o negócio. Você manda e julga; a IA executa.
3
+ Sistema operacional de IA para o negócio do aluno. Ele pede, confere e aprova; a IA executa.
4
4
 
5
- Ative um agente pelo comando (`/companion`, `$companion`, `@companion`). Todo conteúdo Arcane (Companion interno, Consultor, forjas, Pack) vem pelo MCP `arcane` — nunca procurar em `agents/`.
5
+ As regras de base de toda conversa estão no `AGENTS.md` da raiz da pasta, o mesmo arquivo que o Codex
6
+ e o Grok leem. Ele entra aqui pela linha abaixo:
6
7
 
7
- Sua memória e seus documentos ficam na sua máquina e no seu GitHub privado (senhas e `business/vault/` ficam fora). O histórico das sessões com os squads está previsto nos Termos de Uso: vem ligado para quem aceita os termos na plataforma (quem já tinha dito "não" continua sem gravar) e você desliga quando quiser em Minha Conta, "Guardar minhas conversas". Se a pasta do seu negócio tem dados de clientes, desligue. Além disso, a Arka registra qual conteúdo licenciado foi entregue, quando e pra qual conta — igual a uma nota de entrega.
8
+ @../AGENTS.md
8
9
 
9
- Rules em `.claude/rules/`. Constitution em `.auroq-core/constitution.md`.
10
+ ## Só no Claude Code
11
+
12
+ - As regras de `.claude/rules/` carregam sozinhas. `.claude/rules/custom-do-aluno.md` vale acima de todas.
13
+ - O estilo de resposta é o Direto (`.claude/output-styles/direto.md`).
14
+ - Agentes pelo comando: `/auroq-companion` (ou o nome que o aluno deu ao Companion, como
15
+ `/atlas-companion`), `/AuroqOS:agents:ops`, `/auroq-consultor` e as forjas `/auroq-squad-forge`,
16
+ `/auroq-mind-forge`, `/auroq-worker-forge`, `/auroq-clone-forge`, `/auroq-etlmaker`. O aluno não
17
+ precisa usar nenhum: falar o que quer basta.
@@ -1,6 +1,6 @@
1
1
  # companion
2
2
 
3
- Parceiro cognitivo pessoal. Situa, lembra, pensa junto, roteia.
3
+ Parceiro cognitivo pessoal. Situa, lembra, pensa junto e aciona a equipe sozinho.
4
4
 
5
5
  Todo conteúdo deste agente vem pelo MCP `arcane` (catálogo `auroq-core`). Chame a tool `squad_ativar` com recurso_id `companion`. Enquanto o servidor responder, nunca procurar arquivo local deste agente.
6
6
 
@@ -4,7 +4,8 @@
4
4
  * Auroq OS — Sync Hook
5
5
  *
6
6
  * --session-start (SessionStart) checa o GitHub ao abrir; PUXA se for seguro; relata o que chegou
7
- * --remind (UserPromptSubmit) lembra, com moderacao, de trabalho nao entregue
7
+ * --remind (UserPromptSubmit) lembra, com moderacao, de trabalho nao entregue, e traz da
8
+ * memoria do negocio (app Auroq OS) o que tem a ver com o pedido
8
9
  *
9
10
  * Regras de ouro:
10
11
  * - Nunca bloqueia a sessao (sempre exit 0) e fica em silencio quando nao ha o que dizer.
@@ -97,8 +98,23 @@ function versionNotice() {
97
98
  return `[Auroq update] Saiu versao nova do sistema: v${local} instalada → v${latest} disponivel. Na sua primeira resposta, avise o expert em 1 frase simples e pergunte: "Quer que eu atualize agora? Leva uns 5 minutos e voce nao precisa fazer nada." Se ele disser sim: a atualizacao e feita pelo Ops, sem o expert abrir terminal — chame a tool forja_bundle (MCP arcane) com forge_id "ops" e fase "manutencao" e siga aqueles passos a risca (e o mesmo que ele pedir ao Ops "atualiza o sistema"). Se ele disser nao, nao pergunte de novo nesta conversa.`;
98
99
  }
99
100
 
101
+ // ─── Bilhete da memória (app Auroq OS, Fase 1 da memória do aluno) ───────────
102
+ // O app refaz `agents/companion/data/nucleo.md` sozinho depois das conversas (teto de 3.000
103
+ // caracteres). Toda conversa nova comeca com ele, no lugar de ler os arquivos inteiros da memoria.
104
+ const NUCLEO = path.join('agents', 'companion', 'data', 'nucleo.md');
105
+ const NUCLEO_MAX = 3000;
106
+
107
+ function nucleoNotice() {
108
+ let texto;
109
+ try { texto = fs.readFileSync(path.join(CWD, NUCLEO), 'utf8'); } catch { return null; }
110
+ texto = texto.replace(/^\uFEFF/, '').replace(/\r\n/g, '\n').trim();
111
+ if (!texto) return null;
112
+ if (texto.length > NUCLEO_MAX) texto = `${texto.slice(0, NUCLEO_MAX - 1)}…`;
113
+ return `[Memoria do sistema] Bilhete da memoria deste negocio (${NUCLEO}, refeito sozinho pelo app Auroq OS a partir das conversas anteriores; vale para todo agente, squad e skill desta pasta, nao so o Companion). Use para saber quem e o aluno, onde voces pararam, o que ja foi decidido e o que esta pendente; nao leia para o aluno sem ele pedir. Detalhes em agents/companion/data/.\n${texto}`;
114
+ }
115
+
100
116
  function sessionStart() {
101
- const vNotice = versionNotice();
117
+ const vNotice = [nucleoNotice(), versionNotice()].filter(Boolean).join('\n') || null;
102
118
  if (!isRepo() || !hasRemote()) { emit('SessionStart', vNotice); return; }
103
119
  const up = upstream();
104
120
  if (!up) { emit('SessionStart', vNotice); return; }
@@ -139,8 +155,54 @@ function sessionStart() {
139
155
  emit('SessionStart', lines.join('\n'));
140
156
  }
141
157
 
142
- function remind() {
143
- if (!isRepo()) return;
158
+ // ─── Busca na memória a cada pedido (app Auroq OS, Fase 2 da memória do aluno) ─────
159
+ // O app mantém um índice da memória do negócio; `auroq memoria busca --gancho` devolve até 3 trechos
160
+ // (1.500 caracteres) que passam no filtro de relevância, ou nada. Pedido trivial não busca. Sem o app
161
+ // (comando ausente, app fechado, demorou), segue em silêncio: a conversa nunca espera por isso.
162
+ const MEMORIA = path.join('agents', 'companion', 'data');
163
+ const TEMPO_DA_BUSCA_MS = 3500;
164
+ const TRIVIAL = /^(?:oi|ola|olá|ok|blz|beleza|obrigad[oa]|valeu|sim|n[aã]o|pode|segue|continua|bom dia|boa tarde|boa noite|top|show|perfeito|certo|isso|entendi|bora|go)\b/i;
165
+
166
+ function acharAuroq() {
167
+ const nomes = process.platform === 'win32' ? ['auroq.exe'] : ['auroq'];
168
+ const pastas = String(process.env.PATH || '').split(path.delimiter).filter(Boolean);
169
+ if (process.platform === 'darwin') pastas.push('/Applications/Auroq OS.app/Contents/Resources/bin');
170
+ for (const pasta of pastas) {
171
+ for (const nome of nomes) {
172
+ const alvo = path.join(pasta, nome);
173
+ try { if (fs.statSync(alvo).isFile()) return alvo; } catch { /* segue */ }
174
+ }
175
+ }
176
+ return null;
177
+ }
178
+
179
+ function pedidoDoAluno(entrada) {
180
+ try {
181
+ const dado = JSON.parse(entrada || '{}');
182
+ return typeof dado.prompt === 'string' ? dado.prompt : '';
183
+ } catch {
184
+ return '';
185
+ }
186
+ }
187
+
188
+ function buscaNaMemoria(pedido) {
189
+ const texto = String(pedido || '').replace(/\s+/g, ' ').trim();
190
+ if (texto.length < 8 || (texto.length < 30 && TRIVIAL.test(texto))) return null;
191
+ if (!fs.existsSync(path.join(CWD, MEMORIA))) return null;
192
+ const programa = acharAuroq();
193
+ if (!programa) return null;
194
+ try {
195
+ const saida = execFileSync(programa, ['memoria', 'busca', texto.slice(0, 600), '--gancho'], {
196
+ cwd: CWD, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: TEMPO_DA_BUSCA_MS, windowsHide: true,
197
+ }).trim();
198
+ return /^\[Memoria do (sistema|Companion)\]/.test(saida) ? saida.slice(0, 2500) : null;
199
+ } catch {
200
+ return null;
201
+ }
202
+ }
203
+
204
+ function lembrete() {
205
+ if (!isRepo()) return null;
144
206
  const dirty = dirtyFiles();
145
207
  const up = upstream();
146
208
  const ahead = up ? countRange(`${up}..HEAD`) : 0;
@@ -148,30 +210,37 @@ function remind() {
148
210
  const now = Date.now();
149
211
  if (dirty.length === 0 && ahead === 0) {
150
212
  if (st.dirtySince) { st.dirtySince = 0; writeState(st); }
151
- return;
213
+ return null;
152
214
  }
153
- if (!st.dirtySince) { st.dirtySince = now; writeState(st); return; }
154
- if (now - st.dirtySince < REMIND_AFTER_MS) return;
155
- if (st.lastRemind && now - st.lastRemind < REMIND_EVERY_MS) return;
215
+ if (!st.dirtySince) { st.dirtySince = now; writeState(st); return null; }
216
+ if (now - st.dirtySince < REMIND_AFTER_MS) return null;
217
+ if (st.lastRemind && now - st.lastRemind < REMIND_EVERY_MS) return null;
156
218
  st.lastRemind = now;
157
219
  writeState(st);
158
220
  const mins = Math.round((now - st.dirtySince) / 60000);
159
- emit('UserPromptSubmit', `[Auroq lembrete] Ha trabalho nao entregue ha ~${mins} min (${dirty.length} arquivo(s) alterado(s)${ahead ? `, ${ahead} commit(s) sem push` : ''}). NAO interrompa o raciocinio atual. Ao fechar o bloco em andamento, ofereca em UMA linha: "quer que eu salve e entregue pro GitHub?". Nunca entregue sem o expert pedir.`);
221
+ return (`[Auroq lembrete] Ha trabalho nao entregue ha ~${mins} min (${dirty.length} arquivo(s) alterado(s)${ahead ? `, ${ahead} commit(s) sem push` : ''}). NAO interrompa o raciocinio atual. Ao fechar o bloco em andamento, ofereca em UMA linha: "quer que eu salve e entregue pro GitHub?". Nunca entregue sem o expert pedir.`);
222
+ }
223
+
224
+ function remind(entrada) {
225
+ const partes = [buscaNaMemoria(pedidoDoAluno(entrada)), lembrete()].filter(Boolean);
226
+ emit('UserPromptSubmit', partes.join('\n\n') || null);
160
227
  }
161
228
 
162
229
  let ran = false;
230
+ let entrada = '';
163
231
  function main() {
164
232
  if (ran) return;
165
233
  ran = true;
166
- try { if (MODE === '--remind') remind(); else sessionStart(); }
234
+ try { if (MODE === '--remind') remind(entrada); else sessionStart(); }
167
235
  catch (e) { console.error(`[auroq-sync] ${e.message}`); }
168
236
  process.exit(0);
169
237
  }
170
238
 
171
239
  // Consome o JSON que o Claude Code envia no stdin (nao depende dele) e roda no fim;
172
240
  // fallback por timer garante execucao mesmo se o stdin nao fechar.
241
+ // O pedido do aluno (UserPromptSubmit) vem nesse JSON e é o que a busca da memória usa.
173
242
  process.stdin.resume();
174
- process.stdin.on('data', () => {});
243
+ process.stdin.on('data', (pedaco) => { if (entrada.length < 200000) entrada += pedaco; });
175
244
  process.stdin.on('end', main);
176
245
  process.stdin.on('error', main);
177
246
  setTimeout(main, 400).unref();
@@ -1,46 +1,182 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Claude Code Hook: PreCompact Session Digest
3
+ * Gancho da compactação do Claude Code (2.7.0).
4
4
  *
5
- * This hook is registered with Claude Code to trigger before context compact.
6
- * It delegates to the unified hook runner in auroq-core.
5
+ * Quando a conversa enche, o Claude Code resume e "compacta" o histórico. O resumo perde detalhe e a
6
+ * IA costuma esquecer o que estava fazendo e as regras de fechamento. Este gancho roda em dois momentos:
7
7
  *
8
- * Installation:
9
- * - Claude Code automatically discovers hooks in .claude/hooks/
10
- * - Hook naming: {event}-{name}.js (e.g., precompact-session-digest.js)
8
+ * - PreCompact: lê o fim da conversa (arquivo `transcript_path` que o Claude Code entrega) e grava em
9
+ * `.auroq/compactacao/ultima.md` os últimos pedidos do aluno, os arquivos mexidos e a última resposta.
10
+ * Fica só no computador do aluno (`.auroq/` não vai para o GitHub) e passa pelo mesmo filtro de
11
+ * senhas e chaves do registro de conversas.
12
+ * - SessionStart com origem "compact" (logo depois do resumo): devolve à IA um lembrete curto com esse
13
+ * retrato e as regras que mais escapam em conversa longa.
11
14
  *
12
- * @see .auroq-core/hooks/unified/runners/precompact-runner.js
13
- * @see Story MIS-3 - Session Digest (PreCompact Hook)
15
+ * Nunca bloqueia a compactação nem a conversa: qualquer falha sai em silêncio com código 0.
16
+ * Até a 2.6.10 este arquivo carregava um executor de `.auroq-core/hooks/unified/` que não
17
+ * existe no pacote, e só exportava uma função que ninguém chamava: não salvava nada.
14
18
  */
15
19
 
16
20
  'use strict';
17
21
 
18
- const path = require('path');
22
+ const fs = require('node:fs');
23
+ const path = require('node:path');
19
24
 
20
- // Resolve path to the unified hook runner
21
- const PROJECT_ROOT = path.resolve(__dirname, '..', '..');
22
- const runnerPath = path.join(
23
- PROJECT_ROOT,
24
- '.auroq-core',
25
- 'hooks',
26
- 'unified',
27
- 'runners',
28
- 'precompact-runner.js',
29
- );
25
+ const RAIZ = path.resolve(__dirname, '..', '..');
26
+ const PASTA = path.join(RAIZ, '.auroq', 'compactacao');
27
+ const ARQUIVO = path.join(PASTA, 'ultima.md');
28
+ const LER_DO_FIM = 4 * 1024 * 1024; // conversa grande: só o fim interessa
29
+ const VALE_POR_MS = 2 * 60 * 60 * 1000; // retrato com mais de 2 h não volta para a conversa
30
+ const MAX_PEDIDOS = 6;
31
+ const MAX_ARQUIVOS = 15;
30
32
 
31
- // Load and execute the hook runner
33
+ let limpar = s => String(s);
32
34
  try {
33
- const { onPreCompact } = require(runnerPath);
35
+ const { scrub } = require('./ahi/security.cjs');
36
+ limpar = s => String(scrub(String(s)).value);
37
+ } catch { /* sem o filtro do registro de conversas: o texto passa pelo corte abaixo */ }
34
38
 
35
- // Export the hook handler for Claude Code
36
- module.exports = async (context) => {
37
- return await onPreCompact(context);
39
+ function cortar(texto, max) {
40
+ const t = String(texto || '').replace(/\s+/g, ' ').trim();
41
+ return t.length > max ? `${t.slice(0, max - 1)}…` : t;
42
+ }
43
+
44
+ function lerEntrada() {
45
+ try {
46
+ const bruto = fs.readFileSync(0, 'utf8');
47
+ const dados = JSON.parse(bruto.replace(/^/, ''));
48
+ return dados && typeof dados === 'object' ? dados : {};
49
+ } catch { return {}; }
50
+ }
51
+
52
+ function fimDoArquivo(arquivo) {
53
+ const fd = fs.openSync(arquivo, 'r');
54
+ try {
55
+ const { size } = fs.fstatSync(fd);
56
+ const inicio = Math.max(0, size - LER_DO_FIM);
57
+ const buf = Buffer.alloc(size - inicio);
58
+ fs.readSync(fd, buf, 0, buf.length, inicio);
59
+ const linhas = buf.toString('utf8').split('\n');
60
+ if (inicio > 0) linhas.shift(); // primeira linha pode ter vindo cortada
61
+ return linhas;
62
+ } finally { fs.closeSync(fd); }
63
+ }
64
+
65
+ const textoDe = conteudo => (typeof conteudo === 'string'
66
+ ? conteudo
67
+ : (Array.isArray(conteudo) ? conteudo.filter(p => p && p.type === 'text').map(p => p.text || '').join('\n') : ''));
68
+
69
+ // /tmp e /private/tmp (Mac), atalhos e letras de unidade: compara o caminho de verdade dos dois lados.
70
+ // Arquivo que já não existe: resolve a pasta mais próxima que existe e junta o resto.
71
+ function real(p) {
72
+ let base = path.resolve(p);
73
+ const resto = [];
74
+ for (;;) {
75
+ try { return path.join(fs.realpathSync.native(base), ...resto); } catch { /* sobe uma pasta */ }
76
+ const acima = path.dirname(base);
77
+ if (acima === base) return path.resolve(p);
78
+ resto.unshift(path.basename(base));
79
+ base = acima;
80
+ }
81
+ }
82
+
83
+ function relativo(arquivo) {
84
+ const abs = real(path.resolve(RAIZ, String(arquivo)));
85
+ const rel = path.relative(real(RAIZ), abs);
86
+ return (rel && !rel.startsWith('..') && !path.isAbsolute(rel) ? rel : abs).split(path.sep).join('/');
87
+ }
88
+
89
+ function retrato(transcript) {
90
+ const pedidos = [];
91
+ const arquivos = [];
92
+ let ultimaResposta = '';
93
+ for (const linha of fimDoArquivo(transcript)) {
94
+ if (!linha.trim()) continue;
95
+ let ev;
96
+ try { ev = JSON.parse(linha); } catch { continue; }
97
+ const msg = ev && ev.message;
98
+ if (!msg || ev.isMeta || ev.isSidechain) continue;
99
+ if (ev.type === 'user' && msg.role === 'user') {
100
+ const texto = textoDe(msg.content).trim();
101
+ // Resultado de ferramenta, lembrete do sistema e comando com marcação não são pedido do aluno.
102
+ if (texto && !texto.startsWith('<')) pedidos.push(texto);
103
+ } else if (ev.type === 'assistant' && Array.isArray(msg.content)) {
104
+ for (const parte of msg.content) {
105
+ if (parte && parte.type === 'text' && String(parte.text || '').trim()) ultimaResposta = parte.text;
106
+ const alvo = parte && parte.type === 'tool_use' && parte.input && (parte.input.file_path || parte.input.notebook_path);
107
+ if (alvo && /^(Edit|MultiEdit|Write|NotebookEdit)$/.test(String(parte.name))) {
108
+ const rel = relativo(alvo);
109
+ const i = arquivos.indexOf(rel);
110
+ if (i >= 0) arquivos.splice(i, 1);
111
+ arquivos.push(rel);
112
+ }
113
+ }
114
+ }
115
+ }
116
+ return {
117
+ pedidos: pedidos.slice(-MAX_PEDIDOS).map(p => cortar(limpar(p), 400)),
118
+ arquivos: arquivos.slice(-MAX_ARQUIVOS).map(a => cortar(a, 200)), // caminho não passa no filtro: ele apaga nome longo
119
+ ultimaResposta: cortar(limpar(ultimaResposta), 700),
38
120
  };
39
- } catch (error) {
40
- console.error('[PreCompact Hook] Failed to load hook runner:', error.message);
121
+ }
122
+
123
+ function antesDeCompactar(entrada) {
124
+ const transcript = entrada.transcript_path;
125
+ if (!transcript || !fs.existsSync(transcript)) return;
126
+ const r = retrato(transcript);
127
+ const quando = new Date().toISOString();
128
+ const md = [
129
+ '# Antes da última compactação',
130
+ '',
131
+ `- Quando: ${quando}`,
132
+ `- Conversa: ${cortar(entrada.session_id || '?', 80)} (${entrada.trigger === 'manual' ? 'compactação pedida' : 'compactação automática'})`,
133
+ '',
134
+ '## Últimos pedidos do aluno (do mais antigo ao mais novo)',
135
+ '',
136
+ ...(r.pedidos.length ? r.pedidos.map(p => `- ${p}`) : ['- (nenhum pedido em texto no fim da conversa)']),
137
+ '',
138
+ '## Arquivos mexidos',
139
+ '',
140
+ ...(r.arquivos.length ? r.arquivos.map(a => `- \`${a}\``) : ['- (nenhum)']),
141
+ '',
142
+ '## Última resposta da IA',
143
+ '',
144
+ r.ultimaResposta || '(sem texto)',
145
+ '',
146
+ ].join('\n');
147
+ fs.mkdirSync(PASTA, { recursive: true });
148
+ const tmp = `${ARQUIVO}.${process.pid}.tmp`;
149
+ fs.writeFileSync(tmp, md, { mode: 0o600 });
150
+ fs.renameSync(tmp, ARQUIVO);
151
+ }
41
152
 
42
- // Graceful degradation - export no-op function
43
- module.exports = async () => {
44
- console.log('[PreCompact Hook] Hook runner not available, skipping');
153
+ function depoisDeCompactar() {
154
+ let st;
155
+ try { st = fs.statSync(ARQUIVO); } catch { return; }
156
+ if (Date.now() - st.mtimeMs > VALE_POR_MS) return;
157
+ const md = fs.readFileSync(ARQUIVO, 'utf8');
158
+ const secao = nome => {
159
+ const m = md.split(`## ${nome}`)[1];
160
+ return m ? m.split('\n## ')[0].trim() : '';
45
161
  };
162
+ const ultimoPedido = (secao('Últimos pedidos do aluno (do mais antigo ao mais novo)').split('\n').filter(l => l.startsWith('- ')).pop() || '').slice(2);
163
+ const arquivos = secao('Arquivos mexidos').split('\n').filter(l => l.startsWith('- `')).map(l => l.slice(2)).slice(-8);
164
+ const contexto = [
165
+ 'A conversa acabou de ser compactada. O retrato de antes do resumo está em `.auroq/compactacao/ultima.md` (leia se precisar do detalhe).',
166
+ ultimoPedido ? `Último pedido do aluno: ${ultimoPedido}` : '',
167
+ arquivos.length ? `Arquivos mexidos: ${arquivos.join(', ')}` : '',
168
+ 'Antes de seguir: se uma tarefa já terminou e a memória não foi salva (`agents/companion/data/contexto-dinamico.md` e `log-decisoes.md`), salve agora.',
169
+ 'Continua valendo: fechar a resposta que fez trabalho com **Resumo:** e **Onde parou:**; as travas do AGENTS.md (dinheiro, mensagem ou post para outra pessoa, apagar, force push, senha) só com ordem do aluno para aquela ação.',
170
+ ].filter(Boolean).join('\n');
171
+ process.stdout.write(JSON.stringify({ hookSpecificOutput: { hookEventName: 'SessionStart', additionalContext: contexto } }));
172
+ }
173
+
174
+ function principal() {
175
+ const entrada = lerEntrada();
176
+ const evento = String(entrada.hook_event_name || '');
177
+ if (evento === 'PreCompact') antesDeCompactar(entrada);
178
+ else if (evento === 'SessionStart' && entrada.source === 'compact') depoisDeCompactar();
46
179
  }
180
+
181
+ try { principal(); } catch { /* nunca atrapalha a conversa */ }
182
+ process.exitCode = 0;
@@ -8,12 +8,17 @@ keep-coding-instructions: true
8
8
 
9
9
  Comece pelo resultado. Pergunta simples recebe resposta simples; entrega ou
10
10
  analise costuma caber em um paragrafo de 3 a 5 frases com: o que aconteceu, o
11
- contexto minimo, o que muda para o expert e a decisao necessaria.
11
+ contexto minimo, o que muda para o aluno e a decisao necessaria.
12
12
 
13
- - Use **Na sua mao:** somente quando houver uma decisao ou acao real do expert.
13
+ - Use **Na sua mao:** somente quando houver uma decisao ou acao real do aluno.
14
14
  - Use **Risco:** somente para risco material que muda a decisao.
15
+ - Decisao so quando nenhuma ferramenta resolve, assim: **Decide: A ou B?** A → o que acontece. B → o que acontece.
15
16
  - Preserve ressalva importante, teste que falhou, erro corrigido e atalho tomado.
16
17
  - Explique primeiro o efeito em linguagem comum; detalhe tecnico so quando ajuda a decidir.
17
18
  - Sem preambulo, repeticao, validacao vazia, emoji ou tabela quando uma frase resolve.
18
- - Nao termine oferecendo aprofundamento. Se o expert pedir "aprofunda X", entregue apenas o detalhe daquele ponto.
19
+ - Nao termine oferecendo aprofundamento. Se o aluno pedir "aprofunda X", entregue apenas o detalhe daquele ponto.
19
20
  - O tamanho acompanha o pedido. Concisao corta enfeite, nunca fato que muda a decisao.
21
+ - Resposta que fez trabalho termina com duas linhas curtas, em linguagem simples: **Resumo:** do
22
+ que se trata a conversa e o que ja ficou feito; **Onde parou:** o que ja funciona, o que segue
23
+ rodando e o que espera pelo aluno (ou "fechado, nada pendente"). Pergunta simples respondida em
24
+ uma frase dispensa.