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.
- package/.claude-plugin/plugin.json +1 -1
- package/.codebuddy-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +2 -2
- package/.cursor-plugin/plugin.json +1 -1
- package/.kimi-plugin/plugin.json +1 -1
- package/.qoder-plugin/plugin.json +1 -1
- package/CHANGELOG.md +56 -0
- package/INSTALL.md +2 -2
- package/INSTALL.zh-CN.md +2 -2
- package/README.md +16 -10
- package/README.zh-CN.md +16 -10
- package/adapters/claude/.claude-plugin/plugin.json +1 -1
- package/adapters/claude/bin/consumer-contract-vectors.json +7 -7
- package/adapters/claude/bin/foundation-resource-binding.json +1 -1
- package/adapters/claude/bin/release-skill.bundle.mjs +2631 -1678
- package/adapters/claude/skills/release-assess/SKILL.md +64 -1
- package/adapters/claude/skills/release-finish/SKILL.md +2 -0
- package/adapters/claude/skills/release-help/SKILL.md +2 -2
- package/adapters/claude/skills/release-setup/SKILL.md +5 -2
- package/adapters/codex/.codex-plugin/plugin.json +2 -2
- package/adapters/codex/bin/consumer-contract-vectors.json +7 -7
- package/adapters/codex/bin/foundation-resource-binding.json +1 -1
- package/adapters/codex/bin/release-skill.bundle.mjs +2631 -1678
- package/adapters/codex/skills/release-assess/SKILL.md +64 -1
- package/adapters/codex/skills/release-finish/SKILL.md +2 -0
- package/adapters/codex/skills/release-help/SKILL.md +2 -2
- package/adapters/codex/skills/release-setup/SKILL.md +5 -2
- package/adapters/cursor/.cursor-plugin/plugin.json +1 -1
- package/adapters/cursor/bin/consumer-contract-vectors.json +7 -7
- package/adapters/cursor/bin/foundation-resource-binding.json +1 -1
- package/adapters/cursor/bin/release-skill.bundle.mjs +2631 -1678
- package/adapters/cursor/skills/release-assess/SKILL.md +64 -1
- package/adapters/cursor/skills/release-finish/SKILL.md +2 -0
- package/adapters/cursor/skills/release-help/SKILL.md +2 -2
- package/adapters/cursor/skills/release-setup/SKILL.md +5 -2
- package/adapters/kimi/.kimi-plugin/plugin.json +1 -1
- package/adapters/kimi/bin/consumer-contract-vectors.json +7 -7
- package/adapters/kimi/bin/foundation-resource-binding.json +1 -1
- package/adapters/kimi/bin/release-skill.bundle.mjs +2631 -1678
- package/adapters/kimi/skills/release-assess/SKILL.md +64 -1
- package/adapters/kimi/skills/release-finish/SKILL.md +2 -0
- package/adapters/kimi/skills/release-help/SKILL.md +2 -2
- package/adapters/kimi/skills/release-setup/SKILL.md +5 -2
- package/adapters/qoder/.qoder-plugin/plugin.json +1 -1
- package/adapters/qoder/bin/consumer-contract-vectors.json +7 -7
- package/adapters/qoder/bin/foundation-resource-binding.json +1 -1
- package/adapters/qoder/bin/release-skill.bundle.mjs +2631 -1678
- package/adapters/qoder/skills/release-assess/SKILL.md +64 -1
- package/adapters/qoder/skills/release-finish/SKILL.md +2 -0
- package/adapters/qoder/skills/release-help/SKILL.md +2 -2
- package/adapters/qoder/skills/release-setup/SKILL.md +5 -2
- package/adapters/workbuddy/.codebuddy-plugin/plugin.json +1 -1
- package/adapters/workbuddy/bin/consumer-contract-vectors.json +7 -7
- package/adapters/workbuddy/bin/foundation-resource-binding.json +1 -1
- package/adapters/workbuddy/bin/release-skill.bundle.mjs +2631 -1678
- package/adapters/workbuddy/skills/release-assess/SKILL.md +64 -1
- package/adapters/workbuddy/skills/release-finish/SKILL.md +2 -0
- package/adapters/workbuddy/skills/release-help/SKILL.md +2 -2
- package/adapters/workbuddy/skills/release-setup/SKILL.md +5 -2
- package/bin/consumer-contract-vectors.json +7 -7
- package/bin/foundation-resource-binding.json +1 -1
- package/bin/release-skill-cli.mjs +45 -1
- package/bin/release-skill.bundle.mjs +2631 -1678
- package/package.json +4 -4
- package/platform-manifest.json +4 -4
- package/skills/release-assess/SKILL.md +64 -1
- package/skills/release-finish/SKILL.md +2 -0
- package/skills/release-help/SKILL.md +2 -2
- package/skills/release-setup/SKILL.md +5 -2
- package/skills-src/release-assess/SKILL.md +64 -1
- package/skills-src/release-finish/SKILL.md +2 -0
- package/skills-src/release-help/SKILL.md +2 -2
- package/skills-src/release-setup/SKILL.md +5 -2
- package/src/commands/post-release-local.mjs +36 -92
- package/src/commands/setup.mjs +149 -6
- package/src/core/adoption-assessment.mjs +502 -4
- package/src/core/local-host-observation.mjs +225 -0
- package/src/platforms/registry.mjs +12 -4
- package/src/producers/foundation-resource-projection.mjs +3 -3
package/src/commands/setup.mjs
CHANGED
|
@@ -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({
|
|
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(
|
|
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
|
}
|