grimoire-framework 1.0.3 → 1.0.4
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/.grimoire/memory/README.md +13 -0
- package/.grimoire/memory/entities/context.md +19 -0
- package/.grimoire/memory/entities/decisions.md +17 -0
- package/.grimoire/memory/entities/patterns.md +20 -0
- package/.grimoire/memory/sessions/2026-02-22.json +9 -0
- package/.grimoire/memory/sessions/archived/2026-02-22.jsonl +3 -0
- package/bin/commands/memory.js +215 -0
- package/bin/commands/metrics.js +107 -0
- package/bin/grimoire-cli.js +103 -0
- package/bin/grimoire-ids.js +2 -2
- package/bin/grimoire-minimal.js +1 -1
- package/bin/grimoire.js +1118 -1118
- package/bin/modules/env-config.js +2 -2
- package/bin/modules/mcp-installer.js +1 -1
- package/bin/utils/install-errors.js +1 -1
- package/bin/utils/install-transaction.js +2 -2
- package/bin/utils/pro-detector.js +110 -110
- package/package.json +158 -158
- package/packages/gemini-grimoire-extension/commands/grimoire-agents.js +2 -2
- package/packages/gemini-grimoire-extension/commands/grimoire-master.js +1 -1
- package/packages/gemini-grimoire-extension/commands/grimoire-status.js +2 -2
- package/packages/gemini-grimoire-extension/commands/grimoire-validate.js +2 -2
- package/packages/gemini-grimoire-extension/commands/lib/agent-launcher.js +147 -147
- package/packages/gemini-grimoire-extension/extension.json +2 -2
- package/packages/gemini-grimoire-extension/gemini-extension.json +2 -2
- package/packages/gemini-grimoire-extension/hooks/hooks.json +2 -2
- package/packages/grimoire-install/.releaserc.json +1 -1
- package/packages/grimoire-install/CHANGELOG.md +1 -1
- package/packages/grimoire-install/README.md +2 -2
- package/packages/grimoire-install/bin/edmcp.js +1 -1
- package/packages/grimoire-install/bin/grimoire-install.js +1 -1
- package/packages/grimoire-install/jest.config.js +1 -1
- package/packages/grimoire-install/package.json +2 -2
- package/packages/grimoire-install/src/edmcp/index.js +1 -1
- package/packages/grimoire-install/src/installer.js +2 -2
- package/packages/grimoire-pro-cli/bin/grimoire-pro.js +2 -2
- package/packages/grimoire-pro-cli/package.json +2 -2
- package/packages/grimoire-pro-cli/src/recover.js +1 -1
- package/packages/installer/package.json +1 -1
- package/packages/installer/src/__tests__/performance-benchmark.js +2 -2
- package/packages/installer/src/config/configure-environment.js +2 -2
- package/packages/installer/src/config/ide-configs.js +2 -2
- package/packages/installer/src/config/templates/core-config-template.js +2 -2
- package/packages/installer/src/config/templates/env-template.js +2 -2
- package/packages/installer/src/config/validation/config-validator.js +1 -1
- package/packages/installer/src/detection/detect-project-type.js +2 -2
- package/packages/installer/src/installer/brownfield-upgrader.js +2 -2
- package/packages/installer/src/installer/grimoire-core-installer.js +2 -2
- package/packages/installer/src/installer/manifest-signature.js +2 -2
- package/packages/installer/src/installer/post-install-validator.js +2 -2
- package/packages/installer/src/installer/squad-installer.js +109 -0
- package/packages/installer/src/merger/index.js +1 -1
- package/packages/installer/src/merger/parsers/markdown-section-parser.js +1 -1
- package/packages/installer/src/merger/strategies/env-merger.js +1 -1
- package/packages/installer/src/merger/strategies/markdown-merger.js +1 -1
- package/packages/installer/src/merger/types.js +1 -1
- package/packages/installer/src/pro/pro-scaffolder.js +2 -2
- package/packages/installer/src/updater/index.js +2 -2
- package/packages/installer/src/utils/grimoire-colors.js +1 -1
- package/packages/installer/src/wizard/i18n.js +2 -2
- package/packages/installer/src/wizard/ide-config-generator.js +2 -2
- package/packages/installer/src/wizard/ide-selector.js +1 -1
- package/packages/installer/src/wizard/index.js +26 -0
- package/packages/installer/src/wizard/pro-setup.js +2 -2
- package/packages/installer/src/wizard/questions.js +20 -15
- package/packages/installer/src/wizard/validation/index.js +1 -1
- package/packages/installer/src/wizard/validation/report-generator.js +1 -1
- package/packages/installer/src/wizard/validation/troubleshooting-system.js +2 -2
- package/packages/installer/src/wizard/validation/validators/config-validator.js +2 -2
- package/packages/installer/src/wizard/validation/validators/file-structure-validator.js +1 -1
- package/packages/installer/src/wizard/wizard.js +2 -2
- package/packages/installer/tests/integration/environment-configuration.test.js +2 -2
- package/packages/installer/tests/integration/wizard-detection.test.js +2 -2
- package/packages/installer/tests/unit/config-validator.test.js +1 -1
- package/packages/installer/tests/unit/detection/detect-project-type.test.js +2 -2
- package/packages/installer/tests/unit/env-template.test.js +2 -2
- package/packages/installer/tests/unit/merger/env-merger.test.js +1 -1
- package/packages/installer/tests/unit/merger/markdown-merger.test.js +1 -1
- package/scripts/check-markdown-links.py +352 -352
- package/scripts/code-intel-health-check.js +1 -1
- package/scripts/dashboard-parallel-dev.sh +1 -1
- package/scripts/dashboard-parallel-phase3.sh +1 -1
- package/scripts/dashboard-parallel-phase4.sh +1 -1
- package/scripts/ensure-manifest.js +1 -1
- package/scripts/generate-install-manifest.js +2 -2
- package/scripts/install-monitor-hooks.sh +2 -2
- package/scripts/package-synapse.js +2 -2
- package/scripts/semantic-lint.js +1 -1
- package/scripts/sign-manifest.sh +2 -2
- package/scripts/validate-manifest.js +2 -2
- package/scripts/validate-package-completeness.js +2 -2
|
@@ -1,147 +1,147 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
const fs = require('fs');
|
|
5
|
-
const path = require('path');
|
|
6
|
-
const { spawnSync } = require('child_process');
|
|
7
|
-
|
|
8
|
-
const AGENT_INFO = {
|
|
9
|
-
'grimoire-master': { icon: '🎨', role: 'Leonardo (Master Orchestrator)' },
|
|
10
|
-
analyst: { icon: '🕯️', role: 'Vermeer (Business Analyst)' },
|
|
11
|
-
architect: { icon: '🔨', role: 'Michelangelo (System Architect)' },
|
|
12
|
-
'data-engineer': { icon: '🟦', role: 'Mondrian (Data Engineer)' },
|
|
13
|
-
dev: { icon: '🖌️', role: 'Rembrandt (Developer)' },
|
|
14
|
-
devops: { icon: '🚄', role: 'Boccioni (DevOps)' },
|
|
15
|
-
pm: { icon: '📜', role: 'Raphael (Product Manager)' },
|
|
16
|
-
po: { icon: '🔍', role: 'Van Eyck (Product Owner)' },
|
|
17
|
-
qa: { icon: '📐', role: 'Dürer (QA Engineer)' },
|
|
18
|
-
sm: { icon: '🌊', role: 'Monet (Scrum Master)' },
|
|
19
|
-
'squad-creator': { icon: '🌀', role: 'Kandinsky (Squad Creator)' },
|
|
20
|
-
'ux-design-expert': { icon: '🎭', role: 'Matisse (UX Expert)' },
|
|
21
|
-
salvador: { icon: '🕰️', role: 'Salvador (Creative Engineer)' },
|
|
22
|
-
frida: { icon: '🌺', role: 'Frida (Resilience Expert)' },
|
|
23
|
-
'van-gogh': { icon: '🌻', role: 'Van Gogh (Performance Guru)' },
|
|
24
|
-
warhol: { icon: '🥫', role: 'Warhol (Documentation Expert)' },
|
|
25
|
-
caravaggio: { icon: '🔦', role: 'Caravaggio (Security Auditor)' },
|
|
26
|
-
picasso: { icon: '💠', role: 'Picasso (Refactoring Specialist)' },
|
|
27
|
-
tarsila: { icon: '🌵', role: 'Tarsila (Localization Expert)' },
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
function listAvailableAgents(projectRoot = process.cwd()) {
|
|
31
|
-
const sourceDir = path.join(projectRoot, '.grimoire', 'development', 'agents');
|
|
32
|
-
if (!fs.existsSync(sourceDir)) return [];
|
|
33
|
-
return fs
|
|
34
|
-
.readdirSync(sourceDir)
|
|
35
|
-
.filter((f) => f.endsWith('.md') && !f.startsWith('_'))
|
|
36
|
-
.map((f) => f.replace('.md', ''))
|
|
37
|
-
.sort();
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function commandNameForAgent(agentId) {
|
|
41
|
-
if (agentId.startsWith('grimoire-')) {
|
|
42
|
-
return `/grimoire-${agentId.replace(/^grimoire-/, '')}`;
|
|
43
|
-
}
|
|
44
|
-
return `/grimoire-${agentId}`;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function hasAgent(projectRoot, agentId) {
|
|
48
|
-
const canonical = path.join(projectRoot, '.grimoire', 'development', 'agents', `${agentId}.md`);
|
|
49
|
-
const gemini = path.join(projectRoot, '.gemini', 'rules', 'grimoire', 'agents', `${agentId}.md`);
|
|
50
|
-
return fs.existsSync(canonical) || fs.existsSync(gemini);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function renderGreeting(projectRoot, agentId) {
|
|
54
|
-
const scriptPath = path.join(projectRoot, '.grimoire', 'development', 'scripts', 'generate-greeting.js');
|
|
55
|
-
if (!fs.existsSync(scriptPath)) {
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const result = spawnSync('node', [scriptPath, agentId], {
|
|
60
|
-
cwd: projectRoot,
|
|
61
|
-
encoding: 'utf8',
|
|
62
|
-
timeout: 10000,
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
if (result.status !== 0) {
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return (result.stdout || '').trim() || null;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function buildActivationPrompt(agentId) {
|
|
73
|
-
return [
|
|
74
|
-
`Ative o agente ${agentId} usando .grimoire/development/agents/${agentId}.md`,
|
|
75
|
-
`(fallback: .gemini/rules/grimoire/agents/${agentId}.md),`,
|
|
76
|
-
`renderize o greeting via node .grimoire/development/scripts/generate-greeting.js ${agentId}`,
|
|
77
|
-
'e mantenha a persona ate *exit.',
|
|
78
|
-
].join(' ');
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function runAgentLauncher(agentId, projectRoot = process.cwd()) {
|
|
82
|
-
if (!agentId) {
|
|
83
|
-
console.log('Uso: /grimoire-agent <agent-id>');
|
|
84
|
-
return 1;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (!hasAgent(projectRoot, agentId)) {
|
|
88
|
-
const available = listAvailableAgents(projectRoot);
|
|
89
|
-
console.log(`❌ Agente não encontrado: ${agentId}`);
|
|
90
|
-
if (available.length > 0) {
|
|
91
|
-
console.log('\nAgentes disponíveis:');
|
|
92
|
-
for (const id of available) {
|
|
93
|
-
console.log(`- ${commandNameForAgent(id)}`);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
return 1;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const info = AGENT_INFO[agentId] || { icon: '🤖', role: 'Agent' };
|
|
100
|
-
const activationPrompt = buildActivationPrompt(agentId);
|
|
101
|
-
const greeting = renderGreeting(projectRoot, agentId);
|
|
102
|
-
|
|
103
|
-
console.log(`${info.icon} grimoire Agent Selected: ${agentId}`);
|
|
104
|
-
console.log(`Role: ${info.role}`);
|
|
105
|
-
console.log('');
|
|
106
|
-
console.log('Activation Prompt (copy and send as your next message):');
|
|
107
|
-
console.log(activationPrompt);
|
|
108
|
-
|
|
109
|
-
if (greeting) {
|
|
110
|
-
console.log('\nGreeting Preview:');
|
|
111
|
-
console.log(greeting.split('\n').slice(0, 8).join('\n'));
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return 0;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function runAgentMenu(projectRoot = process.cwd()) {
|
|
118
|
-
const agents = listAvailableAgents(projectRoot);
|
|
119
|
-
|
|
120
|
-
console.log('🤖 grimoire Quick Agent Menu (Gemini)');
|
|
121
|
-
console.log('');
|
|
122
|
-
|
|
123
|
-
if (agents.length === 0) {
|
|
124
|
-
console.log('No grimoire agents found. Run: npm run sync:ide:gemini');
|
|
125
|
-
return 1;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
for (const id of agents) {
|
|
129
|
-
const info = AGENT_INFO[id] || { icon: '🤖', role: 'Agent' };
|
|
130
|
-
console.log(`${info.icon} ${commandNameForAgent(id)} (${info.role})`);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
console.log('\nTip: run /grimoire-<agent-id> to prepare activation prompt quickly.');
|
|
134
|
-
return 0;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
module.exports = {
|
|
138
|
-
AGENT_INFO,
|
|
139
|
-
listAvailableAgents,
|
|
140
|
-
hasAgent,
|
|
141
|
-
buildActivationPrompt,
|
|
142
|
-
commandNameForAgent,
|
|
143
|
-
runAgentLauncher,
|
|
144
|
-
runAgentMenu,
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const { spawnSync } = require('child_process');
|
|
7
|
+
|
|
8
|
+
const AGENT_INFO = {
|
|
9
|
+
'grimoire-master': { icon: '🎨', role: 'Leonardo (Master Orchestrator)' },
|
|
10
|
+
analyst: { icon: '🕯️', role: 'Vermeer (Business Analyst)' },
|
|
11
|
+
architect: { icon: '🔨', role: 'Michelangelo (System Architect)' },
|
|
12
|
+
'data-engineer': { icon: '🟦', role: 'Mondrian (Data Engineer)' },
|
|
13
|
+
dev: { icon: '🖌️', role: 'Rembrandt (Developer)' },
|
|
14
|
+
devops: { icon: '🚄', role: 'Boccioni (DevOps)' },
|
|
15
|
+
pm: { icon: '📜', role: 'Raphael (Product Manager)' },
|
|
16
|
+
po: { icon: '🔍', role: 'Van Eyck (Product Owner)' },
|
|
17
|
+
qa: { icon: '📐', role: 'Dürer (QA Engineer)' },
|
|
18
|
+
sm: { icon: '🌊', role: 'Monet (Scrum Master)' },
|
|
19
|
+
'squad-creator': { icon: '🌀', role: 'Kandinsky (Squad Creator)' },
|
|
20
|
+
'ux-design-expert': { icon: '🎭', role: 'Matisse (UX Expert)' },
|
|
21
|
+
salvador: { icon: '🕰️', role: 'Salvador (Creative Engineer)' },
|
|
22
|
+
frida: { icon: '🌺', role: 'Frida (Resilience Expert)' },
|
|
23
|
+
'van-gogh': { icon: '🌻', role: 'Van Gogh (Performance Guru)' },
|
|
24
|
+
warhol: { icon: '🥫', role: 'Warhol (Documentation Expert)' },
|
|
25
|
+
caravaggio: { icon: '🔦', role: 'Caravaggio (Security Auditor)' },
|
|
26
|
+
picasso: { icon: '💠', role: 'Picasso (Refactoring Specialist)' },
|
|
27
|
+
tarsila: { icon: '🌵', role: 'Tarsila (Localization Expert)' },
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
function listAvailableAgents(projectRoot = process.cwd()) {
|
|
31
|
+
const sourceDir = path.join(projectRoot, '.grimoire', 'development', 'agents');
|
|
32
|
+
if (!fs.existsSync(sourceDir)) return [];
|
|
33
|
+
return fs
|
|
34
|
+
.readdirSync(sourceDir)
|
|
35
|
+
.filter((f) => f.endsWith('.md') && !f.startsWith('_'))
|
|
36
|
+
.map((f) => f.replace('.md', ''))
|
|
37
|
+
.sort();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function commandNameForAgent(agentId) {
|
|
41
|
+
if (agentId.startsWith('grimoire-')) {
|
|
42
|
+
return `/grimoire-${agentId.replace(/^grimoire-/, '')}`;
|
|
43
|
+
}
|
|
44
|
+
return `/grimoire-${agentId}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function hasAgent(projectRoot, agentId) {
|
|
48
|
+
const canonical = path.join(projectRoot, '.grimoire', 'development', 'agents', `${agentId}.md`);
|
|
49
|
+
const gemini = path.join(projectRoot, '.gemini', 'rules', 'grimoire', 'agents', `${agentId}.md`);
|
|
50
|
+
return fs.existsSync(canonical) || fs.existsSync(gemini);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function renderGreeting(projectRoot, agentId) {
|
|
54
|
+
const scriptPath = path.join(projectRoot, '.grimoire', 'development', 'scripts', 'generate-greeting.js');
|
|
55
|
+
if (!fs.existsSync(scriptPath)) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const result = spawnSync('node', [scriptPath, agentId], {
|
|
60
|
+
cwd: projectRoot,
|
|
61
|
+
encoding: 'utf8',
|
|
62
|
+
timeout: 10000,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
if (result.status !== 0) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return (result.stdout || '').trim() || null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function buildActivationPrompt(agentId) {
|
|
73
|
+
return [
|
|
74
|
+
`Ative o agente ${agentId} usando .grimoire/development/agents/${agentId}.md`,
|
|
75
|
+
`(fallback: .gemini/rules/grimoire/agents/${agentId}.md),`,
|
|
76
|
+
`renderize o greeting via node .grimoire/development/scripts/generate-greeting.js ${agentId}`,
|
|
77
|
+
'e mantenha a persona ate *exit.',
|
|
78
|
+
].join(' ');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function runAgentLauncher(agentId, projectRoot = process.cwd()) {
|
|
82
|
+
if (!agentId) {
|
|
83
|
+
console.log('Uso: /grimoire-agent <agent-id>');
|
|
84
|
+
return 1;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (!hasAgent(projectRoot, agentId)) {
|
|
88
|
+
const available = listAvailableAgents(projectRoot);
|
|
89
|
+
console.log(`❌ Agente não encontrado: ${agentId}`);
|
|
90
|
+
if (available.length > 0) {
|
|
91
|
+
console.log('\nAgentes disponíveis:');
|
|
92
|
+
for (const id of available) {
|
|
93
|
+
console.log(`- ${commandNameForAgent(id)}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return 1;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const info = AGENT_INFO[agentId] || { icon: '🤖', role: 'Agent' };
|
|
100
|
+
const activationPrompt = buildActivationPrompt(agentId);
|
|
101
|
+
const greeting = renderGreeting(projectRoot, agentId);
|
|
102
|
+
|
|
103
|
+
console.log(`${info.icon} grimoire Agent Selected: ${agentId}`);
|
|
104
|
+
console.log(`Role: ${info.role}`);
|
|
105
|
+
console.log('');
|
|
106
|
+
console.log('Activation Prompt (copy and send as your next message):');
|
|
107
|
+
console.log(activationPrompt);
|
|
108
|
+
|
|
109
|
+
if (greeting) {
|
|
110
|
+
console.log('\nGreeting Preview:');
|
|
111
|
+
console.log(greeting.split('\n').slice(0, 8).join('\n'));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return 0;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function runAgentMenu(projectRoot = process.cwd()) {
|
|
118
|
+
const agents = listAvailableAgents(projectRoot);
|
|
119
|
+
|
|
120
|
+
console.log('🤖 grimoire Quick Agent Menu (Gemini)');
|
|
121
|
+
console.log('');
|
|
122
|
+
|
|
123
|
+
if (agents.length === 0) {
|
|
124
|
+
console.log('No grimoire agents found. Run: npm run sync:ide:gemini');
|
|
125
|
+
return 1;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
for (const id of agents) {
|
|
129
|
+
const info = AGENT_INFO[id] || { icon: '🤖', role: 'Agent' };
|
|
130
|
+
console.log(`${info.icon} ${commandNameForAgent(id)} (${info.role})`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
console.log('\nTip: run /grimoire-<agent-id> to prepare activation prompt quickly.');
|
|
134
|
+
return 0;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
module.exports = {
|
|
138
|
+
AGENT_INFO,
|
|
139
|
+
listAvailableAgents,
|
|
140
|
+
hasAgent,
|
|
141
|
+
buildActivationPrompt,
|
|
142
|
+
commandNameForAgent,
|
|
143
|
+
runAgentLauncher,
|
|
144
|
+
runAgentMenu,
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
|
|
@@ -30,4 +30,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
30
30
|
---
|
|
31
31
|
|
|
32
32
|
*Generated by [semantic-release](https://github.com/semantic-release/semantic-release) and [conventional-changelog](https://github.com/conventional-changelog/conventional-changelog)*
|
|
33
|
-
|
|
33
|
+
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Squad Installer
|
|
3
|
+
*
|
|
4
|
+
* Handles copying selected squads from the framework to the target project.
|
|
5
|
+
*
|
|
6
|
+
* @module installer/squad-installer
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const fse = require('fs-extra');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Install selected squads to the target project
|
|
14
|
+
*
|
|
15
|
+
* @param {Object} options - Installation options
|
|
16
|
+
* @param {string} options.targetDir - Target project directory
|
|
17
|
+
* @param {string[]} options.selectedSquads - List of squad IDs to install
|
|
18
|
+
* @param {Function} [options.onProgress] - Progress callback
|
|
19
|
+
* @returns {Promise<Object>} Installation result
|
|
20
|
+
*/
|
|
21
|
+
async function installSquads(options = {}) {
|
|
22
|
+
const {
|
|
23
|
+
targetDir = process.cwd(),
|
|
24
|
+
selectedSquads = [],
|
|
25
|
+
onProgress = () => {},
|
|
26
|
+
} = options;
|
|
27
|
+
|
|
28
|
+
if (selectedSquads.length === 0) {
|
|
29
|
+
return { success: true, installedSquads: [], skipped: true };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const results = {
|
|
33
|
+
success: true,
|
|
34
|
+
installedSquads: [],
|
|
35
|
+
errors: [],
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
// 1. Locate source squads directory
|
|
40
|
+
// In framework-dev mode, it's in the root
|
|
41
|
+
// In installed mode, it's inside the package
|
|
42
|
+
const possibleSourceDirs = [
|
|
43
|
+
path.join(__dirname, '..', '..', '..', '..', 'squads'), // Root squads/
|
|
44
|
+
path.join(targetDir, '.grimoire', 'squads'), // Fallback if already there
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
let sourceSquadsDir = null;
|
|
48
|
+
for (const dir of possibleSourceDirs) {
|
|
49
|
+
if (await fse.pathExists(dir)) {
|
|
50
|
+
sourceSquadsDir = dir;
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!sourceSquadsDir) {
|
|
56
|
+
throw new Error('Source squads directory not found');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 2. Prepare target directory
|
|
60
|
+
const targetSquadsDir = path.join(targetDir, '.grimoire', 'squads');
|
|
61
|
+
await fse.ensureDir(targetSquadsDir);
|
|
62
|
+
|
|
63
|
+
// 3. Copy each selected squad
|
|
64
|
+
for (const squadId of selectedSquads) {
|
|
65
|
+
const sourceSquadPath = path.join(sourceSquadsDir, squadId);
|
|
66
|
+
const targetSquadPath = path.join(targetSquadsDir, squadId);
|
|
67
|
+
|
|
68
|
+
onProgress({ step: 'squad', message: `Installing squad: ${squadId}...` });
|
|
69
|
+
|
|
70
|
+
if (await fse.pathExists(sourceSquadPath)) {
|
|
71
|
+
// Guard against source === dest
|
|
72
|
+
if (path.resolve(sourceSquadPath) !== path.resolve(targetSquadPath)) {
|
|
73
|
+
await fse.copy(sourceSquadPath, targetSquadPath, {
|
|
74
|
+
overwrite: true,
|
|
75
|
+
preserveTimestamps: true,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
results.installedSquads.push(squadId);
|
|
79
|
+
} else {
|
|
80
|
+
results.errors.push(`Squad source not found: ${squadId}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// 4. Copy README if it exists
|
|
85
|
+
const sourceReadme = path.join(sourceSquadsDir, 'README.md');
|
|
86
|
+
const targetReadme = path.join(targetSquadsDir, 'README.md');
|
|
87
|
+
if (await fse.pathExists(sourceReadme)) {
|
|
88
|
+
if (path.resolve(sourceReadme) !== path.resolve(targetReadme)) {
|
|
89
|
+
await fse.copy(sourceReadme, targetReadme);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (results.errors.length > 0 && results.installedSquads.length === 0) {
|
|
94
|
+
results.success = false;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return results;
|
|
98
|
+
} catch (error) {
|
|
99
|
+
return {
|
|
100
|
+
success: false,
|
|
101
|
+
installedSquads: results.installedSquads,
|
|
102
|
+
error: error.message,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
module.exports = {
|
|
108
|
+
installSquads,
|
|
109
|
+
};
|