grimoire-framework 1.0.9 → 1.0.11
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.
|
|
11
|
-
generated_at: "2026-02-22T12:
|
|
10
|
+
version: 1.0.11
|
|
11
|
+
generated_at: "2026-02-22T12:28:20.727Z"
|
|
12
12
|
generator: scripts/generate-install-manifest.js
|
|
13
13
|
file_count: 1011
|
|
14
14
|
files:
|
|
@@ -3553,9 +3553,9 @@ files:
|
|
|
3553
3553
|
type: template
|
|
3554
3554
|
size: 3073
|
|
3555
3555
|
- path: product/templates/ide-rules/gemini-rules.md
|
|
3556
|
-
hash: sha256:
|
|
3556
|
+
hash: sha256:325b227bb6b1bb2006e5b1b62f724b0aba51b60e03cdb113e27ff485f7f35aae
|
|
3557
3557
|
type: template
|
|
3558
|
-
size:
|
|
3558
|
+
size: 4546
|
|
3559
3559
|
- path: product/templates/index-strategy-tmpl.yaml
|
|
3560
3560
|
hash: sha256:6db2b40f6eef47f4faa31ce513ee7b0d5f04d9a5e081a72e0cdbad402eb444ae
|
|
3561
3561
|
type: template
|
|
@@ -1,16 +1,97 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
# Grimoire Framework — {{projectName}}
|
|
2
|
+
|
|
3
|
+
<!-- grimoire-MANAGED-START: default-agent -->
|
|
4
|
+
## 👑 Agente Padrão: Michelangelo (grimoire-master)
|
|
5
|
+
|
|
6
|
+
**INSTRUÇÃO OBRIGATÓRIA AO INICIAR:** Você é o **Michelangelo**, o Mestre Orquestrador do Grimoire Framework. Adote esta persona imediatamente.
|
|
7
|
+
|
|
8
|
+
Ao iniciar a sessão, apresente-se assim:
|
|
9
|
+
|
|
10
|
+
> 👑 **Michelangelo** (grimoire-master) pronto para orquestrar!
|
|
11
|
+
> Projeto: **{{projectName}}**
|
|
12
|
+
>
|
|
13
|
+
> Posso ajudá-lo com qualquer tarefa. Para ativar um agente especializado, diga o nome ou use:
|
|
14
|
+
> - `@dev` → Da Vinci (Desenvolvedor Full-Stack)
|
|
15
|
+
> - `@qa` → Dürer (Quality Assurance)
|
|
16
|
+
> - `@architect` → Gaudí (Arquitetura de Sistemas)
|
|
17
|
+
> - `@pm` → Raphael (Product Manager)
|
|
18
|
+
> - `@sm` → Monet (Scrum Master)
|
|
19
|
+
> - `@ux-design-expert` → Matisse (UX/Design)
|
|
20
|
+
> - `@devops` → Boccioni (DevOps)
|
|
21
|
+
> - `@data-engineer` → Escher (Engenharia de Dados)
|
|
22
|
+
> - `@analyst` → Vermeer (Análise & Pesquisa)
|
|
23
|
+
> - `@po` → Velázquez (Product Owner)
|
|
24
|
+
>
|
|
25
|
+
> Digite `*help` para ver todos os comandos. — Michelangelo 🎯
|
|
26
|
+
|
|
27
|
+
### Definição do agente
|
|
28
|
+
|
|
29
|
+
```yaml
|
|
30
|
+
activation-instructions:
|
|
31
|
+
- STEP 1: Adote a persona Michelangelo imediatamente
|
|
32
|
+
- STEP 2: Apresente o greeting acima ao usuário
|
|
33
|
+
- STEP 3: Aguarde input do usuário — NÃO faça varredura do filesystem
|
|
34
|
+
- STAY IN CHARACTER como Michelangelo até o usuário digitar *exit
|
|
35
|
+
- CRITICAL: NÃO carregue arquivos externos durante ativação
|
|
36
|
+
|
|
37
|
+
agent:
|
|
38
|
+
name: Michelangelo
|
|
39
|
+
id: grimoire-master
|
|
40
|
+
title: Grimoire Master Orchestrator & Framework Developer
|
|
41
|
+
icon: 👑
|
|
42
|
+
whenToUse: Use para orquestração, criação de componentes do framework, e quando precisar de expertise cross-domínio.
|
|
43
|
+
|
|
44
|
+
commands:
|
|
45
|
+
- "*help — Lista todos os comandos"
|
|
46
|
+
- "*status — Contexto atual e progresso"
|
|
47
|
+
- "*task {x} — Execute tarefa específica"
|
|
48
|
+
- "*workflow {x} — Inicie workflow"
|
|
49
|
+
- "*exit — Sair do modo agente"
|
|
50
|
+
|
|
51
|
+
signature_closing: '— Michelangelo, orquestrando o sistema 🎯'
|
|
52
|
+
```
|
|
53
|
+
<!-- grimoire-MANAGED-END: default-agent -->
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
<!-- grimoire-MANAGED-START: agent-roster -->
|
|
58
|
+
## 🧙 Roster de Agentes — Grimoire
|
|
59
|
+
|
|
60
|
+
| Agente | Persona | Especialidade |
|
|
61
|
+
|--------|---------|---------------|
|
|
62
|
+
| `@grimoire-master` | 👑 Michelangelo | Orquestração & framework |
|
|
63
|
+
| `@dev` | 🎨 Da Vinci | Desenvolvimento full-stack |
|
|
64
|
+
| `@qa` | 🖌️ Dürer | Qualidade & testes |
|
|
65
|
+
| `@architect` | 🏛️ Gaudí | Arquitetura de sistemas |
|
|
66
|
+
| `@pm` | 📋 Raphael | Produto & estratégia |
|
|
67
|
+
| `@sm` | 🌊 Monet | Scrum Master |
|
|
68
|
+
| `@devops` | ⚡ Boccioni | DevOps & infraestrutura |
|
|
69
|
+
| `@data-engineer` | 📊 Escher | Dados & banco |
|
|
70
|
+
| `@analyst` | 🔍 Vermeer | Pesquisa & análise |
|
|
71
|
+
| `@ux-design-expert` | 🎭 Matisse | UX/UI Design |
|
|
72
|
+
| `@po` | 🎯 Velázquez | Product Owner |
|
|
73
|
+
| `@squad-creator` | 🗿 Rodin | Criação de squads |
|
|
74
|
+
|
|
75
|
+
### Como ativar um agente especializado
|
|
76
|
+
|
|
77
|
+
Diga no chat: `ative o @dev` ou `quero falar com Da Vinci` ou simplesmente `@architect`.
|
|
78
|
+
|
|
79
|
+
O agente carregará sua definição completa de `.gemini/rules/grimoire/agents/{agente}.md`.
|
|
80
|
+
<!-- grimoire-MANAGED-END: agent-roster -->
|
|
81
|
+
|
|
82
|
+
---
|
|
4
83
|
|
|
5
84
|
<!-- grimoire-MANAGED-START: core -->
|
|
6
85
|
## Core Rules
|
|
7
86
|
|
|
8
87
|
1. Siga a Constitution em `.grimoire/constitution.md`
|
|
9
|
-
2. Priorize `CLI First
|
|
88
|
+
2. Priorize `CLI First → Observability Second → UI Third`
|
|
10
89
|
3. Trabalhe por stories em `docs/stories/`
|
|
11
90
|
4. Nao invente requisitos fora dos artefatos existentes
|
|
12
91
|
<!-- grimoire-MANAGED-END: core -->
|
|
13
92
|
|
|
93
|
+
---
|
|
94
|
+
|
|
14
95
|
<!-- grimoire-MANAGED-START: quality -->
|
|
15
96
|
## Quality Gates
|
|
16
97
|
|
|
@@ -20,70 +101,28 @@ Este arquivo define as instrucoes do projeto para Gemini CLI neste repositorio.
|
|
|
20
101
|
- Atualize checklist e file list da story antes de concluir
|
|
21
102
|
<!-- grimoire-MANAGED-END: quality -->
|
|
22
103
|
|
|
23
|
-
|
|
24
|
-
## Project Map
|
|
25
|
-
|
|
26
|
-
- Core framework: `.grimoire/`
|
|
27
|
-
- CLI entrypoints: `bin/`
|
|
28
|
-
- Shared packages: `packages/`
|
|
29
|
-
- Tests: `tests/`
|
|
30
|
-
- Docs: `docs/`
|
|
31
|
-
<!-- grimoire-MANAGED-END: codebase -->
|
|
32
|
-
|
|
33
|
-
<!-- grimoire-MANAGED-START: gemini-integration -->
|
|
34
|
-
## Gemini Integration
|
|
35
|
-
|
|
36
|
-
Fonte de verdade de agentes:
|
|
37
|
-
- Canonico: `.grimoire/development/agents/*.md`
|
|
38
|
-
- Espelhado para Gemini: `.gemini/rules/grimoire/agents/*.md`
|
|
39
|
-
|
|
40
|
-
Hooks e settings:
|
|
41
|
-
- Hooks locais: `.gemini/hooks/`
|
|
42
|
-
- Settings locais: `.gemini/settings.json`
|
|
43
|
-
|
|
44
|
-
Sempre que houver drift, execute:
|
|
45
|
-
- `npm run sync:ide:gemini`
|
|
46
|
-
- `npm run validate:gemini-sync`
|
|
47
|
-
- `npm run validate:gemini-integration`
|
|
48
|
-
<!-- grimoire-MANAGED-END: gemini-integration -->
|
|
49
|
-
|
|
50
|
-
<!-- grimoire-MANAGED-START: parity -->
|
|
51
|
-
## Multi-IDE Parity
|
|
52
|
-
|
|
53
|
-
Para garantir paridade entre Claude Code, Codex e Gemini:
|
|
54
|
-
- `npm run validate:parity`
|
|
55
|
-
- `npm run validate:paths`
|
|
56
|
-
<!-- grimoire-MANAGED-END: parity -->
|
|
104
|
+
---
|
|
57
105
|
|
|
58
106
|
<!-- grimoire-MANAGED-START: activation -->
|
|
59
|
-
##
|
|
107
|
+
## Ativação de Agente Especializado
|
|
60
108
|
|
|
61
|
-
|
|
62
|
-
1.
|
|
63
|
-
2.
|
|
109
|
+
Quando o usuário solicitar um agente especializado (ex: `@dev`, `@qa`, etc.):
|
|
110
|
+
1. Carregue `.gemini/rules/grimoire/agents/{agente-id}.md`
|
|
111
|
+
2. Adote completamente a persona definida no arquivo
|
|
112
|
+
3. Apresente o greeting definido em `greeting_levels`
|
|
113
|
+
4. Mantenha a persona até `*exit`
|
|
64
114
|
|
|
65
|
-
|
|
66
|
-
-
|
|
67
|
-
-
|
|
68
|
-
- manter persona ativa ate `*exit`
|
|
69
|
-
|
|
70
|
-
Atalhos recomendados no Gemini:
|
|
71
|
-
- `/grimoire-menu` para listar agentes
|
|
72
|
-
- `/grimoire-<agent-id>` (ex.: `/grimoire-dev`, `/grimoire-architect`)
|
|
73
|
-
- `/grimoire-agent <agent-id>` para launcher generico
|
|
115
|
+
Shortcuts:
|
|
116
|
+
- `/grimoire-menu` — lista todos os agentes
|
|
117
|
+
- `/grimoire-{id}` ex: `/grimoire-dev`, `/grimoire-architect`
|
|
74
118
|
<!-- grimoire-MANAGED-END: activation -->
|
|
75
119
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
- `
|
|
82
|
-
- `
|
|
83
|
-
-
|
|
84
|
-
-
|
|
85
|
-
- `npm run validate:structure`
|
|
86
|
-
- `npm run validate:agents`
|
|
87
|
-
<!-- grimoire-MANAGED-END: commands -->
|
|
88
|
-
|
|
89
|
-
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
<!-- grimoire-MANAGED-START: framework -->
|
|
123
|
+
## Framework vs CLI
|
|
124
|
+
|
|
125
|
+
- **CLI (terminal):** `grimoire status` · `grimoire whoami` · `grimoire agents list`
|
|
126
|
+
- **Agentes (chat):** `@dev` · `@qa` · `@architect` — ativados no chat do IDE
|
|
127
|
+
- Estes são interfaces DIFERENTES — CLI gerencia o framework, agentes respondem no chat.
|
|
128
|
+
<!-- grimoire-MANAGED-END: framework -->
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* grimoire Smart Update Command
|
|
5
|
+
* Updates grimoire-framework preserving project customizations.
|
|
6
|
+
*
|
|
7
|
+
* Strategy:
|
|
8
|
+
* 1. Check installed vs latest version on npm
|
|
9
|
+
* 2. npm install grimoire-framework@latest
|
|
10
|
+
* 3. Selectively copy framework files (agents, rules, hooks)
|
|
11
|
+
* - OVERWRITE files that are part of the framework source
|
|
12
|
+
* - PRESERVE files that only exist in the project (custom agents, etc.)
|
|
13
|
+
* 4. Report what was updated/preserved
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
const https = require('https');
|
|
19
|
+
const { execSync } = require('child_process');
|
|
20
|
+
|
|
21
|
+
// Framework source directories (relative to node_modules/grimoire-framework)
|
|
22
|
+
const FRAMEWORK_SYNC_DIRS = [
|
|
23
|
+
{ src: '.codex/agents', dest: '.codex/agents' },
|
|
24
|
+
{ src: '.gemini/rules/grimoire', dest: '.gemini/rules/grimoire' },
|
|
25
|
+
{ src: '.cursor/rules/agents', dest: '.cursor/rules/agents' },
|
|
26
|
+
{ src: '.claude/commands/grimoire', dest: '.claude/commands/grimoire' },
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Fetch latest version from npm registry
|
|
31
|
+
*/
|
|
32
|
+
function getLatestVersion() {
|
|
33
|
+
return new Promise((resolve) => {
|
|
34
|
+
const req = https.get(
|
|
35
|
+
'https://registry.npmjs.org/grimoire-framework/latest',
|
|
36
|
+
{ timeout: 10000 },
|
|
37
|
+
(res) => {
|
|
38
|
+
let data = '';
|
|
39
|
+
res.on('data', (chunk) => { data += chunk; });
|
|
40
|
+
res.on('end', () => {
|
|
41
|
+
try { resolve(JSON.parse(data).version || null); }
|
|
42
|
+
catch { resolve(null); }
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
req.on('error', () => resolve(null));
|
|
47
|
+
req.on('timeout', () => { req.destroy(); resolve(null); });
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get installed version from node_modules
|
|
53
|
+
*/
|
|
54
|
+
function getInstalledVersion(cwd) {
|
|
55
|
+
const pkgPath = path.join(cwd, 'node_modules', 'grimoire-framework', 'package.json');
|
|
56
|
+
if (fs.existsSync(pkgPath)) {
|
|
57
|
+
try { return JSON.parse(fs.readFileSync(pkgPath, 'utf8')).version; }
|
|
58
|
+
catch { /* ignore */ }
|
|
59
|
+
}
|
|
60
|
+
// Fallback: check project package.json dependencies
|
|
61
|
+
const projPkg = path.join(cwd, 'package.json');
|
|
62
|
+
if (fs.existsSync(projPkg)) {
|
|
63
|
+
try {
|
|
64
|
+
const pkg = JSON.parse(fs.readFileSync(projPkg, 'utf8'));
|
|
65
|
+
return (pkg.dependencies || {})['grimoire-framework'] ||
|
|
66
|
+
(pkg.devDependencies || {})['grimoire-framework'] || null;
|
|
67
|
+
} catch { /* ignore */ }
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Compare semver strings. Returns: -1 (v1<v2), 0 (equal), 1 (v1>v2)
|
|
74
|
+
*/
|
|
75
|
+
function compareVersions(v1, v2) {
|
|
76
|
+
const parse = (v) => v.replace(/[^0-9.]/g, '').split('.').map(Number);
|
|
77
|
+
const [a, b] = [parse(v1), parse(v2)];
|
|
78
|
+
for (let i = 0; i < 3; i++) {
|
|
79
|
+
if ((a[i] || 0) > (b[i] || 0)) return 1;
|
|
80
|
+
if ((a[i] || 0) < (b[i] || 0)) return -1;
|
|
81
|
+
}
|
|
82
|
+
return 0;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Recursively get all .md files in a directory
|
|
87
|
+
*/
|
|
88
|
+
function listMdFiles(dir) {
|
|
89
|
+
if (!fs.existsSync(dir)) return [];
|
|
90
|
+
const results = [];
|
|
91
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
92
|
+
const full = path.join(dir, entry.name);
|
|
93
|
+
if (entry.isDirectory()) results.push(...listMdFiles(full));
|
|
94
|
+
else if (entry.name.endsWith('.md')) results.push(full);
|
|
95
|
+
}
|
|
96
|
+
return results;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Sync a directory from framework source to project destination.
|
|
101
|
+
* - Overwrites files that exist in source (framework files)
|
|
102
|
+
* - Preserves files that only exist in destination (custom files)
|
|
103
|
+
* Returns stats: { updated, preserved, added }
|
|
104
|
+
*/
|
|
105
|
+
function syncDir(srcDir, destDir) {
|
|
106
|
+
const stats = { updated: [], preserved: [], added: [] };
|
|
107
|
+
|
|
108
|
+
if (!fs.existsSync(srcDir)) return stats;
|
|
109
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
110
|
+
|
|
111
|
+
// Get files in both dirs
|
|
112
|
+
const srcFiles = new Set(
|
|
113
|
+
fs.readdirSync(srcDir).filter(f => f.endsWith('.md') || f.endsWith('.json') || f.endsWith('.yaml'))
|
|
114
|
+
);
|
|
115
|
+
const destFiles = new Set(
|
|
116
|
+
fs.existsSync(destDir)
|
|
117
|
+
? fs.readdirSync(destDir).filter(f => f.endsWith('.md') || f.endsWith('.json') || f.endsWith('.yaml'))
|
|
118
|
+
: []
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
// Copy/overwrite source files to dest
|
|
122
|
+
for (const file of srcFiles) {
|
|
123
|
+
const srcFile = path.join(srcDir, file);
|
|
124
|
+
const destFile = path.join(destDir, file);
|
|
125
|
+
const existed = destFiles.has(file);
|
|
126
|
+
fs.copyFileSync(srcFile, destFile);
|
|
127
|
+
if (existed) stats.updated.push(file);
|
|
128
|
+
else stats.added.push(file);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Files in dest but NOT in source = custom files → preserve (do nothing)
|
|
132
|
+
for (const file of destFiles) {
|
|
133
|
+
if (!srcFiles.has(file)) {
|
|
134
|
+
stats.preserved.push(file);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return stats;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Main run function called by the CLI
|
|
143
|
+
*/
|
|
144
|
+
async function run(args = []) {
|
|
145
|
+
const cwd = process.cwd();
|
|
146
|
+
const dryRun = args.includes('--dry-run') || args.includes('--check');
|
|
147
|
+
const force = args.includes('--force');
|
|
148
|
+
const verbose = args.includes('--verbose') || args.includes('-v');
|
|
149
|
+
|
|
150
|
+
console.log('\n🔮 Grimoire Smart Update\n' + '='.repeat(40));
|
|
151
|
+
|
|
152
|
+
// 1. Get versions
|
|
153
|
+
const installed = getInstalledVersion(cwd);
|
|
154
|
+
process.stdout.write('📡 Checking latest version on npm...');
|
|
155
|
+
const latest = await getLatestVersion();
|
|
156
|
+
console.log(latest ? ` v${latest}` : ' (offline)');
|
|
157
|
+
|
|
158
|
+
console.log(`📦 Installed: ${installed ? `v${installed}` : '❌ not found in node_modules'}`);
|
|
159
|
+
console.log(`📦 Latest: ${latest ? `v${latest}` : '❌ could not fetch'}\n`);
|
|
160
|
+
|
|
161
|
+
if (!installed) {
|
|
162
|
+
console.log('⚠️ grimoire-framework not found in this project.\n Run: npx grimoire-framework install\n');
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (!latest) {
|
|
167
|
+
console.log('⚠️ Could not reach npm registry. Check your internet connection.\n');
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const cmp = compareVersions(installed.replace(/[\^~><=]/g, ''), latest);
|
|
172
|
+
|
|
173
|
+
if (cmp >= 0 && !force) {
|
|
174
|
+
console.log('✅ Already up to date! No update needed.\n');
|
|
175
|
+
console.log(' Use --force to reinstall anyway.\n');
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (dryRun) {
|
|
180
|
+
console.log(`🔍 DRY RUN — would update v${installed} → v${latest}\n`);
|
|
181
|
+
console.log(' Directories that would be synced:');
|
|
182
|
+
for (const { dest } of FRAMEWORK_SYNC_DIRS) {
|
|
183
|
+
console.log(` • ${dest}`);
|
|
184
|
+
}
|
|
185
|
+
console.log('\n Custom files (only in project) would be PRESERVED.');
|
|
186
|
+
console.log(' Run without --dry-run to apply.\n');
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// 2. Update npm package
|
|
191
|
+
console.log(`⬇️ Updating grimoire-framework v${installed} → v${latest}...`);
|
|
192
|
+
try {
|
|
193
|
+
execSync('npm install grimoire-framework@latest', {
|
|
194
|
+
cwd,
|
|
195
|
+
stdio: verbose ? 'inherit' : 'pipe',
|
|
196
|
+
timeout: 120000,
|
|
197
|
+
});
|
|
198
|
+
console.log('✅ npm package updated.\n');
|
|
199
|
+
} catch (err) {
|
|
200
|
+
console.error(`❌ npm install failed: ${err.message}\n`);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// 3. Sync framework files selectively
|
|
205
|
+
const frameworkRoot = path.join(cwd, 'node_modules', 'grimoire-framework');
|
|
206
|
+
|
|
207
|
+
if (!fs.existsSync(frameworkRoot)) {
|
|
208
|
+
console.log('❌ Could not find grimoire-framework in node_modules after install.\n');
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
console.log('🔄 Syncing framework files...\n');
|
|
213
|
+
|
|
214
|
+
const totalStats = { updated: 0, added: 0, preserved: 0 };
|
|
215
|
+
const report = [];
|
|
216
|
+
|
|
217
|
+
for (const { src, dest } of FRAMEWORK_SYNC_DIRS) {
|
|
218
|
+
const srcPath = path.join(frameworkRoot, src);
|
|
219
|
+
const destPath = path.join(cwd, dest);
|
|
220
|
+
|
|
221
|
+
if (!fs.existsSync(srcPath)) {
|
|
222
|
+
if (verbose) console.log(` ⏭️ Skip (not in source): ${src}`);
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const stats = syncDir(srcPath, destPath);
|
|
227
|
+
totalStats.updated += stats.updated.length;
|
|
228
|
+
totalStats.added += stats.added.length;
|
|
229
|
+
totalStats.preserved += stats.preserved.length;
|
|
230
|
+
|
|
231
|
+
report.push({ dir: dest, stats });
|
|
232
|
+
|
|
233
|
+
if (stats.updated.length || stats.added.length || stats.preserved.length) {
|
|
234
|
+
console.log(` 📁 ${dest}`);
|
|
235
|
+
if (stats.updated.length) console.log(` ✅ Updated: ${stats.updated.join(', ')}`);
|
|
236
|
+
if (stats.added.length) console.log(` ➕ Added: ${stats.added.join(', ')}`);
|
|
237
|
+
if (stats.preserved.length) console.log(` 🔒 Preserved: ${stats.preserved.join(', ')} (custom)`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// 4. Summary
|
|
242
|
+
console.log('\n' + '='.repeat(40));
|
|
243
|
+
console.log(`✅ Updated to v${latest}\n`);
|
|
244
|
+
console.log(` 📝 Files updated: ${totalStats.updated}`);
|
|
245
|
+
console.log(` ➕ Files added: ${totalStats.added}`);
|
|
246
|
+
console.log(` 🔒 Files preserved: ${totalStats.preserved} (custom — untouched)\n`);
|
|
247
|
+
console.log(' Run: grimoire status to verify the framework is healthy.');
|
|
248
|
+
console.log(' Run: grimoire doctor for full diagnostics.\n');
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
module.exports = { run };
|
package/bin/grimoire-cli.js
CHANGED
|
@@ -33,11 +33,13 @@ async function main() {
|
|
|
33
33
|
case 'whoami':
|
|
34
34
|
handleWhoami();
|
|
35
35
|
break;
|
|
36
|
+
case 'update':
|
|
37
|
+
await require('./commands/update').run(args.slice(1));
|
|
38
|
+
break;
|
|
36
39
|
case 'doctor':
|
|
37
40
|
handleDoctor();
|
|
38
41
|
break;
|
|
39
42
|
case 'install':
|
|
40
|
-
case 'update':
|
|
41
43
|
case 'validate':
|
|
42
44
|
case 'info':
|
|
43
45
|
console.log(`Delegating ${command} to core logic...`);
|