release-skill 0.6.1 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codebuddy-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +2 -2
- package/.kimi-plugin/plugin.json +1 -1
- package/CHANGELOG.md +40 -0
- package/CONTRIBUTING.md +1 -1
- package/INSTALL.md +47 -2
- package/INSTALL.zh-CN.md +29 -2
- package/README.md +129 -9
- package/README.zh-CN.md +111 -9
- package/adapters/claude/.claude-plugin/marketplace.json +1 -1
- package/adapters/claude/.claude-plugin/plugin.json +1 -1
- package/adapters/claude/bin/release-skill.bundle.mjs +6596 -1586
- package/adapters/claude/schemas/.render-manifest.json +10 -6
- package/adapters/claude/schemas/postpublish-approval-record.schema.json +47 -0
- package/adapters/claude/schemas/release-plan.schema.json +74 -1
- package/adapters/claude/schemas/release-project.schema.json +73 -1
- package/adapters/claude/schemas/release-run.schema.json +11 -6
- package/adapters/codex/.codex-plugin/plugin.json +2 -2
- package/adapters/codex/bin/release-skill.bundle.mjs +6596 -1586
- package/adapters/codex/schemas/.render-manifest.json +10 -6
- package/adapters/codex/schemas/postpublish-approval-record.schema.json +47 -0
- package/adapters/codex/schemas/release-plan.schema.json +74 -1
- package/adapters/codex/schemas/release-project.schema.json +73 -1
- package/adapters/codex/schemas/release-run.schema.json +11 -6
- package/adapters/kimi/.kimi-plugin/plugin.json +1 -1
- package/adapters/kimi/bin/release-skill.bundle.mjs +6596 -1586
- package/adapters/kimi/schemas/.render-manifest.json +10 -6
- package/adapters/kimi/schemas/postpublish-approval-record.schema.json +47 -0
- package/adapters/kimi/schemas/release-plan.schema.json +74 -1
- package/adapters/kimi/schemas/release-project.schema.json +73 -1
- package/adapters/kimi/schemas/release-run.schema.json +11 -6
- package/adapters/workbuddy/.codebuddy-plugin/plugin.json +1 -1
- package/adapters/workbuddy/bin/release-skill.bundle.mjs +6596 -1586
- package/adapters/workbuddy/schemas/.render-manifest.json +10 -6
- package/adapters/workbuddy/schemas/postpublish-approval-record.schema.json +47 -0
- package/adapters/workbuddy/schemas/release-plan.schema.json +74 -1
- package/adapters/workbuddy/schemas/release-project.schema.json +73 -1
- package/adapters/workbuddy/schemas/release-run.schema.json +11 -6
- package/bin/release-skill-cli.mjs +181 -3
- package/bin/release-skill.bundle.mjs +6596 -1586
- package/package.json +2 -1
- package/platform-manifest.json +4 -4
- package/references/.render-manifest.json +5 -5
- package/references/01-state-machine.md +22 -2
- package/schemas/.render-manifest.json +10 -6
- package/schemas/postpublish-approval-record.schema.json +47 -0
- package/schemas/release-plan.schema.json +74 -1
- package/schemas/release-project.schema.json +73 -1
- package/schemas/release-run.schema.json +11 -6
- package/src/commands/approve.mjs +167 -1
- package/src/commands/distribute.mjs +411 -33
- package/src/commands/lineage.mjs +101 -32
- package/src/commands/postverify.mjs +734 -0
- package/src/commands/prepare.mjs +339 -43
- package/src/commands/publish.mjs +10 -1
- package/src/commands/setup.mjs +715 -0
- package/src/commands/ship.mjs +152 -5
- package/src/commands/verify.mjs +92 -15
- package/src/core/approval.mjs +93 -68
- package/src/core/bounded-output.mjs +46 -0
- package/src/core/derived-artifact-gates.mjs +258 -0
- package/src/core/docs-refresh-preset.mjs +167 -0
- package/src/core/errors.mjs +4 -0
- package/src/core/hooks.mjs +28 -0
- package/src/core/marketplace-registry-entry.mjs +174 -0
- package/src/core/notify-handoff.mjs +76 -0
- package/src/core/postpublish-approval.mjs +110 -0
- package/src/core/postpublish.mjs +424 -7
- package/src/core/preset-executor.mjs +156 -0
- package/src/core/preset-gitwrite.mjs +463 -0
- package/src/core/presets.mjs +706 -0
- package/src/core/proposal-inbox.mjs +630 -0
- package/src/core/run.mjs +91 -6
- package/src/core/skill-resource-closure.mjs +240 -10
- package/src/platforms/registry.mjs +12 -0
package/src/commands/lineage.mjs
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* @module commands/lineage
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
|
-
import { execFile as execFileCb } from 'node:child_process';
|
|
30
|
+
import { execFile as execFileCb, spawn } from 'node:child_process';
|
|
31
31
|
import { promisify } from 'node:util';
|
|
32
32
|
|
|
33
33
|
const execFile = promisify(execFileCb);
|
|
@@ -132,6 +132,50 @@ async function git(root, args, options = {}) {
|
|
|
132
132
|
return stdout.trim();
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
+
/**
|
|
136
|
+
* Run a git command and return RAW stdout (no trimming). Trailing bytes are
|
|
137
|
+
* significant for `cat-file commit` output: the message tail (trailing
|
|
138
|
+
* whitespace / blank lines) must survive untouched.
|
|
139
|
+
*/
|
|
140
|
+
async function gitRaw(root, args, options = {}) {
|
|
141
|
+
const { stdout } = await execFile('git', args, {
|
|
142
|
+
cwd: root,
|
|
143
|
+
encoding: 'utf8',
|
|
144
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
145
|
+
...options,
|
|
146
|
+
});
|
|
147
|
+
return stdout;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Parse a commit ident line — the value of an `author`/`committer` header in
|
|
152
|
+
* the raw commit object — of the canonical form `Name <email> ts tz`.
|
|
153
|
+
*
|
|
154
|
+
* The anchored regex uses a greedy `.*` so the LAST ` <email> ts tz` group at
|
|
155
|
+
* the end of the line wins: names containing spaces (or even '<'/'>' — legal
|
|
156
|
+
* in stored objects) still resolve correctly. This replaces the broken
|
|
157
|
+
* `split(/\s+>/)` parsing, which never matched a legal ident line (there is
|
|
158
|
+
* no whitespace before '>') and silently produced name=<entire line>,
|
|
159
|
+
* email='' — corrupting identities written by `commit-tree` during rebuild
|
|
160
|
+
* (SFA field report 2026-08-18).
|
|
161
|
+
*
|
|
162
|
+
* @param {string} line - e.g. `乌龙 <2505468+mzdbxqh@users.noreply.github.com> 1785824474 +0000`
|
|
163
|
+
* @returns {{ name: string, email: string, ts: string, tz: string }}
|
|
164
|
+
* @throws {Error} when the line is not a valid ident line. Fail-closed:
|
|
165
|
+
* rebuild must never fabricate or silently write an empty identity.
|
|
166
|
+
*/
|
|
167
|
+
export function parseCommitIdent(line) {
|
|
168
|
+
if (typeof line !== 'string') {
|
|
169
|
+
throw new Error(`commit ident line must be a string, got ${typeof line}`);
|
|
170
|
+
}
|
|
171
|
+
const match = line.match(/^(.*) <([^>]*)> (\d+) ([+-]\d{4})$/);
|
|
172
|
+
if (!match) {
|
|
173
|
+
throw new Error(`unparseable commit ident line (fail-closed): "${line}"`);
|
|
174
|
+
}
|
|
175
|
+
const [, name, email, ts, tz] = match;
|
|
176
|
+
return { name, email, ts, tz };
|
|
177
|
+
}
|
|
178
|
+
|
|
135
179
|
/**
|
|
136
180
|
* List all tags with their commit SHAs.
|
|
137
181
|
*
|
|
@@ -201,45 +245,44 @@ export async function objectExists(root, sha) {
|
|
|
201
245
|
/**
|
|
202
246
|
* Read the raw commit object and extract identity/date fields.
|
|
203
247
|
*
|
|
248
|
+
* The cat-file output is NOT trimmed: the header block and the message are
|
|
249
|
+
* split at the first blank line and the message tail bytes (trailing
|
|
250
|
+
* whitespace / blank lines) are preserved exactly, so a rebuilt commit can
|
|
251
|
+
* carry byte-identical message bytes.
|
|
252
|
+
*
|
|
204
253
|
* @param {string} root - repository root
|
|
205
254
|
* @param {string} commitSha - commit SHA
|
|
206
255
|
* @returns {Promise<{ message: string, author: { name: string, email: string, date: string }, committer: { name: string, email: string, date: string } }>}
|
|
256
|
+
* @throws {Error} when the object is malformed or an ident line does not
|
|
257
|
+
* parse (fail-closed — never fabricate or silently write empty identity).
|
|
207
258
|
*/
|
|
208
259
|
export async function readCommitMeta(root, commitSha) {
|
|
209
|
-
const raw = await
|
|
210
|
-
const
|
|
260
|
+
const raw = await gitRaw(root, ['cat-file', 'commit', commitSha]);
|
|
261
|
+
const separator = raw.indexOf('\n\n');
|
|
262
|
+
if (separator === -1) {
|
|
263
|
+
throw new Error(`malformed commit object ${commitSha}: missing header/message separator`);
|
|
264
|
+
}
|
|
265
|
+
const message = raw.slice(separator + 2);
|
|
211
266
|
const headers = {};
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
if (
|
|
216
|
-
|
|
217
|
-
continue;
|
|
218
|
-
}
|
|
219
|
-
if (line === '') {
|
|
220
|
-
inBody = true;
|
|
221
|
-
continue;
|
|
222
|
-
}
|
|
223
|
-
const colon = line.indexOf(' ');
|
|
224
|
-
if (colon === -1) continue;
|
|
225
|
-
const key = line.slice(0, colon);
|
|
226
|
-
const value = line.slice(colon + 1);
|
|
227
|
-
headers[key] = value;
|
|
267
|
+
for (const line of raw.slice(0, separator).split('\n')) {
|
|
268
|
+
if (line.startsWith(' ')) continue; // continuation line (e.g. gpgsig)
|
|
269
|
+
const space = line.indexOf(' ');
|
|
270
|
+
if (space === -1) continue;
|
|
271
|
+
headers[line.slice(0, space)] = line.slice(space + 1);
|
|
228
272
|
}
|
|
229
|
-
const
|
|
230
|
-
const
|
|
231
|
-
const committerParts = (headers.committer ?? '').split(/\s+>/);
|
|
273
|
+
const author = parseCommitIdent(headers.author ?? '');
|
|
274
|
+
const committer = parseCommitIdent(headers.committer ?? '');
|
|
232
275
|
return {
|
|
233
276
|
message,
|
|
234
277
|
author: {
|
|
235
|
-
name:
|
|
236
|
-
email:
|
|
237
|
-
date:
|
|
278
|
+
name: author.name,
|
|
279
|
+
email: author.email,
|
|
280
|
+
date: `${author.ts} ${author.tz}`,
|
|
238
281
|
},
|
|
239
282
|
committer: {
|
|
240
|
-
name:
|
|
241
|
-
email:
|
|
242
|
-
date:
|
|
283
|
+
name: committer.name,
|
|
284
|
+
email: committer.email,
|
|
285
|
+
date: `${committer.ts} ${committer.tz}`,
|
|
243
286
|
},
|
|
244
287
|
};
|
|
245
288
|
}
|
|
@@ -384,20 +427,25 @@ export async function analyzeLineage(root) {
|
|
|
384
427
|
* Create a rebuilt commit object with `commit-tree`, preserving the original
|
|
385
428
|
* author/committer identity and dates via environment variables.
|
|
386
429
|
*
|
|
430
|
+
* The message is delivered via stdin (`-F -`) rather than `-m`: `commit-tree
|
|
431
|
+
* -m` strips trailing blank lines, while stdin preserves the original message
|
|
432
|
+
* bytes exactly (including trailing whitespace), so a rebuilt node can be
|
|
433
|
+
* byte-identical to the original commit.
|
|
434
|
+
*
|
|
387
435
|
* Writes ONLY a commit object into the local object database. Never writes a
|
|
388
436
|
* ref and never pushes.
|
|
389
437
|
*
|
|
390
438
|
* @param {string} root - repository root
|
|
391
439
|
* @param {string} treeSha - tree hash for the rebuilt commit
|
|
392
440
|
* @param {string|null} parentSha - parent commit (null → chain root)
|
|
393
|
-
* @param {string} message - commit message
|
|
441
|
+
* @param {string} message - commit message (exact bytes)
|
|
394
442
|
* @param {{ author: {name: string, email: string, date: string}, committer: {name: string, email: string, date: string} }} identity
|
|
395
443
|
* @returns {Promise<string>} rebuilt commit SHA
|
|
396
444
|
*/
|
|
397
445
|
export async function createRebuiltCommit(root, treeSha, parentSha, message, identity) {
|
|
398
446
|
const args = ['commit-tree', treeSha];
|
|
399
447
|
if (parentSha) args.push('-p', parentSha);
|
|
400
|
-
args.push('-
|
|
448
|
+
args.push('-F', '-');
|
|
401
449
|
const env = {
|
|
402
450
|
...process.env,
|
|
403
451
|
GIT_AUTHOR_NAME: identity.author.name,
|
|
@@ -407,8 +455,29 @@ export async function createRebuiltCommit(root, treeSha, parentSha, message, ide
|
|
|
407
455
|
GIT_COMMITTER_EMAIL: identity.committer.email,
|
|
408
456
|
GIT_COMMITTER_DATE: identity.committer.date,
|
|
409
457
|
};
|
|
410
|
-
|
|
411
|
-
|
|
458
|
+
// Async execFile has no stdin-input support (options.input would be ignored
|
|
459
|
+
// and the child would hang waiting on an open stdin), so spawn is used to
|
|
460
|
+
// deliver the exact message bytes on stdin.
|
|
461
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
462
|
+
const child = spawn('git', args, { cwd: root, env });
|
|
463
|
+
let stdout = '';
|
|
464
|
+
let stderr = '';
|
|
465
|
+
child.stdout.setEncoding('utf8');
|
|
466
|
+
child.stderr.setEncoding('utf8');
|
|
467
|
+
child.stdout.on('data', (chunk) => { stdout += chunk; });
|
|
468
|
+
child.stderr.on('data', (chunk) => { stderr += chunk; });
|
|
469
|
+
child.on('error', rejectPromise);
|
|
470
|
+
child.on('close', (code) => {
|
|
471
|
+
if (code !== 0) {
|
|
472
|
+
rejectPromise(new Error(`git commit-tree failed (exit ${code}): ${stderr.trim()}`));
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
resolvePromise(stdout.trim());
|
|
476
|
+
});
|
|
477
|
+
child.stdin.on('error', () => {}); // EPIPE after early exit is reported via close
|
|
478
|
+
child.stdin.write(message);
|
|
479
|
+
child.stdin.end();
|
|
480
|
+
});
|
|
412
481
|
}
|
|
413
482
|
|
|
414
483
|
/**
|