Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
20 changes: 18 additions & 2 deletions packages/core/src/provider-extensions/installer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ function command(
input: {
dependencies?: Record<string, string>
installedIntegrity?: string
maintainers?: Array<string | { name: string }>
metadataIntegrities?: string[]
npmUser?: string | { name: string }
npmUser?: string | { name: string } | null
registerSecondProvider?: boolean
unpackedSize?: number
version?: string
Expand All @@ -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,
Expand Down Expand Up @@ -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 <npm@example.com>'],
npmUser: null,
})
const release = await inspectProviderPackage('@example/fixture', {
packagesDir,
run: fake.run,
})

assert.equal(release.publisher, 'Ian Nuttall <npm@example.com>')
})

test('package inspection rejects dependency trees before installation', async () => {
const fake = command({ dependencies: { surprise: '^1.0.0' } })
await assert.rejects(
Expand Down
10 changes: 8 additions & 2 deletions packages/core/src/provider-extensions/installer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(
Expand Down