grimoire-framework 1.0.18 → 1.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.grimoire/install-manifest.yaml +2 -2
- package/bin/commands/agent.js +216 -0
- package/bin/commands/memory.js +131 -25
- package/bin/commands/update.js +1 -0
- package/bin/grimoire-cli.js +92 -1
- package/package.json +2 -1
- package/packages/installer/src/utils/grimoire-colors.js +65 -36
- package/squads/.gitkeep +2 -0
- package/squads/README.md +25 -0
- package/squads/devops/squad.yaml +9 -0
- package/squads/devops/workflows/deploy-pipeline.md +22 -0
- package/squads/fullstack/squad.yaml +11 -0
- package/squads/fullstack/workflows/code-review.md +22 -0
- package/squads/fullstack/workflows/new-feature.md +24 -0
- package/squads/planning/squad.yaml +10 -0
- package/squads/planning/workflows/prd-to-stories.md +20 -0
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
# - SHA256 hashes for change detection
|
|
8
8
|
# - File types for categorization
|
|
9
9
|
#
|
|
10
|
-
version: 1.0.
|
|
11
|
-
generated_at: "2026-02-22T14:
|
|
10
|
+
version: 1.0.20
|
|
11
|
+
generated_at: "2026-02-22T14:39:48.383Z"
|
|
12
12
|
generator: scripts/generate-install-manifest.js
|
|
13
13
|
file_count: 1011
|
|
14
14
|
files:
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* grimoire agent — Agent Management CLI
|
|
3
|
+
*
|
|
4
|
+
* Commands:
|
|
5
|
+
* grimoire agent create — interative wizard to create a custom agent
|
|
6
|
+
* grimoire agent list — alias for grimoire agents list
|
|
7
|
+
* grimoire agent edit <n> — open agent in $EDITOR
|
|
8
|
+
* grimoire agent remove <n>— delete a custom agent
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const { execSync } = require('child_process');
|
|
16
|
+
|
|
17
|
+
// ── Personas catalogue for the wizard ─────────────────────────────────────────
|
|
18
|
+
const BASE_AGENTS = [
|
|
19
|
+
{ id: 'dev', icon: '🎨', name: 'Da Vinci', specialty: 'Full-Stack Development' },
|
|
20
|
+
{ id: 'qa', icon: '🖌️', name: 'Dürer', specialty: 'Quality Assurance' },
|
|
21
|
+
{ id: 'architect', icon: '🏛️', name: 'Gaudí', specialty: 'System Architecture' },
|
|
22
|
+
{ id: 'analyst', icon: '🔍', name: 'Vermeer', specialty: 'Research & Analysis' },
|
|
23
|
+
{ id: 'grimoire-master', icon: '👑', name: 'Michelangelo', specialty: 'Orchestration' },
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const ICON_OPTIONS = ['🎭', '🎪', '🌟', '⚡', '🔮', '🦊', '🐉', '🌊', '🔥', '🌙', '🎯', '🗡️', '🧙', '🎸', '🦅'];
|
|
27
|
+
|
|
28
|
+
// ── Template generator ─────────────────────────────────────────────────────────
|
|
29
|
+
function generateAgentTemplate({ id, name, icon, specialty, baseAgent, description }) {
|
|
30
|
+
const base = BASE_AGENTS.find(a => a.id === baseAgent);
|
|
31
|
+
const baseRef = base ? `Based on ${base.name} (${base.id})` : 'Custom Agent';
|
|
32
|
+
|
|
33
|
+
return `# ${id}
|
|
34
|
+
|
|
35
|
+
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.
|
|
36
|
+
|
|
37
|
+
CRITICAL: Read the full YAML BLOCK that FOLLOWS IN THIS FILE to understand your operating params, start and follow exactly your activation-instructions to alter your state of being, stay in this being until told to exit this mode:
|
|
38
|
+
|
|
39
|
+
## COMPLETE AGENT DEFINITION FOLLOWS - NO EXTERNAL FILES NEEDED
|
|
40
|
+
|
|
41
|
+
\`\`\`yaml
|
|
42
|
+
activation-instructions:
|
|
43
|
+
- STEP 1: Read THIS ENTIRE FILE to understand your persona
|
|
44
|
+
- STEP 2: Adopt the ${name} persona completely
|
|
45
|
+
- STEP 3: Present your greeting to the user
|
|
46
|
+
- STEP 4: HALT and await user input
|
|
47
|
+
- STAY IN CHARACTER until user types *exit
|
|
48
|
+
|
|
49
|
+
agent:
|
|
50
|
+
name: ${name}
|
|
51
|
+
id: ${id}
|
|
52
|
+
title: ${specialty}
|
|
53
|
+
icon: ${icon}
|
|
54
|
+
base: ${baseRef}
|
|
55
|
+
whenToUse: ${description}
|
|
56
|
+
|
|
57
|
+
persona:
|
|
58
|
+
role: ${specialty} Specialist
|
|
59
|
+
style: Collaborative and focused on ${specialty.toLowerCase()}
|
|
60
|
+
identity: ${name} — ${description}
|
|
61
|
+
|
|
62
|
+
greeting_levels:
|
|
63
|
+
default: |
|
|
64
|
+
${icon} **${name}** (${id}) pronto!
|
|
65
|
+
Especialidade: **${specialty}**
|
|
66
|
+
|
|
67
|
+
Como posso ajudar?
|
|
68
|
+
|
|
69
|
+
commands:
|
|
70
|
+
- "*help — Show available commands"
|
|
71
|
+
- "*exit — Exit agent mode"
|
|
72
|
+
|
|
73
|
+
signature_closing: '— ${name} ${icon}'
|
|
74
|
+
\`\`\`
|
|
75
|
+
`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ── Prompt helpers (without @clack/prompts dependency) ─────────────────────────
|
|
79
|
+
const readline = require('readline');
|
|
80
|
+
|
|
81
|
+
function prompt(question) {
|
|
82
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
83
|
+
return new Promise(resolve => {
|
|
84
|
+
rl.question(question, answer => { rl.close(); resolve(answer.trim()); });
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function select(question, options) {
|
|
89
|
+
console.log(`\n${question}`);
|
|
90
|
+
options.forEach((opt, i) => console.log(` ${i + 1}. ${opt.label || opt}`));
|
|
91
|
+
const answer = await prompt(' Escolha (número): ');
|
|
92
|
+
const idx = parseInt(answer) - 1;
|
|
93
|
+
if (idx >= 0 && idx < options.length) return options[idx].value || options[idx];
|
|
94
|
+
return options[0].value || options[0];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ── Commands ───────────────────────────────────────────────────────────────────
|
|
98
|
+
async function run(args) {
|
|
99
|
+
const sub = args[0];
|
|
100
|
+
switch (sub) {
|
|
101
|
+
case 'create': await createWizard(); break;
|
|
102
|
+
case 'edit': await editAgent(args[1]); break;
|
|
103
|
+
case 'remove':
|
|
104
|
+
case 'delete': await removeAgent(args[1]); break;
|
|
105
|
+
case 'list':
|
|
106
|
+
default: listAgents(); break;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function listAgents() {
|
|
111
|
+
const cwd = process.cwd();
|
|
112
|
+
const agentsDir = path.join(cwd, '.codex', 'agents');
|
|
113
|
+
if (!fs.existsSync(agentsDir)) {
|
|
114
|
+
console.log('❌ Agents directory not found. Run: npx grimoire-framework install');
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const agents = fs.readdirSync(agentsDir).filter(f => f.endsWith('.md'));
|
|
118
|
+
console.log(`\n🤖 Grimoire Agents (${agents.length}):\n`);
|
|
119
|
+
agents.forEach(f => {
|
|
120
|
+
const name = f.replace('.md', '');
|
|
121
|
+
const base = BASE_AGENTS.find(a => a.id === name);
|
|
122
|
+
const icon = base ? base.icon : '🎭';
|
|
123
|
+
const persona = base ? ` — ${base.name} (${base.specialty})` : ' — Custom Agent';
|
|
124
|
+
console.log(` ${icon} @${name}${persona}`);
|
|
125
|
+
});
|
|
126
|
+
console.log('\n💡 Para criar um agente customizado: grimoire agent create');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async function createWizard() {
|
|
130
|
+
console.log('\n🧙 Grimoire Agent Creator\n');
|
|
131
|
+
|
|
132
|
+
// Step 1: Name/ID
|
|
133
|
+
const id = await prompt('Nome do agente (ex: caravaggio): ');
|
|
134
|
+
if (!id || !/^[a-z0-9-]+$/.test(id)) {
|
|
135
|
+
console.log('❌ Nome inválido. Use apenas letras minúsculas, números e hífens.');
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const cwd = process.cwd();
|
|
140
|
+
const agentsDir = path.join(cwd, '.codex', 'agents');
|
|
141
|
+
const agentFile = path.join(agentsDir, `${id}.md`);
|
|
142
|
+
|
|
143
|
+
if (fs.existsSync(agentFile)) {
|
|
144
|
+
console.log(`❌ Agente "${id}" já existe em ${agentFile}`);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Step 2: Display name
|
|
149
|
+
const name = await prompt(`Nome de exibição (ex: Caravaggio): `);
|
|
150
|
+
|
|
151
|
+
// Step 3: Specialty
|
|
152
|
+
const specialty = await prompt('Especialidade (ex: Arte Generativa e Criatividade): ');
|
|
153
|
+
|
|
154
|
+
// Step 4: Icon
|
|
155
|
+
const iconChoice = await select(
|
|
156
|
+
'Ícone do agente:',
|
|
157
|
+
ICON_OPTIONS.map((i, idx) => ({ label: `${i} (${idx + 1})`, value: i }))
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
// Step 5: Base agent (inherit style from)
|
|
161
|
+
const baseChoice = await select(
|
|
162
|
+
'Baseado em (herda o estilo de):',
|
|
163
|
+
[...BASE_AGENTS.map(a => ({ label: `${a.icon} ${a.name} — ${a.specialty}`, value: a.id })),
|
|
164
|
+
{ label: '🆕 Custom (do zero)', value: 'custom' }]
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
// Step 6: Short description
|
|
168
|
+
const description = await prompt('Descrição curta (ex: Especialista em arte e criatividade visual): ');
|
|
169
|
+
|
|
170
|
+
// Generate & write
|
|
171
|
+
if (!fs.existsSync(agentsDir)) fs.mkdirSync(agentsDir, { recursive: true });
|
|
172
|
+
|
|
173
|
+
const content = generateAgentTemplate({
|
|
174
|
+
id, name: name || id, icon: iconChoice, specialty: specialty || 'Custom Specialist',
|
|
175
|
+
baseAgent: baseChoice !== 'custom' ? baseChoice : null,
|
|
176
|
+
description: description || `Custom agent: ${id}`
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
fs.writeFileSync(agentFile, content, 'utf8');
|
|
180
|
+
|
|
181
|
+
console.log(`
|
|
182
|
+
✅ Agente criado: .codex/agents/${id}.md
|
|
183
|
+
|
|
184
|
+
🎭 ${iconChoice} ${name || id} — ${specialty}
|
|
185
|
+
|
|
186
|
+
Para ativar no Gemini CLI:
|
|
187
|
+
@${id}
|
|
188
|
+
|
|
189
|
+
Para sincronizar com outras IDEs:
|
|
190
|
+
npx grimoire-framework update
|
|
191
|
+
`);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async function editAgent(id) {
|
|
195
|
+
if (!id) { console.log('Usage: grimoire agent edit <name>'); return; }
|
|
196
|
+
const file = path.join(process.cwd(), '.codex', 'agents', `${id}.md`);
|
|
197
|
+
if (!fs.existsSync(file)) { console.log(`❌ Agent "${id}" not found.`); return; }
|
|
198
|
+
const editor = process.env.EDITOR || process.env.VISUAL || 'notepad';
|
|
199
|
+
try { execSync(`${editor} "${file}"`, { stdio: 'inherit' }); }
|
|
200
|
+
catch (e) { console.log(`❌ Could not open editor. Edit manually: ${file}`); }
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async function removeAgent(id) {
|
|
204
|
+
if (!id) { console.log('Usage: grimoire agent remove <name>'); return; }
|
|
205
|
+
const file = path.join(process.cwd(), '.codex', 'agents', `${id}.md`);
|
|
206
|
+
if (!fs.existsSync(file)) { console.log(`❌ Agent "${id}" not found.`); return; }
|
|
207
|
+
const confirmed = await prompt(`Remove @${id}? (y/N): `);
|
|
208
|
+
if (confirmed.toLowerCase() === 'y') {
|
|
209
|
+
fs.unlinkSync(file);
|
|
210
|
+
console.log(`✅ Agent "${id}" removed.`);
|
|
211
|
+
} else {
|
|
212
|
+
console.log('Cancelled.');
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
module.exports = { run };
|
package/bin/commands/memory.js
CHANGED
|
@@ -16,13 +16,13 @@ const lockfile = require('proper-lockfile');
|
|
|
16
16
|
*/
|
|
17
17
|
async function run(args) {
|
|
18
18
|
const subCommand = args[0];
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
// Look for .grimoire directory in cwd or in grimoire/ subdirectory
|
|
21
21
|
let baseDir = process.cwd();
|
|
22
22
|
if (!existsSync(path.join(baseDir, '.grimoire')) && existsSync(path.join(baseDir, 'grimoire', '.grimoire'))) {
|
|
23
23
|
baseDir = path.join(baseDir, 'grimoire');
|
|
24
24
|
}
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
const memoryDir = path.join(baseDir, '.grimoire', 'memory');
|
|
27
27
|
|
|
28
28
|
if (!existsSync(memoryDir)) {
|
|
@@ -41,6 +41,15 @@ async function run(args) {
|
|
|
41
41
|
case 'show':
|
|
42
42
|
await showSession(args.slice(1), memoryDir);
|
|
43
43
|
break;
|
|
44
|
+
case 'search':
|
|
45
|
+
await searchMemory(args.slice(1), memoryDir);
|
|
46
|
+
break;
|
|
47
|
+
case 'export':
|
|
48
|
+
await exportMemory(args.slice(1), memoryDir);
|
|
49
|
+
break;
|
|
50
|
+
case 'clear':
|
|
51
|
+
await clearMemory(args.slice(1), memoryDir);
|
|
52
|
+
break;
|
|
44
53
|
case 'digest':
|
|
45
54
|
await digestMemory(args.slice(1), memoryDir);
|
|
46
55
|
break;
|
|
@@ -80,7 +89,7 @@ async function saveMemory(args, memoryDir) {
|
|
|
80
89
|
let release;
|
|
81
90
|
try {
|
|
82
91
|
// Acquire lock with retry logic
|
|
83
|
-
release = await lockfile.lock(sessionFile, {
|
|
92
|
+
release = await lockfile.lock(sessionFile, {
|
|
84
93
|
retries: {
|
|
85
94
|
retries: 5,
|
|
86
95
|
factor: 3,
|
|
@@ -115,8 +124,14 @@ async function listSessions(memoryDir) {
|
|
|
115
124
|
* Shows session parsing JSONL or legacy JSON
|
|
116
125
|
*/
|
|
117
126
|
async function showSession(args, memoryDir) {
|
|
118
|
-
|
|
119
|
-
|
|
127
|
+
// Support: grimoire memory show --last 5
|
|
128
|
+
let date, lastN;
|
|
129
|
+
for (let i = 0; i < args.length; i++) {
|
|
130
|
+
if (args[i] === '--last' && args[i + 1]) lastN = parseInt(args[i + 1]);
|
|
131
|
+
else if (!args[i].startsWith('-')) date = args[i];
|
|
132
|
+
}
|
|
133
|
+
date = date || new Date().toISOString().split('T')[0];
|
|
134
|
+
|
|
120
135
|
let sessionFile = path.join(memoryDir, 'sessions', `${date}.jsonl`);
|
|
121
136
|
let isJsonl = true;
|
|
122
137
|
|
|
@@ -131,20 +146,101 @@ async function showSession(args, memoryDir) {
|
|
|
131
146
|
}
|
|
132
147
|
|
|
133
148
|
const rawContent = await fs.readFile(sessionFile, 'utf8');
|
|
134
|
-
|
|
135
|
-
|
|
149
|
+
let entries;
|
|
136
150
|
if (isJsonl) {
|
|
137
|
-
|
|
138
|
-
lines.forEach(line => {
|
|
139
|
-
const e = JSON.parse(line);
|
|
140
|
-
console.log(` [${e.timestamp.split('T')[1].split('.')[0]}] ${e.content}`);
|
|
141
|
-
});
|
|
151
|
+
entries = rawContent.split('\n').filter(l => l.trim()).map(l => JSON.parse(l));
|
|
142
152
|
} else {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
153
|
+
entries = JSON.parse(rawContent).entries;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (lastN) entries = entries.slice(-lastN);
|
|
157
|
+
|
|
158
|
+
console.log(`\n📖 Session: ${date} (${entries.length} entr${entries.length === 1 ? 'y' : 'ies'})`);
|
|
159
|
+
entries.forEach(e => {
|
|
160
|
+
console.log(` [${e.timestamp.split('T')[1].split('.')[0]}] ${e.content}`);
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Search entries across all sessions
|
|
166
|
+
*/
|
|
167
|
+
async function searchMemory(args, memoryDir) {
|
|
168
|
+
const keyword = args.join(' ');
|
|
169
|
+
if (!keyword) { console.error('❌ Provide a search term.'); return; }
|
|
170
|
+
|
|
171
|
+
const sessionsDir = path.join(memoryDir, 'sessions');
|
|
172
|
+
const files = (await fs.readdir(sessionsDir)).filter(f => f.endsWith('.jsonl') || f.endsWith('.json'));
|
|
173
|
+
|
|
174
|
+
let matches = [];
|
|
175
|
+
for (const file of files) {
|
|
176
|
+
const raw = await fs.readFile(path.join(sessionsDir, file), 'utf8');
|
|
177
|
+
const entries = file.endsWith('.jsonl')
|
|
178
|
+
? raw.split('\n').filter(l => l.trim()).map(l => JSON.parse(l))
|
|
179
|
+
: JSON.parse(raw).entries || [];
|
|
180
|
+
const date = file.replace(/\.(jsonl|json)$/, '');
|
|
181
|
+
for (const e of entries) {
|
|
182
|
+
if (e.content.toLowerCase().includes(keyword.toLowerCase())) {
|
|
183
|
+
matches.push({ date, time: e.timestamp.split('T')[1].split('.')[0], content: e.content });
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
console.log(`\n🔍 Search: "${keyword}" — ${matches.length} result${matches.length === 1 ? '' : 's'}`);
|
|
189
|
+
if (matches.length === 0) { console.log(' (No entries found)'); return; }
|
|
190
|
+
matches.forEach(m => console.log(` [${m.date} ${m.time}] ${m.content}`));
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Export all sessions to a Markdown file
|
|
195
|
+
*/
|
|
196
|
+
async function exportMemory(args, memoryDir) {
|
|
197
|
+
const format = args.includes('--format') ? args[args.indexOf('--format') + 1] : 'markdown';
|
|
198
|
+
const sessionsDir = path.join(memoryDir, 'sessions');
|
|
199
|
+
const files = (await fs.readdir(sessionsDir)).filter(f => f.endsWith('.jsonl') || f.endsWith('.json')).sort();
|
|
200
|
+
|
|
201
|
+
let output = '# Grimoire Memory Export\n\n';
|
|
202
|
+
output += `> Exported: ${new Date().toISOString()}\n\n`;
|
|
203
|
+
|
|
204
|
+
for (const file of files) {
|
|
205
|
+
const date = file.replace(/\.(jsonl|json)$/, '');
|
|
206
|
+
const raw = await fs.readFile(path.join(sessionsDir, file), 'utf8');
|
|
207
|
+
const entries = file.endsWith('.jsonl')
|
|
208
|
+
? raw.split('\n').filter(l => l.trim()).map(l => JSON.parse(l))
|
|
209
|
+
: JSON.parse(raw).entries || [];
|
|
210
|
+
|
|
211
|
+
output += `## ${date}\n\n`;
|
|
212
|
+
entries.forEach(e => {
|
|
213
|
+
output += `- \`${e.timestamp.split('T')[1].split('.')[0]}\` ${e.content}\n`;
|
|
146
214
|
});
|
|
215
|
+
output += '\n';
|
|
147
216
|
}
|
|
217
|
+
|
|
218
|
+
const outFile = path.join(process.cwd(), `grimoire-memory-export-${Date.now()}.md`);
|
|
219
|
+
await fs.writeFile(outFile, output, 'utf8');
|
|
220
|
+
console.log(`✅ Memory exported to: ${outFile}`);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Clear sessions older than N days
|
|
225
|
+
*/
|
|
226
|
+
async function clearMemory(args, memoryDir) {
|
|
227
|
+
const olderThan = args.find(a => a.startsWith('--older-than'));
|
|
228
|
+
const days = olderThan ? parseInt(olderThan.replace('--older-than=', '').replace('--older-than', '') || args[args.indexOf(olderThan) + 1] || '30') : 30;
|
|
229
|
+
|
|
230
|
+
const sessionsDir = path.join(memoryDir, 'sessions');
|
|
231
|
+
const files = (await fs.readdir(sessionsDir)).filter(f => f.match(/^\d{4}-\d{2}-\d{2}/));
|
|
232
|
+
const cutoff = new Date();
|
|
233
|
+
cutoff.setDate(cutoff.getDate() - days);
|
|
234
|
+
|
|
235
|
+
let removed = 0;
|
|
236
|
+
for (const file of files) {
|
|
237
|
+
const datePart = file.replace(/\.(jsonl|json)$/, '');
|
|
238
|
+
if (new Date(datePart) < cutoff) {
|
|
239
|
+
await fs.unlink(path.join(sessionsDir, file));
|
|
240
|
+
removed++;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
console.log(`✅ Cleared ${removed} session${removed === 1 ? '' : 's'} older than ${days} days.`);
|
|
148
244
|
}
|
|
149
245
|
|
|
150
246
|
/**
|
|
@@ -175,7 +271,7 @@ async function digestMemory(args, memoryDir) {
|
|
|
175
271
|
categories.misc = entries.filter(e => !categorizedIds.has(e));
|
|
176
272
|
|
|
177
273
|
console.log(`📊 Analysis complete: ${entries.length} entries processed.`);
|
|
178
|
-
|
|
274
|
+
|
|
179
275
|
if (categories.decisions.length > 0) {
|
|
180
276
|
console.log('\n🏛️ PROPOSED DECISIONS (Update entities/decisions.md):');
|
|
181
277
|
categories.decisions.forEach(e => console.log(` - ${e.content}`));
|
|
@@ -194,21 +290,31 @@ async function digestMemory(args, memoryDir) {
|
|
|
194
290
|
console.log('\n--- SESSION ARCHIVING ---');
|
|
195
291
|
const archiveDir = path.join(memoryDir, 'sessions', 'archived');
|
|
196
292
|
if (!existsSync(archiveDir)) await fs.mkdir(archiveDir);
|
|
197
|
-
|
|
293
|
+
|
|
198
294
|
await fs.rename(sessionFile, path.join(archiveDir, `${date}.jsonl`));
|
|
199
295
|
console.log(`✅ Session ${date} digested and archived.`);
|
|
200
296
|
}
|
|
201
297
|
|
|
202
298
|
function showHelp() {
|
|
203
299
|
console.log(`
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
grimoire memory save "<
|
|
208
|
-
grimoire memory list
|
|
209
|
-
grimoire memory show
|
|
210
|
-
grimoire memory
|
|
211
|
-
grimoire memory
|
|
300
|
+
💾 Grimoire Memory — Gestão de Contexto
|
|
301
|
+
|
|
302
|
+
USO:
|
|
303
|
+
grimoire memory save "<texto>" Salva uma entrada na sessão de hoje
|
|
304
|
+
grimoire memory list Lista todas as sessões
|
|
305
|
+
grimoire memory show [data] Mostra entradas de uma data (padrão: hoje)
|
|
306
|
+
grimoire memory show --last 5 Mostra as últimas 5 entradas de hoje
|
|
307
|
+
grimoire memory search "<termo>" Busca em todas as sessões
|
|
308
|
+
grimoire memory export Exporta tudo para Markdown
|
|
309
|
+
grimoire memory digest [data] Categoriza e arquiva uma sessão
|
|
310
|
+
grimoire memory clear --older-than 30d Remove sessões antigas
|
|
311
|
+
|
|
312
|
+
EXEMPLOS:
|
|
313
|
+
grimoire memory save "Decidimos usar PostgreSQL por performance"
|
|
314
|
+
grimoire memory show --last 10
|
|
315
|
+
grimoire memory search "PostgreSQL"
|
|
316
|
+
grimoire memory export --format markdown
|
|
317
|
+
grimoire memory clear --older-than 30
|
|
212
318
|
`);
|
|
213
319
|
}
|
|
214
320
|
|
package/bin/commands/update.js
CHANGED
|
@@ -24,6 +24,7 @@ const FRAMEWORK_SYNC_DIRS = [
|
|
|
24
24
|
{ src: '.gemini/rules/grimoire', dest: '.gemini/rules/grimoire' },
|
|
25
25
|
{ src: '.cursor/rules/agents', dest: '.cursor/rules/agents' },
|
|
26
26
|
{ src: '.claude/commands/grimoire', dest: '.claude/commands/grimoire' },
|
|
27
|
+
{ src: 'squads', dest: '.grimoire/squads' },
|
|
27
28
|
];
|
|
28
29
|
|
|
29
30
|
// Single files to sync (framework root → project root)
|
package/bin/grimoire-cli.js
CHANGED
|
@@ -12,8 +12,93 @@ const { execSync } = require('child_process');
|
|
|
12
12
|
const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8'));
|
|
13
13
|
const args = process.argv.slice(2);
|
|
14
14
|
const command = args[0];
|
|
15
|
+
const currentVersion = packageJson.version;
|
|
16
|
+
|
|
17
|
+
// ── Update Banner (async, non-blocking, 24h cooldown) ─────────────────────────
|
|
18
|
+
const UPDATE_CACHE_FILE = path.join(
|
|
19
|
+
process.env.HOME || process.env.USERPROFILE || require('os').homedir(),
|
|
20
|
+
'.grimoire-update-cache.json'
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
function shouldCheckForUpdates() {
|
|
24
|
+
if (process.env.GRIMOIRE_NO_UPDATE_CHECK) return false;
|
|
25
|
+
if (['update', '--version', '-v', 'doctor'].includes(command)) return false;
|
|
26
|
+
try {
|
|
27
|
+
if (fs.existsSync(UPDATE_CACHE_FILE)) {
|
|
28
|
+
const cache = JSON.parse(fs.readFileSync(UPDATE_CACHE_FILE, 'utf8'));
|
|
29
|
+
const hoursSince = (Date.now() - cache.lastCheck) / (1000 * 60 * 60);
|
|
30
|
+
if (hoursSince < 24) return false;
|
|
31
|
+
}
|
|
32
|
+
} catch (e) { /* ignore */ }
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function checkForUpdates() {
|
|
37
|
+
if (!shouldCheckForUpdates()) return;
|
|
38
|
+
try {
|
|
39
|
+
const https = require('https');
|
|
40
|
+
const data = await new Promise((resolve, reject) => {
|
|
41
|
+
const req = https.get(
|
|
42
|
+
'https://registry.npmjs.org/grimoire-framework/latest',
|
|
43
|
+
{ timeout: 3000 },
|
|
44
|
+
(res) => {
|
|
45
|
+
let body = '';
|
|
46
|
+
res.on('data', d => body += d);
|
|
47
|
+
res.on('end', () => { try { resolve(JSON.parse(body)); } catch (e) { reject(e); } });
|
|
48
|
+
}
|
|
49
|
+
);
|
|
50
|
+
req.on('error', reject);
|
|
51
|
+
req.on('timeout', () => req.destroy());
|
|
52
|
+
});
|
|
53
|
+
const latest = data.version;
|
|
54
|
+
// Save to cache
|
|
55
|
+
fs.writeFileSync(UPDATE_CACHE_FILE, JSON.stringify({ lastCheck: Date.now(), latest }));
|
|
56
|
+
// Compare versions
|
|
57
|
+
const [maj, min, pat] = currentVersion.split('.').map(Number);
|
|
58
|
+
const [lmaj, lmin, lpat] = latest.split('.').map(Number);
|
|
59
|
+
const isNewer = lmaj > maj || (lmaj === maj && lmin > min) || (lmaj === maj && lmin === min && lpat > pat);
|
|
60
|
+
if (isNewer) {
|
|
61
|
+
console.log(`\n💡 Nova versão disponível: v${latest} (você tem v${currentVersion})`);
|
|
62
|
+
console.log(` Atualize com: npx grimoire-framework update\n`);
|
|
63
|
+
}
|
|
64
|
+
} catch (e) { /* offline ou erro — ignorar silenciosamente */ }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ── First-Run Experience ───────────────────────────────────────────────────────
|
|
68
|
+
function showNextSteps(projectDir) {
|
|
69
|
+
const cwd = projectDir || process.cwd();
|
|
70
|
+
// Detect installed IDE
|
|
71
|
+
const hasGemini = fs.existsSync(path.join(cwd, 'GEMINI.md'));
|
|
72
|
+
const hasCursor = fs.existsSync(path.join(cwd, '.cursor'));
|
|
73
|
+
const hasClaude = fs.existsSync(path.join(cwd, '.claude'));
|
|
74
|
+
|
|
75
|
+
let ideStep = ' 2. Abra o Gemini CLI: gemini';
|
|
76
|
+
if (hasClaude) ideStep = ' 2. Abra o Claude Code — agentes prontos com /dev, /qa, /architect';
|
|
77
|
+
else if (hasCursor) ideStep = ' 2. Abra o Cursor — @dev, @qa, @architect disponíveis no chat';
|
|
78
|
+
else if (hasGemini) ideStep = ' 2. Abra o Gemini CLI: gemini → Michelangelo te recebe';
|
|
79
|
+
|
|
80
|
+
const agentsDir = path.join(cwd, '.codex', 'agents');
|
|
81
|
+
const agentCount = fs.existsSync(agentsDir)
|
|
82
|
+
? fs.readdirSync(agentsDir).filter(f => f.endsWith('.md')).length : 0;
|
|
83
|
+
|
|
84
|
+
console.log(`
|
|
85
|
+
🎯 Próximos passos:
|
|
86
|
+
1. Verifique a instalação: grimoire status
|
|
87
|
+
${ideStep}
|
|
88
|
+
3. Para ver todos os agentes: grimoire agents list
|
|
89
|
+
4. Para diagnóstico: grimoire doctor
|
|
90
|
+
|
|
91
|
+
💡 Dica rápida: Diga \"@dev implementa o login\" no chat da sua IDE
|
|
92
|
+
Você tem ${agentCount} agentes prontos: @dev, @qa, @architect, @grimoire-master...
|
|
93
|
+
|
|
94
|
+
📚 Docs: https://github.com/gabrielrlima/grimoire#readme
|
|
95
|
+
`);
|
|
96
|
+
}
|
|
15
97
|
|
|
16
98
|
async function main() {
|
|
99
|
+
// Non-blocking update check (runs in background)
|
|
100
|
+
const updatePromise = checkForUpdates().catch(() => { });
|
|
101
|
+
|
|
17
102
|
switch (command) {
|
|
18
103
|
case 'memory':
|
|
19
104
|
await require('./commands/memory').run(args.slice(1));
|
|
@@ -27,6 +112,9 @@ async function main() {
|
|
|
27
112
|
case 'agents':
|
|
28
113
|
handleAgents(args.slice(1));
|
|
29
114
|
break;
|
|
115
|
+
case 'agent':
|
|
116
|
+
await require('./commands/agent').run(args.slice(1));
|
|
117
|
+
break;
|
|
30
118
|
case 'status':
|
|
31
119
|
handleStatus();
|
|
32
120
|
break;
|
|
@@ -47,7 +135,7 @@ async function main() {
|
|
|
47
135
|
break;
|
|
48
136
|
case '--version':
|
|
49
137
|
case '-v':
|
|
50
|
-
console.log(`Grimoire
|
|
138
|
+
console.log(`Grimoire v${packageJson.version}`);
|
|
51
139
|
break;
|
|
52
140
|
case '--help':
|
|
53
141
|
case '-h':
|
|
@@ -55,6 +143,9 @@ async function main() {
|
|
|
55
143
|
showHelp();
|
|
56
144
|
break;
|
|
57
145
|
}
|
|
146
|
+
|
|
147
|
+
// Wait for update check to finish (prints banner after main command if needed)
|
|
148
|
+
await updatePromise;
|
|
58
149
|
}
|
|
59
150
|
|
|
60
151
|
function handleSquads(args) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "grimoire-framework",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.20",
|
|
4
4
|
"description": "Grimoire: AI-Orchestrated System for Full Stack Development - Core Framework",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
".claude/hooks/",
|
|
37
37
|
"GEMINI.md",
|
|
38
38
|
"pro/license/",
|
|
39
|
+
"squads/",
|
|
39
40
|
"README.md",
|
|
40
41
|
"LICENSE"
|
|
41
42
|
],
|
|
@@ -19,73 +19,73 @@ const colors = {
|
|
|
19
19
|
// ============================================
|
|
20
20
|
// CORE BRAND COLORS
|
|
21
21
|
// ============================================
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
/**
|
|
24
24
|
* Primary brand color - ClickUp-inspired purple
|
|
25
25
|
* Usage: Main questions, headers, CTAs, primary actions
|
|
26
26
|
*/
|
|
27
27
|
primary: chalk.hex('#8B5CF6'),
|
|
28
|
-
|
|
28
|
+
|
|
29
29
|
/**
|
|
30
30
|
* Secondary brand color - Magenta accent from logo gradient
|
|
31
31
|
* Usage: Important highlights, special emphasis, key information
|
|
32
32
|
*/
|
|
33
33
|
secondary: chalk.hex('#EC4899'),
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
/**
|
|
36
36
|
* Tertiary brand color - Blue accent from logo gradient
|
|
37
37
|
* Usage: Secondary actions, links, complementary elements
|
|
38
38
|
*/
|
|
39
39
|
tertiary: chalk.hex('#3B82F6'),
|
|
40
|
-
|
|
40
|
+
|
|
41
41
|
// ============================================
|
|
42
42
|
// FUNCTIONAL COLORS
|
|
43
43
|
// ============================================
|
|
44
|
-
|
|
44
|
+
|
|
45
45
|
/**
|
|
46
46
|
* Success state color
|
|
47
47
|
* Usage: Checkmarks, completed steps, success messages
|
|
48
48
|
*/
|
|
49
49
|
success: chalk.hex('#10B981'),
|
|
50
|
-
|
|
50
|
+
|
|
51
51
|
/**
|
|
52
52
|
* Warning state color - Orange from logo gradient
|
|
53
53
|
* Usage: Warnings, confirmations, caution states
|
|
54
54
|
*/
|
|
55
55
|
warning: chalk.hex('#F59E0B'),
|
|
56
|
-
|
|
56
|
+
|
|
57
57
|
/**
|
|
58
58
|
* Error state color
|
|
59
59
|
* Usage: Errors, critical alerts, validation failures
|
|
60
60
|
*/
|
|
61
61
|
error: chalk.hex('#EF4444'),
|
|
62
|
-
|
|
62
|
+
|
|
63
63
|
/**
|
|
64
64
|
* Info state color - Cyan-blue from logo gradient
|
|
65
65
|
* Usage: Info messages, tips, helper text, additional context
|
|
66
66
|
*/
|
|
67
67
|
info: chalk.hex('#06B6D4'),
|
|
68
|
-
|
|
68
|
+
|
|
69
69
|
// ============================================
|
|
70
70
|
// NEUTRAL COLORS
|
|
71
71
|
// ============================================
|
|
72
|
-
|
|
72
|
+
|
|
73
73
|
/**
|
|
74
74
|
* Muted text color
|
|
75
75
|
* Usage: Subtle text, disabled states, secondary content
|
|
76
76
|
*/
|
|
77
77
|
muted: chalk.hex('#94A3B8'),
|
|
78
|
-
|
|
78
|
+
|
|
79
79
|
/**
|
|
80
80
|
* Dim text color
|
|
81
81
|
* Usage: Secondary text, muted content, less important info
|
|
82
82
|
*/
|
|
83
83
|
dim: chalk.hex('#64748B'),
|
|
84
|
-
|
|
84
|
+
|
|
85
85
|
// ============================================
|
|
86
86
|
// GRADIENT SYSTEM
|
|
87
87
|
// ============================================
|
|
88
|
-
|
|
88
|
+
|
|
89
89
|
/**
|
|
90
90
|
* Brand gradient colors for animations and special effects
|
|
91
91
|
* Matches grimoire logo gradient: magenta → purple → blue
|
|
@@ -93,28 +93,28 @@ const colors = {
|
|
|
93
93
|
gradient: {
|
|
94
94
|
/** Gradient start - Magenta (logo top) */
|
|
95
95
|
start: chalk.hex('#EC4899'),
|
|
96
|
-
|
|
96
|
+
|
|
97
97
|
/** Gradient middle - Purple (brand) */
|
|
98
98
|
middle: chalk.hex('#8B5CF6'),
|
|
99
|
-
|
|
99
|
+
|
|
100
100
|
/** Gradient end - Blue (logo bottom) */
|
|
101
101
|
end: chalk.hex('#3B82F6'),
|
|
102
102
|
},
|
|
103
|
-
|
|
103
|
+
|
|
104
104
|
// ============================================
|
|
105
105
|
// SEMANTIC SHORTCUTS
|
|
106
106
|
// ============================================
|
|
107
|
-
|
|
107
|
+
|
|
108
108
|
/**
|
|
109
109
|
* Highlighted text - Bold magenta for key information
|
|
110
110
|
*/
|
|
111
111
|
highlight: chalk.hex('#EC4899').bold,
|
|
112
|
-
|
|
112
|
+
|
|
113
113
|
/**
|
|
114
114
|
* Primary branding - Bold purple for grimoire brand moments
|
|
115
115
|
*/
|
|
116
116
|
brandPrimary: chalk.hex('#8B5CF6').bold,
|
|
117
|
-
|
|
117
|
+
|
|
118
118
|
/**
|
|
119
119
|
* Secondary branding - Cyan for supporting brand elements
|
|
120
120
|
*/
|
|
@@ -127,25 +127,25 @@ const colors = {
|
|
|
127
127
|
const status = {
|
|
128
128
|
/** Success indicator: ✓ (green) */
|
|
129
129
|
success: (text) => `${colors.success('✓')} ${text}`,
|
|
130
|
-
|
|
130
|
+
|
|
131
131
|
/** Error indicator: ✗ (red) */
|
|
132
132
|
error: (text) => `${colors.error('✗')} ${text}`,
|
|
133
|
-
|
|
133
|
+
|
|
134
134
|
/** Warning indicator: ⚠️ (orange) */
|
|
135
135
|
warning: (text) => `${colors.warning('⚠️')} ${text}`,
|
|
136
|
-
|
|
136
|
+
|
|
137
137
|
/** Info indicator: ℹ (cyan) */
|
|
138
138
|
info: (text) => `${colors.info('ℹ')} ${text}`,
|
|
139
|
-
|
|
139
|
+
|
|
140
140
|
/** Loading indicator: ⏳ (cyan) */
|
|
141
141
|
loading: (text) => `${colors.info('⏳')} ${text}`,
|
|
142
|
-
|
|
142
|
+
|
|
143
143
|
/** Skipped indicator: ⊘ (muted) */
|
|
144
144
|
skipped: (text) => `${colors.muted('⊘')} ${text}`,
|
|
145
|
-
|
|
145
|
+
|
|
146
146
|
/** Tip indicator: 💡 (info) */
|
|
147
147
|
tip: (text) => `${colors.info('💡')} ${text}`,
|
|
148
|
-
|
|
148
|
+
|
|
149
149
|
/** Party indicator: 🎉 (brand primary) */
|
|
150
150
|
celebrate: (text) => `${colors.brandPrimary('🎉')} ${text}`,
|
|
151
151
|
};
|
|
@@ -156,13 +156,13 @@ const status = {
|
|
|
156
156
|
const headings = {
|
|
157
157
|
/** H1 - Brand primary, bold, large spacing */
|
|
158
158
|
h1: (text) => `\n${colors.brandPrimary(text)}\n`,
|
|
159
|
-
|
|
159
|
+
|
|
160
160
|
/** H2 - Primary color, bold */
|
|
161
161
|
h2: (text) => `\n${colors.primary.bold(text)}\n`,
|
|
162
|
-
|
|
162
|
+
|
|
163
163
|
/** H3 - Primary color */
|
|
164
164
|
h3: (text) => colors.primary(text),
|
|
165
|
-
|
|
165
|
+
|
|
166
166
|
/** Section divider */
|
|
167
167
|
divider: () => colors.dim('─'.repeat(50)),
|
|
168
168
|
};
|
|
@@ -173,10 +173,10 @@ const headings = {
|
|
|
173
173
|
const lists = {
|
|
174
174
|
/** Bullet point (primary) */
|
|
175
175
|
bullet: (text) => `${colors.primary('•')} ${text}`,
|
|
176
|
-
|
|
176
|
+
|
|
177
177
|
/** Numbered item (primary) */
|
|
178
178
|
numbered: (num, text) => `${colors.primary(`${num}.`)} ${text}`,
|
|
179
|
-
|
|
179
|
+
|
|
180
180
|
/** Checkbox unchecked */
|
|
181
181
|
checkbox: (text, checked = false) => {
|
|
182
182
|
const icon = checked ? colors.success('☑') : colors.muted('☐');
|
|
@@ -192,31 +192,60 @@ const examples = {
|
|
|
192
192
|
console.log(headings.h1('🎉 Welcome to grimoire v4 Installer!'));
|
|
193
193
|
console.log(colors.info('Let\'s configure your project in just a few steps...\n'));
|
|
194
194
|
},
|
|
195
|
-
|
|
195
|
+
|
|
196
196
|
question: () => {
|
|
197
197
|
console.log(colors.primary('? Select your project type:'));
|
|
198
198
|
console.log(lists.bullet('Greenfield (new project)'));
|
|
199
199
|
console.log(lists.bullet('Brownfield (existing project)'));
|
|
200
200
|
},
|
|
201
|
-
|
|
201
|
+
|
|
202
202
|
progress: () => {
|
|
203
203
|
console.log(status.loading('Installing dependencies...'));
|
|
204
204
|
console.log(status.success('Dependencies installed'));
|
|
205
205
|
console.log(status.loading('Configuring environment...'));
|
|
206
206
|
},
|
|
207
|
-
|
|
207
|
+
|
|
208
208
|
feedback: () => {
|
|
209
209
|
console.log(status.success('Configuration complete!'));
|
|
210
210
|
console.log(status.warning('Existing .env found. Overwrite?'));
|
|
211
211
|
console.log(status.error('Invalid path provided'));
|
|
212
212
|
console.log(status.tip('You can change this later in settings'));
|
|
213
213
|
},
|
|
214
|
-
|
|
215
|
-
complete: () => {
|
|
214
|
+
|
|
215
|
+
complete: (projectDir) => {
|
|
216
216
|
console.log('\n' + headings.divider());
|
|
217
217
|
console.log(status.celebrate('Installation Complete!'));
|
|
218
218
|
console.log(colors.info('Your grimoire project is ready to use.'));
|
|
219
219
|
console.log(headings.divider() + '\n');
|
|
220
|
+
|
|
221
|
+
// First-Run Next Steps
|
|
222
|
+
const path = require('path');
|
|
223
|
+
const fs = require('fs');
|
|
224
|
+
const cwd = projectDir || process.cwd();
|
|
225
|
+
const hasGemini = fs.existsSync(path.join(cwd, 'GEMINI.md'));
|
|
226
|
+
const hasClaude = fs.existsSync(path.join(cwd, '.claude'));
|
|
227
|
+
const hasCursor = fs.existsSync(path.join(cwd, '.cursor'));
|
|
228
|
+
|
|
229
|
+
let ideStep = ' 2. Abra o Gemini CLI: gemini → Michelangelo te recebe';
|
|
230
|
+
if (hasClaude) ideStep = ' 2. Abra o Claude Code — use /dev, /qa, /architect';
|
|
231
|
+
else if (hasCursor) ideStep = ' 2. Abra o Cursor — @dev, @qa, @architect no chat';
|
|
232
|
+
else if (hasGemini) ideStep = ' 2. Abra o Gemini CLI: gemini → Michelangelo te recebe';
|
|
233
|
+
|
|
234
|
+
const agentsDir = path.join(cwd, '.codex', 'agents');
|
|
235
|
+
const agentCount = fs.existsSync(agentsDir)
|
|
236
|
+
? fs.readdirSync(agentsDir).filter(f => f.endsWith('.md')).length : 0;
|
|
237
|
+
|
|
238
|
+
console.log(`🎯 Próximos passos:
|
|
239
|
+
1. Verifique a instalação: grimoire status
|
|
240
|
+
${ideStep}
|
|
241
|
+
3. Liste os agentes: grimoire agents list
|
|
242
|
+
4. Diagnóstico completo: grimoire doctor
|
|
243
|
+
|
|
244
|
+
💡 Dica: Diga "@dev implementa o login" no chat da sua IDE.
|
|
245
|
+
Você tem ${agentCount} agentes prontos: @dev @qa @architect @grimoire-master...
|
|
246
|
+
|
|
247
|
+
📚 Docs: https://github.com/gabrielrlima/grimoire#readme
|
|
248
|
+
`);
|
|
220
249
|
},
|
|
221
250
|
};
|
|
222
251
|
|
package/squads/.gitkeep
ADDED
package/squads/README.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Grimoire Squads
|
|
2
|
+
|
|
3
|
+
Este diretório contém os squads pré-configurados para organizar o fluxo de trabalho dos agentes.
|
|
4
|
+
|
|
5
|
+
## Squads Disponíveis
|
|
6
|
+
|
|
7
|
+
### 1. Full Stack Development
|
|
8
|
+
**Agentes:** @dev, @qa, @architect
|
|
9
|
+
**Foco:** Planejamento técnico, codificação e testes.
|
|
10
|
+
|
|
11
|
+
### 2. Planning & Strategy
|
|
12
|
+
**Agentes:** @analyst, @pm, @architect, @ux-design-expert
|
|
13
|
+
**Foco:** Transformar ideias em PRDs e arquitetura.
|
|
14
|
+
|
|
15
|
+
### 3. DevOps & Management
|
|
16
|
+
**Agentes:** @devops, @sm, @po
|
|
17
|
+
**Foco:** Backlog, sprints e pipelines de deploy.
|
|
18
|
+
|
|
19
|
+
## Como Usar
|
|
20
|
+
Para carregar um squad durante a instalação ou re-setup, selecione as opções correspondentes no CLI do Grimoire.
|
|
21
|
+
|
|
22
|
+
## Como Criar Squads Customizados
|
|
23
|
+
1. Crie uma nova pasta dentro de `squads/`.
|
|
24
|
+
2. Adicione um arquivo `squad.yaml` com nome, descrição e a lista de agentes (referenciando arquivos em `.codex/agents/`).
|
|
25
|
+
3. Adicione fluxos de trabalho em uma pasta `workflows/` opcional.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Workflow: Pipeline de Deploy (Deploy Pipeline)
|
|
2
|
+
|
|
3
|
+
**Objetivo:** Garantir a entrega segura e automatizada do software.
|
|
4
|
+
|
|
5
|
+
## Etapas do Fluxo
|
|
6
|
+
|
|
7
|
+
### 1. Quality Gate
|
|
8
|
+
- **Agente:** @devops
|
|
9
|
+
- **Ação:** Rodar lint, testes e build no CI antes do push.
|
|
10
|
+
|
|
11
|
+
### 2. Versionamento
|
|
12
|
+
- **Agente:** @devops
|
|
13
|
+
- **Ação:** Realizar o bump de versão e criar tags de release.
|
|
14
|
+
|
|
15
|
+
### 3. Promoção de Ambiente
|
|
16
|
+
- **Agente:** @devops
|
|
17
|
+
- **Ação:** Orquestrar o deploy para staging ou produção.
|
|
18
|
+
|
|
19
|
+
## Ferramentas
|
|
20
|
+
- GitHub Actions
|
|
21
|
+
- Semantic Release
|
|
22
|
+
- Quality Gate Hooks
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
name: "Full Stack Development Squad"
|
|
2
|
+
description: "Squad completo para planejamento, implementação e teste de features de software."
|
|
3
|
+
agents:
|
|
4
|
+
- dev
|
|
5
|
+
- qa
|
|
6
|
+
- architect
|
|
7
|
+
workflows:
|
|
8
|
+
- id: new-feature
|
|
9
|
+
file: workflows/new-feature.md
|
|
10
|
+
- id: code-review
|
|
11
|
+
file: workflows/code-review.md
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Workflow: Revisão de Código (Code Review)
|
|
2
|
+
|
|
3
|
+
**Objetivo:** Garantir a qualidade e consistência do código antes do merge.
|
|
4
|
+
|
|
5
|
+
## Etapas do Fluxo
|
|
6
|
+
|
|
7
|
+
### 1. Análise Estática
|
|
8
|
+
- **Agente:** @qa (em conjunto com CodeRabbit)
|
|
9
|
+
- **Ação:** Identificar bugs latentes, vulnerabilidades e dívida técnica.
|
|
10
|
+
|
|
11
|
+
### 2. Revisão de Padrões
|
|
12
|
+
- **Agente:** @architect
|
|
13
|
+
- **Ação:** Validar se o código segue os padrões arquiteturais do projeto.
|
|
14
|
+
|
|
15
|
+
### 3. Validação de Lógica
|
|
16
|
+
- **Agente:** @dev (Peer Review)
|
|
17
|
+
- **Ação:** Revisar a lógica de negócio implementada.
|
|
18
|
+
|
|
19
|
+
## Critérios de Aprovação
|
|
20
|
+
- Zero problemas críticos de segurança.
|
|
21
|
+
- Lint e testes passando 100%.
|
|
22
|
+
- Conformidade com ADRs existentes.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Workflow: Nova Funcionalidade (New Feature)
|
|
2
|
+
|
|
3
|
+
**Objetivo:** Levar uma ideia de funcionalidade até a produção através de um fluxo estruturado.
|
|
4
|
+
|
|
5
|
+
## Etapas do Fluxo
|
|
6
|
+
|
|
7
|
+
### 1. Planejamento Arquitetural
|
|
8
|
+
- **Agente:** @architect
|
|
9
|
+
- **Ação:** Criar ou atualizar o design do sistema para a nova feature.
|
|
10
|
+
- **Saída:** Documento de arquitetura técnica.
|
|
11
|
+
|
|
12
|
+
### 2. Implementação
|
|
13
|
+
- **Agente:** @dev
|
|
14
|
+
- **Ação:** Codificar a funcionalidade seguindo o design aprovado.
|
|
15
|
+
- **Saída:** Código-fonte implementado e commitado localmente.
|
|
16
|
+
|
|
17
|
+
### 3. Garantia de Qualidade (QA)
|
|
18
|
+
- **Agente:** @qa
|
|
19
|
+
- **Ação:** Validar a implementação contra os critérios de aceitação.
|
|
20
|
+
- **Saída:** Aprovação de QA e sugestões de melhoria.
|
|
21
|
+
|
|
22
|
+
## Gatilhos (Triggers)
|
|
23
|
+
- Solicitação de nova feature pelo @pm
|
|
24
|
+
- Backlog de sprints atualizado pelo @sm
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
name: "Planning & Strategy Squad"
|
|
2
|
+
description: "Squad focado em requisitos, design de sistema e experiência do usuário (UX)."
|
|
3
|
+
agents:
|
|
4
|
+
- analyst
|
|
5
|
+
- pm
|
|
6
|
+
- architect
|
|
7
|
+
- ux-design-expert
|
|
8
|
+
workflows:
|
|
9
|
+
- id: prd-to-stories
|
|
10
|
+
file: workflows/prd-to-stories.md
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Workflow: PRD para Histórias (PRD to Stories)
|
|
2
|
+
|
|
3
|
+
**Objetivo:** Fragmentar requisitos de alto nível em unidades de desenvolvimento acionáveis.
|
|
4
|
+
|
|
5
|
+
## Etapas do Fluxo
|
|
6
|
+
|
|
7
|
+
### 1. Refinamento de Requisitos
|
|
8
|
+
- **Agente:** @analyst + @pm
|
|
9
|
+
- **Ação:** Validar o briefing e transformar em um PRD completo.
|
|
10
|
+
|
|
11
|
+
### 2. Design Visual (UX)
|
|
12
|
+
- **Agente:** @ux-design-expert
|
|
13
|
+
- **Ação:** Criar fluxos de usuário e wireframes para os requisitos.
|
|
14
|
+
|
|
15
|
+
### 3. Fragmentação (Sharding)
|
|
16
|
+
- **Agente:** @sm
|
|
17
|
+
- **Ação:** Criar as histórias (stories) em `docs/stories/` com base no PRD.
|
|
18
|
+
|
|
19
|
+
## Saída Esperada
|
|
20
|
+
- Backlog de stories priorizado e pronto para implementação.
|