kld-sdd 2.6.17 → 2.7.3

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.
@@ -50,6 +50,10 @@ if (args[0] === 'log') {
50
50
  process.exit(1);
51
51
  }
52
52
  console.log(`✅ sync-repos 完成,接入 ${result.attached.length} 个代码仓`);
53
+ } else if (args[0] === 'auth') {
54
+ // 登录体系已随 2.7.0 移除:仅保留 deprecation 提示,不进入 init 向导
55
+ console.log('ℹ️ auth 子命令已于 2.7.0 移除:使用统计按本机 git 身份(user.name/user.email)上报,无需绑定。');
56
+ process.exit(0);
53
57
  } else {
54
58
  // 正常初始化模式
55
59
  const { main } = require('../lib/init');
@@ -1116,6 +1116,28 @@ node skywalk-sdd/log.cjs semantic-observe --project=. --change=add-user-auth">
1116
1116
  <div class="metric reveal"><div class="key">P-H</div><h4>Telemetry 健康度</h4><div class="value">doctor health × 100</div><p>与 P-C 阶段闭合率一起判断数据是否足够可信。</p></div>
1117
1117
  </div>
1118
1118
 
1119
+ <p class="lead reveal" style="margin-top:32px"><b>KB 实时进度同步(Progress Agent)</b>:本地 JSONL 事实经 canonical CloudEvent 适配后发送到 Engineering KB;仅 durable ACK(accepted / duplicate_accepted)且仍 active 的 Run 才会发送 heartbeat。Agent 是<strong>事件触发、有限会话</strong>的用户级单实例——<strong>不再默认开机自启动/长期驻留</strong>:每条新 SDD 事实 durable append 后自动拉起(<code>ensureAgentRunning</code> 按 <code>agent.lock</code> 探测避免重复 spawn),按 5/15/10 分钟三档租约(clean idle / active silence / retry budget)自行退出;旧版开机自启动会在 init/config 时被幂等迁移移除。</p>
1120
+ <div class="codeblock reveal">
1121
+ <button type="button" class="copy" data-copy="# 配置 KB(需 progress:write scope)后 init 会自动 register,best-effort 迁移旧自启动 + 拉起当前会话 Agent
1122
+ npx kld-sdd --tool codex
1123
+
1124
+ # 手动单次同步(强制 batch → ACK 落盘 → heartbeat,不进入有限会话循环)
1125
+ node skywalk-sdd/progress/agent-main.cjs --once --user-state-root=%LOCALAPPDATA%/kld-sdd/progress
1126
+
1127
+ # 手动触发有限会话(正常由新 SDD 事实自动拉起,一般无需手动执行)
1128
+ node skywalk-sdd/progress/agent-main.cjs --user-state-root=%LOCALAPPDATA%/kld-sdd/progress
1129
+
1130
+ # 查看自启动/迁移状态(degraded 时可再次 config/init 恢复)
1131
+ node -e &quot;require('./skywalk-sdd/progress/autostart.cjs').statusAutostart({testMode:true}).then(console.log)&quot;">复制</button>
1132
+ <pre><span class="k-c"># apiBase 必须包含 /api,例如 http://host:8090/api</span>
1133
+ <span class="k-c"># 状态目录:%LOCALAPPDATA%/kld-sdd/progress(Windows)或 ~/.local/state/kld-sdd/progress</span>
1134
+ <span class="k-p">➜</span> <span class="k-s">node</span> skywalk-sdd/progress/agent-main.cjs <span class="k-f">--once</span> --user-state-root=&lt;USER_STATE_ROOT&gt;
1135
+ <span class="k-p">➜</span> <span class="k-s">node</span> skywalk-sdd/progress/agent-main.cjs --user-state-root=&lt;USER_STATE_ROOT&gt; <span class="k-o">(triggered finite session)</span>
1136
+ <span class="k-p">➜</span> <span class="k-s">node</span> skywalk-sdd/progress/autostart.cjs <span class="k-f">status</span> <span class="k-o">(platform adapter)</span></pre>
1137
+ </div>
1138
+ <div class="callout reveal"><b>破坏性对齐(CloudEvent 出站):</b>出站 envelope 已与 KB canonical 契约对齐——<code>type</code> 为 <code>com.kunlunzhima.sdd.&lt;event_kind&gt;.v1</code>,<code>source</code> 为 <code>urn:kld-sdd:installation:…:workspace:…</code>,payload 含 <code>seq</code>/<code>change_key</code>,哈希模式 <code>canonical-json-v1</code>。旧版自定义 type/source/外壳<strong>不再兼容</strong>;升级后请以 KB schema/fixture 为准,勿依赖旧出站字段。</div>
1139
+ <div class="callout reveal"><b>排障:</b>401/403 或 rejected_scope → 检查 API Key 是否含 <code>progress:write</code>;schema 拒绝只隔离单条事件;<code>autostart=degraded</code> 不破坏 kb-state,可重跑 config/init;heartbeat 不含 <code>active</code> 字段,资格来自 durable ACK + 本地 active Run 交集;可选 <code>agent_state</code>(<code>active</code>/<code>sleeping</code>/<code>terminal</code>)仅在会话启动/唤醒与计划退出前 best-effort 上报。</div>
1140
+
1119
1141
  <p class="lead reveal" style="margin-top:32px"><b>事件与单变更报告</b>:事件落 <code>skywalk-sdd/events/</code>、活跃状态落 <code>skywalk-sdd/state/</code>(均 gitignored);归档报告落 <code>openspec/changes/archive/&lt;date&gt;-&lt;name&gt;/reports/</code>。</p>
1120
1142
  <div class="codeblock reveal">
1121
1143
  <button type="button" class="copy" data-copy="node skywalk-sdd/log.cjs start --command=propose --project=. --change=add-user-auth --agent=codex --source=opsx-skill --session-id=sess_001
package/lib/init.js CHANGED
@@ -31,10 +31,24 @@ const { deployCodebuddyHookPack } = require('./deploy-codebuddy-hooks');
31
31
  const workspaceLayout = require('./workspace-layout');
32
32
  const sddConfig = require('../skywalk-sdd/ontology/sdd-config.cjs');
33
33
 
34
- const rl = readline.createInterface({
35
- input: process.stdin,
36
- output: process.stdout
37
- });
34
+ let rl = null;
35
+
36
+ function getRl() {
37
+ if (!rl) {
38
+ rl = readline.createInterface({
39
+ input: process.stdin,
40
+ output: process.stdout,
41
+ });
42
+ }
43
+ return rl;
44
+ }
45
+
46
+ function closeRl() {
47
+ if (rl) {
48
+ rl.close();
49
+ rl = null;
50
+ }
51
+ }
38
52
 
39
53
  // 工具类型配置(路径字段从 TOOL_PROFILES 派生,避免双份漂移)
40
54
  const TOOL_CONFIGS = buildToolConfigs();
@@ -44,10 +58,30 @@ const TOOL_CONFIGS = buildToolConfigs();
44
58
  */
45
59
  function ask(question) {
46
60
  return new Promise(resolve => {
47
- rl.question(question, answer => resolve(answer.trim()));
61
+ getRl().question(question, answer => resolve(answer.trim()));
48
62
  });
49
63
  }
50
64
 
65
+ /**
66
+ * 输出详细度判定(惰性读取,便于测试在运行期注入)
67
+ * 触发条件:argv 含 --verbose,或 KLD_SDD_VERBOSE 为 1/true(大小写不敏感)
68
+ * 注意:-v 已被 --version 占用,不复用
69
+ */
70
+ function isVerbose() {
71
+ if (process.argv.includes('--verbose')) return true;
72
+ const env = process.env.KLD_SDD_VERBOSE;
73
+ return typeof env === 'string' && /^(1|true)$/i.test(env);
74
+ }
75
+
76
+ /**
77
+ * verbose 模式下的明细日志;默认模式静默
78
+ */
79
+ function vlog(...args) {
80
+ if (isVerbose()) {
81
+ console.log(...args);
82
+ }
83
+ }
84
+
51
85
  /**
52
86
  * 从命令行参数读取指定编辑器
53
87
  * 支持:--tool codex、--tool=codex、--tool cursor,codex、--tool all
@@ -496,7 +530,7 @@ function deployOpsxSkills(selectedTools = Object.keys(TOOL_CONFIGS)) {
496
530
 
497
531
  if (fs.existsSync(sourceDir)) {
498
532
  copyDirRendered(sourceDir, targetDir, config, toolKey);
499
- console.log(` ✓ ${skillDir}/`);
533
+ vlog(` ✓ ${skillDir}/`);
500
534
  }
501
535
  }
502
536
  }
@@ -706,14 +740,14 @@ function copyTemplatesToProject(cwd = process.cwd()) {
706
740
  }
707
741
 
708
742
  copyDir(templatePath, targetDir);
709
-
710
- console.log(`✅ 标准文档模版已复制到: ${targetDir}`);
711
- console.log(' 包含文件:');
743
+
712
744
  const files = fs.readdirSync(targetDir);
745
+ console.log(`✅ 标准文档模版已复制到: ${targetDir}(${files.length} 个文件)`);
746
+ vlog(' 包含文件:');
713
747
  files.forEach(file => {
714
- console.log(` - ${file}`);
748
+ vlog(` - ${file}`);
715
749
  });
716
-
750
+
717
751
  return true;
718
752
  }
719
753
 
@@ -803,13 +837,15 @@ function deployTelemetryDataDir(targetCwd = process.cwd()) {
803
837
  // 创建本地数据目录(事件和活跃状态存储;报告落 change 子目录,不在此创建)
804
838
  const eventsDir = path.join(dataDir, 'events');
805
839
  const stateDir = path.join(dataDir, 'state');
840
+ // 成功部署的模块计数(用于默认模式单行摘要)
841
+ let deployedModules = 0;
806
842
  if (!fs.existsSync(eventsDir)) {
807
843
  fs.mkdirSync(eventsDir, { recursive: true });
808
- console.log(' ✓ 创建 skywalk-sdd/events/ 数据目录');
844
+ vlog(' ✓ 创建 skywalk-sdd/events/ 数据目录');
809
845
  }
810
846
  if (!fs.existsSync(stateDir)) {
811
847
  fs.mkdirSync(stateDir, { recursive: true });
812
- console.log(' ✓ 创建 skywalk-sdd/state/ 活跃阶段状态目录');
848
+ vlog(' ✓ 创建 skywalk-sdd/state/ 活跃阶段状态目录');
813
849
  }
814
850
 
815
851
  // 部署项目内 Telemetry CLI(不依赖 npm 发布)
@@ -817,7 +853,8 @@ function deployTelemetryDataDir(targetCwd = process.cwd()) {
817
853
  const telemetryDst = path.join(dataDir, 'log.cjs');
818
854
  if (fs.existsSync(telemetrySrc)) {
819
855
  fs.copyFileSync(telemetrySrc, telemetryDst);
820
- console.log(' ✓ 部署 skywalk-sdd/log.cjs(本地 Telemetry CLI)');
856
+ deployedModules += 1;
857
+ vlog(' ✓ 部署 skywalk-sdd/log.cjs(本地 Telemetry CLI)');
821
858
  } else {
822
859
  console.log(` ⚠️ Telemetry CLI 源文件缺失: ${telemetrySrc}`);
823
860
  }
@@ -826,7 +863,8 @@ function deployTelemetryDataDir(targetCwd = process.cwd()) {
826
863
  const metricsV3Dst = path.join(dataDir, 'metrics-v3.cjs');
827
864
  if (fs.existsSync(metricsV3Src)) {
828
865
  fs.copyFileSync(metricsV3Src, metricsV3Dst);
829
- console.log(' ✓ 部署 skywalk-sdd/metrics-v3.cjs(V3 指标运行时)');
866
+ deployedModules += 1;
867
+ vlog(' ✓ 部署 skywalk-sdd/metrics-v3.cjs(V3 指标运行时)');
830
868
  } else {
831
869
  console.log(` ⚠️ V3 指标模块源文件缺失: ${metricsV3Src}(report/metrics 可能无法加载 metrics-v3)`);
832
870
  }
@@ -836,7 +874,8 @@ function deployTelemetryDataDir(targetCwd = process.cwd()) {
836
874
  const specRootDst = path.join(dataDir, 'spec-root.cjs');
837
875
  if (fs.existsSync(specRootSrc)) {
838
876
  fs.copyFileSync(specRootSrc, specRootDst);
839
- console.log(' ✓ 部署 skywalk-sdd/spec-root.cjs(CWD 无关的 spec 根路径解析器)');
877
+ deployedModules += 1;
878
+ vlog(' ✓ 部署 skywalk-sdd/spec-root.cjs(CWD 无关的 spec 根路径解析器)');
840
879
  } else {
841
880
  console.log(' ⚠️ spec-root.cjs 源文件缺失,$(cat .sdd-spec-root) 模式可能受影响');
842
881
  }
@@ -850,11 +889,35 @@ function deployTelemetryDataDir(targetCwd = process.cwd()) {
850
889
  const sharedDst = path.join(sharedLibDir, 'shared.cjs');
851
890
  if (fs.existsSync(sharedSrc)) {
852
891
  fs.copyFileSync(sharedSrc, sharedDst);
853
- console.log(' ✓ 部署 skywalk-sdd/lib/shared.cjs(共用工具:parseArgs/JSON/BOM/HTTP/specRoot/KB)');
892
+ deployedModules += 1;
893
+ vlog(' ✓ 部署 skywalk-sdd/lib/shared.cjs(共用工具:parseArgs/JSON/BOM/HTTP/specRoot/KB)');
854
894
  } else {
855
895
  console.log(' ⚠️ lib/shared.cjs 源文件缺失,多个脚本将缺少共用工具依赖');
856
896
  }
857
897
 
898
+ // kld-fixback: 部署使用统计上报模块(usage-contract/usage-reporter/user-config/git-identity)
899
+ // 这些模块被部署后的 skywalk-sdd/log.cjs(即 index.cjs 副本)在 cmdStart 中 require,
900
+ // 缺失时会上报静默失效。四件套必须同时部署,路径与 npm 包内布局一致。
901
+ // ⚠️ usage-reporter.cjs 顶层 require('./git-identity.cjs'):缺它时模块加载即抛
902
+ // MODULE_NOT_FOUND,被 cmdStart try/catch 吞掉后上报 100% 静默丢失。
903
+ const usageModules = [
904
+ { src: 'usage-contract.cjs', desc: '使用统计契约(七值/ApiResponse/HTTP)' },
905
+ { src: 'usage-reporter.cjs', desc: '使用事件上报器(git 身份缓存 + 3s deadline + pending 队列)' },
906
+ { src: 'user-config.cjs', desc: '用户级配置(~/.kld-sdd/config.json,仅 server)' },
907
+ { src: 'git-identity.cjs', desc: 'git 身份持久缓存与后台刷新(usage-reporter 的硬依赖)' },
908
+ ];
909
+ for (const mod of usageModules) {
910
+ const src = path.join(pkgPath, 'skywalk-sdd', 'lib', mod.src);
911
+ const dst = path.join(sharedLibDir, mod.src);
912
+ if (fs.existsSync(src)) {
913
+ fs.copyFileSync(src, dst);
914
+ deployedModules += 1;
915
+ vlog(` ✓ 部署 skywalk-sdd/lib/${mod.src}(${mod.desc})`);
916
+ } else {
917
+ console.log(` ⚠️ ${mod.src} 源文件缺失,使用统计上报将不可用`);
918
+ }
919
+ }
920
+
858
921
  const packageVersion = require(path.join(pkgPath, 'package.json')).version;
859
922
  const runtimeMetadataDst = path.join(dataDir, 'runtime-metadata.cjs');
860
923
  fs.writeFileSync(
@@ -862,13 +925,15 @@ function deployTelemetryDataDir(targetCwd = process.cwd()) {
862
925
  `'use strict';\nmodule.exports = { resolveRuntimeMetadata() { return { version: '${packageVersion}', producer: 'kld-sdd@${packageVersion}' }; } };\n`,
863
926
  'utf8',
864
927
  );
865
- console.log(` ✓ 部署 kld-sdd 运行时身份(${packageVersion})`);
928
+ deployedModules += 1;
929
+ vlog(` ✓ 部署 kld-sdd 运行时身份(${packageVersion})`);
866
930
 
867
931
  const reportingSrc = path.join(pkgPath, 'skywalk-sdd', 'reporting');
868
932
  const reportingDst = path.join(dataDir, 'reporting');
869
933
  if (fs.existsSync(reportingSrc)) {
870
934
  copyDir(reportingSrc, reportingDst);
871
- console.log(' ✓ 部署 skywalk-sdd/reporting/(V3 单次变更报告)');
935
+ deployedModules += 1;
936
+ vlog(' ✓ 部署 skywalk-sdd/reporting/(V3 单次变更报告)');
872
937
  } else {
873
938
  throw new Error(`E_INIT_MISSING_CRITICAL: reporting/ 目录缺失: ${reportingSrc}(归档报告生成将失败)`);
874
939
  }
@@ -877,7 +942,8 @@ function deployTelemetryDataDir(targetCwd = process.cwd()) {
877
942
  const contextClientDst = path.join(dataDir, 'context-client.cjs');
878
943
  if (fs.existsSync(contextClientSrc)) {
879
944
  fs.copyFileSync(contextClientSrc, contextClientDst);
880
- console.log(' ✓ 部署 skywalk-sdd/context-client.cjs(工程 Spec 上下文客户端)');
945
+ deployedModules += 1;
946
+ vlog(' ✓ 部署 skywalk-sdd/context-client.cjs(工程 Spec 上下文客户端)');
881
947
  } else {
882
948
  console.log(` ⚠️ 工程 Spec 上下文客户端缺失: ${contextClientSrc}`);
883
949
  }
@@ -886,7 +952,8 @@ function deployTelemetryDataDir(targetCwd = process.cwd()) {
886
952
  const ontologyDst = path.join(dataDir, 'ontology');
887
953
  if (fs.existsSync(ontologySrc)) {
888
954
  copyDir(ontologySrc, ontologyDst);
889
- console.log(' ✓ 部署 skywalk-sdd/ontology/(本地本体语义运行时)');
955
+ deployedModules += 1;
956
+ vlog(' ✓ 部署 skywalk-sdd/ontology/(本地本体语义运行时)');
890
957
  } else {
891
958
  console.log(` ⚠️ 本体语义运行时缺失: ${ontologySrc}`);
892
959
  }
@@ -895,7 +962,8 @@ function deployTelemetryDataDir(targetCwd = process.cwd()) {
895
962
  const worktreeFinishDst = path.join(dataDir, 'apply-worktree-finish.cjs');
896
963
  if (fs.existsSync(worktreeFinishSrc)) {
897
964
  fs.copyFileSync(worktreeFinishSrc, worktreeFinishDst);
898
- console.log(' ✓ 部署 skywalk-sdd/apply-worktree-finish.cjs(Apply 收尾脚本)');
965
+ deployedModules += 1;
966
+ vlog(' ✓ 部署 skywalk-sdd/apply-worktree-finish.cjs(Apply 收尾脚本)');
899
967
  } else {
900
968
  console.log(' ⚠️ apply-worktree-finish.cjs 源文件缺失,Apply 阶段收尾需要手动执行');
901
969
  }
@@ -904,7 +972,8 @@ function deployTelemetryDataDir(targetCwd = process.cwd()) {
904
972
  const openspecShimDst = path.join(dataDir, 'openspec-shim.cjs');
905
973
  if (fs.existsSync(openspecShimSrc)) {
906
974
  fs.copyFileSync(openspecShimSrc, openspecShimDst);
907
- console.log(' ✓ 部署 skywalk-sdd/openspec-shim.cjs(openspec 包裹包路径 shim)');
975
+ deployedModules += 1;
976
+ vlog(' ✓ 部署 skywalk-sdd/openspec-shim.cjs(openspec 包裹包路径 shim)');
908
977
  } else {
909
978
  console.log(' ⚠️ openspec-shim.cjs 源文件缺失,openspec 命令需要手动 cd 到 spec 包执行');
910
979
  }
@@ -914,7 +983,8 @@ function deployTelemetryDataDir(targetCwd = process.cwd()) {
914
983
  const kbUploadDst = path.join(dataDir, 'kb-upload.cjs');
915
984
  if (fs.existsSync(kbUploadSrc)) {
916
985
  fs.copyFileSync(kbUploadSrc, kbUploadDst);
917
- console.log(' ✓ 部署 skywalk-sdd/kb-upload.cjs(知识库上传:文件夹/zip 双模式)');
986
+ deployedModules += 1;
987
+ vlog(' ✓ 部署 skywalk-sdd/kb-upload.cjs(知识库上传:文件夹/zip 双模式)');
918
988
  } else {
919
989
  console.log(' ⚠️ kb-upload.cjs 源文件缺失,随时入库功能不可用');
920
990
  }
@@ -924,16 +994,18 @@ function deployTelemetryDataDir(targetCwd = process.cwd()) {
924
994
  const kbSyncIdentityDst = path.join(dataDir, 'kb-sync-identity.cjs');
925
995
  if (fs.existsSync(kbSyncIdentitySrc)) {
926
996
  fs.copyFileSync(kbSyncIdentitySrc, kbSyncIdentityDst);
927
- console.log(' ✓ 部署 skywalk-sdd/kb-sync-identity.cjs(KB 身份同步:重新入库前同步 version-id)');
997
+ deployedModules += 1;
998
+ vlog(' ✓ 部署 skywalk-sdd/kb-sync-identity.cjs(KB 身份同步:重新入库前同步 version-id)');
928
999
  } else {
929
1000
  console.log(' ⚠️ kb-sync-identity.cjs 源文件缺失,重新入库身份同步不可用');
930
1001
  }
931
1002
 
932
- console.log('调用方式: node skywalk-sdd/log.cjs start|end|metrics|semantic-identity|semantic-reconcile|semantic-check');
933
- console.log(' ✓ openspec: node skywalk-sdd/openspec-shim.cjs list(自动 cd 到 *-sdd-specs)');
934
- console.log(' ✓ Spec Context: node skywalk-sdd/context-client.cjs --query="<自然语言需求>" --target-stage=spec');
935
- console.log(' ✓ Apply worktree: record-base 在 §1.5;收尾 apply-worktree-finish.cjs --change=<name>');
936
- console.log(' ℹ️ project-identity.json 将在首次配置 KB(opsx-kb-config)时自动创建');
1003
+ console.log(`部署 skywalk-sdd/ 运行时(events/state 数据目录 + ${deployedModules} 个模块)`);
1004
+ vlog(' ✓ 调用方式: node skywalk-sdd/log.cjs start|end|metrics|semantic-identity|semantic-reconcile|semantic-check');
1005
+ vlog(' ✓ openspec: node skywalk-sdd/openspec-shim.cjs list(自动 cd 到 *-sdd-specs)');
1006
+ vlog(' ✓ Spec Context: node skywalk-sdd/context-client.cjs --query="<自然语言需求>" --target-stage=spec');
1007
+ vlog(' ✓ Apply worktree: record-base 在 §1.5;收尾 apply-worktree-finish.cjs --change=<name>');
1008
+ vlog(' ℹ️ project-identity.json 将在首次配置 KB(opsx-kb-config)时自动创建');
937
1009
  console.log('✅ Telemetry 已就绪(数据存储在 skywalk-sdd/,无需配置 MCP)');
938
1010
  return true;
939
1011
  }
@@ -1025,22 +1097,17 @@ function deployCodeRepoHooks(codeRepoRoot, specRepoRoot, options = {}) {
1025
1097
  if (!fs.existsSync(specHooksDir)) {
1026
1098
  fs.mkdirSync(specHooksDir, { recursive: true });
1027
1099
  }
1028
- const hookScripts = [
1029
- 'pre-commit-sdd-check.cjs',
1030
- 'pre-commit-consistency-check.cjs',
1031
- 'pre-push-sdd-check.cjs',
1032
- 'pre-push-consistency-check.cjs',
1033
- ];
1100
+ // 复制全部 .cjs 脚本 + hooks.config 配置文件
1101
+ const hookScripts = fs.existsSync(gitHooksTemplateDir)
1102
+ ? fs.readdirSync(gitHooksTemplateDir).filter(f => f.endsWith('.cjs') || f === 'hooks.config')
1103
+ : [];
1034
1104
  for (const script of hookScripts) {
1035
1105
  const src = path.join(gitHooksTemplateDir, script);
1036
1106
  const dst = path.join(specHooksDir, script);
1037
- // 删除 !fs.existsSync(dst) 条件:原逻辑只在目标不存在时复制,导致模板升级后 spec 仓不会拿到最新脚本。
1038
- // 现在始终覆盖,与 shell hook 的 marker 覆盖策略保持一致。
1039
- if (fs.existsSync(src)) {
1040
- fs.copyFileSync(src, dst);
1041
- }
1107
+ // 始终覆盖,与 shell hook marker 覆盖策略保持一致
1108
+ fs.copyFileSync(src, dst);
1042
1109
  }
1043
- console.log(` ✓ 确保 spec 仓 skywalk-sdd/git-hooks/ 脚本齐全`);
1110
+ console.log(` ✓ 确保 spec 仓 skywalk-sdd/git-hooks/ 脚本齐全(${hookScripts.length} 个文件)`);
1044
1111
 
1045
1112
  // ── 2. 安装三个 shell hooks 到代码仓 .git/hooks/ ──
1046
1113
  // 模板 hook 运行时自动定位 spec 仓的 .cjs 脚本
@@ -1099,16 +1166,17 @@ function deployQualityGateTemplates(targetCwd = process.cwd(), options = {}) {
1099
1166
  const targetCiDir = path.join(dataDir, 'ci');
1100
1167
 
1101
1168
  if (fs.existsSync(gitHooksTemplateDir)) {
1102
- // 只复制 .cjs 脚本到 skywalk-sdd/git-hooks/,shell hook 模板(commit-msg/pre-commit/pre-push)不落此目录
1169
+ // 复制 .cjs 脚本 + hooks.config 到 skywalk-sdd/git-hooks/
1170
+ // shell hook 模板(commit-msg/pre-commit/pre-push)不落此目录
1103
1171
  if (!fs.existsSync(targetGitHooksDir)) {
1104
1172
  fs.mkdirSync(targetGitHooksDir, { recursive: true });
1105
1173
  }
1106
1174
  for (const file of fs.readdirSync(gitHooksTemplateDir)) {
1107
- if (file.endsWith('.cjs')) {
1175
+ if (file.endsWith('.cjs') || file === 'hooks.config') {
1108
1176
  fs.copyFileSync(path.join(gitHooksTemplateDir, file), path.join(targetGitHooksDir, file));
1109
1177
  }
1110
1178
  }
1111
- console.log(' ✓ 部署 skywalk-sdd/git-hooks/(.cjs 脚本)');
1179
+ console.log(' ✓ 部署 skywalk-sdd/git-hooks/(.cjs 脚本 + hooks.config)');
1112
1180
  } else {
1113
1181
  console.log(` ⚠️ Git hooks 模板缺失: ${gitHooksTemplateDir}`);
1114
1182
  }
@@ -1408,10 +1476,11 @@ function detectTools() {
1408
1476
 
1409
1477
  /**
1410
1478
  * 创建 / 更新 .gitignore
1479
+ * @param {string} [cwd=process.cwd()]
1411
1480
  */
1412
- function updateGitignore() {
1413
- const cwd = process.cwd();
1414
- const gitignorePath = path.join(cwd, '.gitignore');
1481
+ function updateGitignore(cwd = process.cwd()) {
1482
+ const root = path.resolve(cwd);
1483
+ const gitignorePath = path.join(root, '.gitignore');
1415
1484
  const requiredLines = [
1416
1485
  '.cursor/commands/personal-*',
1417
1486
  '.claude/commands/personal-*',
@@ -1420,6 +1489,10 @@ function updateGitignore() {
1420
1489
  '.kunlunzhima/commands/personal-*',
1421
1490
  '.workbuddy/commands/personal-*',
1422
1491
  '.agents/commands/personal-*',
1492
+ 'kb-state.json',
1493
+ '**/kb-state.json',
1494
+ 'skywalk-sdd/kb-state.json',
1495
+ '**/skywalk-sdd/kb-state.json',
1423
1496
  'skywalk-sdd/events/',
1424
1497
  'skywalk-sdd/state/',
1425
1498
  '**/skywalk-sdd/events/',
@@ -1437,6 +1510,12 @@ function updateGitignore() {
1437
1510
  .workbuddy/commands/personal-*
1438
1511
  .agents/commands/personal-*
1439
1512
 
1513
+ # KB 绑定状态(含 API Key,勿提交)
1514
+ kb-state.json
1515
+ **/kb-state.json
1516
+ skywalk-sdd/kb-state.json
1517
+ **/skywalk-sdd/kb-state.json
1518
+
1440
1519
  # SDD Telemetry 数据(实体在 *-sdd-specs/skywalk-sdd/;工作区可能是 symlink)
1441
1520
  skywalk-sdd/events/
1442
1521
  skywalk-sdd/state/
@@ -1480,6 +1559,7 @@ KLD SDD 项目初始化工具
1480
1559
  选项:
1481
1560
  -h, --help 显示帮助信息
1482
1561
  -v, --version 显示版本号
1562
+ --verbose 输出每个安装文件/目录的明细日志(默认仅摘要,亦可用 KLD_SDD_VERBOSE=1)
1483
1563
  --skip-openspec 跳过 openspec init 步骤
1484
1564
  --skip-template 跳过复制内置模版
1485
1565
  --tool <name> 指定编辑器,可用值: cursor, claude, codebuddy, qoder, opencode, kunlunzhima, workbuddy, codex, all
@@ -1548,14 +1628,14 @@ async function main() {
1548
1628
  // 解析参数
1549
1629
  if (args.includes('-h') || args.includes('--help')) {
1550
1630
  showHelp();
1551
- rl.close();
1631
+ closeRl();
1552
1632
  return;
1553
1633
  }
1554
1634
 
1555
1635
  if (args.includes('-v') || args.includes('--version')) {
1556
1636
  const pkg = require('../package.json');
1557
1637
  console.log(pkg.version);
1558
- rl.close();
1638
+ closeRl();
1559
1639
  return;
1560
1640
  }
1561
1641
 
@@ -1574,7 +1654,7 @@ async function main() {
1574
1654
  selectedTools = parseSelectedTools(args) || await selectEditor();
1575
1655
  } catch (error) {
1576
1656
  console.error(`❌ ${error.message}`);
1577
- rl.close();
1657
+ closeRl();
1578
1658
  process.exit(1);
1579
1659
  }
1580
1660
 
@@ -1694,7 +1774,7 @@ async function main() {
1694
1774
  }
1695
1775
 
1696
1776
  // 7. 更新工作目录 .gitignore
1697
- updateGitignore();
1777
+ updateGitignore(workspaceRoot);
1698
1778
 
1699
1779
  // 8. 收尾:无论前面是否走过 symlink/旧副本路径,强制清掉工作区/Git 根多余 skywalk-sdd/
1700
1780
  if (specPackage && fs.existsSync(specPackage.abs)) {
@@ -1750,23 +1830,23 @@ async function main() {
1750
1830
  }
1751
1831
  console.log();
1752
1832
 
1753
- // 统一的 skill 格式说明
1754
- console.log('可用 skillsopsx-* 系列,15 个 SDD skills):');
1755
- console.log(' opsx-propose - 创建业务意图文档(Why)');
1756
- console.log(' opsx-spec - 创建技术契约文档(What)');
1757
- console.log(' opsx-design - 创建技术实现方案(How)');
1758
- console.log(' opsx-task - 拆解AI编码任务(Do)');
1759
- console.log(' opsx-check - 质量门禁检查(Verify)');
1760
- console.log(' opsx-apply - 申请变更实施(Apply)');
1761
- console.log(' opsx-test - 执行单元测试(Test)');
1762
- console.log(' opsx-archive - 归档变更(Archive)');
1763
- console.log(' opsx-explore - 浏览变更状态(Explore)');
1764
- console.log(' opsx-knowledge - 业务知识库检索(辅助)');
1765
- console.log(' opsx-rules - 生成/更新 Agent 规则(辅助)');
1766
- console.log(' opsx-consistency-check - Spec 一致性校验(代码 vs spec)');
1767
- console.log(' opsx-kb-config - 知识库配置(API Key + Space/KB 选择)');
1768
- console.log(' opsx-ontology-query - 本体知识库查询');
1769
- console.log(' opsx-kb-ingest - 知识库入库(zip / 文件夹上传)');
1833
+ // 统一的 skill 格式说明(默认单行指引,完整列表走 --verbose)
1834
+ console.log('可用 skillsopsx-* 系列 15 个 SDD skills(--verbose 查看完整列表,或见 openspec/kld-sdd操作手册.html)');
1835
+ vlog(' opsx-propose - 创建业务意图文档(Why)');
1836
+ vlog(' opsx-spec - 创建技术契约文档(What)');
1837
+ vlog(' opsx-design - 创建技术实现方案(How)');
1838
+ vlog(' opsx-task - 拆解AI编码任务(Do)');
1839
+ vlog(' opsx-check - 质量门禁检查(Verify)');
1840
+ vlog(' opsx-apply - 申请变更实施(Apply)');
1841
+ vlog(' opsx-test - 执行单元测试(Test)');
1842
+ vlog(' opsx-archive - 归档变更(Archive)');
1843
+ vlog(' opsx-explore - 浏览变更状态(Explore)');
1844
+ vlog(' opsx-knowledge - 业务知识库检索(辅助)');
1845
+ vlog(' opsx-rules - 生成/更新 Agent 规则(辅助)');
1846
+ vlog(' opsx-consistency-check - Spec 一致性校验(代码 vs spec)');
1847
+ vlog(' opsx-kb-config - 知识库配置(API Key + Space/KB 选择)');
1848
+ vlog(' opsx-ontology-query - 本体知识库查询');
1849
+ vlog(' opsx-kb-ingest - 知识库入库(zip / 文件夹上传)');
1770
1850
 
1771
1851
  console.log();
1772
1852
  console.log('后续步骤:');
@@ -1782,13 +1862,13 @@ async function main() {
1782
1862
  console.log(' 5. 随时入库:node skywalk-sdd/kb-upload.cjs --folder=<变更目录> --minimum-level=propose-spec');
1783
1863
  console.log(' (需先运行 /opsx-kb-config 配置知识库)');
1784
1864
  if (selectedTools.includes('codebuddy')) {
1785
- console.log(' 5. CodeBuddy 用户:SDD Hook Pack 已随初始化自动部署,门禁自动生效,无需手动配置');
1865
+ console.log(' - CodeBuddy 用户:SDD Hook Pack 已随初始化自动部署,门禁自动生效,无需手动配置');
1786
1866
  }
1787
1867
  if (selectedTools.includes('kunlunzhima')) {
1788
- console.log(' 5. KunlunZhima 用户:在 Craft 模式使用 /opsx:propose 等 OPSX 命令(.kunlunzhima/commands/opsx/)');
1868
+ console.log(' - KunlunZhima 用户:在 Craft 模式使用 /opsx:propose 等 OPSX 命令(.kunlunzhima/commands/opsx/)');
1789
1869
  }
1790
1870
  if (selectedTools.includes('opencode')) {
1791
- console.log(' 5. OpenCode 用户:用 /opsx-propose 或 /skills 启动 SDD(.opencode/skills/opsx-*/,skill 即斜杠命令)');
1871
+ console.log(' - OpenCode 用户:用 /opsx-propose 或 /skills 启动 SDD(.opencode/skills/opsx-*/,skill 即斜杠命令)');
1792
1872
  }
1793
1873
  console.log();
1794
1874
  console.log('📊 SDD Telemetry(嵌入在 skill 流程中,自动执行,无需配置):');
@@ -1800,16 +1880,22 @@ async function main() {
1800
1880
  console.log(' - 运行 node skywalk-sdd/log.cjs metrics --project=<包裹包> 查看四维度指标');
1801
1881
  console.log();
1802
1882
 
1803
- rl.close();
1883
+ closeRl();
1804
1884
  }
1805
1885
 
1886
+ // kld-T06: Progress Runtime 已删除,用户级 workspace 注册函数一并移除。
1887
+ // 使用统计按本机 git 身份(user.name/user.email)上报,无需任何账号绑定。
1888
+
1806
1889
  module.exports = {
1807
1890
  main,
1808
1891
  detectTools,
1809
1892
  getTemplatePath,
1810
1893
  getPackagePath,
1811
1894
  selectEditor,
1895
+ isVerbose,
1896
+ vlog,
1812
1897
  deployOpsxSkills,
1898
+ copyTemplatesToProject,
1813
1899
  deployProfileArtifacts,
1814
1900
  deploySddGuideManual,
1815
1901
  deployModulesYaml,
@@ -1821,6 +1907,7 @@ module.exports = {
1821
1907
  removeOuterSkywalk,
1822
1908
  deployQualityGateTemplates,
1823
1909
  deployTelemetryDataDir,
1910
+ updateGitignore,
1824
1911
  attachCodeRepoLite,
1825
1912
  syncWorkspaceRepos,
1826
1913
  copyDirRendered,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kld-sdd",
3
- "version": "2.6.17",
3
+ "version": "2.7.3",
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 test/evidence-integrity-oracle.cjs test/evidence-integrity-wiring.cjs 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"
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/evidence-integrity-oracle.cjs test/evidence-integrity-wiring.cjs 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 && node test/init-output-verbosity.cjs && node --test test/usage-contract.test.cjs test/user-config.test.cjs test/usage-runtime-removal.test.cjs skywalk-sdd/usage-reporter.test.cjs skywalk-sdd/usage-reporting.test.cjs && node test/progress-package-artifact.test.cjs && node --test test/progress-package-install.e2e.cjs"
12
12
  },
13
13
  "keywords": [
14
14
  "kld",
@@ -24,7 +24,7 @@
24
24
  "author": "KLD Team",
25
25
  "license": "MIT",
26
26
  "engines": {
27
- "node": ">=14.0.0"
27
+ "node": ">=14.18.0"
28
28
  },
29
29
  "files": [
30
30
  "bin/",