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.
- package/.auroq-core/como-funciona.md +162 -0
- package/.auroq-core/core/synapse/output/formatter.js +22 -2
- package/.auroq-core/core-config.yaml +1 -1
- package/.claude/CLAUDE.md +12 -4
- package/.claude/commands/auroq-companion.md +1 -1
- package/.claude/hooks/auroq-sync.cjs +80 -11
- package/.claude/hooks/precompact-session-digest.cjs +165 -29
- package/.claude/output-styles/direto.md +8 -3
- package/.claude/rules/agent-authority.md +5 -5
- package/.claude/rules/dna-operacional.md +1 -1
- package/.claude/rules/memoria-inteligente.md +71 -7
- package/.claude/rules/natural-language-first.md +3 -3
- package/.claude/rules/operacao-autonoma.md +20 -23
- package/.claude/rules/puxar-e-entregar.md +7 -7
- package/.claude/rules/titulo-da-barra.md +1 -1
- package/.claude/settings.local.json +11 -1
- package/.synapse/agent-companion +2 -1
- package/.synapse/agent-ops +4 -4
- package/.synapse/agent-organizer +2 -2
- package/.synapse/context +3 -3
- package/.synapse/global +1 -0
- package/.synapse/squad-clone-forge +1 -1
- package/.synapse/squad-etlmaker +2 -2
- package/.synapse/squad-squad-forge +1 -1
- package/AGENTS.md +149 -12
- package/CHANGELOG.md +46 -0
- package/README.md +5 -5
- package/agents/organizer/agents/organizer.md +8 -8
- package/agents/organizer/tasks/start.md +1 -1
- package/bin/auroq-os.js +76 -19
- package/installer/install.ps1 +2 -2
- package/installer/install.sh +2 -2
- package/lib/legacy-shims.json +17 -0
- package/lib/runtime-integrity.json +13 -13
- package/lib/synapse-hashes.json +2 -1
- package/package.json +5 -4
- package/scripts/runtime-integrity.mjs +1 -1
- package/scripts/sync-codex-skills.mjs +2 -1
- package/seeds/agents/companion/data/contexto-dinamico.md +1 -1
- package/seeds/agents/companion/data/padroes-observados.md +1 -1
- package/seeds/business/cockpit.md +2 -2
- 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
|
|
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
|
};
|
package/.claude/CLAUDE.md
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
# Auroq OS
|
|
2
2
|
|
|
3
|
-
Sistema operacional de IA para o negócio.
|
|
3
|
+
Sistema operacional de IA para o negócio do aluno. Ele pede, confere e aprova; a IA executa.
|
|
4
4
|
|
|
5
|
-
|
|
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
|
-
|
|
8
|
+
@../AGENTS.md
|
|
8
9
|
|
|
9
|
-
|
|
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
|
|
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
|
-
|
|
143
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
3
|
+
* Gancho da compactação do Claude Code (2.7.0).
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
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
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
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
|
-
*
|
|
13
|
-
*
|
|
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
|
|
22
|
+
const fs = require('node:fs');
|
|
23
|
+
const path = require('node:path');
|
|
19
24
|
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
33
|
+
let limpar = s => String(s);
|
|
32
34
|
try {
|
|
33
|
-
const {
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
}
|
|
40
|
-
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
|
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
|
|
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
|
|
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.
|