symfonia-ai-tools 1.8.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -9
- package/lib/i18n.mjs +30 -0
- package/lib/installer.mjs +100 -0
- package/lib/questions.mjs +104 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,21 +19,33 @@ symfonia-ai-tools
|
|
|
19
19
|
|
|
20
20
|
The configurator guides you through:
|
|
21
21
|
1. Language selection (Polish / English)
|
|
22
|
-
2.
|
|
23
|
-
3.
|
|
24
|
-
4.
|
|
25
|
-
5.
|
|
26
|
-
6.
|
|
27
|
-
7.
|
|
28
|
-
8.
|
|
22
|
+
2. **Install scope** — choose what to install
|
|
23
|
+
3. Project type, description, tech stack *(full setup only)*
|
|
24
|
+
4. Instruction & skill packs (checkboxes)
|
|
25
|
+
5. AI tools to configure (checkboxes)
|
|
26
|
+
6. Skills to install (checkboxes — base + from selected packs)
|
|
27
|
+
7. Paths, commands, CI pipeline, JIRA prefix *(full setup only)*
|
|
28
|
+
8. MCP servers (checkboxes + tokens)
|
|
29
|
+
9. CLI installation (checkboxes) *(full setup only)*
|
|
29
30
|
|
|
30
31
|
The entire configurator uses arrow-key navigation — no typing numbers.
|
|
31
32
|
|
|
32
|
-
Finally, it automatically runs GSD:
|
|
33
|
+
Finally (full setup only), it automatically runs GSD:
|
|
33
34
|
- **New project** → `/gsd-new-project`
|
|
34
35
|
- **Existing project** → `/gsd-map-codebase` → `/gsd-new-project`
|
|
35
36
|
|
|
36
|
-
###
|
|
37
|
+
### Install scopes
|
|
38
|
+
|
|
39
|
+
| Scope | What it installs | Use case |
|
|
40
|
+
|-------|-----------------|----------|
|
|
41
|
+
| **Full setup** | Everything — files, skills, MCP, CLI, GSD bootstrap | First-time setup or full reinstall |
|
|
42
|
+
| **Skills only** | Selected skills → `.ai/skills/` + mirrors | Add new skills to existing project |
|
|
43
|
+
| **MCP only** | MCP server configuration | Add/update MCP servers |
|
|
44
|
+
| **Skills + MCP** | Skills and MCP servers | Add skills and MCP in one go |
|
|
45
|
+
|
|
46
|
+
Slim scopes (skills only, MCP only, skills + MCP) skip project metadata, commands, CI, JIRA, and CLI installation — they only ask for what's needed.
|
|
47
|
+
|
|
48
|
+
### Installation modes (existing project, full setup)
|
|
37
49
|
|
|
38
50
|
| Mode | Description |
|
|
39
51
|
|------|-------------|
|
package/lib/i18n.mjs
CHANGED
|
@@ -32,6 +32,15 @@ const translations = {
|
|
|
32
32
|
'q.quit_hint': 'Wpisz',
|
|
33
33
|
'q.quit_hint2': 'aby przerwac w dowolnym momencie.',
|
|
34
34
|
|
|
35
|
+
// questions.mjs — install scope
|
|
36
|
+
'q.section.scope': 'Zakres instalacji',
|
|
37
|
+
'q.scope.select': 'Co chcesz zainstalowac?',
|
|
38
|
+
'q.scope.full': 'Pelny setup',
|
|
39
|
+
'q.scope.full_hint': '(pliki, skille, MCP, CLI)',
|
|
40
|
+
'q.scope.skills_only': 'Tylko skille',
|
|
41
|
+
'q.scope.mcp_only': 'Tylko serwery MCP',
|
|
42
|
+
'q.scope.skills_mcp': 'Skille + MCP',
|
|
43
|
+
|
|
35
44
|
// questions.mjs — project
|
|
36
45
|
'q.section.project': 'Projekt',
|
|
37
46
|
'q.project_type': 'Typ projektu:',
|
|
@@ -143,12 +152,16 @@ const translations = {
|
|
|
143
152
|
'i.mode.overwrite': 'nadpisz wszystko',
|
|
144
153
|
'i.mode.skip': 'tylko nowe pliki',
|
|
145
154
|
'i.mode.mcp': 'tylko MCP',
|
|
155
|
+
'i.mode.skills': 'tylko skille',
|
|
156
|
+
'i.mode.skills_mcp': 'skille + MCP',
|
|
146
157
|
'i.dir': 'Katalog:',
|
|
147
158
|
'i.mode_label': 'Tryb:',
|
|
148
159
|
'i.step.base': 'Szablony bazowe',
|
|
149
160
|
'i.step.pack': 'Pakiet',
|
|
150
161
|
'i.step.guidelines': 'Skladanie',
|
|
151
162
|
'i.step.mirror': 'Mirror',
|
|
163
|
+
'i.step.skills': 'Kopiowanie skilli',
|
|
164
|
+
'i.step.skills_mirror': 'Mirror skilli',
|
|
152
165
|
'i.step.mcp': 'Konfiguracja MCP',
|
|
153
166
|
'i.step.cli': 'Instalacja CLI',
|
|
154
167
|
|
|
@@ -170,6 +183,8 @@ const translations = {
|
|
|
170
183
|
|
|
171
184
|
// installer.mjs — next steps
|
|
172
185
|
'i.done.mcp': 'Konfiguracja MCP zakonczona!',
|
|
186
|
+
'i.done.skills': 'Instalacja skilli zakonczona!',
|
|
187
|
+
'i.done.skills_mcp': 'Instalacja skilli i MCP zakonczona!',
|
|
173
188
|
'i.done.install': 'Instalacja zakonczona!',
|
|
174
189
|
'i.done.check_mcp': 'Sprawdz MCP:',
|
|
175
190
|
'i.done.install_gsd': 'Zainstaluj GSD',
|
|
@@ -213,6 +228,15 @@ const translations = {
|
|
|
213
228
|
'q.quit_hint': 'Type',
|
|
214
229
|
'q.quit_hint2': 'to abort at any time.',
|
|
215
230
|
|
|
231
|
+
// questions.mjs — install scope
|
|
232
|
+
'q.section.scope': 'Installation scope',
|
|
233
|
+
'q.scope.select': 'What do you want to install?',
|
|
234
|
+
'q.scope.full': 'Full setup',
|
|
235
|
+
'q.scope.full_hint': '(files, skills, MCP, CLI)',
|
|
236
|
+
'q.scope.skills_only': 'Skills only',
|
|
237
|
+
'q.scope.mcp_only': 'MCP servers only',
|
|
238
|
+
'q.scope.skills_mcp': 'Skills + MCP',
|
|
239
|
+
|
|
216
240
|
// questions.mjs — project
|
|
217
241
|
'q.section.project': 'Project',
|
|
218
242
|
'q.project_type': 'Project type:',
|
|
@@ -324,12 +348,16 @@ const translations = {
|
|
|
324
348
|
'i.mode.overwrite': 'overwrite all',
|
|
325
349
|
'i.mode.skip': 'new files only',
|
|
326
350
|
'i.mode.mcp': 'MCP only',
|
|
351
|
+
'i.mode.skills': 'skills only',
|
|
352
|
+
'i.mode.skills_mcp': 'skills + MCP',
|
|
327
353
|
'i.dir': 'Directory:',
|
|
328
354
|
'i.mode_label': 'Mode:',
|
|
329
355
|
'i.step.base': 'Base templates',
|
|
330
356
|
'i.step.pack': 'Pack',
|
|
331
357
|
'i.step.guidelines': 'Assembling',
|
|
332
358
|
'i.step.mirror': 'Mirror',
|
|
359
|
+
'i.step.skills': 'Copying skills',
|
|
360
|
+
'i.step.skills_mirror': 'Mirroring skills',
|
|
333
361
|
'i.step.mcp': 'MCP configuration',
|
|
334
362
|
'i.step.cli': 'CLI installation',
|
|
335
363
|
|
|
@@ -351,6 +379,8 @@ const translations = {
|
|
|
351
379
|
|
|
352
380
|
// installer.mjs — next steps
|
|
353
381
|
'i.done.mcp': 'MCP configuration complete!',
|
|
382
|
+
'i.done.skills': 'Skills installation complete!',
|
|
383
|
+
'i.done.skills_mcp': 'Skills & MCP installation complete!',
|
|
354
384
|
'i.done.install': 'Installation complete!',
|
|
355
385
|
'i.done.check_mcp': 'Check MCP:',
|
|
356
386
|
'i.done.install_gsd': 'Install GSD',
|
package/lib/installer.mjs
CHANGED
|
@@ -116,6 +116,12 @@ export async function install(packageRoot, answers) {
|
|
|
116
116
|
const templatesDir = join(packageRoot, 'templates');
|
|
117
117
|
const mode = answers.installMode || 'fresh';
|
|
118
118
|
|
|
119
|
+
// Handle slim install scopes (skills-only, mcp-only, skills+mcp)
|
|
120
|
+
const installScope = answers.installScope || 'full';
|
|
121
|
+
if (installScope !== 'full') {
|
|
122
|
+
return await installSlim(targetDir, templatesDir, answers, installScope);
|
|
123
|
+
}
|
|
124
|
+
|
|
119
125
|
const modeLabel = mode === 'fresh' ? boldGreen(t('i.mode.fresh'))
|
|
120
126
|
: mode === 'overwrite' ? boldYellow(t('i.mode.overwrite'))
|
|
121
127
|
: mode === 'skip-existing' ? boldCyan(t('i.mode.skip'))
|
|
@@ -183,6 +189,100 @@ export async function install(packageRoot, answers) {
|
|
|
183
189
|
}
|
|
184
190
|
}
|
|
185
191
|
|
|
192
|
+
// ─── Slim install (skills-only, mcp-only, skills+mcp) ───
|
|
193
|
+
|
|
194
|
+
async function installSlim(targetDir, templatesDir, answers, scope) {
|
|
195
|
+
const needsSkills = scope !== 'mcp-only';
|
|
196
|
+
const needsMcp = scope !== 'skills-only';
|
|
197
|
+
const needsMirror = needsSkills && (answers.toolCopilot || answers.toolClaude);
|
|
198
|
+
|
|
199
|
+
const totalSteps = (needsSkills ? 1 : 0) + (needsMirror ? 1 : 0) + (needsMcp ? 1 : 0);
|
|
200
|
+
let currentStep = 0;
|
|
201
|
+
|
|
202
|
+
const modeLabel = scope === 'skills-only' ? boldCyan(t('i.mode.skills'))
|
|
203
|
+
: scope === 'mcp-only' ? boldCyan(t('i.mode.mcp'))
|
|
204
|
+
: boldCyan(t('i.mode.skills_mcp'));
|
|
205
|
+
|
|
206
|
+
section(t('i.section.install'));
|
|
207
|
+
console.log(info(`${t('i.dir')} ${bold(targetDir)}`));
|
|
208
|
+
console.log(info(`${t('i.mode_label')} ${modeLabel}`));
|
|
209
|
+
console.log('');
|
|
210
|
+
|
|
211
|
+
if (needsSkills) {
|
|
212
|
+
step(++currentStep, totalSteps, t('i.step.skills'));
|
|
213
|
+
await installSkills(templatesDir, targetDir, answers);
|
|
214
|
+
|
|
215
|
+
if (needsMirror) {
|
|
216
|
+
step(++currentStep, totalSteps, t('i.step.skills_mirror'));
|
|
217
|
+
const aiSkillsDir = join(targetDir, '.ai', 'skills');
|
|
218
|
+
if (answers.toolCopilot) {
|
|
219
|
+
await mirrorDir(aiSkillsDir, join(targetDir, '.github', 'skills'), targetDir, 'overwrite');
|
|
220
|
+
}
|
|
221
|
+
if (answers.toolClaude) {
|
|
222
|
+
await mirrorDir(aiSkillsDir, join(targetDir, '.claude', 'skills'), targetDir, 'overwrite');
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (needsMcp) {
|
|
228
|
+
step(++currentStep, totalSteps, t('i.step.mcp'));
|
|
229
|
+
await generateMcpConfig(targetDir, answers);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
console.log('');
|
|
233
|
+
const doneKey = scope === 'skills-only' ? 'i.done.skills'
|
|
234
|
+
: scope === 'mcp-only' ? 'i.done.mcp'
|
|
235
|
+
: 'i.done.skills_mcp';
|
|
236
|
+
box([boldGreen(` ${t(doneKey)}`)], boldGreen);
|
|
237
|
+
console.log('');
|
|
238
|
+
|
|
239
|
+
if (needsMcp && answers.toolClaude) {
|
|
240
|
+
console.log(info(`${t('i.done.check_mcp')} ${cyan('claude mcp list')}`));
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async function installSkills(templatesDir, targetDir, answers) {
|
|
245
|
+
const selectedSkills = answers.selectedSkills || [];
|
|
246
|
+
if (selectedSkills.length === 0) {
|
|
247
|
+
console.log(` ${dim(t('q.skills.none'))}`);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const aiSkillsDir = join(targetDir, '.ai', 'skills');
|
|
252
|
+
await mkdir(aiSkillsDir, { recursive: true });
|
|
253
|
+
|
|
254
|
+
// Base skills
|
|
255
|
+
const baseSkillsDir = join(templatesDir, 'base', '_ai', 'skills');
|
|
256
|
+
try {
|
|
257
|
+
const entries = await readdir(baseSkillsDir, { withFileTypes: true });
|
|
258
|
+
for (const e of entries) {
|
|
259
|
+
if (e.isDirectory() && selectedSkills.includes(e.name)) {
|
|
260
|
+
const dest = join(aiSkillsDir, e.name);
|
|
261
|
+
await mkdir(dest, { recursive: true });
|
|
262
|
+
await copyTemplateDir(join(baseSkillsDir, e.name), dest, answers, 'overwrite');
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
} catch { /* no base skills */ }
|
|
266
|
+
|
|
267
|
+
// Pack skills
|
|
268
|
+
const allPacks = await loadPacks(join(templatesDir, 'packs'));
|
|
269
|
+
for (const packId of (answers.packs || [])) {
|
|
270
|
+
const pack = allPacks[packId];
|
|
271
|
+
if (!pack) continue;
|
|
272
|
+
const packSkillsDir = join(pack._dir, '_ai', 'skills');
|
|
273
|
+
try {
|
|
274
|
+
const entries = await readdir(packSkillsDir, { withFileTypes: true });
|
|
275
|
+
for (const e of entries) {
|
|
276
|
+
if (e.isDirectory() && selectedSkills.includes(e.name)) {
|
|
277
|
+
const dest = join(aiSkillsDir, e.name);
|
|
278
|
+
await mkdir(dest, { recursive: true });
|
|
279
|
+
await copyTemplateDir(join(packSkillsDir, e.name), dest, answers, 'overwrite');
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
} catch { /* no pack skills */ }
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
186
286
|
// ─── Guidelines assembly ───
|
|
187
287
|
|
|
188
288
|
async function assembleGuidelines(templatesDir, targetDir, answers, selectedPacks, allPacks, mode) {
|
package/lib/questions.mjs
CHANGED
|
@@ -80,6 +80,25 @@ export async function askQuestions(packsDir) {
|
|
|
80
80
|
console.log('');
|
|
81
81
|
console.log(` ${dim(t('q.quit_hint'))} ${yellow('"q"')} ${dim(t('q.quit_hint2'))}`);
|
|
82
82
|
|
|
83
|
+
// --- Install scope ---
|
|
84
|
+
section(t('q.section.scope'));
|
|
85
|
+
answers.installScope = await askChoice(t('q.scope.select'), [
|
|
86
|
+
'full',
|
|
87
|
+
'skills-only',
|
|
88
|
+
'mcp-only',
|
|
89
|
+
'skills+mcp',
|
|
90
|
+
], [
|
|
91
|
+
`${boldGreen(t('q.scope.full'))} ${dim(t('q.scope.full_hint'))}`,
|
|
92
|
+
`${boldCyan(t('q.scope.skills_only'))}`,
|
|
93
|
+
`${boldCyan(t('q.scope.mcp_only'))}`,
|
|
94
|
+
`${boldCyan(t('q.scope.skills_mcp'))}`,
|
|
95
|
+
]);
|
|
96
|
+
|
|
97
|
+
// Slim flows — early return
|
|
98
|
+
if (answers.installScope !== 'full') {
|
|
99
|
+
return await askSlimFlow(answers, packsDir);
|
|
100
|
+
}
|
|
101
|
+
|
|
83
102
|
// --- Project type ---
|
|
84
103
|
section(t('q.section.project'));
|
|
85
104
|
|
|
@@ -494,3 +513,88 @@ function printSummary(answers, allPacks) {
|
|
|
494
513
|
tableRow(t('q.summary.gsd'), cyan(gsdSteps.join(dim(' → '))));
|
|
495
514
|
}
|
|
496
515
|
}
|
|
516
|
+
|
|
517
|
+
// ─── Slim flow (skills-only, mcp-only, skills+mcp) ───
|
|
518
|
+
|
|
519
|
+
async function askSlimFlow(answers, packsDir) {
|
|
520
|
+
const scope = answers.installScope;
|
|
521
|
+
const needsSkills = scope !== 'mcp-only';
|
|
522
|
+
const needsMcp = scope !== 'skills-only';
|
|
523
|
+
|
|
524
|
+
// --- Target directory ---
|
|
525
|
+
answers.targetDir = await ask(t('q.target_dir'), '.');
|
|
526
|
+
answers.installMode = 'overwrite';
|
|
527
|
+
|
|
528
|
+
// --- Tool selection ---
|
|
529
|
+
await askToolSelection(answers);
|
|
530
|
+
|
|
531
|
+
// --- Skills flow ---
|
|
532
|
+
let allPacks = {};
|
|
533
|
+
if (needsSkills) {
|
|
534
|
+
allPacks = await loadPacks(packsDir);
|
|
535
|
+
await askPackSelection(answers, allPacks);
|
|
536
|
+
const baseSkillsDir = join(packsDir, '..', 'base', '_ai', 'skills');
|
|
537
|
+
await askSkillSelection(answers, allPacks, baseSkillsDir);
|
|
538
|
+
await askPackPlaceholders(answers, allPacks);
|
|
539
|
+
checkCliDeps(answers.selectedSkills || []);
|
|
540
|
+
} else {
|
|
541
|
+
answers.packs = [];
|
|
542
|
+
answers.selectedSkills = [];
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// --- MCP flow ---
|
|
546
|
+
if (needsMcp) {
|
|
547
|
+
const requiredMcps = collectRequiredMcps(answers.selectedSkills || []);
|
|
548
|
+
await askMcpServers(answers, requiredMcps);
|
|
549
|
+
} else {
|
|
550
|
+
answers.mcpJira = false;
|
|
551
|
+
answers.mcpBitbucket = false;
|
|
552
|
+
answers.mcpFigma = false;
|
|
553
|
+
answers.mcpGrafana = false;
|
|
554
|
+
answers.mcpContext7 = false;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// --- Defaults for unused fields ---
|
|
558
|
+
answers.installClaudeCli = false;
|
|
559
|
+
answers.installGsd = false;
|
|
560
|
+
|
|
561
|
+
// --- Summary ---
|
|
562
|
+
printSlimSummary(answers, allPacks);
|
|
563
|
+
|
|
564
|
+
const proceed = await askYN(`\n ${t('q.summary.continue')}`, true);
|
|
565
|
+
if (!proceed) throw new Error('USER_ABORT');
|
|
566
|
+
|
|
567
|
+
return answers;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
function printSlimSummary(answers, allPacks) {
|
|
571
|
+
section(t('q.section.summary'));
|
|
572
|
+
|
|
573
|
+
tableRow(t('q.summary.dir'), answers.targetDir);
|
|
574
|
+
|
|
575
|
+
const tools = [];
|
|
576
|
+
if (answers.toolClaude) tools.push('Claude');
|
|
577
|
+
if (answers.toolCopilot) tools.push('Copilot');
|
|
578
|
+
if (answers.toolCursor) tools.push('Cursor');
|
|
579
|
+
if (answers.toolGemini) tools.push('Gemini');
|
|
580
|
+
if (answers.toolJunie) tools.push('Junie');
|
|
581
|
+
tableRow(t('q.summary.tools'), tools.length > 0 ? cyan(tools.join(dim(' · '))) : dim(t('q.summary.none')));
|
|
582
|
+
|
|
583
|
+
if (answers.installScope !== 'mcp-only') {
|
|
584
|
+
const packNames = (answers.packs || []).map(id => allPacks[id]?.name || id);
|
|
585
|
+
tableRow(t('q.summary.packs'), packNames.length > 0 ? cyan(packNames.join(dim(' · '))) : dim(t('q.summary.none')));
|
|
586
|
+
|
|
587
|
+
const skills = answers.selectedSkills || [];
|
|
588
|
+
tableRow(t('q.summary.skills'), skills.length > 0 ? cyan(skills.join(dim(' · '))) : dim(t('q.summary.none')));
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
if (answers.installScope !== 'skills-only') {
|
|
592
|
+
const mcps = [];
|
|
593
|
+
if (answers.mcpJira) mcps.push('Jira');
|
|
594
|
+
if (answers.mcpBitbucket) mcps.push('Bitbucket');
|
|
595
|
+
if (answers.mcpFigma) mcps.push('Figma');
|
|
596
|
+
if (answers.mcpGrafana) mcps.push('Grafana');
|
|
597
|
+
if (answers.mcpContext7) mcps.push('Context7');
|
|
598
|
+
tableRow(t('q.summary.mcp'), mcps.length > 0 ? cyan(mcps.join(dim(' · '))) : dim(t('q.summary.none')));
|
|
599
|
+
}
|
|
600
|
+
}
|