Skip to content

Commit a805aea

Browse files
authored
Merge pull request #1276 from constructive-io/feat/builtin-skill-installer
feat(pgpm): use built-in SkillInstaller for fast skill installation
2 parents 30fdaaa + efc61a6 commit a805aea

6 files changed

Lines changed: 2570 additions & 7294 deletions

File tree

pgpm/cli/__tests__/init.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
jest.setTimeout(60000);
22
process.env.PGPM_SKIP_UPDATE_CHECK = 'true';
3+
process.env.PGPM_SKIP_SKILL_INSTALL = 'true';
34

45
import { PgpmPackage } from '@pgpmjs/core';
56
import { existsSync } from 'fs';

pgpm/cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
"@pgsql/quotes": "^17.1.0",
5555
"appstash": "^0.7.0",
5656
"find-and-require-package-json": "^0.9.1",
57-
"genomic": "^5.5.0",
57+
"genomic": "^5.6.0",
5858
"inquirerer": "^4.8.1",
5959
"js-yaml": "^4.1.0",
6060
"pg-cache": "workspace:^",

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

Lines changed: 49 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,46 @@ 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 (process.env.PGPM_SKIP_SKILL_INSTALL) return;
295+
296+
if (useNpxSkills) {
297+
installSkillsViaNpx(skills, cwd);
298+
} else {
299+
installSkillsBuiltin(skills, cwd);
300+
}
301+
}
302+
303+
function installSkillsBuiltin(skills: BoilerplateSkill[], cwd: string): void {
304+
const installer = new SkillInstaller({ toolName: DEFAULT_TEMPLATE_TOOL_NAME });
305+
const result = installer.install(skills, cwd);
306+
307+
if (result.installed.length > 0) {
308+
for (const name of result.installed) {
309+
process.stdout.write(` installed ${name}\n`);
310+
}
311+
}
312+
313+
if (result.failed.length > 0) {
314+
process.stdout.write('\n⚠️ Some skills could not be installed automatically.\n');
315+
process.stdout.write('Run the following commands manually:\n\n');
316+
for (const f of result.failed) {
317+
const source = f.source.includes('://')
318+
? f.source
319+
: `https://github.com/${f.source}`;
320+
process.stdout.write(` npx skills add ${source} --skill ${f.skill}\n`);
321+
}
322+
process.stdout.write('\n');
323+
}
278324
}
279325

280-
function installSkills(skills: BoilerplateSkill[], cwd: string): void {
326+
function installSkillsViaNpx(skills: BoilerplateSkill[], cwd: string): void {
281327
const failed: string[] = [];
282328

283329
for (const entry of skills) {
@@ -373,7 +419,7 @@ async function handleWorkspaceInit(
373419
});
374420
if (templateInfo.config?.skills?.length) {
375421
process.stdout.write('\n📦 Installing skills...\n\n');
376-
installSkills(templateInfo.config.skills, targetPath);
422+
installSkills(templateInfo.config.skills, targetPath, Boolean(ctx.useNpxSkills));
377423
}
378424

379425
const relPath = path.relative(process.cwd(), targetPath);
@@ -678,7 +724,7 @@ async function handleModuleInit(
678724
if (moduleTemplateInfo.config?.skills?.length) {
679725
const skillsCwd = project.workspacePath || modulePath;
680726
process.stdout.write('\n📦 Installing skills...\n\n');
681-
installSkills(moduleTemplateInfo.config.skills, skillsCwd);
727+
installSkills(moduleTemplateInfo.config.skills, skillsCwd, Boolean(ctx.useNpxSkills));
682728
}
683729

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

pgpm/core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
"@pgpmjs/server-utils": "workspace:^",
5454
"@pgpmjs/types": "workspace:^",
5555
"csv-to-pg": "workspace:^",
56-
"genomic": "^5.5.0",
56+
"genomic": "^5.6.0",
5757
"glob": "^13.0.6",
5858
"minimatch": "^10.2.5",
5959
"parse-package-name": "^1.0.0",

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)