release-skill 0.2.9 → 0.3.0
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 +21 -0
- package/INSTALL.md +4 -4
- package/INSTALL.zh-CN.md +4 -4
- package/README.md +38 -7
- package/README.zh-CN.md +36 -7
- 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 +3908 -2864
- package/adapters/claude/skills/release-help/SKILL.md +9 -1
- package/adapters/codex/.codex-plugin/plugin.json +2 -2
- package/adapters/codex/bin/release-skill.bundle.mjs +3908 -2864
- package/adapters/codex/skills/release-help/SKILL.md +9 -1
- package/adapters/kimi/.kimi-plugin/plugin.json +1 -1
- package/adapters/kimi/bin/release-skill.bundle.mjs +3908 -2864
- package/adapters/kimi/skills/release-help/SKILL.md +9 -1
- package/adapters/workbuddy/.codebuddy-plugin/plugin.json +1 -1
- package/adapters/workbuddy/bin/release-skill.bundle.mjs +3908 -2864
- package/adapters/workbuddy/skills/release-help/SKILL.md +9 -1
- package/bin/release-skill-cli.mjs +176 -4
- package/bin/release-skill.bundle.mjs +3908 -2864
- package/package.json +1 -1
- package/skills/release-help/SKILL.md +9 -1
- package/skills-src/release-help/SKILL.md +9 -1
- package/src/adapters/plugin-marketplace.mjs +1 -1
- package/src/adapters/push-snapshot.mjs +3 -1
- package/src/commands/attest.mjs +195 -0
- package/src/commands/hooks.mjs +46 -0
- package/src/commands/ship.mjs +356 -0
- package/src/commands/verify.mjs +147 -4
- package/src/core/git-transport.mjs +93 -0
- package/src/core/release-metadata.mjs +105 -0
|
@@ -12,7 +12,7 @@ description: "Discoverable entry point for release-skill: dependency and environ
|
|
|
12
12
|
## 职责
|
|
13
13
|
|
|
14
14
|
- 依赖和环境检查:Node.js >= 22、Git 决定本地准备就绪度;npm/gh 另行决定生产依赖就绪度
|
|
15
|
-
- 能力说明:缺少配置时走 `help → setup → assess`;已有配置的安全默认路径是 `help → assess → prepare --offline
|
|
15
|
+
- 能力说明:缺少配置时走 `help → setup → assess`;已有配置的安全默认路径是 `help → assess → prepare --offline`;日常生产发布优先使用可恢复的 `ship`,兼容的分阶段闭环仍是 `prepare --online --production → approve → publish → verify`
|
|
16
16
|
- 最小示例:展示从 release-help 到 release-assess 的最短路径
|
|
17
17
|
- 只读诊断:运行 dry-run 检查,不修改任何文件
|
|
18
18
|
- 故障引导:根据错误码指向对应的修复 Skill
|
|
@@ -35,6 +35,14 @@ description: "Discoverable entry point for release-skill: dependency and environ
|
|
|
35
35
|
node "${CODEBUDDY_PLUGIN_ROOT}/bin/release-skill.mjs" help --json
|
|
36
36
|
node "${CODEBUDDY_PLUGIN_ROOT}/bin/release-skill.mjs" setup --root <path> --json
|
|
37
37
|
node "${CODEBUDDY_PLUGIN_ROOT}/bin/release-skill.mjs" assess --root <path> --offline --json
|
|
38
|
+
# 日常发布快速路径:最多两个授权摘要,状态文件可恢复
|
|
39
|
+
node "${CODEBUDDY_PLUGIN_ROOT}/bin/release-skill.mjs" ship --root <path> --target-version <version> --json
|
|
40
|
+
# 开发阶段执行声明 hooks 并生成 prepare 可复用的内容绑定收据
|
|
41
|
+
node "${CODEBUDDY_PLUGIN_ROOT}/bin/release-skill.mjs" hooks validate --root <path> \
|
|
42
|
+
--acknowledge-hook-side-effects --json
|
|
43
|
+
# Kimi/CodeBuddy 安装完成后记录人工事实,不手改 JSON
|
|
44
|
+
node "${CODEBUDDY_PLUGIN_ROOT}/bin/release-skill.mjs" attest --root <path> \
|
|
45
|
+
--platform <kimi|codebuddy> --plugin <id> --result <passed|failed> --actor <person> --json
|
|
38
46
|
# 发布文档刷新:默认只读演练
|
|
39
47
|
node "${CODEBUDDY_PLUGIN_ROOT}/bin/release-skill.mjs" docs refresh --unit <id> --json
|
|
40
48
|
# 摘要确认后的本地写入(三项绑定缺一不可)
|
|
@@ -19,7 +19,7 @@ registerPathRedactor(redactSensitivePaths);
|
|
|
19
19
|
|
|
20
20
|
const execFile = promisify(execFileCb);
|
|
21
21
|
|
|
22
|
-
const COMMANDS = new Set(['help', 'setup', 'assess', 'prepare', 'approve', 'publish', 'reconcile', 'verify', 'artifacts', 'docs']);
|
|
22
|
+
const COMMANDS = new Set(['help', 'setup', 'assess', 'prepare', 'approve', 'publish', 'reconcile', 'verify', 'ship', 'attest', 'hooks', 'artifacts', 'docs']);
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* Check if a command is available and get its version.
|
|
@@ -222,6 +222,9 @@ Commands:
|
|
|
222
222
|
publish Publish frozen GitHub/npm artifacts after approval and digest confirmation
|
|
223
223
|
reconcile Resume PARTIAL state from evidence; conflicts require a human
|
|
224
224
|
verify Fresh remote and consumer verification; only this reaches VERIFIED
|
|
225
|
+
ship Resume one durable prepare -> approve -> publish -> verify flow
|
|
226
|
+
attest Record a generated Kimi/CodeBuddy manual result without editing JSON
|
|
227
|
+
hooks Run declared development hooks and populate reusable receipts
|
|
225
228
|
artifacts Artifact status, inspect, update/apply, resolution, and diagnostics
|
|
226
229
|
docs Refresh declared release documents (read-only dry-run by default)
|
|
227
230
|
|
|
@@ -242,6 +245,15 @@ Options:
|
|
|
242
245
|
--ack-local-document-write Acknowledge the explicit local release-document write (docs refresh --write)
|
|
243
246
|
--acknowledge-hook-side-effects Acknowledge unsandboxed legacy hook execution
|
|
244
247
|
--acknowledge-gate-side-effects Acknowledge unsandboxed local verification gate execution
|
|
248
|
+
--platform <id> Manual attestation platform: kimi or codebuddy
|
|
249
|
+
--plugin <id> Plugin id for a generated manual requirement
|
|
250
|
+
--actor <name> Person confirming an approval or manual result
|
|
251
|
+
--result <value> Manual result: passed or failed
|
|
252
|
+
--install-path <path> Actual managed plugin path when closure verification requires it
|
|
253
|
+
--install-channel <desktop|cli> CodeBuddy installation channel when required
|
|
254
|
+
--authorize-hooks <digest> Authorize the exact ship hook/config digest
|
|
255
|
+
--approve-plan <digest> Approve the exact ship plan and its verification gates
|
|
256
|
+
--state <path> Override the durable ship state file
|
|
245
257
|
--no-hook-cache Force every prepare hook to run in full; neither read nor write the hook cache
|
|
246
258
|
--json Output results as JSON
|
|
247
259
|
--version Show version and exit
|
|
@@ -500,6 +512,154 @@ if (command === 'assess') {
|
|
|
500
512
|
}
|
|
501
513
|
}
|
|
502
514
|
|
|
515
|
+
// --- Reusable hook receipt routing ---
|
|
516
|
+
if (command === 'hooks') {
|
|
517
|
+
const subcommand = positional[1];
|
|
518
|
+
const rootIdx = args.indexOf('--root');
|
|
519
|
+
const root = resolve(rootIdx !== -1 && args[rootIdx + 1] ? args[rootIdx + 1] : process.cwd());
|
|
520
|
+
if (subcommand !== 'validate') {
|
|
521
|
+
const message = 'hooks requires subcommand: hooks validate';
|
|
522
|
+
if (hasJson) console.log(JSON.stringify({ error: 'MISSING_PARAMETERS', message }));
|
|
523
|
+
else console.error(`Error: ${message}`);
|
|
524
|
+
process.exit(1);
|
|
525
|
+
}
|
|
526
|
+
try {
|
|
527
|
+
const { validateDeclaredHooks } = await import('../src/commands/hooks.mjs');
|
|
528
|
+
const result = await validateDeclaredHooks({
|
|
529
|
+
root,
|
|
530
|
+
hooksAuthorized: args.includes('--acknowledge-hook-side-effects'),
|
|
531
|
+
hookCache: !args.includes('--no-hook-cache'),
|
|
532
|
+
});
|
|
533
|
+
if (hasJson) console.log(JSON.stringify(result, null, 2));
|
|
534
|
+
else {
|
|
535
|
+
console.log(`Declared hooks: ${result.status}`);
|
|
536
|
+
console.log(`Reusable receipt evidence: ${result.evidenceDir}`);
|
|
537
|
+
}
|
|
538
|
+
process.exit(0);
|
|
539
|
+
} catch (err) {
|
|
540
|
+
if (hasJson) {
|
|
541
|
+
console.log(JSON.stringify({
|
|
542
|
+
error: err.code ?? 'UNKNOWN_ERROR',
|
|
543
|
+
message: err.message,
|
|
544
|
+
details: err.details ?? {},
|
|
545
|
+
exitCode: err.exitCode ?? 1,
|
|
546
|
+
}));
|
|
547
|
+
} else console.error(`Error: ${err.message}`);
|
|
548
|
+
process.exit(err.exitCode ?? 1);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// --- Durable end-to-end ship routing ---
|
|
553
|
+
if (command === 'ship') {
|
|
554
|
+
const value = (flag) => {
|
|
555
|
+
const idx = args.indexOf(flag);
|
|
556
|
+
return idx !== -1 && args[idx + 1] ? args[idx + 1] : undefined;
|
|
557
|
+
};
|
|
558
|
+
const root = resolve(value('--root') ?? process.cwd());
|
|
559
|
+
try {
|
|
560
|
+
const [
|
|
561
|
+
{ advanceShip },
|
|
562
|
+
{ createGitGithubAdapter },
|
|
563
|
+
{ createNpmAdapter },
|
|
564
|
+
{ createPluginMarketplaceAdapter },
|
|
565
|
+
{ createPushSnapshotAdapter },
|
|
566
|
+
{ createAdapterRegistry },
|
|
567
|
+
] = await Promise.all([
|
|
568
|
+
import('../src/commands/ship.mjs'),
|
|
569
|
+
import('../src/adapters/git-github.mjs'),
|
|
570
|
+
import('../src/adapters/npm.mjs'),
|
|
571
|
+
import('../src/adapters/plugin-marketplace.mjs'),
|
|
572
|
+
import('../src/adapters/push-snapshot.mjs'),
|
|
573
|
+
import('../src/adapters/contract.mjs'),
|
|
574
|
+
]);
|
|
575
|
+
const adapterRegistry = createAdapterRegistry([
|
|
576
|
+
createGitGithubAdapter(),
|
|
577
|
+
createNpmAdapter(),
|
|
578
|
+
createPluginMarketplaceAdapter(),
|
|
579
|
+
createPushSnapshotAdapter(),
|
|
580
|
+
]);
|
|
581
|
+
const result = await advanceShip({
|
|
582
|
+
root,
|
|
583
|
+
statePath: value('--state'),
|
|
584
|
+
targetVersion: value('--target-version') ?? value('--version'),
|
|
585
|
+
hookAuthorizationDigest: value('--authorize-hooks'),
|
|
586
|
+
planApprovalDigest: value('--approve-plan'),
|
|
587
|
+
actor: value('--actor'),
|
|
588
|
+
adapterRegistry,
|
|
589
|
+
});
|
|
590
|
+
if (hasJson) {
|
|
591
|
+
console.log(JSON.stringify(result, null, 2));
|
|
592
|
+
} else {
|
|
593
|
+
console.log(`Ship status: ${result.status}`);
|
|
594
|
+
console.log(`State: ${result.statePath}`);
|
|
595
|
+
if (result.hookAuthorizationDigest && result.status === 'NEEDS_HOOK_AUTHORIZATION') {
|
|
596
|
+
console.log(`Authorize hooks: --authorize-hooks ${result.hookAuthorizationDigest}`);
|
|
597
|
+
}
|
|
598
|
+
if (result.planDigest && result.status === 'NEEDS_PLAN_APPROVAL') {
|
|
599
|
+
console.log(`Approve plan and verification gates: --approve-plan ${result.planDigest} --actor <person>`);
|
|
600
|
+
}
|
|
601
|
+
for (const requirement of result.requirements ?? []) {
|
|
602
|
+
console.log(`Manual requirement [${requirement.platform}]: ${requirement.requirementPath}`);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
process.exit(0);
|
|
606
|
+
} catch (err) {
|
|
607
|
+
if (hasJson) {
|
|
608
|
+
console.log(JSON.stringify({
|
|
609
|
+
error: err.code ?? 'UNKNOWN_ERROR',
|
|
610
|
+
message: err.message,
|
|
611
|
+
details: err.details ?? {},
|
|
612
|
+
exitCode: err.exitCode ?? 1,
|
|
613
|
+
}));
|
|
614
|
+
} else {
|
|
615
|
+
console.error(`Error: ${err.message}`);
|
|
616
|
+
}
|
|
617
|
+
process.exit(err.exitCode ?? 1);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// --- Manual attestation command routing ---
|
|
622
|
+
if (command === 'attest') {
|
|
623
|
+
const value = (flag) => {
|
|
624
|
+
const idx = args.indexOf(flag);
|
|
625
|
+
return idx !== -1 && args[idx + 1] ? args[idx + 1] : undefined;
|
|
626
|
+
};
|
|
627
|
+
const root = resolve(value('--root') ?? process.cwd());
|
|
628
|
+
try {
|
|
629
|
+
const { recordManualAttestation } = await import('../src/commands/attest.mjs');
|
|
630
|
+
const result = await recordManualAttestation({
|
|
631
|
+
root,
|
|
632
|
+
platform: value('--platform'),
|
|
633
|
+
plugin: value('--plugin'),
|
|
634
|
+
actor: value('--actor'),
|
|
635
|
+
result: value('--result'),
|
|
636
|
+
installPath: value('--install-path'),
|
|
637
|
+
installChannel: value('--install-channel'),
|
|
638
|
+
note: value('--note'),
|
|
639
|
+
});
|
|
640
|
+
if (hasJson) {
|
|
641
|
+
console.log(JSON.stringify(result, null, 2));
|
|
642
|
+
} else {
|
|
643
|
+
console.log(`Manual attestation recorded: ${result.platform}/${result.plugin} ${result.version}`);
|
|
644
|
+
console.log(`Bound plan digest: ${result.planDigest}`);
|
|
645
|
+
console.log(`Receipt: ${result.attestationPath}`);
|
|
646
|
+
}
|
|
647
|
+
process.exit(0);
|
|
648
|
+
} catch (err) {
|
|
649
|
+
if (hasJson) {
|
|
650
|
+
console.log(JSON.stringify({
|
|
651
|
+
error: err.code ?? 'UNKNOWN_ERROR',
|
|
652
|
+
message: err.message,
|
|
653
|
+
details: err.details ?? {},
|
|
654
|
+
exitCode: err.exitCode ?? 1,
|
|
655
|
+
}));
|
|
656
|
+
} else {
|
|
657
|
+
console.error(`Error: ${err.message}`);
|
|
658
|
+
}
|
|
659
|
+
process.exit(err.exitCode ?? 1);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
503
663
|
// --- Prepare command routing ---
|
|
504
664
|
if (command === 'prepare') {
|
|
505
665
|
const rootIdx = args.indexOf('--root');
|
|
@@ -542,15 +702,27 @@ if (command === 'prepare') {
|
|
|
542
702
|
});
|
|
543
703
|
|
|
544
704
|
if (hasJson) {
|
|
545
|
-
//
|
|
546
|
-
//
|
|
705
|
+
// Keep stdout compact and stable. The immutable plan remains the single
|
|
706
|
+
// authority at planPath; callers should not have to carry its full
|
|
707
|
+
// source closure, manifests and action payloads through chat/logs.
|
|
547
708
|
const planContent = await readFileFs(result.planPath, 'utf8');
|
|
548
709
|
const plan = JSON.parse(planContent);
|
|
549
710
|
console.log(JSON.stringify({
|
|
550
|
-
|
|
711
|
+
command: 'prepare',
|
|
712
|
+
status: plan.status,
|
|
551
713
|
planPath: result.planPath,
|
|
552
714
|
planDigest: result.planDigest,
|
|
553
715
|
evidenceDir: result.evidenceDir,
|
|
716
|
+
units: (plan.units ?? []).map((unit) => ({
|
|
717
|
+
id: unit.id,
|
|
718
|
+
targetVersion: unit.targetVersion ?? unit.version,
|
|
719
|
+
})),
|
|
720
|
+
actionCount: (plan.externalActions ?? []).length,
|
|
721
|
+
actions: (plan.externalActions ?? []).map((action) => ({
|
|
722
|
+
id: action.id,
|
|
723
|
+
type: action.type,
|
|
724
|
+
unitId: action.unitId,
|
|
725
|
+
})),
|
|
554
726
|
warnings: result.warnings,
|
|
555
727
|
}, null, 2));
|
|
556
728
|
} else {
|