Skip to content
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
15668ee
feat: add license metadata to skill frontmatter schema
jack-piplabs Mar 4, 2026
a5d37ec
fix: wire license into inspect output and fix false upload warning
jack-piplabs Mar 4, 2026
716b29c
fix: handle structured license frontmatter and advanced terms toggle
jack-piplabs Mar 4, 2026
d274a03
fix: wire license into API schema, fix frontmatter regex, and style w…
jack-piplabs Mar 4, 2026
a5190f4
fix: preserve advanced terms on collapse and use version-specific lic…
jack-piplabs Mar 4, 2026
376050d
fix: don't fall back to latest license when inspecting a specific ver…
jack-piplabs Mar 4, 2026
2a4d962
fix: include license in list API and handle CRLF in frontmatter parsing
jack-piplabs Mar 4, 2026
35514f2
fix: use proper YAML parsing for frontmatter license and correct docs
jack-piplabs Mar 4, 2026
082308e
fix: show "All checks passed" when no errors, render warnings separately
jack-piplabs Mar 4, 2026
9b548d9
feat: default license to MIT and remove no-license warning
jack-piplabs Mar 4, 2026
d86f1e4
fix: fall back to frontmatter license when client payload is invalid
jack-piplabs Mar 4, 2026
a424f13
fix: only send license in payload when user interacts with selector
jack-piplabs Mar 4, 2026
53a574b
fix: omit license from soul publish payload and seed advanced editor …
jack-piplabs Mar 4, 2026
272cc74
fix: allow explicitly clearing frontmatter license from upload form
jack-piplabs Mar 4, 2026
1648cd9
fix: preserve full license terms from frontmatter and compare deeply
jack-piplabs Mar 4, 2026
d9d888f
refactor: make frontmatter the single source of truth for license
jack-piplabs Mar 4, 2026
ad9c1d4
fix: don't emit invalid license object when entering custom mode
jack-piplabs Mar 4, 2026
8cd499c
Revert "fix: don't emit invalid license object when entering custom m…
jack-piplabs Mar 4, 2026
05236c3
fix: harden custom license mode UX
jack-piplabs Mar 4, 2026
54f23d4
fix: rename ambiguous "Remove" button to "Clear license"
jack-piplabs Mar 4, 2026
0dfbe7f
fix: use full SkillLicenseSchema in API response types
jack-piplabs Mar 4, 2026
5052b2a
fix: guard stale frontmatter reads and show unknown SPDX in dropdown
jack-piplabs Mar 4, 2026
f3252f8
fix: address code review feedback on license metadata
jack-piplabs Mar 4, 2026
48c4ead
fix: invalidate stale frontmatter reads when SKILL.md disappears
jack-piplabs Mar 4, 2026
658a8c8
fix: invalidate stale frontmatter reads on soul mode transition
jack-piplabs Mar 4, 2026
eb71713
fix: preserve full SkillLicense from structured frontmatter
jack-piplabs Mar 4, 2026
dd5698a
fix: clear stale license state when switching to folder without front…
jack-piplabs Mar 4, 2026
6fcdfee
fix: rebuild schema dist to sync CC-BY-NC commercialAttribution preset
jack-piplabs Mar 4, 2026
f53d4fd
fix: don't silently fall back to frontmatter license when explicit li…
jack-piplabs Mar 4, 2026
c75305d
fix: use SkillLicense type for license cast, align test with no-fallb…
jack-piplabs Mar 4, 2026
acdce40
fix: preserve license field during summary backfill
jack-piplabs Mar 4, 2026
3e27390
fix: use SkillLicense type for ListSkillsResult license annotation
jack-piplabs Mar 4, 2026
0f029ba
fix: don't clear user-entered license when frontmatter effect re-fires
jack-piplabs Mar 4, 2026
c294653
fix: guard frontmatter async callback against stale license closure
jack-piplabs Mar 4, 2026
d3946b2
fix: widen PublicSkillListVersion license type to canonical SkillLicense
jack-piplabs Mar 5, 2026
d971220
fix: widen CLI inspect license annotations to canonical SkillLicense
jack-piplabs Mar 5, 2026
9c6e912
fix: print all SkillLicense boolean fields in CLI inspect output
jack-piplabs Mar 5, 2026
310a8e5
fix: suppress empty-SPDX license hint and reject empty-string in fron…
jack-piplabs Mar 5, 2026
8f7c206
fix: preserve license during backfill & scope licenseTouchedRef reset
jack-piplabs Mar 5, 2026
393763b
fix: reject control chars and whitespace in SPDX identifiers
jack-piplabs Mar 5, 2026
21f0a4d
fix: validate SPDX token format in LicenseSelector frontend
jack-piplabs Mar 5, 2026
42b383d
fix: clear stale license when advanced SPDX becomes invalid
jack-piplabs Mar 5, 2026
8ac51d7
docs: update PR workflow to use comment-based review triggers
jack-piplabs Mar 5, 2026
e80d23b
fix: validate detected SPDX in upload frontmatter probe
jack-piplabs Mar 5, 2026
a177609
fix: align backfill validator with ParsedSkillData and add SPDX lengt…
jack-piplabs Mar 5, 2026
a8ca253
fix: gate publish on frontmatter license detection to prevent race
jack-piplabs Mar 5, 2026
0668dca
fix: inline SkillLicenseSchema in CLI to drop workspace:* dep
jack-piplabs Mar 5, 2026
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
5 changes: 4 additions & 1 deletion convex/httpApiV1/skillsV1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ type ListSkillsResult = {
version: string
createdAt: number
changelog: string
parsed?: { clawdis?: { os?: string[]; nix?: { plugin?: boolean; systems?: string[] } } }
parsed?: { clawdis?: { os?: string[]; nix?: { plugin?: boolean; systems?: string[] } }; license?: { spdx: string; uri?: string } }
} | null
}>
nextCursor: string | null
Expand Down Expand Up @@ -213,6 +213,7 @@ export async function listSkillsV1Handler(ctx: ActionCtx, request: Request) {
systems: item.latestVersion.parsed.clawdis.nix?.systems ?? null,
}
: null,
license: item.latestVersion?.parsed?.license ?? null,
}))

return json({ items, nextCursor: result.nextCursor ?? null }, 200, rate.headers)
Expand Down Expand Up @@ -309,6 +310,7 @@ export async function skillsGetRouterV1Handler(ctx: ActionCtx, request: Request)
systems: result.latestVersion.parsed.clawdis.nix?.systems ?? null,
}
: null,
license: result.latestVersion?.parsed?.license ?? null,
owner: result.owner
? {
handle: result.owner.handle ?? null,
Expand Down Expand Up @@ -417,6 +419,7 @@ export async function skillsGetRouterV1Handler(ctx: ActionCtx, request: Request)
contentType: file.contentType ?? null,
})),
security,
license: version.parsed?.license ?? null,
},
},
200,
Expand Down
22 changes: 22 additions & 0 deletions convex/lib/skillPublish.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,26 @@ description: Expert guidance for sushi-rolls.
expect(signals.bodyWords).toBeGreaterThanOrEqual(45)
expect(quality.decision).toBe('pass')
})

describe('resolveLicense', () => {
it('uses explicit args.license when valid', () => {
const license = __test.resolveLicense({ spdx: 'Apache-2.0' }, { license: 'MIT' })
expect(license).toEqual(expect.objectContaining({ spdx: 'Apache-2.0' }))
})

it('falls back to frontmatter when args.license is null', () => {
const license = __test.resolveLicense(null, { license: 'MIT' })
expect(license).toEqual(expect.objectContaining({ spdx: 'MIT' }))
})

it('falls back to frontmatter when args.license is invalid', () => {
const license = __test.resolveLicense({ bad: 'data' }, { license: 'MIT' })
expect(license).toEqual(expect.objectContaining({ spdx: 'MIT' }))
})

it('returns undefined when both are invalid', () => {
const license = __test.resolveLicense({ bad: 'data' }, {})
expect(license).toBeUndefined()
})
})
})
9 changes: 9 additions & 0 deletions convex/lib/skillPublish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
isTextFile,
parseClawdisMetadata,
parseFrontmatter,
parseLicenseField,
sanitizePath,
} from './skills'
import type { WebhookSkillPayload } from './webhooks'
Expand All @@ -46,6 +47,7 @@ export type PublishVersionArgs = {
version: string
changelog: string
tags?: string[]
license?: unknown
forkOf?: { slug: string; version?: string }
source?: {
kind: 'github'
Expand Down Expand Up @@ -130,6 +132,7 @@ export async function publishVersionForUser(
const readmeText = await fetchText(ctx, readmeFile.storageId)
const frontmatter = parseFrontmatter(readmeText)
const clawdis = parseClawdisMetadata(frontmatter)
const license = resolveLicense(args.license, frontmatter)
const owner = (await ctx.runQuery(internal.users.getByIdInternal, {
userId,
})) as Doc<'users'> | null
Expand Down Expand Up @@ -268,6 +271,7 @@ export async function publishVersionForUser(
frontmatter,
metadata,
clawdis,
license,
},
summary,
embedding,
Expand Down Expand Up @@ -356,11 +360,16 @@ function mergeSourceIntoMetadata(
return Object.keys(base).length ? base : undefined
}

function resolveLicense(argsLicense: unknown, frontmatter: Record<string, unknown>) {
return (argsLicense != null ? parseLicenseField({ license: argsLicense }) : undefined) ?? parseLicenseField(frontmatter)
}

export const __test = {
mergeSourceIntoMetadata,
computeQualitySignals,
evaluateQuality,
toStructuralFingerprint,
resolveLicense,
}

export async function queueHighlightedWebhook(ctx: MutationCtx, skillId: Id<'skills'>) {
Expand Down
Loading