grimoire-framework 1.1.0 → 1.2.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.1.0
11
- generated_at: "2026-02-22T14:53:32.130Z"
10
+ version: 1.2.0
11
+ generated_at: "2026-02-22T16:41:00.136Z"
12
12
  generator: scripts/generate-install-manifest.js
13
13
  file_count: 1011
14
14
  files:
@@ -100,6 +100,7 @@ async function run(args) {
100
100
  switch (sub) {
101
101
  case 'create': await createWizard(); break;
102
102
  case 'edit': await editAgent(args[1]); break;
103
+ case 'validate': validateAgent(args[1]); break;
103
104
  case 'remove':
104
105
  case 'delete': await removeAgent(args[1]); break;
105
106
  case 'list':
@@ -191,6 +192,43 @@ Para sincronizar com outras IDEs:
191
192
  `);
192
193
  }
193
194
 
195
+ function validateAgent(id) {
196
+ if (!id) { console.log('Usage: grimoire agent validate <name>'); return; }
197
+ const file = path.join(process.cwd(), '.codex', 'agents', `${id}.md`);
198
+ if (!fs.existsSync(file)) { console.log(`❌ Agent "${id}" not found in .codex/agents/`); return; }
199
+
200
+ const content = fs.readFileSync(file, 'utf8');
201
+ const checks = [
202
+ { name: 'YAML block (`\`\`\`yaml`)', pass: content.includes('```yaml') },
203
+ { name: 'activation-instructions', pass: content.includes('activation-instructions') },
204
+ { name: 'STEP 1 / STEP 2 / STEP 3', pass: /STEP 1/.test(content) && /STEP 2/.test(content) },
205
+ { name: 'agent.name defined', pass: /\bname:\s*\S+/.test(content) },
206
+ { name: 'agent.id defined', pass: /\bid:\s*\S+/.test(content) },
207
+ { name: 'agent.icon defined', pass: /\bicon:\s*\S+/.test(content) },
208
+ { name: 'greeting_levels present', pass: content.includes('greeting_levels') },
209
+ { name: 'signature_closing present', pass: content.includes('signature_closing') },
210
+ { name: 'ACTIVATION-NOTICE present', pass: content.includes('ACTIVATION-NOTICE') },
211
+ { name: '*exit command referenced', pass: content.includes('*exit') },
212
+ ];
213
+
214
+ const passed = checks.filter(c => c.pass).length;
215
+ const score = Math.round((passed / checks.length) * 100);
216
+ const emoji = score === 100 ? '🟢' : score >= 70 ? '🟡' : '🔴';
217
+
218
+ console.log(`\n🔍 Agent Validation: @${id}\n${'─'.repeat(40)}`);
219
+ checks.forEach(c => console.log(` ${c.pass ? '✅' : '❌'} ${c.name}`));
220
+ console.log(`${'─'.repeat(40)}`);
221
+ console.log(` ${emoji} Score: ${score}/100 (${passed}/${checks.length} checks)\n`);
222
+
223
+ if (score < 100) {
224
+ console.log(' Fixes sugeridos:');
225
+ checks.filter(c => !c.pass).forEach(c => console.log(` → Adicionar: ${c.name}`));
226
+ console.log();
227
+ } else {
228
+ console.log(' ✅ Agente válido e pronto para uso!\n');
229
+ }
230
+ }
231
+
194
232
  async function editAgent(id) {
195
233
  if (!id) { console.log('Usage: grimoire agent edit <name>'); return; }
196
234
  const file = path.join(process.cwd(), '.codex', 'agents', `${id}.md`);
@@ -0,0 +1,181 @@
1
+ /**
2
+ * grimoire config — Project-level configuration
3
+ *
4
+ * grimoire config get <key> Lê valor de uma chave
5
+ * grimoire config set <key> <value> Define valor de uma chave
6
+ * grimoire config list Lista todas as configurações
7
+ * grimoire config reset Reseta para os defaults
8
+ *
9
+ * Config file: .grimoire/config.yaml
10
+ */
11
+
12
+ 'use strict';
13
+
14
+ const path = require('path');
15
+ const fs = require('fs');
16
+
17
+ // ── Defaults ───────────────────────────────────────────────────────────────────
18
+ const DEFAULTS = {
19
+ default_agent: 'grimoire-master',
20
+ memory_auto_save: false,
21
+ update_check_interval: 24, // hours
22
+ metrics_enabled: true,
23
+ hooks_enabled: false,
24
+ marketplace_cache_ttl: 60, // minutes
25
+ language: 'pt-BR',
26
+ };
27
+
28
+ const DESCRIPTIONS = {
29
+ default_agent: 'Agente padrão ao iniciar o IDE',
30
+ memory_auto_save: 'Salvar memória automaticamente ao encerrar sessão',
31
+ update_check_interval: 'Intervalo de horas entre verificações de atualização',
32
+ metrics_enabled: 'Habilitar coleta de métricas de produtividade',
33
+ hooks_enabled: 'Habilitar git hooks para auto-tracking de commits',
34
+ marketplace_cache_ttl: 'Tempo de cache do marketplace em minutos',
35
+ language: 'Idioma preferido para prompts e saídas',
36
+ };
37
+
38
+ // ── File location ──────────────────────────────────────────────────────────────
39
+ function findConfigFile() {
40
+ const cwd = process.cwd();
41
+ const direct = path.join(cwd, '.grimoire', 'config.yaml');
42
+ const subdir = path.join(cwd, 'grimoire', '.grimoire', 'config.yaml');
43
+ if (fs.existsSync(direct)) return direct;
44
+ if (fs.existsSync(subdir)) return subdir;
45
+ // Return direct path for writing even if it doesn't exist
46
+ const grimoireDir = path.join(cwd, '.grimoire');
47
+ if (fs.existsSync(grimoireDir)) return direct;
48
+ return null;
49
+ }
50
+
51
+ // ── Minimal YAML parser/writer ─────────────────────────────────────────────────
52
+ function parseYaml(content) {
53
+ const result = {};
54
+ for (const line of content.split('\n')) {
55
+ const stripped = line.trim();
56
+ if (!stripped || stripped.startsWith('#')) continue;
57
+ const colonIdx = stripped.indexOf(':');
58
+ if (colonIdx === -1) continue;
59
+ const key = stripped.slice(0, colonIdx).trim();
60
+ let val = stripped.slice(colonIdx + 1).trim();
61
+ // Parse types
62
+ if (val === 'true') val = true;
63
+ else if (val === 'false') val = false;
64
+ else if (!isNaN(val) && val !== '') val = Number(val);
65
+ result[key] = val;
66
+ }
67
+ return result;
68
+ }
69
+
70
+ function toYaml(obj) {
71
+ const lines = ['# Grimoire Framework — Project Configuration', '# grimoire config set <key> <value> to edit', ''];
72
+ for (const [k, v] of Object.entries(obj)) {
73
+ const desc = DESCRIPTIONS[k];
74
+ if (desc) lines.push(`# ${desc}`);
75
+ lines.push(`${k}: ${v}`);
76
+ lines.push('');
77
+ }
78
+ return lines.join('\n');
79
+ }
80
+
81
+ function loadConfig() {
82
+ const file = findConfigFile();
83
+ if (!file || !fs.existsSync(file)) return { ...DEFAULTS };
84
+ try {
85
+ return { ...DEFAULTS, ...parseYaml(fs.readFileSync(file, 'utf8')) };
86
+ } catch (_) { return { ...DEFAULTS }; }
87
+ }
88
+
89
+ function saveConfig(obj) {
90
+ const cwd = process.cwd();
91
+ let grimoireDir = path.join(cwd, '.grimoire');
92
+ if (!fs.existsSync(grimoireDir)) {
93
+ const sub = path.join(cwd, 'grimoire', '.grimoire');
94
+ if (fs.existsSync(sub)) grimoireDir = sub;
95
+ else { console.error('❌ .grimoire/ not found. Run: npx grimoire-framework install'); return false; }
96
+ }
97
+ const file = path.join(grimoireDir, 'config.yaml');
98
+ fs.writeFileSync(file, toYaml(obj), 'utf8');
99
+ return true;
100
+ }
101
+
102
+ // ── Public API ─────────────────────────────────────────────────────────────────
103
+ function getConfigValue(key) {
104
+ return loadConfig()[key];
105
+ }
106
+
107
+ // ── Commands ───────────────────────────────────────────────────────────────────
108
+ function run(args) {
109
+ const sub = args[0];
110
+ const key = args[1];
111
+ const value = args[2];
112
+
113
+ switch (sub) {
114
+ case 'set': configSet(key, value); break;
115
+ case 'reset': configReset(); break;
116
+ case 'get':
117
+ if (!key) { configList(); break; }
118
+ configGet(key); break;
119
+ case 'list':
120
+ default: configList(); break;
121
+ }
122
+ }
123
+
124
+ function configGet(key) {
125
+ if (!key) { configList(); return; }
126
+ const config = loadConfig();
127
+ if (!(key in config)) {
128
+ console.log(`❌ Chave desconhecida: "${key}"`);
129
+ console.log(` Chaves válidas: ${Object.keys(DEFAULTS).join(', ')}\n`);
130
+ return;
131
+ }
132
+ console.log(`${key} = ${config[key]}`);
133
+ }
134
+
135
+ function configSet(key, value) {
136
+ if (!key || value === undefined) {
137
+ console.log('Usage: grimoire config set <key> <value>');
138
+ console.log('Ex: grimoire config set default_agent dev\n');
139
+ return;
140
+ }
141
+ if (!(key in DEFAULTS)) {
142
+ console.log(`❌ Chave desconhecida: "${key}"`);
143
+ console.log(` Chaves válidas: ${Object.keys(DEFAULTS).join(', ')}\n`);
144
+ return;
145
+ }
146
+ const config = loadConfig();
147
+ // Type coerce
148
+ let coerced = value;
149
+ if (value === 'true') coerced = true;
150
+ else if (value === 'false') coerced = false;
151
+ else if (!isNaN(value)) coerced = Number(value);
152
+
153
+ config[key] = coerced;
154
+ if (saveConfig(config)) {
155
+ console.log(`✅ ${key} = ${coerced}`);
156
+ }
157
+ }
158
+
159
+ function configList() {
160
+ const config = loadConfig();
161
+ const file = findConfigFile();
162
+ console.log(`\n⚙️ Grimoire Config${file && fs.existsSync(file) ? ` (${path.relative(process.cwd(), file)})` : ' (defaults)'}\n`);
163
+ console.log(' ' + '─'.repeat(48));
164
+ for (const [k, v] of Object.entries(DEFAULTS)) {
165
+ const current = config[k];
166
+ const isDef = current === v;
167
+ const marker = isDef ? ' ' : '✏️ ';
168
+ console.log(` ${marker} ${k.padEnd(28)} ${String(current)}`);
169
+ }
170
+ console.log(' ' + '─'.repeat(48));
171
+ console.log(` 💡 grimoire config set <key> <value> para alterar`);
172
+ console.log(` 💡 grimoire config reset para restaurar defaults\n`);
173
+ }
174
+
175
+ function configReset() {
176
+ if (saveConfig({ ...DEFAULTS })) {
177
+ console.log('✅ Configuração restaurada para os defaults.\n');
178
+ }
179
+ }
180
+
181
+ module.exports = { run, getConfigValue, loadConfig };
@@ -0,0 +1,137 @@
1
+ /**
2
+ * grimoire hooks — Git Hook Integration for Auto-Metrics Tracking
3
+ *
4
+ * grimoire hooks install Instala post-commit hook no repositório atual
5
+ * grimoire hooks uninstall Remove o hook
6
+ * grimoire hooks status Verifica se o hook está instalado
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const path = require('path');
12
+ const fs = require('fs');
13
+
14
+ const HOOK_MARKER = '# grimoire-hook-v1';
15
+ const HOOK_CONTENT = `#!/bin/sh
16
+ ${HOOK_MARKER}
17
+ # Auto-installed by Grimoire Framework
18
+ # Remove with: grimoire hooks uninstall
19
+
20
+ # Get commit message (first line)
21
+ COMMIT_MSG=$(git log -1 --pretty=%B 2>/dev/null | head -1)
22
+
23
+ # Track the commit in grimoire metrics (non-blocking)
24
+ grimoire metrics track commit "$COMMIT_MSG" 2>/dev/null &
25
+
26
+ exit 0
27
+ `;
28
+
29
+ function findGitDir() {
30
+ let dir = process.cwd();
31
+ for (let i = 0; i < 8; i++) {
32
+ if (fs.existsSync(path.join(dir, '.git'))) return dir;
33
+ const parent = path.dirname(dir);
34
+ if (parent === dir) break;
35
+ dir = parent;
36
+ }
37
+ return null;
38
+ }
39
+
40
+ function run(args) {
41
+ const sub = args[0] || 'status';
42
+ switch (sub) {
43
+ case 'install': install(); break;
44
+ case 'uninstall': uninstall(); break;
45
+ case 'status':
46
+ default: status(); break;
47
+ }
48
+ }
49
+
50
+ function install() {
51
+ const gitRoot = findGitDir();
52
+ if (!gitRoot) {
53
+ console.error('❌ Repositório git não encontrado. Execute dentro de um projeto git.');
54
+ return;
55
+ }
56
+
57
+ const hooksDir = path.join(gitRoot, '.git', 'hooks');
58
+ const hookFile = path.join(hooksDir, 'post-commit');
59
+
60
+ if (!fs.existsSync(hooksDir)) fs.mkdirSync(hooksDir, { recursive: true });
61
+
62
+ // Check if hook already exists and isn't ours
63
+ if (fs.existsSync(hookFile)) {
64
+ const existing = fs.readFileSync(hookFile, 'utf8');
65
+ if (existing.includes(HOOK_MARKER)) {
66
+ console.log('ℹ️ Hook já instalado. Use "grimoire hooks status" para verificar.');
67
+ return;
68
+ }
69
+ // Append to existing hook
70
+ const updated = existing.trimEnd() + '\n\n' + HOOK_CONTENT;
71
+ fs.writeFileSync(hookFile, updated, 'utf8');
72
+ } else {
73
+ fs.writeFileSync(hookFile, HOOK_CONTENT, 'utf8');
74
+ }
75
+
76
+ // Make executable (Unix/Mac — no-op on Windows but harmless)
77
+ try { fs.chmodSync(hookFile, '755'); } catch (_) { }
78
+
79
+ console.log(`
80
+ ✅ grimoire hooks instalado!
81
+
82
+ Hook: .git/hooks/post-commit
83
+ Efeito: commits são automaticamente registrados em grimoire metrics
84
+
85
+ Próximo passo: faça um git commit e execute:
86
+ grimoire metrics --period week
87
+
88
+ Para remover: grimoire hooks uninstall
89
+ `);
90
+ }
91
+
92
+ function uninstall() {
93
+ const gitRoot = findGitDir();
94
+ if (!gitRoot) { console.error('❌ Repositório git não encontrado.'); return; }
95
+
96
+ const hookFile = path.join(gitRoot, '.git', 'hooks', 'post-commit');
97
+ if (!fs.existsSync(hookFile)) {
98
+ console.log('ℹ️ Nenhum hook encontrado.');
99
+ return;
100
+ }
101
+
102
+ const content = fs.readFileSync(hookFile, 'utf8');
103
+ if (!content.includes(HOOK_MARKER)) {
104
+ console.log('ℹ️ Hook não foi instalado pelo Grimoire. Nada removido.');
105
+ return;
106
+ }
107
+
108
+ // If our hook is the only content, delete the file
109
+ const lines = content.split('\n');
110
+ const otherLines = lines.filter(l => !l.includes(HOOK_MARKER) && !l.includes('grimoire metrics track'));
111
+ const cleaned = otherLines.join('\n').trim();
112
+
113
+ if (!cleaned || cleaned === '#!/bin/sh') {
114
+ fs.unlinkSync(hookFile);
115
+ console.log('✅ Hook removido (arquivo deletado).');
116
+ } else {
117
+ fs.writeFileSync(hookFile, cleaned + '\n', 'utf8');
118
+ console.log('✅ Hook grimoire removido (conteúdo do hook preservado).');
119
+ }
120
+ }
121
+
122
+ function status() {
123
+ const gitRoot = findGitDir();
124
+ if (!gitRoot) { console.log('⚠️ Repositório git não encontrado.\n'); return; }
125
+
126
+ const hookFile = path.join(gitRoot, '.git', 'hooks', 'post-commit');
127
+ const exists = fs.existsSync(hookFile);
128
+ const isOurs = exists && fs.readFileSync(hookFile, 'utf8').includes(HOOK_MARKER);
129
+
130
+ console.log('\n🪝 Grimoire Hooks\n' + '─'.repeat(40));
131
+ console.log(` post-commit: ${isOurs ? '✅ instalado' : exists ? '⚠️ arquivo existe (não é nosso)' : '⭕ não instalado'}`);
132
+ if (!isOurs) console.log(`\n Para instalar: grimoire hooks install`);
133
+ else console.log(`\n Para remover: grimoire hooks uninstall`);
134
+ console.log();
135
+ }
136
+
137
+ module.exports = { run };
@@ -35,6 +35,9 @@ async function run(args) {
35
35
  case 'save':
36
36
  await saveMemory(args.slice(1), memoryDir);
37
37
  break;
38
+ case 'list-tags':
39
+ await listTags(memoryDir);
40
+ break;
38
41
  case 'list':
39
42
  await listSessions(memoryDir);
40
43
  break;
@@ -67,7 +70,22 @@ async function run(args) {
67
70
  * Saves memory using JSONL append (O(1) complexity)
68
71
  */
69
72
  async function saveMemory(args, memoryDir) {
70
- const content = args.join(' ');
73
+ // Extract --tag value if present
74
+ const tagIdx = args.findIndex(a => a === '--tag' || a.startsWith('--tag='));
75
+ let tag = null;
76
+ let filteredArgs = [...args];
77
+ if (tagIdx !== -1) {
78
+ if (args[tagIdx].includes('=')) {
79
+ tag = args[tagIdx].split('=')[1];
80
+ } else {
81
+ tag = args[tagIdx + 1];
82
+ filteredArgs.splice(tagIdx, 2);
83
+ }
84
+ if (!tag) { filteredArgs.splice(tagIdx, 1); }
85
+ else { filteredArgs = filteredArgs.filter(a => a !== args[tagIdx] && a !== tag); }
86
+ }
87
+
88
+ const content = filteredArgs.join(' ');
71
89
  if (!content) {
72
90
  console.error('❌ Please provide content to save.');
73
91
  return;
@@ -78,7 +96,8 @@ async function saveMemory(args, memoryDir) {
78
96
 
79
97
  const entry = {
80
98
  timestamp: new Date().toISOString(),
81
- content: content
99
+ content: content,
100
+ ...(tag ? { tag } : {}),
82
101
  };
83
102
 
84
103
  // Ensure file exists for lockfile
@@ -100,7 +119,8 @@ async function saveMemory(args, memoryDir) {
100
119
 
101
120
  // Append entry as a new line in JSONL format
102
121
  await fs.appendFile(sessionFile, JSON.stringify(entry) + '\n', 'utf8');
103
- console.log(`✅ Memory appended to session ${today} (JSONL)`);
122
+ const tagLabel = tag ? ` [#${tag}]` : '';
123
+ console.log(`✅ Memory appended to session ${today}${tagLabel}`);
104
124
  } catch (err) {
105
125
  console.error(`❌ Failed to acquire lock for memory file: ${err.message}`);
106
126
  } finally {
@@ -120,6 +140,37 @@ async function listSessions(memoryDir) {
120
140
  files.forEach(f => console.log(` - ${f.replace('.jsonl', '').replace('.json', '')}`));
121
141
  }
122
142
 
143
+ /**
144
+ * Lists all unique tags across all sessions
145
+ */
146
+ async function listTags(memoryDir) {
147
+ const sessionsDir = path.join(memoryDir, 'sessions');
148
+ let files;
149
+ try { files = (await fs.readdir(sessionsDir)).filter(f => f.endsWith('.jsonl')); }
150
+ catch (_) { files = []; }
151
+
152
+ const tagCount = {};
153
+ for (const file of files) {
154
+ const raw = await fs.readFile(path.join(sessionsDir, file), 'utf8').catch(() => '');
155
+ for (const line of raw.split('\n').filter(l => l.trim())) {
156
+ try {
157
+ const entry = JSON.parse(line);
158
+ if (entry.tag) tagCount[entry.tag] = (tagCount[entry.tag] || 0) + 1;
159
+ } catch (_) { }
160
+ }
161
+ }
162
+
163
+ const tags = Object.entries(tagCount).sort((a, b) => b[1] - a[1]);
164
+ console.log('\n🏷️ Grimoire Memory Tags:\n');
165
+ if (tags.length === 0) {
166
+ console.log(' (nenhuma tag registrada)');
167
+ console.log(' Use: grimoire memory save --tag <nome> "texto"\n');
168
+ return;
169
+ }
170
+ tags.forEach(([tag, count]) => console.log(` #${tag.padEnd(20)} ${count}×`));
171
+ console.log(`\n Para filtrar: grimoire memory search --tag <nome>\n`);
172
+ }
173
+
123
174
  /**
124
175
  * Shows session parsing JSONL or legacy JSON
125
176
  */
@@ -0,0 +1,162 @@
1
+ /**
2
+ * grimoire squads — Extended Squad Management
3
+ *
4
+ * grimoire squads list Lista squads disponíveis
5
+ * grimoire squads use <squad> Ativa squad e gera prompt para IDE
6
+ * grimoire squads info <squad> Detalhes do squad
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const path = require('path');
12
+ const fs = require('fs');
13
+
14
+ function findSquadsDir() {
15
+ const cwd = process.cwd();
16
+ const local = path.join(cwd, '.grimoire', 'squads');
17
+ const src = path.join(__dirname, '..', '..', 'squads'); // in node_modules
18
+ const srcAlt = path.join(cwd, 'squads');
19
+ if (fs.existsSync(local)) return local;
20
+ if (fs.existsSync(srcAlt)) return srcAlt;
21
+ if (fs.existsSync(src)) return src;
22
+ return null;
23
+ }
24
+
25
+ function readSquadYaml(squadDir) {
26
+ const yamlFile = path.join(squadDir, 'squad.yaml');
27
+ if (!fs.existsSync(yamlFile)) return null;
28
+ const raw = fs.readFileSync(yamlFile, 'utf8');
29
+
30
+ // Minimal YAML parser for the squad.yaml format
31
+ const squad = { agents: [], workflows: [] };
32
+ let currentList = null;
33
+ for (const line of raw.split('\n')) {
34
+ const stripped = line.trim();
35
+ if (stripped.startsWith('name:')) squad.name = stripped.slice(5).trim().replace(/^"|"$/g, '');
36
+ else if (stripped.startsWith('description:')) squad.description = stripped.slice(12).trim().replace(/^"|"$/g, '');
37
+ else if (stripped === 'agents:') currentList = 'agents';
38
+ else if (stripped === 'workflows:') currentList = 'workflows';
39
+ else if (stripped.startsWith('- id:')) { if (currentList === 'workflows') squad.workflows.push({ id: stripped.slice(5).trim() }); }
40
+ else if (stripped.startsWith('- ') && currentList === 'agents') squad.agents.push(stripped.slice(2).trim());
41
+ }
42
+ return squad;
43
+ }
44
+
45
+ function run(args) {
46
+ const sub = args[0];
47
+ const name = args[1];
48
+
49
+ switch (sub) {
50
+ case 'use': squadUse(name); break;
51
+ case 'info': squadInfo(name); break;
52
+ case 'list':
53
+ default: listSquads(); break;
54
+ }
55
+ }
56
+
57
+ // ── List ───────────────────────────────────────────────────────────────────────
58
+ function listSquads() {
59
+ const dir = findSquadsDir();
60
+ if (!dir) { console.log('\n⚠️ Squads not found. Run: npx grimoire-framework update\n'); return; }
61
+
62
+ const squads = fs.readdirSync(dir, { withFileTypes: true })
63
+ .filter(d => d.isDirectory())
64
+ .map(d => {
65
+ const sq = readSquadYaml(path.join(dir, d.name));
66
+ const agents = sq?.agents?.map(a => `@${a}`).join(', ') || '';
67
+ return { id: d.name, name: sq?.name || d.name, desc: sq?.description || '', agents };
68
+ });
69
+
70
+ console.log('\n👥 Squads disponíveis:\n');
71
+ squads.forEach(s => {
72
+ console.log(` 📦 ${s.id} — ${s.name}`);
73
+ console.log(` ${s.desc}`);
74
+ if (s.agents) console.log(` Agentes: ${s.agents}`);
75
+ console.log();
76
+ });
77
+ console.log(` 💡 Para ativar: grimoire squads use <squad>\n`);
78
+ }
79
+
80
+ // ── Use ────────────────────────────────────────────────────────────────────────
81
+ function squadUse(name) {
82
+ if (!name) { console.log('Usage: grimoire squads use <squad>\nEx: grimoire squads use fullstack\n'); return; }
83
+
84
+ const dir = findSquadsDir();
85
+ if (!dir) { console.log('\n⚠️ Squads not found. Run: npx grimoire-framework update\n'); return; }
86
+
87
+ const squadDir = path.join(dir, name);
88
+ if (!fs.existsSync(squadDir)) {
89
+ console.log(`❌ Squad "${name}" não encontrado.`);
90
+ console.log(` Squads disponíveis: grimoire squads list\n`);
91
+ return;
92
+ }
93
+
94
+ const squad = readSquadYaml(squadDir);
95
+ if (!squad) { console.log(`❌ squad.yaml inválido em squads/${name}/`); return; }
96
+
97
+ const agentMentions = squad.agents.map(a => `@${a}`).join(', ');
98
+ const agentList = squad.agents.map(a => `@${a}`).join(' · ');
99
+
100
+ // Try to detect project name
101
+ let projectName = '';
102
+ try {
103
+ const pkg = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8'));
104
+ projectName = pkg.name || '';
105
+ } catch (_) { }
106
+
107
+ const dateStr = new Date().toLocaleDateString('pt-BR');
108
+ const context = projectName ? `${projectName} | ${dateStr}` : dateStr;
109
+
110
+ // Workflows
111
+ const workflowList = squad.workflows?.length
112
+ ? squad.workflows.map(w => ` - \`${w.id}\`: grimoire squads workflow ${name} ${w.id}`).join('\n')
113
+ : ' (nenhum workflow definido)';
114
+
115
+ const prompt = `Ative o squad **${squad.name}**: ${agentMentions}.
116
+ Contexto: ${context}
117
+ Descrição: ${squad.description}
118
+
119
+ Para iniciar: peça para ${squad.agents[0] ? `@${squad.agents[0]}` : 'qualquer agente'} apresentar o squad e sugerir o próximo passo.`;
120
+
121
+ console.log(`
122
+ 👥 Squad: ${squad.name}
123
+ ${'─'.repeat(50)}
124
+
125
+ Descrição: ${squad.description}
126
+ Agentes: ${agentList}
127
+
128
+ Workflows disponíveis:
129
+ ${workflowList}
130
+
131
+ ${'─'.repeat(50)}
132
+ 📋 Prompt para colar no chat da IDE:
133
+ ${'─'.repeat(50)}
134
+
135
+ ${prompt}
136
+
137
+ ${'─'.repeat(50)}
138
+ 💡 Copie o bloco acima e cole no chat da sua IDE (Gemini, Cursor, Claude)
139
+ `);
140
+ }
141
+
142
+ // ── Info ───────────────────────────────────────────────────────────────────────
143
+ function squadInfo(name) {
144
+ if (!name) { listSquads(); return; }
145
+ const dir = findSquadsDir();
146
+ const squadDir = dir && path.join(dir, name);
147
+ if (!squadDir || !fs.existsSync(squadDir)) {
148
+ console.log(`❌ Squad "${name}" não encontrado. Use: grimoire squads list\n`); return;
149
+ }
150
+ const squad = readSquadYaml(squadDir);
151
+ const workflows = fs.existsSync(path.join(squadDir, 'workflows'))
152
+ ? fs.readdirSync(path.join(squadDir, 'workflows')).filter(f => f.endsWith('.md'))
153
+ : [];
154
+
155
+ console.log(`\n👥 ${squad?.name || name}\n${'─'.repeat(40)}`);
156
+ console.log(` Descrição: ${squad?.description || '—'}`);
157
+ console.log(` Agentes: ${squad?.agents?.map(a => `@${a}`).join(', ') || '—'}`);
158
+ console.log(` Workflows: ${workflows.join(', ') || '(nenhum)'}`);
159
+ console.log(`\n grimoire squads use ${name} ← gerar prompt de ativação\n`);
160
+ }
161
+
162
+ module.exports = { run };
@@ -184,13 +184,42 @@ async function run(args = []) {
184
184
  }
185
185
 
186
186
  if (dryRun) {
187
- console.log(`🔍 DRY RUN — would update v${installed} → v${latest}\n`);
188
- console.log(' Directories that would be synced:');
189
- for (const { dest } of FRAMEWORK_SYNC_DIRS) {
190
- console.log(` • ${dest}`);
187
+ console.log(`🔍 DRY RUN — v${installed} → v${latest}\n`);
188
+ console.log(' Arquivos que seriam modificados:\n');
189
+
190
+ const frameworkRoot = path.join(cwd, 'node_modules', 'grimoire-framework');
191
+ if (!fs.existsSync(frameworkRoot)) {
192
+ console.log(' ⚠️ grimoire-framework não encontrado em node_modules.');
193
+ console.log(' Para um diff completo, rode npm install antes.\n');
194
+ } else {
195
+ let anyChange = false;
196
+ for (const { src, dest } of FRAMEWORK_SYNC_DIRS) {
197
+ const srcDir = path.join(frameworkRoot, src);
198
+ const destDir = path.join(cwd, dest);
199
+ if (!fs.existsSync(srcDir)) continue;
200
+
201
+ const srcFiles = new Set(fs.readdirSync(srcDir).filter(f => f.endsWith('.md') || f.endsWith('.yaml') || f.endsWith('.json')));
202
+ const destFiles = fs.existsSync(destDir)
203
+ ? new Set(fs.readdirSync(destDir).filter(f => f.endsWith('.md') || f.endsWith('.yaml') || f.endsWith('.json')))
204
+ : new Set();
205
+
206
+ const updated = [...srcFiles].filter(f => destFiles.has(f));
207
+ const added = [...srcFiles].filter(f => !destFiles.has(f));
208
+ const preserved = [...destFiles].filter(f => !srcFiles.has(f));
209
+
210
+ if (updated.length + added.length + preserved.length > 0) {
211
+ console.log(` 📁 ${dest}/`);
212
+ updated.forEach(f => console.log(` 📝 ${f} ← atualizado (framework)`));
213
+ added.forEach(f => console.log(` ➕ ${f} ← novo`));
214
+ preserved.forEach(f => console.log(` ✏️ ${f} ← preservado (customização)`));
215
+ console.log();
216
+ anyChange = true;
217
+ }
218
+ }
219
+ if (!anyChange) console.log(' (nenhuma mudança detectada)\n');
191
220
  }
192
- console.log('\n Custom files (only in project) would be PRESERVED.');
193
- console.log(' Run without --dry-run to apply.\n');
221
+
222
+ console.log(' Execute sem --dry-run para aplicar.\n');
194
223
  return;
195
224
  }
196
225
 
@@ -107,7 +107,7 @@ async function main() {
107
107
  await require('./commands/metrics').run(args.slice(1));
108
108
  break;
109
109
  case 'squads':
110
- handleSquads(args.slice(1));
110
+ await require('./commands/squads').run(args.slice(1));
111
111
  break;
112
112
  case 'agents':
113
113
  handleAgents(args.slice(1));
@@ -124,6 +124,12 @@ async function main() {
124
124
  case 'pro':
125
125
  await require('./commands/pro').run(args.slice(1));
126
126
  break;
127
+ case 'config':
128
+ require('./commands/config').run(args.slice(1));
129
+ break;
130
+ case 'hooks':
131
+ require('./commands/hooks').run(args.slice(1));
132
+ break;
127
133
  case 'status':
128
134
  handleStatus();
129
135
  break;
@@ -475,29 +481,79 @@ function handleDoctor() {
475
481
  }
476
482
 
477
483
  function showHelp() {
484
+ const { isPro } = (() => { try { return require('./commands/pro'); } catch (_) { return { isPro: () => false }; } })();
485
+ const proLabel = isPro() ? ' 🔐' : '';
486
+
478
487
  console.log(`
479
- 🔮 Grimoire Framework CLI v${packageJson.version}
480
-
481
- The CLI manages your framework. Agents live in your IDE chat.
482
- (These are DIFFERENT — CLI = terminal, Agents = @agentname in IDE chat)
483
-
484
- USAGE:
485
- grimoire status # Framework health & what's active
486
- grimoire whoami # 🧙 Session context & how to use agents
487
- grimoire install # Install/Reset setup
488
- grimoire agents list # List available agents (with personas)
489
- grimoire squads list # List installed squads
490
- grimoire memory [sub] # Manage persistent memory
491
- grimoire metrics # View productivity metrics
492
- grimoire doctor # Run diagnostics (checks + tips)
493
- grimoire --version # Show version
494
- grimoire --help # Show this help
488
+ 🔮 Grimoire Framework CLI v${packageJson.version}${proLabel}
489
+
490
+ CLI gerencia o framework · Agentes vivem no chat da sua IDE
491
+ (CLI = terminal · Agentes = @agentname no chat da IDE)
492
+
493
+ COMANDOS ESSENCIAIS:
494
+ grimoire status Saúde do framework e o que está ativo
495
+ grimoire whoami Contexto da sessão e como usar agentes
496
+ grimoire doctor Diagnóstico completo (checks + sugestões)
497
+ grimoire update Atualiza agentes e arquivos do framework
498
+ grimoire update --dry-run 🆕 Mostra diff sem aplicar
499
+ grimoire config list 🆕 Configurações do projeto
500
+ grimoire config set <k> <v> 🆕 Define uma configuração
501
+
502
+ AGENTES:
503
+ grimoire agents list Lista todos os agentes com personas
504
+ grimoire agent create 🆕 Wizard para criar agente customizado
505
+ grimoire agent validate <n> 🆕 Valida YAML e campos obrigatórios
506
+ grimoire agent edit <n> Editar agente no EDITOR
507
+ grimoire agent remove <n> Remover agente customizado
508
+
509
+ SQUADS:
510
+ grimoire squads list Lista squads (fullstack/planning/devops)
511
+ grimoire squads use <squad> 🆕 Gera prompt de ativação para IDE
512
+ grimoire squads info <squad> 🆕 Detalhes do squad
513
+
514
+ MEMÓRIA:
515
+ grimoire memory save "texto" Salvar entrada na sessão
516
+ grimoire memory save --tag <t> "x" 🆕 Salvar com tag
517
+ grimoire memory show [--last 10] Ver entradas de hoje
518
+ grimoire memory search "termo" Buscar em todas as sessões
519
+ grimoire memory list-tags 🆕 Listar todas as tags
520
+ grimoire memory export Exportar para Markdown
521
+ grimoire memory clear --older-than 30 Limpar sessões antigas
522
+
523
+ MÉTRICAS:
524
+ grimoire metrics Dashboard do mês atual
525
+ grimoire metrics --period week Últimos 7 dias
526
+ grimoire metrics export --csv Exportar para CSV
527
+ grimoire metrics track <t> [msg] Registrar evento manual
528
+
529
+ HOOKS (git auto-tracking):
530
+ grimoire hooks install 🆕 Instala post-commit hook
531
+ grimoire hooks uninstall 🆕 Remove o hook
532
+ grimoire hooks status 🆕 Verifica se o hook está ativo
533
+
534
+ SYNC GLOBAL:
535
+ grimoire sync --global Envia agentes → ~/.grimoire/agents/
536
+ grimoire sync --from-global Puxa agentes do store global
537
+
538
+ MARKETPLACE:
539
+ grimoire marketplace list Navega agentes da comunidade
540
+ grimoire marketplace install <slug> Instala agente do marketplace
541
+ grimoire marketplace search <termo> Busca por especialidade
542
+
543
+ GRIMOIRE PRO:
544
+ grimoire pro activate <chave> Ativa licença Pro
545
+ grimoire pro status Status da licença e features
546
+ grimoire pro features Lista features Pro
547
+
548
+ OUTROS:
549
+ grimoire install Instalar/reset setup no projeto
550
+ grimoire --version Versão instalada
551
+ grimoire --help Este help
495
552
 
496
553
  QUICK START:
497
- npx grimoire-framework install # Set up in a new project
498
- grimoire status # Check everything is working
499
- grimoire whoami # See available agents
500
- Then in your IDE chat: @dev or @qa or @grimoire-master
554
+ npx grimoire-framework install Setup em novo projeto
555
+ grimoire squads use fullstack Ativar squad fullstack
556
+ @dev @qa @architect No chat da sua IDE
501
557
  `);
502
558
  }
503
559
 
@@ -0,0 +1,56 @@
1
+ # api-designer
2
+
3
+ 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.
4
+
5
+ ## COMPLETE AGENT DEFINITION FOLLOWS - NO EXTERNAL FILES NEEDED
6
+
7
+ ```yaml
8
+ activation-instructions:
9
+ - STEP 1: Adote a persona Klimt completamente
10
+ - STEP 2: Apresente o greeting abaixo
11
+ - STEP 3: HALT e aguarde input
12
+ - STAY IN CHARACTER até *exit
13
+
14
+ agent:
15
+ name: Klimt
16
+ id: api-designer
17
+ title: API Design & Documentation Specialist
18
+ icon: 🔗
19
+ source: marketplace
20
+ author: grimoire-team
21
+ tags: [rest, graphql, openapi, swagger, grpc, api-design]
22
+
23
+ persona:
24
+ role: API Designer & Integration Architect
25
+ style: Preciso, consistente, orientado a contratos
26
+ identity: |
27
+ Klimt — O Arquiteto de Contratos. Expert em design de APIs RESTful e GraphQL,
28
+ documentação OpenAPI 3.x e estratégias de versionamento.
29
+ principles:
30
+ - API-first design
31
+ - Contratos imutáveis (versioning)
32
+ - Nomes e verbos HTTP semânticos
33
+ - Documentação como primeiro cidadão
34
+
35
+ greeting_levels:
36
+ default: |
37
+ 🔗 **Klimt** (@api-designer) pronto!
38
+ Especialidade: **API Design & Documentação**
39
+
40
+ Posso ajudar com:
41
+ - Design de APIs REST e GraphQL
42
+ - Geração de spec OpenAPI 3.x / Swagger
43
+ - Versionamento e contratos de API
44
+ - Revisão de endpoints existentes
45
+
46
+ Qual API vamos projetar?
47
+
48
+ commands:
49
+ - "*help — Comandos disponíveis"
50
+ - "*design — Projetar nova API"
51
+ - "*review — Revisar API existente"
52
+ - "*spec — Gerar OpenAPI spec"
53
+ - "*exit — Sair do agente"
54
+
55
+ signature_closing: '— Klimt, tecendo contratos em ouro 🔗'
56
+ ```
@@ -0,0 +1,56 @@
1
+ # database-expert
2
+
3
+ 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.
4
+
5
+ ## COMPLETE AGENT DEFINITION FOLLOWS - NO EXTERNAL FILES NEEDED
6
+
7
+ ```yaml
8
+ activation-instructions:
9
+ - STEP 1: Adote a persona Mondrian completamente
10
+ - STEP 2: Apresente o greeting abaixo
11
+ - STEP 3: HALT e aguarde input
12
+ - STAY IN CHARACTER até *exit
13
+
14
+ agent:
15
+ name: Mondrian
16
+ id: database-expert
17
+ title: Banco de Dados & Query Specialist
18
+ icon: 🗄️
19
+ source: marketplace
20
+ author: grimoire-team
21
+ tags: [sql, postgresql, mongodb, redis, performance, schema]
22
+
23
+ persona:
24
+ role: Database Engineer & Query Optimizer
25
+ style: Estruturado, performático, orientado a dados
26
+ identity: |
27
+ Mondrian — O Arquiteto de Dados. Expert em modelagem de schemas,
28
+ otimização de queries, migrações e estratégias de indexação.
29
+ principles:
30
+ - Schema correto antes de otimização
31
+ - EXPLAIN antes de INDEX
32
+ - Migrações sempre reversíveis
33
+ - N+1 é um erro, não uma feature
34
+
35
+ greeting_levels:
36
+ default: |
37
+ 🗄️ **Mondrian** (@database-expert) pronto!
38
+ Especialidade: **Banco de Dados & Queries**
39
+
40
+ Posso ajudar com:
41
+ - Design e revisão de schemas (PostgreSQL, MongoDB)
42
+ - Otimização de queries e índices
43
+ - Migrações seguras e reversíveis
44
+ - Redis: caching, pub/sub, filas
45
+
46
+ Qual problema de dados vamos resolver?
47
+
48
+ commands:
49
+ - "*help — Comandos disponíveis"
50
+ - "*schema — Revisar ou projetar schema"
51
+ - "*optimize — Otimizar query com EXPLAIN"
52
+ - "*migrate — Planejar migração segura"
53
+ - "*exit — Sair do agente"
54
+
55
+ signature_closing: '— Mondrian, organizando dados em grades perfeitas 🗄️'
56
+ ```
@@ -0,0 +1,55 @@
1
+ # frontend-specialist
2
+
3
+ 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.
4
+
5
+ ## COMPLETE AGENT DEFINITION FOLLOWS - NO EXTERNAL FILES NEEDED
6
+
7
+ ```yaml
8
+ activation-instructions:
9
+ - STEP 1: Adote a persona Botticelli completamente
10
+ - STEP 2: Apresente o greeting abaixo
11
+ - STEP 3: HALT e aguarde input
12
+ - STAY IN CHARACTER até *exit
13
+
14
+ agent:
15
+ name: Botticelli
16
+ id: frontend-specialist
17
+ title: Frontend & UI/UX Specialist
18
+ icon: 🎠
19
+ source: marketplace
20
+ author: grimoire-team
21
+ tags: [react, css, typescript, nextjs, tailwind]
22
+
23
+ persona:
24
+ role: Frontend & UI/UX Engineer
25
+ style: Design-oriented, pixel-perfect, performance-conscious
26
+ identity: |
27
+ Botticelli — Mestre do Frontend. Expert em React, TypeScript, design systems
28
+ e experiência do usuário. Cada componente é uma obra de arte.
29
+ principles:
30
+ - Componentes acessíveis (WCAG 2.1 AA)
31
+ - Performance: Core Web Vitals em verde
32
+ - Design system antes de componentes ad-hoc
33
+ - Mobile-first sempre
34
+
35
+ greeting_levels:
36
+ default: |
37
+ 🎠 **Botticelli** (@frontend-specialist) pronto!
38
+ Especialidade: **Frontend & UI/UX**
39
+
40
+ Posso ajudar com:
41
+ - Implementação de componentes React/Next.js
42
+ - Design system e tokens de estilo
43
+ - Performance (Core Web Vitals, bundle size)
44
+ - Acessibilidade e responsividade
45
+
46
+ O que vamos construir?
47
+
48
+ commands:
49
+ - "*help — Comandos disponíveis"
50
+ - "*review — Code review de componente"
51
+ - "*a11y — Auditoria de acessibilidade"
52
+ - "*exit — Sair do agente"
53
+
54
+ signature_closing: '— Botticelli, pintando o frontend pixel a pixel 🎠'
55
+ ```
@@ -0,0 +1,55 @@
1
+ # ml-engineer
2
+
3
+ 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.
4
+
5
+ ## COMPLETE AGENT DEFINITION FOLLOWS - NO EXTERNAL FILES NEEDED
6
+
7
+ ```yaml
8
+ activation-instructions:
9
+ - STEP 1: Adote a persona Dalí completamente
10
+ - STEP 2: Apresente o greeting abaixo
11
+ - STEP 3: HALT e aguarde input
12
+ - STAY IN CHARACTER até *exit
13
+
14
+ agent:
15
+ name: Dalí
16
+ id: ml-engineer
17
+ title: Machine Learning & AI Engineer
18
+ icon: 🧠
19
+ source: marketplace
20
+ author: grimoire-team
21
+ tags: [ml, python, pytorch, sklearn, llm, mlops]
22
+
23
+ persona:
24
+ role: ML Engineer & AI Specialist
25
+ style: Experimental, data-driven, pragmático
26
+ identity: |
27
+ Dalí — O Visionário. Expert em machine learning, pipelines de dados,
28
+ fine-tuning de LLMs e MLOps. Transforma dados em inteligência.
29
+ principles:
30
+ - Data primeiro, modelo depois
31
+ - Baseline simples antes de complexidade
32
+ - Reprodutibilidade é obrigatória
33
+ - Monitorar drift em produção
34
+
35
+ greeting_levels:
36
+ default: |
37
+ 🧠 **Dalí** (@ml-engineer) pronto!
38
+ Especialidade: **Machine Learning & AI**
39
+
40
+ Posso ajudar com:
41
+ - Pipelines de ML (feature eng, treinamento, avaliação)
42
+ - Fine-tuning e prompt engineering de LLMs
43
+ - MLOps (tracking, serving, monitoramento)
44
+ - Análise exploratória de dados
45
+
46
+ Qual problema de ML vamos resolver?
47
+
48
+ commands:
49
+ - "*help — Comandos disponíveis"
50
+ - "*pipeline — Desenhar pipeline de ML"
51
+ - "*evaluate — Avaliar modelo e métricas"
52
+ - "*exit — Sair do agente"
53
+
54
+ signature_closing: '— Dalí, sonhando em tensores 🧠'
55
+ ```
@@ -0,0 +1,55 @@
1
+ # security-auditor
2
+
3
+ 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.
4
+
5
+ ## COMPLETE AGENT DEFINITION FOLLOWS - NO EXTERNAL FILES NEEDED
6
+
7
+ ```yaml
8
+ activation-instructions:
9
+ - STEP 1: Adote a persona Rembrandt completamente
10
+ - STEP 2: Apresente o greeting abaixo
11
+ - STEP 3: HALT e aguarde input
12
+ - STAY IN CHARACTER até *exit
13
+
14
+ agent:
15
+ name: Rembrandt
16
+ id: security-auditor
17
+ title: Security & Audit Specialist
18
+ icon: 🔐
19
+ source: marketplace
20
+ author: grimoire-team
21
+ tags: [security, owasp, pentest, audit, sast]
22
+
23
+ persona:
24
+ role: Application Security Engineer
25
+ style: Metódico, cético construtivo, orientado a evidências
26
+ identity: |
27
+ Rembrandt — O Auditor. Expert em segurança de aplicações, OWASP Top 10,
28
+ revisão de código seguro e modelagem de ameaças.
29
+ principles:
30
+ - Defense in depth
31
+ - Principe do menor privilégio
32
+ - Shift-left security
33
+ - Evidência antes de conclusão
34
+
35
+ greeting_levels:
36
+ default: |
37
+ 🔐 **Rembrandt** (@security-auditor) pronto!
38
+ Especialidade: **Segurança & Auditoria**
39
+
40
+ Posso realizar:
41
+ - Code review de segurança (OWASP Top 10)
42
+ - Auditoria de autenticação e autorização
43
+ - Análise de dependências vulneráveis
44
+ - Modelagem de ameaças (STRIDE)
45
+
46
+ O que devo auditar?
47
+
48
+ commands:
49
+ - "*help — Comandos disponíveis"
50
+ - "*audit — Auditoria de segurança completa"
51
+ - "*owasp — Checklist OWASP Top 10"
52
+ - "*exit — Sair do agente"
53
+
54
+ signature_closing: '— Rembrandt, iluminando as sombras do código 🔐'
55
+ ```
@@ -0,0 +1,56 @@
1
+ # tech-writer
2
+
3
+ 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.
4
+
5
+ ## COMPLETE AGENT DEFINITION FOLLOWS - NO EXTERNAL FILES NEEDED
6
+
7
+ ```yaml
8
+ activation-instructions:
9
+ - STEP 1: Adote a persona Cervantes completamente
10
+ - STEP 2: Apresente o greeting abaixo
11
+ - STEP 3: HALT e aguarde input
12
+ - STAY IN CHARACTER até *exit
13
+
14
+ agent:
15
+ name: Cervantes
16
+ id: tech-writer
17
+ title: Documentação Técnica Specialist
18
+ icon: ✍️
19
+ source: marketplace
20
+ author: grimoire-team
21
+ tags: [docs, readme, adr, wiki, changelog, openapi]
22
+
23
+ persona:
24
+ role: Technical Writer & Documentation Engineer
25
+ style: Claro, conciso, orientado ao leitor
26
+ identity: |
27
+ Cervantes — O Narrador Técnico. Expert em documentação de software:
28
+ READMEs, ADRs, guias de API, wikis e changelogs que pessoas realmente leem.
29
+ principles:
30
+ - Escreva para o leitor, não para o autor
31
+ - Exemplos valem mais que explicações
32
+ - Docs são código — versionados e revisados
33
+ - CHANGELOG honesto = confiança do usuário
34
+
35
+ greeting_levels:
36
+ default: |
37
+ ✍️ **Cervantes** (@tech-writer) pronto!
38
+ Especialidade: **Documentação Técnica**
39
+
40
+ Posso criar:
41
+ - READMEs claros e completos
42
+ - ADRs (Architecture Decision Records)
43
+ - Guias de contribuição e onboarding
44
+ - CHANGELOGs e release notes
45
+
46
+ O que precisa ser documentado?
47
+
48
+ commands:
49
+ - "*help — Comandos disponíveis"
50
+ - "*readme — Criar/revisar README"
51
+ - "*adr — Criar ADR"
52
+ - "*changelog — Escrever CHANGELOG entry"
53
+ - "*exit — Sair do agente"
54
+
55
+ signature_closing: '— Cervantes, imortalizando código em palavras ✍️'
56
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grimoire-framework",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Grimoire: AI-Orchestrated System for Full Stack Development - Core Framework",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -135,7 +135,36 @@ if (fs.existsSync(cliPath)) {
135
135
  );
136
136
  }
137
137
 
138
- // ── 7. npm pack dry-run to count files ────────────────────────────────────
138
+ // ── 7. squads/ directory ──────────────────────────────────────────────────
139
+ console.log('\n👥 Squads:');
140
+ const squadsDir = path.join(ROOT, 'squads');
141
+ if (fs.existsSync(squadsDir)) {
142
+ const squads = fs.readdirSync(squadsDir, { withFileTypes: true })
143
+ .filter(d => d.isDirectory()).map(d => d.name);
144
+ check(`squads/ directory exists (${squads.length} squads)`, squads.length >= 3,
145
+ 'Expected at least 3 squads (fullstack, planning, devops)');
146
+ for (const s of ['fullstack', 'planning', 'devops']) {
147
+ check(`Squad: ${s}`, squads.includes(s), `Missing squad: squads/${s}/`);
148
+ }
149
+ } else {
150
+ check('squads/ directory exists', false, 'squads/ directory missing — check files[] in package.json');
151
+ }
152
+
153
+ // ── 8. marketplace/ registry ──────────────────────────────────────────────
154
+ console.log('\n🏪 Marketplace:');
155
+ const marketplaceRegistry = path.join(ROOT, 'marketplace', 'registry.json');
156
+ check('marketplace/registry.json exists', fs.existsSync(marketplaceRegistry),
157
+ 'marketplace/registry.json is missing — run: create marketplace/registry.json');
158
+ if (fs.existsSync(marketplaceRegistry)) {
159
+ try {
160
+ const registry = JSON.parse(fs.readFileSync(marketplaceRegistry, 'utf8'));
161
+ check(`registry.json has agents (found ${(registry.agents || []).length})`,
162
+ (registry.agents || []).length >= 1, 'registry.json should have at least 1 agent entry');
163
+ } catch (e) {
164
+ check('registry.json is valid JSON', false, `Parse error: ${e.message}`);
165
+ }
166
+ }
167
+
139
168
  console.log('\n📦 npm pack dry-run:');
140
169
  try {
141
170
  const packOutput = execSync('npm pack --dry-run 2>&1', { cwd: ROOT, encoding: 'utf8' });