kld-sdd 2.4.9 → 2.4.11

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 (37) hide show
  1. package/README.md +9 -6
  2. package/lib/init.js +76 -21
  3. package/lib/skills-bundle.js +32 -0
  4. package/package.json +3 -3
  5. package/skywalk-sdd/{index.js → index.cjs} +31 -25
  6. package/templates/ci/github-actions-sdd.yml +3 -3
  7. package/templates/ci/gitlab-ci-sdd.yml +3 -3
  8. package/templates/git-hooks/{pre-push-sdd-check.js → pre-push-sdd-check.cjs} +1 -1
  9. package/templates/hooks/claude/hooks/{sdd-prompt.js → sdd-prompt.cjs} +2 -2
  10. package/templates/hooks/claude/hooks/{sdd-stop.js → sdd-stop.cjs} +1 -1
  11. package/templates/hooks/claude/settings.json +4 -4
  12. package/templates/opsx-commands/apply.md +11 -9
  13. package/templates/opsx-commands/archive.md +8 -8
  14. package/templates/opsx-commands/check.md +8 -8
  15. package/templates/opsx-commands/design.md +3 -3
  16. package/templates/opsx-commands/explore.md +3 -3
  17. package/templates/opsx-commands/propose.md +3 -3
  18. package/templates/opsx-commands/spec.md +3 -3
  19. package/templates/opsx-commands/task.md +3 -3
  20. package/templates/opsx-commands/test.md +5 -5
  21. package/templates/skills/kld-sdd/SKILLS.md +28 -0
  22. package/templates/skills/{opsx-apply → kld-sdd/opsx-apply}/SKILL.md +12 -4
  23. package/templates/skills/{opsx-archive → kld-sdd/opsx-archive}/SKILL.md +8 -8
  24. package/templates/skills/{opsx-check → kld-sdd/opsx-check}/SKILL.md +6 -6
  25. package/templates/skills/{opsx-design → kld-sdd/opsx-design}/SKILL.md +6 -2
  26. package/templates/skills/{opsx-explore → kld-sdd/opsx-explore}/SKILL.md +2 -2
  27. package/templates/skills/kld-sdd/opsx-knowledge/SKILL.md +132 -0
  28. package/templates/skills/kld-sdd/opsx-knowledge/references/modules.md +26 -0
  29. package/templates/skills/kld-sdd/opsx-knowledge/scripts/config.json +39 -0
  30. package/templates/skills/kld-sdd/opsx-knowledge/scripts/retrieve.cjs +199 -0
  31. package/templates/skills/{opsx-propose → kld-sdd/opsx-propose}/SKILL.md +6 -2
  32. package/templates/skills/{opsx-spec → kld-sdd/opsx-spec}/SKILL.md +6 -2
  33. package/templates/skills/{opsx-task → kld-sdd/opsx-task}/SKILL.md +2 -2
  34. package/templates/skills/{opsx-test → kld-sdd/opsx-test}/SKILL.md +2 -2
  35. /package/templates/git-hooks/{pre-commit-sdd-check.js → pre-commit-sdd-check.cjs} +0 -0
  36. /package/templates/hooks/claude/hooks/{sdd-post-tool.js → sdd-post-tool.cjs} +0 -0
  37. /package/templates/hooks/claude/hooks/{sdd-pre-tool.js → sdd-pre-tool.cjs} +0 -0
package/README.md CHANGED
@@ -136,9 +136,9 @@ propose → spec → design → task → check
136
136
 
137
137
  ---
138
138
 
139
- ## 预置技能:`opsx-sdd`
139
+ ## 预置技能:`kld-sdd`
140
140
 
141
- 初始化后,`opsx-sdd` 技能会自动部署到你的编辑器技能目录(`.*/skills/opsx-sdd/`)。
141
+ 初始化后,SDD 全套技能会统一打包部署到 `.*/skills/kld-sdd/`(与 `gitnexus/` 目录模式一致),包含 `opsx-propose`、`opsx-spec`、`opsx-design`、`opsx-task`、`opsx-check`、`opsx-apply`、`opsx-test`、`opsx-archive`、`opsx-explore`、`opsx-knowledge` 等子技能。
142
142
 
143
143
  激活后,AI 会化身 **SDD 工作流专家**,检测当前项目的文档状态,引导你从当前进度继续:
144
144
 
@@ -190,16 +190,19 @@ your-project/
190
190
  │ └── task.md
191
191
  ├── .cursor/
192
192
  │ ├── commands/opsx/ # opsx 命令(9个)
193
- │ └── skills/opsx-*/ # 预置 SDD 技能
193
+ │ └── skills/kld-sdd/ # SDD 技能统一目录
194
+ │ ├── opsx-propose/
195
+ │ ├── opsx-spec/
196
+ │ └── ... # 其他 opsx-* 子技能
194
197
  ├── .claude/
195
198
  │ ├── commands/opsx/ # opsx 命令(9个)
196
- │ └── skills/opsx-*/ # 预置 SDD 技能
199
+ │ └── skills/kld-sdd/ # SDD 技能统一目录
197
200
  ├── .codebuddy/
198
201
  │ ├── commands/opsx/ # opsx 命令(9个)
199
- │ └── skills/opsx-*/ # 预置 SDD 技能
202
+ │ └── skills/kld-sdd/ # SDD 技能统一目录
200
203
  └── .agents/
201
204
  ├── commands/opsx/ # Codex opsx 命令(9个)
202
- └── skills/opsx-*/ # Codex 项目级技能
205
+ └── skills/kld-sdd/ # Codex 项目级技能
203
206
  ```
204
207
 
205
208
  ---
package/lib/init.js CHANGED
@@ -327,9 +327,7 @@ function copyDir(source, target) {
327
327
  }
328
328
 
329
329
  function renderTemplate(content, config, toolKey) {
330
- return content
331
- .replace(/^\uFEFF/, '')
332
- .replace(/<Agent(?:\u7c7b\u578b|\u7eeb\u8bf2\u7037)>/g, config.agentType || toolKey);
330
+ return require('./skills-bundle').renderTemplate(content, config, toolKey);
333
331
  }
334
332
 
335
333
  function copyDirRendered(source, target, config, toolKey) {
@@ -504,9 +502,50 @@ function overrideOpsxCommands(selectedTools = Object.keys(TOOL_CONFIGS)) {
504
502
  return true;
505
503
  }
506
504
 
505
+ const {
506
+ SKILLS_BUNDLE_NAME,
507
+ OPSX_SKILL_DIRS,
508
+ renderTemplate
509
+ } = require('./skills-bundle');
510
+
511
+ /**
512
+ * 清理旧版扁平 opsx-* skills(迁移到 skills/kld-sdd/ 统一目录)
513
+ * @param {string[]} selectedTools - 用户选择的编辑器列表
514
+ */
515
+ function cleanupLegacyFlatOpsxSkills(selectedTools = Object.keys(TOOL_CONFIGS)) {
516
+ console.log('🧹 正在清理旧版扁平 opsx-* skills...');
517
+
518
+ const cwd = process.cwd();
519
+ const legacySkillDirs = [...OPSX_SKILL_DIRS, 'opsx-sdd'];
520
+
521
+ for (const toolKey of selectedTools) {
522
+ const config = TOOL_CONFIGS[toolKey];
523
+ if (!config || !config.skillsDir) continue;
524
+
525
+ const skillsDir = path.join(cwd, config.skillsDir);
526
+ if (!fs.existsSync(skillsDir)) continue;
527
+
528
+ let removed = 0;
529
+ for (const legacySkill of legacySkillDirs) {
530
+ const legacySkillDir = path.join(skillsDir, legacySkill);
531
+ if (fs.existsSync(legacySkillDir)) {
532
+ fs.rmSync(legacySkillDir, { recursive: true, force: true });
533
+ removed++;
534
+ }
535
+ }
536
+
537
+ if (removed > 0) {
538
+ console.log(` 🗑 ${config.name}: 删除 ${removed} 个旧版扁平 opsx skills`);
539
+ }
540
+ }
541
+
542
+ console.log('✅ 旧版扁平 opsx skills 清理完成');
543
+ return true;
544
+ }
545
+
507
546
  /**
508
- * 部署 SDD opsx skills(9 个技能文件)
509
- * 从 templates/skills/ 目录复制到各编辑器的 skills 目录
547
+ * 部署 SDD opsx skills
548
+ * 从 templates/skills/kld-sdd/ 复制到各编辑器的 skills/kld-sdd/ 目录
510
549
  * @param {string[]} selectedTools - 用户选择的编辑器列表
511
550
  */
512
551
  function deployOpsxSkills(selectedTools = Object.keys(TOOL_CONFIGS)) {
@@ -515,14 +554,14 @@ function deployOpsxSkills(selectedTools = Object.keys(TOOL_CONFIGS)) {
515
554
  const pkgPath = getPackagePath();
516
555
  const cwd = process.cwd();
517
556
 
518
- const skillsTemplatePath = path.join(pkgPath, 'templates', 'skills');
557
+ const skillsTemplatePath = path.join(pkgPath, 'templates', 'skills', SKILLS_BUNDLE_NAME);
519
558
 
520
559
  if (!fs.existsSync(skillsTemplatePath)) {
521
560
  console.log(`⚠️ SDD skills 模板目录不存在: ${skillsTemplatePath}`);
522
561
  return false;
523
562
  }
524
563
 
525
- // 获取所有 skill 目录
564
+ // 获取 bundle 内所有 skill 目录
526
565
  const skillDirs = fs.readdirSync(skillsTemplatePath).filter(d => {
527
566
  return fs.statSync(path.join(skillsTemplatePath, d)).isDirectory();
528
567
  });
@@ -537,17 +576,21 @@ function deployOpsxSkills(selectedTools = Object.keys(TOOL_CONFIGS)) {
537
576
  if (!config || !config.skillsDir) continue;
538
577
 
539
578
  const targetSkillsDir = path.join(cwd, config.skillsDir);
579
+ const targetBundleDir = path.join(targetSkillsDir, SKILLS_BUNDLE_NAME);
540
580
 
541
581
  // 确保 skills 目录存在
542
582
  if (!fs.existsSync(targetSkillsDir)) {
543
583
  fs.mkdirSync(targetSkillsDir, { recursive: true });
544
584
  }
585
+ if (!fs.existsSync(targetBundleDir)) {
586
+ fs.mkdirSync(targetBundleDir, { recursive: true });
587
+ }
545
588
 
546
- console.log(` 部署 ${config.name} 的 opsx skills(${skillDirs.length} 个)...`);
589
+ console.log(` 部署 ${config.name} 的 ${SKILLS_BUNDLE_NAME} skills(${skillDirs.length} 个)...`);
547
590
 
548
591
  for (const skillDir of skillDirs) {
549
592
  const sourceDir = path.join(skillsTemplatePath, skillDir);
550
- const targetDir = path.join(targetSkillsDir, skillDir);
593
+ const targetDir = path.join(targetBundleDir, skillDir);
551
594
 
552
595
  if (!fs.existsSync(targetDir)) {
553
596
  fs.mkdirSync(targetDir, { recursive: true });
@@ -555,9 +598,18 @@ function deployOpsxSkills(selectedTools = Object.keys(TOOL_CONFIGS)) {
555
598
 
556
599
  if (fs.existsSync(sourceDir)) {
557
600
  copyDirRendered(sourceDir, targetDir, config, toolKey);
558
- console.log(` ✓ ${skillDir}`);
601
+ console.log(` ✓ ${SKILLS_BUNDLE_NAME}/${skillDir}`);
559
602
  }
560
603
  }
604
+
605
+ // 复制 bundle 根目录说明文件(如 SKILLS.md)
606
+ for (const file of fs.readdirSync(skillsTemplatePath)) {
607
+ const sourceFile = path.join(skillsTemplatePath, file);
608
+ if (!fs.statSync(sourceFile).isFile()) continue;
609
+ const rendered = renderTemplate(fs.readFileSync(sourceFile, 'utf8'), config, toolKey);
610
+ fs.writeFileSync(path.join(targetBundleDir, file), rendered, 'utf8');
611
+ console.log(` ✓ ${SKILLS_BUNDLE_NAME}/${file}`);
612
+ }
561
613
  }
562
614
 
563
615
  console.log('✅ opsx skills 部署完成');
@@ -566,7 +618,7 @@ function deployOpsxSkills(selectedTools = Object.keys(TOOL_CONFIGS)) {
566
618
 
567
619
  /**
568
620
  * 部署 Claude Code Hook Pack(可选增强)
569
- * Hook 只调用 skywalk-sdd/log.js,不写私有日志,不替代 OPSX 模板采集。
621
+ * Hook 只调用 skywalk-sdd/log.cjs,不写私有日志,不替代 OPSX 模板采集。
570
622
  */
571
623
  function deployClaudeHookPack(selectedTools = Object.keys(TOOL_CONFIGS)) {
572
624
  if (!selectedTools.includes('claude')) {
@@ -593,7 +645,7 @@ function deployClaudeHookPack(selectedTools = Object.keys(TOOL_CONFIGS)) {
593
645
  }
594
646
 
595
647
  copyDir(hookSourceDir, targetHookDir);
596
- console.log(' ✓ 部署 .claude/hooks/sdd-*.js');
648
+ console.log(' ✓ 部署 .claude/hooks/sdd-*.cjs');
597
649
 
598
650
  const targetSettingsPath = path.join(claudeDir, 'settings.json');
599
651
  const templateSettings = JSON.parse(fs.readFileSync(settingsTemplatePath, 'utf8'));
@@ -790,16 +842,16 @@ function deployTelemetryDataDir() {
790
842
  }
791
843
 
792
844
  // 部署项目内 Telemetry CLI(不依赖 npm 发布)
793
- const telemetrySrc = path.join(pkgPath, 'skywalk-sdd', 'index.js');
794
- const telemetryDst = path.join(dataDir, 'log.js');
845
+ const telemetrySrc = path.join(pkgPath, 'skywalk-sdd', 'index.cjs');
846
+ const telemetryDst = path.join(dataDir, 'log.cjs');
795
847
  if (fs.existsSync(telemetrySrc)) {
796
848
  fs.copyFileSync(telemetrySrc, telemetryDst);
797
- console.log(' ✓ 部署 skywalk-sdd/log.js(本地 Telemetry CLI)');
849
+ console.log(' ✓ 部署 skywalk-sdd/log.cjs(本地 Telemetry CLI)');
798
850
  } else {
799
851
  console.log(` ⚠️ Telemetry CLI 源文件缺失: ${telemetrySrc}`);
800
852
  }
801
853
 
802
- console.log(' ✓ 调用方式: node skywalk-sdd/log.js start|end|metrics');
854
+ console.log(' ✓ 调用方式: node skywalk-sdd/log.cjs start|end|metrics');
803
855
  console.log('✅ Telemetry 已就绪(数据存储在 skywalk-sdd/,无需配置 MCP)');
804
856
  return true;
805
857
  }
@@ -869,8 +921,8 @@ function deployQualityGateTemplates() {
869
921
  console.log(` ⚠️ CI 模板缺失: ${ciTemplateDir}`);
870
922
  }
871
923
 
872
- installGitHookShim(cwd, 'pre-commit', 'skywalk-sdd/git-hooks/pre-commit-sdd-check.js');
873
- installGitHookShim(cwd, 'pre-push', 'skywalk-sdd/git-hooks/pre-push-sdd-check.js');
924
+ installGitHookShim(cwd, 'pre-commit', 'skywalk-sdd/git-hooks/pre-commit-sdd-check.cjs');
925
+ installGitHookShim(cwd, 'pre-push', 'skywalk-sdd/git-hooks/pre-push-sdd-check.cjs');
874
926
 
875
927
  console.log('✅ Git hooks / CI 兜底模板已就绪');
876
928
  return true;
@@ -1019,10 +1071,13 @@ async function main() {
1019
1071
  // 3. 部署 SDD opsx 命令(根据编辑器类型使用不同模板)
1020
1072
  overrideOpsxCommands(selectedTools);
1021
1073
 
1022
- // 4. 部署 SDD opsx skills
1074
+ // 4. 清理旧版扁平 opsx-* skills(迁移到 skills/kld-sdd/)
1075
+ cleanupLegacyFlatOpsxSkills(selectedTools);
1076
+
1077
+ // 4.1 部署 SDD opsx skills(统一打包到 skills/kld-sdd/)
1023
1078
  deployOpsxSkills(selectedTools);
1024
1079
 
1025
- // 4.1 清理原生 openspec-* skills(防止残留)
1080
+ // 4.2 清理原生 openspec-* skills(防止残留)
1026
1081
  cleanupNativeOpenspecSkills(selectedTools);
1027
1082
 
1028
1083
  // 4.2 部署 Claude Code Hook Pack(可选增强)
@@ -1086,7 +1141,7 @@ async function main() {
1086
1141
  console.log();
1087
1142
  console.log('📊 SDD Telemetry(嵌入在命令流程中,自动执行,无需配置):');
1088
1143
  console.log(' - 度量数据自动采集到 skywalk-sdd/events/');
1089
- console.log(' - 运行 node skywalk-sdd/log.js metrics --project=. 查看四维度指标');
1144
+ console.log(' - 运行 node skywalk-sdd/log.cjs metrics --project=. 查看四维度指标');
1090
1145
  console.log();
1091
1146
 
1092
1147
  rl.close();
@@ -0,0 +1,32 @@
1
+ /**
2
+ * SDD skills bundle 共享配置与模板渲染
3
+ */
4
+
5
+ const SKILLS_BUNDLE_NAME = 'kld-sdd';
6
+
7
+ const OPSX_SKILL_DIRS = [
8
+ 'opsx-propose',
9
+ 'opsx-spec',
10
+ 'opsx-design',
11
+ 'opsx-task',
12
+ 'opsx-check',
13
+ 'opsx-apply',
14
+ 'opsx-test',
15
+ 'opsx-archive',
16
+ 'opsx-explore',
17
+ 'opsx-knowledge'
18
+ ];
19
+
20
+ function renderTemplate(content, config, toolKey) {
21
+ const skillsBundlePath = `${config.skillsDir}/${SKILLS_BUNDLE_NAME}`;
22
+ return content
23
+ .replace(/^\uFEFF/, '')
24
+ .replace(/<Agent(?:\u7c7b\u578b|\u7eeb\u8bf2\u7037)>/g, config.agentType || toolKey)
25
+ .replace(/<SkillsBundlePath>/g, skillsBundlePath);
26
+ }
27
+
28
+ module.exports = {
29
+ SKILLS_BUNDLE_NAME,
30
+ OPSX_SKILL_DIRS,
31
+ renderTemplate
32
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kld-sdd",
3
- "version": "2.4.9",
3
+ "version": "2.4.11",
4
4
  "description": "KLD SDD OpenSpec 项目初始化工具 - 内置模版一键初始化",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -8,7 +8,7 @@
8
8
  "kld-sdd-init": "bin/kld-sdd-init.js"
9
9
  },
10
10
  "scripts": {
11
- "test": "echo \"Error: no test specified\" && exit 1"
11
+ "test": "node test/validate-skills-bundle.cjs"
12
12
  },
13
13
  "keywords": [
14
14
  "kld",
@@ -30,7 +30,7 @@
30
30
  "bin/",
31
31
  "lib/",
32
32
  "templates/",
33
- "skywalk-sdd/index.js",
33
+ "skywalk-sdd/index.cjs",
34
34
  "README.md"
35
35
  ]
36
36
  }
@@ -6,9 +6,9 @@
6
6
  * AI Agent 通过终端命令调用,将事件写入项目本地 skywalk-sdd/ 目录。
7
7
  *
8
8
  * 用法:
9
- * node skywalk-sdd/log.js start --command=propose --project=/path --change=xxx [--agent=cursor]
10
- * node skywalk-sdd/log.js end --event-id=evt_xxx --result=success --summary="..."
11
- * node skywalk-sdd/log.js metrics --project=/path [--change=xxx]
9
+ * node skywalk-sdd/log.cjs start --command=propose --project=/path --change=xxx [--agent=cursor]
10
+ * node skywalk-sdd/log.cjs end --event-id=evt_xxx --result=success --summary="..."
11
+ * node skywalk-sdd/log.cjs metrics --project=/path [--change=xxx]
12
12
  */
13
13
 
14
14
  const fs = require('fs');
@@ -1395,7 +1395,9 @@ function computeAiAdoptionMetrics(events) {
1395
1395
  const review = getAiAdoptionReview(e);
1396
1396
  return review?.review_status === 'final' || e.status === 'final';
1397
1397
  });
1398
- const latestEvent = latestByTimestamp(finalEvents.length > 0 ? finalEvents : adoptionEvents);
1398
+ // 优先使用 final 事件,无 final fallback snapshot(AI 产出快照)
1399
+ const candidates = finalEvents.length > 0 ? finalEvents : adoptionEvents;
1400
+ const latestEvent = latestByTimestamp(candidates);
1399
1401
  const latestReview = getAiAdoptionReview(latestEvent);
1400
1402
  if (!latestReview) {
1401
1403
  return {
@@ -1665,7 +1667,7 @@ function computeSinglePdfMvpMetrics(events, options = {}) {
1665
1667
  const gitDocumentMetrics = options.projectRoot && options.change
1666
1668
  ? computeGitDocumentMetrics(options.projectRoot, options.change)
1667
1669
  : null;
1668
- const specIterationCount = gitDocumentMetrics
1670
+ const specIterationCount = gitDocumentMetrics?.spec_iteration_count != null
1669
1671
  ? gitDocumentMetrics.spec_iteration_count
1670
1672
  : stageSpecIterationCount;
1671
1673
 
@@ -1902,6 +1904,10 @@ function renderExecutiveReportMarkdown(report) {
1902
1904
  '',
1903
1905
  '## 说明',
1904
1906
  '- `null` 表示当前还没有采集到对应事件或该指标暂不适用。',
1907
+ '- E4 AI 一次成码率:依赖 `task_update` 事件中的 `task_id` 和首次执行结果,若 apply 阶段未正确记录则显示 null。',
1908
+ '- P1 文档迭代次数:优先使用 Git 提交历史统计,无 Git 仓库或无提交历史时使用阶段 start 事件数。',
1909
+ '- P2 AI 代码保留率:优先使用 `ai_adoption_review` 的 final 状态,无 final 时 fallback 到 AI 产出快照(ai_snapshot)。',
1910
+ '- Q4 规约驱动测试覆盖率:依赖 check/test 阶段记录 spec 断言到测试用例的映射数据(spec_test_coverage 字段),属高级功能。',
1905
1911
  '- Q1 规约符合度与人工反馈类指标属于评审信号,默认不作为强阻断门禁。',
1906
1912
  ].join('\n');
1907
1913
  }
@@ -2064,7 +2070,7 @@ function cmdStart(args) {
2064
2070
 
2065
2071
  if (!command) {
2066
2072
  console.error('错误: 缺少 --command 参数');
2067
- console.error('用法: node skywalk-sdd/log.js start --command=propose --project=/path');
2073
+ console.error('用法: node skywalk-sdd/log.cjs start --command=propose --project=/path');
2068
2074
  process.exit(1);
2069
2075
  }
2070
2076
 
@@ -2556,7 +2562,7 @@ function cmdArchiveDocs(args) {
2556
2562
  function main() {
2557
2563
  // 支持两种调用方式:
2558
2564
  // 1) npx kld-sdd log start ... → argv 含 'log'
2559
- // 2) node skywalk-sdd/log.js start ... → argv 直接以子命令开头
2565
+ // 2) node skywalk-sdd/log.cjs start ... → argv 直接以子命令开头
2560
2566
  const argv = process.argv.slice(2);
2561
2567
  const logIdx = argv.indexOf('log');
2562
2568
  const subArgs = logIdx === -1 ? argv : argv.slice(logIdx + 1);
@@ -2604,12 +2610,12 @@ function showHelp() {
2604
2610
  SDD Telemetry CLI - 流程度量采集工具
2605
2611
 
2606
2612
  用法:
2607
- node skywalk-sdd/log.js start --command=<cmd> --project=<path> [--change=<name>] [--agent=<type>]
2608
- node skywalk-sdd/log.js end --event-id=<id> --result=<success|failure|partial> --summary="..."
2609
- node skywalk-sdd/log.js metrics --project=<path> [--change=<name>] [--pdf-mvp] [--format=json|markdown]
2610
- node skywalk-sdd/log.js report --project=<path> [--change=<name>] [--format=json|markdown] [--output=<file>]
2611
- node skywalk-sdd/log.js tasks-status --project=<path> --change=<name> [--require-complete]
2612
- node skywalk-sdd/log.js archive-docs --project=<path> --change=<name> [--reason=<text>] [--event-id=<id>] [--report-output=<file>]
2613
+ node skywalk-sdd/log.cjs start --command=<cmd> --project=<path> [--change=<name>] [--agent=<type>]
2614
+ node skywalk-sdd/log.cjs end --event-id=<id> --result=<success|failure|partial> --summary="..."
2615
+ node skywalk-sdd/log.cjs metrics --project=<path> [--change=<name>] [--pdf-mvp] [--format=json|markdown]
2616
+ node skywalk-sdd/log.cjs report --project=<path> [--change=<name>] [--format=json|markdown] [--output=<file>]
2617
+ node skywalk-sdd/log.cjs tasks-status --project=<path> --change=<name> [--require-complete]
2618
+ node skywalk-sdd/log.cjs archive-docs --project=<path> --change=<name> [--reason=<text>] [--event-id=<id>] [--report-output=<file>]
2613
2619
 
2614
2620
  子命令:
2615
2621
  start 记录 SDD 阶段开始,返回 event_id
@@ -2622,18 +2628,18 @@ SDD Telemetry CLI - 流程度量采集工具
2622
2628
  archive-docs 将 Simple/Full spec 变更真实移动到 openspec/changes/archive/,并可结束 archive 阶段生成报告
2623
2629
 
2624
2630
  示例:
2625
- node skywalk-sdd/log.js start --command=propose --project=/my/project --change=user-auth --agent=cursor
2626
- node skywalk-sdd/log.js end --event-id=evt_abc123 --result=success --summary="创建 proposal.md"
2627
- node skywalk-sdd/log.js record --type=task_update --command=apply --project=/my/project --change=user-auth --task-id=TASK-01 --status=completed
2628
- node skywalk-sdd/log.js record --type=conformance_review --command=check --project=/my/project --change=user-auth --source=manual --details-file=conformance-review.json
2629
- node skywalk-sdd/log.js record --type=ai_adoption_review --command=apply --project=/my/project --change=user-auth --status=final --details-file=ai-adoption.json
2630
- node skywalk-sdd/log.js record --type=survey_result --project=/my/project --change=user-auth --source=manual --details-file=survey.json
2631
- node skywalk-sdd/log.js metrics --project=/my/project --change=user-auth
2632
- node skywalk-sdd/log.js metrics --project=/my/project --change=user-auth --pdf-mvp --format=markdown
2633
- node skywalk-sdd/log.js report --project=/my/project --change=user-auth --format=markdown
2634
- node skywalk-sdd/log.js doctor --project=/my/project --change=user-auth
2635
- node skywalk-sdd/log.js tasks-status --project=/my/project --change=user-auth --require-complete
2636
- node skywalk-sdd/log.js archive-docs --project=/my/project --change=user-auth --reason="变更已完成实施" --event-id=evt_archive --report-output=skywalk-sdd/reports/user-auth-report.md
2631
+ node skywalk-sdd/log.cjs start --command=propose --project=/my/project --change=user-auth --agent=cursor
2632
+ node skywalk-sdd/log.cjs end --event-id=evt_abc123 --result=success --summary="创建 proposal.md"
2633
+ node skywalk-sdd/log.cjs record --type=task_update --command=apply --project=/my/project --change=user-auth --task-id=TASK-01 --status=completed
2634
+ node skywalk-sdd/log.cjs record --type=conformance_review --command=check --project=/my/project --change=user-auth --source=manual --details-file=conformance-review.json
2635
+ node skywalk-sdd/log.cjs record --type=ai_adoption_review --command=apply --project=/my/project --change=user-auth --status=final --details-file=ai-adoption.json
2636
+ node skywalk-sdd/log.cjs record --type=survey_result --project=/my/project --change=user-auth --source=manual --details-file=survey.json
2637
+ node skywalk-sdd/log.cjs metrics --project=/my/project --change=user-auth
2638
+ node skywalk-sdd/log.cjs metrics --project=/my/project --change=user-auth --pdf-mvp --format=markdown
2639
+ node skywalk-sdd/log.cjs report --project=/my/project --change=user-auth --format=markdown
2640
+ node skywalk-sdd/log.cjs doctor --project=/my/project --change=user-auth
2641
+ node skywalk-sdd/log.cjs tasks-status --project=/my/project --change=user-auth --require-complete
2642
+ node skywalk-sdd/log.cjs archive-docs --project=/my/project --change=user-auth --reason="变更已完成实施" --event-id=evt_archive --report-output=skywalk-sdd/reports/user-auth-report.md
2637
2643
  `);
2638
2644
  }
2639
2645
 
@@ -28,7 +28,7 @@ jobs:
28
28
  run: npm install
29
29
 
30
30
  - name: SDD doctor
31
- run: node skywalk-sdd/log.js doctor --project="$SDD_PROJECT" --change="$SDD_CHANGE"
31
+ run: node skywalk-sdd/log.cjs doctor --project="$SDD_PROJECT" --change="$SDD_CHANGE"
32
32
 
33
33
  - name: Build and record SDD result
34
34
  shell: bash
@@ -45,7 +45,7 @@ jobs:
45
45
  result="failure"
46
46
  success="false"
47
47
  fi
48
- node skywalk-sdd/log.js record --type=build_result --command=ci --project="$SDD_PROJECT" --change="$SDD_CHANGE" --agent="$SDD_AGENT" --source=ci --session-id="$SDD_SESSION" --result="$result" --summary="CI build $result" --details-json="{\"build_results\":{\"command\":\"$SDD_BUILD_COMMAND\",\"success\":$success,\"duration_ms\":$duration,\"error_count\":0}}"
48
+ node skywalk-sdd/log.cjs record --type=build_result --command=ci --project="$SDD_PROJECT" --change="$SDD_CHANGE" --agent="$SDD_AGENT" --source=ci --session-id="$SDD_SESSION" --result="$result" --summary="CI build $result" --details-json="{\"build_results\":{\"command\":\"$SDD_BUILD_COMMAND\",\"success\":$success,\"duration_ms\":$duration,\"error_count\":0}}"
49
49
  exit "$status"
50
50
 
51
51
  - name: Test and record SDD result
@@ -63,5 +63,5 @@ jobs:
63
63
  result="failure"
64
64
  failed=1
65
65
  fi
66
- node skywalk-sdd/log.js record --type=test_result --command=ci --project="$SDD_PROJECT" --change="$SDD_CHANGE" --agent="$SDD_AGENT" --source=ci --session-id="$SDD_SESSION" --result="$result" --summary="CI test $result" --details-json="{\"test_results\":{\"command\":\"$SDD_TEST_COMMAND\",\"passed\":0,\"failed\":$failed,\"skipped\":0,\"coverage\":null,\"duration_ms\":$duration}}"
66
+ node skywalk-sdd/log.cjs record --type=test_result --command=ci --project="$SDD_PROJECT" --change="$SDD_CHANGE" --agent="$SDD_AGENT" --source=ci --session-id="$SDD_SESSION" --result="$result" --summary="CI test $result" --details-json="{\"test_results\":{\"command\":\"$SDD_TEST_COMMAND\",\"passed\":0,\"failed\":$failed,\"skipped\":0,\"coverage\":null,\"duration_ms\":$duration}}"
67
67
  exit "$status"
@@ -11,7 +11,7 @@ sdd_quality_gate:
11
11
  before_script:
12
12
  - npm install
13
13
  script:
14
- - node skywalk-sdd/log.js doctor --project="$SDD_PROJECT" --change="$SDD_CHANGE"
14
+ - node skywalk-sdd/log.cjs doctor --project="$SDD_PROJECT" --change="$SDD_CHANGE"
15
15
  - |
16
16
  set +e
17
17
  start_ms=$(date +%s%3N)
@@ -25,7 +25,7 @@ sdd_quality_gate:
25
25
  result="failure"
26
26
  success="false"
27
27
  fi
28
- node skywalk-sdd/log.js record --type=build_result --command=ci --project="$SDD_PROJECT" --change="$SDD_CHANGE" --agent="$SDD_AGENT" --source=ci --session-id="$SDD_SESSION" --result="$result" --summary="CI build $result" --details-json="{\"build_results\":{\"command\":\"$SDD_BUILD_COMMAND\",\"success\":$success,\"duration_ms\":$duration,\"error_count\":0}}"
28
+ node skywalk-sdd/log.cjs record --type=build_result --command=ci --project="$SDD_PROJECT" --change="$SDD_CHANGE" --agent="$SDD_AGENT" --source=ci --session-id="$SDD_SESSION" --result="$result" --summary="CI build $result" --details-json="{\"build_results\":{\"command\":\"$SDD_BUILD_COMMAND\",\"success\":$success,\"duration_ms\":$duration,\"error_count\":0}}"
29
29
  if [ "$status" -ne 0 ]; then exit "$status"; fi
30
30
  - |
31
31
  set +e
@@ -40,5 +40,5 @@ sdd_quality_gate:
40
40
  result="failure"
41
41
  failed=1
42
42
  fi
43
- node skywalk-sdd/log.js record --type=test_result --command=ci --project="$SDD_PROJECT" --change="$SDD_CHANGE" --agent="$SDD_AGENT" --source=ci --session-id="$SDD_SESSION" --result="$result" --summary="CI test $result" --details-json="{\"test_results\":{\"command\":\"$SDD_TEST_COMMAND\",\"passed\":0,\"failed\":$failed,\"skipped\":0,\"coverage\":null,\"duration_ms\":$duration}}"
43
+ node skywalk-sdd/log.cjs record --type=test_result --command=ci --project="$SDD_PROJECT" --change="$SDD_CHANGE" --agent="$SDD_AGENT" --source=ci --session-id="$SDD_SESSION" --result="$result" --summary="CI test $result" --details-json="{\"test_results\":{\"command\":\"$SDD_TEST_COMMAND\",\"passed\":0,\"failed\":$failed,\"skipped\":0,\"coverage\":null,\"duration_ms\":$duration}}"
44
44
  if [ "$status" -ne 0 ]; then exit "$status"; fi
@@ -16,7 +16,7 @@ function main() {
16
16
  const logCli = path.join(projectRoot, 'skywalk-sdd', 'log.js');
17
17
 
18
18
  if (!fs.existsSync(logCli)) {
19
- console.log('SDD pre-push: skywalk-sdd/log.js not found; doctor gate skipped.');
19
+ console.log('SDD pre-push: skywalk-sdd/log.cjs not found; doctor gate skipped.');
20
20
  return;
21
21
  }
22
22
 
@@ -54,8 +54,8 @@ const activeStages = readActiveStages(projectRoot)
54
54
 
55
55
  const lines = [
56
56
  'SDD Telemetry reminder:',
57
- '- Run skywalk-sdd/log.js start before the OPSX stage work begins.',
58
- '- Run skywalk-sdd/log.js end before stopping the stage.',
57
+ '- Run skywalk-sdd/log.cjs start before the OPSX stage work begins.',
58
+ '- Run skywalk-sdd/log.cjs end before stopping the stage.',
59
59
  '- Hooks are only an enhancement; OPSX command instructions remain authoritative.',
60
60
  ];
61
61
 
@@ -78,5 +78,5 @@ for (const event of activeStages) {
78
78
  console.log([
79
79
  'SDD Telemetry warning: open stage(s) detected.',
80
80
  ...activeStages.map(event => `- ${event.change || 'general'}:${event.command || event.stage || 'unknown'} event_id=${event.event_id}`),
81
- 'Run skywalk-sdd/log.js end before closing the OPSX stage, or explicitly mark it partial/failure.',
81
+ 'Run skywalk-sdd/log.cjs end before closing the OPSX stage, or explicitly mark it partial/failure.',
82
82
  ].join('\n'));
@@ -5,7 +5,7 @@
5
5
  "hooks": [
6
6
  {
7
7
  "type": "command",
8
- "command": "node .claude/hooks/sdd-prompt.js"
8
+ "command": "node .claude/hooks/sdd-prompt.cjs"
9
9
  }
10
10
  ]
11
11
  }
@@ -16,7 +16,7 @@
16
16
  "hooks": [
17
17
  {
18
18
  "type": "command",
19
- "command": "node .claude/hooks/sdd-pre-tool.js"
19
+ "command": "node .claude/hooks/sdd-pre-tool.cjs"
20
20
  }
21
21
  ]
22
22
  }
@@ -27,7 +27,7 @@
27
27
  "hooks": [
28
28
  {
29
29
  "type": "command",
30
- "command": "node .claude/hooks/sdd-post-tool.js"
30
+ "command": "node .claude/hooks/sdd-post-tool.cjs"
31
31
  }
32
32
  ]
33
33
  }
@@ -37,7 +37,7 @@
37
37
  "hooks": [
38
38
  {
39
39
  "type": "command",
40
- "command": "node .claude/hooks/sdd-stop.js"
40
+ "command": "node .claude/hooks/sdd-stop.cjs"
41
41
  }
42
42
  ]
43
43
  }
@@ -12,7 +12,7 @@ argument-hint: "[change-name] [capability-name] [上下文文件...]"
12
12
  > - 在 Windows Bash / Git Bash / Claude Bash 中,禁止裸写 Windows 反斜杠绝对路径(如 `D:\project\demo`);如必须使用绝对路径,请写成正斜杠路径或加引号。
13
13
  > - 不要省略 `--source=opsx-command` 与 `--session-id=<会话ID>`。
14
14
  > **📊 Telemetry(必做,不得跳过)**
15
- > 在终端执行(必须成功):`node skywalk-sdd/log.js start --command=apply --project=. --change=<变更名称> --capability=<capability-name> --agent=<Agent类型> --source=opsx-command --session-id=<会话ID> --git-sha=<base_git_sha_or_none>`,记录返回的 event_id。
15
+ > 在终端执行(必须成功):`node skywalk-sdd/log.cjs start --command=apply --project=. --change=<变更名称> --capability=<capability-name> --agent=<Agent类型> --source=opsx-command --session-id=<会话ID> --git-sha=<base_git_sha_or_none>`,记录返回的 event_id。
16
16
 
17
17
  > **🔒 Git 策略(只读增强,不改变开发流)**
18
18
  > - Git 只作为可选度量数据源,不是 apply 前置条件。
@@ -65,7 +65,7 @@ argument-hint: "[change-name] [capability-name] [上下文文件...]"
65
65
 
66
66
  在终端执行(若命令失败必须中止本阶段,不得跳过):
67
67
  ```bash
68
- node skywalk-sdd/log.js start --command=apply --project=. --change=<变更名称> --capability=<capability-name> --agent=<Agent类型> --source=opsx-command --session-id=<会话ID> --git-sha=<base_git_sha_or_none>
68
+ node skywalk-sdd/log.cjs start --command=apply --project=. --change=<变更名称> --capability=<capability-name> --agent=<Agent类型> --source=opsx-command --session-id=<会话ID> --git-sha=<base_git_sha_or_none>
69
69
  ```
70
70
  保存输出 JSON 中的 `event_id`,供阶段结束使用。
71
71
 
@@ -350,7 +350,7 @@ argument-hint: "[change-name] [capability-name] [上下文文件...]"
350
350
 
351
351
  5. **记录构建 Telemetry**:
352
352
  ```bash
353
- node skywalk-sdd/log.js record --type=build_result --command=apply --project=. --change=<变更名称> --capability=<capability-name> --task-id=<TASK-ID> --agent=<Agent类型> --source=opsx-command --session-id=<会话ID> --result=success/failure --summary="编译检查结果" --details-json="{\"build_results\":{\"command\":\"<实际编译命令>\",\"success\":true,\"duration_ms\":0,\"error_count\":0}}"
353
+ node skywalk-sdd/log.cjs record --type=build_result --command=apply --project=. --change=<变更名称> --capability=<capability-name> --task-id=<TASK-ID> --agent=<Agent类型> --source=opsx-command --session-id=<会话ID> --result=success/failure --summary="编译检查结果" --details-json="{\"build_results\":{\"command\":\"<实际编译命令>\",\"success\":true,\"duration_ms\":0,\"error_count\":0}}"
354
354
  ```
355
355
  `build_results` 字段口径:
356
356
  - `command`: 实际执行的编译/构建命令。
@@ -402,8 +402,10 @@ argument-hint: "[change-name] [capability-name] [上下文文件...]"
402
402
 
403
403
  4. **记录任务级 Telemetry**:
404
404
  ```bash
405
- node skywalk-sdd/log.js record --type=task_update --command=apply --project=. --change=<变更名称> --capability=<capability-name> --task-id=<TASK-ID> --agent=<Agent类型> --source=opsx-command --session-id=<会话ID> --status=completed --result=success --summary="<TASK-ID> 完成" --details-json="{\"files_changed\":[],\"build_results\":{\"command\":\"<实际编译命令>\",\"success\":true,\"duration_ms\":0,\"error_count\":0},\"test_results\":{\"command\":\"<实际测试命令>\",\"passed\":0,\"failed\":0,\"skipped\":0,\"coverage\":null,\"duration_ms\":0}}"
405
+ node skywalk-sdd/log.cjs record --type=task_update --command=apply --project=. --change=<变更名称> --capability=<capability-name> --task-id=<TASK-ID> --agent=<Agent类型> --source=opsx-command --session-id=<会话ID> --status=completed --result=success --summary="<TASK-ID> 完成" --details-json="{\"files_changed\":[],\"build_results\":{\"command\":\"<实际编译命令>\",\"success\":true,\"duration_ms\":0,\"error_count\":0},\"test_results\":{\"command\":\"<实际测试命令>\",\"passed\":0,\"failed\":0,\"skipped\":0,\"coverage\":null,\"duration_ms\":0}}"
406
406
  ```
407
+ **⚠️ 注意**:`--task-id=<TASK-ID>` 中的 `<TASK-ID>` 必须替换为当前任务的实际 ID(如 `TASK-USER-AUTH-01`),不得保留占位符,否则 E4 指标无法计算。
408
+
407
409
  若任务失败或暂停,`--status` 使用 `failed` / `blocked`,`--result` 使用 `failure` / `partial`,并在 details 中记录失败原因。
408
410
 
409
411
  g. **继续下一个可执行任务**
@@ -481,8 +483,8 @@ argument-hint: "[change-name] [capability-name] [上下文文件...]"
481
483
  > - 在 Windows Bash / Git Bash / Claude Bash 中,禁止裸写 Windows 反斜杠绝对路径(如 `D:\project\demo`);如必须使用绝对路径,请写成正斜杠路径或加引号。
482
484
  > - 不要省略 `--source=opsx-command` 与 `--session-id=<会话ID>`。
483
485
  > **📊 Telemetry(必做,不得跳过)**
484
- > 每次编译检查后,必须记录构建事件:`node skywalk-sdd/log.js record --type=build_result --command=apply --project=. --change=<变更名称> --capability=<capability-name> --task-id=<TASK-ID> --agent=<Agent类型> --source=opsx-command --session-id=<会话ID> --result=success/failure --summary="编译检查结果" --details-json="{\"build_results\":{\"command\":\"<实际编译命令>\",\"success\":true,\"duration_ms\":0,\"error_count\":0}}"`
485
- > 每完成一个任务,必须记录任务级结构化事件:`node skywalk-sdd/log.js record --type=task_update --command=apply --project=. --change=<变更名称> --capability=<capability-name> --task-id=<TASK-ID> --agent=<Agent类型> --source=opsx-command --session-id=<会话ID> --status=completed --result=success --summary="<TASK-ID> 完成" --details-json="{\"files_changed\":[],\"build_results\":{\"command\":\"<实际编译命令>\",\"success\":true,\"duration_ms\":0,\"error_count\":0},\"test_results\":{\"command\":\"<实际测试命令>\",\"passed\":0,\"failed\":0,\"skipped\":0,\"coverage\":null,\"duration_ms\":0}}"`
486
- > AI 代码产出完成后,必须记录采纳率快照,但不得为了采集快照自动提交 commit。Git 可用时只读统计 SHA/diff;Git 不可用时使用 `vcs_mode=no-git` 和 `base_git_sha=null`:`node skywalk-sdd/log.js record --type=ai_adoption_review --command=apply --project=. --change=<变更名称> --capability=<capability-name> --agent=<Agent类型> --source=opsx-command --session-id=<会话ID> --status=ai_snapshot --result=success --summary="AI 代码产出快照" --details-json="{\"ai_adoption\":{\"review_status\":\"ai_snapshot\",\"vcs_mode\":\"<readonly|no-git>\",\"base_git_sha\":\"<base_git_sha_or_null>\",\"ai_git_sha\":\"<ai_git_sha_or_null>\",\"ai_diff\":{\"files_changed\":0,\"added_lines\":null,\"deleted_lines\":null},\"notes\":\"未自动创建 Git 仓库,未自动提交 commit;仅记录可用统计\"}}"`
487
- > 若后续发生人工修改并完成采纳确认,必须补录最终采纳统计;若非 Git 项目,SHA 可填 `null`,采纳行数由人工或工具统计补录:`node skywalk-sdd/log.js record --type=ai_adoption_review --command=apply --project=. --change=<变更名称> --capability=<capability-name> --agent=<Agent类型> --source=manual --session-id=<会话ID> --status=final --result=success --summary="AI 代码采纳率人工确认" --details-json="{\"ai_adoption\":{\"review_status\":\"final\",\"vcs_mode\":\"<readonly|no-git>\",\"base_git_sha\":\"<base_git_sha_or_null>\",\"ai_git_sha\":\"<ai_git_sha_or_null>\",\"final_git_sha\":\"<final_git_sha_or_null>\",\"retained_lines\":0,\"rewritten_lines\":0,\"deleted_lines\":0,\"ai_diff\":{\"files_changed\":0,\"added_lines\":null,\"deleted_lines\":null},\"final_diff\":{\"files_changed\":0,\"added_lines\":null,\"deleted_lines\":null},\"notes\":\"只记录行数统计,不记录源码;非 Git 项目允许 SHA 为 null\"}}"`
488
- > 阶段结束时执行(必须成功):`node skywalk-sdd/log.js end --event-id=<开头记录的event_id> --command=apply --project=. --change=<变更名称> --capability=<capability-name> --agent=<Agent类型> --source=opsx-command --session-id=<会话ID> --result=success/failure --summary="实施结果摘要"`
486
+ > 每次编译检查后,必须记录构建事件:`node skywalk-sdd/log.cjs record --type=build_result --command=apply --project=. --change=<变更名称> --capability=<capability-name> --task-id=<TASK-ID> --agent=<Agent类型> --source=opsx-command --session-id=<会话ID> --result=success/failure --summary="编译检查结果" --details-json="{\"build_results\":{\"command\":\"<实际编译命令>\",\"success\":true,\"duration_ms\":0,\"error_count\":0}}"`
487
+ > 每完成一个任务,必须记录任务级结构化事件:`node skywalk-sdd/log.cjs record --type=task_update --command=apply --project=. --change=<变更名称> --capability=<capability-name> --task-id=<TASK-ID> --agent=<Agent类型> --source=opsx-command --session-id=<会话ID> --status=completed --result=success --summary="<TASK-ID> 完成" --details-json="{\"files_changed\":[],\"build_results\":{\"command\":\"<实际编译命令>\",\"success\":true,\"duration_ms\":0,\"error_count\":0},\"test_results\":{\"command\":\"<实际测试命令>\",\"passed\":0,\"failed\":0,\"skipped\":0,\"coverage\":null,\"duration_ms\":0}}"`
488
+ > AI 代码产出完成后,必须记录采纳率快照,但不得为了采集快照自动提交 commit。Git 可用时只读统计 SHA/diff;Git 不可用时使用 `vcs_mode=no-git` 和 `base_git_sha=null`:`node skywalk-sdd/log.cjs record --type=ai_adoption_review --command=apply --project=. --change=<变更名称> --capability=<capability-name> --agent=<Agent类型> --source=opsx-command --session-id=<会话ID> --status=ai_snapshot --result=success --summary="AI 代码产出快照" --details-json="{\"ai_adoption\":{\"review_status\":\"ai_snapshot\",\"vcs_mode\":\"<readonly|no-git>\",\"base_git_sha\":\"<base_git_sha_or_null>\",\"ai_git_sha\":\"<ai_git_sha_or_null>\",\"ai_diff\":{\"files_changed\":0,\"added_lines\":null,\"deleted_lines\":null},\"notes\":\"未自动创建 Git 仓库,未自动提交 commit;仅记录可用统计\"}}"`
489
+ > 若后续发生人工修改并完成采纳确认,必须补录最终采纳统计;若非 Git 项目,SHA 可填 `null`,采纳行数由人工或工具统计补录:`node skywalk-sdd/log.cjs record --type=ai_adoption_review --command=apply --project=. --change=<变更名称> --capability=<capability-name> --agent=<Agent类型> --source=manual --session-id=<会话ID> --status=final --result=success --summary="AI 代码采纳率人工确认" --details-json="{\"ai_adoption\":{\"review_status\":\"final\",\"vcs_mode\":\"<readonly|no-git>\",\"base_git_sha\":\"<base_git_sha_or_null>\",\"ai_git_sha\":\"<ai_git_sha_or_null>\",\"final_git_sha\":\"<final_git_sha_or_null>\",\"retained_lines\":0,\"rewritten_lines\":0,\"deleted_lines\":0,\"ai_diff\":{\"files_changed\":0,\"added_lines\":null,\"deleted_lines\":null},\"final_diff\":{\"files_changed\":0,\"added_lines\":null,\"deleted_lines\":null},\"notes\":\"只记录行数统计,不记录源码;非 Git 项目允许 SHA 为 null\"}}"`
490
+ > 阶段结束时执行(必须成功):`node skywalk-sdd/log.cjs end --event-id=<开头记录的event_id> --command=apply --project=. --change=<变更名称> --capability=<capability-name> --agent=<Agent类型> --source=opsx-command --session-id=<会话ID> --result=success/failure --summary="实施结果摘要"`