release-skill 0.4.0 → 0.4.1

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.
@@ -9,8 +9,12 @@
9
9
  {
10
10
  "name": "release-skill",
11
11
  "source": "./",
12
- "version": "0.4.0",
13
- "description": "Safe preparation and frozen GitHub/npm production publishing with full happy end verification"
12
+ "version": "0.4.1",
13
+ "description": "Safe preparation and frozen GitHub/npm production publishing with full happy end verification",
14
+ "author": {
15
+ "name": "广州市风荷科技有限公司"
16
+ },
17
+ "license": "Apache-2.0"
14
18
  }
15
19
  ]
16
20
  }
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "release-skill",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Safe preparation and frozen GitHub/npm production publishing with full happy end verification",
5
5
  "author": {
6
6
  "name": "广州市风荷科技有限公司"
7
7
  },
8
- "license": "MIT",
8
+ "license": "Apache-2.0",
9
9
  "skills": "./adapters/claude/skills/"
10
10
  }
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "release-skill",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Safe preparation and frozen GitHub/npm production publishing with full happy end verification",
5
5
  "author": {
6
6
  "name": "广州市风荷科技有限公司"
7
7
  },
8
- "license": "MIT",
8
+ "license": "Apache-2.0",
9
9
  "skills": [
10
10
  "./adapters/workbuddy/skills/"
11
11
  ]
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "release-skill",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Safe preparation and frozen GitHub/npm production publishing with full happy end verification",
5
5
  "author": {
6
6
  "name": "广州市风荷科技有限公司"
7
7
  },
8
- "license": "MIT",
8
+ "license": "Apache-2.0",
9
9
  "skills": "./adapters/codex/skills/",
10
10
  "interface": {
11
11
  "displayName": "Release Skill",
12
12
  "shortDescription": "Safe preparation and frozen GitHub/npm publishing",
13
13
  "longDescription": "Prepares byte-faithful public snapshots without rewriting project source files and publishes approved frozen GitHub/npm artifacts. Full happy end verification confirms consumer installation from frozen Git ref.",
14
- "developerName": "ifoohoo",
14
+ "developerName": "广州市风荷科技有限公司",
15
15
  "category": "DevOps",
16
16
  "capabilities": [
17
17
  "Write",
@@ -19,7 +19,7 @@
19
19
  ],
20
20
  "defaultPrompt": [
21
21
  "Assess this project for release readiness.",
22
- "Prepare a release plan for version 0.4.0.",
22
+ "Prepare a release plan for version 0.4.1.",
23
23
  "Help me understand the release workflow."
24
24
  ]
25
25
  }
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "release-skill",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Safe preparation and frozen GitHub/npm production publishing with full happy end verification",
5
5
  "author": {
6
6
  "name": "广州市风荷科技有限公司"
7
7
  },
8
- "license": "MIT",
8
+ "license": "Apache-2.0",
9
9
  "homepage": "https://github.com/ifoohoo/release-skill#readme",
10
10
  "keywords": [
11
11
  "release",
@@ -21,7 +21,7 @@
21
21
  "displayName": "Release Skill",
22
22
  "shortDescription": "Safe preparation and frozen GitHub/npm publishing",
23
23
  "longDescription": "Prepares byte-faithful public snapshots without rewriting project source files and publishes approved frozen GitHub/npm artifacts. Full happy end verification confirms consumer installation from frozen Git ref.",
24
- "developerName": "ifoohoo",
24
+ "developerName": "广州市风荷科技有限公司",
25
25
  "websiteURL": "https://github.com/ifoohoo/release-skill"
26
26
  }
27
27
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ <!-- release-skill:changelog:start version=0.4.1 locale=en baseline=sha256:e2ccb5ddfef6c5e00b047bbf4fa009f5f5c10b003aa2254756f6f9b73a9b3346 -->
4
+ ## [0.4.1] - 2026-08-03
5
+
6
+ v0.4.1 is the unreleased source candidate that migrates release-skill to Apache License 2.0 and aligns public author metadata while preserving repository ownership and historical attribution.
7
+
8
+ ### Changed
9
+
10
+ - **Open-source license**: package, plugin manifests, README, LICENSE, and NOTICE now consistently use Apache-2.0.
11
+ - **Identity separation**: project author and developer metadata names 广州市风荷科技有限公司, while the `ifoohoo` repository and Marketplace owner coordinates remain unchanged.
12
+ - **Historical attribution**: existing contributor and company copyright notices remain; metadata and license changes do not assert a copyright transfer.
13
+ <!-- release-skill:changelog:end version=0.4.1 locale=en -->
14
+
15
+
3
16
  <!-- release-skill:changelog:start version=0.4.0 locale=en baseline=sha256:099d9db24049aeaae78a21d3bd5152582bb20d4efad1ceff085f9609e8e6d52e -->
4
17
  ## [0.4.0] - 2026-08-02
5
18
 
package/INSTALL.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [简体中文](INSTALL.zh-CN.md)
4
4
 
5
- <!-- release-skill:release-version: 0.4.0 -->
5
+ <!-- release-skill:release-version: 0.4.1 -->
6
6
  ## Prerequisites
7
7
 
8
8
  - Node.js 22.0.0 or later
@@ -114,7 +114,7 @@ tag pinned to the exact version (never the bare repository URL, which installs
114
114
  the latest release or default branch), confirm the trust prompt, then reload:
115
115
 
116
116
  ```
117
- /plugins install https://github.com/ifoohoo/release-skill/releases/tag/release-skill-v0.4.0
117
+ /plugins install https://github.com/ifoohoo/release-skill/releases/tag/release-skill-v0.4.1
118
118
  /plugins reload
119
119
  ```
120
120
 
package/INSTALL.zh-CN.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [English](INSTALL.md)
4
4
 
5
- <!-- release-skill:release-version: 0.4.0 -->
5
+ <!-- release-skill:release-version: 0.4.1 -->
6
6
  ## 前置条件
7
7
 
8
8
  - Node.js 22.0.0 或更高版本
@@ -104,7 +104,7 @@ Kimi Code 有交互式插件市场,但**没有可脚本化的非交互安装
104
104
  (切勿使用裸仓库地址,它会安装最新 release 或默认分支),确认信任提示后重新加载:
105
105
 
106
106
  ```
107
- /plugins install https://github.com/ifoohoo/release-skill/releases/tag/release-skill-v0.4.0
107
+ /plugins install https://github.com/ifoohoo/release-skill/releases/tag/release-skill-v0.4.1
108
108
  /plugins reload
109
109
  ```
110
110
 
package/LICENSE CHANGED
@@ -1,22 +1,187 @@
1
- MIT License
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction, and
10
+ distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by the
13
+ copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all other
16
+ entities that control, are controlled by, or are under common control with
17
+ that entity. For the purposes of this definition, "control" means (i) the
18
+ power, direct or indirect, to cause the direction or management of such
19
+ entity, whether by contract or otherwise, or (ii) ownership of fifty percent
20
+ (50%) or more of the outstanding shares, or (iii) beneficial ownership of
21
+ such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity exercising
24
+ permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation source, and
28
+ configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical transformation
31
+ or translation of a Source form, including but not limited to compiled object
32
+ code, generated documentation, and conversions to other media types.
33
+
34
+ "Work" shall mean the work of authorship, whether in Source or Object form,
35
+ made available under the License, as indicated by a copyright notice that is
36
+ included in or attached to the work (an example is provided in the Appendix
37
+ below).
38
+
39
+ "Derivative Works" shall mean any work, whether in Source or Object form,
40
+ that is based on (or derived from) the Work and for which the editorial
41
+ revisions, annotations, elaborations, or other modifications represent, as a
42
+ whole, an original work of authorship. For the purposes of this License,
43
+ Derivative Works shall not include works that remain separable from, or merely
44
+ link (or bind by name) to the interfaces of, the Work and Derivative Works
45
+ thereof.
46
+
47
+ "Contribution" shall mean any work of authorship, including the original
48
+ version of the Work and any modifications or additions to that Work or
49
+ Derivative Works thereof, that is intentionally submitted to Licensor for
50
+ inclusion in the Work by the copyright owner or by an individual or Legal
51
+ Entity authorized to submit on behalf of the copyright owner. For the purposes
52
+ of this definition, "submitted" means any form of electronic, verbal, or
53
+ written communication sent to the Licensor or its representatives, including
54
+ but not limited to communication on electronic mailing lists, source code
55
+ control systems, and issue tracking systems that are managed by, or on behalf
56
+ of, the Licensor for the purpose of discussing and improving the Work, but
57
+ excluding communication that is conspicuously marked or otherwise designated
58
+ in writing by the copyright owner as "Not a Contribution."
59
+
60
+ "Contributor" shall mean Licensor and any individual or Legal Entity on
61
+ behalf of whom a Contribution has been received by Licensor and subsequently
62
+ incorporated within the Work.
63
+
64
+ 2. Grant of Copyright License. Subject to the terms and conditions of this
65
+ License, each Contributor hereby grants to You a perpetual, worldwide,
66
+ non-exclusive, no-charge, royalty-free, irrevocable copyright license to
67
+ reproduce, prepare Derivative Works of, publicly display, publicly perform,
68
+ sublicense, and distribute the Work and such Derivative Works in Source or
69
+ Object form.
70
+
71
+ 3. Grant of Patent License. Subject to the terms and conditions of this
72
+ License, each Contributor hereby grants to You a perpetual, worldwide,
73
+ non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this
74
+ section) patent license to make, have made, use, offer to sell, sell, import,
75
+ and otherwise transfer the Work, where such license applies only to those
76
+ patent claims licensable by such Contributor that are necessarily infringed by
77
+ their Contribution(s) alone or by combination of their Contribution(s) with
78
+ the Work to which such Contribution(s) was submitted. If You institute patent
79
+ litigation against any entity (including a cross-claim or counterclaim in a
80
+ lawsuit) alleging that the Work or a Contribution incorporated within the Work
81
+ constitutes direct or contributory patent infringement, then any patent
82
+ licenses granted to You under this License for that Work shall terminate as of
83
+ the date such litigation is filed.
84
+
85
+ 4. Redistribution. You may reproduce and distribute copies of the Work or
86
+ Derivative Works thereof in any medium, with or without modifications, and in
87
+ Source or Object form, provided that You meet the following conditions:
88
+
89
+ (a) You must give any other recipients of the Work or Derivative Works a copy
90
+ of this License; and
91
+
92
+ (b) You must cause any modified files to carry prominent notices stating that
93
+ You changed the files; and
94
+
95
+ (c) You must retain, in the Source form of any Derivative Works that You
96
+ distribute, all copyright, patent, trademark, and attribution notices from the
97
+ Source form of the Work, excluding those notices that do not pertain to any
98
+ part of the Derivative Works; and
99
+
100
+ (d) If the Work includes a "NOTICE" text file as part of its distribution,
101
+ then any Derivative Works that You distribute must include a readable copy of
102
+ the attribution notices contained within such NOTICE file, excluding those
103
+ notices that do not pertain to any part of the Derivative Works, in at least
104
+ one of the following places: within a NOTICE text file distributed as part of
105
+ the Derivative Works; within the Source form or documentation, if provided
106
+ along with the Derivative Works; or, within a display generated by the
107
+ Derivative Works, if and wherever such third-party notices normally appear.
108
+ The contents of the NOTICE file are for informational purposes only and do not
109
+ modify the License. You may add Your own attribution notices within Derivative
110
+ Works that You distribute, alongside or as an addendum to the NOTICE text from
111
+ the Work, provided that such additional attribution notices cannot be
112
+ construed as modifying the License.
113
+
114
+ You may add Your own copyright statement to Your modifications and may provide
115
+ additional or different license terms and conditions for use, reproduction, or
116
+ distribution of Your modifications, or for any such Derivative Works as a
117
+ whole, provided Your use, reproduction, and distribution of the Work otherwise
118
+ complies with the conditions stated in this License.
119
+
120
+ 5. Submission of Contributions. Unless You explicitly state otherwise, any
121
+ Contribution intentionally submitted for inclusion in the Work by You to the
122
+ Licensor shall be under the terms and conditions of this License, without any
123
+ additional terms or conditions. Notwithstanding the above, nothing herein
124
+ shall supersede or modify the terms of any separate license agreement you may
125
+ have executed with Licensor regarding such Contributions.
126
+
127
+ 6. Trademarks. This License does not grant permission to use the trade names,
128
+ trademarks, service marks, or product names of the Licensor, except as required
129
+ for reasonable and customary use in describing the origin of the Work and
130
+ reproducing the content of the NOTICE file.
131
+
132
+ 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in
133
+ writing, Licensor provides the Work (and each Contributor provides its
134
+ Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
135
+ KIND, either express or implied, including, without limitation, any warranties
136
+ or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
137
+ PARTICULAR PURPOSE. You are solely responsible for determining the
138
+ appropriateness of using or redistributing the Work and assume any risks
139
+ associated with Your exercise of permissions under this License.
140
+
141
+ 8. Limitation of Liability. In no event and under no legal theory, whether in
142
+ tort (including negligence), contract, or otherwise, unless required by
143
+ applicable law (such as deliberate and grossly negligent acts) or agreed to in
144
+ writing, shall any Contributor be liable to You for damages, including any
145
+ direct, indirect, special, incidental, or consequential damages of any
146
+ character arising as a result of this License or out of the use or inability to
147
+ use the Work (including but not limited to damages for loss of goodwill, work
148
+ stoppage, computer failure or malfunction, or any and all other commercial
149
+ damages or losses), even if such Contributor has been advised of the
150
+ possibility of such damages.
151
+
152
+ 9. Accepting Warranty or Additional Liability. While redistributing the Work
153
+ or Derivative Works thereof, You may choose to offer, and charge a fee for,
154
+ acceptance of support, warranty, indemnity, or other liability obligations
155
+ and/or rights consistent with this License. However, in accepting such
156
+ obligations, You may act only on Your own behalf and on Your sole
157
+ responsibility, not on behalf of any other Contributor, and only if You agree
158
+ to indemnify, defend, and hold each Contributor harmless for any liability
159
+ incurred by, or claims asserted against, such Contributor by reason of your
160
+ accepting any such warranty or additional liability.
161
+
162
+ END OF TERMS AND CONDITIONS
163
+
164
+ APPENDIX: How to apply the Apache License to your work.
165
+
166
+ To apply the Apache License to your work, attach the following boilerplate
167
+ notice, with the fields enclosed by brackets "[]" replaced with your own
168
+ identifying information. (Don't include the brackets!) The text should be
169
+ enclosed in the appropriate comment syntax for the file format. We also
170
+ recommend that a file or class name and description of purpose be included on
171
+ the same "printed page" as the copyright notice for easier identification
172
+ within third-party archives.
2
173
 
3
174
  Copyright (c) 2026 release-skill contributors
4
175
  Copyright (c) 2026 广州市风荷科技有限公司
5
176
 
6
- Permission is hereby granted, free of charge, to any person obtaining a copy
7
- of this software and associated documentation files (the "Software"), to deal
8
- in the Software without restriction, including without limitation the rights
9
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
- copies of the Software, and to permit persons to whom the Software is
11
- furnished to do so, subject to the following conditions:
12
-
13
- The above copyright notice and this permission notice shall be included in all
14
- copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
- SOFTWARE.
177
+ Licensed under the Apache License, Version 2.0 (the "License");
178
+ you may not use this file except in compliance with the License.
179
+ You may obtain a copy of the License at
180
+
181
+ http://www.apache.org/licenses/LICENSE-2.0
182
+
183
+ Unless required by applicable law or agreed to in writing, software
184
+ distributed under the License is distributed on an "AS IS" BASIS,
185
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
186
+ See the License for the specific language governing permissions and
187
+ limitations under the License.
package/NOTICE CHANGED
@@ -1,13 +1,15 @@
1
1
  # Notice
2
2
 
3
- release-skill is an open-source project released under the MIT License.
3
+ release-skill is an open-source project released under the Apache License,
4
+ Version 2.0.
4
5
 
5
6
  ## Maintenance
6
7
 
7
8
  release-skill is maintained by 广州市风荷科技有限公司 (Guangzhou Fenghe
8
9
  Technology Co., Ltd.), together with the release-skill contributors. Copyright
9
10
  in the project is held by the release-skill contributors and 广州市风荷科技
10
- 有限公司 as stated in the LICENSE file. The transfer of the public GitHub
11
+ 有限公司 as stated in the LICENSE file. The 0.4.1 license change does not by
12
+ itself assign or transfer those historical rights. The transfer of the public GitHub
11
13
  repository to the `ifoohoo` organization is an administrative change of the
12
14
  hosting location and maintenance identity; it does not by itself constitute a
13
15
  copyright assignment or transfer.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [简体中文](README.zh-CN.md) · Installation: [English](INSTALL.md) / [简体中文](INSTALL.zh-CN.md)
4
4
 
5
- <!-- release-skill:release-version: 0.4.0 -->
5
+ <!-- release-skill:release-version: 0.4.1 -->
6
6
  Release preparation for Claude Code, CodeBuddy, WorkBuddy, Codex, and Kimi Code, with human-edited files kept intact.
7
7
 
8
8
  release-skill helps a maintainer answer three questions: what will be released,
@@ -14,26 +14,20 @@ Setup surfaces only the deterministic `compactSummary` review view; the full
14
14
  report stays in a temporary session directory.
15
15
 
16
16
  <!-- release-skill:managed:start id=latest-release -->
17
- **0.4.0** (2026-08-02)
17
+ **0.4.1** (2026-08-03)
18
18
 
19
- v0.4.0 simplifies the production release path to one frozen-plan approval and moves unavailable Kimi or CodeBuddy installations into non-blocking manual follow-up tasks.
20
-
21
- **Added**
22
-
23
- - **Manual consumer follow-ups**: when Kimi or CodeBuddy cannot be installed automatically, the release records an installation task after publishing without requiring system attestation.
19
+ v0.4.1 is the unreleased source candidate that migrates release-skill to Apache License 2.0 and aligns public author metadata while preserving repository ownership and historical attribution.
24
20
 
25
21
  **Changed**
26
22
 
27
- - **Single release approval**: the normal production path now asks only for approval of the immutable release plan; invoking a command authorizes its configured hooks and gates.
28
- - **Non-blocking human consumers**: Kimi and CodeBuddy manual installation tasks no longer prevent the automated release from reaching `VERIFIED`.
29
-
30
- **Removed**
31
-
32
- - **Redundant confirmation flags**: legacy plan-digest repetition, production-confirmation, hook-authorization, and manual-attestation interactions were removed from the normal release path.
23
+ - **Open-source license**: package, plugin manifests, README, LICENSE, and NOTICE now consistently use Apache-2.0.
24
+ - **Identity separation**: project author and developer metadata names 广州市风荷科技有限公司, while the `ifoohoo` repository and Marketplace owner coordinates remain unchanged.
25
+ - **Historical attribution**: existing contributor and company copyright notices remain; metadata and license changes do not assert a copyright transfer.
33
26
  <!-- release-skill:managed:end id=latest-release -->
34
27
 
35
28
  <!-- release-skill:capability:external-write-boundary -->
36
- > **Current boundary:** v0.4.0 is the current release (v0.2.2 previously held
29
+ > **Current boundary:** v0.4.1 is the current source candidate; v0.4.0 remains
30
+ > the latest published release (v0.2.2 previously held
37
31
  > published status before the platform verification convergence fix was added).
38
32
  > v0.1.1 completed a real production release to GitHub and npm — the first
39
33
  > production-verified milestone — followed by
@@ -51,8 +45,8 @@ v0.4.0 simplifies the production release path to one frozen-plan approval and mo
51
45
  > publish global preflight.
52
46
 
53
47
  <!-- release-skill:capability:safe-first-command -->
54
- > **Production path verified since the v0.1.1 milestone; v0.4.0 is the current
55
- > release.** The npm-installed CLI is the supported user entry. Source checkout
48
+ > **Production path verified since the v0.1.1 milestone; v0.4.1 is the current
49
+ > source candidate and v0.4.0 is the latest published release.** The npm-installed CLI is the supported user entry. Source checkout
56
50
  > is the development/contributor fallback.
57
51
  >
58
52
  > **Start here:**
@@ -62,7 +56,13 @@ v0.4.0 simplifies the production release path to one frozen-plan approval and mo
62
56
  <!-- release-skill:maturity:v0.1-boundary -->
63
57
  <!-- release-skill:maturity:boundary -->
64
58
  > **Safe defaults:** the recommended path is `help → assess → prepare --offline →
65
- > human review`. Production publishing uses `ship --target-version <ver> ship --approve --actor <name>`.
59
+ > human review`. `help` and `assess` are read-only. The release-skill-owned part of
60
+ > `prepare --offline` writes only under `.release-skill/` and does not call remote
61
+ > publish adapters, but configured hooks and gates are unsandboxed processes: they
62
+ > may write outside the project, access credentials, make network calls, or publish.
63
+ > Treat `prepare` as local-only only when those processes are absent or separately
64
+ > audited and explicitly acknowledged. Production publishing uses
65
+ > `ship --target-version <ver> → ship --approve --actor <name>`.
66
66
  > The `ship` command runs hooks and gates automatically; the only human gate is plan approval.
67
67
  > Kimi/CodeBuddy installations are non-blocking manual follow-up tasks (not verified by system).
68
68
 
@@ -130,8 +130,11 @@ release-skill ship --root "$PROJECT" --approve --actor "$ACTOR" --json
130
130
  During development, `release-skill hooks validate` runs declared hooks and
131
131
  writes the same content-bound cache receipts that `prepare` consumes.
132
132
 
133
- Run these steps in order. Steps 1-4 are safe (read-only or local-only);
134
- steps 5-9 require explicit human gates.
133
+ Run these steps in order. Steps 1-3 are read-only. In step 4, release-skill's own
134
+ prepare pipeline is local-only and never invokes remote publish adapters; configured
135
+ hooks and gates remain arbitrary unsandboxed processes and can perform local or remote
136
+ side effects. Step 4 is therefore local-only only when those processes are absent or
137
+ separately audited and explicitly acknowledged. Steps 5-9 require explicit human gates.
135
138
 
136
139
  ```bash
137
140
  CLI=(release-skill) # or: CLI=(node "$RELEASE_SKILL_HOME/packages/release-skill/bin/release-skill.mjs")
@@ -210,6 +213,9 @@ ACTOR=your-name
210
213
  --actor "$ACTOR" --json)
211
214
  APPROVAL_PATH=$(printf '%s\n' "$APPROVAL_JSON" | jq -r '.approvalPath')
212
215
  ```
216
+ `--actor` is an unauthenticated local audit string. It is not identity
217
+ verification, a signature, or proof that a particular human approved the plan;
218
+ use an external authenticated approval system when that assurance is required.
213
219
  8. **publish** — remote writes start here:
214
220
  ```bash
215
221
  PUBLISH_JSON=$("${CLI[@]}" publish --root "$PROJECT" \
@@ -276,9 +282,12 @@ but blocks a lost or reverted README by path. It never merges, switches
276
282
  branches, pushes, or creates a PR.
277
283
 
278
284
  **Write safety:** `setup` is read-only by default (create-once after digest
279
- confirmation). `prepare` writes only under `.release-skill/`. `publish` is the
280
- production write entry, requiring both approval and the current plan digest.
281
- Project hooks and gates are acknowledged local processes without an OS sandbox.
285
+ confirmation). Release-skill's own `prepare` pipeline writes only under
286
+ `.release-skill/` and does not invoke remote publish adapters. Project hooks and
287
+ gates are acknowledged processes without an OS sandbox; they may write outside the
288
+ project, access credentials, make network calls, or publish. `publish` is the
289
+ release-skill-owned production write entry and requires both approval and the current
290
+ plan digest.
282
291
 
283
292
  ## Documentation
284
293
 
@@ -519,4 +528,4 @@ declaration is narrowed to concrete targets.
519
528
 
520
529
  ## License
521
530
 
522
- MIT. See [LICENSE](LICENSE).
531
+ Apache License 2.0. See [LICENSE](LICENSE) and [NOTICE](NOTICE).
package/README.zh-CN.md CHANGED
@@ -2,32 +2,25 @@
2
2
 
3
3
  [English](README.md) · 安装指南:[中文](INSTALL.zh-CN.md) / [English](INSTALL.md)
4
4
 
5
- <!-- release-skill:release-version: 0.4.0 -->
5
+ <!-- release-skill:release-version: 0.4.1 -->
6
6
  面向 Claude Code、CodeBuddy、WorkBuddy、Codex 和 Kimi Code 的发布准备工具,完整保留人工维护的文件内容。
7
7
 
8
8
  release-skill 帮助维护者回答三个问题:准备发布什么、还有哪些检查未通过、最终发布的内容是什么。它不重新生成、也不回写项目源文件。`prepare` 把每个配置的公开文件复制到隔离快照并验证字节——先冻结并供人工审阅,再从同一份冻结产物发布。`setup` 只显示确定性的 `compactSummary` 审阅视图,完整报告保留在临时会话目录中。
9
9
 
10
10
  <!-- release-skill:managed:start id=latest-release -->
11
- **0.4.0** (2026-08-02)
11
+ **0.4.1** (2026-08-03)
12
12
 
13
- v0.4.0 将生产发布收敛为一次冻结计划批准,并把无法自动安装的 Kimi CodeBuddy 转为不阻塞发布的人工后续任务。
14
-
15
- **新增**
16
-
17
- - **人工消费者后续任务**:Kimi 或 CodeBuddy 无法自动安装时,发布完成后记录人工安装待办,系统不再要求安装证明。
13
+ v0.4.1 是尚未发布的源码候选:将 release-skill 调整为 Apache License 2.0,并统一公开作者元数据,同时保留仓库 owner 与历史归属。
18
14
 
19
15
  **变更**
20
16
 
21
- - **单次发布批准**:普通生产发布只需批准一次不可变发布计划;调用命令即授权执行其配置的 hook gate
22
- - **人工消费者不阻塞发布**:Kimi CodeBuddy 的人工安装待办不再阻止自动发布达到 `VERIFIED`。
23
-
24
- **移除**
25
-
26
- - **冗余确认参数**:普通发布路径移除了重复输入 plan digest、production confirmation、hook authorization 和人工证明等交互。
17
+ - **开源许可证**:package、插件清单、README、LICENSE NOTICE 统一使用 Apache-2.0
18
+ - **身份分层**:项目作者和开发者字段使用广州市风荷科技有限公司,`ifoohoo` 仓库与 Marketplace owner 坐标保持不变。
19
+ - **历史归属**:继续保留贡献者和公司的既有版权声明;元数据与许可证变化不声明版权转让。
27
20
  <!-- release-skill:managed:end id=latest-release -->
28
21
 
29
22
  <!-- release-skill:capability:external-write-boundary -->
30
- > **当前边界:** v0.4.0 是当前发布版本(v0.2.2 曾处于已发布状态,后因平台验证收敛修复而更新)。
23
+ > **当前边界:** v0.4.1 是当前源码候选,v0.4.0 仍是最新已发布版本(v0.2.2 曾处于已发布状态,后因平台验证收敛修复而更新)。
31
24
  > v0.1.1 已完成 GitHub 与 npm 的
32
25
  > 真实生产发布,是首次生产验证的历史里程碑,并从冻结 Git ref 完成精确 npm
33
26
  > 安装及 Claude/Codex 消费者安装验证;"当前发布版本"与"首次生产验证里程碑"
@@ -40,7 +33,7 @@ v0.4.0 将生产发布收敛为一次冻结计划批准,并把无法自动安
40
33
  > 远端唯一性检查在 `publish` 全局预检执行。
41
34
 
42
35
  <!-- release-skill:capability:safe-first-command -->
43
- > **生产路径自 v0.1.1 里程碑起已完成真实生产验证;v0.4.0 是当前发布版本。**
36
+ > **生产路径自 v0.1.1 里程碑起已完成真实生产验证;v0.4.1 是当前源码候选,v0.4.0 是最新已发布版本。**
44
37
  > npm 安装的 CLI 是受支持的用户入口;源码 checkout 保留为开发/贡献者路径。
45
38
  >
46
39
  > **第一条命令:**
@@ -49,8 +42,12 @@ v0.4.0 将生产发布收敛为一次冻结计划批准,并把无法自动安
49
42
 
50
43
  <!-- release-skill:maturity:v0.1-boundary -->
51
44
  <!-- release-skill:maturity:boundary -->
52
- > **安全默认路径:** 推荐 `help → assess → prepare --offline → 人工审阅`;
53
- > 生产发布使用 `ship --target-version <ver> ship --approve --actor <name>`。
45
+ > **安全默认路径:** 推荐 `help → assess → prepare --offline → 人工审阅`。
46
+ > `help` `assess` 只读;`prepare --offline` 中由 release-skill 自身控制的部分只写
47
+ > `.release-skill/`,且不调用远端发布适配器。但配置的 hook 和 gate 是无操作系统沙箱的进程,
48
+ > 可以写出项目目录、访问凭据、发起网络请求或执行远端发布。只有未配置这些进程,或已单独审计并
49
+ > 显式确认其副作用时,才可以把 `prepare` 视为“仅本地”。生产发布使用
50
+ > `ship --target-version <ver> → ship --approve --actor <name>`。
54
51
  > `ship` 自动执行 hooks 和门禁,唯一的人工门禁是计划批准。
55
52
  > Kimi/CodeBuddy 安装是非阻塞的人工后续任务(系统不核验)。
56
53
 
@@ -117,7 +114,10 @@ release-skill ship --root "$PROJECT" --approve --actor "$ACTOR" --json
117
114
  开发阶段可运行 `release-skill hooks validate`;它会执行声明的 hooks,并写入
118
115
  `prepare` 可复用的内容绑定收据。
119
116
 
120
- 按以下顺序执行。步骤 1-4 是安全默认(只读或仅本地);步骤 5-9 需要显式人工门禁。
117
+ 按以下顺序执行。步骤 1-3 只读。步骤 4 中,release-skill 自身的 prepare 流水线仅在本地写入,
118
+ 且不会调用远端发布适配器;配置的 hook 和 gate 仍是可产生本地或远端副作用的任意无沙箱进程。
119
+ 只有这些进程不存在,或已单独审计并显式确认副作用时,步骤 4 才能视为“仅本地”。步骤 5-9
120
+ 需要显式人工门禁。
121
121
 
122
122
  ```bash
123
123
  CLI=(release-skill) # 或:CLI=(node "$RELEASE_SKILL_HOME/packages/release-skill/bin/release-skill.mjs")
@@ -193,6 +193,8 @@ ACTOR=your-name
193
193
  --actor "$ACTOR" --json)
194
194
  APPROVAL_PATH=$(printf '%s\n' "$APPROVAL_JSON" | jq -r '.approvalPath')
195
195
  ```
196
+ `--actor` 只是未经身份认证的本地审计字符串,不是身份核验、数字签名,也不能证明某位特定人员
197
+ 已批准计划;需要此类保证时,应接入外部已认证的审批系统。
196
198
  8. **publish** — 远端写入开始:
197
199
  ```bash
198
200
  PUBLISH_JSON=$("${CLI[@]}" publish --root "$PROJECT" \
@@ -237,7 +239,10 @@ DISCOVERED -> ASSESSED -> PREPARED -> APPROVED -> PUBLISHING -> PUBLISHED -> VER
237
239
 
238
240
  **保存契约:** release-skill 不重新生成或回写项目源文件。`prepare` 把每个配置的公开文件复制到隔离快照并验证字节。后续 prepare 重新读取当前文件,不会从模板重建。只有 `publicFiles` 列出的文件会被复制。`prepare` 不会刷新或重写人工文档——维护者先更新 README、INSTALL 和 CHANGELOG,再 prepare、审阅和批准。
239
241
 
240
- **写入安全:** `setup` 默认只读(摘要确认后仅首次创建配置)。`prepare` 只写 `.release-skill/`。`publish` 是生产写入入口,需要有效批准;内部计划摘要由系统自动绑定和校验。项目 hook 和 gate 在对应命令调用时获得授权,没有操作系统沙箱。
242
+ **写入安全:** `setup` 默认只读(摘要确认后仅首次创建配置)。release-skill 自身的 `prepare`
243
+ 流水线只写 `.release-skill/`,且不调用远端发布适配器。项目 hook 和 gate 在对应命令调用时获得
244
+ 授权,但没有操作系统沙箱;它们可以写出项目目录、访问凭据、发起网络请求或执行远端发布。
245
+ `publish` 是 release-skill 自身的生产写入入口,需要有效批准;内部计划摘要由系统自动绑定和校验。
241
246
 
242
247
  **Workspace 源码权威:** 生产配置使用 `project.sourceRepository` 指定 workspace
243
248
  源仓库,并用 `project.defaultBranch` 指定其真实远端默认分支。prepare 冻结所有
@@ -463,4 +468,4 @@ hook 在 `prepare` 调用时运行,命令调用本身即授权执行。gate
463
468
 
464
469
  ## 许可证
465
470
 
466
- MIT,见 [LICENSE](LICENSE)。
471
+ Apache License 2.0,见 [LICENSE](LICENSE) 与 [NOTICE](NOTICE)
@@ -9,8 +9,12 @@
9
9
  {
10
10
  "name": "release-skill",
11
11
  "source": "./",
12
- "version": "0.4.0",
13
- "description": "Safe preparation and frozen GitHub/npm production publishing with full happy end verification"
12
+ "version": "0.4.1",
13
+ "description": "Safe preparation and frozen GitHub/npm production publishing with full happy end verification",
14
+ "author": {
15
+ "name": "广州市风荷科技有限公司"
16
+ },
17
+ "license": "Apache-2.0"
14
18
  }
15
19
  ]
16
20
  }
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "release-skill",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Safe preparation and frozen GitHub/npm production publishing with full happy end verification",
5
5
  "author": {
6
6
  "name": "广州市风荷科技有限公司"
7
7
  },
8
- "license": "MIT",
8
+ "license": "Apache-2.0",
9
9
  "skills": "./skills/"
10
10
  }
@@ -9,7 +9,7 @@ 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.4.0"});
12
+ const __bundlePkg = Object.freeze({"name":"release-skill","version":"0.4.1"});
13
13
 
14
14
  var __create = Object.create;
15
15
  var __defProp = Object.defineProperty;
@@ -71134,6 +71134,7 @@ function extractManifestFromTarball(tarballBuffer, expected) {
71134
71134
  }
71135
71135
  let offset = 0;
71136
71136
  let manifest = null;
71137
+ let readme = null;
71137
71138
  while (offset + 512 <= data.length) {
71138
71139
  let allZero = true;
71139
71140
  for (let i = 0; i < 512; i++) {
@@ -71183,6 +71184,20 @@ function extractManifestFromTarball(tarballBuffer, expected) {
71183
71184
  throw new Error(`tarball manifest version "${manifest.version}" does not match expected "${expected.version}"`);
71184
71185
  }
71185
71186
  }
71187
+ if (entryName.toLowerCase() === "package/readme.md") {
71188
+ if (readme) throw new Error("tarball contains conflicting package README entries");
71189
+ if (!(typeFlag === 0 || typeFlag === 48)) {
71190
+ throw new Error("tarball package README must be a regular file");
71191
+ }
71192
+ const bodyStart = offset + 512;
71193
+ const bodyEnd = bodyStart + size;
71194
+ if (bodyEnd > data.length) throw new Error("frozen npm tarball entry exceeds archive bounds");
71195
+ if (size > 10 * 1024 * 1024) throw new Error("tarball package README is unreasonably large");
71196
+ readme = {
71197
+ content: data.subarray(bodyStart, bodyEnd).toString("utf8"),
71198
+ filename: entryName.slice("package/".length)
71199
+ };
71200
+ }
71186
71201
  const nextOffset = offset + 512 + Math.ceil(size / 512) * 512;
71187
71202
  if (!Number.isSafeInteger(nextOffset) || nextOffset > data.length) {
71188
71203
  throw new Error("frozen npm tarball entry exceeds archive bounds");
@@ -71190,6 +71205,10 @@ function extractManifestFromTarball(tarballBuffer, expected) {
71190
71205
  offset = nextOffset;
71191
71206
  }
71192
71207
  if (!manifest) throw new Error("tarball does not contain package/package.json");
71208
+ if (readme) {
71209
+ manifest.readme = readme.content;
71210
+ manifest.readmeFilename = readme.filename;
71211
+ }
71193
71212
  return manifest;
71194
71213
  }
71195
71214
  async function verifyFrozenNpmTarballIdentity(action, root) {
@@ -91,6 +91,12 @@ node -e 'const fs=require("node:fs");const [c,p,a]=process.argv.slice(1).map(x=>
91
91
  node -e 'require("node:fs").rmSync(process.argv[1],{recursive:true,force:false})' "$SETUP_SESSION"
92
92
  ```
93
93
 
94
+ ## publisher 字段与个人片段泄漏策略
95
+
96
+ setup 生成的 `.release-skill/project.yaml` 中 `publisher` 是**已批准的公开发布身份**:它是人工确认的对外发布署名,属于公开面的一部分,而非需要隐藏的私有信息。
97
+
98
+ 若目标仓库用“个人片段”类泄漏策略扫描仓树,而该策略按片段匹配恰好覆盖到 `publisher` 字段,正确做法不是删除或改写策略,而是由贡献者在**本地、gitignore 的个人片段 overlay** 中为该规则声明位置豁免(如 `approvedPlacements`:限定 `publisher` 所在的确切文件路径与行键前缀)。豁免只放行已批准的放置位置,其余出现照常命中;overlay 不入库,个人片段本身不进 committed 策略。
99
+
94
100
  ## 故障路由
95
101
 
96
102
  - `NEEDS_INPUT`:审阅紧凑摘要;有冲突先修正权威事实并重跑。
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "release-skill",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Safe preparation and frozen GitHub/npm production publishing with full happy end verification",
5
5
  "author": {
6
6
  "name": "广州市风荷科技有限公司"
7
7
  },
8
- "license": "MIT",
8
+ "license": "Apache-2.0",
9
9
  "skills": "./skills/",
10
10
  "interface": {
11
11
  "displayName": "Release Skill",
12
12
  "shortDescription": "Safe preparation and frozen GitHub/npm publishing",
13
13
  "longDescription": "Prepares byte-faithful public snapshots without rewriting project source files and publishes approved frozen GitHub/npm artifacts. Full happy end verification confirms consumer installation from frozen Git ref.",
14
- "developerName": "ifoohoo",
14
+ "developerName": "广州市风荷科技有限公司",
15
15
  "category": "DevOps",
16
16
  "capabilities": [
17
17
  "Write",
@@ -19,7 +19,7 @@
19
19
  ],
20
20
  "defaultPrompt": [
21
21
  "Assess this project for release readiness.",
22
- "Prepare a release plan for version 0.4.0.",
22
+ "Prepare a release plan for version 0.4.1.",
23
23
  "Help me understand the release workflow."
24
24
  ]
25
25
  }
@@ -9,7 +9,7 @@ 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.4.0"});
12
+ const __bundlePkg = Object.freeze({"name":"release-skill","version":"0.4.1"});
13
13
 
14
14
  var __create = Object.create;
15
15
  var __defProp = Object.defineProperty;
@@ -71134,6 +71134,7 @@ function extractManifestFromTarball(tarballBuffer, expected) {
71134
71134
  }
71135
71135
  let offset = 0;
71136
71136
  let manifest = null;
71137
+ let readme = null;
71137
71138
  while (offset + 512 <= data.length) {
71138
71139
  let allZero = true;
71139
71140
  for (let i = 0; i < 512; i++) {
@@ -71183,6 +71184,20 @@ function extractManifestFromTarball(tarballBuffer, expected) {
71183
71184
  throw new Error(`tarball manifest version "${manifest.version}" does not match expected "${expected.version}"`);
71184
71185
  }
71185
71186
  }
71187
+ if (entryName.toLowerCase() === "package/readme.md") {
71188
+ if (readme) throw new Error("tarball contains conflicting package README entries");
71189
+ if (!(typeFlag === 0 || typeFlag === 48)) {
71190
+ throw new Error("tarball package README must be a regular file");
71191
+ }
71192
+ const bodyStart = offset + 512;
71193
+ const bodyEnd = bodyStart + size;
71194
+ if (bodyEnd > data.length) throw new Error("frozen npm tarball entry exceeds archive bounds");
71195
+ if (size > 10 * 1024 * 1024) throw new Error("tarball package README is unreasonably large");
71196
+ readme = {
71197
+ content: data.subarray(bodyStart, bodyEnd).toString("utf8"),
71198
+ filename: entryName.slice("package/".length)
71199
+ };
71200
+ }
71186
71201
  const nextOffset = offset + 512 + Math.ceil(size / 512) * 512;
71187
71202
  if (!Number.isSafeInteger(nextOffset) || nextOffset > data.length) {
71188
71203
  throw new Error("frozen npm tarball entry exceeds archive bounds");
@@ -71190,6 +71205,10 @@ function extractManifestFromTarball(tarballBuffer, expected) {
71190
71205
  offset = nextOffset;
71191
71206
  }
71192
71207
  if (!manifest) throw new Error("tarball does not contain package/package.json");
71208
+ if (readme) {
71209
+ manifest.readme = readme.content;
71210
+ manifest.readmeFilename = readme.filename;
71211
+ }
71193
71212
  return manifest;
71194
71213
  }
71195
71214
  async function verifyFrozenNpmTarballIdentity(action, root) {
@@ -98,6 +98,12 @@ node -e 'const fs=require("node:fs");const [c,p,a]=process.argv.slice(1).map(x=>
98
98
  node -e 'require("node:fs").rmSync(process.argv[1],{recursive:true,force:false})' "$SETUP_SESSION"
99
99
  ```
100
100
 
101
+ ## publisher 字段与个人片段泄漏策略
102
+
103
+ setup 生成的 `.release-skill/project.yaml` 中 `publisher` 是**已批准的公开发布身份**:它是人工确认的对外发布署名,属于公开面的一部分,而非需要隐藏的私有信息。
104
+
105
+ 若目标仓库用“个人片段”类泄漏策略扫描仓树,而该策略按片段匹配恰好覆盖到 `publisher` 字段,正确做法不是删除或改写策略,而是由贡献者在**本地、gitignore 的个人片段 overlay** 中为该规则声明位置豁免(如 `approvedPlacements`:限定 `publisher` 所在的确切文件路径与行键前缀)。豁免只放行已批准的放置位置,其余出现照常命中;overlay 不入库,个人片段本身不进 committed 策略。
106
+
101
107
  ## 故障路由
102
108
 
103
109
  - `NEEDS_INPUT`:审阅紧凑摘要;有冲突先修正权威事实并重跑。
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "release-skill",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Safe preparation and frozen GitHub/npm production publishing with full happy end verification",
5
5
  "author": {
6
6
  "name": "广州市风荷科技有限公司"
7
7
  },
8
- "license": "MIT",
8
+ "license": "Apache-2.0",
9
9
  "homepage": "https://github.com/ifoohoo/release-skill#readme",
10
10
  "keywords": [
11
11
  "release",
@@ -21,7 +21,7 @@
21
21
  "displayName": "Release Skill",
22
22
  "shortDescription": "Safe preparation and frozen GitHub/npm publishing",
23
23
  "longDescription": "Prepares byte-faithful public snapshots without rewriting project source files and publishes approved frozen GitHub/npm artifacts. Full happy end verification confirms consumer installation from frozen Git ref.",
24
- "developerName": "ifoohoo",
24
+ "developerName": "广州市风荷科技有限公司",
25
25
  "websiteURL": "https://github.com/ifoohoo/release-skill"
26
26
  }
27
27
  }
@@ -9,7 +9,7 @@ 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.4.0"});
12
+ const __bundlePkg = Object.freeze({"name":"release-skill","version":"0.4.1"});
13
13
 
14
14
  var __create = Object.create;
15
15
  var __defProp = Object.defineProperty;
@@ -71134,6 +71134,7 @@ function extractManifestFromTarball(tarballBuffer, expected) {
71134
71134
  }
71135
71135
  let offset = 0;
71136
71136
  let manifest = null;
71137
+ let readme = null;
71137
71138
  while (offset + 512 <= data.length) {
71138
71139
  let allZero = true;
71139
71140
  for (let i = 0; i < 512; i++) {
@@ -71183,6 +71184,20 @@ function extractManifestFromTarball(tarballBuffer, expected) {
71183
71184
  throw new Error(`tarball manifest version "${manifest.version}" does not match expected "${expected.version}"`);
71184
71185
  }
71185
71186
  }
71187
+ if (entryName.toLowerCase() === "package/readme.md") {
71188
+ if (readme) throw new Error("tarball contains conflicting package README entries");
71189
+ if (!(typeFlag === 0 || typeFlag === 48)) {
71190
+ throw new Error("tarball package README must be a regular file");
71191
+ }
71192
+ const bodyStart = offset + 512;
71193
+ const bodyEnd = bodyStart + size;
71194
+ if (bodyEnd > data.length) throw new Error("frozen npm tarball entry exceeds archive bounds");
71195
+ if (size > 10 * 1024 * 1024) throw new Error("tarball package README is unreasonably large");
71196
+ readme = {
71197
+ content: data.subarray(bodyStart, bodyEnd).toString("utf8"),
71198
+ filename: entryName.slice("package/".length)
71199
+ };
71200
+ }
71186
71201
  const nextOffset = offset + 512 + Math.ceil(size / 512) * 512;
71187
71202
  if (!Number.isSafeInteger(nextOffset) || nextOffset > data.length) {
71188
71203
  throw new Error("frozen npm tarball entry exceeds archive bounds");
@@ -71190,6 +71205,10 @@ function extractManifestFromTarball(tarballBuffer, expected) {
71190
71205
  offset = nextOffset;
71191
71206
  }
71192
71207
  if (!manifest) throw new Error("tarball does not contain package/package.json");
71208
+ if (readme) {
71209
+ manifest.readme = readme.content;
71210
+ manifest.readmeFilename = readme.filename;
71211
+ }
71193
71212
  return manifest;
71194
71213
  }
71195
71214
  async function verifyFrozenNpmTarballIdentity(action, root) {
@@ -98,6 +98,12 @@ node -e 'const fs=require("node:fs");const [c,p,a]=process.argv.slice(1).map(x=>
98
98
  node -e 'require("node:fs").rmSync(process.argv[1],{recursive:true,force:false})' "$SETUP_SESSION"
99
99
  ```
100
100
 
101
+ ## publisher 字段与个人片段泄漏策略
102
+
103
+ setup 生成的 `.release-skill/project.yaml` 中 `publisher` 是**已批准的公开发布身份**:它是人工确认的对外发布署名,属于公开面的一部分,而非需要隐藏的私有信息。
104
+
105
+ 若目标仓库用“个人片段”类泄漏策略扫描仓树,而该策略按片段匹配恰好覆盖到 `publisher` 字段,正确做法不是删除或改写策略,而是由贡献者在**本地、gitignore 的个人片段 overlay** 中为该规则声明位置豁免(如 `approvedPlacements`:限定 `publisher` 所在的确切文件路径与行键前缀)。豁免只放行已批准的放置位置,其余出现照常命中;overlay 不入库,个人片段本身不进 committed 策略。
106
+
101
107
  ## 故障路由
102
108
 
103
109
  - `NEEDS_INPUT`:审阅紧凑摘要;有冲突先修正权威事实并重跑。
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "release-skill",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Safe preparation and frozen GitHub/npm production publishing with full happy end verification",
5
5
  "author": {
6
6
  "name": "广州市风荷科技有限公司"
7
7
  },
8
- "license": "MIT",
8
+ "license": "Apache-2.0",
9
9
  "skills": [
10
10
  "./skills/"
11
11
  ]
@@ -9,7 +9,7 @@ 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.4.0"});
12
+ const __bundlePkg = Object.freeze({"name":"release-skill","version":"0.4.1"});
13
13
 
14
14
  var __create = Object.create;
15
15
  var __defProp = Object.defineProperty;
@@ -71134,6 +71134,7 @@ function extractManifestFromTarball(tarballBuffer, expected) {
71134
71134
  }
71135
71135
  let offset = 0;
71136
71136
  let manifest = null;
71137
+ let readme = null;
71137
71138
  while (offset + 512 <= data.length) {
71138
71139
  let allZero = true;
71139
71140
  for (let i = 0; i < 512; i++) {
@@ -71183,6 +71184,20 @@ function extractManifestFromTarball(tarballBuffer, expected) {
71183
71184
  throw new Error(`tarball manifest version "${manifest.version}" does not match expected "${expected.version}"`);
71184
71185
  }
71185
71186
  }
71187
+ if (entryName.toLowerCase() === "package/readme.md") {
71188
+ if (readme) throw new Error("tarball contains conflicting package README entries");
71189
+ if (!(typeFlag === 0 || typeFlag === 48)) {
71190
+ throw new Error("tarball package README must be a regular file");
71191
+ }
71192
+ const bodyStart = offset + 512;
71193
+ const bodyEnd = bodyStart + size;
71194
+ if (bodyEnd > data.length) throw new Error("frozen npm tarball entry exceeds archive bounds");
71195
+ if (size > 10 * 1024 * 1024) throw new Error("tarball package README is unreasonably large");
71196
+ readme = {
71197
+ content: data.subarray(bodyStart, bodyEnd).toString("utf8"),
71198
+ filename: entryName.slice("package/".length)
71199
+ };
71200
+ }
71186
71201
  const nextOffset = offset + 512 + Math.ceil(size / 512) * 512;
71187
71202
  if (!Number.isSafeInteger(nextOffset) || nextOffset > data.length) {
71188
71203
  throw new Error("frozen npm tarball entry exceeds archive bounds");
@@ -71190,6 +71205,10 @@ function extractManifestFromTarball(tarballBuffer, expected) {
71190
71205
  offset = nextOffset;
71191
71206
  }
71192
71207
  if (!manifest) throw new Error("tarball does not contain package/package.json");
71208
+ if (readme) {
71209
+ manifest.readme = readme.content;
71210
+ manifest.readmeFilename = readme.filename;
71211
+ }
71193
71212
  return manifest;
71194
71213
  }
71195
71214
  async function verifyFrozenNpmTarballIdentity(action, root) {
@@ -91,6 +91,12 @@ node -e 'const fs=require("node:fs");const [c,p,a]=process.argv.slice(1).map(x=>
91
91
  node -e 'require("node:fs").rmSync(process.argv[1],{recursive:true,force:false})' "$SETUP_SESSION"
92
92
  ```
93
93
 
94
+ ## publisher 字段与个人片段泄漏策略
95
+
96
+ setup 生成的 `.release-skill/project.yaml` 中 `publisher` 是**已批准的公开发布身份**:它是人工确认的对外发布署名,属于公开面的一部分,而非需要隐藏的私有信息。
97
+
98
+ 若目标仓库用“个人片段”类泄漏策略扫描仓树,而该策略按片段匹配恰好覆盖到 `publisher` 字段,正确做法不是删除或改写策略,而是由贡献者在**本地、gitignore 的个人片段 overlay** 中为该规则声明位置豁免(如 `approvedPlacements`:限定 `publisher` 所在的确切文件路径与行键前缀)。豁免只放行已批准的放置位置,其余出现照常命中;overlay 不入库,个人片段本身不进 committed 策略。
99
+
94
100
  ## 故障路由
95
101
 
96
102
  - `NEEDS_INPUT`:审阅紧凑摘要;有冲突先修正权威事实并重跑。
@@ -9,7 +9,7 @@ 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.4.0"});
12
+ const __bundlePkg = Object.freeze({"name":"release-skill","version":"0.4.1"});
13
13
 
14
14
  var __create = Object.create;
15
15
  var __defProp = Object.defineProperty;
@@ -71134,6 +71134,7 @@ function extractManifestFromTarball(tarballBuffer, expected) {
71134
71134
  }
71135
71135
  let offset = 0;
71136
71136
  let manifest = null;
71137
+ let readme = null;
71137
71138
  while (offset + 512 <= data.length) {
71138
71139
  let allZero = true;
71139
71140
  for (let i = 0; i < 512; i++) {
@@ -71183,6 +71184,20 @@ function extractManifestFromTarball(tarballBuffer, expected) {
71183
71184
  throw new Error(`tarball manifest version "${manifest.version}" does not match expected "${expected.version}"`);
71184
71185
  }
71185
71186
  }
71187
+ if (entryName.toLowerCase() === "package/readme.md") {
71188
+ if (readme) throw new Error("tarball contains conflicting package README entries");
71189
+ if (!(typeFlag === 0 || typeFlag === 48)) {
71190
+ throw new Error("tarball package README must be a regular file");
71191
+ }
71192
+ const bodyStart = offset + 512;
71193
+ const bodyEnd = bodyStart + size;
71194
+ if (bodyEnd > data.length) throw new Error("frozen npm tarball entry exceeds archive bounds");
71195
+ if (size > 10 * 1024 * 1024) throw new Error("tarball package README is unreasonably large");
71196
+ readme = {
71197
+ content: data.subarray(bodyStart, bodyEnd).toString("utf8"),
71198
+ filename: entryName.slice("package/".length)
71199
+ };
71200
+ }
71186
71201
  const nextOffset = offset + 512 + Math.ceil(size / 512) * 512;
71187
71202
  if (!Number.isSafeInteger(nextOffset) || nextOffset > data.length) {
71188
71203
  throw new Error("frozen npm tarball entry exceeds archive bounds");
@@ -71190,6 +71205,10 @@ function extractManifestFromTarball(tarballBuffer, expected) {
71190
71205
  offset = nextOffset;
71191
71206
  }
71192
71207
  if (!manifest) throw new Error("tarball does not contain package/package.json");
71208
+ if (readme) {
71209
+ manifest.readme = readme.content;
71210
+ manifest.readmeFilename = readme.filename;
71211
+ }
71193
71212
  return manifest;
71194
71213
  }
71195
71214
  async function verifyFrozenNpmTarballIdentity(action, root) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "release-skill",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Safe preparation and frozen GitHub/npm production publishing with full happy end verification",
5
5
  "author": {
6
6
  "name": "广州市风荷科技有限公司"
@@ -94,5 +94,5 @@
94
94
  "codex",
95
95
  "kimi-code"
96
96
  ],
97
- "license": "MIT"
97
+ "license": "Apache-2.0"
98
98
  }
@@ -91,6 +91,12 @@ node -e 'const fs=require("node:fs");const [c,p,a]=process.argv.slice(1).map(x=>
91
91
  node -e 'require("node:fs").rmSync(process.argv[1],{recursive:true,force:false})' "$SETUP_SESSION"
92
92
  ```
93
93
 
94
+ ## publisher 字段与个人片段泄漏策略
95
+
96
+ setup 生成的 `.release-skill/project.yaml` 中 `publisher` 是**已批准的公开发布身份**:它是人工确认的对外发布署名,属于公开面的一部分,而非需要隐藏的私有信息。
97
+
98
+ 若目标仓库用“个人片段”类泄漏策略扫描仓树,而该策略按片段匹配恰好覆盖到 `publisher` 字段,正确做法不是删除或改写策略,而是由贡献者在**本地、gitignore 的个人片段 overlay** 中为该规则声明位置豁免(如 `approvedPlacements`:限定 `publisher` 所在的确切文件路径与行键前缀)。豁免只放行已批准的放置位置,其余出现照常命中;overlay 不入库,个人片段本身不进 committed 策略。
99
+
94
100
  ## 故障路由
95
101
 
96
102
  - `NEEDS_INPUT`:审阅紧凑摘要;有冲突先修正权威事实并重跑。
@@ -91,6 +91,12 @@ node -e 'const fs=require("node:fs");const [c,p,a]=process.argv.slice(1).map(x=>
91
91
  node -e 'require("node:fs").rmSync(process.argv[1],{recursive:true,force:false})' "$SETUP_SESSION"
92
92
  ```
93
93
 
94
+ ## publisher 字段与个人片段泄漏策略
95
+
96
+ setup 生成的 `.release-skill/project.yaml` 中 `publisher` 是**已批准的公开发布身份**:它是人工确认的对外发布署名,属于公开面的一部分,而非需要隐藏的私有信息。
97
+
98
+ 若目标仓库用“个人片段”类泄漏策略扫描仓树,而该策略按片段匹配恰好覆盖到 `publisher` 字段,正确做法不是删除或改写策略,而是由贡献者在**本地、gitignore 的个人片段 overlay** 中为该规则声明位置豁免(如 `approvedPlacements`:限定 `publisher` 所在的确切文件路径与行键前缀)。豁免只放行已批准的放置位置,其余出现照常命中;overlay 不入库,个人片段本身不进 committed 策略。
99
+
94
100
  ## 故障路由
95
101
 
96
102
  - `NEEDS_INPUT`:审阅紧凑摘要;有冲突先修正权威事实并重跑。
@@ -257,6 +257,13 @@ async function readVerifiedTarballBytes(action, root) {
257
257
  * is missing, not valid JSON, or the extracted name/version do not match the
258
258
  * expected action values.
259
259
  *
260
+ * When the tarball contains a package/README.md entry (matched
261
+ * case-insensitively), its content is injected as manifest.readme and its
262
+ * real entry file name as manifest.readmeFilename, so the registry listing
263
+ * displays the package README. Ambiguous or unsafe README entries
264
+ * (duplicate or case-colliding names, non-regular files, out-of-bounds or
265
+ * oversized bodies) fail closed instead of being treated as metadata.
266
+ *
260
267
  * @param {Buffer} tarballBuffer - the raw tarball bytes (gzipped or plain tar)
261
268
  * @param {{ name: string, version: string }} expected - must match manifest
262
269
  * @returns {object} the parsed package.json manifest
@@ -275,6 +282,7 @@ function extractManifestFromTarball(tarballBuffer, expected) {
275
282
  // Walk tar entries to find package/package.json
276
283
  let offset = 0;
277
284
  let manifest = null;
285
+ let readme = null;
278
286
  while (offset + 512 <= data.length) {
279
287
  // Check for all-zero end-of-archive block
280
288
  let allZero = true;
@@ -330,6 +338,25 @@ function extractManifestFromTarball(tarballBuffer, expected) {
330
338
  }
331
339
  }
332
340
 
341
+ // Match package/README.md case-insensitively (npm registry metadata).
342
+ // The real entry file name is preserved in readmeFilename, and any
343
+ // duplicate, case-colliding, or non-regular README entry fails closed,
344
+ // mirroring the package/package.json checks above.
345
+ if (entryName.toLowerCase() === 'package/readme.md') {
346
+ if (readme) throw new Error('tarball contains conflicting package README entries');
347
+ if (!(typeFlag === 0 || typeFlag === 48 /* '0' */)) {
348
+ throw new Error('tarball package README must be a regular file');
349
+ }
350
+ const bodyStart = offset + 512;
351
+ const bodyEnd = bodyStart + size;
352
+ if (bodyEnd > data.length) throw new Error('frozen npm tarball entry exceeds archive bounds');
353
+ if (size > 10 * 1024 * 1024) throw new Error('tarball package README is unreasonably large');
354
+ readme = {
355
+ content: data.subarray(bodyStart, bodyEnd).toString('utf8'),
356
+ filename: entryName.slice('package/'.length),
357
+ };
358
+ }
359
+
333
360
  // Advance to next header: 512 header + ceil(size/512)*512 data
334
361
  const nextOffset = offset + 512 + Math.ceil(size / 512) * 512;
335
362
  if (!Number.isSafeInteger(nextOffset) || nextOffset > data.length) {
@@ -338,6 +365,10 @@ function extractManifestFromTarball(tarballBuffer, expected) {
338
365
  offset = nextOffset;
339
366
  }
340
367
  if (!manifest) throw new Error('tarball does not contain package/package.json');
368
+ if (readme) {
369
+ manifest.readme = readme.content;
370
+ manifest.readmeFilename = readme.filename;
371
+ }
341
372
  return manifest;
342
373
  }
343
374