grimoire-framework 1.3.0 → 1.4.1

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.
@@ -7,8 +7,8 @@
7
7
  # - SHA256 hashes for change detection
8
8
  # - File types for categorization
9
9
  #
10
- version: 1.3.0
11
- generated_at: "2026-02-22T16:50:35.905Z"
10
+ version: 1.4.1
11
+ generated_at: "2026-02-22T17:28:34.901Z"
12
12
  generator: scripts/generate-install-manifest.js
13
13
  file_count: 1011
14
14
  files:
package/README.md CHANGED
@@ -1,171 +1,209 @@
1
- # Grimoire: AI-Orchestrated System for Full Stack Development 🧙‍♂️
1
+ # 🔮 Grimoire Framework
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/grimoire-framework.svg)](https://www.npmjs.com/package/grimoire-framework)
4
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
- [![Node.js](https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen)](https://nodejs.org)
3
+ > **CLI de gestão para frameworks de agentes de IA** — instale uma vez, use em todos os seus projetos
6
4
 
7
- > Framework de desenvolvimento autônomo orientado por IA que orquestra equipes de agentes especializados para transformar requisitos em software funcional.
5
+ [![npm](https://img.shields.io/npm/v/grimoire-framework.svg)](https://www.npmjs.com/package/grimoire-framework)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
7
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D18-brightgreen)](https://nodejs.org)
8
8
 
9
9
  ---
10
10
 
11
- ## 🚀 Início Rápido
11
+ ## Quick Start (5 passos)
12
12
 
13
13
  ```bash
14
- # Instalar o Grimoire em um projeto existente
14
+ # 1. Instale no projeto
15
15
  npx grimoire-framework install
16
16
 
17
- # Atualizar para a versão mais recente (preserva customizações)
18
- npx grimoire-framework update
19
- ```
17
+ # 2. Gere o prompt de início de sessão
18
+ grimoire session start
20
19
 
21
- Após a instalação, abra o **Gemini CLI** no diretório do projeto:
20
+ # 3. Cole o prompt no chat da sua IDE e ative um agente
21
+ # @dev @qa @architect @grimoire-master
22
22
 
23
- ```bash
24
- gemini
25
- # 👑 Michelangelo (grimoire-master) pronto para orquestrar!
23
+ # 4. Crie e rastreie stories
24
+ grimoire story create "Implementar autenticação JWT"
25
+
26
+ # 5. Veja o relatório ao final do dia
27
+ grimoire report
26
28
  ```
27
29
 
28
30
  ---
29
31
 
30
- ## 🛠️ Comandos CLI
31
-
32
- ### Gerenciamento do Framework
33
-
34
- | Comando | Descrição |
35
- |---|---|
36
- | `npx grimoire-framework install` | Instalar no projeto atual |
37
- | `npx grimoire-framework update` | Atualizar preservando customizações |
38
- | `npx grimoire-framework update --force` | Forçar resync de todos os arquivos |
39
- | `npx grimoire-framework update --dry-run` | Simular update (sem aplicar) |
40
-
41
- ### Diagnóstico
32
+ ## 🧙 O que é o Grimoire?
42
33
 
43
- | Comando | Descrição |
44
- |---|---|
45
- | `grimoire status` | Saúde do framework + integração IDE |
46
- | `grimoire doctor` | Diagnóstico detalhado com dicas de fix |
47
- | `grimoire agents list` | Listar agentes com personas |
48
- | `grimoire whoami` | Contexto da sessão atual |
49
- | `grimoire --version` | Versão instalada |
34
+ O Grimoire é um framework que adiciona **agentes de IA especializados** ao chat da sua IDE (Gemini CLI, Cursor, VS Code, Claude). Cada agente tem uma persona, expertise e estilo próprios.
50
35
 
51
- > 💡 **CLI vs Agentes:** `grimoire` (terminal) gerencia o framework. `@dev`, `@qa`, `@architect` (chat da IDE) são os agentes de IA — interfaces completamente diferentes.
36
+ ```
37
+ CLI (terminal) → gerencia o framework
38
+ Agentes (IDE) → @dev, @qa, @architect no chat da IDE
39
+ ```
52
40
 
53
41
  ---
54
42
 
55
- ## 🧙 Agentes Disponíveis
56
-
57
- Após instalar, ative os agentes diretamente no chat da sua IDE:
43
+ ## 🤖 Agentes incluídos
58
44
 
59
- | Comando | Persona | Especialidade |
45
+ | Agente | Persona | Especialidade |
60
46
  |---|---|---|
61
- | `@grimoire-master` | 👑 Michelangelo | Orquestração & framework |
62
- | `@dev` | 🎨 Da Vinci | Desenvolvimento Full-Stack |
63
- | `@qa` | 🖌️ Dürer | Quality Assurance & Testes |
64
- | `@architect` | 🏛️ Gaudí | Arquitetura de Sistemas |
65
- | `@pm` | 📋 Raphael | Product Manager |
66
- | `@sm` | 🌊 Monet | Scrum Master |
67
- | `@devops` | ⚡ Boccioni | DevOps & Infraestrutura |
68
- | `@data-engineer` | 📊 Escher | Engenharia de Dados |
69
- | `@analyst` | 🔍 Vermeer | Pesquisa & Análise |
70
- | `@ux-design-expert` | 🎭 Matisse | UX/UI Design |
47
+ | `@grimoire-master` | 👑 Michelangelo | Orquestração |
48
+ | `@dev` | 🎨 Da Vinci | Full-stack |
49
+ | `@qa` | 🖌️ Dürer | QA & testes |
50
+ | `@architect` | 🏛️ Gaudí | Arquitetura |
51
+ | `@pm` | 📋 Raphael | Produto |
52
+ | `@ux-design-expert` | 🎭 Matisse | UX/Design |
53
+ | `@devops` | ⚡ Boccioni | DevOps |
54
+ | `@data-engineer` | 📊 Escher | Dados |
55
+ | `@analyst` | 🔍 Vermeer | Pesquisa |
71
56
  | `@po` | 🎯 Velázquez | Product Owner |
57
+ | `@sm` | 🌊 Monet | Scrum Master |
58
+ | `@squad-creator` | 🗿 Rodin | Criação de squads |
72
59
 
73
60
  ---
74
61
 
75
- ## 💻 Integração com IDEs
76
-
77
- ### Gemini CLI (Recomendado)
78
- O Grimoire configura automaticamente o arquivo `GEMINI.md` na raiz do projeto. O Michelangelo se apresenta ao abrir o Gemini CLI.
62
+ ## 📋 Referência de comandos
79
63
 
64
+ ### Essenciais
80
65
  ```bash
81
- gemini
82
- # 👑 Michelangelo (grimoire-master) pronto para orquestrar!
83
- # Projeto: meu-projeto
66
+ grimoire status # saúde do framework
67
+ grimoire whoami # contexto completo (config+pro+stories)
68
+ grimoire doctor # diagnóstico completo com dicas
69
+ grimoire update # atualiza agentes e arquivos
70
+ grimoire update --dry-run # preview sem aplicar
84
71
  ```
85
72
 
86
- Para ativar um agente especializado:
87
- ```
88
- @dev ou Da Vinci ou ative o @architect
73
+ ### Sessão & Workflow
74
+ ```bash
75
+ grimoire session start # prompt para IDE (squad+stories+memória)
76
+ grimoire session start --squad dev # com squad específico
77
+ grimoire story create "Título" # nova story
78
+ grimoire story list # stories ativas
79
+ grimoire story done US-001 # marcar concluída
80
+ grimoire story note US-001 "obs" # adicionar nota
81
+ grimoire story show US-001 # detalhes + memórias linkadas
82
+ grimoire report # relatório de hoje
83
+ grimoire report --period week # relatório da semana (7 dias)
84
+ grimoire report --md # exportar relatório para Markdown
89
85
  ```
90
86
 
91
- ### Claude Code
92
- Agentes disponíveis em `.claude/commands/grimoire/`. Use `/dev`, `/qa`, `/architect`.
93
-
94
- ### Cursor
95
- Regras instaladas em `.cursor/rules/agents/`. Agentes ativados via contexto do chat.
96
-
97
- ---
87
+ ### Agentes
88
+ ```bash
89
+ grimoire agents list # lista com personas
90
+ grimoire agent create # wizard interativo
91
+ grimoire agent validate dev # valida YAML e campos (score 0-100)
92
+ grimoire agent edit dev # abre no $EDITOR
93
+ grimoire agent remove dev # remove agente customizado
94
+ ```
98
95
 
99
- ## 🏗️ Estrutura Instalada
96
+ ### Squads
97
+ ```bash
98
+ grimoire squads list # lista squads disponíveis
99
+ grimoire squads use fullstack # prompt de ativação para IDE
100
+ grimoire squads info fullstack # detalhes do squad
101
+ ```
100
102
 
103
+ ### Memória
104
+ ```bash
105
+ grimoire memory save "texto" # salvar entrada
106
+ grimoire memory save --tag decisão "txt" # com tag
107
+ grimoire memory save --story US-001 "txt" # linkada a story
108
+ grimoire memory show # entradas de hoje
109
+ grimoire memory search "termo" # buscar em sessões
110
+ grimoire memory list-tags # tags únicas com contagem
111
+ grimoire memory export # exportar para Markdown
112
+ grimoire memory clear --older-than 30 # limpar antigas
101
113
  ```
102
- projeto/
103
- ├── GEMINI.md ← Config principal do Gemini CLI (Michelangelo)
104
- ├── .gemini/
105
- │ ├── rules.md ← Compatibilidade com versões antigas do Gemini CLI
106
- │ └── rules/grimoire/ ← Definições de agentes para Gemini
107
- ├── .codex/agents/ ← Definições de todos os agentes (12 core)
108
- ├── .cursor/rules/agents/ ← Agentes para Cursor
109
- ├── .claude/commands/ ← Comandos para Claude Code
110
- └── .grimoire/ ← Memória, métricas e configurações
114
+
115
+ ### Busca & Backup
116
+ ```bash
117
+ grimoire search "JWT" # busca global (memória+stories+agentes)
118
+ grimoire search "JWT" --memory # memória
119
+ grimoire search "JWT" --stories # stories
120
+ grimoire backup # backup de .grimoire/
121
+ grimoire backup --list # listar backups
122
+ grimoire backup --restore <arq> # restaurar backup
123
+ grimoire export --all # bundle Markdown completo
124
+ grimoire export --all --json # bundle JSON completo
111
125
  ```
112
126
 
113
- ---
127
+ ### Métricas
128
+ ```bash
129
+ grimoire metrics # dashboard do mês
130
+ grimoire metrics --period week # últimos 7 dias
131
+ grimoire metrics --period all # histórico completo
132
+ grimoire metrics export --csv # exportar para CSV
133
+ grimoire metrics track session "desc" # registrar evento manual
134
+ ```
114
135
 
115
- ## 🔄 Fluxo de Update
136
+ ### Config & Hooks
137
+ ```bash
138
+ grimoire config list # todas as configurações
139
+ grimoire config get default_agent # valor de uma chave
140
+ grimoire config set default_agent dev # definir valor
141
+ grimoire config reset # restaurar defaults
142
+ grimoire hooks install # git post-commit (auto-tracking)
143
+ grimoire hooks status # verificar status do hook
144
+ grimoire hooks uninstall # remover hook
145
+ ```
116
146
 
117
- O `grimoire update` é inteligente — preserva as suas customizações:
147
+ ### Sync Global
148
+ ```bash
149
+ grimoire sync --global # envia agentes → ~/.grimoire/agents/
150
+ grimoire sync --from-global # puxa agentes do store global
151
+ grimoire sync --list # lista agentes no store
152
+ ```
118
153
 
119
- - ✅ **Atualiza** arquivos do framework (agentes core, GEMINI.md)
120
- - 🔒 **Preserva** arquivos que só existem no seu projeto (agentes customizados)
121
- - **Adiciona** arquivos novos do framework
154
+ ### Marketplace
155
+ ```bash
156
+ grimoire marketplace list # navega agentes da comunidade
157
+ grimoire marketplace install slug # instala agente
158
+ grimoire marketplace search "termo" # busca por especialidade
159
+ grimoire marketplace submit # guia para publicar
160
+ ```
122
161
 
162
+ ### Grimoire Pro
123
163
  ```bash
124
- npx grimoire-framework update # Update normal
125
- npx grimoire-framework update --force # Forçar re-sync completo
126
- npx grimoire-framework update --dry-run # Ver o que mudaria
164
+ grimoire pro activate <chave> # ativa licença Pro
165
+ grimoire pro status # status e features
166
+ grimoire pro features # lista features Pro
167
+ grimoire pro deactivate # remove licença
127
168
  ```
128
169
 
129
170
  ---
130
171
 
131
- ## ⚙️ Requisitos
172
+ ## 📁 Estrutura do projeto
132
173
 
133
- - **Node.js** >= 18.0.0
134
- - **npm** >= 9.0.0
135
- - **Git** (recomendado)
174
+ ```
175
+ seu-projeto/
176
+ ├── .grimoire/
177
+ │ ├── memory/sessions/ # memória JSONL
178
+ │ ├── metrics/ # métricas JSONL
179
+ │ ├── stories/ # stories JSON
180
+ │ ├── backups/ # backups .grimbak
181
+ │ ├── exports/ # bundles exportados
182
+ │ └── config.yaml # configurações do projeto
183
+ ├── .codex/agents/ # agentes Gemini CLI
184
+ ├── .gemini/ # config Gemini CLI
185
+ ├── .cursor/ # regras Cursor
186
+ └── GEMINI.md # persona principal
187
+ ```
136
188
 
137
189
  ---
138
190
 
139
- ## 🗺️ Roadmap
191
+ ## 📦 Versões
140
192
 
141
- - [x] Smart Update com preservação de customizações
142
- - [x] Publisher Quality Gate (CI/CD)
143
- - [x] Integração Gemini CLI via `GEMINI.md`
144
- - [x] 12 agentes com personas artísticas (Michelangelo, Da Vinci, Dürer...)
145
- - [ ] **Sistema de Login & Autenticação** Governança para equipes
146
- - [ ] **Project Models** Templates para SaaS, Mobile, API
147
- - [ ] **Persistent Memory Layer** Aprendizado de padrões do seu código
148
- - [ ] **Enterprise Connectors** — Integração com Jira, Notion e Slack
193
+ | Versão | Features |
194
+ |---|---|
195
+ | v1.0.x | Setup, agents, sync global |
196
+ | v1.1.x | Marketplace, Grimoire Pro |
197
+ | v1.2.0 | Squads use, config, hooks, agent validate, memory tags |
198
+ | v1.3.0 | Report, story, session start, doctor v3, whoami v2 |
199
+ | **v1.4.0** | **Search global, backup, export --all, memory --story, story note** |
149
200
 
150
201
  ---
151
202
 
152
203
  ## 🤝 Contribuindo
153
204
 
154
- Contribuições são bem-vindas! Veja [CONTRIBUTING.md](CONTRIBUTING.md) para detalhes.
155
-
156
- ```bash
157
- git clone https://github.com/gabrielrlima/grimoire.git
158
- cd grimoire
159
- npm install
160
- npm test
161
- ```
162
-
163
- ---
164
-
165
- ## 📄 Licença
166
-
167
- Distribuído sob a licença MIT. Veja [LICENSE](LICENSE) para mais informações.
205
+ Veja [CONTRIBUTING.md](CONTRIBUTING.md) e [docs/CHANGELOG.md](docs/CHANGELOG.md).
168
206
 
169
207
  ---
170
208
 
171
- Built with ❤️ by the **Grimoire Team** [npm](https://www.npmjs.com/package/grimoire-framework) [GitHub](https://github.com/gabrielrlima/grimoire)
209
+ *Grimoire Framework · MIT License · Made with ❤️ by the community*
@@ -0,0 +1,165 @@
1
+ /**
2
+ * grimoire backup — Backup & Restore .grimoire/
3
+ *
4
+ * grimoire backup Cria backup zip de .grimoire/
5
+ * grimoire backup --restore <file> Restaura a partir de um zip
6
+ * grimoire backup --list Lista backups existentes
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const path = require('path');
12
+ const fs = require('fs');
13
+ const zlib = require('zlib');
14
+
15
+ function findGrimoireDir() {
16
+ const cwd = process.cwd();
17
+ const direct = path.join(cwd, '.grimoire');
18
+ const sub = path.join(cwd, 'grimoire', '.grimoire');
19
+ if (fs.existsSync(direct)) return { dir: direct, root: cwd };
20
+ if (fs.existsSync(sub)) return { dir: sub, root: path.join(cwd, 'grimoire') };
21
+ return null;
22
+ }
23
+
24
+ function getBackupDir(grimoireDir) {
25
+ const d = path.join(grimoireDir, 'backups');
26
+ if (!fs.existsSync(d)) fs.mkdirSync(d, { recursive: true });
27
+ return d;
28
+ }
29
+
30
+ // Simple tar-like serialization using NDJSON (no native zip without deps)
31
+ // Format: one header line + files as base64 encoded NDJSON entries
32
+ function createBackup(grimoireDir, backupDir) {
33
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
34
+ const backupFile = path.join(backupDir, `backup-${timestamp}.grimbak`);
35
+
36
+ const SKIP_DIRS = new Set(['backups', 'exports']);
37
+ const entries = [];
38
+
39
+ function walk(dir, rel = '') {
40
+ for (const item of fs.readdirSync(dir, { withFileTypes: true })) {
41
+ const relPath = rel ? `${rel}/${item.name}` : item.name;
42
+ const abs = path.join(dir, item.name);
43
+ if (item.isDirectory()) {
44
+ if (!SKIP_DIRS.has(item.name)) walk(abs, relPath);
45
+ } else {
46
+ try {
47
+ const content = fs.readFileSync(abs).toString('base64');
48
+ entries.push({ path: relPath, content, size: fs.statSync(abs).size });
49
+ } catch (_) { }
50
+ }
51
+ }
52
+ }
53
+
54
+ walk(grimoireDir);
55
+
56
+ const header = JSON.stringify({
57
+ version: 1,
58
+ createdAt: new Date().toISOString(),
59
+ files: entries.length,
60
+ source: grimoireDir
61
+ });
62
+
63
+ const lines = [header, ...entries.map(e => JSON.stringify(e))];
64
+ const raw = lines.join('\n');
65
+ const buf = zlib.gzipSync(Buffer.from(raw, 'utf8'));
66
+ fs.writeFileSync(backupFile, buf);
67
+
68
+ return { file: backupFile, count: entries.length, size: buf.length };
69
+ }
70
+
71
+ function restoreBackup(backupFile, grimoireDir) {
72
+ if (!fs.existsSync(backupFile)) {
73
+ throw new Error(`Arquivo não encontrado: ${backupFile}`);
74
+ }
75
+ const buf = fs.readFileSync(backupFile);
76
+ const raw = zlib.gunzipSync(buf).toString('utf8');
77
+ const lines = raw.split('\n').filter(l => l.trim());
78
+ const header = JSON.parse(lines[0]);
79
+
80
+ let restored = 0;
81
+ for (const line of lines.slice(1)) {
82
+ try {
83
+ const entry = JSON.parse(line);
84
+ const dest = path.join(grimoireDir, entry.path);
85
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
86
+ fs.writeFileSync(dest, Buffer.from(entry.content, 'base64'));
87
+ restored++;
88
+ } catch (_) { }
89
+ }
90
+ return { count: restored, header };
91
+ }
92
+
93
+ function listBackups(backupDir) {
94
+ if (!fs.existsSync(backupDir)) return [];
95
+ return fs.readdirSync(backupDir)
96
+ .filter(f => f.endsWith('.grimbak'))
97
+ .map(f => {
98
+ const stat = fs.statSync(path.join(backupDir, f));
99
+ return { name: f, size: stat.size, mtime: stat.mtime };
100
+ })
101
+ .sort((a, b) => b.mtime - a.mtime);
102
+ }
103
+
104
+ // ── run ───────────────────────────────────────────────────────────────────────
105
+ function run(args) {
106
+ const found = findGrimoireDir();
107
+ if (!found) {
108
+ console.error('❌ .grimoire/ not found. Run: npx grimoire-framework install');
109
+ return;
110
+ }
111
+ const { dir: grimoireDir } = found;
112
+ const backupDir = getBackupDir(grimoireDir);
113
+
114
+ if (args.includes('--list')) {
115
+ const backups = listBackups(backupDir);
116
+ console.log(`\n💾 Backups (${backups.length}):\n`);
117
+ if (backups.length === 0) {
118
+ console.log(' (nenhum backup encontrado)');
119
+ console.log(' grimoire backup ← criar agora\n');
120
+ } else {
121
+ backups.forEach(b => {
122
+ const kb = (b.size / 1024).toFixed(1);
123
+ const d = b.mtime.toLocaleDateString('pt-BR');
124
+ console.log(` 📦 ${b.name} (${kb} KB · ${d})`);
125
+ });
126
+ console.log(`\n grimoire backup --restore <arquivo> ← restaurar\n`);
127
+ }
128
+ return;
129
+ }
130
+
131
+ const restoreIdx = args.indexOf('--restore');
132
+ if (restoreIdx !== -1) {
133
+ const file = args[restoreIdx + 1];
134
+ if (!file) {
135
+ console.log('Usage: grimoire backup --restore <arquivo.grimbak>\n');
136
+ return;
137
+ }
138
+ const absFile = path.isAbsolute(file) ? file : path.join(backupDir, file);
139
+ console.log(`\n⏳ Restaurando backup: ${path.basename(absFile)}...\n`);
140
+ try {
141
+ const { count, header } = restoreBackup(absFile, grimoireDir);
142
+ console.log(`✅ ${count} arquivos restaurados`);
143
+ console.log(` Backup criado em: ${new Date(header.createdAt).toLocaleString('pt-BR')}\n`);
144
+ } catch (e) {
145
+ console.error(`❌ Erro ao restaurar: ${e.message}\n`);
146
+ }
147
+ return;
148
+ }
149
+
150
+ // Default: create backup
151
+ console.log('\n⏳ Criando backup de .grimoire/ ...\n');
152
+ try {
153
+ const { file, count, size } = createBackup(grimoireDir, backupDir);
154
+ const kb = (size / 1024).toFixed(1);
155
+ const rel = path.relative(process.cwd(), file);
156
+ console.log(`✅ Backup criado: ${rel}`);
157
+ console.log(` ${count} arquivos · ${kb} KB\n`);
158
+ console.log(`💡 Para restaurar: grimoire backup --restore ${path.basename(file)}`);
159
+ console.log(` Para listar: grimoire backup --list\n`);
160
+ } catch (e) {
161
+ console.error(`❌ Erro ao criar backup: ${e.message}\n`);
162
+ }
163
+ }
164
+
165
+ module.exports = { run };
@@ -0,0 +1,200 @@
1
+ /**
2
+ * grimoire export — Full Export
3
+ *
4
+ * grimoire export --all Markdown bundle de tudo
5
+ * grimoire export --all --json JSON bundle de tudo
6
+ * grimoire export --all --out <dir> Diretório de saída customizado
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const path = require('path');
12
+ const fs = require('fs');
13
+
14
+ function findGrimoireDir() {
15
+ const cwd = process.cwd();
16
+ const direct = path.join(cwd, '.grimoire');
17
+ const sub = path.join(cwd, 'grimoire', '.grimoire');
18
+ if (fs.existsSync(direct)) return direct;
19
+ if (fs.existsSync(sub)) return sub;
20
+ return null;
21
+ }
22
+
23
+ // ── Loaders ───────────────────────────────────────────────────────────────────
24
+ function loadAllStories(grimoireDir) {
25
+ const dir = path.join(grimoireDir, 'stories');
26
+ if (!fs.existsSync(dir)) return [];
27
+ return fs.readdirSync(dir)
28
+ .filter(f => f.endsWith('.json'))
29
+ .map(f => { try { return JSON.parse(fs.readFileSync(path.join(dir, f), 'utf8')); } catch (_) { return null; } })
30
+ .filter(Boolean)
31
+ .sort((a, b) => a.createdAt > b.createdAt ? 1 : -1);
32
+ }
33
+
34
+ function loadAllMemory(grimoireDir) {
35
+ const sessionsDir = path.join(grimoireDir, 'memory', 'sessions');
36
+ if (!fs.existsSync(sessionsDir)) return [];
37
+ const entries = [];
38
+ const files = fs.readdirSync(sessionsDir).filter(f => f.endsWith('.jsonl')).sort();
39
+ for (const f of files) {
40
+ const raw = fs.readFileSync(path.join(sessionsDir, f), 'utf8');
41
+ for (const line of raw.split('\n').filter(l => l.trim())) {
42
+ try { entries.push({ date: f.slice(0, 10), ...JSON.parse(line) }); } catch (_) { }
43
+ }
44
+ }
45
+ return entries;
46
+ }
47
+
48
+ function loadMetrics(grimoireDir) {
49
+ const dir = path.join(grimoireDir, 'metrics');
50
+ if (!fs.existsSync(dir)) return { sessions: 0, commits: 0, storiesDone: 0 };
51
+ let sessions = 0, commits = 0, storiesDone = 0;
52
+ for (const f of fs.readdirSync(dir).filter(f => f.endsWith('.jsonl'))) {
53
+ const raw = fs.readFileSync(path.join(dir, f), 'utf8');
54
+ for (const line of raw.split('\n').filter(l => l.trim())) {
55
+ try {
56
+ const e = JSON.parse(line);
57
+ if (e.type === 'session_start' || e.type === 'agent_session') sessions++;
58
+ if (e.type === 'commit') commits++;
59
+ if (e.type === 'story_complete') storiesDone++;
60
+ } catch (_) { }
61
+ }
62
+ }
63
+ return { sessions, commits, storiesDone };
64
+ }
65
+
66
+ // ── Markdown export ───────────────────────────────────────────────────────────
67
+ function buildMarkdown(stories, memory, metrics, projectName) {
68
+ const now = new Date().toISOString().split('T')[0];
69
+ const lines = [];
70
+
71
+ lines.push(`# Grimoire Export — ${projectName}`);
72
+ lines.push(`\n> Exportado em: ${now}\n`);
73
+
74
+ // Stats
75
+ lines.push(`## 📈 Resumo\n`);
76
+ lines.push(`| Métrica | Valor |`);
77
+ lines.push(`|---|---|`);
78
+ lines.push(`| Stories totais | ${stories.length} |`);
79
+ lines.push(`| Sessões | ${metrics.sessions} |`);
80
+ lines.push(`| Commits | ${metrics.commits} |`);
81
+ lines.push(`| Stories concluídas | ${metrics.storiesDone} |`);
82
+ lines.push(`| Entradas de memória | ${memory.length} |`);
83
+ lines.push('');
84
+
85
+ // Stories
86
+ lines.push(`## 📋 Stories\n`);
87
+ const openStories = stories.filter(s => s.status !== 'done');
88
+ const doneStories = stories.filter(s => s.status === 'done');
89
+
90
+ if (openStories.length) {
91
+ lines.push(`### Em andamento\n`);
92
+ openStories.forEach(s => {
93
+ lines.push(`#### 🔄 [${s.id}] ${s.title}`);
94
+ lines.push(`- **Criada:** ${s.createdAt.slice(0, 10)}`);
95
+ if (s.notes && s.notes.length) {
96
+ lines.push(`- **Notas:**`);
97
+ s.notes.forEach(n => lines.push(` - ${n}`));
98
+ }
99
+ lines.push('');
100
+ });
101
+ }
102
+ if (doneStories.length) {
103
+ lines.push(`### Concluídas\n`);
104
+ doneStories.forEach(s => {
105
+ lines.push(`- ✅ **[${s.id}]** ${s.title}${s.doneAt ? ` — ${s.doneAt.slice(0, 10)}` : ''}`);
106
+ });
107
+ lines.push('');
108
+ }
109
+
110
+ // Memory by tag
111
+ lines.push(`## 🧠 Memória\n`);
112
+ const byTag = {};
113
+ const noTag = [];
114
+ for (const e of memory) {
115
+ if (e.tag) { byTag[e.tag] = byTag[e.tag] || []; byTag[e.tag].push(e); }
116
+ else noTag.push(e);
117
+ }
118
+
119
+ for (const [tag, entries] of Object.entries(byTag)) {
120
+ lines.push(`### #${tag}\n`);
121
+ entries.forEach(e => {
122
+ const story = e.story ? ` \`[${e.story}]\`` : '';
123
+ lines.push(`- **${e.date}** ${e.content}${story}`);
124
+ });
125
+ lines.push('');
126
+ }
127
+
128
+ if (noTag.length) {
129
+ lines.push(`### Sem tag\n`);
130
+ noTag.slice(0, 20).forEach(e => lines.push(`- **${e.date}** ${e.content}`));
131
+ if (noTag.length > 20) lines.push(`- *(e mais ${noTag.length - 20} entradas...)*`);
132
+ lines.push('');
133
+ }
134
+
135
+ return lines.join('\n');
136
+ }
137
+
138
+ // ── JSON export ───────────────────────────────────────────────────────────────
139
+ function buildJSON(stories, memory, metrics, projectName) {
140
+ return JSON.stringify({
141
+ exportedAt: new Date().toISOString(),
142
+ project: projectName,
143
+ metrics,
144
+ stories,
145
+ memory,
146
+ }, null, 2);
147
+ }
148
+
149
+ // ── run ───────────────────────────────────────────────────────────────────────
150
+ function run(args) {
151
+ if (!args.includes('--all')) {
152
+ console.log('\nUsage:\n');
153
+ console.log(' grimoire export --all Markdown bundle de tudo');
154
+ console.log(' grimoire export --all --json JSON bundle de tudo\n');
155
+ return;
156
+ }
157
+
158
+ const grimoireDir = findGrimoireDir();
159
+ if (!grimoireDir) {
160
+ console.error('❌ .grimoire/ not found. Run: npx grimoire-framework install');
161
+ return;
162
+ }
163
+
164
+ // Project name
165
+ let projectName = path.basename(process.cwd());
166
+ try {
167
+ const pkg = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8'));
168
+ projectName = pkg.name || projectName;
169
+ } catch (_) { }
170
+
171
+ // Output dir
172
+ const outIdx = args.indexOf('--out');
173
+ const outDir = outIdx !== -1 && args[outIdx + 1]
174
+ ? args[outIdx + 1]
175
+ : path.join(grimoireDir, 'exports');
176
+ if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });
177
+
178
+ const now = new Date().toISOString().split('T')[0];
179
+ const isJson = args.includes('--json');
180
+ const ext = isJson ? 'json' : 'md';
181
+ const outFile = path.join(outDir, `${now}-export.${ext}`);
182
+
183
+ console.log('\n⏳ Exportando dados do .grimoire/ ...\n');
184
+
185
+ const stories = loadAllStories(grimoireDir);
186
+ const memory = loadAllMemory(grimoireDir);
187
+ const metrics = loadMetrics(grimoireDir);
188
+
189
+ const content = isJson
190
+ ? buildJSON(stories, memory, metrics, projectName)
191
+ : buildMarkdown(stories, memory, metrics, projectName);
192
+
193
+ fs.writeFileSync(outFile, content, 'utf8');
194
+
195
+ const rel = path.relative(process.cwd(), outFile);
196
+ console.log(`✅ Export gerado: ${rel}`);
197
+ console.log(` Stories: ${stories.length} · Memória: ${memory.length} · Formato: ${isJson ? 'JSON' : 'Markdown'}\n`);
198
+ }
199
+
200
+ module.exports = { run };
@@ -85,6 +85,19 @@ async function saveMemory(args, memoryDir) {
85
85
  else { filteredArgs = filteredArgs.filter(a => a !== args[tagIdx] && a !== tag); }
86
86
  }
87
87
 
88
+ // Extract --story value if present
89
+ const storyIdx = filteredArgs.findIndex(a => a === '--story' || a.startsWith('--story='));
90
+ let storyId = null;
91
+ if (storyIdx !== -1) {
92
+ if (filteredArgs[storyIdx].includes('=')) {
93
+ storyId = filteredArgs[storyIdx].split('=')[1];
94
+ filteredArgs.splice(storyIdx, 1);
95
+ } else {
96
+ storyId = filteredArgs[storyIdx + 1];
97
+ filteredArgs.splice(storyIdx, 2);
98
+ }
99
+ }
100
+
88
101
  const content = filteredArgs.join(' ');
89
102
  if (!content) {
90
103
  console.error('❌ Please provide content to save.');
@@ -98,6 +111,7 @@ async function saveMemory(args, memoryDir) {
98
111
  timestamp: new Date().toISOString(),
99
112
  content: content,
100
113
  ...(tag ? { tag } : {}),
114
+ ...(storyId ? { story: storyId } : {}),
101
115
  };
102
116
 
103
117
  // Ensure file exists for lockfile
@@ -1,13 +1,11 @@
1
1
  /**
2
- * grimoire pro — License & Feature Gate System
2
+ * grimoire pro — Feature Catalogue
3
3
  *
4
- * Phase 1: Offline-first with HMAC key validation
5
- * Phase 2: Online validation via Supabase Edge Function (future)
4
+ * Todos os recursos Pro estão disponíveis por padrão.
5
+ * O sistema de licença foi removido sem chave, sem bloqueio.
6
6
  *
7
- * grimoire pro activate <key> Ativa a licença Pro
8
- * grimoire pro status Mostra status da licença
9
- * grimoire pro features Lista features Pro disponíveis
10
- * grimoire pro deactivate Remove a licença
7
+ * grimoire pro status Mostra status e features
8
+ * grimoire pro features Lista todas as features
11
9
  */
12
10
 
13
11
  'use strict';
@@ -69,17 +67,19 @@ function isLicenseValid(license) {
69
67
  return license.checksum === expected;
70
68
  }
71
69
 
72
- // ── Public API for other modules ───────────────────────────────────────────────
70
+ // ── Public API (all features unlocked by default) ────────────────────────────
71
+ /**
72
+ * Always returns true — all Pro features are available without a license key.
73
+ */
73
74
  function isPro() {
74
- const license = loadLicense();
75
- return isLicenseValid(license) && license.type === 'pro';
75
+ return true;
76
76
  }
77
77
 
78
+ /**
79
+ * Always returns true — every feature ID is available.
80
+ */
78
81
  function hasFeature(featureId) {
79
- const license = loadLicense();
80
- if (!isLicenseValid(license)) return false;
81
- if (license.type === 'pro') return PRO_FEATURES.some(f => f.id === featureId);
82
- return license.features?.includes(featureId) || false;
82
+ return true;
83
83
  }
84
84
 
85
85
  // ── readline prompt ────────────────────────────────────────────────────────────
@@ -93,145 +93,43 @@ function prompt(q) {
93
93
  async function run(args) {
94
94
  const sub = args[0];
95
95
  switch (sub) {
96
- case 'activate': await activate(args[1]); break;
97
- case 'deactivate': deactivate(); break;
96
+ case 'activate': showAlreadyOpen(); break;
97
+ case 'deactivate': showAlreadyOpen(); break;
98
98
  case 'features': showFeatures(); break;
99
99
  case 'status':
100
100
  default: showStatus(); break;
101
101
  }
102
102
  }
103
103
 
104
- // ── Activate ───────────────────────────────────────────────────────────────────
105
- async function activate(keyArg) {
106
- console.log('\n🔐 Grimoire Pro Ativação de Licença\n');
107
-
108
- const key = keyArg || await prompt('Chave de licença (PRO-XXXX-XXXX-XXXX-XXXX): ');
109
-
110
- if (!validateKeyFormat(key)) {
111
- console.error('❌ Formato inválido. Use: PRO-XXXX-XXXX-XXXX-XXXX');
112
- console.log(' Obtenha sua chave em: https://grimoire.dev/pro\n');
113
- return;
114
- }
115
-
116
- const email = await prompt('Email associado à licença: ');
117
- if (!email || !email.includes('@')) {
118
- console.error('❌ Email inválido.');
119
- return;
120
- }
121
-
122
- // Offline HMAC validation
123
- const checksum = generateKeyChecksum(email, key);
124
-
125
- // Try online validation (Phase 2 ready but not blocking)
126
- let onlineValidated = false;
127
- try {
128
- const https = require('https');
129
- const result = await new Promise((resolve, reject) => {
130
- const data = JSON.stringify({ key, email });
131
- const req = https.request({
132
- hostname: 'api.grimoire.dev',
133
- path: '/v1/license/validate',
134
- method: 'POST',
135
- headers: { 'Content-Type': 'application/json', 'Content-Length': data.length },
136
- timeout: 4000,
137
- }, (res) => {
138
- let body = '';
139
- res.on('data', d => body += d);
140
- res.on('end', () => { try { resolve(JSON.parse(body)); } catch (e) { reject(e); } });
141
- });
142
- req.on('error', reject);
143
- req.on('timeout', () => req.destroy());
144
- req.write(data);
145
- req.end();
146
- });
147
- onlineValidated = result.valid === true;
148
- } catch (_) {
149
- // Offline — use local validation only
150
- }
151
-
152
- const license = {
153
- type: 'pro',
154
- key,
155
- email,
156
- checksum,
157
- activatedAt: new Date().toISOString(),
158
- expiresAt: null, // null = never (or set by server in Phase 2)
159
- onlineValidated,
160
- features: PRO_FEATURES.map(f => f.id),
161
- };
162
-
163
- saveLicense(license);
164
-
165
- console.log(`
166
- ✅ Grimoire Pro ativado!
167
-
168
- Email: ${email}
169
- Chave: ${key.substring(0, 7)}...${key.slice(-4)}
170
- Status: ${onlineValidated ? '✅ Validado online' : '✅ Ativado (validação offline)'}
171
-
172
- 🎯 Features ativas:`);
173
- PRO_FEATURES.forEach(f => console.log(` ✅ ${f.name} — ${f.desc}`));
174
- console.log('\n💡 Use "grimoire pro status" para ver os detalhes\n');
104
+ // ── showAlreadyOpen ───────────────────────────────────────────────────────────
105
+ function showAlreadyOpen() {
106
+ console.log('\n🔓 Grimoire Pro está aberto!');
107
+ console.log(' Todos os recursos já estão disponíveis sem preciso de chave.\n');
108
+ showFeatures();
175
109
  }
176
110
 
177
111
  // ── Status ─────────────────────────────────────────────────────────────────────
178
112
  function showStatus() {
179
- const license = loadLicense();
180
113
  const sep = '─'.repeat(50);
181
-
182
- console.log(`\n🔐 Grimoire Pro Status da Licença\n${sep}`);
183
-
184
- if (!license) {
185
- console.log(` Status: ⭕ Não ativado (Community)\n`);
186
- console.log(` ${sep}`);
187
- console.log(` Para ativar: grimoire pro activate <chave>`);
188
- console.log(` Obter licença: https://grimoire.dev/pro\n`);
189
- showFeatureList(false);
190
- return;
191
- }
192
-
193
- const valid = isLicenseValid(license);
194
- const isPro = license.type === 'pro' && valid;
195
- const expires = license.expiresAt ? new Date(license.expiresAt).toLocaleDateString('pt-BR') : 'Sem expiração';
196
-
197
- console.log(` Status: ${valid ? (isPro ? '✅ Pro Ativo' : '✅ Community') : '❌ Licença inválida/expirada'}`);
198
- if (license.email) console.log(` Email: ${license.email}`);
199
- if (license.key) console.log(` Chave: ${license.key.substring(0, 7)}...${license.key.slice(-4)}`);
200
- console.log(` Ativado: ${license.activatedAt ? new Date(license.activatedAt).toLocaleDateString('pt-BR') : 'N/A'}`);
201
- console.log(` Expira: ${expires}`);
114
+ console.log(`\n🔓 Grimoire Pro — Status\n${sep}`);
115
+ console.log(` Status: ✅ Aberto (todos os recursos disponíveis)`);
116
+ console.log(` Licença: Sem necessidade de chave`);
117
+ console.log(` Features: ${PRO_FEATURES.length} recursos ativos`);
202
118
  console.log(` ${sep}`);
203
-
204
- showFeatureList(isPro);
119
+ showFeatureList(true);
205
120
  }
206
121
 
207
122
  // ── Features ───────────────────────────────────────────────────────────────────
208
123
  function showFeatures() {
209
- const license = loadLicense();
210
- const active = license && isLicenseValid(license) && license.type === 'pro';
211
- console.log('\n🎯 Grimoire Pro — Features\n');
212
- showFeatureList(active);
124
+ console.log('\n🎯 Grimoire Pro — Features (todas ativas)\n');
125
+ showFeatureList(true);
213
126
  }
214
127
 
215
128
  function showFeatureList(active) {
216
129
  PRO_FEATURES.forEach(f => {
217
- const status = active ? '' : '🔒';
218
- console.log(` ${status} ${f.name}`);
130
+ console.log(` ${f.name}`);
219
131
  console.log(` ${f.desc}\n`);
220
132
  });
221
- if (!active) {
222
- console.log(` 🔗 Ative em: https://grimoire.dev/pro`);
223
- console.log(` grimoire pro activate\n`);
224
- }
225
- }
226
-
227
- // ── Deactivate ─────────────────────────────────────────────────────────────────
228
- function deactivate() {
229
- if (!fs.existsSync(LICENSE_FILE)) {
230
- console.log('ℹ️ Nenhuma licença ativa encontrada.');
231
- return;
232
- }
233
- fs.unlinkSync(LICENSE_FILE);
234
- console.log('✅ Licença removida. Grimoire voltou ao modo Community.');
235
133
  }
236
134
 
237
135
  module.exports = { run, isPro, hasFeature };
@@ -0,0 +1,171 @@
1
+ /**
2
+ * grimoire search — Global Search
3
+ *
4
+ * grimoire search "termo" Busca em memory + stories + agents
5
+ * grimoire search "JWT" --memory Só memória
6
+ * grimoire search "JWT" --stories Só stories
7
+ * grimoire search "JWT" --agents Só agentes
8
+ */
9
+
10
+ 'use strict';
11
+
12
+ const path = require('path');
13
+ const fs = require('fs');
14
+
15
+ function findGrimoireDir() {
16
+ const cwd = process.cwd();
17
+ const direct = path.join(cwd, '.grimoire');
18
+ const sub = path.join(cwd, 'grimoire', '.grimoire');
19
+ if (fs.existsSync(direct)) return direct;
20
+ if (fs.existsSync(sub)) return sub;
21
+ return null;
22
+ }
23
+
24
+ function findAgentsDir() {
25
+ const cwd = process.cwd();
26
+ const dirs = [
27
+ path.join(cwd, '.codex', 'agents'),
28
+ path.join(cwd, 'node_modules', 'grimoire-framework', '.codex', 'agents'),
29
+ ];
30
+ return dirs.find(d => fs.existsSync(d)) || null;
31
+ }
32
+
33
+ function hl(text, query) {
34
+ if (!query) return text;
35
+ const re = new RegExp(query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi');
36
+ return text.replace(re, m => `\x1b[33m${m}\x1b[0m`);
37
+ }
38
+
39
+ // ── Search memory sessions ────────────────────────────────────────────────────
40
+ function searchMemory(grimoireDir, query) {
41
+ const sessionsDir = path.join(grimoireDir, 'memory', 'sessions');
42
+ if (!fs.existsSync(sessionsDir)) return [];
43
+ const lq = query.toLowerCase();
44
+ const results = [];
45
+ const files = fs.readdirSync(sessionsDir).filter(f => f.endsWith('.jsonl')).sort().reverse();
46
+ for (const f of files) {
47
+ const raw = fs.readFileSync(path.join(sessionsDir, f), 'utf8');
48
+ for (const line of raw.split('\n').filter(l => l.trim())) {
49
+ try {
50
+ const e = JSON.parse(line);
51
+ if ((e.content || '').toLowerCase().includes(lq)) {
52
+ results.push({ date: f.slice(0, 10), content: e.content, tag: e.tag || null, story: e.story || null });
53
+ }
54
+ } catch (_) { }
55
+ }
56
+ if (results.length >= 20) break;
57
+ }
58
+ return results;
59
+ }
60
+
61
+ // ── Search stories ────────────────────────────────────────────────────────────
62
+ function searchStories(grimoireDir, query) {
63
+ const storiesDir = path.join(grimoireDir, 'stories');
64
+ if (!fs.existsSync(storiesDir)) return [];
65
+ const lq = query.toLowerCase();
66
+ return fs.readdirSync(storiesDir)
67
+ .filter(f => f.endsWith('.json'))
68
+ .map(f => { try { return JSON.parse(fs.readFileSync(path.join(storiesDir, f), 'utf8')); } catch (_) { return null; } })
69
+ .filter(Boolean)
70
+ .filter(s => (s.title || '').toLowerCase().includes(lq) ||
71
+ (s.notes || []).some(n => n.toLowerCase().includes(lq)));
72
+ }
73
+
74
+ // ── Search agents ─────────────────────────────────────────────────────────────
75
+ function searchAgents(agentsDir, query) {
76
+ if (!agentsDir) return [];
77
+ const lq = query.toLowerCase();
78
+ const results = [];
79
+ for (const f of fs.readdirSync(agentsDir).filter(f => f.endsWith('.md'))) {
80
+ const raw = fs.readFileSync(path.join(agentsDir, f), 'utf8').toLowerCase();
81
+ if (raw.includes(lq)) {
82
+ const id = f.replace('.md', '');
83
+ // Extract name from yaml block
84
+ let name = id;
85
+ const nameMatch = raw.match(/name:\s*([^\n]+)/);
86
+ if (nameMatch) name = nameMatch[1].trim();
87
+ const titleMatch = raw.match(/title:\s*([^\n]+)/);
88
+ const title = titleMatch ? titleMatch[1].trim() : '';
89
+ results.push({ id, name, title });
90
+ }
91
+ }
92
+ return results;
93
+ }
94
+
95
+ // ── run ───────────────────────────────────────────────────────────────────────
96
+ function run(args) {
97
+ const query = args.filter(a => !a.startsWith('-')).join(' ');
98
+ if (!query) {
99
+ console.log('Usage: grimoire search "termo"\n');
100
+ console.log('Busca em: memória · stories · agentes\n');
101
+ return;
102
+ }
103
+
104
+ const onlyMemory = args.includes('--memory');
105
+ const onlyStories = args.includes('--stories');
106
+ const onlyAgents = args.includes('--agents');
107
+ const all = !onlyMemory && !onlyStories && !onlyAgents;
108
+
109
+ const grimoireDir = findGrimoireDir();
110
+ const agentsDir = findAgentsDir();
111
+
112
+ let totalResults = 0;
113
+
114
+ console.log(`\n🔍 Grimoire Search — "${query}"\n${'─'.repeat(44)}`);
115
+
116
+ // Memory
117
+ if (all || onlyMemory) {
118
+ if (grimoireDir) {
119
+ const memResults = searchMemory(grimoireDir, query);
120
+ if (memResults.length > 0) {
121
+ console.log('\n🧠 Memória:');
122
+ for (const r of memResults) {
123
+ const tag = r.tag ? ` [#${r.tag}]` : '';
124
+ const story = r.story ? ` [${r.story}]` : '';
125
+ console.log(` ${r.date} ${hl(r.content, query)}${tag}${story}`);
126
+ }
127
+ totalResults += memResults.length;
128
+ }
129
+ }
130
+ }
131
+
132
+ // Stories
133
+ if (all || onlyStories) {
134
+ if (grimoireDir) {
135
+ const storyResults = searchStories(grimoireDir, query);
136
+ if (storyResults.length > 0) {
137
+ console.log('\n📋 Stories:');
138
+ for (const s of storyResults) {
139
+ const icon = s.status === 'done' ? '✅' : '🔄';
140
+ console.log(` ${icon} [${s.id}] ${hl(s.title, query)}`);
141
+ for (const n of (s.notes || []).filter(n => n.toLowerCase().includes(query.toLowerCase()))) {
142
+ console.log(` 📝 ${hl(n, query)}`);
143
+ }
144
+ }
145
+ totalResults += storyResults.length;
146
+ }
147
+ }
148
+ }
149
+
150
+ // Agents
151
+ if (all || onlyAgents) {
152
+ const agentResults = searchAgents(agentsDir, query);
153
+ if (agentResults.length > 0) {
154
+ console.log('\n🤖 Agentes:');
155
+ for (const a of agentResults) {
156
+ console.log(` @${hl(a.id, query)} — ${a.name}${a.title ? ' ' + a.title : ''}`);
157
+ }
158
+ totalResults += agentResults.length;
159
+ }
160
+ }
161
+
162
+ if (totalResults === 0) {
163
+ console.log('\n (nenhum resultado encontrado)');
164
+ console.log(` Dica: tente termos mais curtos ou use --memory / --stories / --agents\n`);
165
+ } else {
166
+ console.log(`\n${'─'.repeat(44)}`);
167
+ console.log(` ${totalResults} resultado(s)\n`);
168
+ }
169
+ }
170
+
171
+ module.exports = { run };
@@ -61,9 +61,10 @@ function run(args) {
61
61
  switch (sub) {
62
62
  case 'create': storyCreate(rest, storiesDir, grimoireDir); break;
63
63
  case 'done': storyDone(rest[0], storiesDir, grimoireDir); break;
64
+ case 'note': storyNote(rest[0], rest.slice(1), storiesDir); break;
64
65
  case 'delete':
65
66
  case 'remove': storyDelete(rest[0], storiesDir); break;
66
- case 'show': storyShow(rest[0], storiesDir); break;
67
+ case 'show': storyShow(rest[0], storiesDir, grimoireDir); break;
67
68
  case 'list':
68
69
  default: storyList(rest, storiesDir); break;
69
70
  }
@@ -161,8 +162,22 @@ function storyDone(id, storiesDir, grimoireDir) {
161
162
  console.log(`\n✅ [${story.id}] ${story.title} — marcada como concluída! 🎉\n`);
162
163
  }
163
164
 
164
- // ── show ──────────────────────────────────────────────────────────────────────
165
- function storyShow(id, storiesDir) {
165
+ // ── note ─────────────────────────────────────────────────────────────────────────
166
+ function storyNote(id, args, storiesDir) {
167
+ if (!id) { console.log('Usage: grimoire story note <id> "nota"\n'); return; }
168
+ const note = args.filter(a => !a.startsWith('-')).join(' ');
169
+ if (!note) { console.log('Usage: grimoire story note <id> "nota"\n'); return; }
170
+ const storyFile = path.join(storiesDir, id.endsWith('.json') ? id : `${id}.json`);
171
+ if (!fs.existsSync(storyFile)) { console.log(`❌ Story "${id}" not found.\n`); return; }
172
+ const story = JSON.parse(fs.readFileSync(storyFile, 'utf8'));
173
+ story.notes = story.notes || [];
174
+ story.notes.push(note);
175
+ fs.writeFileSync(storyFile, JSON.stringify(story, null, 2), 'utf8');
176
+ console.log(`\n📝 Nota adicionada a [${id}]:\n ${note}\n`);
177
+ }
178
+
179
+ // ── show ─────────────────────────────────────────────────────────────────────────
180
+ function storyShow(id, storiesDir, grimoireDir) {
166
181
  if (!id) { storyList([], storiesDir); return; }
167
182
  const storyFile = path.join(storiesDir, id.endsWith('.json') ? id : `${id}.json`);
168
183
  if (!fs.existsSync(storyFile)) {
@@ -171,15 +186,39 @@ function storyShow(id, storiesDir) {
171
186
  const s = JSON.parse(fs.readFileSync(storyFile, 'utf8'));
172
187
  const statusIcon = s.status === 'done' ? '✅' : '🔄';
173
188
  console.log(`\n${statusIcon} [${s.id}] ${s.title}`);
174
- console.log(`${''.repeat(50)}`);
189
+ console.log(`${'\u2500'.repeat(50)}`);
175
190
  console.log(` Status: ${s.status}`);
176
191
  console.log(` Criada: ${s.createdAt.slice(0, 10)}`);
177
192
  if (s.doneAt) console.log(` Concluída: ${s.doneAt.slice(0, 10)}`);
178
193
  if (s.notes && s.notes.length) {
179
- console.log('\n Notas:');
194
+ console.log('\n 📝 Notas:');
180
195
  s.notes.forEach(n => console.log(` - ${n}`));
181
196
  }
182
- console.log();
197
+ // Linked memory entries
198
+ if (grimoireDir) {
199
+ const sessionsDir = path.join(grimoireDir, 'memory', 'sessions');
200
+ if (fs.existsSync(sessionsDir)) {
201
+ const linked = [];
202
+ for (const f of fs.readdirSync(sessionsDir).filter(f => f.endsWith('.jsonl'))) {
203
+ const raw = fs.readFileSync(path.join(sessionsDir, f), 'utf8');
204
+ for (const line of raw.split('\n').filter(l => l.trim())) {
205
+ try {
206
+ const e = JSON.parse(line);
207
+ if (e.story === s.id) linked.push({ date: f.slice(0, 10), ...e });
208
+ } catch (_) { }
209
+ }
210
+ }
211
+ if (linked.length > 0) {
212
+ console.log('\n 🧠 Memórias linkadas:');
213
+ linked.forEach(e => {
214
+ const tag = e.tag ? ` [#${e.tag}]` : '';
215
+ console.log(` ${e.date} ${e.content}${tag}`);
216
+ });
217
+ }
218
+ }
219
+ }
220
+ console.log(`\n grimoire story note ${s.id} "obs" ← adicionar nota`);
221
+ console.log(` grimoire memory save --story ${s.id} "texto" ← linkar memória\n`);
183
222
  }
184
223
 
185
224
  // ── delete ────────────────────────────────────────────────────────────────────
@@ -142,6 +142,15 @@ async function main() {
142
142
  case 'session':
143
143
  require('./commands/session').run(args.slice(1));
144
144
  break;
145
+ case 'search':
146
+ require('./commands/search').run(args.slice(1));
147
+ break;
148
+ case 'backup':
149
+ require('./commands/backup').run(args.slice(1));
150
+ break;
151
+ case 'export':
152
+ require('./commands/exportall').run(args.slice(1));
153
+ break;
145
154
  case 'whoami':
146
155
  handleWhoami();
147
156
  break;
@@ -607,14 +616,16 @@ COMANDOS ESSENCIAIS:
607
616
  grimoire config set <k> <v> Define uma configuração
608
617
 
609
618
  SESSÃO & WORKFLOW:
610
- grimoire session start 🆕 Prompt de início de sessão para IDE
611
- grimoire session start --squad <s> 🆕 Com squad específico
612
- grimoire story create "Título" 🆕 Criar story
613
- grimoire story list 🆕 Ver stories ativas
614
- grimoire story done US-001 🆕 Marcar concluída
615
- grimoire report 🆕 Relatório de hoje
616
- grimoire report --period week 🆕 Relatório da semana
617
- grimoire report --md 🆕 Exportar relatório
619
+ grimoire session start Prompt de início de sessão para IDE
620
+ grimoire session start --squad <s> Com squad específico
621
+ grimoire story create "Título" Criar story
622
+ grimoire story note US-001 "obs" 🆕 Adicionar nota a uma story
623
+ grimoire story list Ver stories ativas
624
+ grimoire story done US-001 Marcar concluída
625
+ grimoire story show US-001 🆕 Detalhes + memórias linkadas
626
+ grimoire report Relatório de hoje
627
+ grimoire report --period week Relatório da semana
628
+ grimoire report --md Exportar relatório
618
629
 
619
630
  AGENTES:
620
631
  grimoire agents list Lista todos os agentes com personas
@@ -629,13 +640,22 @@ SQUADS:
629
640
  grimoire squads info <squad> 🆕 Detalhes do squad
630
641
 
631
642
  MEMÓRIA:
632
- grimoire memory save "texto" Salvar entrada na sessão
633
- grimoire memory save --tag <t> "x" 🆕 Salvar com tag
634
- grimoire memory show [--last 10] Ver entradas de hoje
635
- grimoire memory search "termo" Buscar em todas as sessões
636
- grimoire memory list-tags 🆕 Listar todas as tags
637
- grimoire memory export Exportar para Markdown
638
- grimoire memory clear --older-than 30 Limpar sessões antigas
643
+ grimoire memory save "texto" Salvar entrada
644
+ grimoire memory save --tag <t> "x" Salvar com tag
645
+ grimoire memory save --story US-001 "x" 🆕 Linkar memória a uma story
646
+ grimoire memory show [--last 10] Ver entradas de hoje
647
+ grimoire memory search "termo" Buscar em todas as sessões
648
+ grimoire memory list-tags Listar todas as tags
649
+ grimoire memory export Exportar para Markdown
650
+ grimoire memory clear --older-than 30 Limpar sessões antigas
651
+
652
+ BUSCA & BACKUP:
653
+ grimoire search "JWT" 🆕 Busca global (memória+stories+agentes)
654
+ grimoire backup 🆕 Cria backup de .grimoire/
655
+ grimoire backup --restore <f> 🆕 Restaura backup
656
+ grimoire backup --list 🆕 Lista backups
657
+ grimoire export --all 🆕 Bundle Markdown completo
658
+ grimoire export --all --json 🆕 Bundle JSON completo
639
659
 
640
660
  MÉTRICAS:
641
661
  grimoire metrics Dashboard do mês atual
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grimoire-framework",
3
- "version": "1.3.0",
3
+ "version": "1.4.1",
4
4
  "description": "Grimoire: AI-Orchestrated System for Full Stack Development - Core Framework",
5
5
  "publishConfig": {
6
6
  "access": "public"