release-skill 0.9.34 → 0.9.35
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 +20 -0
- package/INSTALL.md +2 -2
- package/INSTALL.zh-CN.md +2 -2
- package/README.md +9 -17
- package/README.zh-CN.md +9 -17
- package/adapters/claude/.claude-plugin/plugin.json +1 -1
- package/adapters/claude/bin/release-skill.bundle.mjs +33 -17
- package/adapters/claude/skills/release-finish/SKILL.md +5 -1
- package/adapters/claude/skills/release-help/SKILL.md +1 -1
- package/adapters/codex/.codex-plugin/plugin.json +2 -2
- package/adapters/codex/bin/release-skill.bundle.mjs +33 -17
- package/adapters/codex/skills/release-finish/SKILL.md +5 -1
- package/adapters/codex/skills/release-help/SKILL.md +1 -1
- package/adapters/cursor/.cursor-plugin/plugin.json +1 -1
- package/adapters/cursor/bin/release-skill.bundle.mjs +33 -17
- package/adapters/cursor/skills/release-finish/SKILL.md +5 -1
- package/adapters/cursor/skills/release-help/SKILL.md +1 -1
- package/adapters/kimi/.kimi-plugin/plugin.json +1 -1
- package/adapters/kimi/bin/release-skill.bundle.mjs +33 -17
- package/adapters/kimi/skills/release-finish/SKILL.md +5 -1
- package/adapters/kimi/skills/release-help/SKILL.md +1 -1
- package/adapters/qoder/.qoder-plugin/plugin.json +1 -1
- package/adapters/qoder/bin/release-skill.bundle.mjs +33 -17
- package/adapters/qoder/skills/release-finish/SKILL.md +5 -1
- package/adapters/qoder/skills/release-help/SKILL.md +1 -1
- package/adapters/workbuddy/.codebuddy-plugin/plugin.json +1 -1
- package/adapters/workbuddy/bin/release-skill.bundle.mjs +33 -17
- package/adapters/workbuddy/skills/release-finish/SKILL.md +5 -1
- package/adapters/workbuddy/skills/release-help/SKILL.md +1 -1
- package/bin/release-skill.bundle.mjs +33 -17
- package/package.json +1 -1
- package/platform-manifest.json +4 -4
- package/references/.render-manifest.json +2 -2
- package/references/02-project-config.md +3 -1
- package/skills/release-finish/SKILL.md +5 -1
- package/skills/release-help/SKILL.md +1 -1
- package/skills-src/release-finish/SKILL.md +5 -1
- package/skills-src/release-help/SKILL.md +1 -1
- package/src/commands/post-release-local.mjs +33 -15
|
@@ -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.35"});
|
|
13
13
|
// Build-time source digest for the BUNDLE_STALE freshness gate (see above).
|
|
14
|
-
const __bundleSourceDigest = "
|
|
14
|
+
const __bundleSourceDigest = "e14a5aba2f50cba3982bbe413cc9faebf0d45629e53f410e4942f562d7161754";
|
|
15
15
|
|
|
16
16
|
var __create = Object.create;
|
|
17
17
|
var __defProp = Object.defineProperty;
|
|
@@ -139650,14 +139650,26 @@ async function observeQoderPlugin(target, command2, env, run6) {
|
|
|
139650
139650
|
};
|
|
139651
139651
|
}
|
|
139652
139652
|
async function readQoderManifest(root, label) {
|
|
139653
|
-
const
|
|
139654
|
-
|
|
139655
|
-
|
|
139656
|
-
|
|
139657
|
-
|
|
139658
|
-
|
|
139653
|
+
const candidates = [];
|
|
139654
|
+
for (const manifestPath of [".qoder-plugin/plugin.json", ".claude-plugin/plugin.json"]) {
|
|
139655
|
+
let text;
|
|
139656
|
+
try {
|
|
139657
|
+
text = await readFileContained(root, manifestPath, { encoding: "utf8" });
|
|
139658
|
+
} catch (error) {
|
|
139659
|
+
if (error?.code === "SFC2004" && error?.details?.kind === "missing-resource") continue;
|
|
139660
|
+
throw error;
|
|
139661
|
+
}
|
|
139662
|
+
const manifest = parseJson2(text, `${label} (${manifestPath})`);
|
|
139663
|
+
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
|
|
139664
|
+
throw new Error(`${label} (${manifestPath}) did not return an object`);
|
|
139665
|
+
}
|
|
139666
|
+
candidates.push({ manifestPath, manifest });
|
|
139659
139667
|
}
|
|
139660
|
-
|
|
139668
|
+
if (candidates.length === 0) throw new Error(`${label} has no native or compatible plugin manifest`);
|
|
139669
|
+
if (candidates.length === 2 && ["name", "version", "skills"].some((field) => candidates[0].manifest[field] !== candidates[1].manifest[field])) {
|
|
139670
|
+
throw new Error(`${label} does not match the frozen Qoder plugin identity and projection path: conflicting native and compatible manifests`);
|
|
139671
|
+
}
|
|
139672
|
+
return candidates[0];
|
|
139661
139673
|
}
|
|
139662
139674
|
async function assertQoderPluginManifest(target, manifest, label, pluginRoot, frozenSkills) {
|
|
139663
139675
|
if (manifest.name !== target.plugin || manifest.version !== target.version || typeof manifest.skills !== "string" || manifest.skills.length === 0 || frozenSkills !== void 0 && manifest.skills !== frozenSkills) {
|
|
@@ -139675,8 +139687,9 @@ async function assertFrozenQoderManifest(target, root) {
|
|
|
139675
139687
|
snapshotPath: target.snapshotPath,
|
|
139676
139688
|
expectedDigest: target.manifestDigest
|
|
139677
139689
|
});
|
|
139678
|
-
const manifest = await readQoderManifest(snapshotRoot, "frozen Qoder plugin manifest");
|
|
139679
|
-
|
|
139690
|
+
const { manifestPath, manifest } = await readQoderManifest(snapshotRoot, "frozen Qoder plugin manifest");
|
|
139691
|
+
const skills = await assertQoderPluginManifest(target, manifest, "frozen Qoder plugin manifest", snapshotRoot);
|
|
139692
|
+
return { manifestPath, skills };
|
|
139680
139693
|
}
|
|
139681
139694
|
async function assertQoderHubEntry(target, marketplaceRoot) {
|
|
139682
139695
|
const marketplace = parseJson2(
|
|
@@ -139699,11 +139712,14 @@ async function verifyQoderInstalledPayload({
|
|
|
139699
139712
|
target,
|
|
139700
139713
|
root,
|
|
139701
139714
|
installPath,
|
|
139702
|
-
|
|
139715
|
+
frozenManifest,
|
|
139703
139716
|
verifyInstalledPayload
|
|
139704
139717
|
}) {
|
|
139705
|
-
const manifest = await readQoderManifest(installPath, "installed Qoder plugin manifest");
|
|
139706
|
-
|
|
139718
|
+
const { manifestPath, manifest } = await readQoderManifest(installPath, "installed Qoder plugin manifest");
|
|
139719
|
+
if (manifestPath !== frozenManifest.manifestPath) {
|
|
139720
|
+
throw new Error("installed Qoder plugin manifest path differs from the frozen manifest selection");
|
|
139721
|
+
}
|
|
139722
|
+
await assertQoderPluginManifest(target, manifest, "installed Qoder plugin manifest", installPath, frozenManifest.skills);
|
|
139707
139723
|
await verifyInstalledPayload({
|
|
139708
139724
|
snapshotPath: target.snapshotPath,
|
|
139709
139725
|
manifestDigest: target.manifestDigest,
|
|
@@ -139716,7 +139732,7 @@ async function runQoderUpdate(target, detected, run6, {
|
|
|
139716
139732
|
verifyInstalledPayload
|
|
139717
139733
|
}) {
|
|
139718
139734
|
const env = hostEnvironment("qoder");
|
|
139719
|
-
const
|
|
139735
|
+
const frozenManifest = await assertFrozenQoderManifest(target, root);
|
|
139720
139736
|
const marketplace = await observeQoderMarketplace(target, detected.command, env, run6);
|
|
139721
139737
|
if (!marketplace.installed) {
|
|
139722
139738
|
return {
|
|
@@ -139737,7 +139753,7 @@ async function runQoderUpdate(target, detected, run6, {
|
|
|
139737
139753
|
target,
|
|
139738
139754
|
root,
|
|
139739
139755
|
installPath: before.installPath,
|
|
139740
|
-
|
|
139756
|
+
frozenManifest,
|
|
139741
139757
|
verifyInstalledPayload
|
|
139742
139758
|
});
|
|
139743
139759
|
return { status: "ALREADY_CURRENT", version: target.version };
|
|
@@ -139764,7 +139780,7 @@ async function runQoderUpdate(target, detected, run6, {
|
|
|
139764
139780
|
target,
|
|
139765
139781
|
root,
|
|
139766
139782
|
installPath: after.installPath,
|
|
139767
|
-
|
|
139783
|
+
frozenManifest,
|
|
139768
139784
|
verifyInstalledPayload
|
|
139769
139785
|
});
|
|
139770
139786
|
return {
|
|
@@ -80,7 +80,11 @@ node "$RELEASE_SKILL_LOCAL_FINISH_ENTRY" \
|
|
|
80
80
|
- Claude/Codex 的 Hub 目标只使用已经登记的同源市场。升级前先读取声明的 Hub 分支,并核对宿主专用索引中的插件仓库、标签、提交及存在的版本字段。刷新市场后再次核对实际目录和条目,再执行原有更新或重装命令。Claude 精确当前安装会离线核对真实载荷;Codex 列表不提供安装根时,仍须正式重装,从安装输出取得 `installedPath` 后核对载荷。旧安装动作继续使用冻结市场引用预检;远端不可达或引用不符时返回 `MANUAL_REQUIRED`。每个宿主单独保留结果,失败不会中断其他已选宿主。
|
|
81
81
|
- 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 会话中先移除,再按发布标签安装。
|
|
82
82
|
- 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`。
|
|
83
|
-
- Qoder
|
|
83
|
+
- 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`。
|
|
84
|
+
|
|
85
|
+
冻结快照与真实 `installPath` 均需比对 `.qoder-plugin/plugin.json` 与 `.claude-plugin/plugin.json` 两个候选清单。仅当原生清单缺失时,方可启用兼容清单;原生清单存在但无效时不得回退。存在的清单均须可解析为有效对象,并满足插件身份与根内技能目录要求,双清单的 `name/version/skills` 必须完全一致,且以原生清单为优先。安装侧选择的相对清单路径及 `skills` 必须与冻结侧选择一致。安装侧额外出现的损坏或冲突清单不得忽略;允许宿主附加文件的 `external-marketplace-v1` 载荷完整比较,不可替代对清单的独立核验。
|
|
86
|
+
|
|
87
|
+
冻结快照摘要须在宿主写入前完成复验,安装后仍需复验完整声明载荷与冻结来源;即使版本已为目标版本,也必须完成全部复验后方可返回 `ALREADY_CURRENT`。市场在检查与更新之间可能发生变更,若事后身份校验失败,报告错误,不进行自动降级、卸载或覆盖。
|
|
84
88
|
|
|
85
89
|
Kimi 不使用只有仓库 URL 的 Hub 文件证明标签或提交。它先克隆冻结发布标签并核对提交,再进入既有终端交互流程;启动进程保留 `http_proxy`、`https_proxy` 和 `all_proxy`。
|
|
86
90
|
|
|
@@ -38,7 +38,7 @@ description: "Discoverable entry point for release-skill: dependency and environ
|
|
|
38
38
|
|
|
39
39
|
治理诊断只运行 release-skill 自己的只读检查程序,不运行目标 Skill、业务脚本、构建、hook 或发布动作,也不把 `prepare`、`verify`、`release-finish` 当作静态治理入口。用户要求实际接入或发布时,把已有授权带到对应原业务入口;各入口继续执行原有确认、副作用和状态机合同。
|
|
40
40
|
|
|
41
|
-
## 0.9.
|
|
41
|
+
## 0.9.35 能力与证据边界
|
|
42
42
|
|
|
43
43
|
四类工程材料接到现有 `release-project` 字段。公开正式材料写入 `publicFiles`(`sourceScope` / `from` / `to` / `mode`)。运行必需资源写入 `requiredPublicFiles`,必须被同一单元 `publicFiles[].to` 精确覆盖。私有正式材料与非正式运行材料不列入 `publicFiles`,需要挡住时使用 `forbiddenPaths` / `forbiddenContentPatterns`。正式不等于公开。是否进入公开快照由 `publicFiles` 是否列出决定。npm 是载荷渠道。`package.json` 的 `files` 与入口闭包和公开映射同时约束。`delivery.include` / `delivery.exclude` 只选择宿主,不是文件包含或排除。排除不得拿掉运行必需资源。互相冲突的配置必须失败关闭。
|
|
44
44
|
|
|
@@ -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.35"});
|
|
13
13
|
// Build-time source digest for the BUNDLE_STALE freshness gate (see above).
|
|
14
|
-
const __bundleSourceDigest = "
|
|
14
|
+
const __bundleSourceDigest = "e14a5aba2f50cba3982bbe413cc9faebf0d45629e53f410e4942f562d7161754";
|
|
15
15
|
|
|
16
16
|
var __create = Object.create;
|
|
17
17
|
var __defProp = Object.defineProperty;
|
|
@@ -139650,14 +139650,26 @@ async function observeQoderPlugin(target, command2, env, run6) {
|
|
|
139650
139650
|
};
|
|
139651
139651
|
}
|
|
139652
139652
|
async function readQoderManifest(root, label) {
|
|
139653
|
-
const
|
|
139654
|
-
|
|
139655
|
-
|
|
139656
|
-
|
|
139657
|
-
|
|
139658
|
-
|
|
139653
|
+
const candidates = [];
|
|
139654
|
+
for (const manifestPath of [".qoder-plugin/plugin.json", ".claude-plugin/plugin.json"]) {
|
|
139655
|
+
let text;
|
|
139656
|
+
try {
|
|
139657
|
+
text = await readFileContained(root, manifestPath, { encoding: "utf8" });
|
|
139658
|
+
} catch (error) {
|
|
139659
|
+
if (error?.code === "SFC2004" && error?.details?.kind === "missing-resource") continue;
|
|
139660
|
+
throw error;
|
|
139661
|
+
}
|
|
139662
|
+
const manifest = parseJson2(text, `${label} (${manifestPath})`);
|
|
139663
|
+
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
|
|
139664
|
+
throw new Error(`${label} (${manifestPath}) did not return an object`);
|
|
139665
|
+
}
|
|
139666
|
+
candidates.push({ manifestPath, manifest });
|
|
139659
139667
|
}
|
|
139660
|
-
|
|
139668
|
+
if (candidates.length === 0) throw new Error(`${label} has no native or compatible plugin manifest`);
|
|
139669
|
+
if (candidates.length === 2 && ["name", "version", "skills"].some((field) => candidates[0].manifest[field] !== candidates[1].manifest[field])) {
|
|
139670
|
+
throw new Error(`${label} does not match the frozen Qoder plugin identity and projection path: conflicting native and compatible manifests`);
|
|
139671
|
+
}
|
|
139672
|
+
return candidates[0];
|
|
139661
139673
|
}
|
|
139662
139674
|
async function assertQoderPluginManifest(target, manifest, label, pluginRoot, frozenSkills) {
|
|
139663
139675
|
if (manifest.name !== target.plugin || manifest.version !== target.version || typeof manifest.skills !== "string" || manifest.skills.length === 0 || frozenSkills !== void 0 && manifest.skills !== frozenSkills) {
|
|
@@ -139675,8 +139687,9 @@ async function assertFrozenQoderManifest(target, root) {
|
|
|
139675
139687
|
snapshotPath: target.snapshotPath,
|
|
139676
139688
|
expectedDigest: target.manifestDigest
|
|
139677
139689
|
});
|
|
139678
|
-
const manifest = await readQoderManifest(snapshotRoot, "frozen Qoder plugin manifest");
|
|
139679
|
-
|
|
139690
|
+
const { manifestPath, manifest } = await readQoderManifest(snapshotRoot, "frozen Qoder plugin manifest");
|
|
139691
|
+
const skills = await assertQoderPluginManifest(target, manifest, "frozen Qoder plugin manifest", snapshotRoot);
|
|
139692
|
+
return { manifestPath, skills };
|
|
139680
139693
|
}
|
|
139681
139694
|
async function assertQoderHubEntry(target, marketplaceRoot) {
|
|
139682
139695
|
const marketplace = parseJson2(
|
|
@@ -139699,11 +139712,14 @@ async function verifyQoderInstalledPayload({
|
|
|
139699
139712
|
target,
|
|
139700
139713
|
root,
|
|
139701
139714
|
installPath,
|
|
139702
|
-
|
|
139715
|
+
frozenManifest,
|
|
139703
139716
|
verifyInstalledPayload
|
|
139704
139717
|
}) {
|
|
139705
|
-
const manifest = await readQoderManifest(installPath, "installed Qoder plugin manifest");
|
|
139706
|
-
|
|
139718
|
+
const { manifestPath, manifest } = await readQoderManifest(installPath, "installed Qoder plugin manifest");
|
|
139719
|
+
if (manifestPath !== frozenManifest.manifestPath) {
|
|
139720
|
+
throw new Error("installed Qoder plugin manifest path differs from the frozen manifest selection");
|
|
139721
|
+
}
|
|
139722
|
+
await assertQoderPluginManifest(target, manifest, "installed Qoder plugin manifest", installPath, frozenManifest.skills);
|
|
139707
139723
|
await verifyInstalledPayload({
|
|
139708
139724
|
snapshotPath: target.snapshotPath,
|
|
139709
139725
|
manifestDigest: target.manifestDigest,
|
|
@@ -139716,7 +139732,7 @@ async function runQoderUpdate(target, detected, run6, {
|
|
|
139716
139732
|
verifyInstalledPayload
|
|
139717
139733
|
}) {
|
|
139718
139734
|
const env = hostEnvironment("qoder");
|
|
139719
|
-
const
|
|
139735
|
+
const frozenManifest = await assertFrozenQoderManifest(target, root);
|
|
139720
139736
|
const marketplace = await observeQoderMarketplace(target, detected.command, env, run6);
|
|
139721
139737
|
if (!marketplace.installed) {
|
|
139722
139738
|
return {
|
|
@@ -139737,7 +139753,7 @@ async function runQoderUpdate(target, detected, run6, {
|
|
|
139737
139753
|
target,
|
|
139738
139754
|
root,
|
|
139739
139755
|
installPath: before.installPath,
|
|
139740
|
-
|
|
139756
|
+
frozenManifest,
|
|
139741
139757
|
verifyInstalledPayload
|
|
139742
139758
|
});
|
|
139743
139759
|
return { status: "ALREADY_CURRENT", version: target.version };
|
|
@@ -139764,7 +139780,7 @@ async function runQoderUpdate(target, detected, run6, {
|
|
|
139764
139780
|
target,
|
|
139765
139781
|
root,
|
|
139766
139782
|
installPath: after.installPath,
|
|
139767
|
-
|
|
139783
|
+
frozenManifest,
|
|
139768
139784
|
verifyInstalledPayload
|
|
139769
139785
|
});
|
|
139770
139786
|
return {
|
|
@@ -79,7 +79,11 @@ node "$RELEASE_SKILL_LOCAL_FINISH_ENTRY" \
|
|
|
79
79
|
- Claude/Codex 的 Hub 目标只使用已经登记的同源市场。升级前先读取声明的 Hub 分支,并核对宿主专用索引中的插件仓库、标签、提交及存在的版本字段。刷新市场后再次核对实际目录和条目,再执行原有更新或重装命令。Claude 精确当前安装会离线核对真实载荷;Codex 列表不提供安装根时,仍须正式重装,从安装输出取得 `installedPath` 后核对载荷。旧安装动作继续使用冻结市场引用预检;远端不可达或引用不符时返回 `MANUAL_REQUIRED`。每个宿主单独保留结果,失败不会中断其他已选宿主。
|
|
80
80
|
- 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 会话中先移除,再按发布标签安装。
|
|
81
81
|
- 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`。
|
|
82
|
-
- Qoder
|
|
82
|
+
- 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`。
|
|
83
|
+
|
|
84
|
+
冻结快照与真实 `installPath` 均需比对 `.qoder-plugin/plugin.json` 与 `.claude-plugin/plugin.json` 两个候选清单。仅当原生清单缺失时,方可启用兼容清单;原生清单存在但无效时不得回退。存在的清单均须可解析为有效对象,并满足插件身份与根内技能目录要求,双清单的 `name/version/skills` 必须完全一致,且以原生清单为优先。安装侧选择的相对清单路径及 `skills` 必须与冻结侧选择一致。安装侧额外出现的损坏或冲突清单不得忽略;允许宿主附加文件的 `external-marketplace-v1` 载荷完整比较,不可替代对清单的独立核验。
|
|
85
|
+
|
|
86
|
+
冻结快照摘要须在宿主写入前完成复验,安装后仍需复验完整声明载荷与冻结来源;即使版本已为目标版本,也必须完成全部复验后方可返回 `ALREADY_CURRENT`。市场在检查与更新之间可能发生变更,若事后身份校验失败,报告错误,不进行自动降级、卸载或覆盖。
|
|
83
87
|
|
|
84
88
|
Kimi 不使用只有仓库 URL 的 Hub 文件证明标签或提交。它先克隆冻结发布标签并核对提交,再进入既有终端交互流程;启动进程保留 `http_proxy`、`https_proxy` 和 `all_proxy`。
|
|
85
89
|
|
|
@@ -37,7 +37,7 @@ description: "Discoverable entry point for release-skill: dependency and environ
|
|
|
37
37
|
|
|
38
38
|
治理诊断只运行 release-skill 自己的只读检查程序,不运行目标 Skill、业务脚本、构建、hook 或发布动作,也不把 `prepare`、`verify`、`release-finish` 当作静态治理入口。用户要求实际接入或发布时,把已有授权带到对应原业务入口;各入口继续执行原有确认、副作用和状态机合同。
|
|
39
39
|
|
|
40
|
-
## 0.9.
|
|
40
|
+
## 0.9.35 能力与证据边界
|
|
41
41
|
|
|
42
42
|
四类工程材料接到现有 `release-project` 字段。公开正式材料写入 `publicFiles`(`sourceScope` / `from` / `to` / `mode`)。运行必需资源写入 `requiredPublicFiles`,必须被同一单元 `publicFiles[].to` 精确覆盖。私有正式材料与非正式运行材料不列入 `publicFiles`,需要挡住时使用 `forbiddenPaths` / `forbiddenContentPatterns`。正式不等于公开。是否进入公开快照由 `publicFiles` 是否列出决定。npm 是载荷渠道。`package.json` 的 `files` 与入口闭包和公开映射同时约束。`delivery.include` / `delivery.exclude` 只选择宿主,不是文件包含或排除。排除不得拿掉运行必需资源。互相冲突的配置必须失败关闭。
|
|
43
43
|
|
|
@@ -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.35"});
|
|
13
13
|
// Build-time source digest for the BUNDLE_STALE freshness gate (see above).
|
|
14
|
-
const __bundleSourceDigest = "
|
|
14
|
+
const __bundleSourceDigest = "e14a5aba2f50cba3982bbe413cc9faebf0d45629e53f410e4942f562d7161754";
|
|
15
15
|
|
|
16
16
|
var __create = Object.create;
|
|
17
17
|
var __defProp = Object.defineProperty;
|
|
@@ -139650,14 +139650,26 @@ async function observeQoderPlugin(target, command2, env, run6) {
|
|
|
139650
139650
|
};
|
|
139651
139651
|
}
|
|
139652
139652
|
async function readQoderManifest(root, label) {
|
|
139653
|
-
const
|
|
139654
|
-
|
|
139655
|
-
|
|
139656
|
-
|
|
139657
|
-
|
|
139658
|
-
|
|
139653
|
+
const candidates = [];
|
|
139654
|
+
for (const manifestPath of [".qoder-plugin/plugin.json", ".claude-plugin/plugin.json"]) {
|
|
139655
|
+
let text;
|
|
139656
|
+
try {
|
|
139657
|
+
text = await readFileContained(root, manifestPath, { encoding: "utf8" });
|
|
139658
|
+
} catch (error) {
|
|
139659
|
+
if (error?.code === "SFC2004" && error?.details?.kind === "missing-resource") continue;
|
|
139660
|
+
throw error;
|
|
139661
|
+
}
|
|
139662
|
+
const manifest = parseJson2(text, `${label} (${manifestPath})`);
|
|
139663
|
+
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
|
|
139664
|
+
throw new Error(`${label} (${manifestPath}) did not return an object`);
|
|
139665
|
+
}
|
|
139666
|
+
candidates.push({ manifestPath, manifest });
|
|
139659
139667
|
}
|
|
139660
|
-
|
|
139668
|
+
if (candidates.length === 0) throw new Error(`${label} has no native or compatible plugin manifest`);
|
|
139669
|
+
if (candidates.length === 2 && ["name", "version", "skills"].some((field) => candidates[0].manifest[field] !== candidates[1].manifest[field])) {
|
|
139670
|
+
throw new Error(`${label} does not match the frozen Qoder plugin identity and projection path: conflicting native and compatible manifests`);
|
|
139671
|
+
}
|
|
139672
|
+
return candidates[0];
|
|
139661
139673
|
}
|
|
139662
139674
|
async function assertQoderPluginManifest(target, manifest, label, pluginRoot, frozenSkills) {
|
|
139663
139675
|
if (manifest.name !== target.plugin || manifest.version !== target.version || typeof manifest.skills !== "string" || manifest.skills.length === 0 || frozenSkills !== void 0 && manifest.skills !== frozenSkills) {
|
|
@@ -139675,8 +139687,9 @@ async function assertFrozenQoderManifest(target, root) {
|
|
|
139675
139687
|
snapshotPath: target.snapshotPath,
|
|
139676
139688
|
expectedDigest: target.manifestDigest
|
|
139677
139689
|
});
|
|
139678
|
-
const manifest = await readQoderManifest(snapshotRoot, "frozen Qoder plugin manifest");
|
|
139679
|
-
|
|
139690
|
+
const { manifestPath, manifest } = await readQoderManifest(snapshotRoot, "frozen Qoder plugin manifest");
|
|
139691
|
+
const skills = await assertQoderPluginManifest(target, manifest, "frozen Qoder plugin manifest", snapshotRoot);
|
|
139692
|
+
return { manifestPath, skills };
|
|
139680
139693
|
}
|
|
139681
139694
|
async function assertQoderHubEntry(target, marketplaceRoot) {
|
|
139682
139695
|
const marketplace = parseJson2(
|
|
@@ -139699,11 +139712,14 @@ async function verifyQoderInstalledPayload({
|
|
|
139699
139712
|
target,
|
|
139700
139713
|
root,
|
|
139701
139714
|
installPath,
|
|
139702
|
-
|
|
139715
|
+
frozenManifest,
|
|
139703
139716
|
verifyInstalledPayload
|
|
139704
139717
|
}) {
|
|
139705
|
-
const manifest = await readQoderManifest(installPath, "installed Qoder plugin manifest");
|
|
139706
|
-
|
|
139718
|
+
const { manifestPath, manifest } = await readQoderManifest(installPath, "installed Qoder plugin manifest");
|
|
139719
|
+
if (manifestPath !== frozenManifest.manifestPath) {
|
|
139720
|
+
throw new Error("installed Qoder plugin manifest path differs from the frozen manifest selection");
|
|
139721
|
+
}
|
|
139722
|
+
await assertQoderPluginManifest(target, manifest, "installed Qoder plugin manifest", installPath, frozenManifest.skills);
|
|
139707
139723
|
await verifyInstalledPayload({
|
|
139708
139724
|
snapshotPath: target.snapshotPath,
|
|
139709
139725
|
manifestDigest: target.manifestDigest,
|
|
@@ -139716,7 +139732,7 @@ async function runQoderUpdate(target, detected, run6, {
|
|
|
139716
139732
|
verifyInstalledPayload
|
|
139717
139733
|
}) {
|
|
139718
139734
|
const env = hostEnvironment("qoder");
|
|
139719
|
-
const
|
|
139735
|
+
const frozenManifest = await assertFrozenQoderManifest(target, root);
|
|
139720
139736
|
const marketplace = await observeQoderMarketplace(target, detected.command, env, run6);
|
|
139721
139737
|
if (!marketplace.installed) {
|
|
139722
139738
|
return {
|
|
@@ -139737,7 +139753,7 @@ async function runQoderUpdate(target, detected, run6, {
|
|
|
139737
139753
|
target,
|
|
139738
139754
|
root,
|
|
139739
139755
|
installPath: before.installPath,
|
|
139740
|
-
|
|
139756
|
+
frozenManifest,
|
|
139741
139757
|
verifyInstalledPayload
|
|
139742
139758
|
});
|
|
139743
139759
|
return { status: "ALREADY_CURRENT", version: target.version };
|
|
@@ -139764,7 +139780,7 @@ async function runQoderUpdate(target, detected, run6, {
|
|
|
139764
139780
|
target,
|
|
139765
139781
|
root,
|
|
139766
139782
|
installPath: after.installPath,
|
|
139767
|
-
|
|
139783
|
+
frozenManifest,
|
|
139768
139784
|
verifyInstalledPayload
|
|
139769
139785
|
});
|
|
139770
139786
|
return {
|
|
@@ -79,7 +79,11 @@ node "$RELEASE_SKILL_LOCAL_FINISH_ENTRY" \
|
|
|
79
79
|
- Claude/Codex 的 Hub 目标只使用已经登记的同源市场。升级前先读取声明的 Hub 分支,并核对宿主专用索引中的插件仓库、标签、提交及存在的版本字段。刷新市场后再次核对实际目录和条目,再执行原有更新或重装命令。Claude 精确当前安装会离线核对真实载荷;Codex 列表不提供安装根时,仍须正式重装,从安装输出取得 `installedPath` 后核对载荷。旧安装动作继续使用冻结市场引用预检;远端不可达或引用不符时返回 `MANUAL_REQUIRED`。每个宿主单独保留结果,失败不会中断其他已选宿主。
|
|
80
80
|
- 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 会话中先移除,再按发布标签安装。
|
|
81
81
|
- 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`。
|
|
82
|
-
- Qoder
|
|
82
|
+
- 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`。
|
|
83
|
+
|
|
84
|
+
冻结快照与真实 `installPath` 均需比对 `.qoder-plugin/plugin.json` 与 `.claude-plugin/plugin.json` 两个候选清单。仅当原生清单缺失时,方可启用兼容清单;原生清单存在但无效时不得回退。存在的清单均须可解析为有效对象,并满足插件身份与根内技能目录要求,双清单的 `name/version/skills` 必须完全一致,且以原生清单为优先。安装侧选择的相对清单路径及 `skills` 必须与冻结侧选择一致。安装侧额外出现的损坏或冲突清单不得忽略;允许宿主附加文件的 `external-marketplace-v1` 载荷完整比较,不可替代对清单的独立核验。
|
|
85
|
+
|
|
86
|
+
冻结快照摘要须在宿主写入前完成复验,安装后仍需复验完整声明载荷与冻结来源;即使版本已为目标版本,也必须完成全部复验后方可返回 `ALREADY_CURRENT`。市场在检查与更新之间可能发生变更,若事后身份校验失败,报告错误,不进行自动降级、卸载或覆盖。
|
|
83
87
|
|
|
84
88
|
Kimi 不使用只有仓库 URL 的 Hub 文件证明标签或提交。它先克隆冻结发布标签并核对提交,再进入既有终端交互流程;启动进程保留 `http_proxy`、`https_proxy` 和 `all_proxy`。
|
|
85
89
|
|
|
@@ -37,7 +37,7 @@ description: "Discoverable entry point for release-skill: dependency and environ
|
|
|
37
37
|
|
|
38
38
|
治理诊断只运行 release-skill 自己的只读检查程序,不运行目标 Skill、业务脚本、构建、hook 或发布动作,也不把 `prepare`、`verify`、`release-finish` 当作静态治理入口。用户要求实际接入或发布时,把已有授权带到对应原业务入口;各入口继续执行原有确认、副作用和状态机合同。
|
|
39
39
|
|
|
40
|
-
## 0.9.
|
|
40
|
+
## 0.9.35 能力与证据边界
|
|
41
41
|
|
|
42
42
|
四类工程材料接到现有 `release-project` 字段。公开正式材料写入 `publicFiles`(`sourceScope` / `from` / `to` / `mode`)。运行必需资源写入 `requiredPublicFiles`,必须被同一单元 `publicFiles[].to` 精确覆盖。私有正式材料与非正式运行材料不列入 `publicFiles`,需要挡住时使用 `forbiddenPaths` / `forbiddenContentPatterns`。正式不等于公开。是否进入公开快照由 `publicFiles` 是否列出决定。npm 是载荷渠道。`package.json` 的 `files` 与入口闭包和公开映射同时约束。`delivery.include` / `delivery.exclude` 只选择宿主,不是文件包含或排除。排除不得拿掉运行必需资源。互相冲突的配置必须失败关闭。
|
|
43
43
|
|
|
@@ -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.35"});
|
|
13
13
|
// Build-time source digest for the BUNDLE_STALE freshness gate (see above).
|
|
14
|
-
const __bundleSourceDigest = "
|
|
14
|
+
const __bundleSourceDigest = "e14a5aba2f50cba3982bbe413cc9faebf0d45629e53f410e4942f562d7161754";
|
|
15
15
|
|
|
16
16
|
var __create = Object.create;
|
|
17
17
|
var __defProp = Object.defineProperty;
|
|
@@ -139650,14 +139650,26 @@ async function observeQoderPlugin(target, command2, env, run6) {
|
|
|
139650
139650
|
};
|
|
139651
139651
|
}
|
|
139652
139652
|
async function readQoderManifest(root, label) {
|
|
139653
|
-
const
|
|
139654
|
-
|
|
139655
|
-
|
|
139656
|
-
|
|
139657
|
-
|
|
139658
|
-
|
|
139653
|
+
const candidates = [];
|
|
139654
|
+
for (const manifestPath of [".qoder-plugin/plugin.json", ".claude-plugin/plugin.json"]) {
|
|
139655
|
+
let text;
|
|
139656
|
+
try {
|
|
139657
|
+
text = await readFileContained(root, manifestPath, { encoding: "utf8" });
|
|
139658
|
+
} catch (error) {
|
|
139659
|
+
if (error?.code === "SFC2004" && error?.details?.kind === "missing-resource") continue;
|
|
139660
|
+
throw error;
|
|
139661
|
+
}
|
|
139662
|
+
const manifest = parseJson2(text, `${label} (${manifestPath})`);
|
|
139663
|
+
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
|
|
139664
|
+
throw new Error(`${label} (${manifestPath}) did not return an object`);
|
|
139665
|
+
}
|
|
139666
|
+
candidates.push({ manifestPath, manifest });
|
|
139659
139667
|
}
|
|
139660
|
-
|
|
139668
|
+
if (candidates.length === 0) throw new Error(`${label} has no native or compatible plugin manifest`);
|
|
139669
|
+
if (candidates.length === 2 && ["name", "version", "skills"].some((field) => candidates[0].manifest[field] !== candidates[1].manifest[field])) {
|
|
139670
|
+
throw new Error(`${label} does not match the frozen Qoder plugin identity and projection path: conflicting native and compatible manifests`);
|
|
139671
|
+
}
|
|
139672
|
+
return candidates[0];
|
|
139661
139673
|
}
|
|
139662
139674
|
async function assertQoderPluginManifest(target, manifest, label, pluginRoot, frozenSkills) {
|
|
139663
139675
|
if (manifest.name !== target.plugin || manifest.version !== target.version || typeof manifest.skills !== "string" || manifest.skills.length === 0 || frozenSkills !== void 0 && manifest.skills !== frozenSkills) {
|
|
@@ -139675,8 +139687,9 @@ async function assertFrozenQoderManifest(target, root) {
|
|
|
139675
139687
|
snapshotPath: target.snapshotPath,
|
|
139676
139688
|
expectedDigest: target.manifestDigest
|
|
139677
139689
|
});
|
|
139678
|
-
const manifest = await readQoderManifest(snapshotRoot, "frozen Qoder plugin manifest");
|
|
139679
|
-
|
|
139690
|
+
const { manifestPath, manifest } = await readQoderManifest(snapshotRoot, "frozen Qoder plugin manifest");
|
|
139691
|
+
const skills = await assertQoderPluginManifest(target, manifest, "frozen Qoder plugin manifest", snapshotRoot);
|
|
139692
|
+
return { manifestPath, skills };
|
|
139680
139693
|
}
|
|
139681
139694
|
async function assertQoderHubEntry(target, marketplaceRoot) {
|
|
139682
139695
|
const marketplace = parseJson2(
|
|
@@ -139699,11 +139712,14 @@ async function verifyQoderInstalledPayload({
|
|
|
139699
139712
|
target,
|
|
139700
139713
|
root,
|
|
139701
139714
|
installPath,
|
|
139702
|
-
|
|
139715
|
+
frozenManifest,
|
|
139703
139716
|
verifyInstalledPayload
|
|
139704
139717
|
}) {
|
|
139705
|
-
const manifest = await readQoderManifest(installPath, "installed Qoder plugin manifest");
|
|
139706
|
-
|
|
139718
|
+
const { manifestPath, manifest } = await readQoderManifest(installPath, "installed Qoder plugin manifest");
|
|
139719
|
+
if (manifestPath !== frozenManifest.manifestPath) {
|
|
139720
|
+
throw new Error("installed Qoder plugin manifest path differs from the frozen manifest selection");
|
|
139721
|
+
}
|
|
139722
|
+
await assertQoderPluginManifest(target, manifest, "installed Qoder plugin manifest", installPath, frozenManifest.skills);
|
|
139707
139723
|
await verifyInstalledPayload({
|
|
139708
139724
|
snapshotPath: target.snapshotPath,
|
|
139709
139725
|
manifestDigest: target.manifestDigest,
|
|
@@ -139716,7 +139732,7 @@ async function runQoderUpdate(target, detected, run6, {
|
|
|
139716
139732
|
verifyInstalledPayload
|
|
139717
139733
|
}) {
|
|
139718
139734
|
const env = hostEnvironment("qoder");
|
|
139719
|
-
const
|
|
139735
|
+
const frozenManifest = await assertFrozenQoderManifest(target, root);
|
|
139720
139736
|
const marketplace = await observeQoderMarketplace(target, detected.command, env, run6);
|
|
139721
139737
|
if (!marketplace.installed) {
|
|
139722
139738
|
return {
|
|
@@ -139737,7 +139753,7 @@ async function runQoderUpdate(target, detected, run6, {
|
|
|
139737
139753
|
target,
|
|
139738
139754
|
root,
|
|
139739
139755
|
installPath: before.installPath,
|
|
139740
|
-
|
|
139756
|
+
frozenManifest,
|
|
139741
139757
|
verifyInstalledPayload
|
|
139742
139758
|
});
|
|
139743
139759
|
return { status: "ALREADY_CURRENT", version: target.version };
|
|
@@ -139764,7 +139780,7 @@ async function runQoderUpdate(target, detected, run6, {
|
|
|
139764
139780
|
target,
|
|
139765
139781
|
root,
|
|
139766
139782
|
installPath: after.installPath,
|
|
139767
|
-
|
|
139783
|
+
frozenManifest,
|
|
139768
139784
|
verifyInstalledPayload
|
|
139769
139785
|
});
|
|
139770
139786
|
return {
|
|
@@ -72,7 +72,11 @@ node "${CODEBUDDY_PLUGIN_ROOT}/bin/release-skill-local-finish.mjs" \
|
|
|
72
72
|
- Claude/Codex 的 Hub 目标只使用已经登记的同源市场。升级前先读取声明的 Hub 分支,并核对宿主专用索引中的插件仓库、标签、提交及存在的版本字段。刷新市场后再次核对实际目录和条目,再执行原有更新或重装命令。Claude 精确当前安装会离线核对真实载荷;Codex 列表不提供安装根时,仍须正式重装,从安装输出取得 `installedPath` 后核对载荷。旧安装动作继续使用冻结市场引用预检;远端不可达或引用不符时返回 `MANUAL_REQUIRED`。每个宿主单独保留结果,失败不会中断其他已选宿主。
|
|
73
73
|
- 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 会话中先移除,再按发布标签安装。
|
|
74
74
|
- 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`。
|
|
75
|
-
- Qoder
|
|
75
|
+
- 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`。
|
|
76
|
+
|
|
77
|
+
冻结快照与真实 `installPath` 均需比对 `.qoder-plugin/plugin.json` 与 `.claude-plugin/plugin.json` 两个候选清单。仅当原生清单缺失时,方可启用兼容清单;原生清单存在但无效时不得回退。存在的清单均须可解析为有效对象,并满足插件身份与根内技能目录要求,双清单的 `name/version/skills` 必须完全一致,且以原生清单为优先。安装侧选择的相对清单路径及 `skills` 必须与冻结侧选择一致。安装侧额外出现的损坏或冲突清单不得忽略;允许宿主附加文件的 `external-marketplace-v1` 载荷完整比较,不可替代对清单的独立核验。
|
|
78
|
+
|
|
79
|
+
冻结快照摘要须在宿主写入前完成复验,安装后仍需复验完整声明载荷与冻结来源;即使版本已为目标版本,也必须完成全部复验后方可返回 `ALREADY_CURRENT`。市场在检查与更新之间可能发生变更,若事后身份校验失败,报告错误,不进行自动降级、卸载或覆盖。
|
|
76
80
|
|
|
77
81
|
Kimi 不使用只有仓库 URL 的 Hub 文件证明标签或提交。它先克隆冻结发布标签并核对提交,再进入既有终端交互流程;启动进程保留 `http_proxy`、`https_proxy` 和 `all_proxy`。
|
|
78
82
|
|
|
@@ -29,7 +29,7 @@ description: "Discoverable entry point for release-skill: dependency and environ
|
|
|
29
29
|
|
|
30
30
|
治理诊断只运行 release-skill 自己的只读检查程序,不运行目标 Skill、业务脚本、构建、hook 或发布动作,也不把 `prepare`、`verify`、`release-finish` 当作静态治理入口。用户要求实际接入或发布时,把已有授权带到对应原业务入口;各入口继续执行原有确认、副作用和状态机合同。
|
|
31
31
|
|
|
32
|
-
## 0.9.
|
|
32
|
+
## 0.9.35 能力与证据边界
|
|
33
33
|
|
|
34
34
|
四类工程材料接到现有 `release-project` 字段。公开正式材料写入 `publicFiles`(`sourceScope` / `from` / `to` / `mode`)。运行必需资源写入 `requiredPublicFiles`,必须被同一单元 `publicFiles[].to` 精确覆盖。私有正式材料与非正式运行材料不列入 `publicFiles`,需要挡住时使用 `forbiddenPaths` / `forbiddenContentPatterns`。正式不等于公开。是否进入公开快照由 `publicFiles` 是否列出决定。npm 是载荷渠道。`package.json` 的 `files` 与入口闭包和公开映射同时约束。`delivery.include` / `delivery.exclude` 只选择宿主,不是文件包含或排除。排除不得拿掉运行必需资源。互相冲突的配置必须失败关闭。
|
|
35
35
|
|