release-skill 0.9.35 → 0.9.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codebuddy-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +2 -2
  4. package/.cursor-plugin/plugin.json +1 -1
  5. package/.kimi-plugin/plugin.json +1 -1
  6. package/.qoder-plugin/plugin.json +1 -1
  7. package/CHANGELOG.md +45 -0
  8. package/INSTALL.md +2 -2
  9. package/INSTALL.zh-CN.md +2 -2
  10. package/README.md +24 -9
  11. package/README.zh-CN.md +15 -9
  12. package/adapters/claude/.claude-plugin/plugin.json +1 -1
  13. package/adapters/claude/bin/release-skill.bundle.mjs +270 -58
  14. package/adapters/claude/schemas/.render-manifest.json +6 -2
  15. package/adapters/claude/skills/release-config/SKILL.md +36 -41
  16. package/adapters/claude/skills/release-docs/SKILL.md +38 -35
  17. package/adapters/claude/skills/release-finish/SKILL.md +2 -2
  18. package/adapters/claude/skills/release-help/SKILL.md +51 -41
  19. package/adapters/claude/skills/release-marketplace/SKILL.md +38 -46
  20. package/adapters/claude/skills/release-prepare/SKILL.md +19 -28
  21. package/adapters/claude/skills/release-publish/SKILL.md +29 -45
  22. package/adapters/codex/.codex-plugin/plugin.json +2 -2
  23. package/adapters/codex/bin/release-skill.bundle.mjs +270 -58
  24. package/adapters/codex/schemas/.render-manifest.json +6 -2
  25. package/adapters/codex/skills/release-config/SKILL.md +36 -41
  26. package/adapters/codex/skills/release-docs/SKILL.md +38 -35
  27. package/adapters/codex/skills/release-finish/SKILL.md +2 -2
  28. package/adapters/codex/skills/release-help/SKILL.md +51 -41
  29. package/adapters/codex/skills/release-marketplace/SKILL.md +38 -46
  30. package/adapters/codex/skills/release-prepare/SKILL.md +19 -28
  31. package/adapters/codex/skills/release-publish/SKILL.md +29 -45
  32. package/adapters/cursor/.cursor-plugin/plugin.json +1 -1
  33. package/adapters/cursor/bin/release-skill.bundle.mjs +270 -58
  34. package/adapters/cursor/schemas/.render-manifest.json +6 -2
  35. package/adapters/cursor/skills/release-config/SKILL.md +36 -41
  36. package/adapters/cursor/skills/release-docs/SKILL.md +38 -35
  37. package/adapters/cursor/skills/release-finish/SKILL.md +2 -2
  38. package/adapters/cursor/skills/release-help/SKILL.md +51 -41
  39. package/adapters/cursor/skills/release-marketplace/SKILL.md +38 -46
  40. package/adapters/cursor/skills/release-prepare/SKILL.md +19 -28
  41. package/adapters/cursor/skills/release-publish/SKILL.md +29 -45
  42. package/adapters/kimi/.kimi-plugin/plugin.json +1 -1
  43. package/adapters/kimi/bin/release-skill.bundle.mjs +270 -58
  44. package/adapters/kimi/schemas/.render-manifest.json +6 -2
  45. package/adapters/kimi/skills/release-config/SKILL.md +36 -41
  46. package/adapters/kimi/skills/release-docs/SKILL.md +38 -35
  47. package/adapters/kimi/skills/release-finish/SKILL.md +2 -2
  48. package/adapters/kimi/skills/release-help/SKILL.md +51 -41
  49. package/adapters/kimi/skills/release-marketplace/SKILL.md +38 -46
  50. package/adapters/kimi/skills/release-prepare/SKILL.md +19 -28
  51. package/adapters/kimi/skills/release-publish/SKILL.md +29 -45
  52. package/adapters/qoder/.qoder-plugin/plugin.json +1 -1
  53. package/adapters/qoder/bin/release-skill.bundle.mjs +270 -58
  54. package/adapters/qoder/schemas/.render-manifest.json +6 -2
  55. package/adapters/qoder/skills/release-config/SKILL.md +36 -41
  56. package/adapters/qoder/skills/release-docs/SKILL.md +38 -35
  57. package/adapters/qoder/skills/release-finish/SKILL.md +2 -2
  58. package/adapters/qoder/skills/release-help/SKILL.md +51 -41
  59. package/adapters/qoder/skills/release-marketplace/SKILL.md +38 -46
  60. package/adapters/qoder/skills/release-prepare/SKILL.md +19 -28
  61. package/adapters/qoder/skills/release-publish/SKILL.md +29 -45
  62. package/adapters/workbuddy/.codebuddy-plugin/plugin.json +1 -1
  63. package/adapters/workbuddy/bin/release-skill.bundle.mjs +270 -58
  64. package/adapters/workbuddy/schemas/.render-manifest.json +6 -2
  65. package/adapters/workbuddy/skills/release-config/SKILL.md +36 -41
  66. package/adapters/workbuddy/skills/release-docs/SKILL.md +38 -35
  67. package/adapters/workbuddy/skills/release-finish/SKILL.md +2 -2
  68. package/adapters/workbuddy/skills/release-help/SKILL.md +51 -41
  69. package/adapters/workbuddy/skills/release-marketplace/SKILL.md +38 -46
  70. package/adapters/workbuddy/skills/release-prepare/SKILL.md +19 -28
  71. package/adapters/workbuddy/skills/release-publish/SKILL.md +29 -45
  72. package/bin/release-skill.bundle.mjs +270 -58
  73. package/package.json +1 -1
  74. package/platform-manifest.json +4 -4
  75. package/references/.render-manifest.json +2 -2
  76. package/references/02-project-config.md +91 -8
  77. package/schemas/.render-manifest.json +6 -2
  78. package/skills/release-config/SKILL.md +36 -41
  79. package/skills/release-docs/SKILL.md +38 -35
  80. package/skills/release-finish/SKILL.md +2 -2
  81. package/skills/release-help/SKILL.md +51 -41
  82. package/skills/release-marketplace/SKILL.md +38 -46
  83. package/skills/release-prepare/SKILL.md +19 -28
  84. package/skills/release-publish/SKILL.md +29 -45
  85. package/skills-src/release-config/SKILL.md +36 -41
  86. package/skills-src/release-docs/SKILL.md +38 -35
  87. package/skills-src/release-finish/SKILL.md +2 -2
  88. package/skills-src/release-help/SKILL.md +51 -41
  89. package/skills-src/release-marketplace/SKILL.md +38 -46
  90. package/skills-src/release-prepare/SKILL.md +19 -28
  91. package/skills-src/release-publish/SKILL.md +29 -45
  92. package/src/adapters/plugin-marketplace.mjs +152 -43
  93. package/src/commands/post-release-local.mjs +41 -22
  94. package/src/commands/verify.mjs +123 -17
  95. package/src/core/skill-resource-closure.mjs +1 -1
@@ -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.35"});
12
+ const __bundlePkg = Object.freeze({"name":"release-skill","version":"0.9.37"});
13
13
  // Build-time source digest for the BUNDLE_STALE freshness gate (see above).
14
- const __bundleSourceDigest = "e14a5aba2f50cba3982bbe413cc9faebf0d45629e53f410e4942f562d7161754";
14
+ const __bundleSourceDigest = "6eb7b6560da84252426dc5cfc81297a3d5d6d6101aecb1a8fbf3f35402892715";
15
15
 
16
16
  var __create = Object.create;
17
17
  var __defProp = Object.defineProperty;
@@ -113912,6 +113912,66 @@ function normalizeCodeBuddySkillsRel(skillsRaw) {
113912
113912
  }
113913
113913
  return rel;
113914
113914
  }
113915
+ async function resolveCodexEntrySkillFile(pluginRoot, entrySkill, pluginName) {
113916
+ const { normalizeGenericSkillsRel: normalizeGenericSkillsRel2 } = await init_surface_host_bindings().then(() => surface_host_bindings_exports);
113917
+ if (!entrySkill || typeof entrySkill !== "string" || !SAFE_ID_RE.test(entrySkill)) {
113918
+ throw new Error(`unsafe entrySkill: "${entrySkill}"`);
113919
+ }
113920
+ const platform = getPlatform("codex");
113921
+ const manifestPath = await resolveContained(pluginRoot, platform.manifestPaths.plugin);
113922
+ let manifest;
113923
+ try {
113924
+ manifest = JSON.parse(await readFile26(manifestPath, "utf8"));
113925
+ } catch (err) {
113926
+ throw new Error(`Codex plugin manifest cannot be read: ${err.message}`);
113927
+ }
113928
+ if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
113929
+ throw new Error("Codex plugin manifest must be an object");
113930
+ }
113931
+ const skillsRel = Object.hasOwn(manifest, "skills") ? normalizeGenericSkillsRel2(manifest.skills) : "skills";
113932
+ const skillsRoot = await resolveContained(pluginRoot, skillsRel);
113933
+ let skillsRootStat;
113934
+ try {
113935
+ skillsRootStat = await lstat37(skillsRoot);
113936
+ } catch {
113937
+ throw new Error(`Codex manifest skills root does not exist: ${manifest.skills ?? skillsRel}`);
113938
+ }
113939
+ if (skillsRootStat.isSymbolicLink() || !skillsRootStat.isDirectory()) {
113940
+ throw new Error(`Codex manifest skills root must be a real directory: ${manifest.skills ?? skillsRel}`);
113941
+ }
113942
+ const findCandidate = /* @__PURE__ */ __name(async (skillDirName) => {
113943
+ const skillDir = await resolveContained(pluginRoot, `${skillsRel}/${skillDirName}`);
113944
+ let skillDirStat;
113945
+ try {
113946
+ skillDirStat = await lstat37(skillDir);
113947
+ } catch (err) {
113948
+ if (err?.code === "ENOENT") return null;
113949
+ throw err;
113950
+ }
113951
+ if (skillDirStat.isSymbolicLink() || !skillDirStat.isDirectory()) {
113952
+ throw new Error(`Codex entry skill directory must be a real directory: ${skillsRel}/${skillDirName}`);
113953
+ }
113954
+ const skillFile = await resolveContained(pluginRoot, `${skillsRel}/${skillDirName}/SKILL.md`);
113955
+ let skillStat;
113956
+ try {
113957
+ skillStat = await lstat37(skillFile);
113958
+ } catch (err) {
113959
+ if (err?.code === "ENOENT") return null;
113960
+ throw err;
113961
+ }
113962
+ if (skillStat.isSymbolicLink() || !skillStat.isFile()) {
113963
+ throw new Error(`Codex entry skill must be a regular non-symlink file: ${skillsRel}/${skillDirName}/SKILL.md`);
113964
+ }
113965
+ return skillFile;
113966
+ }, "findCandidate");
113967
+ const shortNameEntry = await findCandidate(entrySkill);
113968
+ if (shortNameEntry) return shortNameEntry;
113969
+ if (pluginName && SAFE_ID_RE.test(pluginName)) {
113970
+ const prefixedEntry = await findCandidate(`${pluginName}-${entrySkill}`);
113971
+ if (prefixedEntry) return prefixedEntry;
113972
+ }
113973
+ throw new Error(`Codex entry skill not found: ${skillsRel}/${entrySkill}/SKILL.md`);
113974
+ }
113915
113975
  async function resolveCodeBuddyEntrySkillFile(pluginRootReal, manifest, entrySkill) {
113916
113976
  if (!entrySkill || typeof entrySkill !== "string" || !SAFE_ID_RE.test(entrySkill)) {
113917
113977
  throw new Error(`unsafe entrySkill: "${entrySkill}"`);
@@ -115067,6 +115127,17 @@ function createPluginMarketplaceAdapter(deps = {}) {
115067
115127
  error: `entry skill not resolvable in snapshot: ${entryErr.message}`
115068
115128
  });
115069
115129
  }
115130
+ } else if (consumer === "codex") {
115131
+ const pluginRootForSkill = pluginRootForEntrySkill || snapshotDirReal;
115132
+ try {
115133
+ await resolveCodexEntrySkillFile(pluginRootForSkill, action.entrySkill, action.plugin);
115134
+ } catch (entryErr) {
115135
+ return createResult({
115136
+ actionType,
115137
+ status: ActionStatus.PREFLIGHT_FAILED,
115138
+ error: `entry skill not resolvable in snapshot: ${entryErr.message}`
115139
+ });
115140
+ }
115070
115141
  } else {
115071
115142
  const pluginRootForSkill = pluginRootForEntrySkill || snapshotDirReal;
115072
115143
  let entrySkillFile = resolve22(pluginRootForSkill, "skills", action.entrySkill, "SKILL.md");
@@ -116181,42 +116252,59 @@ function createPluginMarketplaceAdapter(deps = {}) {
116181
116252
  }
116182
116253
  });
116183
116254
  }
116184
- let entrySkillPath = resolve22(installPath, "skills", action.entrySkill, "SKILL.md");
116185
- let entrySkillFound = false;
116186
- try {
116187
- const skillStat = await lstat37(entrySkillPath);
116188
- if (skillStat.isFile() && !skillStat.isSymbolicLink()) {
116189
- entrySkillFound = true;
116255
+ if (consumer === "codex") {
116256
+ try {
116257
+ await resolveCodexEntrySkillFile(installPath, action.entrySkill, action.plugin);
116258
+ } catch (entryErr) {
116259
+ return createResult({
116260
+ actionType,
116261
+ status: ActionStatus.OBSERVED,
116262
+ observation: {
116263
+ installed: true,
116264
+ installPath,
116265
+ entrySkillFound: false,
116266
+ error: `entry skill not resolvable in installed plugin: ${entryErr.message}`
116267
+ }
116268
+ });
116190
116269
  }
116191
- } catch {
116192
- }
116193
- if (!entrySkillFound && action.plugin) {
116194
- const prefixedPath = resolve22(
116195
- installPath,
116196
- "skills",
116197
- `${action.plugin}-${action.entrySkill}`,
116198
- "SKILL.md"
116199
- );
116270
+ } else {
116271
+ let entrySkillPath = resolve22(installPath, "skills", action.entrySkill, "SKILL.md");
116272
+ let entrySkillFound = false;
116200
116273
  try {
116201
- const skillStat = await lstat37(prefixedPath);
116274
+ const skillStat = await lstat37(entrySkillPath);
116202
116275
  if (skillStat.isFile() && !skillStat.isSymbolicLink()) {
116203
- entrySkillPath = prefixedPath;
116204
116276
  entrySkillFound = true;
116205
116277
  }
116206
116278
  } catch {
116207
116279
  }
116208
- }
116209
- if (!entrySkillFound) {
116210
- return createResult({
116211
- actionType,
116212
- status: ActionStatus.OBSERVED,
116213
- observation: {
116214
- installed: true,
116280
+ if (!entrySkillFound && action.plugin) {
116281
+ const prefixedPath = resolve22(
116215
116282
  installPath,
116216
- entrySkillFound: false,
116217
- error: `entry skill not found: skills/${action.entrySkill}/SKILL.md`
116283
+ "skills",
116284
+ `${action.plugin}-${action.entrySkill}`,
116285
+ "SKILL.md"
116286
+ );
116287
+ try {
116288
+ const skillStat = await lstat37(prefixedPath);
116289
+ if (skillStat.isFile() && !skillStat.isSymbolicLink()) {
116290
+ entrySkillPath = prefixedPath;
116291
+ entrySkillFound = true;
116292
+ }
116293
+ } catch {
116218
116294
  }
116219
- });
116295
+ }
116296
+ if (!entrySkillFound) {
116297
+ return createResult({
116298
+ actionType,
116299
+ status: ActionStatus.OBSERVED,
116300
+ observation: {
116301
+ installed: true,
116302
+ installPath,
116303
+ entrySkillFound: false,
116304
+ error: `entry skill not found: skills/${action.entrySkill}/SKILL.md`
116305
+ }
116306
+ });
116307
+ }
116220
116308
  }
116221
116309
  let manifestDigest;
116222
116310
  let manifestError = null;
@@ -116418,6 +116506,7 @@ var init_plugin_marketplace = __esm({
116418
116506
  __name(normalizeKimiSkillsRel, "normalizeKimiSkillsRel");
116419
116507
  __name(resolveKimiEntrySkillFile, "resolveKimiEntrySkillFile");
116420
116508
  __name(normalizeCodeBuddySkillsRel, "normalizeCodeBuddySkillsRel");
116509
+ __name(resolveCodexEntrySkillFile, "resolveCodexEntrySkillFile");
116421
116510
  __name(resolveCodeBuddyEntrySkillFile, "resolveCodeBuddyEntrySkillFile");
116422
116511
  SUPPORTED_TYPES = [
116423
116512
  ActionType.PLUGIN_MANIFEST_VALIDATE,
@@ -116458,6 +116547,14 @@ var init_plugin_marketplace = __esm({
116458
116547
  });
116459
116548
 
116460
116549
  // src/core/surface-host-bindings.mjs
116550
+ var surface_host_bindings_exports = {};
116551
+ __export(surface_host_bindings_exports, {
116552
+ deriveSurfaceHostBinding: () => deriveSurfaceHostBinding,
116553
+ groupSurfaceHostBindings: () => groupSurfaceHostBindings,
116554
+ normalizeGenericSkillsRel: () => normalizeGenericSkillsRel,
116555
+ normalizeSurfacePath: () => normalizeSurfacePath,
116556
+ pluginRootFromManifestRelativePath: () => pluginRootFromManifestRelativePath
116557
+ });
116461
116558
  import { dirname as dirname10, isAbsolute as isAbsolute18, join as join19, normalize as normalize3, resolve as resolve23, sep as sep9 } from "node:path";
116462
116559
  import { lstat as lstat38 } from "node:fs/promises";
116463
116560
  function normalizeSurfacePath(rawPath) {
@@ -116586,6 +116683,49 @@ import { dirname as dirname11, join as join20, relative as relative18, isAbsolut
116586
116683
  import { tmpdir as tmpdir3 } from "node:os";
116587
116684
  import { execFile as execFileCb7 } from "node:child_process";
116588
116685
  import { promisify as promisify7 } from "node:util";
116686
+ function transportIdentityEntries(entries) {
116687
+ return entries.map(({ path: path43, type, mode, size, contentDigest }) => ({
116688
+ path: path43,
116689
+ type,
116690
+ mode: mode & ~WRITE_BITS,
116691
+ size,
116692
+ contentDigest
116693
+ }));
116694
+ }
116695
+ function compareInstallToFrozenTransport(frozenEntries, installedEntries) {
116696
+ const frozen = transportIdentityEntries(frozenEntries);
116697
+ const installed = transportIdentityEntries(installedEntries);
116698
+ const installedByPath = new Map(installed.map((entry) => [entry.path, entry]));
116699
+ const conflicts = [];
116700
+ for (const frozenEntry of frozen) {
116701
+ const installedEntry = installedByPath.get(frozenEntry.path);
116702
+ if (!installedEntry) {
116703
+ conflicts.push(`missing: ${frozenEntry.path}`);
116704
+ continue;
116705
+ }
116706
+ if (installedEntry.type !== frozenEntry.type) {
116707
+ conflicts.push(`type mismatch: ${frozenEntry.path}`);
116708
+ } else if (installedEntry.size !== frozenEntry.size || installedEntry.contentDigest !== frozenEntry.contentDigest) {
116709
+ conflicts.push(`content mismatch: ${frozenEntry.path}`);
116710
+ } else if (installedEntry.mode !== frozenEntry.mode) {
116711
+ conflicts.push(`mode mismatch: ${frozenEntry.path}`);
116712
+ }
116713
+ }
116714
+ const frozenPaths = new Set(frozen.map((entry) => entry.path));
116715
+ for (const installedEntry of installed) {
116716
+ if (!frozenPaths.has(installedEntry.path)) {
116717
+ conflicts.push(`extra: ${installedEntry.path}`);
116718
+ }
116719
+ }
116720
+ return conflicts;
116721
+ }
116722
+ function frozenSnapshotRelativePath(unit, actions, unitId) {
116723
+ if (typeof unit?.frozenSnapshot?.path === "string" && unit.frozenSnapshot.path.length > 0) {
116724
+ return unit.frozenSnapshot.path;
116725
+ }
116726
+ const action = (actions ?? []).find((item) => item.unitId === unitId && typeof item.parameters?.snapshotPath === "string" && item.parameters.snapshotPath.length > 0);
116727
+ return action?.parameters?.snapshotPath ?? null;
116728
+ }
116589
116729
  async function verifyCursorHostDistributions({
116590
116730
  plan,
116591
116731
  root,
@@ -118161,16 +118301,65 @@ async function verifyRelease(options) {
118161
118301
  }
118162
118302
  const pluginDistCount = (surface.unit?.distributions ?? []).filter((d) => d.type !== "npm").length;
118163
118303
  if (pluginDistCount === 1 && surface.frozenPluginRoot === ".") {
118164
- const observedReceipt = createSkillResourceClosureReceipt(closureResult, {
118165
- unitId: surface.unitId,
118166
- preparedAt: expectedUnitReceipt.preparedAt ?? null,
118167
- exitCode: expectedUnitReceipt.exitCode ?? 0
118168
- });
118169
- assertSkillResourceClosureReceipt(
118170
- expectedUnitReceipt,
118171
- observedReceipt,
118172
- `unit "${surface.unitId}"`
118173
- );
118304
+ const frozenRel = frozenSnapshotRelativePath(surface.unit, actions, surface.unitId);
118305
+ if (frozenRel) {
118306
+ const frozenDir = await resolveFrozenPath(root, frozenRel, "frozen snapshot path");
118307
+ const frozenClosure = await checkSkillResourceClosure({
118308
+ snapshotDir: frozenDir,
118309
+ host: surface.host,
118310
+ surfaceHostBindings: [{ surfaceId: surface.surfaceId, host: surface.host }]
118311
+ });
118312
+ const frozenReceipt = createSkillResourceClosureReceipt(frozenClosure, {
118313
+ unitId: surface.unitId,
118314
+ preparedAt: expectedUnitReceipt.preparedAt ?? null,
118315
+ exitCode: expectedUnitReceipt.exitCode ?? 0
118316
+ });
118317
+ assertSkillResourceClosureReceipt(
118318
+ expectedUnitReceipt,
118319
+ frozenReceipt,
118320
+ `unit "${surface.unitId}"`
118321
+ );
118322
+ const frozenSnap = await computeFrozenSnapshot(frozenDir, {
118323
+ excludeRootEntries: TRANSPORT_EXCLUSIONS
118324
+ });
118325
+ const installedSnap = await computeFrozenSnapshot(surface.installPath, {
118326
+ excludeRootEntries: TRANSPORT_EXCLUSIONS
118327
+ });
118328
+ const conflicts = compareInstallToFrozenTransport(
118329
+ frozenSnap.entries,
118330
+ installedSnap.entries
118331
+ );
118332
+ await evidence.append({
118333
+ phase: "verify",
118334
+ step: "skill-resource-closure",
118335
+ status: "install-comparison",
118336
+ unitId: surface.unitId,
118337
+ frozenInputDigest: frozenSnap.digest,
118338
+ installedInputDigest: installedSnap.digest,
118339
+ frozenModes: frozenSnap.entries.map((entry) => ({ path: entry.path, mode: entry.mode })),
118340
+ installedModes: installedSnap.entries.map((entry) => ({ path: entry.path, mode: entry.mode }))
118341
+ });
118342
+ if (conflicts.length > 0) {
118343
+ const listed = conflicts.slice(0, PAYLOAD_CONFLICT_REPORT_CAP2).join("; ");
118344
+ const overflow = conflicts.length > PAYLOAD_CONFLICT_REPORT_CAP2 ? `; and ${conflicts.length - PAYLOAD_CONFLICT_REPORT_CAP2} more conflicting path(s)` : "";
118345
+ throw new ReleaseError(
118346
+ POST_PUBLISH_VERIFY_FAILED,
118347
+ `installed plugin root differs in path, bytes, size, or non-write mode bits for unit "${surface.unitId}": ${listed}${overflow}`,
118348
+ { unitId: surface.unitId, conflicts }
118349
+ );
118350
+ }
118351
+ } else {
118352
+ const observedReceipt = createSkillResourceClosureReceipt(closureResult, {
118353
+ unitId: surface.unitId,
118354
+ preparedAt: expectedUnitReceipt.preparedAt ?? null,
118355
+ exitCode: expectedUnitReceipt.exitCode ?? 0
118356
+ });
118357
+ assertSkillResourceClosureReceipt(
118358
+ expectedUnitReceipt,
118359
+ observedReceipt,
118360
+ `unit "${surface.unitId}"`
118361
+ );
118362
+ }
118174
118363
  }
118175
118364
  }
118176
118365
  skillResourceClosureReceipts.push(createSkillResourceClosureReceipt(
@@ -118381,7 +118570,7 @@ async function verifyRelease(options) {
118381
118570
  throw err;
118382
118571
  }
118383
118572
  }
118384
- var execFile6, VERIFICATION_RESOLVED_TYPES, ADAPTER_ACTION_TYPE_MAP2, FIRST_RELEASE_BOOTSTRAP_MODE, EXTERNAL_MARKETPLACE_SHA_RE2, defaultNpmExecutor;
118573
+ var execFile6, VERIFICATION_RESOLVED_TYPES, WRITE_BITS, PAYLOAD_CONFLICT_REPORT_CAP2, ADAPTER_ACTION_TYPE_MAP2, FIRST_RELEASE_BOOTSTRAP_MODE, EXTERNAL_MARKETPLACE_SHA_RE2, defaultNpmExecutor;
118385
118574
  var init_verify = __esm({
118386
118575
  async "src/commands/verify.mjs"() {
118387
118576
  init_src2();
@@ -118419,6 +118608,11 @@ var init_verify = __esm({
118419
118608
  PASSED_MANUAL: "PASSED_MANUAL",
118420
118609
  NOT_REQUIRED_UNCHANGED: "NOT_REQUIRED_UNCHANGED"
118421
118610
  });
118611
+ WRITE_BITS = 146;
118612
+ PAYLOAD_CONFLICT_REPORT_CAP2 = 10;
118613
+ __name(transportIdentityEntries, "transportIdentityEntries");
118614
+ __name(compareInstallToFrozenTransport, "compareInstallToFrozenTransport");
118615
+ __name(frozenSnapshotRelativePath, "frozenSnapshotRelativePath");
118422
118616
  __name(verifyCursorHostDistributions, "verifyCursorHostDistributions");
118423
118617
  __name(verifyPublicSourceAuthorityReceiptOffline, "verifyPublicSourceAuthorityReceiptOffline");
118424
118618
  ADAPTER_ACTION_TYPE_MAP2 = {
@@ -139649,7 +139843,7 @@ async function observeQoderPlugin(target, command2, env, run6) {
139649
139843
  found
139650
139844
  };
139651
139845
  }
139652
- async function readQoderManifest(root, label) {
139846
+ async function readQoderManifest(root, label, target) {
139653
139847
  const candidates = [];
139654
139848
  for (const manifestPath of [".qoder-plugin/plugin.json", ".claude-plugin/plugin.json"]) {
139655
139849
  let text;
@@ -139663,10 +139857,11 @@ async function readQoderManifest(root, label) {
139663
139857
  if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
139664
139858
  throw new Error(`${label} (${manifestPath}) did not return an object`);
139665
139859
  }
139860
+ await assertQoderPluginManifest(target, manifest, `${label} (${manifestPath})`, root);
139666
139861
  candidates.push({ manifestPath, manifest });
139667
139862
  }
139668
139863
  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])) {
139864
+ if (candidates.length === 2 && ["name", "version"].some((field) => candidates[0].manifest[field] !== candidates[1].manifest[field])) {
139670
139865
  throw new Error(`${label} does not match the frozen Qoder plugin identity and projection path: conflicting native and compatible manifests`);
139671
139866
  }
139672
139867
  return candidates[0];
@@ -139687,26 +139882,43 @@ async function assertFrozenQoderManifest(target, root) {
139687
139882
  snapshotPath: target.snapshotPath,
139688
139883
  expectedDigest: target.manifestDigest
139689
139884
  });
139690
- const { manifestPath, manifest } = await readQoderManifest(snapshotRoot, "frozen Qoder plugin manifest");
139885
+ const { manifestPath, manifest } = await readQoderManifest(snapshotRoot, "frozen Qoder plugin manifest", target);
139691
139886
  const skills = await assertQoderPluginManifest(target, manifest, "frozen Qoder plugin manifest", snapshotRoot);
139692
139887
  return { manifestPath, skills };
139693
139888
  }
139694
139889
  async function assertQoderHubEntry(target, marketplaceRoot) {
139695
- const marketplace = parseJson2(
139696
- await readFileContained(marketplaceRoot, "marketplace.json", { encoding: "utf8" }),
139697
- "Qoder Hub marketplace manifest"
139698
- );
139699
- if (marketplace?.name !== target.marketplace || !Array.isArray(marketplace.plugins)) {
139700
- throw new Error("Qoder Hub marketplace manifest does not match the frozen marketplace");
139701
- }
139702
- const matches = marketplace.plugins.filter((entry) => entry?.name === target.plugin);
139703
- if (matches.length !== 1) {
139704
- throw new Error(`Qoder Hub marketplace manifest must contain exactly one ${target.plugin} entry`);
139705
- }
139706
- const source = matches[0]?.source;
139707
- if (source?.source !== "url" || normalizeHubGitSource(source.url, target.hub.githubHost) !== target.pluginRepo || source.sha !== target.pluginCommit) {
139708
- throw new Error("Qoder Hub entry does not match the frozen public source identity");
139890
+ let found = false;
139891
+ for (const indexPath of ["marketplace.json", ".claude-plugin/marketplace.json"]) {
139892
+ let text;
139893
+ try {
139894
+ text = await readFileContained(marketplaceRoot, indexPath, { encoding: "utf8" });
139895
+ } catch (error) {
139896
+ if (error?.code === "SFC2004" && error?.details?.kind === "missing-resource") continue;
139897
+ throw error;
139898
+ }
139899
+ found = true;
139900
+ const marketplace = parseJson2(text, `Qoder Hub marketplace manifest (${indexPath})`);
139901
+ if (!marketplace || typeof marketplace !== "object" || Array.isArray(marketplace) || marketplace.name !== target.marketplace || !Array.isArray(marketplace.plugins)) {
139902
+ throw new Error("Qoder Hub marketplace manifest does not match the frozen marketplace");
139903
+ }
139904
+ if (indexPath === ".claude-plugin/marketplace.json") {
139905
+ if (target.hub.githubHost !== "github.com" || typeof target.frozenTag !== "string" || target.frozenTag.length === 0) {
139906
+ throw new Error("Qoder compatible Hub entry requires a frozen GitHub tag identity");
139907
+ }
139908
+ await assertHubHostEntry({ ...target, host: "claude", pluginTag: target.frozenTag, githubHost: target.hub.githubHost }, marketplaceRoot);
139909
+ continue;
139910
+ }
139911
+ const matches = marketplace.plugins.filter((entry2) => entry2?.name === target.plugin);
139912
+ if (matches.length !== 1) {
139913
+ throw new Error(`Qoder Hub marketplace manifest must contain exactly one ${target.plugin} entry`);
139914
+ }
139915
+ const entry = matches[0];
139916
+ const source = entry.source;
139917
+ if (source?.source !== "url" || normalizeHubGitSource(source.url, target.hub.githubHost) !== target.pluginRepo || source.sha !== target.pluginCommit || entry.version !== void 0 && entry.version !== target.version || source.ref !== void 0 && (typeof target.frozenTag !== "string" || source.ref !== target.frozenTag)) {
139918
+ throw new Error("Qoder Hub entry does not match the frozen public source identity");
139919
+ }
139709
139920
  }
139921
+ if (!found) throw new Error("Qoder Hub has no native or compatible marketplace manifest");
139710
139922
  }
139711
139923
  async function verifyQoderInstalledPayload({
139712
139924
  target,
@@ -139715,7 +139927,7 @@ async function verifyQoderInstalledPayload({
139715
139927
  frozenManifest,
139716
139928
  verifyInstalledPayload
139717
139929
  }) {
139718
- const { manifestPath, manifest } = await readQoderManifest(installPath, "installed Qoder plugin manifest");
139930
+ const { manifestPath, manifest } = await readQoderManifest(installPath, "installed Qoder plugin manifest", target);
139719
139931
  if (manifestPath !== frozenManifest.manifestPath) {
139720
139932
  throw new Error("installed Qoder plugin manifest path differs from the frozen manifest selection");
139721
139933
  }
@@ -17,6 +17,10 @@
17
17
  "digest": "86c9cab024ebe9c7f8ec3358fcc12d05b8ed246ab59da2b0acc6a517f31274ea",
18
18
  "bytes": 2488
19
19
  },
20
+ "evidence-event-v2.schema.json": {
21
+ "digest": "693430e636afc85bf4a79e3f7bf3fb87254cbd62f2e3f35c35d6f84e5e8e0be1",
22
+ "bytes": 6058
23
+ },
20
24
  "evidence-event.schema.json": {
21
25
  "digest": "b7e14522a5aba818cab191545f23d2323be0e4c80566e9d3e4da576103a00490",
22
26
  "bytes": 2390
@@ -34,8 +38,8 @@
34
38
  "bytes": 57111
35
39
  },
36
40
  "release-run.schema.json": {
37
- "digest": "4a041b8f6488e7d61b7eb271ebdbc2e043731df41c4437783ef63e77f664c192",
38
- "bytes": 19002
41
+ "digest": "26c44ddf1ed42f266a2ddee7f6132cee94c59593e37f24359c9c48f12612ec22",
42
+ "bytes": 21874
39
43
  },
40
44
  "test-intake.schema.json": {
41
45
  "digest": "654e91ce518cb2b8aca84947d0c3bb8c4262ab047d86081e7e93dc07a9c6c739",
@@ -7,36 +7,29 @@ description: "Config-only workflow profile: route confirms config-only diff, ass
7
7
 
8
8
  ## 触发
9
9
 
10
- 用户询问或执行纯配置变更的发布流程。包含 `.release-skill/project.yaml`、配置 schema 等配置文件的修改,且无代码、文档或 marketplace 变更。
10
+ 用户发起仅配置变更的发布流程,涉及 `.release-skill/project.yaml`、配置 schema、`releaseUnits` 映射、`policy` 策略定义等配置文件的修改,且无代码、文档或 marketplace 变更。
11
11
 
12
12
  ## 当前状态
13
13
 
14
- `release-config` 是工作流配置文件 (§4) 定义的独立工作流之一,专门处理仅配置变更的场景。它遵循简化生命周期,专注于配置验证和发布路径决策分支。
14
+ `release-config` 是工作流配置文件(§4)定义的独立工作流之一,专门处理仅配置变更场景。其遵循简化生命周期,专注于配置验证与发布路径决策分支。
15
15
 
16
- **机械实现**: `prepare --workflow config` 确定性地裁剪代码类门禁(H5),并比较当前
17
- per-unit `snapshotDigest` 与最新冻结 plan——公开字节未变 → `publishPath:
18
- 'no-publish-needed'`(externalActions 被移除,无 publish 路径);字节变化或无对比
19
- plan → `publishPath: 'publish-needed'`(fail-safe)。决策以 `workflowDecision` 绑定
20
- 进 plan digest,不可篡改。**不存在** `NO_PUBLISH_NEEDED`/`REPORTED` 虚构状态:
21
- prepare 的真实输出是 plan 内的 `workflowDecision`。
16
+ **机械实现**:`prepare --workflow config` 确定性地裁剪代码类门禁(H5),并比较当前 per-unit `snapshotDigest` 与最新冻结 plan——公开字节未变 → `publishPath: 'no-publish-needed'`(`externalActions` 被移除,无 publish 路径);字节变化或无对比 plan → `publishPath: 'publish-needed'`(fail-safe)。决策以 `workflowDecision` 绑定进 plan digest,不可篡改。**不存在** `NO_PUBLISH_NEEDED`/`REPORTED` 虚构状态:prepare 的真实输出是 plan 内的 `workflowDecision`。
22
17
 
23
18
  **边界**:裁剪代码类检查(declared hooks、snapshot-verify gates、skill-resource-closure),保留基线、快照、计划冻结和文档新鲜度检查。来源权威检查按发布分支决定,不能一并裁掉:
24
19
 
25
- - 场景 A:公开字节未变,`externalActions=[]`,不生成 `plan.sourceAuthority`(冻结的源码来源权威),也不进入 approve/publish/verify。即使配置了公开来源收据,这一边界也不变。
26
- - 场景 B:公开字节变化或不可判定,需要发布。production prepare 必须通过现有来源权威检查并冻结 `plan.sourceAuthority`;publish、reconcile、verify 继续校验冻结绑定和远端一致性,consumer-verify 保留。
20
+ - **场景 A**:公开字节未变,`externalActions=[]`,不生成 `plan.sourceAuthority`(冻结的源码来源权威),也不进入 approve/publish/verify。即使配置了公开来源收据,这一边界也不变。
21
+ - **场景 B**:公开字节变化或不可判定,需要发布。production prepare 必须通过现有来源权威检查并冻结 `plan.sourceAuthority`;publish、reconcile、verify 继续校验冻结绑定和远端一致性,consumer-verify 保留。
27
22
 
28
23
  旧 production 计划缺少 `sourceAuthority` 时,仍在外部写入前拒绝。未发生外部写入的旧计划必须重新 prepare 并批准新摘要,不补字段、不迁移旧批准。已有 `PARTIAL` 保留检查点,走匹配版本的恢复路径。docs production 同样要求来源权威;marketplace 委托目标工作区发布的边界不变。
29
24
 
30
25
  ## 职责与边界
31
26
 
32
- - **步骤① diff 确认 config-only**: 调用 `release-skill route` command 确认仅有配置变更
33
- - **步骤② assess with schema validation**: 执行配置结构验证、schema 检查、contract 一致性
34
- - **步骤③ prepare --workflow config**: 本地 freeze 检查,生成只读计划;输出 `workflowDecision`
35
- - **步骤④ decision branch**(来自 plan.workflowDecision):
36
- - 场景 A:`publishPath === 'no-publish-needed'`(公开字节未变化)→ 记录决策并结束,
37
- 无 publish 路径,不运行 approve/publish/verify
38
- - 场景 B:`publishPath === 'publish-needed'`(公开 surface 变化或不可判定)→ 运行
39
- 轻链 (approve→publish→verify)
27
+ - **步骤① diff 确认 config-only**:调用 `release-skill route` 命令确认仅有配置变更
28
+ - **步骤② assess with schema validation**:执行配置结构验证、schema 检查、contract 一致性
29
+ - **步骤③ prepare --workflow config**:本地 freeze 检查,生成只读计划;输出 `workflowDecision`
30
+ - **步骤④ decision branch**(来自 plan.workflowDecision):
31
+ - **场景 A**:`publishPath === 'no-publish-needed'`(公开字节未变化)→ 记录决策并结束,无 publish 路径,不运行 approve/publish/verify
32
+ - **场景 B**:`publishPath === 'publish-needed'`(公开 surface 变化或不可判定)→ 运行轻链(approve→publish→verify)
40
33
 
41
34
  存量增量只改本轮目标映射。已有合法字段、历史发行记录与精确授权保持不变,不要重建 `.release-skill/project.yaml`。
42
35
 
@@ -44,25 +37,27 @@ prepare 的真实输出是 plan 内的 `workflowDecision`。
44
37
 
45
38
  `delivery.include/exclude` 不是文件包含或排除。排除不得覆盖 `requiredPublicFiles`。互相冲突的配置必须失败关闭。Foundation 建议不能作为发布批准。未经精确批准不产生远端动作。分类文字不会机械识别全部私有内容。接入评估缺失码 `REQUIRED_PUBLIC_FILE_MISSING` 不属于本技能的 prepare 路径;config-only prepare 仍走 `release-prepare` 原生错误。
46
39
 
47
- **授权边界**: 命令调用本身即授权执行配置的 hooks/gates。config-only 工作流的远程写操作受标准 approval 机制约束,不 bypass 安全 gate。
40
+ **授权边界**:命令调用本身即授权执行配置的 hooks/gates。config-only 工作流的远程写操作受标准 approval 机制约束,不 bypass 安全 gate。
48
41
 
49
- **阶段通过规则**:
50
- - 场景 A: prepare 输出 `workflowDecision.decision === 'public-bytes-unchanged'`(无 publish 路径)
51
- - 场景 B: `status === 'VERIFIED'` + CLI exit code 0
42
+ **接入选择**:进入本入口的新一轮生产准备前,必须按 `release-prepare` 同一完整流程核对 `testIntake`。若缺失,须说明缺项并默认推动 `skill-family-test:skill-family-test-plan`,再在授权范围内调用 `skill-family-test:skill-family-test-run`,由 TEST 组织检查并交回结果。跳过须先明确征询,再依据本轮适用且范围匹配的明确同意决定。**沉默、拒绝、仅普通发布预授权、旧轮次、其他项目、版本或范围不匹配均不可跳过**。本轮匹配答复可复用。**不得把跳过写成测试已达标,也不得用跳过清除已声明的适用要求**。已冻结计划的批准、发布、对账与正式验证不回头追加接入义务。只读和非生产准备不新增接入义务。直接非交互 CLI 仍接受可选 `testIntake`,不新增选择参数。答复与适用范围记入既有任务或执行计划。
43
+
44
+ **阶段通过规则**:
45
+ - **场景 A**:prepare 输出 `workflowDecision.decision === 'public-bytes-unchanged'`(无 publish 路径)
46
+ - **场景 B**:`status === 'VERIFIED'` + CLI exit code 0
52
47
 
53
48
  ## 正向执行路径
54
49
 
55
- 1. 运行 `release-skill route --root <path> [--target-version <ver>] --json` 获取 diff 分类;已知目标版本时显式传入,route 只是工作流建议
50
+ 1. 运行 `release-skill route --root <path> [--target-version <ver>] --json` 获取 diff 分类;已知目标版本时显式传入,`route` 只是工作流建议
56
51
  2. 若推荐 `workflowKind === 'config-only'`,则使用本技能
57
52
  3. 执行步骤②:运行 `release-skill assess --root <path> --offline --json` 进行 schema validation
58
- 4. 执行步骤③:运行 `release-skill prepare --offline --workflow config --target-version <ver> --json`
59
- 5. 读取输出中的 `workflowDecision`:
60
- - **A**: `publishPath === 'no-publish-needed'` → 记录决策(plan 已冻结,无
61
- externalActions),结束。无需 approve/publish/verify
62
- - **B**: `publishPath === 'publish-needed'` → 继续 approve → publish → verify 链条
63
- 6. (场景 B 续) 执行步骤⑥:运行 `release-skill approve --plan <path> --actor <name>`
64
- 7. (场景 B 续) 执行步骤⑦:运行 `release-skill publish --plan <path> --approval <path>`
65
- 8. (场景 B 续) 执行步骤⑧:运行 `release-skill verify --plan <path> --run <path>`
53
+ 4. 进入本入口新一轮生产准备前完成接入选择:按 `release-prepare` 同一完整流程核对 `testIntake`,默认推动 `skill-family-test:skill-family-test-plan` 与授权范围内的 `skill-family-test-run`。跳过须先明确征询再依据本轮匹配答复决定。未完成接入且无有效跳过时,不进入下一步 `prepare`。该分支只决定后续是否进入发布链,不改变生产 prepare 前的测试接入要求。
54
+ 5. 执行步骤③:运行 `release-skill prepare --offline --workflow config --target-version <ver> --json`
55
+ 6. 读取输出中的 `workflowDecision`:
56
+ - **A**:`publishPath === 'no-publish-needed'` → 记录决策(plan 已冻结,无 `externalActions`),结束。无需 approve/publish/verify
57
+ - **B**:`publishPath === 'publish-needed'` → 继续 approve → publish → verify 链条
58
+ 7. (场景 B 续)执行步骤⑥:运行 `release-skill approve --plan <path> --actor <name>`
59
+ 8. (场景 B 续)执行步骤⑦:运行 `release-skill publish --plan <path> --approval <path>`
60
+ 9. (场景 B 续)执行步骤⑧:运行 `release-skill verify --plan <path> --run <path> --json`
66
61
 
67
62
  ## 确定性脚本调用
68
63
 
@@ -135,21 +130,21 @@ node "${CLAUDE_PLUGIN_ROOT}/bin/release-skill.mjs" verify \
135
130
 
136
131
  ## 与其他工作流的关系
137
132
 
138
- - **docs-only**: 纯文档变更时使用 `release-docs`
139
- - **marketplace-only**: 仅 marketplace 变更时使用独立工作流
140
- - **full-happy-end**: 混合变更(code+config+docs)时降级到此完整路径
133
+ - **docs-only**:纯文档变更时使用 `release-docs`
134
+ - **marketplace-only**:仅 marketplace 变更时使用独立工作流
135
+ - **full-happy-end**:混合变更(code+config+docs)时降级到此完整路径
141
136
 
142
137
  ## 关联技能
143
138
 
144
- - `release-skill route`: 快速入门决策路由(§4.3)
145
- - `release-assess`: 配置 schema/contract 验证入口
146
- - `release-prepare`: 离线冻结检查(`--workflow config` 输出 publish-path 决策)
147
- - `release-approve`: Plan approval(仅场景 B)
148
- - `release-publish`: 制品发布(仅在 public surface 变化时执行)
149
- - `release-verify`: 消费者验证(场景 B 的最终验证门)
139
+ - `release-skill route`:快速入门决策路由(§4.3)
140
+ - `release-assess`:配置 schema/contract 验证入口
141
+ - `release-prepare`:离线冻结检查(`--workflow config` 输出 publish-path 决策)
142
+ - `release-approve`:Plan approval(仅场景 B)
143
+ - `release-publish`:制品发布(仅在 public surface 变化时执行)
144
+ - `release-verify`:消费者验证(场景 B 的最终验证门)
150
145
 
151
146
  ## 人类最终回复
152
147
 
153
148
  纯问询或转交时短答:写明实际回答、适用范围或理由、必要限制和下一步。建议保持为建议。
154
149
 
155
- 执行路径说明实际分支、已完成步骤、未执行步骤,以及既有计划、`workflowDecision` 产物和限制。`publishPath` 为 `no-publish-needed` 时按原合同结束,无发布路径。场景 B 只写入已经执行的 approve、publish、verify 结果。已有文件输出按原合同呈现。栏目只收录当前命令或分支已经返回的事实。
150
+ 执行路径说明实际分支、已完成步骤、未执行步骤,以及既有计划、`workflowDecision` 产物和限制。`publishPath` 为 `no-publish-needed` 时按原合同结束,无发布路径。场景 B 只写入已经执行的 approve、publish、verify 结果。已有文件输出按原合同呈现。栏目只收录当前命令或分支已经返回的事实。