release-skill 0.2.2 → 0.2.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 +4 -2
- package/.codex-plugin/plugin.json +2 -2
- package/.kimi-plugin/plugin.json +1 -1
- package/CHANGELOG.md +41 -0
- package/CONTRIBUTING.md +27 -0
- package/INSTALL.md +95 -139
- package/INSTALL.zh-CN.md +70 -121
- package/README.md +265 -916
- package/README.zh-CN.md +222 -537
- 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 +18710 -16694
- package/adapters/claude/schemas/release-plan.schema.json +137 -0
- package/adapters/claude/schemas/release-project.schema.json +93 -0
- package/adapters/claude/schemas/release-run.schema.json +70 -2
- package/adapters/codex/.codex-plugin/plugin.json +2 -2
- package/adapters/codex/bin/release-skill.bundle.mjs +18710 -16694
- package/adapters/codex/schemas/release-plan.schema.json +137 -0
- package/adapters/codex/schemas/release-project.schema.json +93 -0
- package/adapters/codex/schemas/release-run.schema.json +70 -2
- package/adapters/kimi/.kimi-plugin/plugin.json +1 -1
- package/adapters/kimi/bin/release-skill.bundle.mjs +18710 -16694
- package/adapters/kimi/schemas/release-plan.schema.json +137 -0
- package/adapters/kimi/schemas/release-project.schema.json +93 -0
- package/adapters/kimi/schemas/release-run.schema.json +70 -2
- package/adapters/workbuddy/.codebuddy-plugin/plugin.json +1 -1
- package/adapters/workbuddy/bin/release-skill.bundle.mjs +18710 -16694
- package/adapters/workbuddy/schemas/release-plan.schema.json +137 -0
- package/adapters/workbuddy/schemas/release-project.schema.json +93 -0
- package/adapters/workbuddy/schemas/release-run.schema.json +70 -2
- package/bin/release-skill.bundle.mjs +18710 -16694
- package/package.json +1 -1
- package/schemas/release-plan.schema.json +137 -0
- package/schemas/release-project.schema.json +93 -0
- package/schemas/release-run.schema.json +70 -2
- package/src/adapters/plugin-marketplace.mjs +1602 -605
- package/src/commands/prepare.mjs +441 -32
- package/src/commands/publish.mjs +107 -75
- package/src/commands/reconcile.mjs +92 -327
- package/src/commands/setup.mjs +148 -20
- package/src/commands/verify.mjs +315 -25
- package/src/core/baseline.mjs +21 -1
- package/src/core/checkpoints.mjs +50 -7
- package/src/core/config.mjs +15 -0
- package/src/core/errors.mjs +2 -0
- package/src/core/installation-contract.mjs +341 -0
- package/src/core/plan.mjs +307 -6
- package/src/platforms/codebuddy.mjs +191 -238
- package/src/platforms/codex.mjs +369 -0
- package/src/platforms/kimi.mjs +164 -119
- package/src/platforms/registry.mjs +180 -4
- package/src/producers/build-adapters.mjs +9 -2
package/src/commands/setup.mjs
CHANGED
|
@@ -23,6 +23,7 @@ import addFormats from 'ajv-formats';
|
|
|
23
23
|
|
|
24
24
|
import { canonicalJson, sha256Hex } from '../core/digest.mjs';
|
|
25
25
|
import { acquireProjectLock } from '../artifacts/project-lock.mjs';
|
|
26
|
+
import { getPlatform, PLATFORMS } from '../platforms/registry.mjs';
|
|
26
27
|
import {
|
|
27
28
|
CONFIG_EXISTS,
|
|
28
29
|
CONFIG_INVALID,
|
|
@@ -38,6 +39,57 @@ const SKIP_DIRS = new Set([
|
|
|
38
39
|
'node_modules', 'dist', 'coverage', 'build', 'out', 'tmp', 'temp',
|
|
39
40
|
'runs', 'test', 'tests', 'test-fixtures', 'fixtures', 'examples',
|
|
40
41
|
]);
|
|
42
|
+
// Registry-driven discovery: derive marketplace and plugin manifest paths
|
|
43
|
+
// from the platform registry. This replaces hardcoded path checks so that
|
|
44
|
+
// new platforms (e.g. Codex's .agents/plugins/marketplace.json) are
|
|
45
|
+
// automatically discovered without editing setup.mjs.
|
|
46
|
+
const DISCOVERY_MANIFEST_SUFFIXES = new Set(
|
|
47
|
+
PLATFORMS.flatMap((p) => {
|
|
48
|
+
const paths = [p.manifestPaths.plugin];
|
|
49
|
+
if (p.manifestPaths.marketplace) paths.push(p.manifestPaths.marketplace);
|
|
50
|
+
// Kimi has pluginCandidates (kimi.plugin.json, .kimi-plugin/plugin.json)
|
|
51
|
+
if (p.manifestPaths.pluginCandidates) paths.push(...p.manifestPaths.pluginCandidates);
|
|
52
|
+
return paths;
|
|
53
|
+
}),
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
// Build a map from manifest path suffix → host platform id(s).
|
|
57
|
+
// For marketplace and plugin files, this allows deterministic host detection
|
|
58
|
+
// without hardcoded path.includes() checks.
|
|
59
|
+
// Multi-value: Claude and CodeBuddy share .claude-plugin/marketplace.json;
|
|
60
|
+
// a single-value Map would overwrite one platform's identity with the other.
|
|
61
|
+
const MANIFEST_SUFFIX_HOST_MAP = new Map();
|
|
62
|
+
for (const platform of PLATFORMS) {
|
|
63
|
+
const mp = platform.manifestPaths.marketplace;
|
|
64
|
+
if (mp) {
|
|
65
|
+
const existing = MANIFEST_SUFFIX_HOST_MAP.get(mp);
|
|
66
|
+
if (existing) {
|
|
67
|
+
existing.push(platform.id);
|
|
68
|
+
} else {
|
|
69
|
+
MANIFEST_SUFFIX_HOST_MAP.set(mp, [platform.id]);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const pp = platform.manifestPaths.plugin;
|
|
73
|
+
if (pp) {
|
|
74
|
+
const existing = MANIFEST_SUFFIX_HOST_MAP.get(pp);
|
|
75
|
+
if (existing) {
|
|
76
|
+
existing.push(platform.id);
|
|
77
|
+
} else {
|
|
78
|
+
MANIFEST_SUFFIX_HOST_MAP.set(pp, [platform.id]);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (platform.manifestPaths.pluginCandidates) {
|
|
82
|
+
for (const candidate of platform.manifestPaths.pluginCandidates) {
|
|
83
|
+
const existing = MANIFEST_SUFFIX_HOST_MAP.get(candidate);
|
|
84
|
+
if (existing) {
|
|
85
|
+
existing.push(platform.id);
|
|
86
|
+
} else {
|
|
87
|
+
MANIFEST_SUFFIX_HOST_MAP.set(candidate, [platform.id]);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
41
93
|
const MAX_JSON_BYTES = 1024 * 1024;
|
|
42
94
|
const schema = JSON.parse((await readTrustedPackageResource(
|
|
43
95
|
'schemas/release-project.schema.json',
|
|
@@ -86,12 +138,7 @@ async function walkDiscoveryFiles(root, maxDepth = 8) {
|
|
|
86
138
|
/^README(?:\.|$)/i.test(child.name) ||
|
|
87
139
|
/^LICENSE(?:\.|$)/i.test(child.name) ||
|
|
88
140
|
/^CHANGELOG(?:\.|$)/i.test(child.name) ||
|
|
89
|
-
absolute.endsWith(
|
|
90
|
-
absolute.endsWith('/.codex-plugin/plugin.json') ||
|
|
91
|
-
absolute.endsWith('/.kimi-plugin/plugin.json') ||
|
|
92
|
-
absolute.endsWith('/.codebuddy-plugin/plugin.json') ||
|
|
93
|
-
absolute.endsWith('/.claude-plugin/marketplace.json') ||
|
|
94
|
-
absolute.endsWith('/.codex-plugin/marketplace.json'))
|
|
141
|
+
[...DISCOVERY_MANIFEST_SUFFIXES].some((suffix) => absolute.endsWith(`/${suffix}`)))
|
|
95
142
|
) {
|
|
96
143
|
found.push(absolute);
|
|
97
144
|
}
|
|
@@ -458,16 +505,25 @@ async function discoverFacts(root) {
|
|
|
458
505
|
const manifests = [];
|
|
459
506
|
for (const path of [...pluginFiles, ...marketplaceFiles].sort()) {
|
|
460
507
|
const value = await readJsonBounded(path, 'discovered plugin manifest');
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
508
|
+
// Registry-driven host detection: find all platforms whose manifest path
|
|
509
|
+
// suffix matches this file. Falls back to 'codex' for unknown paths
|
|
510
|
+
// (preserving legacy behavior for edge cases).
|
|
511
|
+
// Multi-value: Claude and CodeBuddy share .claude-plugin/marketplace.json,
|
|
512
|
+
// so a single file may be claimed by multiple hosts.
|
|
513
|
+
let detectedHosts = ['codex'];
|
|
514
|
+
for (const [suffix, hostIds] of MANIFEST_SUFFIX_HOST_MAP) {
|
|
515
|
+
if (path.endsWith(`/${suffix}`)) {
|
|
516
|
+
detectedHosts = hostIds;
|
|
517
|
+
break;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
const relPath = safeRelative(root, path);
|
|
521
|
+
const kind = path.endsWith('/marketplace.json') ? 'marketplace' : 'plugin';
|
|
522
|
+
const name = typeof value.name === 'string' ? value.name : null;
|
|
523
|
+
const version = typeof value.version === 'string' ? value.version : null;
|
|
524
|
+
for (const host of detectedHosts) {
|
|
525
|
+
manifests.push({ path: relPath, host, kind, name, version });
|
|
526
|
+
}
|
|
471
527
|
}
|
|
472
528
|
|
|
473
529
|
const legacyReleaseConfigs = [];
|
|
@@ -657,9 +713,20 @@ function buildCandidates(facts) {
|
|
|
657
713
|
const gates = [];
|
|
658
714
|
const ids = new Set();
|
|
659
715
|
const knownFiles = new Set(facts.fileDigests.map((file) => file.path));
|
|
716
|
+
// Extract manifest root from path using registry-driven suffix matching.
|
|
717
|
+
// This handles all platform paths including Codex's .agents/plugins/marketplace.json,
|
|
718
|
+
// not just the hardcoded .{platform}-plugin/ pattern.
|
|
660
719
|
const manifestRoots = facts.manifests.map((manifest) => {
|
|
661
|
-
|
|
662
|
-
|
|
720
|
+
let root = '.';
|
|
721
|
+
for (const suffix of MANIFEST_SUFFIX_HOST_MAP.keys()) {
|
|
722
|
+
const fullSuffix = `/${suffix}`;
|
|
723
|
+
const idx = manifest.path.lastIndexOf(fullSuffix);
|
|
724
|
+
if (idx >= 0) {
|
|
725
|
+
root = manifest.path.slice(0, idx) || '.';
|
|
726
|
+
break;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
return { ...manifest, root };
|
|
663
730
|
});
|
|
664
731
|
const manifestOwners = new Map();
|
|
665
732
|
const legacyUnits = facts.legacyReleaseConfigs.flatMap((config) => config.releaseUnits);
|
|
@@ -968,6 +1035,17 @@ function buildRecommendedProposal(facts, candidates) {
|
|
|
968
1035
|
};
|
|
969
1036
|
}
|
|
970
1037
|
|
|
1038
|
+
// Discover marketplace index candidates for this unit's plugin hosts.
|
|
1039
|
+
//
|
|
1040
|
+
// 单一权威来源:平台注册表的 manifestPaths.marketplace 是默认索引路径;
|
|
1041
|
+
// publicFileMappingCandidates 是该 unit 唯一可发现的公开文件 to 路径集。
|
|
1042
|
+
// 按"等于默认索引路径或以 /<默认索引路径> 精确结尾"筛选候选。
|
|
1043
|
+
// 1 个候选:根布局省略显式路径,嵌套布局写 marketplaceIndexPath;
|
|
1044
|
+
// 0 个候选:产生 MARKETPLACE_ASSET_MISSING;
|
|
1045
|
+
// 多个候选:产生 MARKETPLACE_ASSET_AMBIGUOUS。
|
|
1046
|
+
// Kimi 默认路径为 null,不要求候选;CodeBuddy 复用 Claude 市场索引。
|
|
1047
|
+
const unitPrefix = unit.source === '.' ? '' : `${unit.source}/`;
|
|
1048
|
+
const marketplaceConflicts = [];
|
|
971
1049
|
const distributions = unit.distributionCandidates.map((type) => {
|
|
972
1050
|
if (type === 'npm') {
|
|
973
1051
|
return {
|
|
@@ -978,19 +1056,69 @@ function buildRecommendedProposal(facts, candidates) {
|
|
|
978
1056
|
};
|
|
979
1057
|
}
|
|
980
1058
|
const entrySkill = unit.entrySkillCandidates?.[0];
|
|
981
|
-
|
|
1059
|
+
const host = type.replace(/-plugin$/, '');
|
|
1060
|
+
const platform = getPlatform(host);
|
|
1061
|
+
const defaultMktPath = platform.manifestPaths.marketplace;
|
|
1062
|
+
const dist = {
|
|
982
1063
|
type,
|
|
983
1064
|
plugin: unit.id,
|
|
984
1065
|
marketplace: unit.id,
|
|
985
1066
|
entrySkill,
|
|
1067
|
+
marketplaceSourceType: 'bundled-family',
|
|
986
1068
|
};
|
|
1069
|
+
// Kimi 的 manifestPaths.marketplace 为 null,不要求候选
|
|
1070
|
+
if (defaultMktPath !== null) {
|
|
1071
|
+
const mappingCandidates = unit.publicFileMappingCandidates ?? [];
|
|
1072
|
+
const suffix = `/${defaultMktPath}`;
|
|
1073
|
+
const candidates = mappingCandidates.filter((m) => (
|
|
1074
|
+
m.to === defaultMktPath || m.to.endsWith(suffix)
|
|
1075
|
+
));
|
|
1076
|
+
if (candidates.length === 0) {
|
|
1077
|
+
marketplaceConflicts.push({
|
|
1078
|
+
code: 'MARKETPLACE_ASSET_MISSING',
|
|
1079
|
+
unit: unit.id,
|
|
1080
|
+
platform: host,
|
|
1081
|
+
expectedSuffix: defaultMktPath,
|
|
1082
|
+
});
|
|
1083
|
+
return null;
|
|
1084
|
+
}
|
|
1085
|
+
if (candidates.length > 1) {
|
|
1086
|
+
marketplaceConflicts.push({
|
|
1087
|
+
code: 'MARKETPLACE_ASSET_AMBIGUOUS',
|
|
1088
|
+
unit: unit.id,
|
|
1089
|
+
platform: host,
|
|
1090
|
+
candidates: candidates.map((m) => m.to).sort(),
|
|
1091
|
+
});
|
|
1092
|
+
return null;
|
|
1093
|
+
}
|
|
1094
|
+
// 1 个候选
|
|
1095
|
+
const candidateTo = candidates[0].to;
|
|
1096
|
+
if (candidateTo === defaultMktPath) {
|
|
1097
|
+
// 根布局:marketplace index 在平台默认路径,省略显式 marketplaceIndexPath
|
|
1098
|
+
} else {
|
|
1099
|
+
// 嵌套布局:candidateTo 以 /<defaultMktPath> 精确结尾。
|
|
1100
|
+
// 市场文件仍在同一发布快照内,属于 bundled-family 来源;
|
|
1101
|
+
// marketplaceIndexPath 标记索引在快照内的相对位置。
|
|
1102
|
+
// 不得伪造 marketplaceRepo(那是 standalone-index 独有字段)。
|
|
1103
|
+
dist.marketplaceIndexPath = candidateTo;
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
return dist;
|
|
987
1107
|
}).filter(Boolean);
|
|
988
1108
|
|
|
1109
|
+
// Marketplace asset conflicts: missing or ambiguous marketplace index
|
|
1110
|
+
if (marketplaceConflicts.length > 0) {
|
|
1111
|
+
return {
|
|
1112
|
+
answers: null,
|
|
1113
|
+
conflicts: marketplaceConflicts,
|
|
1114
|
+
assumptions,
|
|
1115
|
+
};
|
|
1116
|
+
}
|
|
1117
|
+
|
|
989
1118
|
// If any distribution failed to construct, bail
|
|
990
1119
|
if (distributions.length !== unit.distributionCandidates.length) return null;
|
|
991
1120
|
|
|
992
1121
|
const tagTemplate = unit.tagTemplateCandidates[0] ?? 'v{version}';
|
|
993
|
-
const unitPrefix = unit.source === '.' ? '' : `${unit.source}/`;
|
|
994
1122
|
const versionSource = unit.packagePath?.startsWith(unitPrefix)
|
|
995
1123
|
? unit.packagePath.slice(unitPrefix.length)
|
|
996
1124
|
: unit.packagePath;
|