release-skill 0.7.3 → 0.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codebuddy-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +2 -2
- package/.kimi-plugin/plugin.json +1 -1
- package/CHANGELOG.md +11 -0
- package/INSTALL.md +2 -2
- package/INSTALL.zh-CN.md +2 -2
- package/README.md +7 -7
- package/README.zh-CN.md +6 -6
- package/adapters/claude/.claude-plugin/marketplace.json +1 -1
- package/adapters/claude/.claude-plugin/plugin.json +1 -1
- package/adapters/claude/bin/release-skill.bundle.mjs +57 -13
- package/adapters/codex/.codex-plugin/plugin.json +2 -2
- package/adapters/codex/bin/release-skill.bundle.mjs +57 -13
- package/adapters/kimi/.kimi-plugin/plugin.json +1 -1
- package/adapters/kimi/bin/release-skill.bundle.mjs +57 -13
- package/adapters/workbuddy/.codebuddy-plugin/plugin.json +1 -1
- package/adapters/workbuddy/bin/release-skill.bundle.mjs +57 -13
- package/bin/release-skill.bundle.mjs +57 -13
- package/package.json +1 -1
- package/platform-manifest.json +2 -2
- package/src/core/skill-resource-closure.mjs +29 -3
- package/src/platforms/registry.mjs +31 -0
|
@@ -13,8 +13,9 @@ import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
|
13
13
|
import { canonicalJson, digestDocument } from 'skill-family-contracts';
|
|
14
14
|
import { GATE_FAILED, ReleaseError } from './errors.mjs';
|
|
15
15
|
import { computeFrozenSnapshot } from '../snapshot/frozen.mjs';
|
|
16
|
+
import { resolveSkillProjectionSurfaceHost } from '../platforms/registry.mjs';
|
|
16
17
|
|
|
17
|
-
export const CHECKER_VERSION = 'skill-resource-closure-
|
|
18
|
+
export const CHECKER_VERSION = 'skill-resource-closure-v3';
|
|
18
19
|
|
|
19
20
|
const BARE_PREFIXES = ['references/', 'assets/', 'schemas/', 'examples/', 'scripts/'];
|
|
20
21
|
const EXPLICIT_PREFIXES = [
|
|
@@ -32,6 +33,7 @@ const MACHINE_ABSOLUTE_PATTERN = /^(?:\/(?:Users|home|root|tmp|var|etc|opt)(?:\/
|
|
|
32
33
|
// isPathToken, so prose words are never matched; fail-closed over silent miss.
|
|
33
34
|
const HOME_DIRECTORY_PATTERN = /^(?:~|\$HOME|\$\{HOME\})\/.+/u;
|
|
34
35
|
const ADAPTER_SURFACE_PATTERN = /^adapters\/([^/]+)$/u;
|
|
36
|
+
const PLATFORM_SURFACE_PATTERN = /^platforms\/(.+)$/u;
|
|
35
37
|
// G3: skill-local resource closure directories. Regular files inside these
|
|
36
38
|
// directories (under a skill directory) must be referenced by a SKILL.md of
|
|
37
39
|
// the same surface or they are reported as stale on adapter surfaces.
|
|
@@ -58,6 +60,7 @@ export const CLASSIFICATION = Object.freeze({
|
|
|
58
60
|
OUT_OF_BOUNDS: 'out_of_bounds',
|
|
59
61
|
RESOURCE_DRIFT: 'resource_drift',
|
|
60
62
|
STALE_RESOURCE: 'stale_resource',
|
|
63
|
+
UNKNOWN_PLATFORM_SURFACE: 'unknown_platform_surface',
|
|
61
64
|
});
|
|
62
65
|
|
|
63
66
|
export const FINDING_CODE = Object.freeze({
|
|
@@ -71,6 +74,7 @@ export const FINDING_CODE = Object.freeze({
|
|
|
71
74
|
SYMLINK_NOT_ALLOWED: 'SYMLINK_NOT_ALLOWED',
|
|
72
75
|
RESOURCE_DRIFT: 'RESOURCE_DRIFT',
|
|
73
76
|
STALE_RESOURCE: 'STALE_RESOURCE',
|
|
77
|
+
UNKNOWN_PLATFORM_SURFACE: 'UNKNOWN_PLATFORM_SURFACE',
|
|
74
78
|
});
|
|
75
79
|
|
|
76
80
|
function toPosix(value) {
|
|
@@ -281,8 +285,14 @@ async function collectRegularFiles(dir, results) {
|
|
|
281
285
|
}
|
|
282
286
|
|
|
283
287
|
function inferSurfaceHost(surfaceId, defaultHost) {
|
|
284
|
-
const
|
|
285
|
-
return
|
|
288
|
+
const adapterMatch = ADAPTER_SURFACE_PATTERN.exec(surfaceId);
|
|
289
|
+
if (adapterMatch) return adapterMatch[1];
|
|
290
|
+
const platformMatch = PLATFORM_SURFACE_PATTERN.exec(surfaceId);
|
|
291
|
+
if (platformMatch) {
|
|
292
|
+
return resolveSkillProjectionSurfaceHost(surfaceId)
|
|
293
|
+
?? `unknown-platform:${platformMatch[1]}`;
|
|
294
|
+
}
|
|
295
|
+
return defaultHost;
|
|
286
296
|
}
|
|
287
297
|
|
|
288
298
|
function receiptProjection(result) {
|
|
@@ -413,6 +423,7 @@ export async function checkSkillResourceClosure({
|
|
|
413
423
|
const pluginRoot = resolvePluginRoot(skill, scanRoot);
|
|
414
424
|
const surfaceId = relativeStable(scanRoot, pluginRoot);
|
|
415
425
|
const surfaceHost = inferSurfaceHost(surfaceId, host);
|
|
426
|
+
const firstSkillOnSurface = !surfaceMap.has(surfaceId);
|
|
416
427
|
const surface = surfaceMap.get(surfaceId) ?? {
|
|
417
428
|
id: surfaceId,
|
|
418
429
|
host: surfaceHost,
|
|
@@ -422,6 +433,21 @@ export async function checkSkillResourceClosure({
|
|
|
422
433
|
};
|
|
423
434
|
surface.skillCount += 1;
|
|
424
435
|
surfaceMap.set(surfaceId, surface);
|
|
436
|
+
if (firstSkillOnSurface
|
|
437
|
+
&& PLATFORM_SURFACE_PATTERN.test(surfaceId)
|
|
438
|
+
&& resolveSkillProjectionSurfaceHost(surfaceId) === null) {
|
|
439
|
+
findings.push({
|
|
440
|
+
host: surfaceHost,
|
|
441
|
+
surface: surfaceId,
|
|
442
|
+
skill: null,
|
|
443
|
+
line: null,
|
|
444
|
+
reference: surfaceId,
|
|
445
|
+
classification: CLASSIFICATION.UNKNOWN_PLATFORM_SURFACE,
|
|
446
|
+
resolutionRoot: surfaceId,
|
|
447
|
+
resolvedTarget: surfaceId,
|
|
448
|
+
code: FINDING_CODE.UNKNOWN_PLATFORM_SURFACE,
|
|
449
|
+
});
|
|
450
|
+
}
|
|
425
451
|
if (!skillsBySurface.has(surfaceId)) skillsBySurface.set(surfaceId, []);
|
|
426
452
|
skillsBySurface.get(surfaceId).push(skill);
|
|
427
453
|
|
|
@@ -134,6 +134,7 @@ function codexCrossValidateListEntry(found, action) {
|
|
|
134
134
|
|
|
135
135
|
const CLAUDE = Object.freeze({
|
|
136
136
|
id: 'claude',
|
|
137
|
+
skillProjectionSurface: 'platforms/claude-code',
|
|
137
138
|
distributionType: 'claude-plugin',
|
|
138
139
|
actionType: 'claude-marketplace-install',
|
|
139
140
|
// Runtime adapter implementing this platform's marketplace-install action.
|
|
@@ -199,6 +200,7 @@ const CLAUDE = Object.freeze({
|
|
|
199
200
|
|
|
200
201
|
const CODEX = Object.freeze({
|
|
201
202
|
id: 'codex',
|
|
203
|
+
skillProjectionSurface: 'platforms/codex',
|
|
202
204
|
distributionType: 'codex-plugin',
|
|
203
205
|
actionType: 'codex-marketplace-install',
|
|
204
206
|
adapter: 'plugin-marketplace',
|
|
@@ -260,6 +262,7 @@ const CODEX = Object.freeze({
|
|
|
260
262
|
|
|
261
263
|
const KIMI = Object.freeze({
|
|
262
264
|
id: 'kimi',
|
|
265
|
+
skillProjectionSurface: 'platforms/kimi-code',
|
|
263
266
|
distributionType: 'kimi-plugin',
|
|
264
267
|
actionType: 'kimi-marketplace-install',
|
|
265
268
|
adapter: 'plugin-marketplace',
|
|
@@ -333,6 +336,7 @@ const KIMI = Object.freeze({
|
|
|
333
336
|
// (distributionType/actionType/consumer) stays `codebuddy`.
|
|
334
337
|
const CODEBUDDY = Object.freeze({
|
|
335
338
|
id: 'codebuddy',
|
|
339
|
+
skillProjectionSurface: 'platforms/workbuddy',
|
|
336
340
|
distributionType: 'codebuddy-plugin',
|
|
337
341
|
actionType: 'codebuddy-marketplace-install',
|
|
338
342
|
adapter: 'plugin-marketplace',
|
|
@@ -418,6 +422,7 @@ const VALID_REF_STRENGTHS = new Set(['commit-sha', 'name-ref', 'unfixable']);
|
|
|
418
422
|
const VALID_OUTPUT_PROTOCOLS = new Set(['structured', 'text', 'none']);
|
|
419
423
|
const VALID_IDENTITY_EVIDENCE = new Set(['list-record', 'install-output', 'filesystem-payload', 'human-attestation']);
|
|
420
424
|
const VALID_DEGRADATION_POLICIES = new Set(['block', 'human-attestation', 'human-attestation-with-fallback']);
|
|
425
|
+
const SKILL_PROJECTION_SURFACE_PATTERN = /^platforms\/[a-z0-9]+(?:-[a-z0-9]+)*$/u;
|
|
421
426
|
|
|
422
427
|
/**
|
|
423
428
|
* Look up a platform descriptor by id.
|
|
@@ -431,6 +436,20 @@ export function getPlatform(id) {
|
|
|
431
436
|
return platform;
|
|
432
437
|
}
|
|
433
438
|
|
|
439
|
+
/**
|
|
440
|
+
* Resolve a declared skill projection surface to its public host name.
|
|
441
|
+
* The host is always derived from the descriptor's authoritative
|
|
442
|
+
* `buildAdapter.name`; unknown surfaces fail closed with `null`.
|
|
443
|
+
*
|
|
444
|
+
* @param {string} surfaceId
|
|
445
|
+
* @param {object[]} [registry]
|
|
446
|
+
* @returns {string|null}
|
|
447
|
+
*/
|
|
448
|
+
export function resolveSkillProjectionSurfaceHost(surfaceId, registry = PLATFORMS) {
|
|
449
|
+
const platform = registry.find((item) => item.skillProjectionSurface === surfaceId);
|
|
450
|
+
return platform?.buildAdapter?.name ?? null;
|
|
451
|
+
}
|
|
452
|
+
|
|
434
453
|
/**
|
|
435
454
|
* Validate a registry (defaults to the module registry). Throws on the first
|
|
436
455
|
* malformed description so a registry typo fails at import time.
|
|
@@ -439,6 +458,7 @@ export function getPlatform(id) {
|
|
|
439
458
|
*/
|
|
440
459
|
export function assertRegistry(registry = PLATFORMS) {
|
|
441
460
|
const seen = new Set();
|
|
461
|
+
const seenSkillProjectionSurfaces = new Set();
|
|
442
462
|
for (const platform of registry) {
|
|
443
463
|
const label = platform?.id ?? '<missing id>';
|
|
444
464
|
if (typeof platform?.id !== 'string' || platform.id.length === 0) {
|
|
@@ -490,6 +510,17 @@ export function assertRegistry(registry = PLATFORMS) {
|
|
|
490
510
|
if (typeof platform.buildAdapter.name !== 'string' || platform.buildAdapter.name.length === 0) {
|
|
491
511
|
throw new Error(`platform registry: ${label} buildAdapter needs a non-empty name (its adapter directory name)`);
|
|
492
512
|
}
|
|
513
|
+
if (typeof platform.skillProjectionSurface !== 'string'
|
|
514
|
+
|| !SKILL_PROJECTION_SURFACE_PATTERN.test(platform.skillProjectionSurface)) {
|
|
515
|
+
throw new Error(`platform registry: ${label} skillProjectionSurface must match platforms/<slug>`);
|
|
516
|
+
}
|
|
517
|
+
if (seenSkillProjectionSurfaces.has(platform.skillProjectionSurface)) {
|
|
518
|
+
throw new Error(`platform registry: duplicate skillProjectionSurface "${platform.skillProjectionSurface}"; aliases must be unique`);
|
|
519
|
+
}
|
|
520
|
+
seenSkillProjectionSurfaces.add(platform.skillProjectionSurface);
|
|
521
|
+
if (resolveSkillProjectionSurfaceHost(platform.skillProjectionSurface, registry) !== platform.buildAdapter.name) {
|
|
522
|
+
throw new Error(`platform registry: ${label} skillProjectionSurface must map to buildAdapter.name`);
|
|
523
|
+
}
|
|
493
524
|
if (!VALID_LIST_OUTPUTS.has(platform.jsonProtocol?.listOutput)) {
|
|
494
525
|
throw new Error(`platform registry: ${label} has illegal jsonProtocol.listOutput "${platform.jsonProtocol?.listOutput}"`);
|
|
495
526
|
}
|