Skip to content

Commit 50eb11c

Browse files
authored
feat: add pnpmPlugin.removePnpmfileCheckSum temporary pnpmfile checksum compatibility for renovate (#14)
## Summary - add a temporary `pnpmPlugin.removePnpmfileChecksum` option to `skills.json` so `pnpm-plugin-skills` can remove `pnpmfileChecksum` from `pnpm-lock.yaml` in `afterAllResolved` - restore preview release change detection by removing the incorrect `predicate-quantifier: 'every'` setting from the preview workflow - add and update manifest/plugin tests plus pnpm plugin documentation for the new manifest-based configuration ## Related Links - renovatebot/renovate#30812 ## Checklist <!-- Check and mark with an "x" --> - [x] Tests updated (or not required). - [x] Documentation updated (or not required).
1 parent 510a7c2 commit 50eb11c

File tree

11 files changed

+148
-5
lines changed

11 files changed

+148
-5
lines changed

.github/workflows/preview.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ jobs:
3232
- uses: dorny/paths-filter@d1c1ffe0248fe513906c8e24db8ea791d46f8590 # v3.0.3
3333
id: changes
3434
with:
35-
predicate-quantifier: 'every'
3635
filters: |
3736
changed:
3837
- "packages/**"

packages/pnpm-plugin-skills/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,17 @@ Then create a `skills.json` in your project root:
2626
{
2727
"installDir": ".agents/skills",
2828
"linkTargets": [".claude/skills"],
29+
"pnpmPlugin": {
30+
"removePnpmfileChecksum": true
31+
},
2932
"skills": {
3033
"my-skill": "https://github.com/owner/repo.git#path:/skills/my-skill"
3134
}
3235
}
3336
```
3437

38+
`pnpmPlugin.removePnpmfileChecksum` is a temporary compatibility switch for repositories that need `pnpm-plugin-skills` to remove `pnpmfileChecksum` from `pnpm-lock.yaml` in `afterAllResolved`.
39+
3540
Now `pnpm install` will automatically install your skills.
3641

3742
## Architecture
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
// pnpm v10
2-
const { preResolution } = require('./dist/index.js')
2+
const { afterAllResolved, preResolution } = require('./dist/index.js')
33

44
module.exports = {
55
hooks: {
6+
afterAllResolved,
67
preResolution,
78
},
89
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
// pnpm v11
2-
import { preResolution } from './dist/index.mjs'
2+
import { afterAllResolved, preResolution } from './dist/index.mjs'
33

4-
export const hooks = { preResolution }
4+
export const hooks = { afterAllResolved, preResolution }

packages/pnpm-plugin-skills/src/index.ts

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,19 @@
1-
import { installCommand } from 'skills-package-manager'
1+
import { readFileSync } from 'node:fs'
2+
import path from 'node:path'
3+
import { installCommand, type SkillsManifest } from 'skills-package-manager'
4+
5+
function readPluginManifest(rootDir: string): SkillsManifest | null {
6+
const filePath = path.join(rootDir, 'skills.json')
7+
8+
try {
9+
const parsed = JSON.parse(readFileSync(filePath, 'utf8')) as {
10+
pnpmPlugin?: SkillsManifest['pnpmPlugin']
11+
}
12+
return parsed as SkillsManifest
13+
} catch {
14+
return null
15+
}
16+
}
217

318
export async function preResolution(
419
options: { lockfileDir?: string; workspaceRoot?: string } = {},
@@ -11,3 +26,28 @@ export async function preResolution(
1126
await installCommand({ cwd: lockfileDir })
1227
return undefined
1328
}
29+
30+
export function afterAllResolved(
31+
lockfile: Record<string, unknown>,
32+
context: { lockfileDir?: string; workspaceDir?: string } = {},
33+
) {
34+
const manifestRoot = context.lockfileDir ?? context.workspaceDir
35+
if (!manifestRoot) {
36+
return lockfile
37+
}
38+
39+
const manifest = readPluginManifest(manifestRoot)
40+
if (!manifest) {
41+
return lockfile
42+
}
43+
44+
if (manifest.pnpmPlugin?.removePnpmfileChecksum !== true) {
45+
return lockfile
46+
}
47+
48+
if ('pnpmfileChecksum' in lockfile) {
49+
delete lockfile.pnpmfileChecksum
50+
}
51+
52+
return lockfile
53+
}

packages/pnpm-plugin-skills/test/index.test.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,50 @@ describe('preResolution', () => {
6565
expect(existsSync(path.join(root, '.claude/skills/hello-skill'))).toBe(true)
6666
})
6767
})
68+
69+
describe('afterAllResolved', () => {
70+
it('removes pnpmfileChecksum when enabled in skills.json', async () => {
71+
const { afterAllResolved } = await import('../src/index')
72+
const root = mkdtempSync(path.join(tmpdir(), 'pnpm-plugin-skills-config-'))
73+
74+
writeFileSync(
75+
path.join(root, 'skills.json'),
76+
JSON.stringify(
77+
{
78+
installDir: '.agents/skills',
79+
linkTargets: [],
80+
pnpmPlugin: {
81+
removePnpmfileChecksum: true,
82+
},
83+
skills: {},
84+
},
85+
null,
86+
2,
87+
),
88+
)
89+
90+
const lockfile = {
91+
lockfileVersion: '9.0',
92+
pnpmfileChecksum: 'checksum-to-remove',
93+
}
94+
95+
const result = afterAllResolved(lockfile, { lockfileDir: root })
96+
97+
expect(result).toBe(lockfile)
98+
expect(result).not.toHaveProperty('pnpmfileChecksum')
99+
})
100+
101+
it('keeps pnpmfileChecksum by default', async () => {
102+
const { afterAllResolved } = await import('../src/index')
103+
104+
const lockfile = {
105+
lockfileVersion: '9.0',
106+
pnpmfileChecksum: 'checksum-to-keep',
107+
}
108+
109+
const result = afterAllResolved(lockfile, {})
110+
111+
expect(result).toBe(lockfile)
112+
expect(result).toHaveProperty('pnpmfileChecksum', 'checksum-to-keep')
113+
})
114+
})

packages/skills-package-manager/src/config/schema.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,17 @@ export const skillsManifestSchema = z
1616
.default([])
1717
.describe('Directories where skill symlinks will be created'),
1818
selfSkill: z.boolean().optional().describe('Whether this project is itself a skill'),
19+
pnpmPlugin: z
20+
.object({
21+
removePnpmfileChecksum: z
22+
.boolean()
23+
.optional()
24+
.describe(
25+
'Temporarily remove pnpmfileChecksum from pnpm lockfiles in pnpm-plugin-skills afterAllResolved',
26+
),
27+
})
28+
.optional()
29+
.describe('pnpm-plugin-skills specific compatibility settings'),
1930
skills: z
2031
.record(z.string(), z.string())
2132
.default({})

packages/skills-package-manager/src/config/skillsManifest.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export function normalizeSkillsManifest(manifest: Partial<SkillsManifest>): Skil
4949
installDir: manifest.installDir ?? '.agents/skills',
5050
linkTargets: manifest.linkTargets ?? [],
5151
selfSkill: manifest.selfSkill ?? false,
52+
pnpmPlugin: manifest.pnpmPlugin,
5253
skills: manifest.skills ?? {},
5354
}
5455
}

packages/skills-package-manager/src/config/writeSkillsManifest.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ export async function writeSkillsManifest(
2424
nextManifest.selfSkill = manifest.selfSkill
2525
}
2626

27+
if (manifest.pnpmPlugin !== undefined) {
28+
nextManifest.pnpmPlugin = manifest.pnpmPlugin
29+
}
30+
2731
try {
2832
await writeFile(filePath, `${JSON.stringify(nextManifest, null, 2)}\n`, 'utf8')
2933
} catch (error) {

packages/skills-package-manager/test/manifest.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,36 @@ describe('manifest io', () => {
1818
$schema: DEFAULT_SCHEMA_URL,
1919
installDir: '.agents/skills',
2020
linkTargets: [],
21+
pnpmPlugin: undefined,
2122
skills: { hello: 'link:./skills/hello' },
2223
})
2324
})
2425

26+
it('reads pnpmPlugin config from skills.json', async () => {
27+
const root = mkdtempSync(path.join(tmpdir(), 'skills-pm-manifest-pnpm-plugin-'))
28+
writeFileSync(
29+
path.join(root, 'skills.json'),
30+
JSON.stringify(
31+
{
32+
installDir: '.agents/skills',
33+
linkTargets: [],
34+
pnpmPlugin: {
35+
removePnpmfileChecksum: true,
36+
},
37+
skills: {},
38+
},
39+
null,
40+
2,
41+
),
42+
)
43+
44+
const manifest = await readSkillsManifest(root)
45+
46+
expect(manifest?.pnpmPlugin).toEqual({
47+
removePnpmfileChecksum: true,
48+
})
49+
})
50+
2551
it('defaults selfSkill to undefined when omitted from skills.json', async () => {
2652
const root = mkdtempSync(path.join(tmpdir(), 'skills-pm-manifest-default-self-'))
2753
writeFileSync(
@@ -104,6 +130,9 @@ describe('manifest validation', () => {
104130
installDir: '.custom/skills',
105131
linkTargets: ['.claude/skills'],
106132
selfSkill: true,
133+
pnpmPlugin: {
134+
removePnpmfileChecksum: true,
135+
},
107136
skills: { test: 'link:./test' },
108137
}
109138

@@ -123,6 +152,7 @@ describe('manifest validation', () => {
123152
expect(result.data.installDir).toBe('.agents/skills')
124153
expect(result.data.linkTargets).toEqual([])
125154
expect(result.data.selfSkill).toBeUndefined()
155+
expect(result.data.pnpmPlugin).toBeUndefined()
126156
expect(result.data.skills).toEqual({})
127157
}
128158
})

0 commit comments

Comments
 (0)