From f40d2c56e31f0a0687db4bb2f535f66d9ea2a1ef Mon Sep 17 00:00:00 2001 From: sunyiteng Date: Tue, 14 Apr 2026 16:41:45 +0800 Subject: [PATCH 1/3] fix: add temporary pnpmfile checksum compatibility --- packages/pnpm-plugin-skills/pnpmfile.cjs | 3 +- packages/pnpm-plugin-skills/pnpmfile.mjs | 4 +-- packages/pnpm-plugin-skills/src/index.ts | 21 +++++++++++ .../pnpm-plugin-skills/test/index.test.ts | 36 +++++++++++++++++++ 4 files changed, 61 insertions(+), 3 deletions(-) diff --git a/packages/pnpm-plugin-skills/pnpmfile.cjs b/packages/pnpm-plugin-skills/pnpmfile.cjs index 1e90c88..2c74ba3 100644 --- a/packages/pnpm-plugin-skills/pnpmfile.cjs +++ b/packages/pnpm-plugin-skills/pnpmfile.cjs @@ -1,8 +1,9 @@ // pnpm v10 -const { preResolution } = require('./dist/index.js') +const { afterAllResolved, preResolution } = require('./dist/index.js') module.exports = { hooks: { + afterAllResolved, preResolution, }, } diff --git a/packages/pnpm-plugin-skills/pnpmfile.mjs b/packages/pnpm-plugin-skills/pnpmfile.mjs index a61d3f6..7c67bac 100644 --- a/packages/pnpm-plugin-skills/pnpmfile.mjs +++ b/packages/pnpm-plugin-skills/pnpmfile.mjs @@ -1,4 +1,4 @@ // pnpm v11 -import { preResolution } from './dist/index.mjs' +import { afterAllResolved, preResolution } from './dist/index.mjs' -export const hooks = { preResolution } +export const hooks = { afterAllResolved, preResolution } diff --git a/packages/pnpm-plugin-skills/src/index.ts b/packages/pnpm-plugin-skills/src/index.ts index 5ac06b9..862bb91 100644 --- a/packages/pnpm-plugin-skills/src/index.ts +++ b/packages/pnpm-plugin-skills/src/index.ts @@ -1,5 +1,11 @@ import { installCommand } from 'skills-package-manager' +type PluginSettings = { + pnpmPlugin?: { + removePnpmFileCheckSum?: boolean + } +} + export async function preResolution( options: { lockfileDir?: string; workspaceRoot?: string } = {}, ) { @@ -11,3 +17,18 @@ export async function preResolution( await installCommand({ cwd: lockfileDir }) return undefined } + +export function afterAllResolved( + lockfile: Record, + context: { config?: PluginSettings } = {}, +) { + if (context.config?.pnpmPlugin?.removePnpmFileCheckSum !== true) { + return lockfile + } + + if ('pnpmfileChecksum' in lockfile) { + delete lockfile.pnpmfileChecksum + } + + return lockfile +} diff --git a/packages/pnpm-plugin-skills/test/index.test.ts b/packages/pnpm-plugin-skills/test/index.test.ts index ebc822b..24005b6 100644 --- a/packages/pnpm-plugin-skills/test/index.test.ts +++ b/packages/pnpm-plugin-skills/test/index.test.ts @@ -65,3 +65,39 @@ describe('preResolution', () => { expect(existsSync(path.join(root, '.claude/skills/hello-skill'))).toBe(true) }) }) + +describe('afterAllResolved', () => { + it('removes pnpmfileChecksum when temporary compatibility flag is enabled', async () => { + const { afterAllResolved } = await import('../src/index') + + const lockfile = { + lockfileVersion: '9.0', + pnpmfileChecksum: 'checksum-to-remove', + } + + const result = afterAllResolved(lockfile, { + config: { + pnpmPlugin: { + removePnpmFileCheckSum: true, + }, + }, + }) + + expect(result).toBe(lockfile) + expect(result).not.toHaveProperty('pnpmfileChecksum') + }) + + it('keeps pnpmfileChecksum by default', async () => { + const { afterAllResolved } = await import('../src/index') + + const lockfile = { + lockfileVersion: '9.0', + pnpmfileChecksum: 'checksum-to-keep', + } + + const result = afterAllResolved(lockfile, {}) + + expect(result).toBe(lockfile) + expect(result).toHaveProperty('pnpmfileChecksum', 'checksum-to-keep') + }) +}) From b2ef84b0b35b1203daee995c97584fcf7ec86c87 Mon Sep 17 00:00:00 2001 From: sunyiteng Date: Tue, 14 Apr 2026 17:00:31 +0800 Subject: [PATCH 2/3] fix: restore preview change detection --- .github/workflows/preview.yml | 1 - packages/pnpm-plugin-skills/src/index.ts | 4 ++-- packages/pnpm-plugin-skills/test/index.test.ts | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index e8737eb..0046067 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -32,7 +32,6 @@ jobs: - uses: dorny/paths-filter@d1c1ffe0248fe513906c8e24db8ea791d46f8590 # v3.0.3 id: changes with: - predicate-quantifier: 'every' filters: | changed: - "packages/**" diff --git a/packages/pnpm-plugin-skills/src/index.ts b/packages/pnpm-plugin-skills/src/index.ts index 862bb91..9610305 100644 --- a/packages/pnpm-plugin-skills/src/index.ts +++ b/packages/pnpm-plugin-skills/src/index.ts @@ -2,7 +2,7 @@ import { installCommand } from 'skills-package-manager' type PluginSettings = { pnpmPlugin?: { - removePnpmFileCheckSum?: boolean + removePnpmfileChecksum?: boolean } } @@ -22,7 +22,7 @@ export function afterAllResolved( lockfile: Record, context: { config?: PluginSettings } = {}, ) { - if (context.config?.pnpmPlugin?.removePnpmFileCheckSum !== true) { + if (context.config?.pnpmPlugin?.removePnpmfileChecksum !== true) { return lockfile } diff --git a/packages/pnpm-plugin-skills/test/index.test.ts b/packages/pnpm-plugin-skills/test/index.test.ts index 24005b6..43983da 100644 --- a/packages/pnpm-plugin-skills/test/index.test.ts +++ b/packages/pnpm-plugin-skills/test/index.test.ts @@ -78,7 +78,7 @@ describe('afterAllResolved', () => { const result = afterAllResolved(lockfile, { config: { pnpmPlugin: { - removePnpmFileCheckSum: true, + removePnpmfileChecksum: true, }, }, }) From 34821618c83bb66cba6e766da60a0f198a785a6e Mon Sep 17 00:00:00 2001 From: sunyiteng Date: Tue, 14 Apr 2026 17:09:47 +0800 Subject: [PATCH 3/3] feat: move pnpm plugin config into skills manifest --- packages/pnpm-plugin-skills/README.md | 5 +++ packages/pnpm-plugin-skills/src/index.ts | 31 +++++++++++++++---- .../pnpm-plugin-skills/test/index.test.ts | 27 +++++++++++----- .../src/config/schema.ts | 11 +++++++ .../src/config/skillsManifest.ts | 1 + .../src/config/writeSkillsManifest.ts | 4 +++ .../test/manifest.test.ts | 30 ++++++++++++++++++ website/docs/architecture/pnpm-plugin.mdx | 5 +++ 8 files changed, 100 insertions(+), 14 deletions(-) diff --git a/packages/pnpm-plugin-skills/README.md b/packages/pnpm-plugin-skills/README.md index 3a30e25..54e9626 100644 --- a/packages/pnpm-plugin-skills/README.md +++ b/packages/pnpm-plugin-skills/README.md @@ -26,12 +26,17 @@ Then create a `skills.json` in your project root: { "installDir": ".agents/skills", "linkTargets": [".claude/skills"], + "pnpmPlugin": { + "removePnpmfileChecksum": true + }, "skills": { "my-skill": "https://github.com/owner/repo.git#path:/skills/my-skill" } } ``` +`pnpmPlugin.removePnpmfileChecksum` is a temporary compatibility switch for repositories that need `pnpm-plugin-skills` to remove `pnpmfileChecksum` from `pnpm-lock.yaml` in `afterAllResolved`. + Now `pnpm install` will automatically install your skills. ## Architecture diff --git a/packages/pnpm-plugin-skills/src/index.ts b/packages/pnpm-plugin-skills/src/index.ts index 9610305..5de5316 100644 --- a/packages/pnpm-plugin-skills/src/index.ts +++ b/packages/pnpm-plugin-skills/src/index.ts @@ -1,8 +1,17 @@ -import { installCommand } from 'skills-package-manager' +import { readFileSync } from 'node:fs' +import path from 'node:path' +import { installCommand, type SkillsManifest } from 'skills-package-manager' -type PluginSettings = { - pnpmPlugin?: { - removePnpmfileChecksum?: boolean +function readPluginManifest(rootDir: string): SkillsManifest | null { + const filePath = path.join(rootDir, 'skills.json') + + try { + const parsed = JSON.parse(readFileSync(filePath, 'utf8')) as { + pnpmPlugin?: SkillsManifest['pnpmPlugin'] + } + return parsed as SkillsManifest + } catch { + return null } } @@ -20,9 +29,19 @@ export async function preResolution( export function afterAllResolved( lockfile: Record, - context: { config?: PluginSettings } = {}, + context: { lockfileDir?: string; workspaceDir?: string } = {}, ) { - if (context.config?.pnpmPlugin?.removePnpmfileChecksum !== true) { + const manifestRoot = context.lockfileDir ?? context.workspaceDir + if (!manifestRoot) { + return lockfile + } + + const manifest = readPluginManifest(manifestRoot) + if (!manifest) { + return lockfile + } + + if (manifest.pnpmPlugin?.removePnpmfileChecksum !== true) { return lockfile } diff --git a/packages/pnpm-plugin-skills/test/index.test.ts b/packages/pnpm-plugin-skills/test/index.test.ts index 43983da..f59076b 100644 --- a/packages/pnpm-plugin-skills/test/index.test.ts +++ b/packages/pnpm-plugin-skills/test/index.test.ts @@ -67,21 +67,32 @@ describe('preResolution', () => { }) describe('afterAllResolved', () => { - it('removes pnpmfileChecksum when temporary compatibility flag is enabled', async () => { + it('removes pnpmfileChecksum when enabled in skills.json', async () => { const { afterAllResolved } = await import('../src/index') + const root = mkdtempSync(path.join(tmpdir(), 'pnpm-plugin-skills-config-')) + + writeFileSync( + path.join(root, 'skills.json'), + JSON.stringify( + { + installDir: '.agents/skills', + linkTargets: [], + pnpmPlugin: { + removePnpmfileChecksum: true, + }, + skills: {}, + }, + null, + 2, + ), + ) const lockfile = { lockfileVersion: '9.0', pnpmfileChecksum: 'checksum-to-remove', } - const result = afterAllResolved(lockfile, { - config: { - pnpmPlugin: { - removePnpmfileChecksum: true, - }, - }, - }) + const result = afterAllResolved(lockfile, { lockfileDir: root }) expect(result).toBe(lockfile) expect(result).not.toHaveProperty('pnpmfileChecksum') diff --git a/packages/skills-package-manager/src/config/schema.ts b/packages/skills-package-manager/src/config/schema.ts index 76fbfca..c60a27f 100644 --- a/packages/skills-package-manager/src/config/schema.ts +++ b/packages/skills-package-manager/src/config/schema.ts @@ -16,6 +16,17 @@ export const skillsManifestSchema = z .default([]) .describe('Directories where skill symlinks will be created'), selfSkill: z.boolean().optional().describe('Whether this project is itself a skill'), + pnpmPlugin: z + .object({ + removePnpmfileChecksum: z + .boolean() + .optional() + .describe( + 'Temporarily remove pnpmfileChecksum from pnpm lockfiles in pnpm-plugin-skills afterAllResolved', + ), + }) + .optional() + .describe('pnpm-plugin-skills specific compatibility settings'), skills: z .record(z.string(), z.string()) .default({}) diff --git a/packages/skills-package-manager/src/config/skillsManifest.ts b/packages/skills-package-manager/src/config/skillsManifest.ts index 09616ac..3b1494f 100644 --- a/packages/skills-package-manager/src/config/skillsManifest.ts +++ b/packages/skills-package-manager/src/config/skillsManifest.ts @@ -49,6 +49,7 @@ export function normalizeSkillsManifest(manifest: Partial): Skil installDir: manifest.installDir ?? '.agents/skills', linkTargets: manifest.linkTargets ?? [], selfSkill: manifest.selfSkill ?? false, + pnpmPlugin: manifest.pnpmPlugin, skills: manifest.skills ?? {}, } } diff --git a/packages/skills-package-manager/src/config/writeSkillsManifest.ts b/packages/skills-package-manager/src/config/writeSkillsManifest.ts index 04934bd..d5ed14f 100644 --- a/packages/skills-package-manager/src/config/writeSkillsManifest.ts +++ b/packages/skills-package-manager/src/config/writeSkillsManifest.ts @@ -24,6 +24,10 @@ export async function writeSkillsManifest( nextManifest.selfSkill = manifest.selfSkill } + if (manifest.pnpmPlugin !== undefined) { + nextManifest.pnpmPlugin = manifest.pnpmPlugin + } + try { await writeFile(filePath, `${JSON.stringify(nextManifest, null, 2)}\n`, 'utf8') } catch (error) { diff --git a/packages/skills-package-manager/test/manifest.test.ts b/packages/skills-package-manager/test/manifest.test.ts index d6266d7..633ce39 100644 --- a/packages/skills-package-manager/test/manifest.test.ts +++ b/packages/skills-package-manager/test/manifest.test.ts @@ -18,10 +18,36 @@ describe('manifest io', () => { $schema: DEFAULT_SCHEMA_URL, installDir: '.agents/skills', linkTargets: [], + pnpmPlugin: undefined, skills: { hello: 'link:./skills/hello' }, }) }) + it('reads pnpmPlugin config from skills.json', async () => { + const root = mkdtempSync(path.join(tmpdir(), 'skills-pm-manifest-pnpm-plugin-')) + writeFileSync( + path.join(root, 'skills.json'), + JSON.stringify( + { + installDir: '.agents/skills', + linkTargets: [], + pnpmPlugin: { + removePnpmfileChecksum: true, + }, + skills: {}, + }, + null, + 2, + ), + ) + + const manifest = await readSkillsManifest(root) + + expect(manifest?.pnpmPlugin).toEqual({ + removePnpmfileChecksum: true, + }) + }) + it('defaults selfSkill to undefined when omitted from skills.json', async () => { const root = mkdtempSync(path.join(tmpdir(), 'skills-pm-manifest-default-self-')) writeFileSync( @@ -104,6 +130,9 @@ describe('manifest validation', () => { installDir: '.custom/skills', linkTargets: ['.claude/skills'], selfSkill: true, + pnpmPlugin: { + removePnpmfileChecksum: true, + }, skills: { test: 'link:./test' }, } @@ -123,6 +152,7 @@ describe('manifest validation', () => { expect(result.data.installDir).toBe('.agents/skills') expect(result.data.linkTargets).toEqual([]) expect(result.data.selfSkill).toBeUndefined() + expect(result.data.pnpmPlugin).toBeUndefined() expect(result.data.skills).toEqual({}) } }) diff --git a/website/docs/architecture/pnpm-plugin.mdx b/website/docs/architecture/pnpm-plugin.mdx index cae9d46..ceea722 100644 --- a/website/docs/architecture/pnpm-plugin.mdx +++ b/website/docs/architecture/pnpm-plugin.mdx @@ -24,12 +24,17 @@ Then add the following to the workspace root: { "installDir": ".agents/skills", "linkTargets": [".claude/skills"], + "pnpmPlugin": { + "removePnpmfileChecksum": true + }, "skills": { "my-skill": "https://github.com/owner/repo.git#path:/skills/my-skill" } } ``` +`pnpmPlugin.removePnpmfileChecksum` is a temporary compatibility option for `pnpm-plugin-skills`. When enabled, the plugin removes `pnpmfileChecksum` from `pnpm-lock.yaml` in `afterAllResolved` to work around tools that run `pnpm install --ignore-pnpmfile`. + ## Suitable team scenarios - You want developers to run only `pnpm install` without remembering extra skill installation commands