release-skill 0.9.26 → 0.9.28

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 (79) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codebuddy-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +2 -2
  4. package/.cursor-plugin/plugin.json +1 -1
  5. package/.kimi-plugin/plugin.json +1 -1
  6. package/.qoder-plugin/plugin.json +1 -1
  7. package/CHANGELOG.md +56 -0
  8. package/INSTALL.md +2 -2
  9. package/INSTALL.zh-CN.md +2 -2
  10. package/README.md +16 -10
  11. package/README.zh-CN.md +16 -10
  12. package/adapters/claude/.claude-plugin/plugin.json +1 -1
  13. package/adapters/claude/bin/consumer-contract-vectors.json +7 -7
  14. package/adapters/claude/bin/foundation-resource-binding.json +1 -1
  15. package/adapters/claude/bin/release-skill.bundle.mjs +2631 -1678
  16. package/adapters/claude/skills/release-assess/SKILL.md +64 -1
  17. package/adapters/claude/skills/release-finish/SKILL.md +2 -0
  18. package/adapters/claude/skills/release-help/SKILL.md +2 -2
  19. package/adapters/claude/skills/release-setup/SKILL.md +5 -2
  20. package/adapters/codex/.codex-plugin/plugin.json +2 -2
  21. package/adapters/codex/bin/consumer-contract-vectors.json +7 -7
  22. package/adapters/codex/bin/foundation-resource-binding.json +1 -1
  23. package/adapters/codex/bin/release-skill.bundle.mjs +2631 -1678
  24. package/adapters/codex/skills/release-assess/SKILL.md +64 -1
  25. package/adapters/codex/skills/release-finish/SKILL.md +2 -0
  26. package/adapters/codex/skills/release-help/SKILL.md +2 -2
  27. package/adapters/codex/skills/release-setup/SKILL.md +5 -2
  28. package/adapters/cursor/.cursor-plugin/plugin.json +1 -1
  29. package/adapters/cursor/bin/consumer-contract-vectors.json +7 -7
  30. package/adapters/cursor/bin/foundation-resource-binding.json +1 -1
  31. package/adapters/cursor/bin/release-skill.bundle.mjs +2631 -1678
  32. package/adapters/cursor/skills/release-assess/SKILL.md +64 -1
  33. package/adapters/cursor/skills/release-finish/SKILL.md +2 -0
  34. package/adapters/cursor/skills/release-help/SKILL.md +2 -2
  35. package/adapters/cursor/skills/release-setup/SKILL.md +5 -2
  36. package/adapters/kimi/.kimi-plugin/plugin.json +1 -1
  37. package/adapters/kimi/bin/consumer-contract-vectors.json +7 -7
  38. package/adapters/kimi/bin/foundation-resource-binding.json +1 -1
  39. package/adapters/kimi/bin/release-skill.bundle.mjs +2631 -1678
  40. package/adapters/kimi/skills/release-assess/SKILL.md +64 -1
  41. package/adapters/kimi/skills/release-finish/SKILL.md +2 -0
  42. package/adapters/kimi/skills/release-help/SKILL.md +2 -2
  43. package/adapters/kimi/skills/release-setup/SKILL.md +5 -2
  44. package/adapters/qoder/.qoder-plugin/plugin.json +1 -1
  45. package/adapters/qoder/bin/consumer-contract-vectors.json +7 -7
  46. package/adapters/qoder/bin/foundation-resource-binding.json +1 -1
  47. package/adapters/qoder/bin/release-skill.bundle.mjs +2631 -1678
  48. package/adapters/qoder/skills/release-assess/SKILL.md +64 -1
  49. package/adapters/qoder/skills/release-finish/SKILL.md +2 -0
  50. package/adapters/qoder/skills/release-help/SKILL.md +2 -2
  51. package/adapters/qoder/skills/release-setup/SKILL.md +5 -2
  52. package/adapters/workbuddy/.codebuddy-plugin/plugin.json +1 -1
  53. package/adapters/workbuddy/bin/consumer-contract-vectors.json +7 -7
  54. package/adapters/workbuddy/bin/foundation-resource-binding.json +1 -1
  55. package/adapters/workbuddy/bin/release-skill.bundle.mjs +2631 -1678
  56. package/adapters/workbuddy/skills/release-assess/SKILL.md +64 -1
  57. package/adapters/workbuddy/skills/release-finish/SKILL.md +2 -0
  58. package/adapters/workbuddy/skills/release-help/SKILL.md +2 -2
  59. package/adapters/workbuddy/skills/release-setup/SKILL.md +5 -2
  60. package/bin/consumer-contract-vectors.json +7 -7
  61. package/bin/foundation-resource-binding.json +1 -1
  62. package/bin/release-skill-cli.mjs +45 -1
  63. package/bin/release-skill.bundle.mjs +2631 -1678
  64. package/package.json +4 -4
  65. package/platform-manifest.json +4 -4
  66. package/skills/release-assess/SKILL.md +64 -1
  67. package/skills/release-finish/SKILL.md +2 -0
  68. package/skills/release-help/SKILL.md +2 -2
  69. package/skills/release-setup/SKILL.md +5 -2
  70. package/skills-src/release-assess/SKILL.md +64 -1
  71. package/skills-src/release-finish/SKILL.md +2 -0
  72. package/skills-src/release-help/SKILL.md +2 -2
  73. package/skills-src/release-setup/SKILL.md +5 -2
  74. package/src/commands/post-release-local.mjs +36 -92
  75. package/src/commands/setup.mjs +149 -6
  76. package/src/core/adoption-assessment.mjs +502 -4
  77. package/src/core/local-host-observation.mjs +225 -0
  78. package/src/platforms/registry.mjs +12 -4
  79. package/src/producers/foundation-resource-projection.mjs +3 -3
@@ -23,7 +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
+ import { getPlatform, PLATFORMS, normalizeHostId } from '../platforms/registry.mjs';
27
27
  import { checkNpmEntryClosure } from '../npm/npm-entry-closure.mjs';
28
28
  import {
29
29
  CONFIG_EXISTS,
@@ -47,10 +47,14 @@ import {
47
47
  deriveHookDurations,
48
48
  deriveLongHookSuggestions,
49
49
  derivePreHookPublicSurfaceFinding,
50
+ emptyHostCoverage,
51
+ evaluateHostCoverage,
52
+ publicHostCoverage,
50
53
  scanGateDeclarationFindings,
51
54
  FINDING_CATEGORY,
52
55
  ASSESSMENT_STATUS,
53
56
  } from '../core/adoption-assessment.mjs';
57
+ import { observeHostPlugin } from '../core/local-host-observation.mjs';
54
58
  import {
55
59
  checkCommonDocs,
56
60
  checkPackageMetadata,
@@ -2684,6 +2688,99 @@ async function readEvidenceEvents(root) {
2684
2688
  return events;
2685
2689
  }
2686
2690
 
2691
+ async function loadHostCoverageInput(hostCoveragePath) {
2692
+ if (hostCoveragePath == null || hostCoveragePath === '') {
2693
+ return { requested: false, input: null, loadError: null };
2694
+ }
2695
+ if (typeof hostCoveragePath !== 'string') {
2696
+ return { requested: true, input: null, loadError: 'host coverage path is invalid' };
2697
+ }
2698
+ try {
2699
+ const raw = await readFile(hostCoveragePath, 'utf8');
2700
+ let parsed;
2701
+ try {
2702
+ parsed = JSON.parse(raw);
2703
+ } catch {
2704
+ return { requested: true, input: null, loadError: 'host coverage file is not valid JSON' };
2705
+ }
2706
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
2707
+ return { requested: true, input: null, loadError: 'host coverage file must be a JSON object' };
2708
+ }
2709
+ return { requested: true, input: parsed, loadError: null };
2710
+ } catch (error) {
2711
+ return {
2712
+ requested: true,
2713
+ input: null,
2714
+ loadError: error?.code === 'ENOENT'
2715
+ ? 'host coverage file is missing'
2716
+ : (error.message ?? 'host coverage file could not be read'),
2717
+ };
2718
+ }
2719
+ }
2720
+
2721
+ async function canonicalObservationHost(value) {
2722
+ if (typeof value !== 'string' || value.trim().length === 0) return null;
2723
+ const trimmed = value.trim();
2724
+ let foundationId = trimmed;
2725
+ try {
2726
+ foundationId = await normalizeHostId(trimmed);
2727
+ } catch {
2728
+ // Fall through to the local platform registry, matching adoption diagnosis.
2729
+ }
2730
+ try {
2731
+ return getPlatform(foundationId).id;
2732
+ } catch {
2733
+ try {
2734
+ return getPlatform(trimmed).id;
2735
+ } catch {
2736
+ return foundationId;
2737
+ }
2738
+ }
2739
+ }
2740
+
2741
+ async function collectLiveHostObservations({ input, runHostCommand } = {}) {
2742
+ const live = [];
2743
+ if (!input || !Array.isArray(input.units)) return live;
2744
+ for (const unit of input.units) {
2745
+ if (typeof unit?.unitId !== 'string' || unit.unitId.length === 0) continue;
2746
+ const includeHosts = new Set();
2747
+ for (const raw of Array.isArray(unit?.delivery?.include) ? unit.delivery.include : []) {
2748
+ const host = await canonicalObservationHost(raw);
2749
+ if (host) includeHosts.add(host);
2750
+ }
2751
+ const excludeHosts = new Set();
2752
+ for (const entry of Array.isArray(unit?.delivery?.exclude) ? unit.delivery.exclude : []) {
2753
+ const host = await canonicalObservationHost(entry?.host);
2754
+ if (host) excludeHosts.add(host);
2755
+ }
2756
+ const identities = new Map();
2757
+ for (const observation of Array.isArray(unit?.observations) ? unit.observations : []) {
2758
+ const host = await canonicalObservationHost(observation?.host);
2759
+ if (host == null || identities.has(host)) continue;
2760
+ const plugin = typeof observation?.plugin === 'string' && observation.plugin.length > 0
2761
+ ? observation.plugin
2762
+ : null;
2763
+ const marketplace = typeof observation?.marketplace === 'string' && observation.marketplace.length > 0
2764
+ ? observation.marketplace
2765
+ : null;
2766
+ identities.set(host, { plugin, marketplace });
2767
+ }
2768
+ for (const host of includeHosts) {
2769
+ if (excludeHosts.has(host)) continue;
2770
+ const identity = identities.get(host);
2771
+ if (identity?.plugin == null || identity?.marketplace == null) continue;
2772
+ const observed = await observeHostPlugin({
2773
+ host,
2774
+ plugin: identity.plugin,
2775
+ marketplace: identity.marketplace,
2776
+ run: runHostCommand,
2777
+ });
2778
+ live.push({ ...observed, unitId: unit.unitId });
2779
+ }
2780
+ }
2781
+ return live;
2782
+ }
2783
+
2687
2784
  /**
2688
2785
  * Build the NOT_CONFIGURED report (acceptance scenario 1).
2689
2786
  *
@@ -2694,9 +2791,10 @@ async function readEvidenceEvents(root) {
2694
2791
  *
2695
2792
  * @param {string} root - Project root.
2696
2793
  * @param {string} configPath - Expected config path.
2794
+ * @param {Object} [hostCoverage] - Host-coverage diagnosis result.
2697
2795
  * @returns {Promise<Object>} Frozen report.
2698
2796
  */
2699
- async function reportNotConfigured(root, configPath) {
2797
+ async function reportNotConfigured(root, configPath, hostCoverage = emptyHostCoverage()) {
2700
2798
  const facts = await discoverFacts(root);
2701
2799
  const candidates = buildCandidates(facts);
2702
2800
  const discovery = {
@@ -2732,6 +2830,7 @@ async function reportNotConfigured(root, configPath) {
2732
2830
  field: '远端前提(npm/GitHub 可用性、发布标签序列)',
2733
2831
  note: '评估默认离线,未观测远端;不能据此否定本地接入,也不代表具备生产发布条件。',
2734
2832
  }],
2833
+ hostCoverage: publicHostCoverage(hostCoverage),
2735
2834
  next,
2736
2835
  discovery,
2737
2836
  summary: [
@@ -2739,6 +2838,9 @@ async function reportNotConfigured(root, configPath) {
2739
2838
  '接入状态: 尚未配置',
2740
2839
  '必选缺口 0 | 已满足 0 | 可选建议 0 | 不适用 0',
2741
2840
  `发现 ${discovery.unitCandidates.length} 个候选发布单元(只读预览,未写入)`,
2841
+ ...(hostCoverage.requested
2842
+ ? [`宿主覆盖: 已请求,${hostCoverage.complete ? '事实完整' : '事实不完整'},未把未知写成已接入。`]
2843
+ : []),
2742
2844
  `下一步: ${next}`,
2743
2845
  '注意: 评估默认离线且只读,未访问远端,未修改任何文件。',
2744
2846
  ].join('\n'),
@@ -2755,9 +2857,10 @@ async function reportNotConfigured(root, configPath) {
2755
2857
  * @param {string} root - Project root.
2756
2858
  * @param {string} configPath - Config path.
2757
2859
  * @param {Object} error - ReleaseError with details.
2860
+ * @param {Object} [hostCoverage] - Host-coverage diagnosis result.
2758
2861
  * @returns {Promise<Object>} Frozen report.
2759
2862
  */
2760
- async function reportConfigLoadError(root, configPath, error) {
2863
+ async function reportConfigLoadError(root, configPath, error, hostCoverage = emptyHostCoverage()) {
2761
2864
  const findings = [];
2762
2865
  const details = error.details ?? {};
2763
2866
  if (Array.isArray(details.validationErrors)) {
@@ -2834,11 +2937,15 @@ async function reportConfigLoadError(root, configPath, error) {
2834
2937
  field: '远端前提(npm/GitHub 可用性、发布标签序列)',
2835
2938
  note: '评估默认离线,未观测远端;不能据此否定本地接入,也不代表具备生产发布条件。',
2836
2939
  }],
2940
+ hostCoverage: publicHostCoverage(hostCoverage),
2837
2941
  summary: [
2838
2942
  '项目: (配置加载失败)',
2839
2943
  '接入状态: 部分接入(存在必选缺口)',
2840
2944
  `必选缺口 ${findings.length} | 已满足 0 | 可选建议 0 | 不适用 0`,
2841
2945
  ...findings.map((finding) => ` - [${finding.code}] ${finding.fieldPath ?? ''} ${finding.message}`),
2946
+ ...(hostCoverage.requested
2947
+ ? [`宿主覆盖: 已请求,${hostCoverage.complete ? '事实完整' : '事实不完整'},配置无效时不得宣称覆盖完成。`]
2948
+ : []),
2842
2949
  `下一步: 修复全部配置错误后重新运行 release-skill setup --assess-adoption。`,
2843
2950
  '注意: 评估默认离线且只读,未访问远端,未修改任何文件。',
2844
2951
  ].join('\n'),
@@ -2858,9 +2965,18 @@ async function reportConfigLoadError(root, configPath, error) {
2858
2965
  *
2859
2966
  * @param {Object} options
2860
2967
  * @param {string} options.root - Absolute project root.
2968
+ * @param {string} [options.hostCoveragePath] - Explicit host-coverage input JSON.
2969
+ * @param {boolean} [options.observeHosts] - Observe Claude/Codex from input identities.
2970
+ * @param {Function} [options.runHostCommand] - Injected process runner for observation tests.
2861
2971
  * @returns {Promise<Object>} Adoption report.
2862
2972
  */
2863
- export async function assessAdoption({ root, conclusionOutput } = {}) {
2973
+ export async function assessAdoption({
2974
+ root,
2975
+ conclusionOutput,
2976
+ hostCoveragePath,
2977
+ observeHosts = false,
2978
+ runHostCommand,
2979
+ } = {}) {
2864
2980
  if (!root || typeof root !== 'string' || !isAbsolute(root)) {
2865
2981
  throw setupError(CONFIG_INVALID, 'assessment root must be an absolute path');
2866
2982
  }
@@ -2874,6 +2990,24 @@ export async function assessAdoption({ root, conclusionOutput } = {}) {
2874
2990
  conclusionOutput,
2875
2991
  });
2876
2992
  const configPath = join(rootReal, '.release-skill', 'project.yaml');
2993
+ const loadedCoverage = await loadHostCoverageInput(
2994
+ typeof hostCoveragePath === 'string' && !isAbsolute(hostCoveragePath)
2995
+ ? resolve(rootReal, hostCoveragePath)
2996
+ : hostCoveragePath,
2997
+ );
2998
+ const liveObservations = loadedCoverage.requested && observeHosts === true && loadedCoverage.input
2999
+ ? await collectLiveHostObservations({
3000
+ input: loadedCoverage.input,
3001
+ runHostCommand,
3002
+ })
3003
+ : [];
3004
+ const diagnoseHostCoverage = (config) => evaluateHostCoverage({
3005
+ requested: loadedCoverage.requested,
3006
+ input: loadedCoverage.input,
3007
+ loadError: loadedCoverage.loadError,
3008
+ config,
3009
+ liveObservations,
3010
+ });
2877
3011
 
2878
3012
  let configExists = false;
2879
3013
  try {
@@ -2883,7 +3017,7 @@ export async function assessAdoption({ root, conclusionOutput } = {}) {
2883
3017
  if (error.code !== 'ENOENT') throw error;
2884
3018
  }
2885
3019
  if (!configExists) {
2886
- return finish(await reportNotConfigured(rootReal, configPath));
3020
+ return finish(await reportNotConfigured(rootReal, configPath, await diagnoseHostCoverage(null)));
2887
3021
  }
2888
3022
 
2889
3023
  let loaded;
@@ -2891,7 +3025,12 @@ export async function assessAdoption({ root, conclusionOutput } = {}) {
2891
3025
  loaded = await loadProjectConfig({ root: rootReal });
2892
3026
  } catch (error) {
2893
3027
  if (error.code !== CONFIG_INVALID) throw error;
2894
- return finish(await reportConfigLoadError(rootReal, configPath, error));
3028
+ return finish(await reportConfigLoadError(
3029
+ rootReal,
3030
+ configPath,
3031
+ error,
3032
+ await diagnoseHostCoverage(null),
3033
+ ));
2895
3034
  }
2896
3035
  const { config, configDigest } = loaded;
2897
3036
 
@@ -3233,6 +3372,9 @@ export async function assessAdoption({ root, conclusionOutput } = {}) {
3233
3372
  const gateSuggestions = unconfiguredGateAssessments.filter((entry) => entry.draft !== null);
3234
3373
  const gateDiagnostics = unconfiguredGateAssessments.filter((entry) => entry.draft === null);
3235
3374
 
3375
+ const hostCoverage = await diagnoseHostCoverage(config);
3376
+ findings.push(...hostCoverage.findings);
3377
+
3236
3378
  const hasBlocking = findings.some((f) => f.category === FINDING_CATEGORY.MANDATORY_GAP);
3237
3379
  const next = hasBlocking
3238
3380
  ? '修复全部必选缺口后重新运行 release-skill setup --assess-adoption。'
@@ -3248,5 +3390,6 @@ export async function assessAdoption({ root, conclusionOutput } = {}) {
3248
3390
  gateSuggestions,
3249
3391
  gateDiagnostics,
3250
3392
  next,
3393
+ hostCoverage,
3251
3394
  }));
3252
3395
  }