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.
Files changed (54) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codebuddy-plugin/plugin.json +4 -2
  4. package/.codex-plugin/plugin.json +2 -2
  5. package/.kimi-plugin/plugin.json +1 -1
  6. package/CHANGELOG.md +41 -0
  7. package/CONTRIBUTING.md +27 -0
  8. package/INSTALL.md +95 -139
  9. package/INSTALL.zh-CN.md +70 -121
  10. package/README.md +265 -916
  11. package/README.zh-CN.md +222 -537
  12. package/adapters/claude/.claude-plugin/marketplace.json +1 -1
  13. package/adapters/claude/.claude-plugin/plugin.json +1 -1
  14. package/adapters/claude/bin/release-skill.bundle.mjs +18710 -16694
  15. package/adapters/claude/schemas/release-plan.schema.json +137 -0
  16. package/adapters/claude/schemas/release-project.schema.json +93 -0
  17. package/adapters/claude/schemas/release-run.schema.json +70 -2
  18. package/adapters/codex/.codex-plugin/plugin.json +2 -2
  19. package/adapters/codex/bin/release-skill.bundle.mjs +18710 -16694
  20. package/adapters/codex/schemas/release-plan.schema.json +137 -0
  21. package/adapters/codex/schemas/release-project.schema.json +93 -0
  22. package/adapters/codex/schemas/release-run.schema.json +70 -2
  23. package/adapters/kimi/.kimi-plugin/plugin.json +1 -1
  24. package/adapters/kimi/bin/release-skill.bundle.mjs +18710 -16694
  25. package/adapters/kimi/schemas/release-plan.schema.json +137 -0
  26. package/adapters/kimi/schemas/release-project.schema.json +93 -0
  27. package/adapters/kimi/schemas/release-run.schema.json +70 -2
  28. package/adapters/workbuddy/.codebuddy-plugin/plugin.json +1 -1
  29. package/adapters/workbuddy/bin/release-skill.bundle.mjs +18710 -16694
  30. package/adapters/workbuddy/schemas/release-plan.schema.json +137 -0
  31. package/adapters/workbuddy/schemas/release-project.schema.json +93 -0
  32. package/adapters/workbuddy/schemas/release-run.schema.json +70 -2
  33. package/bin/release-skill.bundle.mjs +18710 -16694
  34. package/package.json +1 -1
  35. package/schemas/release-plan.schema.json +137 -0
  36. package/schemas/release-project.schema.json +93 -0
  37. package/schemas/release-run.schema.json +70 -2
  38. package/src/adapters/plugin-marketplace.mjs +1602 -605
  39. package/src/commands/prepare.mjs +441 -32
  40. package/src/commands/publish.mjs +107 -75
  41. package/src/commands/reconcile.mjs +92 -327
  42. package/src/commands/setup.mjs +148 -20
  43. package/src/commands/verify.mjs +315 -25
  44. package/src/core/baseline.mjs +21 -1
  45. package/src/core/checkpoints.mjs +50 -7
  46. package/src/core/config.mjs +15 -0
  47. package/src/core/errors.mjs +2 -0
  48. package/src/core/installation-contract.mjs +341 -0
  49. package/src/core/plan.mjs +307 -6
  50. package/src/platforms/codebuddy.mjs +191 -238
  51. package/src/platforms/codex.mjs +369 -0
  52. package/src/platforms/kimi.mjs +164 -119
  53. package/src/platforms/registry.mjs +180 -4
  54. package/src/producers/build-adapters.mjs +9 -2
@@ -42,6 +42,10 @@ import {
42
42
  executeCodeBuddyManualRequirement,
43
43
  readCodeBuddyManifest,
44
44
  } from './codebuddy.mjs';
45
+ import {
46
+ executeCodexManualRequirement,
47
+ readCodexManifest,
48
+ } from './codex.mjs';
45
49
 
46
50
  // --- claude strategy -------------------------------------------------------
47
51
 
@@ -137,6 +141,12 @@ const CLAUDE = Object.freeze({
137
141
  // actionType -> adapter map from it (T2.2 step 3).
138
142
  adapter: 'plugin-marketplace',
139
143
  automatable: true,
144
+ // --- capability contract (平台能力契约) ------------------------------------
145
+ installMethod: 'structured-cli',
146
+ refStrength: 'name-ref',
147
+ outputProtocol: 'text',
148
+ identityEvidence: 'list-record',
149
+ degradationPolicy: 'block',
140
150
  cli: Object.freeze({
141
151
  binary: 'claude',
142
152
  marketplaceAdd: (repo, ref) => ['plugin', 'marketplace', 'add', `${repo}@${ref}`],
@@ -190,6 +200,12 @@ const CODEX = Object.freeze({
190
200
  actionType: 'codex-marketplace-install',
191
201
  adapter: 'plugin-marketplace',
192
202
  automatable: true,
203
+ // --- capability contract (平台能力契约) ------------------------------------
204
+ installMethod: 'structured-cli',
205
+ refStrength: 'commit-sha',
206
+ outputProtocol: 'structured',
207
+ identityEvidence: 'install-output',
208
+ degradationPolicy: 'human-attestation-with-fallback',
193
209
  cli: Object.freeze({
194
210
  binary: 'codex',
195
211
  marketplaceAdd: (repo, ref) => ['plugin', 'marketplace', 'add', repo, '--ref', ref, '--json'],
@@ -231,8 +247,9 @@ const CODEX = Object.freeze({
231
247
  extractInstallPath: codexExtractInstallPath,
232
248
  extractListIdentity: codexExtractListIdentity,
233
249
  crossValidateListEntry: codexCrossValidateListEntry,
234
- buildManualRequirement: null,
235
- readManifest: null,
250
+ // codex's human-attestation fallback strategy lives in ./codex.mjs
251
+ buildManualRequirement: executeCodexManualRequirement,
252
+ readManifest: readCodexManifest,
236
253
  }),
237
254
  });
238
255
 
@@ -242,6 +259,12 @@ const KIMI = Object.freeze({
242
259
  actionType: 'kimi-marketplace-install',
243
260
  adapter: 'plugin-marketplace',
244
261
  automatable: false,
262
+ // --- capability contract (平台能力契约) ------------------------------------
263
+ installMethod: 'interactive-only',
264
+ refStrength: 'unfixable',
265
+ outputProtocol: 'none',
266
+ identityEvidence: 'filesystem-payload',
267
+ degradationPolicy: 'human-attestation',
245
268
  cli: null,
246
269
  jsonProtocol: Object.freeze({
247
270
  listOutput: null,
@@ -249,7 +272,7 @@ const KIMI = Object.freeze({
249
272
  marketplaceAddOutput: null,
250
273
  pluginInstallOutput: null,
251
274
  }),
252
- isolationEnv: (home) => ({ HOME: home, KIMI_CODE_HOME: home }),
275
+ isolationEnv: (home) => ({ HOME: home }),
253
276
  // kimi never execs a CLI; its stable home is created by the
254
277
  // manual-requirement strategy under the attestation authority.
255
278
  isolationSubdirs: Object.freeze([]),
@@ -307,6 +330,12 @@ const CODEBUDDY = Object.freeze({
307
330
  actionType: 'codebuddy-marketplace-install',
308
331
  adapter: 'plugin-marketplace',
309
332
  automatable: false,
333
+ // --- capability contract (平台能力契约) ------------------------------------
334
+ installMethod: 'human-attestation',
335
+ refStrength: 'unfixable',
336
+ outputProtocol: 'none',
337
+ identityEvidence: 'human-attestation',
338
+ degradationPolicy: 'human-attestation',
310
339
  cli: null,
311
340
  jsonProtocol: Object.freeze({
312
341
  listOutput: null,
@@ -324,7 +353,10 @@ const CODEBUDDY = Object.freeze({
324
353
  manifestPaths: Object.freeze({
325
354
  // Single authoritative manifest (no precedence chain, unlike kimi).
326
355
  plugin: '.codebuddy-plugin/plugin.json',
327
- marketplace: null,
356
+ // CodeBuddy bundled-family uses the skill-family marketplace path.
357
+ // This ensures bundled-family installation contracts always include a
358
+ // marketplace entry without requiring an explicit marketplaceIndexPath.
359
+ marketplace: '.claude-plugin/marketplace.json',
328
360
  }),
329
361
  marketplaceSourceForm: null,
330
362
  // codebuddy installs from a unified marketplace but carries no marketplace
@@ -373,6 +405,11 @@ const VALID_MARKETPLACE_REF_FORMS = new Set(['sha', 'name', null]);
373
405
  const VALID_LIST_OUTPUTS = new Set(['array', 'installed-object', null]);
374
406
  const VALID_CLI_OUTPUTS = new Set(['json', null]);
375
407
  const VALID_ENTRY_VERSION_BINDING = new Set([true, false, null]);
408
+ const VALID_INSTALL_METHODS = new Set(['structured-cli', 'interactive-only', 'human-attestation']);
409
+ const VALID_REF_STRENGTHS = new Set(['commit-sha', 'name-ref', 'unfixable']);
410
+ const VALID_OUTPUT_PROTOCOLS = new Set(['structured', 'text', 'none']);
411
+ const VALID_IDENTITY_EVIDENCE = new Set(['list-record', 'install-output', 'filesystem-payload', 'human-attestation']);
412
+ const VALID_DEGRADATION_POLICIES = new Set(['block', 'human-attestation', 'human-attestation-with-fallback']);
376
413
 
377
414
  /**
378
415
  * Look up a platform descriptor by id.
@@ -453,6 +490,23 @@ export function assertRegistry(registry = PLATFORMS) {
453
490
  throw new Error(`platform registry: ${label} isolationSubdirs must be an array`);
454
491
  }
455
492
 
493
+ // --- capability contract validation (能力契约验证) -----------------------
494
+ if (!VALID_INSTALL_METHODS.has(platform.installMethod)) {
495
+ throw new Error(`platform registry: ${label} has illegal installMethod "${platform.installMethod}"`);
496
+ }
497
+ if (!VALID_REF_STRENGTHS.has(platform.refStrength)) {
498
+ throw new Error(`platform registry: ${label} has illegal refStrength "${platform.refStrength}"`);
499
+ }
500
+ if (!VALID_OUTPUT_PROTOCOLS.has(platform.outputProtocol)) {
501
+ throw new Error(`platform registry: ${label} has illegal outputProtocol "${platform.outputProtocol}"`);
502
+ }
503
+ if (!VALID_IDENTITY_EVIDENCE.has(platform.identityEvidence)) {
504
+ throw new Error(`platform registry: ${label} has illegal identityEvidence "${platform.identityEvidence}"`);
505
+ }
506
+ if (!VALID_DEGRADATION_POLICIES.has(platform.degradationPolicy)) {
507
+ throw new Error(`platform registry: ${label} has illegal degradationPolicy "${platform.degradationPolicy}"`);
508
+ }
509
+
456
510
  if (platform.automatable) {
457
511
  if (!platform.cli || typeof platform.cli.marketplaceAdd !== 'function'
458
512
  || typeof platform.cli.install !== 'function' || typeof platform.cli.list !== 'function') {
@@ -467,6 +521,16 @@ export function assertRegistry(registry = PLATFORMS) {
467
521
  if (typeof platform.strategy.extractInstallPath !== 'function') {
468
522
  throw new Error(`platform registry: automatable platform ${label} needs strategy.extractInstallPath`);
469
523
  }
524
+ // Platforms with degradationPolicy 'human-attestation-with-fallback'
525
+ // (codex) also need manual-requirement strategies for the fallback path.
526
+ if (platform.degradationPolicy === 'human-attestation-with-fallback') {
527
+ if (typeof platform.strategy.buildManualRequirement !== 'function') {
528
+ throw new Error(`platform registry: ${label} with human-attestation-with-fallback needs strategy.buildManualRequirement`);
529
+ }
530
+ if (typeof platform.strategy.readManifest !== 'function') {
531
+ throw new Error(`platform registry: ${label} with human-attestation-with-fallback needs strategy.readManifest`);
532
+ }
533
+ }
470
534
  } else {
471
535
  if (platform.cli !== null) {
472
536
  throw new Error(`platform registry: non-automatable platform ${label} must have cli === null`);
@@ -481,8 +545,120 @@ export function assertRegistry(registry = PLATFORMS) {
481
545
  throw new Error(`platform registry: non-automatable platform ${label} needs strategy.readManifest`);
482
546
  }
483
547
  }
548
+
549
+ // --- capability contract consistency rules (能力契约一致性规则) -----------
550
+ // automatable === true 必须有 installMethod === 'structured-cli'
551
+ if (platform.automatable === true && platform.installMethod !== 'structured-cli') {
552
+ throw new Error(`platform registry: ${label} is automatable but installMethod is "${platform.installMethod}" (expected "structured-cli")`);
553
+ }
554
+ // automatable === false 必须有 installMethod !== 'structured-cli'
555
+ if (platform.automatable === false && platform.installMethod === 'structured-cli') {
556
+ throw new Error(`platform registry: ${label} is not automatable but installMethod is "structured-cli"`);
557
+ }
558
+ // installMethod === 'structured-cli' 必须有 refStrength !== 'unfixable'
559
+ if (platform.installMethod === 'structured-cli' && platform.refStrength === 'unfixable') {
560
+ throw new Error(`platform registry: ${label} has structured-cli install but unfixable refStrength`);
561
+ }
562
+ // installMethod === 'human-attestation' 必须有 identityEvidence === 'human-attestation'
563
+ if (platform.installMethod === 'human-attestation' && platform.identityEvidence !== 'human-attestation') {
564
+ throw new Error(`platform registry: ${label} has human-attestation install but identityEvidence is "${platform.identityEvidence}"`);
565
+ }
484
566
  }
485
567
  }
486
568
 
487
569
  // Self-validate at import time: a malformed registry never reaches runtime.
488
570
  assertRegistry();
571
+
572
+ // --- capability-driven routing & conflict detection (能力驱动路由与矛盾检测) --
573
+
574
+ /**
575
+ * 根据平台描述符的 installMethod 决定执行路由。
576
+ *
577
+ * @param {object} platform - 平台描述符
578
+ * @returns {{ route: string, reason: string }}
579
+ * @throws {Error} 无法路由时抛出
580
+ */
581
+ export function resolvePlatformRoute(platform) {
582
+ const { installMethod } = platform;
583
+ if (installMethod === 'structured-cli') {
584
+ return { route: 'structured-cli', reason: `installMethod is structured-cli` };
585
+ }
586
+ if (installMethod === 'interactive-only' || installMethod === 'human-attestation') {
587
+ return { route: 'human-attestation', reason: `installMethod is ${installMethod}` };
588
+ }
589
+ throw new Error(
590
+ `platform "${platform?.id ?? '<unknown>'}" with installMethod="${installMethod}" cannot be routed`,
591
+ );
592
+ }
593
+
594
+ /**
595
+ * 检测平台能力描述符中的矛盾。
596
+ *
597
+ * @param {object} platform - 平台描述符
598
+ * @returns {{ hasConflict: boolean, conflicts: string[] }}
599
+ * @throws {Error} 发现矛盾时抛出
600
+ */
601
+ export function resolveCapabilityConflicts(platform) {
602
+ const conflicts = [];
603
+ const { id, automatable, installMethod, refStrength, identityEvidence, cli, strategy } = platform;
604
+
605
+ // automatable <-> installMethod 一致性
606
+ if (automatable === true && installMethod !== 'structured-cli') {
607
+ conflicts.push(`automatable=true but installMethod="${installMethod}"`);
608
+ }
609
+ if (automatable === false && installMethod === 'structured-cli') {
610
+ conflicts.push(`automatable=false but installMethod="structured-cli"`);
611
+ }
612
+
613
+ // installMethod <-> refStrength 一致性
614
+ if (installMethod === 'structured-cli' && refStrength === 'unfixable') {
615
+ conflicts.push(`structured-cli with unfixable refStrength`);
616
+ }
617
+
618
+ // installMethod <-> identityEvidence 一致性
619
+ if (installMethod === 'human-attestation' && identityEvidence !== 'human-attestation') {
620
+ conflicts.push(`human-attestation install but identityEvidence="${identityEvidence}"`);
621
+ }
622
+
623
+ // 自动化平台必须有 cli 和策略解析函数
624
+ if (automatable === true) {
625
+ if (!cli || typeof cli !== 'object') {
626
+ conflicts.push(`automatable=true but cli is missing`);
627
+ }
628
+ if (!strategy || typeof strategy.parseListOutput !== 'function') {
629
+ conflicts.push(`automatable=true but strategy.parseListOutput is missing`);
630
+ }
631
+ if (!strategy || typeof strategy.extractInstallPath !== 'function') {
632
+ conflicts.push(`automatable=true but strategy.extractInstallPath is missing`);
633
+ }
634
+ }
635
+
636
+ // 非自动化平台必须 cli === null,且有手动策略函数
637
+ if (automatable === false) {
638
+ if (cli !== null) {
639
+ conflicts.push(`automatable=false but cli is not null`);
640
+ }
641
+ if (!strategy || typeof strategy.buildManualRequirement !== 'function') {
642
+ conflicts.push(`automatable=false but strategy.buildManualRequirement is missing`);
643
+ }
644
+ if (!strategy || typeof strategy.readManifest !== 'function') {
645
+ conflicts.push(`automatable=false but strategy.readManifest is missing`);
646
+ }
647
+ }
648
+
649
+ if (conflicts.length > 0) {
650
+ throw new Error(`capability conflict in platform "${id}": ${conflicts.join('; ')}`);
651
+ }
652
+
653
+ return { hasConflict: false, conflicts: [] };
654
+ }
655
+
656
+ /**
657
+ * 从 installMethod 派生 automatable 标志。
658
+ *
659
+ * @param {object} platform - 平台描述符
660
+ * @returns {boolean}
661
+ */
662
+ export function deriveAutomatable(platform) {
663
+ return platform.installMethod === 'structured-cli';
664
+ }
@@ -438,9 +438,16 @@ async function generateAdapterFiles(platform, skills, templateJson, root, market
438
438
  if (typeof adapted.skills === 'string') {
439
439
  adapted.skills = platform.name === 'workbuddy' ? ['./skills/'] : './skills/';
440
440
  } else if (Array.isArray(adapted.skills)) {
441
- for (const skill of adapted.skills) {
441
+ // Handle both string array and object array forms
442
+ adapted.skills = adapted.skills.map((skill) => {
443
+ if (typeof skill === 'string') {
444
+ // String array: transform to relative path for the adapter
445
+ return platform.name === 'workbuddy' ? './skills/' : skill;
446
+ }
447
+ // Object array: preserve existing behavior
442
448
  skill.source = `../skills/${skill.name}/SKILL.md`;
443
- }
449
+ return skill;
450
+ });
444
451
  }
445
452
 
446
453
  const pluginJsonContent = JSON.stringify(adapted, null, 2) + '\n';