kld-sdd 2.6.11 → 2.6.13

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 (87) hide show
  1. package/README.md +20 -0
  2. package/kld-sdd-guide.html +429 -159
  3. package/lib/hook-gate-core.js +41 -5
  4. package/lib/init.js +101 -95
  5. package/package.json +5 -2
  6. package/skywalk-sdd/index.cjs +2668 -415
  7. package/skywalk-sdd/metrics-v3.cjs +1153 -0
  8. package/skywalk-sdd/ontology/archive-package.cjs +114 -3
  9. package/skywalk-sdd/ontology/resolve-spec-root.cjs +20 -80
  10. package/skywalk-sdd/ontology/working-artifacts.cjs +2 -1
  11. package/templates/hooks/claude/hooks/sdd-apply-test-gate.cjs +39 -9
  12. package/templates/hooks/claude/hooks/sdd-post-tool.cjs +87 -8
  13. package/templates/hooks/claude/hooks/sdd-prompt.cjs +25 -5
  14. package/templates/hooks/codebuddy/hooks/sdd-apply-test-gate.cjs +39 -9
  15. package/templates/hooks/codebuddy/hooks/sdd-post-tool.cjs +87 -9
  16. package/templates/hooks/codebuddy/hooks/sdd-prompt.cjs +24 -3
  17. package/templates/skills/kld-sdd/opsx-apply/SKILL.md +17 -16
  18. package/templates/skills/kld-sdd/opsx-apply/checklist.md +22 -22
  19. package/templates/skills/kld-sdd/opsx-apply/implementer-prompt.md +12 -12
  20. package/templates/skills/kld-sdd/opsx-apply/reference.md +43 -42
  21. package/templates/skills/kld-sdd/opsx-apply/worktree-setup.md +3 -3
  22. package/templates/skills/kld-sdd/opsx-archive/SKILL.md +58 -20
  23. package/templates/skills/kld-sdd/opsx-archive/checklist.md +6 -3
  24. package/templates/skills/kld-sdd/opsx-check/SKILL.md +30 -22
  25. package/templates/skills/kld-sdd/opsx-check/checklist.md +7 -6
  26. package/templates/skills/kld-sdd/opsx-design/SKILL.md +5 -5
  27. package/templates/skills/kld-sdd/opsx-design/checklist.md +1 -1
  28. package/templates/skills/kld-sdd/opsx-design/reference.md +2 -2
  29. package/templates/skills/kld-sdd/opsx-explore/SKILL.md +5 -5
  30. package/templates/skills/kld-sdd/opsx-propose/SKILL.md +20 -12
  31. package/templates/skills/kld-sdd/opsx-propose/reference.md +31 -5
  32. package/templates/skills/kld-sdd/opsx-rules/SKILL.md +3 -3
  33. package/templates/skills/kld-sdd/opsx-rules/reference.md +3 -1
  34. package/templates/skills/kld-sdd/opsx-spec/SKILL.md +6 -6
  35. package/templates/skills/kld-sdd/opsx-spec/checklist.md +1 -1
  36. package/templates/skills/kld-sdd/opsx-spec/reference.md +2 -2
  37. package/templates/skills/kld-sdd/opsx-task/SKILL.md +14 -14
  38. package/templates/skills/kld-sdd/opsx-task/checklist.md +3 -3
  39. package/templates/skills/kld-sdd/opsx-task/reference.md +5 -5
  40. package/templates/skills/kld-sdd/opsx-test/SKILL.md +11 -9
  41. package/templates/skills/kld-sdd/{opsx-tdd-anti-patterns → tdd-anti-patterns}/SKILL.md +4 -4
  42. package/templates/skills/kld-sdd/{opsx-tdd-anti-patterns → tdd-anti-patterns}/reference.md +4 -4
  43. package/templates/skills/kld-sdd/{opsx-tdd-core → tdd-core}/SKILL.md +13 -13
  44. package/templates/skills/kld-sdd/{opsx-tdd-core → tdd-core}/checklist.md +5 -5
  45. package/templates/skills/kld-sdd/{opsx-tdd-core → tdd-core}/reference.md +12 -12
  46. package/templates/skills/kld-sdd/{opsx-tdd-metrics → tdd-metrics}/SKILL.md +3 -3
  47. package/templates/skills/kld-sdd/{opsx-tdd-metrics → tdd-metrics}/checklist.md +2 -2
  48. package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/SKILL.md +2 -2
  49. package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/parameterized-testing.md +1 -1
  50. package/templates/skills/kld-sdd/{opsx-tdd-review → tdd-review}/SKILL.md +5 -5
  51. package/templates/skills/kld-sdd/{opsx-tdd-review → tdd-review}/checklist.md +5 -5
  52. package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/SKILL.md +3 -3
  53. package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/non-tdd-modules.md +1 -1
  54. package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/refactor-checklist.md +1 -1
  55. package/templates/skills/kld-sdd/tdd-rules/rules/test-skeleton-telemetry.md +19 -0
  56. package/templates/skills/kld-sdd/opsx-tdd-rules/rules/test-skeleton-telemetry.md +0 -19
  57. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/cause-effect-clarity.md +0 -0
  58. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/clean-test-data.md +0 -0
  59. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/existing-test-awareness.md +0 -0
  60. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/given-when-then.md +0 -0
  61. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/good-test-qualities.md +0 -0
  62. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/mock-boundary.md +0 -0
  63. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/naming-conventions.md +0 -0
  64. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/no-logic-in-tests.md +0 -0
  65. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/one-test-one-scenario.md +0 -0
  66. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/prefer-public-apis.md +0 -0
  67. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/general/test-behaviors-not-methods.md +0 -0
  68. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/java/argument-matching.md +0 -0
  69. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/java/controller-test-rules.md +0 -0
  70. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/java/domain-service-rules.md +0 -0
  71. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/java/java-test-template.md +0 -0
  72. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/java/json-serialization.md +0 -0
  73. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/java/logging-rules.md +0 -0
  74. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/post-generation/compilation-verification.md +0 -0
  75. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/post-generation/execution-verification.md +0 -0
  76. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/python/py-test-template.md +0 -0
  77. /package/templates/skills/kld-sdd/{opsx-tdd-quality → tdd-quality}/rules/typescript/ts-test-template.md +0 -0
  78. /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/controller-strategy.md +0 -0
  79. /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/dag-generation-rules.md +0 -0
  80. /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/des-step-annotation.md +0 -0
  81. /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/exception-path-coverage.md +0 -0
  82. /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/green-scope-declaration.md +0 -0
  83. /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/green-yagni-fence.md +0 -0
  84. /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/multi-validation-split.md +0 -0
  85. /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/task-type-definitions.md +0 -0
  86. /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/tdd-strategy-selection.md +0 -0
  87. /package/templates/skills/kld-sdd/{opsx-tdd-rules → tdd-rules}/rules/test-execution-gate.md +0 -0
@@ -50,16 +50,45 @@ function normalizeHookInput(input) {
50
50
  return normalized;
51
51
  }
52
52
 
53
+ function resolveSkywalkDir(dir) {
54
+ const root = path.resolve(dir || '.');
55
+ const direct = path.join(root, 'skywalk-sdd');
56
+ if (
57
+ fs.existsSync(path.join(direct, 'log.cjs'))
58
+ || fs.existsSync(path.join(direct, 'log.js'))
59
+ ) {
60
+ return direct;
61
+ }
62
+ const hintFile = path.join(root, '.sdd-spec-root');
63
+ if (fs.existsSync(hintFile)) {
64
+ const rel = String(fs.readFileSync(hintFile, 'utf8') || '')
65
+ .trim()
66
+ .split(/\r?\n/)[0]
67
+ .trim();
68
+ if (rel) {
69
+ const nested = path.join(root, rel, 'skywalk-sdd');
70
+ if (
71
+ fs.existsSync(path.join(nested, 'log.cjs'))
72
+ || fs.existsSync(path.join(nested, 'log.js'))
73
+ ) {
74
+ return nested;
75
+ }
76
+ }
77
+ }
78
+ return null;
79
+ }
80
+
53
81
  function hasTelemetryCli(dir) {
54
- return fs.existsSync(path.join(dir, 'skywalk-sdd', 'log.cjs')) ||
55
- fs.existsSync(path.join(dir, 'skywalk-sdd', 'log.js'));
82
+ return Boolean(resolveSkywalkDir(dir));
56
83
  }
57
84
 
58
85
  function findProjectRoot(startDir) {
59
86
  if (!startDir) return '';
60
87
  let current = path.resolve(startDir);
61
88
  for (let i = 0; i < 25; i++) {
62
- if (hasTelemetryCli(current)) return current;
89
+ if (hasTelemetryCli(current) || fs.existsSync(path.join(current, '.sdd-spec-root'))) {
90
+ return current;
91
+ }
63
92
  const parent = path.dirname(current);
64
93
  if (parent === current) return '';
65
94
  current = parent;
@@ -96,7 +125,10 @@ function safeChangeName(name) {
96
125
  }
97
126
 
98
127
  function findActiveApplyStage(projectRoot) {
99
- const stateDir = path.join(projectRoot, 'skywalk-sdd', 'state');
128
+ const skywalk = resolveSkywalkDir(projectRoot);
129
+ const stateDir = skywalk
130
+ ? path.join(skywalk, 'state')
131
+ : path.join(projectRoot, 'skywalk-sdd', 'state');
100
132
  if (!fs.existsSync(stateDir)) return null;
101
133
 
102
134
  let latest = null;
@@ -118,7 +150,10 @@ function findActiveApplyStage(projectRoot) {
118
150
 
119
151
  function hasCompletedCheck(projectRoot, changeName) {
120
152
  const safeName = safeChangeName(changeName);
121
- const eventsChangeDir = path.join(projectRoot, 'skywalk-sdd', 'events', safeName);
153
+ const skywalk = resolveSkywalkDir(projectRoot);
154
+ const eventsChangeDir = skywalk
155
+ ? path.join(skywalk, 'events', safeName)
156
+ : path.join(projectRoot, 'skywalk-sdd', 'events', safeName);
122
157
  if (!fs.existsSync(eventsChangeDir)) return false;
123
158
 
124
159
  const jsonlFiles = fs.readdirSync(eventsChangeDir)
@@ -316,6 +351,7 @@ module.exports = {
316
351
  readStdin,
317
352
  parseHookInput,
318
353
  normalizeHookInput,
354
+ resolveSkywalkDir,
319
355
  hasTelemetryCli,
320
356
  findProjectRoot,
321
357
  getProjectRoot,
package/lib/init.js CHANGED
@@ -822,6 +822,35 @@ function deployTelemetryDataDir(targetCwd = process.cwd()) {
822
822
  console.log(` ⚠️ Telemetry CLI 源文件缺失: ${telemetrySrc}`);
823
823
  }
824
824
 
825
+ const metricsV3Src = path.join(pkgPath, 'skywalk-sdd', 'metrics-v3.cjs');
826
+ const metricsV3Dst = path.join(dataDir, 'metrics-v3.cjs');
827
+ if (fs.existsSync(metricsV3Src)) {
828
+ fs.copyFileSync(metricsV3Src, metricsV3Dst);
829
+ console.log(' ✓ 部署 skywalk-sdd/metrics-v3.cjs(V3 指标运行时)');
830
+ } else {
831
+ console.log(` ⚠️ V3 指标模块源文件缺失: ${metricsV3Src}(report/metrics 可能无法加载 metrics-v3)`);
832
+ }
833
+
834
+ const runtimeMetadataSrc = path.join(pkgPath, 'skywalk-sdd', 'runtime-metadata.cjs');
835
+ const runtimeMetadataDst = path.join(dataDir, 'runtime-metadata.cjs');
836
+ if (fs.existsSync(runtimeMetadataSrc)) {
837
+ fs.copyFileSync(runtimeMetadataSrc, runtimeMetadataDst);
838
+ const packageVersion = require(path.join(pkgPath, 'package.json')).version;
839
+ fs.writeFileSync(path.join(dataDir, 'runtime-metadata.json'), `${JSON.stringify({
840
+ schema_version: 'kld-sdd-runtime-metadata/v1',
841
+ package_name: '@hunterzheng/kld-sdd',
842
+ version: packageVersion,
843
+ }, null, 2)}\n`, 'utf8');
844
+ console.log(` ✓ 部署 kld-sdd 运行时身份(${packageVersion})`);
845
+ }
846
+
847
+ const reportingSrc = path.join(pkgPath, 'skywalk-sdd', 'reporting');
848
+ const reportingDst = path.join(dataDir, 'reporting');
849
+ if (fs.existsSync(reportingSrc)) {
850
+ copyDir(reportingSrc, reportingDst);
851
+ console.log(' ✓ 部署 skywalk-sdd/reporting/(V3 单次变更报告)');
852
+ }
853
+
825
854
  const contextClientSrc = path.join(pkgPath, 'skywalk-sdd', 'context-client.cjs');
826
855
  const contextClientDst = path.join(dataDir, 'context-client.cjs');
827
856
  if (fs.existsSync(contextClientSrc)) {
@@ -873,16 +902,20 @@ function installGitHookShim(cwd, hookName, scriptPath, options = {}) {
873
902
  // Git 把 commit-msg 文件路径放在 $1;--project 等额外参数必须跟在 "$@" 后面,
874
903
  // 否则 trailer 脚本会把 --project=… 误当成 message 文件(argv[2])。
875
904
  const extraArgs = options.extraArgs || '';
876
- const nodeLine = extraArgs
877
- ? ` node "${scriptPath}" "$@" ${extraArgs}`
878
- : ` node "${scriptPath}" "$@"`;
905
+ // Windows 反斜杠路径在 #!/bin/sh(Git for Windows MSYS2)中不被 [ -f ] 和 node 识别,
906
+ // 统一转为正斜杠,确保 hook shim 跨平台可执行。
907
+ const shScriptPath = scriptPath.replace(/\\/g, '/');
908
+ const shExtraArgs = extraArgs.replace(/\\/g, '/');
909
+ const nodeLine = shExtraArgs
910
+ ? ` node "${shScriptPath}" "$@" ${shExtraArgs}`
911
+ : ` node "${shScriptPath}" "$@"`;
879
912
  const shim = [
880
913
  '#!/bin/sh',
881
914
  `# ${marker}`,
882
- `if [ -f "${scriptPath}" ]; then`,
915
+ `if [ -f "${shScriptPath}" ]; then`,
883
916
  nodeLine,
884
917
  'else',
885
- ` echo "[kld-sdd] Hook 脚本不存在: ${scriptPath}" >&2`,
918
+ ` echo "[kld-sdd] Hook 脚本不存在: ${shScriptPath}" >&2`,
886
919
  ' exit 1',
887
920
  'fi',
888
921
  '',
@@ -892,7 +925,7 @@ function installGitHookShim(cwd, hookName, scriptPath, options = {}) {
892
925
  const existing = fs.readFileSync(hookPath, 'utf8');
893
926
  if (existing.includes(marker)) {
894
927
  // 已是 SDD shim:若指向路径变化(如改为引用 spec 仓),允许覆盖升级
895
- if (existing.includes(scriptPath) && (!extraArgs || existing.includes(extraArgs.trim()))) {
928
+ if (existing.includes(shScriptPath) && (!shExtraArgs || existing.includes(shExtraArgs.trim()))) {
896
929
  console.log(` ✓ .git/hooks/${hookName} 已包含 SDD 质量门禁`);
897
930
  return;
898
931
  }
@@ -951,7 +984,7 @@ function installCodeRepoCommitMsgHook(codeRepoRoot, specRepoRoot, options = {})
951
984
  return false;
952
985
  }
953
986
  installGitHookShim(cwd, 'commit-msg', ensured.scriptPath, {
954
- extraArgs: `--project=${cwd}`,
987
+ extraArgs: `"--project=${cwd}"`,
955
988
  });
956
989
  return true;
957
990
  }
@@ -1208,94 +1241,62 @@ function writeSddSpecRootHint(anchorDir, specPackageAbs) {
1208
1241
  }
1209
1242
 
1210
1243
  /**
1211
- * 工作区 / 单仓 Git 根的 skywalk-sdd/ 改为指向包裹包内唯一实体(符号链接)。
1212
- * skills 仍可用 `node skywalk-sdd/log.cjs`,实体文件只在 *-sdd-specs/skywalk-sdd/。
1213
- * 链接失败时回退为薄转发脚本(不复制 ontology)。
1244
+ * 移除工作区 / Git 根上多余的 skywalk-sdd(符号链接或旧完整副本)。
1245
+ * 唯一实体只保留在 *-sdd-specs/skywalk-sdd/。
1246
+ *
1247
+ * 触发条件(满足其一即可):
1248
+ * - 存在 .sdd-spec-root
1249
+ * - 同级已有 *-sdd-specs/skywalk-sdd(多仓/单仓包裹包)
1214
1250
  */
1215
- function deploySkywalkForwarders(anchorDir, specPackageAbs) {
1216
- const anchor = path.resolve(anchorDir);
1217
- const specAbs = path.resolve(specPackageAbs);
1218
- const relSpec = path.relative(anchor, specAbs).replace(/\\/g, '/');
1219
- const skywalkDir = path.join(anchor, 'skywalk-sdd');
1220
- const targetSkywalk = path.join(specAbs, 'skywalk-sdd');
1221
- fs.mkdirSync(path.join(skywalkDir, 'ontology'), { recursive: true });
1222
-
1223
- const writeForwarder = (destRel, targetAbsPath) => {
1224
- const dest = path.join(skywalkDir, destRel);
1225
- fs.mkdirSync(path.dirname(dest), { recursive: true });
1226
- const relFromDestDir = path.relative(path.dirname(dest), targetAbsPath).replace(/\\/g, '/');
1227
- const body = [
1228
- '#!/usr/bin/env node',
1229
- "'use strict';",
1230
- "const { spawnSync } = require('child_process');",
1231
- "const path = require('path');",
1232
- `const target = path.resolve(__dirname, ${JSON.stringify(relFromDestDir)});`,
1233
- 'const r = spawnSync(process.execPath, [target, ...process.argv.slice(2)], { stdio: \'inherit\' });',
1234
- 'process.exit(r.status === null ? 1 : r.status);',
1235
- '',
1236
- ].join('\n');
1237
- fs.writeFileSync(dest, body, 'utf8');
1238
- };
1239
-
1240
- const entries = [
1241
- 'log.cjs',
1242
- 'openspec-shim.cjs',
1243
- 'context-client.cjs',
1244
- 'apply-worktree-finish.cjs',
1245
- ];
1246
- for (const name of entries) {
1247
- writeForwarder(name, path.join(targetSkywalk, name));
1248
- }
1249
- writeForwarder(path.join('ontology', 'cli.cjs'), path.join(targetSkywalk, 'ontology', 'cli.cjs'));
1250
- console.log(` ✓ skywalk-sdd/ 转发脚本 → ${relSpec}/skywalk-sdd/(实体在包裹包)`);
1251
- return { ok: true, mode: 'forwarders', relSpec };
1252
- }
1253
-
1254
- function linkSkywalkToSpecPackage(anchorDir, specPackageAbs) {
1251
+ function removeOuterSkywalk(anchorDir) {
1255
1252
  const anchor = path.resolve(anchorDir);
1256
- const targetAbs = path.join(path.resolve(specPackageAbs), 'skywalk-sdd');
1257
- const linkAbs = path.join(anchor, 'skywalk-sdd');
1258
-
1259
- if (!fs.existsSync(targetAbs)) {
1260
- console.log(' ⚠️ 包裹包尚无 skywalk-sdd/,跳过工作区链接');
1261
- return { ok: false, message: 'spec skywalk-sdd missing' };
1253
+ const outer = path.join(anchor, 'skywalk-sdd');
1254
+ let st = null;
1255
+ try {
1256
+ st = fs.lstatSync(outer);
1257
+ } catch (err) {
1258
+ if (err && err.code === 'ENOENT') return { ok: true, removed: false };
1259
+ throw err;
1262
1260
  }
1263
1261
 
1262
+ const hint = path.join(anchor, '.sdd-spec-root');
1263
+ const hasHint = fs.existsSync(hint);
1264
+ let hasNestedEntity = false;
1264
1265
  try {
1265
- let st = null;
1266
- try {
1267
- st = fs.lstatSync(linkAbs);
1268
- } catch (err) {
1269
- if (!err || err.code !== 'ENOENT') throw err;
1270
- }
1271
- if (st) {
1272
- if (st.isSymbolicLink()) {
1273
- const current = fs.realpathSync(linkAbs);
1274
- if (current === fs.realpathSync(targetAbs)) {
1275
- console.log(' ✓ skywalk-sdd/ 已链接到包裹包(唯一实体)');
1276
- return { ok: true, mode: 'symlink', linkAbs, targetAbs };
1277
- }
1278
- fs.unlinkSync(linkAbs);
1279
- } else if (st.isDirectory()) {
1280
- // 旧完整副本 → 删除后改链接(权威数据在包裹包)
1281
- fs.rmSync(linkAbs, { recursive: true, force: true });
1282
- } else {
1283
- fs.unlinkSync(linkAbs);
1266
+ for (const name of fs.readdirSync(anchor)) {
1267
+ if (!/-sdd-specs$/i.test(name)) continue;
1268
+ const entity = path.join(anchor, name, 'skywalk-sdd', 'log.cjs');
1269
+ if (fs.existsSync(entity)) {
1270
+ hasNestedEntity = true;
1271
+ break;
1284
1272
  }
1285
1273
  }
1286
- } catch (err) {
1287
- console.log(` ⚠️ 清理旧 skywalk-sdd/ 失败: ${err.message}`);
1288
- return deploySkywalkForwarders(anchor, specPackageAbs);
1274
+ } catch {
1275
+ // ignore
1276
+ }
1277
+
1278
+ if (!hasHint && !hasNestedEntity) {
1279
+ // 无包裹包迹象时不贸然删除(可能是旧平铺布局)
1280
+ return { ok: true, removed: false, skipped: true };
1289
1281
  }
1290
1282
 
1291
- const rel = path.relative(anchor, targetAbs);
1292
1283
  try {
1293
- fs.symlinkSync(rel, linkAbs, process.platform === 'win32' ? 'junction' : 'dir');
1294
- console.log(` ✓ skywalk-sdd/ → ${rel.replace(/\\/g, '/')}(唯一实体在包裹包)`);
1295
- return { ok: true, mode: 'symlink', linkAbs, targetAbs, rel };
1284
+ if (st.isSymbolicLink()) {
1285
+ fs.unlinkSync(outer);
1286
+ console.log(' ✓ 已移除 Git/工作区根多余的 skywalk-sdd 符号链接(实体在包裹包)');
1287
+ return { ok: true, removed: true, mode: 'symlink' };
1288
+ }
1289
+ if (st.isDirectory()) {
1290
+ fs.rmSync(outer, { recursive: true, force: true });
1291
+ console.log(' ✓ 已移除 Git/工作区根多余的 skywalk-sdd/(实体在包裹包)');
1292
+ return { ok: true, removed: true, mode: 'directory' };
1293
+ }
1294
+ fs.unlinkSync(outer);
1295
+ console.log(' ✓ 已移除 Git/工作区根多余的 skywalk-sdd');
1296
+ return { ok: true, removed: true, mode: 'file' };
1296
1297
  } catch (err) {
1297
- console.log(` ⚠️ 符号链接失败 (${err.message}),回退为转发脚本`);
1298
- return deploySkywalkForwarders(anchor, specPackageAbs);
1298
+ console.log(` ⚠️ 未能移除根目录 skywalk-sdd/: ${err.message}`);
1299
+ return { ok: false, message: err.message };
1299
1300
  }
1300
1301
  }
1301
1302
 
@@ -1519,8 +1520,7 @@ async function main() {
1519
1520
  asIndependentGit: true,
1520
1521
  });
1521
1522
  writeSddSpecRootHint(workspaceRoot, specPackage.abs);
1522
- // 工作区不复制 skywalk-sdd 实体,只链到包裹包(skills 路径仍可用)
1523
- linkSkywalkToSpecPackage(workspaceRoot, specPackage.abs);
1523
+ removeOuterSkywalk(workspaceRoot);
1524
1524
 
1525
1525
  // 重新探测,刷新 layout.specRepo / codeRepos
1526
1526
  layout = workspaceLayout.detectWorkspaceLayout(workspaceRoot);
@@ -1549,11 +1549,11 @@ async function main() {
1549
1549
  asIndependentGit: false,
1550
1550
  });
1551
1551
  writeSddSpecRootHint(workspaceRoot, specPackage.abs);
1552
- linkSkywalkToSpecPackage(workspaceRoot, specPackage.abs);
1552
+ removeOuterSkywalk(workspaceRoot);
1553
1553
 
1554
1554
  deployMonoSddYaml(workspaceRoot, { specPathRelative: specPackage.name });
1555
- // Hook 装在 Git 根;经 symlink 写入包裹包内的 git-hooks
1556
- deployQualityGateTemplates(workspaceRoot, { mode: 'commit-msg-only' });
1555
+ // Hook 装在 Git 根,脚本在包裹包内(Git 根不落 skywalk-sdd/)
1556
+ installCodeRepoCommitMsgHook(workspaceRoot, specPackage.abs);
1557
1557
  const linked = sddConfig.setSpecPath(workspaceRoot, specPackage.abs, {
1558
1558
  allowNestedPackage: true,
1559
1559
  });
@@ -1571,7 +1571,7 @@ async function main() {
1571
1571
  asIndependentGit: false,
1572
1572
  });
1573
1573
  writeSddSpecRootHint(workspaceRoot, specPackage.abs);
1574
- linkSkywalkToSpecPackage(workspaceRoot, specPackage.abs);
1574
+ removeOuterSkywalk(workspaceRoot);
1575
1575
  deployDotSddYaml(workspaceRoot);
1576
1576
  }
1577
1577
  }
@@ -1603,6 +1603,12 @@ async function main() {
1603
1603
  // 7. 更新工作目录 .gitignore
1604
1604
  updateGitignore();
1605
1605
 
1606
+ // 8. 收尾:无论前面是否走过 symlink/旧副本路径,强制清掉工作区/Git 根多余 skywalk-sdd/
1607
+ if (specPackage && fs.existsSync(specPackage.abs)) {
1608
+ writeSddSpecRootHint(workspaceRoot, specPackage.abs);
1609
+ }
1610
+ removeOuterSkywalk(workspaceRoot);
1611
+
1606
1612
  const selectedNames = selectedTools.map(t => TOOL_CONFIGS[t].name).join(', ');
1607
1613
 
1608
1614
  console.log();
@@ -1616,7 +1622,8 @@ async function main() {
1616
1622
  console.log('工作区部署结果:');
1617
1623
  console.log(' 🎯 工作目录 .*/skills/opsx-*/ # skills 只装这一份');
1618
1624
  console.log(' 📋 .sdd-workspace.yaml # 子仓编排清单');
1619
- console.log(' 🔗 skywalk-sdd/ *-sdd-specs/skywalk-sdd/ # 符号链接,实体只在包裹包');
1625
+ console.log(' 📌 .sdd-spec-root # 指向包裹包;CLI 用 $(cat .sdd-spec-root)/skywalk-sdd/…');
1626
+ console.log(' ✗ 工作区根无 skywalk-sdd/ # 实体只在包裹包内');
1620
1627
  if (specPackage) {
1621
1628
  console.log(` 📄 ${specPackage.name}/ # 统一 SDD 包裹包(独立 Git)`);
1622
1629
  console.log(' modules.yaml / sdd.config.yaml / openspec / openspec-templates / skywalk-sdd');
@@ -1635,8 +1642,8 @@ async function main() {
1635
1642
  console.log(` 📄 ${specPackage.name}/ # 统一 SDD 包裹包(仓内子目录)`);
1636
1643
  console.log(' modules.yaml / sdd.config.yaml / openspec / openspec-templates / skywalk-sdd');
1637
1644
  }
1638
- console.log(' 🔗 .sdd.yaml (layout: mono) + commit-msg Hook');
1639
- console.log(' 🔗 skywalk-sdd/*-sdd-specs/skywalk-sdd/ # 符号链接,实体只在包裹包');
1645
+ console.log(' 🔗 .sdd.yaml (layout: mono) + commit-msg Hook(脚本在包裹包)');
1646
+ console.log(' 📌 .sdd-spec-root包裹包;无根目录 skywalk-sdd/');
1640
1647
  }
1641
1648
  if (selectedTools.includes('kunlunzhima')) {
1642
1649
  console.log(' 📎 .kunlunzhima/commands/opsx/ # KunlunZhima OPSX command bridge(11 个)');
@@ -1712,8 +1719,7 @@ module.exports = {
1712
1719
  deployMonoSddYaml,
1713
1720
  populateSpecPackage,
1714
1721
  writeSddSpecRootHint,
1715
- linkSkywalkToSpecPackage,
1716
- deploySkywalkForwarders,
1722
+ removeOuterSkywalk,
1717
1723
  deployQualityGateTemplates,
1718
1724
  deployTelemetryDataDir,
1719
1725
  attachCodeRepoLite,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kld-sdd",
3
- "version": "2.6.11",
3
+ "version": "2.6.13",
4
4
  "description": "KLD SDD OpenSpec 项目初始化工具 - 一键部署 SDD skills",
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": "node test/external-key.cjs && node test/change-key.cjs && node test/modules-and-sdd-config.cjs && node test/active-changes.cjs && node test/hook-layouts.cjs && node test/spec-package-init.cjs && node test/workspace-layout.cjs && node test/ontology-release-blockers.cjs && node test/ontology-semantic-core.cjs && node test/ontology-identity-versioning.cjs && node test/ontology-identity-continuity.cjs && node test/ontology-state-transaction.cjs && node test/ontology-process-concurrency.cjs && node test/ontology-observer-convergence.cjs && node test/ontology-working-runtime.cjs && node test/ontology-stage-materialization.cjs && node test/ontology-template-contract.cjs && node test/ontology-cli-archive.cjs && node test/archive-package-producer.cjs && node test/validate-skills-bundle.cjs && node test/tool-profiles.cjs && node test/settings-merge.cjs && node test/command-bridge.cjs && node test/codebuddy-hooks.cjs && node test/skill-content-contract.cjs && node test/init-agent-profiles.cjs"
11
+ "test": "node test/external-key.cjs && node test/change-key.cjs && node test/modules-and-sdd-config.cjs && node test/active-changes.cjs && node test/hook-layouts.cjs && node test/spec-package-init.cjs && node test/workspace-layout.cjs && node test/ontology-release-blockers.cjs && node test/ontology-semantic-core.cjs && node test/ontology-identity-versioning.cjs && node test/ontology-identity-continuity.cjs && node test/ontology-state-transaction.cjs && node test/ontology-process-concurrency.cjs && node test/ontology-observer-convergence.cjs && node test/ontology-working-runtime.cjs && node test/ontology-stage-materialization.cjs && node test/ontology-template-contract.cjs && node test/ontology-cli-archive.cjs && node test/archive-package-producer.cjs && node --test test/change-report-correctness.cjs test/report-metrics-correctness.cjs test/change-report-model-ui.cjs && node test/validate-skills-bundle.cjs && node test/tool-profiles.cjs && node test/settings-merge.cjs && node test/command-bridge.cjs && node test/codebuddy-hooks.cjs && node test/skill-content-contract.cjs && node test/init-agent-profiles.cjs && node test/init-report-runtime.cjs"
12
12
  },
13
13
  "keywords": [
14
14
  "kld",
@@ -32,6 +32,9 @@
32
32
  "templates/",
33
33
  "kld-sdd-guide.html",
34
34
  "skywalk-sdd/index.cjs",
35
+ "skywalk-sdd/metrics-v3.cjs",
36
+ "skywalk-sdd/runtime-metadata.cjs",
37
+ "skywalk-sdd/reporting/",
35
38
  "skywalk-sdd/context-client.cjs",
36
39
  "skywalk-sdd/ontology/",
37
40
  "skywalk-sdd/apply-worktree-finish.cjs",