diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 7ab71f21..5f9cb2aa 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -2,7 +2,7 @@ "name": "seo", "displayName": "seo", "description": "Local-first SEO and AI-search diagnostics. Bundles the seo MCP server plus one SEO skill that gives an agent 50+ audit and report tools without filling its context window.", - "version": "0.2.36", + "version": "0.2.37", "author": { "name": "Ian Nuttall" }, diff --git a/package.json b/package.json index 9b8249c2..b1ebb743 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "seo", - "version": "0.2.36", + "version": "0.2.37", "description": "The SEO command for AI agents. Audit sites and research search opportunities with local, evidence-backed reports.", "type": "module", "license": "Apache-2.0", diff --git a/packages/core/src/provider-extensions/installer.test.ts b/packages/core/src/provider-extensions/installer.test.ts index a7419442..684f1d49 100644 --- a/packages/core/src/provider-extensions/installer.test.ts +++ b/packages/core/src/provider-extensions/installer.test.ts @@ -34,8 +34,9 @@ function command( input: { dependencies?: Record installedIntegrity?: string + maintainers?: Array metadataIntegrities?: string[] - npmUser?: string | { name: string } + npmUser?: string | { name: string } | null registerSecondProvider?: boolean unpackedSize?: number version?: string @@ -61,7 +62,9 @@ function command( type: 'git', url: 'git+https://github.com/example/fixture.git', }, - _npmUser: input.npmUser ?? 'example', + _npmUser: + input.npmUser === null ? undefined : (input.npmUser ?? 'example'), + maintainers: input.maintainers, dependencies: input.dependencies, dist: { integrity, @@ -188,6 +191,19 @@ test('direct install accepts an exact version and rejects ranges or URLs', async assert.equal(fake.calls.length, 1) }) +test('package inspection accepts string maintainers from npm metadata', async () => { + const fake = command({ + maintainers: ['Ian Nuttall '], + npmUser: null, + }) + const release = await inspectProviderPackage('@example/fixture', { + packagesDir, + run: fake.run, + }) + + assert.equal(release.publisher, 'Ian Nuttall ') +}) + test('package inspection rejects dependency trees before installation', async () => { const fake = command({ dependencies: { surprise: '^1.0.0' } }) await assert.rejects( diff --git a/packages/core/src/provider-extensions/installer.ts b/packages/core/src/provider-extensions/installer.ts index 3d5ecf6c..f36387e7 100644 --- a/packages/core/src/provider-extensions/installer.ts +++ b/packages/core/src/provider-extensions/installer.ts @@ -52,7 +52,12 @@ const npmPackageMetadataSchema = z ]) .nullish(), maintainers: z - .array(z.object({ name: z.string().trim().max(200) }).passthrough()) + .array( + z.union([ + z.string().trim().max(200), + z.object({ name: z.string().trim().max(200) }).passthrough(), + ]), + ) .max(100) .nullish(), _npmUser: z @@ -233,7 +238,8 @@ function publisher( if (metadata._npmUser?.name) return metadata._npmUser.name if (typeof metadata.author === 'string') return metadata.author if (metadata.author?.name) return metadata.author.name - return metadata.maintainers?.[0]?.name + const maintainer = metadata.maintainers?.[0] + return typeof maintainer === 'string' ? maintainer : maintainer?.name } async function packageMetadata(