release-skill 0.9.20 → 0.9.22
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 +54 -0
- package/INSTALL.md +2 -2
- package/INSTALL.zh-CN.md +2 -2
- package/README.md +23 -27
- package/README.zh-CN.md +20 -25
- package/adapters/claude/.claude-plugin/plugin.json +1 -1
- package/adapters/claude/bin/release-skill.bundle.mjs +221 -26
- package/adapters/claude/skills/release-finish/SKILL.md +5 -3
- package/adapters/claude/skills/release-help/SKILL.md +2 -2
- package/adapters/codex/.codex-plugin/plugin.json +2 -2
- package/adapters/codex/bin/release-skill.bundle.mjs +221 -26
- package/adapters/codex/skills/release-finish/SKILL.md +5 -3
- package/adapters/codex/skills/release-help/SKILL.md +2 -2
- package/adapters/cursor/.cursor-plugin/plugin.json +1 -1
- package/adapters/cursor/bin/release-skill.bundle.mjs +221 -26
- package/adapters/cursor/skills/release-finish/SKILL.md +5 -3
- package/adapters/cursor/skills/release-help/SKILL.md +2 -2
- package/adapters/kimi/.kimi-plugin/plugin.json +1 -1
- package/adapters/kimi/bin/release-skill.bundle.mjs +221 -26
- package/adapters/kimi/skills/release-finish/SKILL.md +5 -3
- package/adapters/kimi/skills/release-help/SKILL.md +2 -2
- package/adapters/qoder/.qoder-plugin/plugin.json +1 -1
- package/adapters/qoder/bin/release-skill.bundle.mjs +221 -26
- package/adapters/qoder/skills/release-finish/SKILL.md +5 -3
- package/adapters/qoder/skills/release-help/SKILL.md +2 -2
- package/adapters/workbuddy/.codebuddy-plugin/plugin.json +1 -1
- package/adapters/workbuddy/bin/release-skill.bundle.mjs +221 -26
- package/adapters/workbuddy/skills/release-finish/SKILL.md +5 -3
- package/adapters/workbuddy/skills/release-help/SKILL.md +2 -2
- package/bin/release-skill.bundle.mjs +221 -26
- package/package.json +1 -1
- package/platform-manifest.json +4 -4
- package/references/02-project-config.md +2 -0
- package/skills/release-finish/SKILL.md +5 -3
- package/skills/release-help/SKILL.md +2 -2
- package/skills-src/release-finish/SKILL.md +5 -3
- package/skills-src/release-help/SKILL.md +2 -2
- package/src/commands/post-release-local.mjs +236 -30
|
@@ -9,9 +9,9 @@ const __bundlePkgRoot = __bundleResolve(__bundleDirname(__bundleFileURLToPath(im
|
|
|
9
9
|
// Provide a real require() for CJS packages bundled into ESM (e.g. yaml, ajv).
|
|
10
10
|
const __bundleRealRequire = __bundleCreateRequire(import.meta.url);
|
|
11
11
|
// Package identity injected at build time — closure-independent --version probe.
|
|
12
|
-
const __bundlePkg = Object.freeze({"name":"release-skill","version":"0.9.
|
|
12
|
+
const __bundlePkg = Object.freeze({"name":"release-skill","version":"0.9.22"});
|
|
13
13
|
// Build-time source digest for the BUNDLE_STALE freshness gate (see above).
|
|
14
|
-
const __bundleSourceDigest = "
|
|
14
|
+
const __bundleSourceDigest = "45eaf443ea0ae412317287ca2fa661303666663c304c4691330b5ea1e700881d";
|
|
15
15
|
|
|
16
16
|
var __create = Object.create;
|
|
17
17
|
var __defProp = Object.defineProperty;
|
|
@@ -134642,14 +134642,9 @@ function hubTargets(plan) {
|
|
|
134642
134642
|
if (!local) return [];
|
|
134643
134643
|
const hub = { ...local.hub, githubHost: local.hub.githubHost ?? "github.com" };
|
|
134644
134644
|
const unit = (plan.units ?? []).find((candidate) => candidate.id === declaration.unitId);
|
|
134645
|
-
const
|
|
134646
|
-
const
|
|
134647
|
-
|
|
134648
|
-
codex: "Use Codex\u2019s existing marketplace management entry.",
|
|
134649
|
-
kimi: `Use the frozen GitHub Release${tag ? ` (${tag})` : ""} and the existing manual confirmation path; Kimi has no marketplace index.`,
|
|
134650
|
-
codebuddy: "Handle manually; CodeBuddy cannot pin a Hub ref in this flow.",
|
|
134651
|
-
workbuddy: "Handle manually; WorkBuddy cannot pin a Hub ref in this flow (it follows the CodeBuddy manual boundary)."
|
|
134652
|
-
};
|
|
134645
|
+
const tagActions = (plan.externalActions ?? []).filter((action) => action.type === "create-tag" && action.unitId === declaration.unitId);
|
|
134646
|
+
const tagIdentity = tagActions[0]?.parameters;
|
|
134647
|
+
const tag = tagIdentity?.tag;
|
|
134653
134648
|
return local.hosts.map((host) => {
|
|
134654
134649
|
if (host === "cursor") {
|
|
134655
134650
|
return {
|
|
@@ -134687,15 +134682,25 @@ function hubTargets(plan) {
|
|
|
134687
134682
|
}
|
|
134688
134683
|
return {
|
|
134689
134684
|
targetKind: "hub-backed",
|
|
134690
|
-
executionMode: "
|
|
134685
|
+
executionMode: "executable",
|
|
134691
134686
|
unitId: declaration.unitId,
|
|
134692
134687
|
host,
|
|
134693
134688
|
plugin: local.plugin,
|
|
134694
134689
|
version: unit?.targetVersion,
|
|
134695
134690
|
hub,
|
|
134696
|
-
|
|
134697
|
-
|
|
134698
|
-
|
|
134691
|
+
marketplace: hub.name,
|
|
134692
|
+
marketplaceRepo: hub.repo,
|
|
134693
|
+
marketplaceRef: hub.ref,
|
|
134694
|
+
githubHost: tagIdentity?.githubHost ?? "github.com",
|
|
134695
|
+
pluginRepo: unit?.publicRepo,
|
|
134696
|
+
pluginCommit: unit?.frozenSnapshot?.commit,
|
|
134697
|
+
pluginTag: tag,
|
|
134698
|
+
frozenTag: tag,
|
|
134699
|
+
snapshotPath: unit?.frozenSnapshot?.path,
|
|
134700
|
+
manifestDigest: unit?.frozenSnapshot?.manifestDigest,
|
|
134701
|
+
timeoutMs: 3e5,
|
|
134702
|
+
message: host === "kimi" ? `Install the frozen GitHub Release ${tag} through Kimi's controlled terminal interface.` : `Update ${local.plugin} from the existing ${hub.name} marketplace and verify the frozen payload.`,
|
|
134703
|
+
...tagActions.length !== 1 || tagIdentity?.repo !== unit?.publicRepo || tagIdentity?.commit !== unit?.frozenSnapshot?.commit || tagIdentity?.version !== unit?.targetVersion ? { invalidFrozenIdentity: true } : {}
|
|
134699
134704
|
};
|
|
134700
134705
|
});
|
|
134701
134706
|
});
|
|
@@ -134707,18 +134712,19 @@ function mergePostReleaseTargets(executableTargets, manualTargets) {
|
|
|
134707
134712
|
const existing = byIdentity.get(key);
|
|
134708
134713
|
if (existing && existing.targetKind === "hub-backed" && target.targetKind === "hub-backed") {
|
|
134709
134714
|
const sameHub = ["name", "githubHost", "repo", "ref"].every((field) => existing.hub[field] === target.hub[field]);
|
|
134710
|
-
|
|
134715
|
+
const samePlugin = ["version", "pluginRepo", "pluginTag", "pluginCommit", "snapshotPath", "manifestDigest"].every((field) => existing[field] === target[field]);
|
|
134716
|
+
if (!sameHub || !samePlugin) throw new Error(`post-release target identity conflict for ${target.unitId}/${target.host}/${target.plugin}`);
|
|
134711
134717
|
continue;
|
|
134712
134718
|
}
|
|
134713
|
-
const hubMatchesExecutable = /* @__PURE__ */ __name((hub, executable) => executable.marketplace === hub.name && executable.marketplaceRepo === hub.repo && executable.marketplaceRef === hub.ref && executable.githubHost === hub.githubHost, "hubMatchesExecutable");
|
|
134719
|
+
const hubMatchesExecutable = /* @__PURE__ */ __name((hub, executable, hubTarget) => executable.marketplace === hub.name && executable.marketplaceRepo === hub.repo && executable.marketplaceRef === hub.ref && executable.githubHost === hub.githubHost && executable.pluginRepo === hubTarget.pluginRepo && executable.pluginTag === hubTarget.pluginTag && executable.pluginCommit === hubTarget.pluginCommit, "hubMatchesExecutable");
|
|
134714
134720
|
if (existing && existing.targetKind !== "hub-backed" && target.targetKind === "hub-backed") {
|
|
134715
|
-
if (!hubMatchesExecutable(target.hub, existing)) {
|
|
134721
|
+
if (!hubMatchesExecutable(target.hub, existing, target)) {
|
|
134716
134722
|
throw new Error(`post-release target identity conflict for ${target.unitId}/${target.host}/${target.plugin}`);
|
|
134717
134723
|
}
|
|
134718
134724
|
continue;
|
|
134719
134725
|
}
|
|
134720
134726
|
if (existing && existing.targetKind === "hub-backed" && target.targetKind !== "hub-backed") {
|
|
134721
|
-
if (!hubMatchesExecutable(existing.hub, target)) {
|
|
134727
|
+
if (!hubMatchesExecutable(existing.hub, target, existing)) {
|
|
134722
134728
|
throw new Error(`post-release target identity conflict for ${target.unitId}/${target.host}/${target.plugin}`);
|
|
134723
134729
|
}
|
|
134724
134730
|
byIdentity.set(key, target);
|
|
@@ -134732,6 +134738,13 @@ function mergePostReleaseTargets(executableTargets, manualTargets) {
|
|
|
134732
134738
|
return [...byIdentity.values()].sort((left, right) => left.host.localeCompare(right.host) || left.unitId.localeCompare(right.unitId) || left.plugin.localeCompare(right.plugin));
|
|
134733
134739
|
}
|
|
134734
134740
|
function assertExecutableTarget(target) {
|
|
134741
|
+
if (target.targetKind === "hub-backed") {
|
|
134742
|
+
assertQoderExecutableTarget(target);
|
|
134743
|
+
if (target.invalidFrozenIdentity || typeof target.pluginTag !== "string" || !target.pluginTag || !/^[\w.-]+\/[\w.-]+$/u.test(target.pluginRepo) || !/^[\w.-]+\/[\w.-]+$/u.test(target.hub.repo) || !target.hub.ref?.startsWith("refs/heads/")) {
|
|
134744
|
+
throw new Error(`local host update target has inconsistent frozen source identity for ${target.unitId}`);
|
|
134745
|
+
}
|
|
134746
|
+
return;
|
|
134747
|
+
}
|
|
134735
134748
|
for (const field of [
|
|
134736
134749
|
"actionId",
|
|
134737
134750
|
"unitId",
|
|
@@ -134817,6 +134830,9 @@ function derivePostReleaseChecklist(plan, {
|
|
|
134817
134830
|
const units = plan.units ?? [];
|
|
134818
134831
|
const uncovered = units.filter((unit) => !BRANCH_ACTION_INCLUDED.has(unit.productionConfig?.branchStrategy));
|
|
134819
134832
|
const declaredHubTargets = hubTargets(plan);
|
|
134833
|
+
for (const target of declaredHubTargets) {
|
|
134834
|
+
if (target.targetKind === "hub-backed" && target.host !== "qoder") assertExecutableTarget(target);
|
|
134835
|
+
}
|
|
134820
134836
|
const executableTargets = [
|
|
134821
134837
|
...pluginTargets(plan),
|
|
134822
134838
|
...declaredHubTargets.filter((target) => target.executionMode === "executable")
|
|
@@ -135104,10 +135120,10 @@ function isCodeBuddyResidualEnvelope(error) {
|
|
|
135104
135120
|
return envelope?.ok === false && envelope.exitStatus === 124 && envelope.processStatus === "TERMINATED" && envelope.terminationReason === "child_exit" && envelope.watchdogReason === "residual_process_group" && evidence?.childExitCode === 0 && (evidence.childSignal === null || evidence.childSignal === void 0) && evidence.residualGroupCleanupCompleted === true && evidence.forcedKill === false && evidence.outputLimitExceeded === null && Array.isArray(signals) && signals.length === 1 && signal?.signal === "SIGTERM" && signal.requestedMode === "process_group" && signal.successfulMode === "process_group";
|
|
135105
135121
|
}
|
|
135106
135122
|
function isCodeBuddyReadCommand(target, command2, args2) {
|
|
135107
|
-
return target.host === "codebuddy" && (command2 === "codebuddy" || command2 === "cbc" || command2 === CODEBUDDY_MACOS_PATH) && Array.isArray(args2) && (args2.every((value, index) => value === CODEBUDDY_PLUGIN_LIST_ARGS[index]) && args2.length === CODEBUDDY_PLUGIN_LIST_ARGS.length || args2.every((value, index) => value === CODEBUDDY_MARKETPLACE_LIST_ARGS[index]) && args2.length === CODEBUDDY_MARKETPLACE_LIST_ARGS.length);
|
|
135123
|
+
return (target.host === "codebuddy" || target.host === "workbuddy") && (command2 === "codebuddy" || command2 === "cbc" || command2 === CODEBUDDY_MACOS_PATH) && Array.isArray(args2) && (args2.every((value, index) => value === CODEBUDDY_PLUGIN_LIST_ARGS[index]) && args2.length === CODEBUDDY_PLUGIN_LIST_ARGS.length || args2.every((value, index) => value === CODEBUDDY_MARKETPLACE_LIST_ARGS[index]) && args2.length === CODEBUDDY_MARKETPLACE_LIST_ARGS.length);
|
|
135108
135124
|
}
|
|
135109
135125
|
function isCodeBuddyWriteCommand(target, command2, args2, kind) {
|
|
135110
|
-
if (target.host
|
|
135126
|
+
if (!["codebuddy", "workbuddy"].includes(target.host) || !["codebuddy", "cbc", CODEBUDDY_MACOS_PATH].includes(command2)) return false;
|
|
135111
135127
|
const expected = kind === "marketplace-update" ? ["plugin", "marketplace", "update", target.marketplace] : ["plugin", "update", `${target.plugin}@${target.marketplace}`, "--scope", "user"];
|
|
135112
135128
|
return Array.isArray(args2) && args2.length === expected.length && args2.every((value, index) => value === expected[index]);
|
|
135113
135129
|
}
|
|
@@ -135125,14 +135141,14 @@ async function runCodeBuddyRead(target, command2, args2, label, env, run6) {
|
|
|
135125
135141
|
};
|
|
135126
135142
|
}
|
|
135127
135143
|
}
|
|
135128
|
-
function exactCodeBuddyMarketplaceObservation(target, stdout) {
|
|
135144
|
+
function exactCodeBuddyMarketplaceObservation(target, stdout, { acceptedTypes = ["git"] } = {}) {
|
|
135129
135145
|
const parsed = parseJson2(stdout, `${target.host} marketplace list`);
|
|
135130
135146
|
if (!Array.isArray(parsed)) throw new Error(`${target.host} marketplace list did not return an array`);
|
|
135131
135147
|
const matches = parsed.filter((entry) => entry?.name === target.marketplace);
|
|
135132
135148
|
if (matches.length !== 1) {
|
|
135133
135149
|
throw new Error(`${target.host} marketplace list did not contain exactly one ${target.marketplace}`);
|
|
135134
135150
|
}
|
|
135135
|
-
if (matches[0].type
|
|
135151
|
+
if (!acceptedTypes.includes(matches[0].type)) {
|
|
135136
135152
|
throw new Error(`${target.host} marketplace ${target.marketplace} is not a git marketplace`);
|
|
135137
135153
|
}
|
|
135138
135154
|
return { installed: true, exact: true, found: matches[0] };
|
|
@@ -135428,10 +135444,15 @@ async function observeMarketplace(target, command2, env, run6) {
|
|
|
135428
135444
|
const parsed = parseJson2(listed.stdout, `${target.host} marketplace list`);
|
|
135429
135445
|
const entries = target.host === "claude" ? parsed : parsed?.marketplaces;
|
|
135430
135446
|
if (!Array.isArray(entries)) throw new Error(`${target.host} marketplace list has an invalid shape`);
|
|
135431
|
-
const
|
|
135447
|
+
const matches = entries.filter((entry) => entry?.name === target.marketplace);
|
|
135448
|
+
if (target.targetKind === "hub-backed" && matches.length > 1) {
|
|
135449
|
+
throw new Error(`${target.host} marketplace list returned conflicting entries for ${target.marketplace}`);
|
|
135450
|
+
}
|
|
135451
|
+
const found = matches[0];
|
|
135432
135452
|
if (!found) return { installed: false };
|
|
135433
135453
|
const source = target.host === "claude" ? found.repo : found.marketplaceSource?.source;
|
|
135434
|
-
|
|
135454
|
+
const observedSource = target.targetKind === "hub-backed" ? normalizeHubGitSource(source, target.hub.githubHost) : normalizeGitSource(source);
|
|
135455
|
+
if (observedSource !== normalizeGitSource(target.marketplaceRepo)) {
|
|
135435
135456
|
throw new Error(`${target.host} marketplace ${target.marketplace} does not point to ${target.marketplaceRepo}`);
|
|
135436
135457
|
}
|
|
135437
135458
|
const root = target.host === "claude" ? found.installLocation : found.root;
|
|
@@ -135471,6 +135492,14 @@ async function bindStructuredMarketplace(target, command2, env, run6, observed)
|
|
|
135471
135492
|
return true;
|
|
135472
135493
|
}
|
|
135473
135494
|
function actionParametersForTarget(plan, target) {
|
|
135495
|
+
if (target.targetKind === "hub-backed") {
|
|
135496
|
+
return {
|
|
135497
|
+
snapshotPath: target.snapshotPath,
|
|
135498
|
+
manifestDigest: target.manifestDigest,
|
|
135499
|
+
payloadContract: "external-marketplace-v1",
|
|
135500
|
+
marketplaceLocation: "external"
|
|
135501
|
+
};
|
|
135502
|
+
}
|
|
135474
135503
|
const action = (plan.externalActions ?? []).find((candidate) => candidate.id === target.actionId);
|
|
135475
135504
|
if (!action || action.type !== target.actionType || action.unitId !== target.unitId) {
|
|
135476
135505
|
throw new Error(`local host update target ${target.actionId} has no matching frozen action`);
|
|
@@ -135494,14 +135523,164 @@ async function verifyStructuredInstalledPayload({
|
|
|
135494
135523
|
target.host
|
|
135495
135524
|
);
|
|
135496
135525
|
}
|
|
135526
|
+
async function assertHubHostEntry(target, marketplaceRoot) {
|
|
135527
|
+
const codex = target.host === "codex";
|
|
135528
|
+
const indexPath = codex ? ".agents/plugins/marketplace.json" : target.host === "claude" ? ".claude-plugin/marketplace.json" : ".codebuddy-plugin/marketplace.json";
|
|
135529
|
+
const index = parseJson2(await readFileContained(marketplaceRoot, indexPath, { encoding: "utf8" }), `${target.host} Hub index`);
|
|
135530
|
+
const matches = index?.plugins?.filter((entry2) => entry2?.name === target.plugin);
|
|
135531
|
+
if (index?.name !== target.marketplace || !Array.isArray(matches) || matches.length !== 1) {
|
|
135532
|
+
throw new Error(`${target.host} Hub index must contain exactly one frozen plugin entry`);
|
|
135533
|
+
}
|
|
135534
|
+
const entry = matches[0];
|
|
135535
|
+
const source = entry.source;
|
|
135536
|
+
const repo = codex ? normalizeHubGitSource(source?.url, target.githubHost) : source?.repo;
|
|
135537
|
+
if (source?.source !== (codex ? "url" : "github") || repo !== target.pluginRepo || source?.sha !== target.pluginCommit || source?.ref !== (codex ? `refs/tags/${target.pluginTag}` : target.pluginTag) || !codex && entry.version !== target.version || entry.version !== void 0 && entry.version !== target.version) {
|
|
135538
|
+
throw new Error(`${target.host} Hub entry does not match the frozen repository, tag, commit and version`);
|
|
135539
|
+
}
|
|
135540
|
+
}
|
|
135541
|
+
async function observeHubCheckout(target, checkout, env, run6) {
|
|
135542
|
+
const remote = await run6("git", ["-C", checkout, "remote", "get-url", "origin"], { env, timeout: 3e4 });
|
|
135543
|
+
if (normalizeHubGitSource(remote.stdout.trim(), target.hub.githubHost) !== target.hub.repo) {
|
|
135544
|
+
throw new Error(`${target.host} marketplace checkout does not match the declared Hub repository`);
|
|
135545
|
+
}
|
|
135546
|
+
if (target.host !== "codex") {
|
|
135547
|
+
const branch = await run6("git", ["-C", checkout, "symbolic-ref", "HEAD"], { env, timeout: 3e4 });
|
|
135548
|
+
if (branch.stdout.trim() !== target.hub.ref) throw new Error(`${target.host} marketplace checkout does not match the declared Hub branch`);
|
|
135549
|
+
}
|
|
135550
|
+
const head = await run6("git", ["-C", checkout, "rev-parse", "HEAD"], { env, timeout: 3e4 });
|
|
135551
|
+
const commit = head.stdout.trim();
|
|
135552
|
+
if (!/^[a-f0-9]{40}$/u.test(commit)) throw new Error(`${target.host} Hub checkout has no exact commit`);
|
|
135553
|
+
return commit;
|
|
135554
|
+
}
|
|
135555
|
+
async function observeHubMarketplace(target, command2, env, run6) {
|
|
135556
|
+
if (target.host === "claude" || target.host === "codex") {
|
|
135557
|
+
const observed = await observeMarketplace(target, command2, env, run6);
|
|
135558
|
+
if (!observed.installed) return observed;
|
|
135559
|
+
return { ...observed, commit: await observeHubCheckout(target, observed.root, env, run6) };
|
|
135560
|
+
}
|
|
135561
|
+
const codeBuddyFamily = target.host === "codebuddy" || target.host === "workbuddy";
|
|
135562
|
+
const listed = codeBuddyFamily ? (await runCodeBuddyRead(target, command2, CODEBUDDY_MARKETPLACE_LIST_ARGS, "codebuddy marketplace list", env, run6)).output : await run6(command2, [...CODEBUDDY_MARKETPLACE_LIST_ARGS], { env });
|
|
135563
|
+
const entries = parseJson2(listed.stdout, `${target.host} marketplace list`);
|
|
135564
|
+
if (!Array.isArray(entries)) throw new Error(`${target.host} marketplace list did not return an array`);
|
|
135565
|
+
if (!entries.some((entry) => entry?.name === target.marketplace)) return { installed: false };
|
|
135566
|
+
exactCodeBuddyMarketplaceObservation(target, listed.stdout, { acceptedTypes: ["git", "github"] });
|
|
135567
|
+
const root = await resolveContained(env.CODEBUDDY_CONFIG_DIR, `plugins/marketplaces/${target.marketplace}`);
|
|
135568
|
+
return { installed: true, root, commit: await observeHubCheckout(target, root, env, run6) };
|
|
135569
|
+
}
|
|
135570
|
+
async function runHubStructuredUpdate(target, detected, run6, { plan, root, verifyInstalledPayload }) {
|
|
135571
|
+
const env = hostEnvironment(target.host);
|
|
135572
|
+
const codeBuddy = target.host === "codebuddy" || target.host === "workbuddy";
|
|
135573
|
+
const readPlugin = /* @__PURE__ */ __name(async () => {
|
|
135574
|
+
const output = codeBuddy ? (await runCodeBuddyRead(target, detected.command, CODEBUDDY_PLUGIN_LIST_ARGS, "codebuddy plugin list", env, run6)).output : await run6(detected.command, ["plugin", "list", "--json"], { env });
|
|
135575
|
+
const listed = parseJson2(output.stdout, `${target.host} plugin list`);
|
|
135576
|
+
const entries = target.host === "codex" ? listed?.installed : listed;
|
|
135577
|
+
if (Array.isArray(entries) && entries.filter((entry) => (target.host === "codex" ? entry?.pluginId : entry?.id) === `${target.plugin}@${target.marketplace}`).length > 1) throw new Error(`${target.host} plugin list returned conflicting entries`);
|
|
135578
|
+
const observed = exactPluginObservation(target, output.stdout);
|
|
135579
|
+
if (observed.installed) {
|
|
135580
|
+
if (codeBuddy && observed.exact) {
|
|
135581
|
+
observed.installPath = await resolveContained(env.CODEBUDDY_CONFIG_DIR, `plugins/cache/${target.marketplace}/${target.plugin}/${target.version}`);
|
|
135582
|
+
} else if (target.host === "codex") {
|
|
135583
|
+
observed.installPath = observed.found.installedPath;
|
|
135584
|
+
const source = observed.found.source;
|
|
135585
|
+
if (source?.source !== "git" || normalizeHubGitSource(source.url, target.githubHost) !== target.pluginRepo) {
|
|
135586
|
+
throw new Error("codex installed plugin does not point to the frozen public repository");
|
|
135587
|
+
}
|
|
135588
|
+
if (source.sha !== target.pluginCommit || source.ref !== `refs/tags/${target.pluginTag}`) observed.exact = false;
|
|
135589
|
+
}
|
|
135590
|
+
if (observed.found.gitCommitSha !== void 0 && observed.found.gitCommitSha !== target.pluginCommit) observed.exact = false;
|
|
135591
|
+
}
|
|
135592
|
+
return observed;
|
|
135593
|
+
}, "readPlugin");
|
|
135594
|
+
const beforeMarket = await observeHubMarketplace(target, detected.command, env, run6);
|
|
135595
|
+
if (!beforeMarket.installed) return { status: "MANUAL_REQUIRED", reason: `${target.host} declared Hub marketplace is not installed; no marketplace was added` };
|
|
135596
|
+
const before = await readPlugin();
|
|
135597
|
+
if (before.exact && before.installPath) {
|
|
135598
|
+
await assertHubHostEntry(target, beforeMarket.root);
|
|
135599
|
+
await verifyStructuredInstalledPayload({ plan, root, target, installPath: before.installPath, verifyInstalledPayload });
|
|
135600
|
+
return { status: "ALREADY_CURRENT", version: target.version };
|
|
135601
|
+
}
|
|
135602
|
+
if (codeBuddy && !before.installed) return { status: "MANUAL_REQUIRED", reason: `${target.host} target plugin is not installed; no initial installation was performed` };
|
|
135603
|
+
const hubCommit = await withTemporaryWorkspace(async (workspace) => {
|
|
135604
|
+
const checkout = join29(workspace.root, "hub");
|
|
135605
|
+
await run6("git", [
|
|
135606
|
+
"clone",
|
|
135607
|
+
"--depth",
|
|
135608
|
+
"1",
|
|
135609
|
+
"--branch",
|
|
135610
|
+
target.hub.ref.slice("refs/heads/".length),
|
|
135611
|
+
"--single-branch",
|
|
135612
|
+
`https://${target.hub.githubHost}/${target.hub.repo}.git`,
|
|
135613
|
+
checkout
|
|
135614
|
+
], { env, timeout: target.timeoutMs });
|
|
135615
|
+
const commit = await observeHubCheckout(target, checkout, env, run6);
|
|
135616
|
+
await assertHubHostEntry(target, checkout);
|
|
135617
|
+
return commit;
|
|
135618
|
+
}, { prefix: "release-skill-hub-update-" });
|
|
135619
|
+
if (target.host === "codex") {
|
|
135620
|
+
await bindStructuredMarketplace(
|
|
135621
|
+
{ ...target, marketplaceCommit: hubCommit },
|
|
135622
|
+
detected.command,
|
|
135623
|
+
env,
|
|
135624
|
+
run6,
|
|
135625
|
+
{ marketplace: { ...beforeMarket, exact: beforeMarket.commit === hubCommit } }
|
|
135626
|
+
);
|
|
135627
|
+
} else {
|
|
135628
|
+
try {
|
|
135629
|
+
await run6(detected.command, ["plugin", "marketplace", "update", target.marketplace], { env, timeout: target.timeoutMs });
|
|
135630
|
+
} catch (error) {
|
|
135631
|
+
if (!isCodeBuddyWriteCommand(target, detected.command, ["plugin", "marketplace", "update", target.marketplace], "marketplace-update") || !isCodeBuddyResidualEnvelope(error)) throw error;
|
|
135632
|
+
}
|
|
135633
|
+
}
|
|
135634
|
+
const refreshed = await observeHubMarketplace(target, detected.command, env, run6);
|
|
135635
|
+
if (!refreshed.installed || refreshed.commit !== hubCommit) throw new Error(`${target.host} Hub branch moved during marketplace refresh`);
|
|
135636
|
+
await assertHubHostEntry(target, refreshed.root);
|
|
135637
|
+
if (codeBuddy) {
|
|
135638
|
+
const remote = await run6("git", ["ls-remote", "--exit-code", `https://${target.hub.githubHost}/${target.hub.repo}.git`, target.hub.ref], { env, timeout: 3e4 });
|
|
135639
|
+
if (!parseFrozenRemoteRef({ ...target, marketplaceCommit: hubCommit }, remote.stdout).exact) {
|
|
135640
|
+
throw new Error(`${target.host} Hub branch moved before plugin update`);
|
|
135641
|
+
}
|
|
135642
|
+
}
|
|
135643
|
+
const current = await readPlugin();
|
|
135644
|
+
const selector = `${target.plugin}@${target.marketplace}`;
|
|
135645
|
+
let installPath;
|
|
135646
|
+
if (target.host === "codex") {
|
|
135647
|
+
if (current.installed) await run6(detected.command, ["plugin", "remove", selector, "--json"], { env });
|
|
135648
|
+
const installed = await run6(detected.command, getPlatform("codex").cli.install(target.plugin, target.marketplace), { env, timeout: target.timeoutMs });
|
|
135649
|
+
const extracted = getPlatform("codex").strategy.extractInstallPath({ execEvidence: { installOutput: parseJson2(installed.stdout, "codex plugin install") } });
|
|
135650
|
+
if (!extracted.ok) throw new Error(extracted.error);
|
|
135651
|
+
installPath = extracted.installPath;
|
|
135652
|
+
} else {
|
|
135653
|
+
const args2 = codeBuddy || current.installed ? ["plugin", "update", selector, "--scope", "user", ...target.host === "claude" ? ["--yes"] : []] : getPlatform("claude").cli.install(target.plugin, target.marketplace);
|
|
135654
|
+
try {
|
|
135655
|
+
await run6(detected.command, args2, { env, timeout: target.timeoutMs });
|
|
135656
|
+
} catch (error) {
|
|
135657
|
+
if (!isCodeBuddyWriteCommand(target, detected.command, args2, "plugin-update") || !isCodeBuddyResidualEnvelope(error)) throw error;
|
|
135658
|
+
}
|
|
135659
|
+
}
|
|
135660
|
+
const after = await readPlugin();
|
|
135661
|
+
if (!after.exact) throw new Error(`${target.host} did not install the frozen plugin identity`);
|
|
135662
|
+
const finalMarket = await observeHubMarketplace(target, detected.command, env, run6);
|
|
135663
|
+
if (!finalMarket.installed || finalMarket.commit !== hubCommit) throw new Error(`${target.host} Hub checkout changed during plugin update`);
|
|
135664
|
+
await assertHubHostEntry(target, finalMarket.root);
|
|
135665
|
+
await verifyStructuredInstalledPayload({ plan, root, target, installPath: installPath ?? after.installPath, verifyInstalledPayload });
|
|
135666
|
+
return { status: "UPDATED", version: target.version, restartRequired: true };
|
|
135667
|
+
}
|
|
135497
135668
|
async function runStructuredUpdate(target, detected, run6, {
|
|
135498
135669
|
plan,
|
|
135499
135670
|
root,
|
|
135500
135671
|
verifyInstalledPayload
|
|
135501
135672
|
}) {
|
|
135673
|
+
if (target.targetKind === "hub-backed") return runHubStructuredUpdate(target, detected, run6, { plan, root, verifyInstalledPayload });
|
|
135502
135674
|
if (target.host === "codebuddy" || target.host === "workbuddy") {
|
|
135503
135675
|
const env2 = hostEnvironment(target.host);
|
|
135504
|
-
const listedObservation =
|
|
135676
|
+
const listedObservation = await runCodeBuddyRead(
|
|
135677
|
+
target,
|
|
135678
|
+
detected.command,
|
|
135679
|
+
CODEBUDDY_PLUGIN_LIST_ARGS,
|
|
135680
|
+
`${target.host} plugin list`,
|
|
135681
|
+
env2,
|
|
135682
|
+
run6
|
|
135683
|
+
);
|
|
135505
135684
|
const listed = listedObservation.output;
|
|
135506
135685
|
const observed = exactPluginObservation(target, listed.stdout);
|
|
135507
135686
|
if (target.sourceForm !== "bundled-family" || target.pluginRepo !== target.marketplaceRepo || target.pluginCommit !== target.marketplaceCommit) {
|
|
@@ -135584,7 +135763,14 @@ async function runStructuredUpdate(target, detected, run6, {
|
|
|
135584
135763
|
pluginRecovered = true;
|
|
135585
135764
|
}
|
|
135586
135765
|
if (!pluginRecovered) {
|
|
135587
|
-
const afterList =
|
|
135766
|
+
const afterList = await runCodeBuddyRead(
|
|
135767
|
+
target,
|
|
135768
|
+
detected.command,
|
|
135769
|
+
CODEBUDDY_PLUGIN_LIST_ARGS,
|
|
135770
|
+
`${target.host} plugin list`,
|
|
135771
|
+
env2,
|
|
135772
|
+
run6
|
|
135773
|
+
);
|
|
135588
135774
|
after2 = exactPluginObservation(target, afterList.output.stdout);
|
|
135589
135775
|
}
|
|
135590
135776
|
if (!after2.exact) throw new Error(`${target.host} did not update to the frozen plugin identity`);
|
|
@@ -136339,6 +136525,9 @@ async function updateLocalHostPluginsInternal({
|
|
|
136339
136525
|
results.push({ host: target.host, unitId: target.unitId, ...outcome2 });
|
|
136340
136526
|
continue;
|
|
136341
136527
|
}
|
|
136528
|
+
if (target.targetKind === "hub-backed" && target.host !== "qoder") {
|
|
136529
|
+
await verifyFrozenSnapshot({ root, snapshotPath: target.snapshotPath, expectedDigest: target.manifestDigest });
|
|
136530
|
+
}
|
|
136342
136531
|
const detected = await detect(target.host);
|
|
136343
136532
|
if (!detected?.available) {
|
|
136344
136533
|
results.push({
|
|
@@ -136410,9 +136599,11 @@ var init_post_release_local = __esm({
|
|
|
136410
136599
|
"TMPDIR",
|
|
136411
136600
|
"HTTP_PROXY",
|
|
136412
136601
|
"HTTPS_PROXY",
|
|
136602
|
+
"ALL_PROXY",
|
|
136413
136603
|
"NO_PROXY",
|
|
136414
136604
|
"http_proxy",
|
|
136415
136605
|
"https_proxy",
|
|
136606
|
+
"all_proxy",
|
|
136416
136607
|
"no_proxy",
|
|
136417
136608
|
"SSL_CERT_FILE",
|
|
136418
136609
|
"SSL_CERT_DIR",
|
|
@@ -136475,6 +136666,10 @@ var init_post_release_local = __esm({
|
|
|
136475
136666
|
__name(bindStructuredMarketplace, "bindStructuredMarketplace");
|
|
136476
136667
|
__name(actionParametersForTarget, "actionParametersForTarget");
|
|
136477
136668
|
__name(verifyStructuredInstalledPayload, "verifyStructuredInstalledPayload");
|
|
136669
|
+
__name(assertHubHostEntry, "assertHubHostEntry");
|
|
136670
|
+
__name(observeHubCheckout, "observeHubCheckout");
|
|
136671
|
+
__name(observeHubMarketplace, "observeHubMarketplace");
|
|
136672
|
+
__name(runHubStructuredUpdate, "runHubStructuredUpdate");
|
|
136478
136673
|
__name(runStructuredUpdate, "runStructuredUpdate");
|
|
136479
136674
|
__name(parseCodeBuddyRemoteObservation, "parseCodeBuddyRemoteObservation");
|
|
136480
136675
|
__name(kimiExpectProgram, "kimiExpectProgram");
|
|
@@ -45,7 +45,7 @@ node "$RELEASE_SKILL_LOCAL_FINISH_ENTRY" \
|
|
|
45
45
|
|
|
46
46
|
1. `merge.promptRequired=false` 时,发布工作流已经推进或初始化目标分支,不再询问合并。
|
|
47
47
|
2. `merge.promptRequired=true` 时,向用户说明尚未覆盖的发布分支,并询问是否需要合并。用户同意后,先只读核对源分支、目标分支、工作区状态和项目既有合并方式,再用明确的分支名执行;本脚本不猜分支,也不自动推送。
|
|
48
|
-
3. `choose-local-hosts`
|
|
48
|
+
3. `choose-local-hosts` 出现时,列出计划覆盖的宿主,询问是否更新本机插件。由 Hub 分发的目标必须显示声明的 Hub、插件和宿主。Claude、Codex、Kimi、CodeBuddy、WorkBuddy 和 Qoder 均为可执行目标,只有用户已选择且冻结身份检查通过时才更新。旧计划中由真实安装动作派生的目标继续使用原路径;身份一致的重复目标只执行一次。分支决定和宿主选择可以一次问完。
|
|
49
49
|
4. 用户明确不处理本机宿主时,向同一命令加入 `--skip-local-hosts`。该选择会显示地跳过宿主更新、加载和 setup,不得与 `--hosts` 或 `--update-local-hosts` 同时使用。
|
|
50
50
|
|
|
51
51
|
## postVerify 提案送达边界
|
|
@@ -74,11 +74,13 @@ node "$RELEASE_SKILL_LOCAL_FINISH_ENTRY" \
|
|
|
74
74
|
|
|
75
75
|
只传用户选择且计划声明的宿主。Cursor 选择方式见下节;其他宿主 CLI 不存在时返回 `SKIPPED_NOT_INSTALLED`。各宿主按以下规则处理:
|
|
76
76
|
|
|
77
|
-
- Claude
|
|
77
|
+
- Claude/Codex 的 Hub 目标只使用已经登记的同源市场。升级前先读取声明的 Hub 分支,并核对宿主专用索引中的插件仓库、标签、提交及存在的版本字段。刷新市场后再次核对实际目录和条目,再执行原有更新或重装命令。Claude 精确当前安装会离线核对真实载荷;Codex 列表不提供安装根时,仍须正式重装,从安装输出取得 `installedPath` 后核对载荷。旧安装动作继续使用冻结市场引用预检;远端不可达或引用不符时返回 `MANUAL_REQUIRED`。每个宿主单独保留结果,失败不会中断其他已选宿主。
|
|
78
78
|
- Kimi 的精确当前安装会在返回 `ALREADY_CURRENT` 前核对真实载荷;发生安装或迁移时,只在操作完成后核对结果。配置根依次取显式 `kimiHome`、`KIMI_CODE_HOME`、用户主目录下的 `.kimi-code`,TUI 与安装后观察使用同一根。release-finish 当前只采用并验证 Kimi Code 的受控终端交互界面(TUI)路径,并把 TUI 工作目录固定为 `--root` 指定的当前发布项目。用户已确认冻结计划、显式选择更新且 `--hosts` 包含 `kimi` 时,流程读取完整的初始 `Trust this folder?` 对话框,要求其中只有一个正向项 `Trust this folder` 和一个选中项。正向项已经选中时不发送方向键,直接确认;已知拒绝项选中时,按它与正向项的相对位置移动一次,重新读取完整对话框并确认正向项已选中,然后才提交。插件命令发出后再次出现目录信任、界面或选中项未知、正向项缺失或重复、移动后无法确认、超时或提前退出时,该宿主失败并停止后续安装步骤。若后续 Kimi 版本出现新的可复现界面差异,先记录 Kimi 版本、可见选项、选中项及选项关系;这些事实足以唯一确认当前目录、正向信任项和选择结果时,在最近测试补回归与兼容并继续,目录身份或选项语义不明时仍停止并交回主会话。插件信任仍先清理 ANSI/OSC 控制序列和软换行,再分别核对冻结仓库与标签;只有选中 `Trust and install` 才提交并重新加载。包名、版本、发布标签、已安装修订号和受管安装根必须与冻结计划一致;`.git` 只提供附加诊断,不是通过条件。旧的本地路径安装会在同一 TUI 会话中先移除,再按发布标签安装。
|
|
79
|
-
- CodeBuddy/WorkBuddy
|
|
79
|
+
- CodeBuddy/WorkBuddy 只升级既有安装,不自动首装。CodeBuddy 仅探测全局 `codebuddy`/`cbc`,配置根固定为有效 `HOME` 下的 `.codebuddy`;WorkBuddy 仅在 macOS 探测应用内嵌 CLI,配置根固定为有效 `HOME` 下的 `.workbuddy`,两者不互作回退。Hub 目标先核对各自 `plugins/marketplaces/<hub>` 的远端和分支,再核对 `.codebuddy-plugin/marketplace.json` 条目。刷新市场后确认远端分支未漂移,才更新插件;最后核对列表中的版本、`gitCommitSha` 和 `plugins/cache/<hub>/<plugin>/<version>` 的完整载荷。旧 bundled-family 目标继续要求插件标签和市场分支均指向冻结提交。非 macOS 的 WorkBuddy 返回 `SKIPPED_UNSUPPORTED_PLATFORM`。
|
|
80
80
|
- Qoder 先用 `qoder plugins marketplace list --json` 核对计划声明的 Hub 名称、仓库、分支和本机 checkout,再用 `qoder plugins list --json` 核对唯一插件条目的市场、`scope=user`、版本与 `installPath`。目标市场或插件未安装时返回 `MANUAL_REQUIRED`,不添加市场,也不执行首次安装。旧版已安装时,只运行 `qoder plugins marketplace update <name>` 和 `qoder plugins update <plugin@marketplace> --scope user`。市场刷新后、插件更新前,必须从 Hub 的 `marketplace.json` 核对目标条目的 `source.url` 和 `source.sha`;操作后从真实 `installPath` 核对 Qoder manifest、完整声明载荷和冻结来源。市场可能在检查与更新之间变化,事后身份不符时报告失败,不自动降级、卸载或覆盖。
|
|
81
81
|
|
|
82
|
+
Kimi 不使用只有仓库 URL 的 Hub 文件证明标签或提交。它先克隆冻结发布标签并核对提交,再进入既有终端交互流程;启动进程保留 `http_proxy`、`https_proxy` 和 `all_proxy`。
|
|
83
|
+
|
|
82
84
|
任何宿主失败都保留其他宿主的实际结果,不把失败冒充成功。Cursor 只在本次交换映射可确认且备份迁移失败时恢复自己的原目录。
|
|
83
85
|
|
|
84
86
|
完成后报告每个宿主的安装状态。Qoder 返回 `UPDATED` 只表示安装载荷已经更新;启动新会话或执行 `/plugins reload` 并核对实际加载版本后,才能报告新版已加载。本机结果只记录收尾事实,不改变发布状态。
|
|
@@ -35,9 +35,9 @@ description: "Discoverable entry point for release-skill: dependency and environ
|
|
|
35
35
|
|
|
36
36
|
治理诊断只运行 release-skill 自己的只读检查程序,不运行目标 Skill、业务脚本、构建、hook 或发布动作,也不把 `prepare`、`verify`、`release-finish` 当作静态治理入口。用户要求实际接入或发布时,把已有授权带到对应原业务入口;各入口继续执行原有确认、副作用和状态机合同。
|
|
37
37
|
|
|
38
|
-
## 0.9.
|
|
38
|
+
## 0.9.22 候选边界
|
|
39
39
|
|
|
40
|
-
当前 0.9.
|
|
40
|
+
当前 0.9.22 候选精确消费 Foundation 0.21.0 的公开包根 API。0.9.22 仍是源码候选,不能从本说明推断已批准、发布或验证。
|
|
41
41
|
|
|
42
42
|
冻结前,`prepare` 或新建 `ship` 状态可重复传入 `--unit <id>`;不传则选择全部单元。延期单元不进入计划,也不获得发布状态。完整配置、生成物新鲜度和顶层 Hook 仍覆盖全项目;`publicSourceAuthorityReceipt` 的 coordinator 与 subjects 必须共同选择。冻结后以 `plan.units` 为唯一范围,publish、reconcile、verify、distribute 不再接受 `--unit`。
|
|
43
43
|
|