release-skill 0.2.8 → 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.
Files changed (39) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codebuddy-plugin/plugin.json +1 -1
  4. package/.codex-plugin/plugin.json +2 -2
  5. package/.kimi-plugin/plugin.json +1 -1
  6. package/CHANGELOG.md +36 -0
  7. package/INSTALL.md +4 -4
  8. package/INSTALL.zh-CN.md +4 -4
  9. package/README.md +38 -13
  10. package/README.zh-CN.md +36 -13
  11. package/adapters/claude/.claude-plugin/marketplace.json +1 -1
  12. package/adapters/claude/.claude-plugin/plugin.json +1 -1
  13. package/adapters/claude/bin/release-skill.bundle.mjs +3961 -2882
  14. package/adapters/claude/skills/release-help/SKILL.md +9 -1
  15. package/adapters/codex/.codex-plugin/plugin.json +2 -2
  16. package/adapters/codex/bin/release-skill.bundle.mjs +3961 -2882
  17. package/adapters/codex/skills/release-help/SKILL.md +9 -1
  18. package/adapters/kimi/.kimi-plugin/plugin.json +1 -1
  19. package/adapters/kimi/bin/release-skill.bundle.mjs +3961 -2882
  20. package/adapters/kimi/skills/release-help/SKILL.md +9 -1
  21. package/adapters/workbuddy/.codebuddy-plugin/plugin.json +1 -1
  22. package/adapters/workbuddy/bin/release-skill.bundle.mjs +3961 -2882
  23. package/adapters/workbuddy/skills/release-help/SKILL.md +9 -1
  24. package/bin/release-skill-cli.mjs +180 -4
  25. package/bin/release-skill.bundle.mjs +3961 -2882
  26. package/package.json +1 -1
  27. package/skills/release-help/SKILL.md +9 -1
  28. package/skills-src/release-help/SKILL.md +9 -1
  29. package/src/adapters/plugin-marketplace.mjs +1 -1
  30. package/src/adapters/push-snapshot.mjs +3 -1
  31. package/src/commands/assess.mjs +18 -5
  32. package/src/commands/attest.mjs +195 -0
  33. package/src/commands/hooks.mjs +46 -0
  34. package/src/commands/prepare.mjs +14 -2
  35. package/src/commands/ship.mjs +356 -0
  36. package/src/commands/verify.mjs +147 -4
  37. package/src/core/git-transport.mjs +93 -0
  38. package/src/core/public-surface.mjs +26 -0
  39. 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`;已有公开版本的生产闭环是显式的 `prepare --online --production → approve → publish → verify`
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,17 +702,33 @@ if (command === 'prepare') {
542
702
  });
543
703
 
544
704
  if (hasJson) {
545
- // Output the full plan object plus metadata so consumers
546
- // can inspect status, units, externalActions, planDigest, etc.
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
- ...plan,
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
+ })),
726
+ warnings: result.warnings,
554
727
  }, null, 2));
555
728
  } else {
729
+ for (const warning of result.warnings) {
730
+ console.log(`Warning [${warning.code}] ${warning.message}`);
731
+ }
556
732
  console.log(`Plan frozen at: ${result.planPath}`);
557
733
  console.log(`Plan digest: ${result.planDigest}`);
558
734
  console.log(`Evidence: ${result.evidenceDir}`);