grimoire-framework 1.0.20 → 1.1.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.
@@ -7,8 +7,8 @@
7
7
  # - SHA256 hashes for change detection
8
8
  # - File types for categorization
9
9
  #
10
- version: 1.0.20
11
- generated_at: "2026-02-22T14:39:48.383Z"
10
+ version: 1.1.0
11
+ generated_at: "2026-02-22T14:53:32.130Z"
12
12
  generator: scripts/generate-install-manifest.js
13
13
  file_count: 1011
14
14
  files:
@@ -0,0 +1,304 @@
1
+ /**
2
+ * grimoire marketplace — Community Agent Registry
3
+ *
4
+ * grimoire marketplace list Navega agentes da comunidade
5
+ * grimoire marketplace install <slug> Instala um agente do marketplace
6
+ * grimoire marketplace search <term> Busca por especialidade ou tag
7
+ * grimoire marketplace submit Instruções para publicar seu agente
8
+ */
9
+
10
+ 'use strict';
11
+
12
+ const path = require('path');
13
+ const fs = require('fs');
14
+ const https = require('https');
15
+ const os = require('os');
16
+
17
+ // ── Curated registry (built-in fallback, also fetched from GitHub) ─────────────
18
+ const REGISTRY_URL = 'https://raw.githubusercontent.com/gabrielrlima/grimoire/master/marketplace/registry.json';
19
+ const CACHE_FILE = path.join(os.homedir(), '.grimoire-marketplace-cache.json');
20
+ const CACHE_TTL_MINS = 60; // 1 hour
21
+
22
+ const BUILTIN_AGENTS = [
23
+ {
24
+ slug: 'frontend-specialist',
25
+ name: 'Botticelli',
26
+ icon: '🎠',
27
+ specialty: 'Frontend & UI/UX',
28
+ tags: ['react', 'css', 'typescript', 'nextjs'],
29
+ author: 'grimoire-team',
30
+ description: 'Especialista em React, TypeScript e design systems. Ideal para times com foco em UI.',
31
+ gist: 'https://raw.githubusercontent.com/gabrielrlima/grimoire/master/marketplace/agents/frontend-specialist.md',
32
+ },
33
+ {
34
+ slug: 'security-auditor',
35
+ name: 'Rembrandt',
36
+ icon: '🔐',
37
+ specialty: 'Segurança & Auditoria',
38
+ tags: ['security', 'owasp', 'pentest', 'audit'],
39
+ author: 'grimoire-team',
40
+ description: 'Audita código em busca de vulnerabilidades OWASP Top 10. SAST e code review de segurança.',
41
+ gist: 'https://raw.githubusercontent.com/gabrielrlima/grimoire/master/marketplace/agents/security-auditor.md',
42
+ },
43
+ {
44
+ slug: 'ml-engineer',
45
+ name: 'Dalí',
46
+ icon: '🧠',
47
+ specialty: 'Machine Learning & AI',
48
+ tags: ['ml', 'python', 'pytorch', 'sklearn'],
49
+ author: 'grimoire-team',
50
+ description: 'Implementa pipelines de ML, feature engineering, treinamento e avaliação de modelos.',
51
+ gist: 'https://raw.githubusercontent.com/gabrielrlima/grimoire/master/marketplace/agents/ml-engineer.md',
52
+ },
53
+ {
54
+ slug: 'api-designer',
55
+ name: 'Klimt',
56
+ icon: '🔗',
57
+ specialty: 'API Design & Documentação',
58
+ tags: ['rest', 'graphql', 'openapi', 'swagger'],
59
+ author: 'grimoire-team',
60
+ description: 'Projeta APIs RESTful e GraphQL. Gera documentação OpenAPI 3.x e valida contratos.',
61
+ gist: 'https://raw.githubusercontent.com/gabrielrlima/grimoire/master/marketplace/agents/api-designer.md',
62
+ },
63
+ {
64
+ slug: 'database-expert',
65
+ name: 'Mondrian',
66
+ icon: '🗄️',
67
+ specialty: 'Banco de Dados & Queries',
68
+ tags: ['sql', 'postgresql', 'mongodb', 'redis'],
69
+ author: 'grimoire-team',
70
+ description: 'Otimiza queries, projeta schemas e revisa migrações. PostgreSQL, MongoDB, Redis.',
71
+ gist: 'https://raw.githubusercontent.com/gabrielrlima/grimoire/master/marketplace/agents/database-expert.md',
72
+ },
73
+ {
74
+ slug: 'tech-writer',
75
+ name: 'Cervantes',
76
+ icon: '✍️',
77
+ specialty: 'Documentação Técnica',
78
+ tags: ['docs', 'readme', 'adr', 'wiki'],
79
+ author: 'grimoire-team',
80
+ description: 'Escreve e revisa documentação técnica: READMEs, ADRs, guias de API e wikis.',
81
+ gist: 'https://raw.githubusercontent.com/gabrielrlima/grimoire/master/marketplace/agents/tech-writer.md',
82
+ },
83
+ ];
84
+
85
+ // ── Fetcher ────────────────────────────────────────────────────────────────────
86
+ async function fetchRegistry() {
87
+ // Use cache if fresh
88
+ if (fs.existsSync(CACHE_FILE)) {
89
+ try {
90
+ const cache = JSON.parse(fs.readFileSync(CACHE_FILE, 'utf8'));
91
+ const minsSince = (Date.now() - cache.ts) / 60000;
92
+ if (minsSince < CACHE_TTL_MINS) return cache.agents;
93
+ } catch (_) { }
94
+ }
95
+
96
+ // Try to fetch remote registry
97
+ try {
98
+ const remote = await new Promise((resolve, reject) => {
99
+ const req = https.get(REGISTRY_URL, { timeout: 4000 }, (res) => {
100
+ let body = '';
101
+ res.on('data', d => body += d);
102
+ res.on('end', () => { try { resolve(JSON.parse(body)); } catch (e) { reject(e); } });
103
+ });
104
+ req.on('error', reject);
105
+ req.on('timeout', () => req.destroy());
106
+ });
107
+ const agents = remote.agents || BUILTIN_AGENTS;
108
+ fs.writeFileSync(CACHE_FILE, JSON.stringify({ ts: Date.now(), agents }), 'utf8');
109
+ return agents;
110
+ } catch (_) {
111
+ // Fall back to built-in
112
+ return BUILTIN_AGENTS;
113
+ }
114
+ }
115
+
116
+ // ── Commands ───────────────────────────────────────────────────────────────────
117
+ async function run(args) {
118
+ const sub = args[0];
119
+ switch (sub) {
120
+ case 'install': await installAgent(args[1]); break;
121
+ case 'search': await searchAgents(args.slice(1)); break;
122
+ case 'submit': showSubmitGuide(); break;
123
+ case 'list':
124
+ default: await listAgents(args); break;
125
+ }
126
+ }
127
+
128
+ // ── List ───────────────────────────────────────────────────────────────────────
129
+ async function listAgents(args) {
130
+ process.stdout.write('🔍 Carregando registry...');
131
+ const agents = await fetchRegistry();
132
+ const tagFilter = args.find(a => a.startsWith('--tag='));
133
+ const filtered = tagFilter
134
+ ? agents.filter(a => a.tags.includes(tagFilter.replace('--tag=', '')))
135
+ : agents;
136
+
137
+ console.log(`\r\n🏪 Grimoire Marketplace — ${filtered.length} agentes disponíveis\n`);
138
+ console.log(' ' + '─'.repeat(60));
139
+
140
+ filtered.forEach(agent => {
141
+ const tags = agent.tags.slice(0, 3).map(t => `#${t}`).join(' ');
142
+ console.log(`
143
+ ${agent.icon} **@${agent.slug}** — ${agent.name}
144
+ ${agent.specialty}
145
+ ${agent.description}
146
+ ${tags} by @${agent.author}`);
147
+ });
148
+
149
+ console.log(`\n ${'─'.repeat(60)}`);
150
+ console.log(` 💡 Para instalar: grimoire marketplace install <slug>`);
151
+ console.log(` 💡 Para buscar: grimoire marketplace search <term>`);
152
+ console.log(` 💡 Para publicar: grimoire marketplace submit\n`);
153
+ }
154
+
155
+ // ── Search ─────────────────────────────────────────────────────────────────────
156
+ async function searchAgents(terms) {
157
+ const query = terms.join(' ').toLowerCase();
158
+ if (!query) { console.log('Usage: grimoire marketplace search <term>'); return; }
159
+
160
+ const agents = await fetchRegistry();
161
+ const results = agents.filter(a =>
162
+ a.slug.includes(query) ||
163
+ a.specialty.toLowerCase().includes(query) ||
164
+ a.description.toLowerCase().includes(query) ||
165
+ a.tags.some(t => t.includes(query))
166
+ );
167
+
168
+ console.log(`\n🔍 Results for "${query}" — ${results.length} found\n`);
169
+ if (results.length === 0) {
170
+ console.log(' No agents found. Try: grimoire marketplace list\n'); return;
171
+ }
172
+ results.forEach(a => {
173
+ console.log(` ${a.icon} @${a.slug} — ${a.name} · ${a.specialty}`);
174
+ console.log(` ${a.description}\n`);
175
+ });
176
+ }
177
+
178
+ // ── Install ────────────────────────────────────────────────────────────────────
179
+ async function installAgent(slug) {
180
+ if (!slug) { console.log('Usage: grimoire marketplace install <slug>'); return; }
181
+
182
+ const agents = await fetchRegistry();
183
+ const agent = agents.find(a => a.slug === slug);
184
+ if (!agent) {
185
+ console.error(`❌ Agent "${slug}" not found in marketplace. Try: grimoire marketplace list`);
186
+ return;
187
+ }
188
+
189
+ const localDir = path.join(process.cwd(), '.codex', 'agents');
190
+ if (!fs.existsSync(localDir)) {
191
+ console.error('❌ No .codex/agents/ found. Run: npx grimoire-framework install');
192
+ return;
193
+ }
194
+
195
+ const destFile = path.join(localDir, `${slug}.md`);
196
+ if (fs.existsSync(destFile)) {
197
+ console.log(`⚠️ Agent @${slug} already installed. Use "grimoire agent edit ${slug}" to customize.`);
198
+ return;
199
+ }
200
+
201
+ // Download agent file
202
+ process.stdout.write(`⬇️ Downloading @${slug} (${agent.name})...`);
203
+ try {
204
+ const content = await new Promise((resolve, reject) => {
205
+ const req = https.get(agent.gist, { timeout: 8000 }, (res) => {
206
+ // If 404 or not found, generate template instead
207
+ if (res.statusCode === 404) { resolve(null); return; }
208
+ let body = '';
209
+ res.on('data', d => body += d);
210
+ res.on('end', () => resolve(body));
211
+ });
212
+ req.on('error', reject);
213
+ req.on('timeout', () => req.destroy());
214
+ });
215
+
216
+ const agentContent = content || generateFallbackAgent(agent);
217
+ fs.writeFileSync(destFile, agentContent, 'utf8');
218
+
219
+ console.log(` ✅\n`);
220
+ console.log(`✅ Instalado: @${slug} — ${agent.name}`);
221
+ console.log(` Especialidade: ${agent.specialty}`);
222
+ console.log(` Arquivo: .codex/agents/${slug}.md`);
223
+ console.log(`\n💡 Para ativar: @${slug} no chat da sua IDE`);
224
+ console.log(` Para outras IDEs: grimoire sync --global && grimoire update\n`);
225
+ } catch (e) {
226
+ console.log(' ⚠️ (offline, usando template local)');
227
+ fs.writeFileSync(destFile, generateFallbackAgent(agent), 'utf8');
228
+ console.log(`✅ Instalado com template local: @${slug}`);
229
+ }
230
+ }
231
+
232
+ // ── Fallback template when gist unavailable ────────────────────────────────────
233
+ function generateFallbackAgent(agent) {
234
+ return `# ${agent.slug}
235
+
236
+ ACTIVATION-NOTICE: This file contains your full agent operating guidelines. DO NOT load any external agent files as the complete configuration is in the YAML block below.
237
+
238
+ ## COMPLETE AGENT DEFINITION FOLLOWS - NO EXTERNAL FILES NEEDED
239
+
240
+ \`\`\`yaml
241
+ activation-instructions:
242
+ - STEP 1: Adote a persona ${agent.name} completamente
243
+ - STEP 2: Apresente o greeting abaixo
244
+ - STEP 3: HALT e aguarde input
245
+ - STAY IN CHARACTER até *exit
246
+
247
+ agent:
248
+ name: ${agent.name}
249
+ id: ${agent.slug}
250
+ title: ${agent.specialty}
251
+ icon: ${agent.icon}
252
+ source: marketplace
253
+ author: ${agent.author}
254
+ tags: [${agent.tags.join(', ')}]
255
+ description: ${agent.description}
256
+
257
+ persona:
258
+ role: ${agent.specialty} Specialist
259
+ identity: ${agent.name} — ${agent.description}
260
+
261
+ greeting_levels:
262
+ default: |
263
+ ${agent.icon} **${agent.name}** (@${agent.slug}) do Grimoire Marketplace!
264
+ Especialidade: **${agent.specialty}**
265
+ ${agent.description}
266
+
267
+ Como posso ajudar?
268
+
269
+ commands:
270
+ - "*help — Comandos disponíveis"
271
+ - "*exit — Sair do agente"
272
+
273
+ signature_closing: '— ${agent.name} ${agent.icon}'
274
+ \`\`\`
275
+ `;
276
+ }
277
+
278
+ // ── Submit guide ───────────────────────────────────────────────────────────────
279
+ function showSubmitGuide() {
280
+ console.log(`
281
+ 📤 Publique seu agente no Grimoire Marketplace!
282
+
283
+ 1. Crie seu agente:
284
+ grimoire agent create
285
+
286
+ 2. Teste em múltiplos projetos:
287
+ grimoire sync --global
288
+ grimoire sync --from-global # em outro projeto
289
+
290
+ 3. Abra um Pull Request:
291
+ https://github.com/gabrielrlima/grimoire/tree/master/marketplace
292
+
293
+ Adicione seu agente em:
294
+ - marketplace/agents/<slug>.md (definição)
295
+ - marketplace/registry.json (entrada no catálogo)
296
+
297
+ 4. Campos obrigatórios no registry.json:
298
+ slug, name, icon, specialty, tags, author, description, gist
299
+
300
+ 🌟 Agentes aprovados ficam disponíveis para toda a comunidade!
301
+ `);
302
+ }
303
+
304
+ module.exports = { run };
@@ -1,107 +1,265 @@
1
1
  /**
2
- * Grimoire Metrics CLI Command - Advanced Aggregator
2
+ * Grimoire Metrics CLI Command Dashboard + Period Filters + CSV Export
3
3
  *
4
- * Aggregates productivity and usage metrics from daily logs.
4
+ * grimoire metrics # Resumo do mês atual
5
+ * grimoire metrics --period week # Últimos 7 dias
6
+ * grimoire metrics --period month # Últimos 30 dias
7
+ * grimoire metrics --period all # Todo o histórico
8
+ * grimoire metrics export --csv # Exporta para CSV
9
+ * grimoire metrics track <event> # Registra um evento manualmente
5
10
  */
6
11
 
12
+ 'use strict';
13
+
7
14
  const path = require('path');
8
15
  const fs = require('fs/promises');
9
- const existsSync = require('fs').existsSync;
16
+ const fsSync = require('fs');
10
17
 
11
- /**
12
- * Run the metrics command
13
- * @param {string[]} args - Command arguments
14
- */
18
+ // ── Agent personas catalogue ───────────────────────────────────────────────────
19
+ const AGENT_ICONS = {
20
+ 'dev': '🎨', 'qa': '🖌️', 'architect': '🏛️', 'pm': '📋', 'sm': '🌊',
21
+ 'devops': '⚡', 'data-engineer': '📊', 'analyst': '🔍',
22
+ 'ux-design-expert': '🎭', 'po': '🎯', 'squad-creator': '🗿',
23
+ 'grimoire-master': '👑',
24
+ };
25
+
26
+ // ── Entry point ────────────────────────────────────────────────────────────────
15
27
  async function run(args) {
16
28
  let baseDir = process.cwd();
17
- if (!existsSync(path.join(baseDir, '.grimoire')) && existsSync(path.join(baseDir, 'grimoire', '.grimoire'))) {
29
+ if (!fsSync.existsSync(path.join(baseDir, '.grimoire')) &&
30
+ fsSync.existsSync(path.join(baseDir, 'grimoire', '.grimoire'))) {
18
31
  baseDir = path.join(baseDir, 'grimoire');
19
32
  }
20
33
 
21
34
  const metricsDir = path.join(baseDir, '.grimoire', 'metrics');
22
35
 
23
- if (!existsSync(metricsDir)) {
24
- console.error('❌ Metrics system not found.');
25
- return;
36
+ // Auto-create metrics dir if .grimoire exists
37
+ if (!fsSync.existsSync(metricsDir)) {
38
+ if (fsSync.existsSync(path.join(baseDir, '.grimoire'))) {
39
+ await fs.mkdir(path.join(metricsDir, 'daily'), { recursive: true });
40
+ console.log('✅ Metrics directory initialized at .grimoire/metrics/');
41
+ } else {
42
+ console.error('❌ Grimoire not installed. Run: npx grimoire-framework install');
43
+ return;
44
+ }
26
45
  }
27
46
 
47
+ // Sub-commands
48
+ const sub = args[0];
49
+ if (sub === 'export') { await exportMetrics(metricsDir, args.slice(1)); return; }
50
+ if (sub === 'track') { await trackEvent(metricsDir, args.slice(1)); return; }
51
+ if (sub === 'help') { showHelp(); return; }
52
+
53
+ // Parse period
54
+ const periodIdx = args.indexOf('--period');
55
+ const period = periodIdx !== -1 ? args[periodIdx + 1] : 'month';
56
+
28
57
  try {
29
- const stats = await aggregateMetrics(metricsDir);
30
- showDashboard(stats);
31
- } catch (error) {
32
- console.error(`❌ Error aggregating metrics: ${error.message}`);
58
+ const stats = await aggregateMetrics(metricsDir, period);
59
+ showDashboard(stats, period);
60
+ } catch (e) {
61
+ console.error(`❌ Error: ${e.message}`);
33
62
  }
34
63
  }
35
64
 
36
- async function aggregateMetrics(metricsDir) {
65
+ // ── Period utilities ───────────────────────────────────────────────────────────
66
+ function cutoffDate(period) {
67
+ const d = new Date();
68
+ if (period === 'week') d.setDate(d.getDate() - 7);
69
+ else if (period === 'month') d.setDate(d.getDate() - 30);
70
+ else if (period === 'all') return new Date(0);
71
+ else d.setDate(d.getDate() - 30); // default month
72
+ return d;
73
+ }
74
+
75
+ function periodLabel(period) {
76
+ const months = ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho',
77
+ 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'];
78
+ if (period === 'week') return 'Últimos 7 dias';
79
+ if (period === 'all') return 'Histórico completo';
80
+ return `${months[new Date().getMonth()]} ${new Date().getFullYear()}`;
81
+ }
82
+
83
+ // ── Aggregator ─────────────────────────────────────────────────────────────────
84
+ async function aggregateMetrics(metricsDir, period) {
37
85
  const dailyDir = path.join(metricsDir, 'daily');
86
+ const cutoff = cutoffDate(period);
87
+
38
88
  const stats = {
39
- totalSessions: 0,
40
- totalStories: 0,
41
- totalTokens: 0,
42
- agentUsage: {},
43
- daysActive: 0
89
+ sessions: 0, stories: 0, tokens: 0,
90
+ agentUsage: {}, daysActive: 0,
91
+ memoryEntries: 0, commandsRun: 0,
92
+ firstDate: null, lastDate: null,
44
93
  };
45
94
 
46
- if (!existsSync(dailyDir)) return stats;
95
+ if (!fsSync.existsSync(dailyDir)) return stats;
96
+
97
+ const files = (await fs.readdir(dailyDir))
98
+ .filter(f => f.match(/^\d{4}-\d{2}-\d{2}/) && (f.endsWith('.jsonl') || f.endsWith('.json')))
99
+ .filter(f => new Date(f.substring(0, 10)) >= cutoff)
100
+ .sort();
47
101
 
48
- const files = (await fs.readdir(dailyDir)).filter(f => f.endsWith('.jsonl'));
49
102
  stats.daysActive = files.length;
103
+ if (files.length > 0) {
104
+ stats.firstDate = files[0].substring(0, 10);
105
+ stats.lastDate = files[files.length - 1].substring(0, 10);
106
+ }
50
107
 
51
108
  for (const file of files) {
52
109
  const content = await fs.readFile(path.join(dailyDir, file), 'utf8');
53
110
  const lines = content.split('\n').filter(l => l.trim());
54
-
55
- stats.totalSessions += lines.length;
111
+ stats.sessions += lines.length;
56
112
 
57
- lines.forEach(line => {
113
+ for (const line of lines) {
58
114
  try {
59
115
  const entry = JSON.parse(line);
60
- // Increment tokens if present
61
- if (entry.tokens) stats.totalTokens += entry.tokens;
62
- // Increment stories if entry marks completion
63
- if (entry.type === 'story_complete') stats.totalStories += 1;
64
- // Track agent usage
116
+ if (entry.tokens) stats.tokens += entry.tokens;
117
+ if (entry.type === 'story_complete') stats.stories += 1;
118
+ if (entry.type === 'memory_save') stats.memoryEntries += 1;
119
+ if (entry.type === 'command') stats.commandsRun += 1;
65
120
  if (entry.agent) {
66
121
  stats.agentUsage[entry.agent] = (stats.agentUsage[entry.agent] || 0) + 1;
67
122
  }
68
- } catch (e) {
69
- // Skip malformed lines
70
- }
71
- });
123
+ } catch (_) { /* skip bad lines */ }
124
+ }
72
125
  }
73
126
 
74
127
  return stats;
75
128
  }
76
129
 
77
- function showDashboard(stats) {
78
- const monthNames = ["Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"];
79
- const now = new Date();
80
-
130
+ // ── Manual event tracking ──────────────────────────────────────────────────────
131
+ async function trackEvent(metricsDir, args) {
132
+ const type = args[0] || 'custom';
133
+ const content = args.slice(1).join(' ');
134
+ const today = new Date().toISOString().split('T')[0];
135
+ const dailyDir = path.join(metricsDir, 'daily');
136
+ if (!fsSync.existsSync(dailyDir)) await fs.mkdir(dailyDir, { recursive: true });
137
+
138
+ const entry = {
139
+ timestamp: new Date().toISOString(),
140
+ type,
141
+ content: content || type,
142
+ };
143
+ const file = path.join(dailyDir, `${today}.jsonl`);
144
+ await fs.appendFile(file, JSON.stringify(entry) + '\n', 'utf8');
145
+ console.log(`✅ Event tracked: [${type}] ${content || ''}`);
146
+ }
147
+
148
+ // ── Dashboard renderer ─────────────────────────────────────────────────────────
149
+ function bar(value, max, width = 20) {
150
+ const filled = max > 0 ? Math.round((value / max) * width) : 0;
151
+ return '█'.repeat(filled) + '░'.repeat(width - filled);
152
+ }
153
+
154
+ function showDashboard(stats, period) {
155
+ const topAgents = Object.entries(stats.agentUsage).sort((a, b) => b[1] - a[1]).slice(0, 5);
156
+ const maxUsage = topAgents.length > 0 ? topAgents[0][1] : 0;
157
+
158
+ const sep = '─'.repeat(51);
159
+
81
160
  console.log(`
82
- 📊 Grimoire Pro Metrics — ${monthNames[now.getMonth()]} ${now.getFullYear()}
83
- ─────────────────────────────────────────────────
84
- Sessões Totais: ${stats.totalSessions}
85
- Dias de Atividade: ${stats.daysActive}
86
- Stories Completas: ${stats.totalStories}
87
- Tokens Estimados: ~${(stats.totalTokens / 1000).toFixed(1)}k
88
- ─────────────────────────────────────────────────
89
- Agentes mais ativos:`);
90
-
91
- const topAgents = Object.entries(stats.agentUsage)
92
- .sort((a, b) => b[1] - a[1])
93
- .slice(0, 3);
161
+ 📊 Grimoire Metrics — ${periodLabel(period)}
162
+ ${sep}
163
+ 📅 Período ${stats.firstDate || 'hoje'} → ${stats.lastDate || 'hoje'}
164
+ 📆 Dias ativos ${stats.daysActive}
165
+ 🔄 Sessões totais ${stats.sessions}
166
+ 📖 Stories concluídas ${stats.stories}
167
+ 💾 Entradas de memória ${stats.memoryEntries}
168
+ Tokens estimados ${stats.tokens > 0 ? '~' + (stats.tokens / 1000).toFixed(1) + 'k' : '(sem dados de hooks)'}
169
+ ${sep}
170
+ 🤖 Agentes mais usados:`);
94
171
 
95
172
  if (topAgents.length > 0) {
96
173
  topAgents.forEach(([agent, count]) => {
97
- console.log(` - ${agent}: ${count} interações`);
174
+ const icon = AGENT_ICONS[agent] || '🎭';
175
+ const b = bar(count, maxUsage, 15);
176
+ console.log(` ${icon} @${agent.padEnd(20)} ${b} ${count}×`);
98
177
  });
99
178
  } else {
100
- console.log(' (Nenhum dado de agente coletado ainda)');
179
+ console.log(' (Nenhum evento de agente registrado ainda)');
180
+ console.log('\n 💡 Para registrar uso: grimoire metrics track agent dev');
101
181
  }
102
182
 
103
- console.log(`─────────────────────────────────────────────────
104
- 💡 Dica: Os dados são coletados automaticamente via hooks.`);
183
+ console.log(`${sep}
184
+ 💡 Dica: Use hooks para coletar dados automaticamente
185
+ grimoire metrics track story_complete "Login implementado"
186
+ grimoire metrics --period week
187
+ grimoire metrics export --csv
188
+ `);
189
+ }
190
+
191
+ // ── CSV / Markdown export ──────────────────────────────────────────────────────
192
+ async function exportMetrics(metricsDir, args) {
193
+ const format = args.includes('--csv') ? 'csv' : 'markdown';
194
+ const dailyDir = path.join(metricsDir, 'daily');
195
+
196
+ if (!fsSync.existsSync(dailyDir)) {
197
+ console.error('❌ No metrics data found.'); return;
198
+ }
199
+
200
+ const files = (await fs.readdir(dailyDir)).filter(f => f.match(/^\d{4}-\d{2}-\d{2}/)).sort();
201
+ const allEntries = [];
202
+
203
+ for (const file of files) {
204
+ const date = file.substring(0, 10);
205
+ const content = await fs.readFile(path.join(dailyDir, file), 'utf8');
206
+ const lines = content.split('\n').filter(l => l.trim());
207
+ for (const line of lines) {
208
+ try { allEntries.push({ date, ...JSON.parse(line) }); } catch (_) { }
209
+ }
210
+ }
211
+
212
+ const outPath = path.join(process.cwd(),
213
+ `grimoire-metrics-${new Date().toISOString().split('T')[0]}.${format === 'csv' ? 'csv' : 'md'}`);
214
+
215
+ if (format === 'csv') {
216
+ const headers = ['date', 'timestamp', 'type', 'agent', 'tokens', 'content'];
217
+ const rows = allEntries.map(e =>
218
+ headers.map(h => JSON.stringify(e[h] ?? '')).join(',')
219
+ );
220
+ await fs.writeFile(outPath, [headers.join(','), ...rows].join('\n'), 'utf8');
221
+ } else {
222
+ let md = '# Grimoire Metrics Export\n\n';
223
+ md += `> Exported: ${new Date().toISOString()}\n\n`;
224
+ md += `| Date | Time | Type | Agent | Tokens | Content |\n`;
225
+ md += `|------|------|------|-------|--------|---------|\n`;
226
+ allEntries.forEach(e => {
227
+ const time = e.timestamp ? e.timestamp.split('T')[1]?.split('.')[0] : '';
228
+ md += `| ${e.date} | ${time} | ${e.type || ''} | ${e.agent || ''} | ${e.tokens || ''} | ${e.content || ''} |\n`;
229
+ });
230
+ await fs.writeFile(outPath, md, 'utf8');
231
+ }
232
+
233
+ console.log(`✅ Metrics exported (${allEntries.length} events) → ${outPath}`);
234
+ }
235
+
236
+ // ── Help ───────────────────────────────────────────────────────────────────────
237
+ function showHelp() {
238
+ console.log(`
239
+ 📊 Grimoire Metrics — Dashboard de Produtividade
240
+
241
+ USO:
242
+ grimoire metrics Resumo do mês atual
243
+ grimoire metrics --period week Últimos 7 dias
244
+ grimoire metrics --period month Últimos 30 dias
245
+ grimoire metrics --period all Histórico completo
246
+ grimoire metrics export --csv Exportar para CSV
247
+ grimoire metrics export Exportar para Markdown
248
+ grimoire metrics track <type> [msg] Registrar evento manualmente
249
+
250
+ TIPOS DE EVENTO:
251
+ story_complete "Nome da story" Marcar story como concluída
252
+ agent <name> Registrar uso de agente
253
+ command <cmd> Registrar comando executado
254
+ memory_save Registrar uso de memória
255
+ custom <desc> Evento personalizado
256
+
257
+ EXEMPLOS:
258
+ grimoire metrics --period week
259
+ grimoire metrics track story_complete "Implementar login"
260
+ grimoire metrics track agent dev
261
+ grimoire metrics export --csv
262
+ `);
105
263
  }
106
264
 
107
265
  module.exports = { run };
@@ -0,0 +1,237 @@
1
+ /**
2
+ * grimoire pro — License & Feature Gate System
3
+ *
4
+ * Phase 1: Offline-first with HMAC key validation
5
+ * Phase 2: Online validation via Supabase Edge Function (future)
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
11
+ */
12
+
13
+ 'use strict';
14
+
15
+ const path = require('path');
16
+ const fs = require('fs');
17
+ const os = require('os');
18
+ const crypto = require('crypto');
19
+
20
+ // ── License storage ────────────────────────────────────────────────────────────
21
+ const LICENSE_FILE = path.join(os.homedir(), '.grimoire-license.json');
22
+
23
+ // ── HMAC secret (public seed — Phase 1 offline validation only) ───────────────
24
+ // In Phase 2 this validation moves to the server
25
+ const HMAC_SEED = 'grimoire-pro-v1-2026';
26
+
27
+ // ── Pro feature catalogue ──────────────────────────────────────────────────────
28
+ const PRO_FEATURES = [
29
+ { id: 'squads-unlimited', name: 'Squads Ilimitados', desc: 'Crie squads customizados sem limite', tier: 'pro' },
30
+ { id: 'memory-sync', name: 'Memória Sincronizada', desc: 'Sync de memória entre projetos via cloud', tier: 'pro' },
31
+ { id: 'metrics-advanced', name: 'Métricas Avançadas', desc: 'Dashboard com insights de IA e tendências', tier: 'pro' },
32
+ { id: 'marketplace-publish', name: 'Publish no Marketplace', desc: 'Publique agentes no marketplace sem PR', tier: 'pro' },
33
+ { id: 'squad-creator', name: 'Squad Creator Avançado', desc: 'Templates de squad por domínio (fintech, saúde, e-comm)', tier: 'pro' },
34
+ { id: 'priority-support', name: 'Suporte Prioritário', desc: 'Acesso a canal privado de suporte', tier: 'pro' },
35
+ ];
36
+
37
+ // ── License validation (offline HMAC-SHA256) ──────────────────────────────────
38
+ function generateKeyChecksum(email, key) {
39
+ const payload = `${email}:${key}:${HMAC_SEED}`;
40
+ return crypto.createHmac('sha256', HMAC_SEED).update(payload).digest('hex').substring(0, 12);
41
+ }
42
+
43
+ function validateKeyFormat(key) {
44
+ // Format: PRO-XXXX-XXXX-XXXX-XXXX (alphanumeric segments)
45
+ return /^PRO-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$/.test(key);
46
+ }
47
+
48
+ function loadLicense() {
49
+ if (!fs.existsSync(LICENSE_FILE)) return null;
50
+ try { return JSON.parse(fs.readFileSync(LICENSE_FILE, 'utf8')); }
51
+ catch (_) { return null; }
52
+ }
53
+
54
+ function saveLicense(data) {
55
+ fs.writeFileSync(LICENSE_FILE, JSON.stringify(data, null, 2), 'utf8');
56
+ }
57
+
58
+ function isLicenseValid(license) {
59
+ if (!license) return false;
60
+ if (license.type === 'community') return true; // Always valid
61
+
62
+ // Check expiry
63
+ if (license.expiresAt && new Date(license.expiresAt) < new Date()) {
64
+ return false;
65
+ }
66
+
67
+ // Offline HMAC check
68
+ const expected = generateKeyChecksum(license.email || '', license.key || '');
69
+ return license.checksum === expected;
70
+ }
71
+
72
+ // ── Public API for other modules ───────────────────────────────────────────────
73
+ function isPro() {
74
+ const license = loadLicense();
75
+ return isLicenseValid(license) && license.type === 'pro';
76
+ }
77
+
78
+ 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;
83
+ }
84
+
85
+ // ── readline prompt ────────────────────────────────────────────────────────────
86
+ const readline = require('readline');
87
+ function prompt(q) {
88
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
89
+ return new Promise(resolve => { rl.question(q, a => { rl.close(); resolve(a.trim()); }); });
90
+ }
91
+
92
+ // ── Commands ───────────────────────────────────────────────────────────────────
93
+ async function run(args) {
94
+ const sub = args[0];
95
+ switch (sub) {
96
+ case 'activate': await activate(args[1]); break;
97
+ case 'deactivate': deactivate(); break;
98
+ case 'features': showFeatures(); break;
99
+ case 'status':
100
+ default: showStatus(); break;
101
+ }
102
+ }
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');
175
+ }
176
+
177
+ // ── Status ─────────────────────────────────────────────────────────────────────
178
+ function showStatus() {
179
+ const license = loadLicense();
180
+ 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}`);
202
+ console.log(` ${sep}`);
203
+
204
+ showFeatureList(isPro);
205
+ }
206
+
207
+ // ── Features ───────────────────────────────────────────────────────────────────
208
+ 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);
213
+ }
214
+
215
+ function showFeatureList(active) {
216
+ PRO_FEATURES.forEach(f => {
217
+ const status = active ? '✅' : '🔒';
218
+ console.log(` ${status} ${f.name}`);
219
+ console.log(` ${f.desc}\n`);
220
+ });
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
+ }
236
+
237
+ module.exports = { run, isPro, hasFeature };
@@ -0,0 +1,203 @@
1
+ /**
2
+ * grimoire sync — Global Agent Synchronization
3
+ *
4
+ * Copies agents from the current project to a global ~/.grimoire/agents/
5
+ * and optionally syncs them to other projects on the machine.
6
+ *
7
+ * grimoire sync --global Sync agents to global store
8
+ * grimoire sync --from-global Pull global agents into current project
9
+ * grimoire sync --list List global agents
10
+ * grimoire sync --projects Show projects registered in global store
11
+ */
12
+
13
+ 'use strict';
14
+
15
+ const path = require('path');
16
+ const fs = require('fs');
17
+ const os = require('os');
18
+
19
+ const GLOBAL_DIR = path.join(os.homedir(), '.grimoire');
20
+ const GLOBAL_AGENTS = path.join(GLOBAL_DIR, 'agents');
21
+ const GLOBAL_REGISTRY = path.join(GLOBAL_DIR, 'projects.json');
22
+
23
+ // ── Utilities ──────────────────────────────────────────────────────────────────
24
+ function ensureGlobalDir() {
25
+ if (!fs.existsSync(GLOBAL_AGENTS)) fs.mkdirSync(GLOBAL_AGENTS, { recursive: true });
26
+ }
27
+
28
+ function loadRegistry() {
29
+ if (!fs.existsSync(GLOBAL_REGISTRY)) return { projects: [], lastSync: null };
30
+ try { return JSON.parse(fs.readFileSync(GLOBAL_REGISTRY, 'utf8')); }
31
+ catch (_) { return { projects: [], lastSync: null }; }
32
+ }
33
+
34
+ function saveRegistry(data) {
35
+ fs.writeFileSync(GLOBAL_REGISTRY, JSON.stringify(data, null, 2), 'utf8');
36
+ }
37
+
38
+ function getLocalAgentsDir() {
39
+ const cwd = process.cwd();
40
+ const dir = path.join(cwd, '.codex', 'agents');
41
+ if (!fs.existsSync(dir)) return null;
42
+ return dir;
43
+ }
44
+
45
+ function copyAgent(src, dest) {
46
+ const content = fs.readFileSync(src, 'utf8');
47
+ fs.writeFileSync(dest, content, 'utf8');
48
+ }
49
+
50
+ // ── Commands ───────────────────────────────────────────────────────────────────
51
+ async function run(args) {
52
+ const sub = args.find(a => a.startsWith('--')) || '--global';
53
+
54
+ switch (sub) {
55
+ case '--global': await syncToGlobal(); break;
56
+ case '--from-global': await syncFromGlobal(); break;
57
+ case '--list': listGlobalAgents(); break;
58
+ case '--projects': listProjects(); break;
59
+ case '--help':
60
+ default: showHelp(); break;
61
+ }
62
+ }
63
+
64
+ // ── Sync local → global ────────────────────────────────────────────────────────
65
+ async function syncToGlobal() {
66
+ const localDir = getLocalAgentsDir();
67
+ if (!localDir) {
68
+ console.error('❌ No .codex/agents/ found. Run: npx grimoire-framework install');
69
+ return;
70
+ }
71
+
72
+ ensureGlobalDir();
73
+
74
+ const agents = fs.readdirSync(localDir).filter(f => f.endsWith('.md'));
75
+ let synced = 0, skipped = 0;
76
+
77
+ console.log(`\n🌐 Syncing ${agents.length} agents to global store (~/.grimoire/agents/)...\n`);
78
+
79
+ for (const file of agents) {
80
+ const src = path.join(localDir, file);
81
+ const dest = path.join(GLOBAL_AGENTS, file);
82
+ const exists = fs.existsSync(dest);
83
+
84
+ // Compare content to decide if update is needed
85
+ if (exists) {
86
+ const srcContent = fs.readFileSync(src, 'utf8');
87
+ const destContent = fs.readFileSync(dest, 'utf8');
88
+ if (srcContent === destContent) { skipped++; continue; }
89
+ }
90
+
91
+ copyAgent(src, dest);
92
+ console.log(` ✅ ${file.replace('.md', '')}`);
93
+ synced++;
94
+ }
95
+
96
+ if (skipped > 0) console.log(` ⏩ ${skipped} already up-to-date (skipped)`);
97
+
98
+ // Register this project
99
+ const registry = loadRegistry();
100
+ const cwd = process.cwd();
101
+ if (!registry.projects.includes(cwd)) registry.projects.push(cwd);
102
+ registry.lastSync = new Date().toISOString();
103
+ saveRegistry(registry);
104
+
105
+ console.log(`\n✅ ${synced} agent${synced === 1 ? '' : 's'} synced to global store`);
106
+ console.log(` ${GLOBAL_AGENTS}`);
107
+ console.log(`\n💡 Use "grimoire sync --from-global" in other projects to pull these agents`);
108
+ }
109
+
110
+ // ── Sync global → local ────────────────────────────────────────────────────────
111
+ async function syncFromGlobal() {
112
+ if (!fs.existsSync(GLOBAL_AGENTS)) {
113
+ console.error('❌ No global agents found. Run "grimoire sync --global" first.');
114
+ return;
115
+ }
116
+
117
+ const localDir = getLocalAgentsDir();
118
+ if (!localDir) {
119
+ console.error('❌ No .codex/agents/ found. Run: npx grimoire-framework install');
120
+ return;
121
+ }
122
+
123
+ const globalAgents = fs.readdirSync(GLOBAL_AGENTS).filter(f => f.endsWith('.md'));
124
+ const localAgents = new Set(fs.readdirSync(localDir).filter(f => f.endsWith('.md')));
125
+
126
+ // Only pull agents that DON'T exist locally (preserve local customizations)
127
+ const newAgents = globalAgents.filter(f => !localAgents.has(f));
128
+
129
+ console.log(`\n📥 Pulling from global store (~/.grimoire/agents/)...\n`);
130
+ console.log(` Global agents: ${globalAgents.length}`);
131
+ console.log(` Local agents: ${localAgents.size}`);
132
+ console.log(` New to pull: ${newAgents.length}\n`);
133
+
134
+ if (newAgents.length === 0) {
135
+ console.log('✅ Local agents are already up-to-date with global store.');
136
+ return;
137
+ }
138
+
139
+ for (const file of newAgents) {
140
+ copyAgent(path.join(GLOBAL_AGENTS, file), path.join(localDir, file));
141
+ console.log(` ✅ Added: @${file.replace('.md', '')}`);
142
+ }
143
+
144
+ console.log(`\n✅ ${newAgents.length} agent${newAgents.length === 1 ? '' : 's'} pulled from global store`);
145
+ }
146
+
147
+ // ── List global agents ─────────────────────────────────────────────────────────
148
+ function listGlobalAgents() {
149
+ if (!fs.existsSync(GLOBAL_AGENTS)) {
150
+ console.log('ℹ️ No global agents yet. Run: grimoire sync --global');
151
+ return;
152
+ }
153
+
154
+ const agents = fs.readdirSync(GLOBAL_AGENTS).filter(f => f.endsWith('.md'));
155
+ const registry = loadRegistry();
156
+
157
+ console.log(`\n🌐 Global Grimoire Agents (${agents.length})`);
158
+ console.log(` Store: ${GLOBAL_AGENTS}`);
159
+ if (registry.lastSync) console.log(` Last sync: ${registry.lastSync.split('T')[0]}\n`);
160
+
161
+ agents.forEach(f => console.log(` 🤖 @${f.replace('.md', '')}`));
162
+ }
163
+
164
+ // ── List registered projects ───────────────────────────────────────────────────
165
+ function listProjects() {
166
+ const registry = loadRegistry();
167
+ console.log(`\n📁 Projects using Grimoire global store (${registry.projects.length}):\n`);
168
+ if (registry.projects.length === 0) {
169
+ console.log(' (No projects registered yet — run "grimoire sync --global")');
170
+ return;
171
+ }
172
+ registry.projects.forEach(p => {
173
+ const exists = fs.existsSync(p);
174
+ console.log(` ${exists ? '✅' : '❌'} ${p}`);
175
+ });
176
+ }
177
+
178
+ // ── Help ───────────────────────────────────────────────────────────────────────
179
+ function showHelp() {
180
+ console.log(`
181
+ 🌐 Grimoire Sync — Sincronização Global de Agentes
182
+
183
+ USO:
184
+ grimoire sync --global Envia agentes locais para ~/.grimoire/agents/
185
+ grimoire sync --from-global Puxa agentes globais para o projeto atual
186
+ grimoire sync --list Lista agentes no store global
187
+ grimoire sync --projects Lista projetos registrados
188
+
189
+ FLUXO TÍPICO:
190
+ # No projeto A (onde você criou um agente customizado):
191
+ grimoire agent create → grimoire sync --global
192
+
193
+ # No projeto B (onde você quer esse agente):
194
+ grimoire sync --from-global
195
+
196
+ NOTAS:
197
+ - --from-global nunca sobrescreve agentes existentes localmente
198
+ - --global sobrescreve apenas se o conteúdo mudou
199
+ - O store global fica em: ${GLOBAL_DIR}
200
+ `);
201
+ }
202
+
203
+ module.exports = { run };
@@ -115,6 +115,15 @@ async function main() {
115
115
  case 'agent':
116
116
  await require('./commands/agent').run(args.slice(1));
117
117
  break;
118
+ case 'sync':
119
+ await require('./commands/sync').run(args.slice(1));
120
+ break;
121
+ case 'marketplace':
122
+ await require('./commands/marketplace').run(args.slice(1));
123
+ break;
124
+ case 'pro':
125
+ await require('./commands/pro').run(args.slice(1));
126
+ break;
118
127
  case 'status':
119
128
  handleStatus();
120
129
  break;
@@ -0,0 +1,96 @@
1
+ {
2
+ "version": "1.0.0",
3
+ "lastUpdated": "2026-02-22",
4
+ "agents": [
5
+ {
6
+ "slug": "frontend-specialist",
7
+ "name": "Botticelli",
8
+ "icon": "🎠",
9
+ "specialty": "Frontend & UI/UX",
10
+ "tags": [
11
+ "react",
12
+ "css",
13
+ "typescript",
14
+ "nextjs"
15
+ ],
16
+ "author": "grimoire-team",
17
+ "description": "Especialista em React, TypeScript e design systems. Ideal para times com foco em UI.",
18
+ "gist": "https://raw.githubusercontent.com/gabrielrlima/grimoire/master/marketplace/agents/frontend-specialist.md"
19
+ },
20
+ {
21
+ "slug": "security-auditor",
22
+ "name": "Rembrandt",
23
+ "icon": "🔐",
24
+ "specialty": "Segurança & Auditoria",
25
+ "tags": [
26
+ "security",
27
+ "owasp",
28
+ "pentest",
29
+ "audit"
30
+ ],
31
+ "author": "grimoire-team",
32
+ "description": "Audita código em busca de vulnerabilidades OWASP Top 10. SAST e code review de segurança.",
33
+ "gist": "https://raw.githubusercontent.com/gabrielrlima/grimoire/master/marketplace/agents/security-auditor.md"
34
+ },
35
+ {
36
+ "slug": "ml-engineer",
37
+ "name": "Dalí",
38
+ "icon": "🧠",
39
+ "specialty": "Machine Learning & AI",
40
+ "tags": [
41
+ "ml",
42
+ "python",
43
+ "pytorch",
44
+ "sklearn"
45
+ ],
46
+ "author": "grimoire-team",
47
+ "description": "Implementa pipelines de ML, feature engineering, treinamento e avaliação de modelos.",
48
+ "gist": "https://raw.githubusercontent.com/gabrielrlima/grimoire/master/marketplace/agents/ml-engineer.md"
49
+ },
50
+ {
51
+ "slug": "api-designer",
52
+ "name": "Klimt",
53
+ "icon": "🔗",
54
+ "specialty": "API Design & Documentação",
55
+ "tags": [
56
+ "rest",
57
+ "graphql",
58
+ "openapi",
59
+ "swagger"
60
+ ],
61
+ "author": "grimoire-team",
62
+ "description": "Projeta APIs RESTful e GraphQL. Gera documentação OpenAPI 3.x e valida contratos.",
63
+ "gist": "https://raw.githubusercontent.com/gabrielrlima/grimoire/master/marketplace/agents/api-designer.md"
64
+ },
65
+ {
66
+ "slug": "database-expert",
67
+ "name": "Mondrian",
68
+ "icon": "🗄️",
69
+ "specialty": "Banco de Dados & Queries",
70
+ "tags": [
71
+ "sql",
72
+ "postgresql",
73
+ "mongodb",
74
+ "redis"
75
+ ],
76
+ "author": "grimoire-team",
77
+ "description": "Otimiza queries, projeta schemas e revisa migrações. PostgreSQL, MongoDB, Redis.",
78
+ "gist": "https://raw.githubusercontent.com/gabrielrlima/grimoire/master/marketplace/agents/database-expert.md"
79
+ },
80
+ {
81
+ "slug": "tech-writer",
82
+ "name": "Cervantes",
83
+ "icon": "✍️",
84
+ "specialty": "Documentação Técnica",
85
+ "tags": [
86
+ "docs",
87
+ "readme",
88
+ "adr",
89
+ "wiki"
90
+ ],
91
+ "author": "grimoire-team",
92
+ "description": "Escreve e revisa documentação técnica: READMEs, ADRs, guias de API e wikis.",
93
+ "gist": "https://raw.githubusercontent.com/gabrielrlima/grimoire/master/marketplace/agents/tech-writer.md"
94
+ }
95
+ ]
96
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grimoire-framework",
3
- "version": "1.0.20",
3
+ "version": "1.1.0",
4
4
  "description": "Grimoire: AI-Orchestrated System for Full Stack Development - Core Framework",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -37,6 +37,7 @@
37
37
  "GEMINI.md",
38
38
  "pro/license/",
39
39
  "squads/",
40
+ "marketplace/",
40
41
  "README.md",
41
42
  "LICENSE"
42
43
  ],