release-skill 0.1.7 → 0.1.8
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/.codex-plugin/plugin.json +2 -2
- package/.kimi-plugin/plugin.json +27 -0
- package/CHANGELOG.md +22 -0
- package/INSTALL.md +73 -2
- package/INSTALL.zh-CN.md +94 -99
- package/README.md +49 -35
- package/README.zh-CN.md +82 -321
- 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 +1061 -182
- package/adapters/claude/schemas/.render-manifest.json +4 -4
- package/adapters/claude/schemas/release-plan.schema.json +20 -2
- package/adapters/claude/schemas/release-project.schema.json +22 -4
- package/adapters/claude/schemas/release-run.schema.json +1 -0
- package/adapters/codex/.codex-plugin/plugin.json +2 -2
- package/adapters/codex/bin/release-skill.bundle.mjs +1061 -182
- package/adapters/codex/schemas/.render-manifest.json +4 -4
- package/adapters/codex/schemas/release-plan.schema.json +20 -2
- package/adapters/codex/schemas/release-project.schema.json +22 -4
- package/adapters/codex/schemas/release-run.schema.json +1 -0
- package/adapters/kimi/.kimi-plugin/plugin.json +27 -0
- package/adapters/kimi/bin/release-skill.bundle.mjs +84415 -0
- package/adapters/kimi/bin/release-skill.mjs +54 -0
- package/adapters/kimi/native/safe-write/binding.gyp +41 -0
- package/adapters/kimi/native/safe-write/prebuilds/darwin-arm64/safe_write.node +0 -0
- package/adapters/kimi/native/safe-write/prebuilds.json +24 -0
- package/adapters/kimi/native/safe-write/src/safe_write.cc +2032 -0
- package/adapters/kimi/schemas/.render-manifest.json +37 -0
- package/adapters/kimi/schemas/approval-record.schema.json +115 -0
- package/adapters/kimi/schemas/artifact-lock.schema.json +111 -0
- package/adapters/kimi/schemas/artifact-plan.schema.json +52 -0
- package/adapters/kimi/schemas/artifact-policy.schema.json +76 -0
- package/adapters/kimi/schemas/evidence-event.schema.json +89 -0
- package/adapters/kimi/schemas/release-plan.schema.json +878 -0
- package/adapters/kimi/schemas/release-project.schema.json +895 -0
- package/adapters/kimi/schemas/release-run.schema.json +343 -0
- package/adapters/kimi/skills/release-assess/SKILL.md +58 -0
- package/adapters/kimi/skills/release-help/SKILL.md +84 -0
- package/adapters/kimi/skills/release-prepare/SKILL.md +99 -0
- package/adapters/kimi/skills/release-publish/SKILL.md +64 -0
- package/adapters/kimi/skills/release-reconcile/SKILL.md +80 -0
- package/adapters/kimi/skills/release-setup/SKILL.md +102 -0
- package/adapters/kimi/skills/release-verify/SKILL.md +77 -0
- package/bin/release-skill-cli.mjs +16 -4
- package/bin/release-skill.bundle.mjs +1061 -182
- package/package.json +4 -2
- package/schemas/.render-manifest.json +4 -4
- package/schemas/release-plan.schema.json +20 -2
- package/schemas/release-project.schema.json +22 -4
- package/schemas/release-run.schema.json +1 -0
- package/src/adapters/contract.mjs +1 -0
- package/src/adapters/plugin-marketplace.mjs +990 -30
- package/src/commands/assess.mjs +50 -1
- package/src/commands/prepare.mjs +65 -0
- package/src/commands/publish.mjs +3 -0
- package/src/commands/reconcile.mjs +3 -0
- package/src/commands/setup.mjs +10 -5
- package/src/commands/verify.mjs +16 -6
- package/src/core/plan.mjs +118 -0
- package/src/core/verification-gates.mjs +1 -1
- package/src/producers/build-adapters.mjs +38 -8
package/src/commands/assess.mjs
CHANGED
|
@@ -128,7 +128,8 @@ function identifyTopology(config) {
|
|
|
128
128
|
const hasNpm = uniqueDistTypes.includes('npm');
|
|
129
129
|
const hasPlugin =
|
|
130
130
|
uniqueDistTypes.includes('claude-plugin') ||
|
|
131
|
-
uniqueDistTypes.includes('codex-plugin')
|
|
131
|
+
uniqueDistTypes.includes('codex-plugin') ||
|
|
132
|
+
uniqueDistTypes.includes('kimi-plugin');
|
|
132
133
|
|
|
133
134
|
if (units.length === 0) {
|
|
134
135
|
type = 'no-release-units';
|
|
@@ -379,6 +380,54 @@ async function checkPluginManifests(root, config) {
|
|
|
379
380
|
}
|
|
380
381
|
}
|
|
381
382
|
}
|
|
383
|
+
|
|
384
|
+
if (distributionTypes.has('kimi-plugin')) {
|
|
385
|
+
const manifestPath = resolve(unitRoot, '.kimi-plugin', 'plugin.json');
|
|
386
|
+
const displayPath = unitFile(unit, '.kimi-plugin/plugin.json');
|
|
387
|
+
const exists = await fileExists(manifestPath);
|
|
388
|
+
if (!exists) {
|
|
389
|
+
gaps.push(
|
|
390
|
+
createGap({
|
|
391
|
+
scope: GapScope.PROFILE,
|
|
392
|
+
category: GapCategory.MANIFEST,
|
|
393
|
+
severity: Severity.ERROR,
|
|
394
|
+
code: 'KIMI_MANIFEST_MISSING',
|
|
395
|
+
message: `发布单元 "${unit.id}" 缺少 .kimi-plugin/plugin.json 插件清单`,
|
|
396
|
+
file: displayPath,
|
|
397
|
+
}),
|
|
398
|
+
);
|
|
399
|
+
} else {
|
|
400
|
+
try {
|
|
401
|
+
const content = await readFile(manifestPath, 'utf8');
|
|
402
|
+
const manifest = JSON.parse(content);
|
|
403
|
+
const requiredFields = ['name', 'version', 'description'];
|
|
404
|
+
const missingFields = requiredFields.filter((f) => !(f in manifest));
|
|
405
|
+
if (missingFields.length > 0) {
|
|
406
|
+
gaps.push(
|
|
407
|
+
createGap({
|
|
408
|
+
scope: GapScope.PROFILE,
|
|
409
|
+
category: GapCategory.MANIFEST,
|
|
410
|
+
severity: Severity.ERROR,
|
|
411
|
+
code: 'KIMI_MANIFEST_INCOMPLETE',
|
|
412
|
+
message: `Kimi 插件清单缺少必填字段: ${missingFields.join(', ')}`,
|
|
413
|
+
file: displayPath,
|
|
414
|
+
}),
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
} catch {
|
|
418
|
+
gaps.push(
|
|
419
|
+
createGap({
|
|
420
|
+
scope: GapScope.PROFILE,
|
|
421
|
+
category: GapCategory.MANIFEST,
|
|
422
|
+
severity: Severity.ERROR,
|
|
423
|
+
code: 'KIMI_MANIFEST_INVALID',
|
|
424
|
+
message: '.kimi-plugin/plugin.json 解析失败',
|
|
425
|
+
file: displayPath,
|
|
426
|
+
}),
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
382
431
|
}
|
|
383
432
|
|
|
384
433
|
return gaps;
|
package/src/commands/prepare.mjs
CHANGED
|
@@ -966,6 +966,35 @@ function buildExternalActions(unitResults, resolvedVersions, productionAssets) {
|
|
|
966
966
|
status: 'PENDING',
|
|
967
967
|
});
|
|
968
968
|
}
|
|
969
|
+
const kimiDist = (unit.distributions ?? []).find((d) => d.type === 'kimi-plugin');
|
|
970
|
+
if (kimiDist) {
|
|
971
|
+
const identity = marketplaceIdentity(kimiDist);
|
|
972
|
+
const kimiTimeoutMs = Number.isInteger(kimiDist.timeoutMs) ? kimiDist.timeoutMs : 300000;
|
|
973
|
+
// Kimi Code has no non-interactive install/marketplace API, so the kimi
|
|
974
|
+
// action carries no marketplace identity (MINOR-1). plugin + entrySkill
|
|
975
|
+
// are the meaningful identity fields.
|
|
976
|
+
actions.push({
|
|
977
|
+
id: `kimi-marketplace-install-${unit.id}`,
|
|
978
|
+
type: 'kimi-marketplace-install',
|
|
979
|
+
adapter: 'plugin-marketplace',
|
|
980
|
+
unitId: unit.id,
|
|
981
|
+
parameters: {
|
|
982
|
+
consumer: 'kimi',
|
|
983
|
+
plugin: identity.plugin,
|
|
984
|
+
repo: unit.publicRepo,
|
|
985
|
+
version,
|
|
986
|
+
entrySkill: identity.entrySkill,
|
|
987
|
+
timeoutMs: kimiTimeoutMs,
|
|
988
|
+
},
|
|
989
|
+
expected: {
|
|
990
|
+
installed: true,
|
|
991
|
+
plugin: identity.plugin,
|
|
992
|
+
version,
|
|
993
|
+
entrySkill: identity.entrySkill,
|
|
994
|
+
},
|
|
995
|
+
status: 'PENDING',
|
|
996
|
+
});
|
|
997
|
+
}
|
|
969
998
|
}
|
|
970
999
|
return actions;
|
|
971
1000
|
}
|
|
@@ -1180,6 +1209,42 @@ function buildExternalActions(unitResults, resolvedVersions, productionAssets) {
|
|
|
1180
1209
|
status: 'PENDING',
|
|
1181
1210
|
});
|
|
1182
1211
|
}
|
|
1212
|
+
const kimiDist = (unit.distributions ?? []).find((d) => d.type === 'kimi-plugin');
|
|
1213
|
+
if (kimiDist) {
|
|
1214
|
+
const identity = marketplaceIdentity(kimiDist);
|
|
1215
|
+
const kimiTimeoutMs = Number.isInteger(kimiDist.timeoutMs) ? kimiDist.timeoutMs : 300000;
|
|
1216
|
+
// No marketplace identity for kimi (MINOR-1): Kimi Code has an interactive
|
|
1217
|
+
// marketplace but no non-interactive install API.
|
|
1218
|
+
actions.push({
|
|
1219
|
+
id: `kimi-marketplace-install-${unit.id}`,
|
|
1220
|
+
type: 'kimi-marketplace-install',
|
|
1221
|
+
adapter: 'plugin-marketplace',
|
|
1222
|
+
unitId: unit.id,
|
|
1223
|
+
parameters: {
|
|
1224
|
+
consumer: 'kimi',
|
|
1225
|
+
plugin: identity.plugin,
|
|
1226
|
+
repo: unit.publicRepo,
|
|
1227
|
+
ref: resolvedTag,
|
|
1228
|
+
version: unitVersion,
|
|
1229
|
+
entrySkill: identity.entrySkill,
|
|
1230
|
+
snapshotPath: asset.snapshotPath,
|
|
1231
|
+
manifestDigest: asset.manifestDigest,
|
|
1232
|
+
timeoutMs: kimiTimeoutMs,
|
|
1233
|
+
},
|
|
1234
|
+
expected: {
|
|
1235
|
+
installed: true,
|
|
1236
|
+
consumer: 'kimi',
|
|
1237
|
+
plugin: identity.plugin,
|
|
1238
|
+
repo: unit.publicRepo,
|
|
1239
|
+
version: unitVersion,
|
|
1240
|
+
ref: resolvedTag,
|
|
1241
|
+
entrySkill: identity.entrySkill,
|
|
1242
|
+
entrySkillFound: true,
|
|
1243
|
+
manifestDigest: asset.manifestDigest,
|
|
1244
|
+
},
|
|
1245
|
+
status: 'PENDING',
|
|
1246
|
+
});
|
|
1247
|
+
}
|
|
1183
1248
|
}
|
|
1184
1249
|
|
|
1185
1250
|
return actions;
|
package/src/commands/publish.mjs
CHANGED
|
@@ -81,6 +81,7 @@ const CHECKPOINT_ORDER = [
|
|
|
81
81
|
'github-release',
|
|
82
82
|
'claude-marketplace-install',
|
|
83
83
|
'codex-marketplace-install',
|
|
84
|
+
'kimi-marketplace-install',
|
|
84
85
|
];
|
|
85
86
|
|
|
86
87
|
/**
|
|
@@ -99,11 +100,13 @@ const ADAPTER_ACTION_TYPE_MAP = {
|
|
|
99
100
|
'github-release': 'github-release',
|
|
100
101
|
'claude-marketplace-install': 'claude-marketplace-install',
|
|
101
102
|
'codex-marketplace-install': 'codex-marketplace-install',
|
|
103
|
+
'kimi-marketplace-install': 'kimi-marketplace-install',
|
|
102
104
|
};
|
|
103
105
|
|
|
104
106
|
const MARKETPLACE_TYPES = new Set([
|
|
105
107
|
'claude-marketplace-install',
|
|
106
108
|
'codex-marketplace-install',
|
|
109
|
+
'kimi-marketplace-install',
|
|
107
110
|
]);
|
|
108
111
|
|
|
109
112
|
// ---------------------------------------------------------------------------
|
|
@@ -77,6 +77,7 @@ const CHECKPOINT_ORDER = [
|
|
|
77
77
|
'github-release',
|
|
78
78
|
'claude-marketplace-install',
|
|
79
79
|
'codex-marketplace-install',
|
|
80
|
+
'kimi-marketplace-install',
|
|
80
81
|
];
|
|
81
82
|
|
|
82
83
|
/**
|
|
@@ -92,6 +93,7 @@ const ADAPTER_ACTION_TYPE_MAP = {
|
|
|
92
93
|
'github-release': 'github-release',
|
|
93
94
|
'claude-marketplace-install': 'claude-marketplace-install',
|
|
94
95
|
'codex-marketplace-install': 'codex-marketplace-install',
|
|
96
|
+
'kimi-marketplace-install': 'kimi-marketplace-install',
|
|
95
97
|
};
|
|
96
98
|
|
|
97
99
|
/**
|
|
@@ -102,6 +104,7 @@ const ADAPTER_ACTION_TYPE_MAP = {
|
|
|
102
104
|
const MARKETPLACE_TYPES = new Set([
|
|
103
105
|
'claude-marketplace-install',
|
|
104
106
|
'codex-marketplace-install',
|
|
107
|
+
'kimi-marketplace-install',
|
|
105
108
|
]);
|
|
106
109
|
|
|
107
110
|
// ---------------------------------------------------------------------------
|
package/src/commands/setup.mjs
CHANGED
|
@@ -33,7 +33,7 @@ import { readTrustedPackageResource } from '../core/trusted-resource.mjs';
|
|
|
33
33
|
|
|
34
34
|
const execFile = promisify(execFileCb);
|
|
35
35
|
const SKIP_DIRS = new Set([
|
|
36
|
-
'.git', '.release-skill', '.worktrees', '.claude', '.codex', '.cache', '.tmp',
|
|
36
|
+
'.git', '.release-skill', '.worktrees', '.claude', '.codex', '.kimi', '.cache', '.tmp',
|
|
37
37
|
'.pytest_cache', '.mypy_cache', '.ruff_cache', '.tox', '.venv', 'venv',
|
|
38
38
|
'node_modules', 'dist', 'coverage', 'build', 'out', 'tmp', 'temp',
|
|
39
39
|
'runs', 'test', 'tests', 'test-fixtures', 'fixtures', 'examples',
|
|
@@ -88,6 +88,7 @@ async function walkDiscoveryFiles(root, maxDepth = 8) {
|
|
|
88
88
|
/^CHANGELOG(?:\.|$)/i.test(child.name) ||
|
|
89
89
|
absolute.endsWith('/.claude-plugin/plugin.json') ||
|
|
90
90
|
absolute.endsWith('/.codex-plugin/plugin.json') ||
|
|
91
|
+
absolute.endsWith('/.kimi-plugin/plugin.json') ||
|
|
91
92
|
absolute.endsWith('/.claude-plugin/marketplace.json') ||
|
|
92
93
|
absolute.endsWith('/.codex-plugin/marketplace.json'))
|
|
93
94
|
) {
|
|
@@ -422,7 +423,7 @@ async function discoverFacts(root) {
|
|
|
422
423
|
const files = await walkDiscoveryFiles(root);
|
|
423
424
|
const packageFiles = files.filter((path) => (
|
|
424
425
|
basename(path) === 'package.json' &&
|
|
425
|
-
!/[\\/]adapters[\\/](?:claude|codex)[\\/]package\.json$/.test(path)
|
|
426
|
+
!/[\\/]adapters[\\/](?:claude|codex|kimi)[\\/]package\.json$/.test(path)
|
|
426
427
|
));
|
|
427
428
|
const pluginFiles = files.filter((path) => path.endsWith('/plugin.json'));
|
|
428
429
|
const marketplaceFiles = files.filter((path) => path.endsWith('/marketplace.json'));
|
|
@@ -458,7 +459,9 @@ async function discoverFacts(root) {
|
|
|
458
459
|
const value = await readJsonBounded(path, 'discovered plugin manifest');
|
|
459
460
|
manifests.push({
|
|
460
461
|
path: safeRelative(root, path),
|
|
461
|
-
host: path.includes('/.claude-plugin/') ? 'claude'
|
|
462
|
+
host: path.includes('/.claude-plugin/') ? 'claude'
|
|
463
|
+
: path.includes('/.kimi-plugin/') ? 'kimi'
|
|
464
|
+
: 'codex',
|
|
462
465
|
kind: path.endsWith('/marketplace.json') ? 'marketplace' : 'plugin',
|
|
463
466
|
name: typeof value.name === 'string' ? value.name : null,
|
|
464
467
|
version: typeof value.version === 'string' ? value.version : null,
|
|
@@ -483,7 +486,8 @@ async function discoverFacts(root) {
|
|
|
483
486
|
name: skillIndex >= 0 ? segments[skillIndex + 1] ?? null : null,
|
|
484
487
|
host: relPath.includes('/adapters/claude/') ? 'claude'
|
|
485
488
|
: relPath.includes('/adapters/codex/') ? 'codex'
|
|
486
|
-
: '
|
|
489
|
+
: relPath.includes('/adapters/kimi/') ? 'kimi'
|
|
490
|
+
: 'shared',
|
|
487
491
|
};
|
|
488
492
|
})
|
|
489
493
|
.filter((item) => item.name)
|
|
@@ -651,7 +655,7 @@ function buildCandidates(facts) {
|
|
|
651
655
|
const ids = new Set();
|
|
652
656
|
const knownFiles = new Set(facts.fileDigests.map((file) => file.path));
|
|
653
657
|
const manifestRoots = facts.manifests.map((manifest) => {
|
|
654
|
-
const match = manifest.path.match(/^(.*?)(?:\/)?(?:\.claude-plugin|\.codex-plugin)\/(?:plugin|marketplace)\.json$/);
|
|
658
|
+
const match = manifest.path.match(/^(.*?)(?:\/)?(?:\.claude-plugin|\.codex-plugin|\.kimi-plugin)\/(?:plugin|marketplace)\.json$/);
|
|
655
659
|
return { ...manifest, root: match?.[1] || '.' };
|
|
656
660
|
});
|
|
657
661
|
const manifestOwners = new Map();
|
|
@@ -698,6 +702,7 @@ function buildCandidates(facts) {
|
|
|
698
702
|
) distributions.push('npm');
|
|
699
703
|
if (pluginHosts.includes('claude')) distributions.push('claude-plugin');
|
|
700
704
|
if (pluginHosts.includes('codex')) distributions.push('codex-plugin');
|
|
705
|
+
if (pluginHosts.includes('kimi')) distributions.push('kimi-plugin');
|
|
701
706
|
if (pkg.private && matchingLegacyUnits.length === 0 && facts.legacyReleaseConfigs.length > 0) continue;
|
|
702
707
|
if (pkg.private && distributions.length === 0) continue;
|
|
703
708
|
|
package/src/commands/verify.mjs
CHANGED
|
@@ -68,6 +68,7 @@ const ADAPTER_ACTION_TYPE_MAP = {
|
|
|
68
68
|
'github-release': 'github-release',
|
|
69
69
|
'claude-marketplace-install': 'claude-marketplace-install',
|
|
70
70
|
'codex-marketplace-install': 'codex-marketplace-install',
|
|
71
|
+
'kimi-marketplace-install': 'kimi-marketplace-install',
|
|
71
72
|
};
|
|
72
73
|
|
|
73
74
|
// ---------------------------------------------------------------------------
|
|
@@ -706,7 +707,8 @@ export async function verifyRelease(options) {
|
|
|
706
707
|
// =======================================================================
|
|
707
708
|
// Step 3: Verify all actions via adapters
|
|
708
709
|
//
|
|
709
|
-
// Marketplace actions (claude-marketplace-install, codex-marketplace-install
|
|
710
|
+
// Marketplace actions (claude-marketplace-install, codex-marketplace-install,
|
|
711
|
+
// kimi-marketplace-install)
|
|
710
712
|
// are verified as fresh, isolated consumer installs in verify's own runDir.
|
|
711
713
|
// This ensures verify does not read the publish run's consumer install
|
|
712
714
|
// directories or evidence.
|
|
@@ -721,6 +723,7 @@ export async function verifyRelease(options) {
|
|
|
721
723
|
const MARKETPLACE_TYPES = new Set([
|
|
722
724
|
'claude-marketplace-install',
|
|
723
725
|
'codex-marketplace-install',
|
|
726
|
+
'kimi-marketplace-install',
|
|
724
727
|
]);
|
|
725
728
|
|
|
726
729
|
for (const action of actions) {
|
|
@@ -831,7 +834,9 @@ export async function verifyRelease(options) {
|
|
|
831
834
|
|
|
832
835
|
const distribution = action.type === 'claude-marketplace-install'
|
|
833
836
|
? 'claude-plugin'
|
|
834
|
-
: 'codex-
|
|
837
|
+
: action.type === 'codex-marketplace-install'
|
|
838
|
+
? 'codex-plugin'
|
|
839
|
+
: 'kimi-plugin';
|
|
835
840
|
const installPath = verifyResult.observation?.installPath;
|
|
836
841
|
consumerGateResults.push(...await runConsumerVerificationGates({
|
|
837
842
|
plan,
|
|
@@ -845,10 +850,15 @@ export async function verifyRelease(options) {
|
|
|
845
850
|
HOME: resolve(runDir, 'consumers', `claude-${action.parameters.plugin}`),
|
|
846
851
|
CLAUDE_CONFIG_DIR: resolve(runDir, 'consumers', `claude-${action.parameters.plugin}`, '.claude'),
|
|
847
852
|
}
|
|
848
|
-
:
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
853
|
+
: action.type === 'codex-marketplace-install'
|
|
854
|
+
? {
|
|
855
|
+
HOME: resolve(runDir, 'consumers', `codex-${action.parameters.plugin}`),
|
|
856
|
+
CODEX_HOME: resolve(runDir, 'consumers', `codex-${action.parameters.plugin}`),
|
|
857
|
+
}
|
|
858
|
+
: {
|
|
859
|
+
HOME: resolve(runDir, 'consumers', `kimi-${action.parameters.plugin}`),
|
|
860
|
+
KIMI_CODE_HOME: resolve(runDir, 'consumers', `kimi-${action.parameters.plugin}`),
|
|
861
|
+
},
|
|
852
862
|
}));
|
|
853
863
|
} else {
|
|
854
864
|
// --- Non-marketplace: read-only adapter.verify() ---
|
package/src/core/plan.mjs
CHANGED
|
@@ -285,6 +285,7 @@ const EXPECTED_ADAPTER = {
|
|
|
285
285
|
'npm-publish': 'npm',
|
|
286
286
|
'claude-marketplace-install': 'plugin-marketplace',
|
|
287
287
|
'codex-marketplace-install': 'plugin-marketplace',
|
|
288
|
+
'kimi-marketplace-install': 'plugin-marketplace',
|
|
288
289
|
'set-default-branch': 'git-github',
|
|
289
290
|
};
|
|
290
291
|
|
|
@@ -301,6 +302,7 @@ const REQUIRED_ACTION_TYPES = ['push-snapshot', 'create-tag', 'github-release'];
|
|
|
301
302
|
* - 1 npm-publish (adapter: npm) — only if the unit has an npm distribution
|
|
302
303
|
* - 1 claude-marketplace-install (adapter: plugin-marketplace) — only if the unit has a claude-plugin distribution
|
|
303
304
|
* - 1 codex-marketplace-install (adapter: plugin-marketplace) — only if the unit has a codex-plugin distribution
|
|
305
|
+
* - 1 kimi-marketplace-install (adapter: plugin-marketplace) — only if the unit has a kimi-plugin distribution
|
|
304
306
|
*
|
|
305
307
|
* Every action must bind to the correct unitId, correct adapter, correct
|
|
306
308
|
* version, correct publicRepo (where applicable), and correct tag derived
|
|
@@ -821,6 +823,122 @@ export function validatePlanActionCompleteness(plan, options = {}) {
|
|
|
821
823
|
}
|
|
822
824
|
}
|
|
823
825
|
|
|
826
|
+
// kimi-marketplace-install: only required if unit has kimi-plugin distribution
|
|
827
|
+
const kimiDist = distributions.find((d) => d.type === 'kimi-plugin');
|
|
828
|
+
if (kimiDist) {
|
|
829
|
+
const plugin = kimiDist.plugin;
|
|
830
|
+
// marketplace is NOT a required identity field for kimi: Kimi Code has an
|
|
831
|
+
// interactive marketplace but no non-interactive install API, so the kimi
|
|
832
|
+
// action carries no executable marketplace identity (MINOR-1). Only plugin
|
|
833
|
+
// and entrySkill are required; a declared marketplace is tolerated as an
|
|
834
|
+
// optional legacy value but never bound as a required condition.
|
|
835
|
+
const marketplace = kimiDist.marketplace;
|
|
836
|
+
const entrySkill = kimiDist.entrySkill;
|
|
837
|
+
if (!plugin || !entrySkill) {
|
|
838
|
+
failures.push(`unit "${unitId}": kimi-plugin distribution requires plugin and entrySkill`);
|
|
839
|
+
}
|
|
840
|
+
expectedCount++;
|
|
841
|
+
const expectedActionId = `kimi-marketplace-install-${unitId}`;
|
|
842
|
+
const kimiActions = actions.filter(
|
|
843
|
+
(a) => a.unitId === unitId && a.type === 'kimi-marketplace-install',
|
|
844
|
+
);
|
|
845
|
+
|
|
846
|
+
if (kimiActions.length === 0) {
|
|
847
|
+
failures.push(
|
|
848
|
+
`unit "${unitId}": kimi-plugin distribution declared but "kimi-marketplace-install" action is missing`,
|
|
849
|
+
);
|
|
850
|
+
} else if (kimiActions.length > 1) {
|
|
851
|
+
failures.push(
|
|
852
|
+
`unit "${unitId}": duplicate kimi-marketplace-install actions (${kimiActions.length} found, expected 1)`,
|
|
853
|
+
);
|
|
854
|
+
} else {
|
|
855
|
+
const action = kimiActions[0];
|
|
856
|
+
|
|
857
|
+
if (action.id !== expectedActionId) {
|
|
858
|
+
failures.push(
|
|
859
|
+
`unit "${unitId}", action "${action.id}": id is "${action.id}", expected "${expectedActionId}"`,
|
|
860
|
+
);
|
|
861
|
+
}
|
|
862
|
+
if (action.unitId !== unitId) {
|
|
863
|
+
failures.push(
|
|
864
|
+
`unit "${unitId}", action "${action.id}": unitId is "${action.unitId}", expected "${unitId}"`,
|
|
865
|
+
);
|
|
866
|
+
}
|
|
867
|
+
if (action.adapter !== 'plugin-marketplace') {
|
|
868
|
+
failures.push(
|
|
869
|
+
`unit "${unitId}", action "${action.id}": adapter is "${action.adapter}", expected "plugin-marketplace"`,
|
|
870
|
+
);
|
|
871
|
+
}
|
|
872
|
+
if (action.status !== 'PENDING') {
|
|
873
|
+
failures.push(
|
|
874
|
+
`unit "${unitId}", action "${action.id}": status is "${action.status ?? '(missing)'}", expected "PENDING"`,
|
|
875
|
+
);
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
// Parameter checks
|
|
879
|
+
_checkRequired(action, 'parameters.consumer', action.parameters?.consumer, 'kimi', unitId, failures);
|
|
880
|
+
_checkRequired(action, 'parameters.plugin', action.parameters?.plugin, plugin, unitId, failures);
|
|
881
|
+
// marketplace is optional for kimi (MINOR-1). If a legacy plan still
|
|
882
|
+
// carries it, it must be consistent with the declared distribution but
|
|
883
|
+
// is never a required identity condition.
|
|
884
|
+
if (action.parameters?.marketplace !== undefined && marketplace !== undefined
|
|
885
|
+
&& action.parameters.marketplace !== marketplace) {
|
|
886
|
+
failures.push(
|
|
887
|
+
`unit "${unitId}", action "${action.id}": parameters.marketplace is "${action.parameters.marketplace}", expected optional legacy value "${marketplace}"`,
|
|
888
|
+
);
|
|
889
|
+
}
|
|
890
|
+
_checkRequired(action, 'parameters.repo', action.parameters?.repo, publicRepo, unitId, failures);
|
|
891
|
+
_checkRequired(action, 'parameters.version', action.parameters?.version, targetVersion, unitId, failures);
|
|
892
|
+
_checkRequired(action, 'parameters.entrySkill', action.parameters?.entrySkill, entrySkill, unitId, failures);
|
|
893
|
+
if (production) {
|
|
894
|
+
_checkRequired(action, 'parameters.snapshotPath', action.parameters?.snapshotPath, frozen?.path, unitId, failures);
|
|
895
|
+
_checkRequired(action, 'parameters.ref', action.parameters?.ref, expectedTag, unitId, failures);
|
|
896
|
+
_checkRequired(action, 'parameters.manifestDigest', action.parameters?.manifestDigest, frozen?.manifestDigest, unitId, failures);
|
|
897
|
+
}
|
|
898
|
+
// timeoutMs is mandatory for all marketplace install actions.
|
|
899
|
+
// Legacy plans (pre-v0.1.5) lack this field; legacyCompatibility
|
|
900
|
+
// relaxes the check for reconcile/verify paths only, and only when
|
|
901
|
+
// the property is genuinely absent (undefined). An explicit null is
|
|
902
|
+
// NOT "absent" -- it is an invalid value and always fails closed,
|
|
903
|
+
// like strings or out-of-range numbers, even in legacyCompatibility.
|
|
904
|
+
{
|
|
905
|
+
const raw = action.parameters?.timeoutMs;
|
|
906
|
+
if (raw === undefined) {
|
|
907
|
+
if (!options.legacyCompatibility) {
|
|
908
|
+
failures.push(
|
|
909
|
+
`unit "${unitId}", action "${action.id}": parameters.timeoutMs is missing, expected a valid timeout (30000-900000)`,
|
|
910
|
+
);
|
|
911
|
+
}
|
|
912
|
+
} else {
|
|
913
|
+
// Field is present -- always validate range/type, even in legacy mode
|
|
914
|
+
if (typeof raw !== 'number' || !Number.isFinite(raw) || !Number.isInteger(raw)) {
|
|
915
|
+
failures.push(
|
|
916
|
+
`unit "${unitId}", action "${action.id}": parameters.timeoutMs must be a finite integer, got: ${JSON.stringify(raw)}`,
|
|
917
|
+
);
|
|
918
|
+
} else if (raw < 30000 || raw > 900000) {
|
|
919
|
+
failures.push(
|
|
920
|
+
`unit "${unitId}", action "${action.id}": parameters.timeoutMs must be between 30000 and 900000, got: ${raw}`,
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
// Expected checks. marketplace is NOT part of the kimi expected
|
|
927
|
+
// identity (MINOR-1): the kimi observation never binds a marketplace.
|
|
928
|
+
_checkRequired(action, 'expected.installed', action.expected?.installed, true, unitId, failures);
|
|
929
|
+
_checkRequired(action, 'expected.plugin', action.expected?.plugin, plugin, unitId, failures);
|
|
930
|
+
_checkRequired(action, 'expected.version', action.expected?.version, targetVersion, unitId, failures);
|
|
931
|
+
_checkRequired(action, 'expected.entrySkill', action.expected?.entrySkill, entrySkill, unitId, failures);
|
|
932
|
+
if (production) {
|
|
933
|
+
_checkRequired(action, 'expected.consumer', action.expected?.consumer, 'kimi', unitId, failures);
|
|
934
|
+
_checkRequired(action, 'expected.repo', action.expected?.repo, publicRepo, unitId, failures);
|
|
935
|
+
_checkRequired(action, 'expected.ref', action.expected?.ref, expectedTag, unitId, failures);
|
|
936
|
+
_checkRequired(action, 'expected.entrySkillFound', action.expected?.entrySkillFound, true, unitId, failures);
|
|
937
|
+
_checkRequired(action, 'expected.manifestDigest', action.expected?.manifestDigest, frozen?.manifestDigest, unitId, failures);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
|
|
824
942
|
// set-default-branch: only required when productionConfig.setAsDefaultBranch is true
|
|
825
943
|
if (branchStrategy === 'initialize-default-branch') {
|
|
826
944
|
const oldBranch = productionConfig.expectedCurrentDefaultBranch;
|
|
@@ -57,7 +57,7 @@ function validateGate(gate) {
|
|
|
57
57
|
if (!gate.scope || typeof gate.scope.unit !== 'string') {
|
|
58
58
|
throw gateError(gate, 'must declare scope.unit');
|
|
59
59
|
}
|
|
60
|
-
if (gate.phase === 'consumer-verify' && !['npm', 'claude-plugin', 'codex-plugin'].includes(gate.scope.distribution)) {
|
|
60
|
+
if (gate.phase === 'consumer-verify' && !['npm', 'claude-plugin', 'codex-plugin', 'kimi-plugin'].includes(gate.scope.distribution)) {
|
|
61
61
|
throw gateError(gate, 'consumer-verify must declare a supported scope.distribution');
|
|
62
62
|
}
|
|
63
63
|
if (!Array.isArray(gate.command) || gate.command.length === 0 || gate.command.some((value) => typeof value !== 'string')) {
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
* Pure producer: build adapters from skills and plugin templates.
|
|
3
3
|
*
|
|
4
4
|
* Reads skill metadata from skills-src/ and plugin.json templates,
|
|
5
|
-
* generates self-contained adapter directories for each platform (claude, codex).
|
|
5
|
+
* generates self-contained adapter directories for each platform (claude, codex, kimi).
|
|
6
6
|
*
|
|
7
7
|
* Each adapter root contains:
|
|
8
|
-
* - Plugin manifest (.claude-plugin
|
|
8
|
+
* - Plugin manifest (.claude-plugin/, .codex-plugin/, or .kimi-plugin/)
|
|
9
9
|
* - Skills with host-specific root resolution
|
|
10
10
|
* - bin/release-skill.mjs (wrapper) + bin/release-skill.bundle.mjs (self-contained bundle)
|
|
11
11
|
* - schemas/ (JSON Schema files)
|
|
@@ -205,6 +205,12 @@ const PLATFORMS = [
|
|
|
205
205
|
templateFileName: 'plugin.json',
|
|
206
206
|
hasMarketplace: false,
|
|
207
207
|
},
|
|
208
|
+
{
|
|
209
|
+
name: 'kimi',
|
|
210
|
+
pluginDirName: '.kimi-plugin',
|
|
211
|
+
templateFileName: 'plugin.json',
|
|
212
|
+
hasMarketplace: false,
|
|
213
|
+
},
|
|
208
214
|
];
|
|
209
215
|
|
|
210
216
|
const REQUIRED_SCHEMA_FILES = Object.freeze([
|
|
@@ -290,37 +296,61 @@ const CODEX_PREAMBLE = `\
|
|
|
290
296
|
>\n\
|
|
291
297
|
`;
|
|
292
298
|
|
|
299
|
+
/**
|
|
300
|
+
* Preamble prepended to every Kimi Code SKILL.md.
|
|
301
|
+
*
|
|
302
|
+
* Kimi Code's official Agent Skills contract defines the body placeholder
|
|
303
|
+
* `${KIMI_SKILL_DIR}`: the host expands it to the absolute directory containing
|
|
304
|
+
* the current SKILL.md before sending the body to the agent. This is the ONLY
|
|
305
|
+
* authoritative input for locating the entry point — the agent must NOT guess
|
|
306
|
+
* any undocumented host-metadata path. The literal placeholder is emitted
|
|
307
|
+
* verbatim (the generator does not pre-expand it); the host expands it at load
|
|
308
|
+
* time. Generated skills live at `<pluginRoot>/skills/<name>/SKILL.md`, so the
|
|
309
|
+
* plugin root is two levels above the expanded skill directory.
|
|
310
|
+
*/
|
|
311
|
+
const KIMI_PREAMBLE = `\
|
|
312
|
+
> **Kimi Code 安装入口解析协议**:Kimi Code 官方技能契约提供正文占位符 \`\${KIMI_SKILL_DIR}\`,宿主在向 Agent 发送正文前会将其展开为当前 \`SKILL.md\` 所在目录的绝对路径。必须把展开后的字面量作为当前技能目录的唯一权威输入,记为 \`SKILL_DIR\`。\n\
|
|
313
|
+
> 禁止从工作目录、可执行搜索路径、源码仓库、shell 调用上下文或任何未记载的宿主元数据路径猜测技能目录。若正文中的 \`\${KIMI_SKILL_DIR}\` 未被宿主展开(仍是字面量占位符),立即停止并报告安装定位失败。\n\
|
|
314
|
+
> 对 \`SKILL_DIR\` 执行 \`realpath\`,取其目录向上两级得到 \`PLUGIN_ROOT\`;校验真实技能路径匹配 \`PLUGIN_ROOT/skills/*/SKILL.md\` 且仍位于插件根内(路径包含检查)。\n\
|
|
315
|
+
> 令 \`RELEASE_SKILL_ENTRY=PLUGIN_ROOT/bin/release-skill.mjs\`,对入口执行 \`realpath\` containment、\`lstat\` 非符号链接且为普通文件校验。\n\
|
|
316
|
+
> 每一次 shell 工具调用都必须在同一个调用中用上述已验证绝对值设置 \`RELEASE_SKILL_ENTRY\`,然后执行 \`node "$RELEASE_SKILL_ENTRY" ...\`;不得依赖前一次 shell 的变量。\n\
|
|
317
|
+
>\n\
|
|
318
|
+
`;
|
|
319
|
+
|
|
293
320
|
/**
|
|
294
321
|
* Render SKILL.md content for a specific platform.
|
|
295
322
|
*
|
|
296
323
|
* - Claude: verbatim copy (uses ${CLAUDE_PLUGIN_ROOT})
|
|
297
324
|
* - Codex: replaces the Claude-only path with $RELEASE_SKILL_ENTRY,
|
|
298
325
|
* and prepends the path resolution protocol preamble.
|
|
326
|
+
* - Kimi: same substitution as Codex, with the Kimi Code preamble.
|
|
299
327
|
*
|
|
300
328
|
* @param {string} content - Canonical SKILL.md content.
|
|
301
|
-
* @param {string} platformName - 'claude' or '
|
|
329
|
+
* @param {string} platformName - 'claude', 'codex', or 'kimi'.
|
|
302
330
|
* @returns {string}
|
|
303
331
|
*/
|
|
304
332
|
function renderSkillForPlatform(content, platformName) {
|
|
305
333
|
if (platformName === 'claude') return content;
|
|
306
334
|
|
|
307
|
-
if (platformName === 'codex') {
|
|
335
|
+
if (platformName === 'codex' || platformName === 'kimi') {
|
|
336
|
+
const hostLabel = platformName === 'codex' ? 'Codex' : 'Kimi';
|
|
337
|
+
const preamble = platformName === 'codex' ? CODEX_PREAMBLE : KIMI_PREAMBLE;
|
|
308
338
|
let rendered = content.replaceAll(
|
|
309
339
|
'${CLAUDE_PLUGIN_ROOT}/bin/release-skill.mjs',
|
|
310
340
|
'$RELEASE_SKILL_ENTRY',
|
|
311
341
|
);
|
|
312
|
-
// A remaining bare host-root reference has no safe
|
|
342
|
+
// A remaining bare host-root reference has no safe path-derived equivalent.
|
|
313
343
|
if (rendered.includes('${CLAUDE_PLUGIN_ROOT}')) {
|
|
314
|
-
throw new Error(
|
|
344
|
+
throw new Error(`${hostLabel} rendering found an unsupported bare CLAUDE_PLUGIN_ROOT reference`);
|
|
315
345
|
}
|
|
316
346
|
// Preserve YAML frontmatter as the first bytes so the host can discover
|
|
317
347
|
// the skill. Insert the host-specific protocol immediately after it.
|
|
318
348
|
const frontmatterEnd = rendered.indexOf('\n---\n', 4);
|
|
319
349
|
if (!rendered.startsWith('---\n') || frontmatterEnd < 0) {
|
|
320
|
-
throw new Error(
|
|
350
|
+
throw new Error(`${hostLabel} rendering requires a valid leading YAML frontmatter block`);
|
|
321
351
|
}
|
|
322
352
|
const insertionPoint = frontmatterEnd + '\n---\n'.length;
|
|
323
|
-
return `${rendered.slice(0, insertionPoint)}\n${
|
|
353
|
+
return `${rendered.slice(0, insertionPoint)}\n${preamble}${rendered.slice(insertionPoint)}`;
|
|
324
354
|
}
|
|
325
355
|
|
|
326
356
|
return content;
|