Merge branch 'codex/package-readme-limitations-audit-20260712' into codex/model-experience-readmes-20260712

This commit is contained in:
Tianyi Cui
2026-07-13 12:07:00 +08:00
33 changed files with 148 additions and 126 deletions

View File

@@ -8,10 +8,10 @@ This package owns the `ctx.skills` interface. It does not know whether skills co
### Public API
- `ctx.skills.registerProvider(provider): () => Promise<void> | void` Registers a readonly provider by unique `provider.name`. Duplicate provider names throw, and `runtime` is reserved for `ctx.skills.register(...)`. The registry borrows the provider object and invokes its methods directly. The registration is effect-scoped and HMR-safe, and the exact Cordis disposer supports ordered composite teardown.
- `ctx.skills.registerProvider(provider): () => void` Registers a readonly provider by unique `provider.name`. Duplicate provider names throw, and `runtime` is reserved for `ctx.skills.register(...)`. The registry borrows the provider object and invokes its methods directly. The registration is effect-scoped and HMR-safe, and the exact Cordis disposer supports ordered composite teardown.
- `ctx.skills.list({ cwd?, signal? })` Borrows the readonly lookup options, then returns model-invocable summaries for the current workspace, merged across providers and sorted by name.
- `ctx.skills.get(name, { cwd?, signal? })` Uses the same readonly options and winning candidate for discovery and loading, rechecks cancellation after discovery or a cache hit, races provider loading against the signal, validates the loaded definition, then returns it, including disabled-for-model skills.
- `ctx.skills.register(skill): () => Promise<void> | void` Registers a readonly runtime embedded skill, adding `provider: "runtime"` when omitted. Same-name runtime registrations are first-wins: a duplicate logs a warning and gets a no-op disposer. Successful registrations return the exact Cordis disposer for ordered composite teardown.
- `ctx.skills.register(skill): () => void` Registers a readonly runtime embedded skill, adding `provider: "runtime"` when omitted. Same-name runtime registrations are first-wins: a duplicate logs a warning and gets a no-op disposer. Successful registrations return the exact Cordis disposer for ordered composite teardown.
### Config

View File

@@ -187,7 +187,7 @@ export class SkillService extends Service {
* @returns the exact Cordis effect disposer that unregisters this provider;
* composite effects may yield it directly to preserve teardown ordering.
*/
registerProvider(provider: SkillProvider): () => Promise<void> | void {
registerProvider(provider: SkillProvider): () => void {
const name = provider.name
if (name === RUNTIME_PROVIDER) {
throw new Error(`"${RUNTIME_PROVIDER}" is reserved for runtime skill registrations`)
@@ -210,6 +210,7 @@ export class SkillService extends Service {
}
ctx.emit('skill/provider-added', provider)
}, 'skills.registerProvider()')
// eslint-disable-next-line @typescript-eslint/no-misused-promises -- synchronous cleanup; direct return preserves disposer identity
return dispose
}
@@ -225,7 +226,7 @@ export class SkillService extends Service {
* contribution and invalidates caches; composite effects may yield it
* directly to preserve teardown ordering.
*/
register(skill: SkillRegistration): () => Promise<void> | void {
register(skill: SkillRegistration): () => void {
validateRuntimeSkill(skill)
const existing = this.runtime.get(skill.name)
if (existing !== undefined) {
@@ -245,6 +246,7 @@ export class SkillService extends Service {
invalidateCache()
}
}, 'skills.register()')
// eslint-disable-next-line @typescript-eslint/no-misused-promises -- synchronous cleanup; direct return preserves disposer identity
return dispose
}

View File

@@ -103,7 +103,7 @@ describe('SkillService registry', () => {
},
})).toThrow('reserved')
await disposeMemory()
disposeMemory()
expect((await ctx.skills.list()).map(skill => skill.name)).toEqual(['same-rank-skill', 'shadowed'])
})
@@ -538,7 +538,7 @@ describe('SkillService registry', () => {
path: 'memory://runtime-skill',
metadata: { owner: 'tests' },
})
await disposeRuntime()
disposeRuntime()
await ctx.skills.list({ cwd: '/tmp/first-cache-key' })
await ctx.skills.list({ cwd: '/tmp/second-cache-key' })
@@ -615,7 +615,7 @@ describe('SkillService registry', () => {
const pending = ctx.skills.list()
await started
await dispose()
dispose()
release?.()
expect(await pending).toEqual([])
@@ -673,9 +673,9 @@ describe('SkillService registry', () => {
const disposeFirst = ctx.skills.register({ name: 'same-skill', description: 'First', source: 'runtime', content: 'first' })
const disposeSecond = ctx.skills.register({ name: 'same-skill', description: 'Second', source: 'runtime', content: 'second' })
await disposeSecond()
disposeSecond()
expect((await ctx.skills.get('same-skill'))?.description).toBe('First')
await disposeFirst()
disposeFirst()
expect(await ctx.skills.get('same-skill')).toBeUndefined()
})
})