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.
Files changed (92) hide show
  1. package/.grimoire/install-manifest.yaml +2 -2
  2. package/.grimoire/memory/README.md +13 -0
  3. package/.grimoire/memory/entities/context.md +19 -0
  4. package/.grimoire/memory/entities/decisions.md +17 -0
  5. package/.grimoire/memory/entities/patterns.md +20 -0
  6. package/.grimoire/memory/sessions/2026-02-22.json +9 -0
  7. package/.grimoire/memory/sessions/archived/2026-02-22.jsonl +3 -0
  8. package/bin/commands/memory.js +215 -0
  9. package/bin/commands/metrics.js +107 -0
  10. package/bin/grimoire-cli.js +103 -0
  11. package/bin/grimoire-ids.js +2 -2
  12. package/bin/grimoire-minimal.js +1 -1
  13. package/bin/grimoire.js +1118 -1118
  14. package/bin/modules/env-config.js +2 -2
  15. package/bin/modules/mcp-installer.js +1 -1
  16. package/bin/utils/install-errors.js +1 -1
  17. package/bin/utils/install-transaction.js +2 -2
  18. package/bin/utils/pro-detector.js +110 -110
  19. package/package.json +158 -158
  20. package/packages/gemini-grimoire-extension/commands/grimoire-agents.js +2 -2
  21. package/packages/gemini-grimoire-extension/commands/grimoire-master.js +1 -1
  22. package/packages/gemini-grimoire-extension/commands/grimoire-status.js +2 -2
  23. package/packages/gemini-grimoire-extension/commands/grimoire-validate.js +2 -2
  24. package/packages/gemini-grimoire-extension/commands/lib/agent-launcher.js +147 -147
  25. package/packages/gemini-grimoire-extension/extension.json +2 -2
  26. package/packages/gemini-grimoire-extension/gemini-extension.json +2 -2
  27. package/packages/gemini-grimoire-extension/hooks/hooks.json +2 -2
  28. package/packages/grimoire-install/.releaserc.json +1 -1
  29. package/packages/grimoire-install/CHANGELOG.md +1 -1
  30. package/packages/grimoire-install/README.md +2 -2
  31. package/packages/grimoire-install/bin/edmcp.js +1 -1
  32. package/packages/grimoire-install/bin/grimoire-install.js +1 -1
  33. package/packages/grimoire-install/jest.config.js +1 -1
  34. package/packages/grimoire-install/package.json +2 -2
  35. package/packages/grimoire-install/src/edmcp/index.js +1 -1
  36. package/packages/grimoire-install/src/installer.js +2 -2
  37. package/packages/grimoire-pro-cli/bin/grimoire-pro.js +2 -2
  38. package/packages/grimoire-pro-cli/package.json +2 -2
  39. package/packages/grimoire-pro-cli/src/recover.js +1 -1
  40. package/packages/installer/package.json +1 -1
  41. package/packages/installer/src/__tests__/performance-benchmark.js +2 -2
  42. package/packages/installer/src/config/configure-environment.js +2 -2
  43. package/packages/installer/src/config/ide-configs.js +2 -2
  44. package/packages/installer/src/config/templates/core-config-template.js +2 -2
  45. package/packages/installer/src/config/templates/env-template.js +2 -2
  46. package/packages/installer/src/config/validation/config-validator.js +1 -1
  47. package/packages/installer/src/detection/detect-project-type.js +2 -2
  48. package/packages/installer/src/installer/brownfield-upgrader.js +2 -2
  49. package/packages/installer/src/installer/grimoire-core-installer.js +2 -2
  50. package/packages/installer/src/installer/manifest-signature.js +2 -2
  51. package/packages/installer/src/installer/post-install-validator.js +2 -2
  52. package/packages/installer/src/installer/squad-installer.js +109 -0
  53. package/packages/installer/src/merger/index.js +1 -1
  54. package/packages/installer/src/merger/parsers/markdown-section-parser.js +1 -1
  55. package/packages/installer/src/merger/strategies/env-merger.js +1 -1
  56. package/packages/installer/src/merger/strategies/markdown-merger.js +1 -1
  57. package/packages/installer/src/merger/types.js +1 -1
  58. package/packages/installer/src/pro/pro-scaffolder.js +2 -2
  59. package/packages/installer/src/updater/index.js +2 -2
  60. package/packages/installer/src/utils/grimoire-colors.js +1 -1
  61. package/packages/installer/src/wizard/i18n.js +2 -2
  62. package/packages/installer/src/wizard/ide-config-generator.js +2 -2
  63. package/packages/installer/src/wizard/ide-selector.js +1 -1
  64. package/packages/installer/src/wizard/index.js +26 -0
  65. package/packages/installer/src/wizard/pro-setup.js +2 -2
  66. package/packages/installer/src/wizard/questions.js +20 -15
  67. package/packages/installer/src/wizard/validation/index.js +1 -1
  68. package/packages/installer/src/wizard/validation/report-generator.js +1 -1
  69. package/packages/installer/src/wizard/validation/troubleshooting-system.js +2 -2
  70. package/packages/installer/src/wizard/validation/validators/config-validator.js +2 -2
  71. package/packages/installer/src/wizard/validation/validators/file-structure-validator.js +1 -1
  72. package/packages/installer/src/wizard/wizard.js +2 -2
  73. package/packages/installer/tests/integration/environment-configuration.test.js +2 -2
  74. package/packages/installer/tests/integration/wizard-detection.test.js +2 -2
  75. package/packages/installer/tests/unit/config-validator.test.js +1 -1
  76. package/packages/installer/tests/unit/detection/detect-project-type.test.js +2 -2
  77. package/packages/installer/tests/unit/env-template.test.js +2 -2
  78. package/packages/installer/tests/unit/merger/env-merger.test.js +1 -1
  79. package/packages/installer/tests/unit/merger/markdown-merger.test.js +1 -1
  80. package/scripts/check-markdown-links.py +352 -352
  81. package/scripts/code-intel-health-check.js +1 -1
  82. package/scripts/dashboard-parallel-dev.sh +1 -1
  83. package/scripts/dashboard-parallel-phase3.sh +1 -1
  84. package/scripts/dashboard-parallel-phase4.sh +1 -1
  85. package/scripts/ensure-manifest.js +1 -1
  86. package/scripts/generate-install-manifest.js +2 -2
  87. package/scripts/install-monitor-hooks.sh +2 -2
  88. package/scripts/package-synapse.js +2 -2
  89. package/scripts/semantic-lint.js +1 -1
  90. package/scripts/sign-manifest.sh +2 -2
  91. package/scripts/validate-manifest.js +2 -2
  92. 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
+
@@ -145,5 +145,5 @@
145
145
  "multi-agent"
146
146
  ]
147
147
  }
148
-
149
-
148
+
149
+
@@ -145,5 +145,5 @@
145
145
  "multi-agent"
146
146
  ]
147
147
  }
148
-
149
-
148
+
149
+
@@ -68,5 +68,5 @@
68
68
  ]
69
69
  }
70
70
  }
71
-
72
-
71
+
72
+
@@ -36,4 +36,4 @@
36
36
  "@semantic-release/github"
37
37
  ]
38
38
  }
39
-
39
+
@@ -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
+
@@ -115,5 +115,5 @@ If you have an existing grimoire installation, the installer will:
115
115
  ## License
116
116
 
117
117
  MIT
118
-
119
-
118
+
119
+
@@ -77,4 +77,4 @@ program.parse(process.argv);
77
77
  if (!process.argv.slice(2).length) {
78
78
  program.outputHelp();
79
79
  }
80
-
80
+
@@ -48,4 +48,4 @@ program
48
48
  });
49
49
 
50
50
  program.parse(process.argv);
51
-
51
+
@@ -24,4 +24,4 @@ module.exports = {
24
24
  ],
25
25
  verbose: true,
26
26
  };
27
-
27
+
@@ -64,5 +64,5 @@
64
64
  "access": "public"
65
65
  }
66
66
  }
67
-
68
-
67
+
68
+
@@ -379,4 +379,4 @@ module.exports = {
379
379
  removeMcp,
380
380
  parseMcpSource,
381
381
  };
382
-
382
+
@@ -483,5 +483,5 @@ module.exports = {
483
483
  tryLoadConfigResolver,
484
484
  createUserConfigDirect,
485
485
  };
486
-
487
-
486
+
487
+
@@ -229,5 +229,5 @@ switch (command) {
229
229
  showHelp();
230
230
  process.exit(1);
231
231
  }
232
-
233
-
232
+
233
+
@@ -34,5 +34,5 @@
34
34
  "registry": "https://registry.npmjs.org"
35
35
  }
36
36
  }
37
-
38
-
37
+
38
+
@@ -98,4 +98,4 @@ async function recoverLicense() {
98
98
  }
99
99
 
100
100
  module.exports = { recoverLicense, maskEmail, promptEmail, openBrowser, RECOVERY_URL, RECOVERY_MESSAGE };
101
-
101
+
@@ -37,4 +37,4 @@
37
37
  }
38
38
  }
39
39
 
40
-
40
+
@@ -380,5 +380,5 @@ runBenchmarks().catch((err) => {
380
380
  console.error('Benchmark failed:', err);
381
381
  process.exit(1);
382
382
  });
383
-
384
-
383
+
384
+
@@ -369,5 +369,5 @@ module.exports = {
369
369
  collectApiKeys,
370
370
  updateGitignore,
371
371
  };
372
-
373
-
372
+
373
+
@@ -157,5 +157,5 @@ module.exports = {
157
157
  isValidIDE,
158
158
  getIDEChoices,
159
159
  };
160
-
161
-
160
+
161
+
@@ -195,5 +195,5 @@ function generateCoreConfig(options = {}) {
195
195
  module.exports = {
196
196
  generateCoreConfig,
197
197
  };
198
-
199
-
198
+
199
+
@@ -268,5 +268,5 @@ module.exports = {
268
268
  generateEnvContent,
269
269
  generateEnvExample,
270
270
  };
271
-
272
-
271
+
272
+
@@ -241,4 +241,4 @@ module.exports = {
241
241
  validatePath,
242
242
  sanitizeInput,
243
243
  };
244
-
244
+
@@ -79,5 +79,5 @@ function detectProjectType(targetDir = process.cwd()) {
79
79
 
80
80
  module.exports = { detectProjectType };
81
81
 
82
-
83
-
82
+
83
+
@@ -436,5 +436,5 @@ module.exports = {
436
436
  buildFileMap,
437
437
  isUserModified,
438
438
  };
439
-
440
-
439
+
440
+
@@ -424,5 +424,5 @@ module.exports = {
424
424
  FOLDERS_TO_COPY,
425
425
  ROOT_FILES_TO_COPY,
426
426
  };
427
-
428
-
427
+
428
+
@@ -376,5 +376,5 @@ module.exports = {
376
376
  PINNED_PUBLIC_KEY,
377
377
  SignatureLimits,
378
378
  };
379
-
380
-
379
+
380
+
@@ -1520,5 +1520,5 @@ module.exports = {
1520
1520
  isPathContained,
1521
1521
  validateManifestEntry,
1522
1522
  };
1523
-
1524
-
1523
+
1524
+
@@ -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
+ };
@@ -69,4 +69,4 @@ module.exports = {
69
69
  slugify,
70
70
  hasgrimoireMarkers,
71
71
  };
72
-
72
+
@@ -196,4 +196,4 @@ module.exports = {
196
196
  hasgrimoireMarkers,
197
197
  getgrimoireSectionIds,
198
198
  };
199
-
199
+
@@ -135,4 +135,4 @@ class EnvMerger extends BaseMerger {
135
135
  }
136
136
 
137
137
  module.exports = { EnvMerger };
138
-
138
+
@@ -205,4 +205,4 @@ class MarkdownMerger extends BaseMerger {
205
205
  }
206
206
 
207
207
  module.exports = { MarkdownMerger };
208
-
208
+