kld-sdd 2.4.18 → 2.5.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 (27) hide show
  1. package/lib/init.js +56 -41
  2. package/package.json +1 -1
  3. package/skywalk-sdd/index.cjs +1808 -129
  4. package/templates/hooks/claude/hooks/sdd-apply-test-gate.cjs +175 -28
  5. package/templates/hooks/claude/hooks/sdd-post-tool.cjs +42 -21
  6. package/templates/openspec/proposal.md +0 -1
  7. package/templates/openspec/spec.md +2 -2
  8. package/templates/skills/kld-sdd/opsx-apply/SKILL.md +64 -355
  9. package/templates/skills/kld-sdd/opsx-apply/checklist.md +94 -0
  10. package/templates/skills/kld-sdd/opsx-apply/reference.md +403 -0
  11. package/templates/skills/kld-sdd/opsx-archive/SKILL.md +21 -5
  12. package/templates/skills/kld-sdd/opsx-archive/checklist.md +33 -0
  13. package/templates/skills/kld-sdd/opsx-check/SKILL.md +28 -4
  14. package/templates/skills/kld-sdd/opsx-check/checklist.md +37 -0
  15. package/templates/skills/kld-sdd/opsx-design/SKILL.md +46 -50
  16. package/templates/skills/kld-sdd/opsx-design/checklist.md +46 -0
  17. package/templates/skills/kld-sdd/opsx-design/reference.md +44 -0
  18. package/templates/skills/kld-sdd/opsx-propose/SKILL.md +51 -95
  19. package/templates/skills/kld-sdd/opsx-propose/checklist.md +44 -0
  20. package/templates/skills/kld-sdd/opsx-propose/reference.md +94 -0
  21. package/templates/skills/kld-sdd/opsx-spec/SKILL.md +46 -50
  22. package/templates/skills/kld-sdd/opsx-spec/checklist.md +46 -0
  23. package/templates/skills/kld-sdd/opsx-spec/reference.md +49 -0
  24. package/templates/skills/kld-sdd/opsx-task/SKILL.md +42 -45
  25. package/templates/skills/kld-sdd/opsx-task/checklist.md +46 -0
  26. package/templates/skills/kld-sdd/opsx-task/reference.md +40 -0
  27. package/templates/skills/kld-sdd/opsx-test/SKILL.md +12 -0
package/lib/init.js CHANGED
@@ -350,23 +350,40 @@ function copyDirRendered(source, target, config, toolKey) {
350
350
  }
351
351
  }
352
352
 
353
+ /** OpenSpec init 生成的 4 个官方 slash command(KLD-SDD 使用 skills,不保留 command) */
354
+ const NATIVE_OPSX_CMDS_HYPHEN = [
355
+ 'opsx-propose.md',
356
+ 'opsx-apply.md',
357
+ 'opsx-archive.md',
358
+ 'opsx-explore.md'
359
+ ];
360
+ const NATIVE_OPSX_CMDS_SLASH = ['propose.md', 'apply.md', 'archive.md', 'explore.md'];
361
+
353
362
  /**
354
- * 清理原生 openspec 命令(opsx-*.md),避免与 opsx:* 命令混淆
355
- * 同时清理 openspec 遗留的 JSON 文件
356
- *
363
+ * SDD 自定义 command 标记(init 不部署;用户手动维护时保留)
364
+ */
365
+ function isSddCustomCommand(content) {
366
+ return content.includes('# SDD') || content.includes('skywalk-sdd/log.cjs');
367
+ }
368
+
369
+ /**
370
+ * 清理原生 openspec 命令,所有编辑器与 Cursor 行为一致:初始化时删除官方 command。
371
+ * 同时清理 openspec 遗留的 JSON 文件。
372
+ *
373
+ * 覆盖 TOOL_CONFIGS 中的全部编辑器(cursor/claude/codebuddy/qoder/opencode/kunlunzhima/workbuddy/codex),
374
+ * 与 Cursor 行为一致:初始化时删除 OpenSpec 官方 command,仅保留 skills。
375
+ *
357
376
  * openspec 对不同工具生成的目录结构:
358
377
  * - Cursor: commands/opsx-*.md(根目录,连字符)
359
- * - Claude/CodeBuddy/Qoder: commands/opsx/*.md(opsx 子目录)
360
- * - OpenCode: command/opsx-*.md(command 单数目录)
361
- *
378
+ * - Claude/CodeBuddy/Qoder 等: commands/opsx/*.md(opsx 子目录)
379
+ * - OpenCode: command/opsx-*.md(command 单数目录,整目录删除)
380
+ *
362
381
  * @param {string[]} selectedTools - 用户选择的编辑器列表
363
382
  */
364
383
  function cleanupNativeOpenspecCommands(selectedTools = Object.keys(TOOL_CONFIGS)) {
365
- console.log('🗑 正在清理原生 openspec 命令(避免与 opsx:* 命令混淆)...');
384
+ console.log('🗑 正在清理原生 openspec 命令(所有编辑器与 Cursor 一致,仅保留 skills)...');
366
385
 
367
386
  const cwd = process.cwd();
368
- // 原生 openspec 根目录命令文件(使用连字符,与 opsx:* 冒号命令混淆)
369
- const nativeCmds = ['opsx-propose.md', 'opsx-apply.md', 'opsx-archive.md', 'opsx-explore.md'];
370
387
 
371
388
  for (const toolKey of selectedTools) {
372
389
  const config = TOOL_CONFIGS[toolKey];
@@ -392,40 +409,41 @@ function cleanupNativeOpenspecCommands(selectedTools = Object.keys(TOOL_CONFIGS)
392
409
  console.log(` 🗑 ${config.name}: 删除 ${deletedJson} 个 JSON 命令文件`);
393
410
  }
394
411
 
395
- // 删除 commands/ 目录下的原生 opsx-* 命令(Cursor 和其他工具都可能有)
412
+ // 删除 commands/ 根目录下的原生 opsx-* 命令(Cursor 等,与 Claude 行为一致)
396
413
  let deletedCmds = 0;
397
- for (const cmd of nativeCmds) {
398
- const filePath = path.join(commandsDir, cmd);
399
- if (fs.existsSync(filePath)) {
400
- fs.unlinkSync(filePath);
401
- deletedCmds++;
402
- }
414
+ for (const file of fs.readdirSync(commandsDir)) {
415
+ const isNativeHyphen = NATIVE_OPSX_CMDS_HYPHEN.includes(file) || /^opsx-.*\.md$/i.test(file);
416
+ if (!isNativeHyphen) continue;
417
+ const filePath = path.join(commandsDir, file);
418
+ if (!fs.statSync(filePath).isFile()) continue;
419
+ const content = fs.readFileSync(filePath, 'utf-8');
420
+ if (isSddCustomCommand(content)) continue;
421
+ fs.unlinkSync(filePath);
422
+ deletedCmds++;
403
423
  }
404
424
  if (deletedCmds > 0) {
405
425
  console.log(` ✓ ${config.name}: 删除 ${deletedCmds} 个原生 opsx-* 命令(commands/)`);
406
426
  }
407
427
 
408
- // 删除 commands/opsx/ 子目录下的原生命令(Claude/CodeBuddy/Qoder
428
+ // 删除 commands/opsx/ 子目录下的官方命令(Claude/CodeBuddy/Qoder 等,与 Cursor 一致无条件删除)
409
429
  const opsxDir = path.join(commandsDir, 'opsx');
410
430
  if (fs.existsSync(opsxDir)) {
411
- const opsxFiles = fs.readdirSync(opsxDir);
412
431
  let deletedOpsxCmds = 0;
413
- // 只删除原生的 4 个命令,保留其他可能存在的文件
414
- const nativeOpsxCmds = ['propose.md', 'apply.md', 'archive.md', 'explore.md'];
415
- for (const file of nativeOpsxCmds) {
432
+ for (const file of NATIVE_OPSX_CMDS_SLASH) {
416
433
  const filePath = path.join(opsxDir, file);
417
- if (fs.existsSync(filePath)) {
418
- // 检查是否是原生命令(不含 YAML frontmatter 中的 SDD 标记)
419
- const content = fs.readFileSync(filePath, 'utf-8');
420
- if (!content.includes('# SDD') && !content.includes('OPSX:')) {
421
- fs.unlinkSync(filePath);
422
- deletedOpsxCmds++;
423
- }
424
- }
434
+ if (!fs.existsSync(filePath)) continue;
435
+ const content = fs.readFileSync(filePath, 'utf-8');
436
+ if (isSddCustomCommand(content)) continue;
437
+ fs.unlinkSync(filePath);
438
+ deletedOpsxCmds++;
425
439
  }
426
440
  if (deletedOpsxCmds > 0) {
427
441
  console.log(` ✓ ${config.name}: 删除 ${deletedOpsxCmds} 个原生命令(commands/opsx/)`);
428
442
  }
443
+ // 子目录为空则移除
444
+ if (fs.readdirSync(opsxDir).length === 0) {
445
+ fs.rmdirSync(opsxDir);
446
+ }
429
447
  }
430
448
  }
431
449
 
@@ -764,7 +782,7 @@ function initGlobalOverview() {
764
782
 
765
783
  /**
766
784
  * 部署 SDD Telemetry 数据目录
767
- * 创建本地数据目录用于存储事件和报告
785
+ * 创建本地数据目录用于存储事件和活跃状态(最终报告落 openspec/changes/<change>/reports/,不再写 skywalk-sdd/reports/)
768
786
  */
769
787
  function deployTelemetryDataDir() {
770
788
  console.log('📊 正在初始化 SDD Telemetry 数据目录...');
@@ -777,18 +795,13 @@ function deployTelemetryDataDir() {
777
795
  fs.mkdirSync(dataDir, { recursive: true });
778
796
  }
779
797
 
780
- // 创建本地数据目录(事件和报告存储)
798
+ // 创建本地数据目录(事件和活跃状态存储;报告落 change 子目录,不在此创建)
781
799
  const eventsDir = path.join(dataDir, 'events');
782
- const reportsDir = path.join(dataDir, 'reports');
783
800
  const stateDir = path.join(dataDir, 'state');
784
801
  if (!fs.existsSync(eventsDir)) {
785
802
  fs.mkdirSync(eventsDir, { recursive: true });
786
803
  console.log(' ✓ 创建 skywalk-sdd/events/ 数据目录');
787
804
  }
788
- if (!fs.existsSync(reportsDir)) {
789
- fs.mkdirSync(reportsDir, { recursive: true });
790
- console.log(' ✓ 创建 skywalk-sdd/reports/ 报告目录');
791
- }
792
805
  if (!fs.existsSync(stateDir)) {
793
806
  fs.mkdirSync(stateDir, { recursive: true });
794
807
  console.log(' ✓ 创建 skywalk-sdd/state/ 活跃阶段状态目录');
@@ -858,10 +871,14 @@ function installGitHookShim(cwd, hookName, scriptPath) {
858
871
  * 这些脚本只做质量门禁和 CI 补充记录,不替代 OPSX 主采集链路。
859
872
  */
860
873
  function deployQualityGateTemplates() {
861
- console.log('🧰 正在部署 SDD Git hooks / CI 兜底模板...');
862
-
863
874
  const pkgPath = getPackagePath();
864
875
  const cwd = process.cwd();
876
+ // U4: 非 Git 项目跳过 Git hooks / CI 兜底模板(按需部署,不污染非 Git 项目根)
877
+ if (!fs.existsSync(path.join(cwd, '.git'))) {
878
+ console.log('🧰 非 Git 项目,跳过 Git hooks / CI 兜底模板部署');
879
+ return false;
880
+ }
881
+ console.log('🧰 正在部署 SDD Git hooks / CI 兜底模板...');
865
882
  const dataDir = path.join(cwd, 'skywalk-sdd');
866
883
  const gitHooksTemplateDir = path.join(pkgPath, 'templates', 'git-hooks');
867
884
  const ciTemplateDir = path.join(pkgPath, 'templates', 'ci');
@@ -918,7 +935,6 @@ function updateGitignore() {
918
935
  '.workbuddy/commands/personal-*',
919
936
  '.agents/commands/personal-*',
920
937
  'skywalk-sdd/events/',
921
- 'skywalk-sdd/reports/',
922
938
  'skywalk-sdd/state/',
923
939
  '.worktrees/'
924
940
  ];
@@ -933,9 +949,8 @@ function updateGitignore() {
933
949
  .workbuddy/commands/personal-*
934
950
  .agents/commands/personal-*
935
951
 
936
- # SDD Telemetry 数据(本地度量,可选提交)
952
+ # SDD Telemetry 数据(本地度量,可选提交;最终报告落 openspec/changes/<change>/reports/ 随 change 提交)
937
953
  skywalk-sdd/events/
938
- skywalk-sdd/reports/
939
954
  skywalk-sdd/state/
940
955
 
941
956
  # SDD Apply 隔离 worktree(本地临时目录)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kld-sdd",
3
- "version": "2.4.18",
3
+ "version": "2.5.0",
4
4
  "description": "KLD SDD OpenSpec 项目初始化工具 - 一键部署 SDD skills",
5
5
  "main": "index.js",
6
6
  "bin": {