Skip to content

Commit 4ba32ea

Browse files
authored
Merge pull request #1271 from constructive-io/feat/skill-install-on-scaffold
feat(pgpm): install skills from .boilerplate.json after scaffold
2 parents 400b866 + 835c827 commit 4ba32ea

2 files changed

Lines changed: 76 additions & 2 deletions

File tree

pgpm/cli/src/commands/init/index.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
import { execSync } from 'child_process';
12
import fs from 'fs';
23
import path from 'path';
34

45
import {
6+
BoilerplateSkill,
57
DEFAULT_TEMPLATE_REPO,
68
DEFAULT_TEMPLATE_TOOL_NAME,
79
inspectTemplate,
@@ -275,6 +277,38 @@ interface InitContext {
275277
createWorkspace?: boolean;
276278
}
277279

280+
function installSkills(skills: BoilerplateSkill[], cwd: string): void {
281+
const failed: string[] = [];
282+
283+
for (const entry of skills) {
284+
const source = entry.source.includes('://')
285+
? entry.source
286+
: `https://github.com/${entry.source}`;
287+
288+
for (const skill of entry.skills) {
289+
const cmd = `npx --yes skills add ${source} --skill ${skill} --yes`;
290+
try {
291+
execSync(cmd, {
292+
cwd,
293+
stdio: ['pipe', 'inherit', 'inherit'],
294+
timeout: 120_000,
295+
});
296+
} catch {
297+
failed.push(` npx skills add ${source} --skill ${skill}`);
298+
}
299+
}
300+
}
301+
302+
if (failed.length > 0) {
303+
process.stdout.write('\n⚠️ Some skills could not be installed automatically.\n');
304+
process.stdout.write('Run the following commands manually:\n\n');
305+
for (const cmd of failed) {
306+
process.stdout.write(`${cmd}\n`);
307+
}
308+
process.stdout.write('\n');
309+
}
310+
}
311+
278312
async function handleWorkspaceInit(
279313
argv: Partial<Record<string, any>>,
280314
prompter: Inquirerer,
@@ -329,6 +363,19 @@ async function handleWorkspaceInit(
329363
process.stdout.write('\n');
330364
}
331365

366+
// Install skills declared in .boilerplate.json
367+
const templateInfo = inspectTemplate({
368+
fromPath: ctx.fromPath,
369+
templateRepo: ctx.templateRepo,
370+
branch: ctx.branch,
371+
dir: ctx.dir,
372+
cwd: ctx.cwd,
373+
});
374+
if (templateInfo.config?.skills?.length) {
375+
process.stdout.write('\n📦 Installing skills...\n\n');
376+
installSkills(templateInfo.config.skills, targetPath);
377+
}
378+
332379
const relPath = path.relative(process.cwd(), targetPath);
333380
process.stdout.write(`\n✨ Enjoy!\n\ncd ./${relPath}\n`);
334381

@@ -620,6 +667,20 @@ async function handleModuleInit(
620667
process.stdout.write('\n');
621668
}
622669

670+
// Install skills declared in .boilerplate.json
671+
const moduleTemplateInfo = inspectTemplate({
672+
fromPath: ctx.fromPath,
673+
templateRepo: ctx.templateRepo,
674+
branch: ctx.branch,
675+
dir: ctx.dir,
676+
cwd: ctx.cwd,
677+
});
678+
if (moduleTemplateInfo.config?.skills?.length) {
679+
const skillsCwd = project.workspacePath || modulePath;
680+
process.stdout.write('\n📦 Installing skills...\n\n');
681+
installSkills(moduleTemplateInfo.config.skills, skillsCwd);
682+
}
683+
623684
const relPath = path.relative(process.cwd(), modulePath);
624685
process.stdout.write(`\n✨ Enjoy!\n\ncd ./${relPath}\n`);
625686

pgpm/core/src/core/template-scaffold.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,15 @@ import type { Inquirerer, Question } from 'inquirerer';
1414
export type WorkspaceType = 'pgpm' | 'pnpm' | 'lerna' | 'npm' | false;
1515

1616
/**
17-
* Extended BoilerplateConfig that adds workspace requirement field.
18-
* This field controls both workspace detection and whether pgpm-specific files are created.
17+
* Declares a skill to install after scaffolding completes.
1918
*/
19+
export interface BoilerplateSkill {
20+
/** GitHub repository (org/repo format) */
21+
source: string;
22+
/** Skill name(s) to install from the source */
23+
skills: string[];
24+
}
25+
2026
export interface BoilerplateConfig extends GenomicBoilerplateConfig {
2127
/**
2228
* Specifies what type of workspace this template requires.
@@ -29,6 +35,13 @@ export interface BoilerplateConfig extends GenomicBoilerplateConfig {
2935
* Defaults to 'pgpm' for 'module' type (backward compatibility), false for others.
3036
*/
3137
requiresWorkspace?: WorkspaceType;
38+
/**
39+
* Skills to install after scaffolding completes.
40+
* Each entry specifies a source repository and skill names to install.
41+
* Runs `npx skills add <source> --skill <name>` for each entry.
42+
* Non-fatal: prints manual install commands on failure.
43+
*/
44+
skills?: BoilerplateSkill[];
3245
}
3346

3447
export interface InspectTemplateOptions {

0 commit comments

Comments
 (0)