Skip to content

Commit e3b5819

Browse files
committed
feat(pgpm): use built-in SkillInstaller for fast skill installation
Replace npx skills add with genomic's SkillInstaller as the default skill installation method. Uses shallow git clone + direct file copy with caching — much faster than the npx path. - Default: SkillInstaller (shallow clone, cached, ~2s) - --use-skills flag: opt into npx skills add (slower, writes skills-lock.json) - Re-exports SkillInstaller from @pgpmjs/core for downstream consumers - Threads --use-skills through all init paths (workspace, module, boilerplate)
1 parent 30fdaaa commit e3b5819

2 files changed

Lines changed: 49 additions & 3 deletions

File tree

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

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
resolveBoilerplateBaseDir,
1212
scaffoldTemplate,
1313
scanBoilerplates,
14+
SkillInstaller,
1415
sluggify,
1516
} from '@pgpmjs/core';
1617
import { resolveWorkspaceByType } from '@pgpmjs/env';
@@ -44,6 +45,7 @@ Options:
4445
--template, -t <path> Full template path (e.g., pnpm/module) - combines dir and fromPath
4546
--boilerplate Prompt to select from available boilerplates
4647
--create-workspace, -w Create a workspace first, then create the module inside it
48+
--use-skills Use npx skills CLI for skill installation (slower, writes skills-lock.json)
4749
4850
Examples:
4951
${binaryName} init Initialize new module (default)
@@ -79,6 +81,7 @@ async function handleInit(argv: Partial<Record<string, any>>, prompter: Inquirer
7981
const noTty = Boolean((argv as any).noTty || argv['no-tty'] || argv.tty === false || process.env.CI === 'true');
8082
const useBoilerplatePrompt = Boolean(argv.boilerplate);
8183
const createWorkspace = Boolean(argv.createWorkspace || argv['create-workspace'] || argv.w);
84+
const useNpxSkills = Boolean(argv.useSkills || argv['use-skills']);
8285

8386
// Get fromPath from first positional arg
8487
const positionalFromPath = argv._?.[0] as string | undefined;
@@ -110,6 +113,7 @@ async function handleInit(argv: Partial<Record<string, any>>, prompter: Inquirer
110113
dir,
111114
noTty,
112115
cwd,
116+
useNpxSkills,
113117
});
114118
}
115119

@@ -139,6 +143,7 @@ async function handleInit(argv: Partial<Record<string, any>>, prompter: Inquirer
139143
dir,
140144
noTty,
141145
cwd,
146+
useNpxSkills,
142147
});
143148
}
144149

@@ -152,6 +157,7 @@ async function handleInit(argv: Partial<Record<string, any>>, prompter: Inquirer
152157
cwd,
153158
requiresWorkspace: inspection.config?.requiresWorkspace,
154159
createWorkspace,
160+
useNpxSkills,
155161
}, wasExplicitModuleRequest);
156162
}
157163

@@ -162,6 +168,7 @@ interface BoilerplateInitContext {
162168
dir?: string;
163169
noTty: boolean;
164170
cwd: string;
171+
useNpxSkills?: boolean;
165172
}
166173

167174
async function handleBoilerplateInit(
@@ -243,6 +250,7 @@ async function handleBoilerplateInit(
243250
dir: ctx.dir,
244251
noTty: ctx.noTty,
245252
cwd: ctx.cwd,
253+
useNpxSkills: ctx.useNpxSkills,
246254
});
247255
}
248256

@@ -256,6 +264,7 @@ async function handleBoilerplateInit(
256264
noTty: ctx.noTty,
257265
cwd: ctx.cwd,
258266
requiresWorkspace: inspection.config?.requiresWorkspace,
267+
useNpxSkills: ctx.useNpxSkills,
259268
}, true);
260269
}
261270

@@ -275,9 +284,44 @@ interface InitContext {
275284
* If true, create a workspace first, then create the module inside it.
276285
*/
277286
createWorkspace?: boolean;
287+
/**
288+
* If true, use npx skills CLI instead of built-in shallow clone.
289+
*/
290+
useNpxSkills?: boolean;
291+
}
292+
293+
function installSkills(skills: BoilerplateSkill[], cwd: string, useNpxSkills: boolean): void {
294+
if (useNpxSkills) {
295+
installSkillsViaNpx(skills, cwd);
296+
} else {
297+
installSkillsBuiltin(skills, cwd);
298+
}
299+
}
300+
301+
function installSkillsBuiltin(skills: BoilerplateSkill[], cwd: string): void {
302+
const installer = new SkillInstaller({ toolName: DEFAULT_TEMPLATE_TOOL_NAME });
303+
const result = installer.install(skills, cwd);
304+
305+
if (result.installed.length > 0) {
306+
for (const name of result.installed) {
307+
process.stdout.write(` installed ${name}\n`);
308+
}
309+
}
310+
311+
if (result.failed.length > 0) {
312+
process.stdout.write('\n⚠️ Some skills could not be installed automatically.\n');
313+
process.stdout.write('Run the following commands manually:\n\n');
314+
for (const f of result.failed) {
315+
const source = f.source.includes('://')
316+
? f.source
317+
: `https://github.com/${f.source}`;
318+
process.stdout.write(` npx skills add ${source} --skill ${f.skill}\n`);
319+
}
320+
process.stdout.write('\n');
321+
}
278322
}
279323

280-
function installSkills(skills: BoilerplateSkill[], cwd: string): void {
324+
function installSkillsViaNpx(skills: BoilerplateSkill[], cwd: string): void {
281325
const failed: string[] = [];
282326

283327
for (const entry of skills) {
@@ -373,7 +417,7 @@ async function handleWorkspaceInit(
373417
});
374418
if (templateInfo.config?.skills?.length) {
375419
process.stdout.write('\n📦 Installing skills...\n\n');
376-
installSkills(templateInfo.config.skills, targetPath);
420+
installSkills(templateInfo.config.skills, targetPath, Boolean(ctx.useNpxSkills));
377421
}
378422

379423
const relPath = path.relative(process.cwd(), targetPath);
@@ -678,7 +722,7 @@ async function handleModuleInit(
678722
if (moduleTemplateInfo.config?.skills?.length) {
679723
const skillsCwd = project.workspacePath || modulePath;
680724
process.stdout.write('\n📦 Installing skills...\n\n');
681-
installSkills(moduleTemplateInfo.config.skills, skillsCwd);
725+
installSkills(moduleTemplateInfo.config.skills, skillsCwd, Boolean(ctx.useNpxSkills));
682726
}
683727

684728
const relPath = path.relative(process.cwd(), modulePath);

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import os from 'os';
22
import path from 'path';
33
import { TemplateScaffolder, BoilerplateConfig as GenomicBoilerplateConfig } from 'genomic';
44
export type { BoilerplateSkill } from 'genomic';
5+
export { SkillInstaller } from 'genomic';
6+
export type { SkillInstallOptions, SkillInstallResult, SkillInstallFailure } from 'genomic';
57
import type { Inquirerer, Question } from 'inquirerer';
68

79
/**

0 commit comments

Comments
 (0)