kld-sdd 2.6.15 → 2.6.17
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.
- package/lib/init.js +174 -76
- package/lib/skills-bundle.js +23 -1
- package/lib/tool-profiles.js +8 -0
- package/package.json +6 -1
- package/skywalk-sdd/apply-worktree-finish.cjs +2 -23
- package/skywalk-sdd/context-client.cjs +38 -87
- package/skywalk-sdd/index.cjs +719 -108
- package/skywalk-sdd/kb-sync-identity.cjs +780 -0
- package/skywalk-sdd/kb-upload.cjs +505 -0
- package/skywalk-sdd/lib/shared.cjs +663 -0
- package/skywalk-sdd/metrics-v3.cjs +138 -8
- package/skywalk-sdd/ontology/archive-package.cjs +19 -34
- package/skywalk-sdd/ontology/change-lock.cjs +3 -7
- package/skywalk-sdd/ontology/external-key.cjs +18 -4
- package/skywalk-sdd/ontology/id.cjs +26 -5
- package/skywalk-sdd/ontology/identity-index.cjs +3 -7
- package/skywalk-sdd/ontology/resolve-spec-root.cjs +20 -6
- package/skywalk-sdd/ontology/runtime.cjs +16 -12
- package/skywalk-sdd/ontology/traceability-validator.cjs +7 -4
- package/skywalk-sdd/openspec-shim.cjs +48 -0
- package/skywalk-sdd/reporting/change-report-markdown.cjs +137 -19
- package/skywalk-sdd/reporting/change-report-model.cjs +993 -14
- package/skywalk-sdd/reporting/change-report-renderer.cjs +106 -41
- package/skywalk-sdd/reporting/change-report-view-model.cjs +272 -43
- package/skywalk-sdd/reporting/core-metric-definitions.cjs +192 -0
- package/skywalk-sdd/spec-root.cjs +31 -0
- package/templates/git-hooks/commit-msg +46 -0
- package/templates/git-hooks/pre-commit +57 -0
- package/templates/git-hooks/pre-commit-consistency-check.cjs +193 -0
- package/templates/git-hooks/pre-push +57 -0
- package/templates/git-hooks/pre-push-consistency-check.cjs +197 -0
- package/templates/hooks/codebuddy/hooks/hook-gate-core.cjs +696 -0
- package/templates/hooks/codebuddy/hooks/sdd-apply-test-gate.cjs +86 -0
- package/templates/hooks/codebuddy/hooks/sdd-mid-checkpoint.cjs +165 -0
- package/templates/hooks/codebuddy/hooks/sdd-post-tool.cjs +36 -1
- package/templates/hooks/codebuddy/hooks/sdd-tdd-rhythm-gate.cjs +296 -0
- package/templates/hooks/codebuddy/settings.json +8 -0
- package/templates/openspec/proposal.md +7 -3
- package/templates/openspec/spec.md +3 -3
- package/templates/skills/kld-sdd/opsx-apply/SKILL.md +21 -6
- package/templates/skills/kld-sdd/opsx-apply/checklist.md +17 -0
- package/templates/skills/kld-sdd/opsx-apply/reference.md +70 -7
- package/templates/skills/kld-sdd/opsx-archive/SKILL.md +6 -5
- package/templates/skills/kld-sdd/opsx-check/SKILL.md +48 -16
- package/templates/skills/kld-sdd/opsx-check/checklist.md +4 -2
- package/templates/skills/kld-sdd/opsx-consistency-check/SKILL.md +592 -0
- package/templates/skills/kld-sdd/opsx-design/SKILL.md +2 -2
- package/templates/skills/kld-sdd/opsx-explore/SKILL.md +2 -2
- package/templates/skills/kld-sdd/opsx-kb-config/SKILL.md +164 -0
- package/templates/skills/kld-sdd/opsx-kb-config/reference.md +116 -0
- package/templates/skills/kld-sdd/opsx-kb-ingest/SKILL.md +218 -53
- package/templates/skills/kld-sdd/opsx-kb-ingest/reference.md +51 -9
- package/templates/skills/kld-sdd/opsx-ontology-query/SKILL.md +12 -50
- package/templates/skills/kld-sdd/opsx-ontology-query/phase-3-postchange.md +2 -2
- package/templates/skills/kld-sdd/opsx-ontology-query/reference.md +1 -1
- package/templates/skills/kld-sdd/opsx-propose/SKILL.md +35 -23
- package/templates/skills/kld-sdd/opsx-propose/checklist.md +2 -0
- package/templates/skills/kld-sdd/opsx-propose/reference.md +22 -17
- package/templates/skills/kld-sdd/opsx-rules/SKILL.md +2 -2
- package/templates/skills/kld-sdd/opsx-spec/SKILL.md +19 -15
- package/templates/skills/kld-sdd/opsx-spec/checklist.md +2 -0
- package/templates/skills/kld-sdd/opsx-task/SKILL.md +2 -4
- package/templates/skills/kld-sdd/opsx-test/SKILL.md +2 -2
- package/templates/skills/kld-sdd/tdd-core/reference.md +1 -1
- package/templates/skills/kld-sdd/tdd-rules/SKILL.md +1 -0
- package/templates/skills/kld-sdd/tdd-rules/rules/tdd-rhythm-enforcement.md +101 -0
- package/templates/skills/kld-sdd/tdd-rules/rules/test-skeleton-telemetry.md +1 -1
- package/templates/skills/kld-sdd/opsx-kb-ingest/state.example.json +0 -7
- package/templates/skills/kld-sdd/opsx-ontology-query/state.example.json +0 -7
package/lib/init.js
CHANGED
|
@@ -831,6 +831,30 @@ function deployTelemetryDataDir(targetCwd = process.cwd()) {
|
|
|
831
831
|
console.log(` ⚠️ V3 指标模块源文件缺失: ${metricsV3Src}(report/metrics 可能无法加载 metrics-v3)`);
|
|
832
832
|
}
|
|
833
833
|
|
|
834
|
+
// 部署 spec-root.cjs(替代脆弱的 $(cat .sdd-spec-root),CWD 无关)
|
|
835
|
+
const specRootSrc = path.join(pkgPath, 'skywalk-sdd', 'spec-root.cjs');
|
|
836
|
+
const specRootDst = path.join(dataDir, 'spec-root.cjs');
|
|
837
|
+
if (fs.existsSync(specRootSrc)) {
|
|
838
|
+
fs.copyFileSync(specRootSrc, specRootDst);
|
|
839
|
+
console.log(' ✓ 部署 skywalk-sdd/spec-root.cjs(CWD 无关的 spec 根路径解析器)');
|
|
840
|
+
} else {
|
|
841
|
+
console.log(' ⚠️ spec-root.cjs 源文件缺失,$(cat .sdd-spec-root) 模式可能受影响');
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
// 部署 lib/shared.cjs(共用工具模块:parseArgs/readJson/atomicWrite/HTTP/specRoot/KB状态)
|
|
845
|
+
const sharedLibDir = path.join(dataDir, 'lib');
|
|
846
|
+
if (!fs.existsSync(sharedLibDir)) {
|
|
847
|
+
fs.mkdirSync(sharedLibDir, { recursive: true });
|
|
848
|
+
}
|
|
849
|
+
const sharedSrc = path.join(pkgPath, 'skywalk-sdd', 'lib', 'shared.cjs');
|
|
850
|
+
const sharedDst = path.join(sharedLibDir, 'shared.cjs');
|
|
851
|
+
if (fs.existsSync(sharedSrc)) {
|
|
852
|
+
fs.copyFileSync(sharedSrc, sharedDst);
|
|
853
|
+
console.log(' ✓ 部署 skywalk-sdd/lib/shared.cjs(共用工具:parseArgs/JSON/BOM/HTTP/specRoot/KB)');
|
|
854
|
+
} else {
|
|
855
|
+
console.log(' ⚠️ lib/shared.cjs 源文件缺失,多个脚本将缺少共用工具依赖');
|
|
856
|
+
}
|
|
857
|
+
|
|
834
858
|
const packageVersion = require(path.join(pkgPath, 'package.json')).version;
|
|
835
859
|
const runtimeMetadataDst = path.join(dataDir, 'runtime-metadata.cjs');
|
|
836
860
|
fs.writeFileSync(
|
|
@@ -885,73 +909,146 @@ function deployTelemetryDataDir(targetCwd = process.cwd()) {
|
|
|
885
909
|
console.log(' ⚠️ openspec-shim.cjs 源文件缺失,openspec 命令需要手动 cd 到 spec 包执行');
|
|
886
910
|
}
|
|
887
911
|
|
|
912
|
+
// 部署 kb-upload.cjs(知识库上传脚本,支持文件夹/zip 双模式随时入库)
|
|
913
|
+
const kbUploadSrc = path.join(pkgPath, 'skywalk-sdd', 'kb-upload.cjs');
|
|
914
|
+
const kbUploadDst = path.join(dataDir, 'kb-upload.cjs');
|
|
915
|
+
if (fs.existsSync(kbUploadSrc)) {
|
|
916
|
+
fs.copyFileSync(kbUploadSrc, kbUploadDst);
|
|
917
|
+
console.log(' ✓ 部署 skywalk-sdd/kb-upload.cjs(知识库上传:文件夹/zip 双模式)');
|
|
918
|
+
} else {
|
|
919
|
+
console.log(' ⚠️ kb-upload.cjs 源文件缺失,随时入库功能不可用');
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
// 部署 kb-sync-identity.cjs(KB 身份同步脚本,重新入库前同步 version-id)
|
|
923
|
+
const kbSyncIdentitySrc = path.join(pkgPath, 'skywalk-sdd', 'kb-sync-identity.cjs');
|
|
924
|
+
const kbSyncIdentityDst = path.join(dataDir, 'kb-sync-identity.cjs');
|
|
925
|
+
if (fs.existsSync(kbSyncIdentitySrc)) {
|
|
926
|
+
fs.copyFileSync(kbSyncIdentitySrc, kbSyncIdentityDst);
|
|
927
|
+
console.log(' ✓ 部署 skywalk-sdd/kb-sync-identity.cjs(KB 身份同步:重新入库前同步 version-id)');
|
|
928
|
+
} else {
|
|
929
|
+
console.log(' ⚠️ kb-sync-identity.cjs 源文件缺失,重新入库身份同步不可用');
|
|
930
|
+
}
|
|
931
|
+
|
|
888
932
|
console.log(' ✓ 调用方式: node skywalk-sdd/log.cjs start|end|metrics|semantic-identity|semantic-reconcile|semantic-check');
|
|
889
933
|
console.log(' ✓ openspec: node skywalk-sdd/openspec-shim.cjs list(自动 cd 到 *-sdd-specs)');
|
|
890
934
|
console.log(' ✓ Spec Context: node skywalk-sdd/context-client.cjs --query="<自然语言需求>" --target-stage=spec');
|
|
891
935
|
console.log(' ✓ Apply worktree: record-base 在 §1.5;收尾 apply-worktree-finish.cjs --change=<name>');
|
|
892
|
-
console.log(' ℹ️ project-identity.json 将在首次配置 KB(opsx-
|
|
936
|
+
console.log(' ℹ️ project-identity.json 将在首次配置 KB(opsx-kb-config)时自动创建');
|
|
893
937
|
console.log('✅ Telemetry 已就绪(数据存储在 skywalk-sdd/,无需配置 MCP)');
|
|
894
938
|
return true;
|
|
895
939
|
}
|
|
896
940
|
|
|
897
|
-
|
|
941
|
+
/**
|
|
942
|
+
* 安装 SDD shell hook(多脚本调用型 hook,如 pre-commit / pre-push / commit-msg)。
|
|
943
|
+
* 直接部署完整的 shell 脚本到 .git/hooks/,脚本内部远程引用 spec 仓的 .cjs 脚本。
|
|
944
|
+
*
|
|
945
|
+
* shell hook 运行时通过 git config sdd.specPath / .sdd-spec-root / 向上搜索
|
|
946
|
+
* 定位 spec 仓的 skywalk-sdd/git-hooks/ 目录,兼容单仓/多仓/mono 布局。
|
|
947
|
+
*
|
|
948
|
+
* @param {string} cwd - Git 仓库根目录
|
|
949
|
+
* @param {string} hookName - hook 名称(如 'pre-commit'、'pre-push')
|
|
950
|
+
* @param {string} pkgPath - kld-sdd 包路径(用于读取模板)
|
|
951
|
+
*/
|
|
952
|
+
function installSddShellHook(cwd, hookName, pkgPath = getPackagePath()) {
|
|
898
953
|
const hooksDir = path.join(cwd, '.git', 'hooks');
|
|
899
954
|
if (!fs.existsSync(hooksDir)) {
|
|
900
955
|
return;
|
|
901
956
|
}
|
|
902
957
|
|
|
958
|
+
const templatePath = path.join(pkgPath, 'templates', 'git-hooks', hookName);
|
|
959
|
+
if (!fs.existsSync(templatePath)) {
|
|
960
|
+
console.log(` ⚠️ Hook 模板不存在: ${templatePath}`);
|
|
961
|
+
return;
|
|
962
|
+
}
|
|
963
|
+
|
|
903
964
|
const hookPath = path.join(hooksDir, hookName);
|
|
904
965
|
const marker = 'KLD SDD quality gate';
|
|
905
|
-
|
|
906
|
-
// 否则 trailer 脚本会把 --project=… 误当成 message 文件(argv[2])。
|
|
907
|
-
const extraArgs = options.extraArgs || '';
|
|
908
|
-
// Windows 反斜杠路径在 #!/bin/sh(Git for Windows MSYS2)中不被 [ -f ] 和 node 识别,
|
|
909
|
-
// 统一转为正斜杠,确保 hook shim 跨平台可执行。
|
|
910
|
-
const shScriptPath = scriptPath.replace(/\\/g, '/');
|
|
911
|
-
const shExtraArgs = extraArgs.replace(/\\/g, '/');
|
|
912
|
-
const nodeLine = shExtraArgs
|
|
913
|
-
? ` node "${shScriptPath}" "$@" ${shExtraArgs}`
|
|
914
|
-
: ` node "${shScriptPath}" "$@"`;
|
|
915
|
-
const shim = [
|
|
916
|
-
'#!/bin/sh',
|
|
917
|
-
`# ${marker}`,
|
|
918
|
-
`if [ -f "${shScriptPath}" ]; then`,
|
|
919
|
-
nodeLine,
|
|
920
|
-
'else',
|
|
921
|
-
` echo "[kld-sdd] Hook 脚本不存在: ${shScriptPath}" >&2`,
|
|
922
|
-
' exit 1',
|
|
923
|
-
'fi',
|
|
924
|
-
'',
|
|
925
|
-
].join('\n');
|
|
966
|
+
const content = fs.readFileSync(templatePath, 'utf8');
|
|
926
967
|
|
|
927
968
|
if (fs.existsSync(hookPath)) {
|
|
928
969
|
const existing = fs.readFileSync(hookPath, 'utf8');
|
|
929
970
|
if (existing.includes(marker)) {
|
|
930
|
-
// 已是 SDD
|
|
931
|
-
|
|
932
|
-
console.log(` ✓ .git/hooks/${hookName} 已包含 SDD 质量门禁`);
|
|
933
|
-
return;
|
|
934
|
-
}
|
|
935
|
-
fs.writeFileSync(hookPath, shim, 'utf8');
|
|
971
|
+
// 已是 SDD hook:允许覆盖升级
|
|
972
|
+
fs.writeFileSync(hookPath, content, 'utf8');
|
|
936
973
|
try {
|
|
937
974
|
fs.chmodSync(hookPath, 0o755);
|
|
938
975
|
} catch {
|
|
939
976
|
// ignore
|
|
940
977
|
}
|
|
941
|
-
console.log(` ✓ 更新 .git/hooks/${hookName} SDD
|
|
978
|
+
console.log(` ✓ 更新 .git/hooks/${hookName} SDD 质量门禁(多脚本)`);
|
|
942
979
|
return;
|
|
943
980
|
}
|
|
944
|
-
console.log(` ℹ️ .git/hooks/${hookName}
|
|
981
|
+
console.log(` ℹ️ .git/hooks/${hookName} 已存在且非 SDD 受管 hook,已保留不覆盖`);
|
|
945
982
|
return;
|
|
946
983
|
}
|
|
947
984
|
|
|
948
|
-
fs.writeFileSync(hookPath,
|
|
985
|
+
fs.writeFileSync(hookPath, content, 'utf8');
|
|
949
986
|
try {
|
|
950
987
|
fs.chmodSync(hookPath, 0o755);
|
|
951
988
|
} catch {
|
|
952
|
-
// Windows 上 chmod
|
|
989
|
+
// Windows 上 chmod 可能没有实际效果
|
|
953
990
|
}
|
|
954
|
-
console.log(` ✓ 安装 .git/hooks/${hookName} SDD
|
|
991
|
+
console.log(` ✓ 安装 .git/hooks/${hookName} SDD 质量门禁(多脚本)`);
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
/**
|
|
995
|
+
* 部署代码仓全部 Git hooks(commit-msg + pre-commit + pre-push)。
|
|
996
|
+
*
|
|
997
|
+
* 统一入口,封装三种 hook 的部署逻辑。代码仓**不落 skywalk-sdd/**,
|
|
998
|
+
* 只在 .git/hooks/ 安装 shell hook 模板,远程引用 spec 仓的 .cjs 脚本:
|
|
999
|
+
*
|
|
1000
|
+
* - **spec 仓**:确保 skywalk-sdd/git-hooks/ 下有全部 .cjs 脚本
|
|
1001
|
+
* (commit-msg-sdd-trailer + pre-commit-* + pre-push-* + ontology)。
|
|
1002
|
+
* - **代码仓 .git/hooks/**:仅安装 3 个 shell hook 模板,运行时通过
|
|
1003
|
+
* git config sdd.specPath / .sdd-spec-root / 向上搜索定位 spec 仓的脚本。
|
|
1004
|
+
*
|
|
1005
|
+
* @param {string} codeRepoRoot - 代码仓根目录
|
|
1006
|
+
* @param {string} specRepoRoot - spec 包裹包根目录
|
|
1007
|
+
* @param {object} options - { pkgPath }
|
|
1008
|
+
* @returns {{ ok: boolean, message?: string }}
|
|
1009
|
+
*/
|
|
1010
|
+
function deployCodeRepoHooks(codeRepoRoot, specRepoRoot, options = {}) {
|
|
1011
|
+
const cwd = path.resolve(codeRepoRoot);
|
|
1012
|
+
const specRoot = path.resolve(specRepoRoot);
|
|
1013
|
+
const pkgPath = options.pkgPath || getPackagePath();
|
|
1014
|
+
|
|
1015
|
+
if (!workspaceLayout.isGitRepo(cwd)) {
|
|
1016
|
+
return { ok: false, message: '不是 Git 仓库' };
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
// ── 1. 确保 spec 仓有全部 hook 脚本 + ontology ──
|
|
1020
|
+
// commit-msg .cjs + ontology(ensureSpecCommitMsgScript 已处理)
|
|
1021
|
+
ensureSpecCommitMsgScript(specRoot, pkgPath);
|
|
1022
|
+
// pre-commit / pre-push .cjs 脚本
|
|
1023
|
+
const specHooksDir = path.join(specRoot, 'skywalk-sdd', 'git-hooks');
|
|
1024
|
+
const gitHooksTemplateDir = path.join(pkgPath, 'templates', 'git-hooks');
|
|
1025
|
+
if (!fs.existsSync(specHooksDir)) {
|
|
1026
|
+
fs.mkdirSync(specHooksDir, { recursive: true });
|
|
1027
|
+
}
|
|
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
|
+
];
|
|
1034
|
+
for (const script of hookScripts) {
|
|
1035
|
+
const src = path.join(gitHooksTemplateDir, script);
|
|
1036
|
+
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
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
console.log(` ✓ 确保 spec 仓 skywalk-sdd/git-hooks/ 脚本齐全`);
|
|
1044
|
+
|
|
1045
|
+
// ── 2. 安装三个 shell hooks 到代码仓 .git/hooks/ ──
|
|
1046
|
+
// 模板 hook 运行时自动定位 spec 仓的 .cjs 脚本
|
|
1047
|
+
installSddShellHook(cwd, 'commit-msg', pkgPath);
|
|
1048
|
+
installSddShellHook(cwd, 'pre-commit', pkgPath);
|
|
1049
|
+
installSddShellHook(cwd, 'pre-push', pkgPath);
|
|
1050
|
+
|
|
1051
|
+
return { ok: true };
|
|
955
1052
|
}
|
|
956
1053
|
|
|
957
1054
|
/**
|
|
@@ -976,22 +1073,6 @@ function ensureSpecCommitMsgScript(specRepoRoot, pkgPath = getPackagePath()) {
|
|
|
976
1073
|
return { ok: true, scriptPath, deployed: true };
|
|
977
1074
|
}
|
|
978
1075
|
|
|
979
|
-
/**
|
|
980
|
-
* 代码仓 commit-msg:只写 .git/hooks,脚本与 ontology 一律用 spec 仓那一份。
|
|
981
|
-
*/
|
|
982
|
-
function installCodeRepoCommitMsgHook(codeRepoRoot, specRepoRoot, options = {}) {
|
|
983
|
-
const cwd = path.resolve(codeRepoRoot);
|
|
984
|
-
const ensured = ensureSpecCommitMsgScript(specRepoRoot, options.pkgPath || getPackagePath());
|
|
985
|
-
if (!ensured.ok) {
|
|
986
|
-
console.log(` ⚠️ ${ensured.message}`);
|
|
987
|
-
return false;
|
|
988
|
-
}
|
|
989
|
-
installGitHookShim(cwd, 'commit-msg', ensured.scriptPath, {
|
|
990
|
-
extraArgs: `"--project=${cwd}"`,
|
|
991
|
-
});
|
|
992
|
-
return true;
|
|
993
|
-
}
|
|
994
|
-
|
|
995
1076
|
/**
|
|
996
1077
|
* 部署 Git hooks / CI 兜底模板
|
|
997
1078
|
* 这些脚本只做质量门禁和 CI 补充记录,不替代 OPSX 主采集链路。
|
|
@@ -999,7 +1080,7 @@ function installCodeRepoCommitMsgHook(codeRepoRoot, specRepoRoot, options = {})
|
|
|
999
1080
|
* @param {string} [targetCwd]
|
|
1000
1081
|
* @param {{ mode?: 'full'|'commit-msg-only' }} [options]
|
|
1001
1082
|
* full: spec 包裹包(pre-commit/pre-push/commit-msg + CI)
|
|
1002
|
-
* commit-msg-only:
|
|
1083
|
+
* commit-msg-only: 仅安装 commit-msg hook(跳过 pre-commit/pre-push)
|
|
1003
1084
|
*/
|
|
1004
1085
|
function deployQualityGateTemplates(targetCwd = process.cwd(), options = {}) {
|
|
1005
1086
|
const pkgPath = getPackagePath();
|
|
@@ -1018,8 +1099,16 @@ function deployQualityGateTemplates(targetCwd = process.cwd(), options = {}) {
|
|
|
1018
1099
|
const targetCiDir = path.join(dataDir, 'ci');
|
|
1019
1100
|
|
|
1020
1101
|
if (fs.existsSync(gitHooksTemplateDir)) {
|
|
1021
|
-
|
|
1022
|
-
|
|
1102
|
+
// 只复制 .cjs 脚本到 skywalk-sdd/git-hooks/,shell hook 模板(commit-msg/pre-commit/pre-push)不落此目录
|
|
1103
|
+
if (!fs.existsSync(targetGitHooksDir)) {
|
|
1104
|
+
fs.mkdirSync(targetGitHooksDir, { recursive: true });
|
|
1105
|
+
}
|
|
1106
|
+
for (const file of fs.readdirSync(gitHooksTemplateDir)) {
|
|
1107
|
+
if (file.endsWith('.cjs')) {
|
|
1108
|
+
fs.copyFileSync(path.join(gitHooksTemplateDir, file), path.join(targetGitHooksDir, file));
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
console.log(' ✓ 部署 skywalk-sdd/git-hooks/(.cjs 脚本)');
|
|
1023
1112
|
} else {
|
|
1024
1113
|
console.log(` ⚠️ Git hooks 模板缺失: ${gitHooksTemplateDir}`);
|
|
1025
1114
|
}
|
|
@@ -1030,18 +1119,18 @@ function deployQualityGateTemplates(targetCwd = process.cwd(), options = {}) {
|
|
|
1030
1119
|
}
|
|
1031
1120
|
|
|
1032
1121
|
if (mode === 'full') {
|
|
1033
|
-
|
|
1034
|
-
|
|
1122
|
+
installSddShellHook(cwd, 'pre-commit', pkgPath);
|
|
1123
|
+
installSddShellHook(cwd, 'pre-push', pkgPath);
|
|
1035
1124
|
}
|
|
1036
|
-
|
|
1125
|
+
installSddShellHook(cwd, 'commit-msg', pkgPath);
|
|
1037
1126
|
|
|
1038
1127
|
console.log('✅ Git hooks 已就绪');
|
|
1039
1128
|
return true;
|
|
1040
1129
|
}
|
|
1041
1130
|
|
|
1042
1131
|
/**
|
|
1043
|
-
*
|
|
1044
|
-
*
|
|
1132
|
+
* 代码仓轻量接入:写 .sdd.yaml + sdd.specPath + .git/hooks/(commit-msg + pre-commit + pre-push)。
|
|
1133
|
+
* 代码仓不落 skywalk-sdd/,shell hook 远程引用 spec 仓 skywalk-sdd/git-hooks/ 下的 .cjs 脚本。
|
|
1045
1134
|
*/
|
|
1046
1135
|
function attachCodeRepoLite(codeRepoRoot, specRepoRoot, options = {}) {
|
|
1047
1136
|
const cwd = path.resolve(codeRepoRoot);
|
|
@@ -1055,7 +1144,7 @@ function attachCodeRepoLite(codeRepoRoot, specRepoRoot, options = {}) {
|
|
|
1055
1144
|
return { ok: false, repo: label, message: `spec 路径不是 Git 仓库: ${specRoot}` };
|
|
1056
1145
|
}
|
|
1057
1146
|
|
|
1058
|
-
console.log(`🔗
|
|
1147
|
+
console.log(`🔗 轻量接入代码仓(commit-msg + pre-commit + pre-push): ${label}`);
|
|
1059
1148
|
|
|
1060
1149
|
const remote = sddConfig.gitRemoteUrl(specRoot) || 'git@gitlab.example.com:biz/xxx-sdd-specs.git';
|
|
1061
1150
|
const sddYamlPath = path.join(cwd, '.sdd.yaml');
|
|
@@ -1081,8 +1170,8 @@ function attachCodeRepoLite(codeRepoRoot, specRepoRoot, options = {}) {
|
|
|
1081
1170
|
}
|
|
1082
1171
|
console.log(` ✓ sdd.specPath = ${linked.path}`);
|
|
1083
1172
|
|
|
1084
|
-
|
|
1085
|
-
|
|
1173
|
+
// 部署代码仓全部 Git hooks(commit-msg + pre-commit + pre-push)
|
|
1174
|
+
deployCodeRepoHooks(cwd, specRoot, options);
|
|
1086
1175
|
|
|
1087
1176
|
return { ok: true, repo: label, path: cwd, specPath: linked.path };
|
|
1088
1177
|
}
|
|
@@ -1555,8 +1644,9 @@ async function main() {
|
|
|
1555
1644
|
removeOuterSkywalk(workspaceRoot);
|
|
1556
1645
|
|
|
1557
1646
|
deployMonoSddYaml(workspaceRoot, { specPathRelative: specPackage.name });
|
|
1558
|
-
//
|
|
1559
|
-
|
|
1647
|
+
// 部署代码仓全部 Git hooks(commit-msg + pre-commit + pre-push)
|
|
1648
|
+
// 单仓 mono:hook 通过 .sdd-spec-root 定位包裹包的 skywalk-sdd/git-hooks/
|
|
1649
|
+
deployCodeRepoHooks(workspaceRoot, specPackage.abs);
|
|
1560
1650
|
const linked = sddConfig.setSpecPath(workspaceRoot, specPackage.abs, {
|
|
1561
1651
|
allowNestedPackage: true,
|
|
1562
1652
|
});
|
|
@@ -1649,7 +1739,7 @@ async function main() {
|
|
|
1649
1739
|
console.log(' 📌 .sdd-spec-root → 包裹包;无根目录 skywalk-sdd/');
|
|
1650
1740
|
}
|
|
1651
1741
|
if (selectedTools.includes('kunlunzhima')) {
|
|
1652
|
-
console.log(' 📎 .kunlunzhima/commands/opsx/ # KunlunZhima OPSX command bridge(
|
|
1742
|
+
console.log(' 📎 .kunlunzhima/commands/opsx/ # KunlunZhima OPSX command bridge(13 个)');
|
|
1653
1743
|
console.log(' ℹ️ KunlunZhima 通过 commands/skills 入口使用 SDD,未启用自动 Hook');
|
|
1654
1744
|
}
|
|
1655
1745
|
if (selectedTools.includes('codebuddy')) {
|
|
@@ -1661,18 +1751,22 @@ async function main() {
|
|
|
1661
1751
|
console.log();
|
|
1662
1752
|
|
|
1663
1753
|
// 统一的 skill 格式说明
|
|
1664
|
-
console.log('可用 skills(opsx-* 系列,
|
|
1665
|
-
console.log(' opsx-propose
|
|
1666
|
-
console.log(' opsx-spec
|
|
1667
|
-
console.log(' opsx-design
|
|
1668
|
-
console.log(' opsx-task
|
|
1669
|
-
console.log(' opsx-check
|
|
1670
|
-
console.log(' opsx-apply
|
|
1671
|
-
console.log(' opsx-test
|
|
1672
|
-
console.log(' opsx-archive
|
|
1673
|
-
console.log(' opsx-explore
|
|
1674
|
-
console.log(' opsx-knowledge
|
|
1675
|
-
console.log(' opsx-rules
|
|
1754
|
+
console.log('可用 skills(opsx-* 系列,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 / 文件夹上传)');
|
|
1676
1770
|
|
|
1677
1771
|
console.log();
|
|
1678
1772
|
console.log('后续步骤:');
|
|
@@ -1685,6 +1779,8 @@ async function main() {
|
|
|
1685
1779
|
console.log(' 2. 按顺序执行 propose → spec → design → task');
|
|
1686
1780
|
console.log(' 3. 激活 opsx-check 验证文档质量');
|
|
1687
1781
|
console.log(' 4. 激活 opsx-apply 申请实施,opsx-archive 归档完成');
|
|
1782
|
+
console.log(' 5. 随时入库:node skywalk-sdd/kb-upload.cjs --folder=<变更目录> --minimum-level=propose-spec');
|
|
1783
|
+
console.log(' (需先运行 /opsx-kb-config 配置知识库)');
|
|
1688
1784
|
if (selectedTools.includes('codebuddy')) {
|
|
1689
1785
|
console.log(' 5. CodeBuddy 用户:SDD Hook Pack 已随初始化自动部署,门禁自动生效,无需手动配置');
|
|
1690
1786
|
}
|
|
@@ -1729,5 +1825,7 @@ module.exports = {
|
|
|
1729
1825
|
syncWorkspaceRepos,
|
|
1730
1826
|
copyDirRendered,
|
|
1731
1827
|
parseSelectedTools,
|
|
1828
|
+
installSddShellHook,
|
|
1829
|
+
deployCodeRepoHooks,
|
|
1732
1830
|
TOOL_CONFIGS,
|
|
1733
1831
|
};
|
package/lib/skills-bundle.js
CHANGED
|
@@ -7,6 +7,22 @@ const path = require('path');
|
|
|
7
7
|
|
|
8
8
|
const SKILLS_BUNDLE_NAME = 'kld-sdd';
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* PowerShell 禁忌 — 追加到 SHELL_GUIDANCE 后,自动注入所有 SKILL.md。
|
|
12
|
+
* 根因:PowerShell 的 cat/curl/Get-Content 行为与 bash 不同,导致管道断裂、
|
|
13
|
+
* HTTP 请求失败、JSON 文件 BOM 污染引发静默降级(sdd-output 错误 #1/#3/#4/#5)。
|
|
14
|
+
*/
|
|
15
|
+
const POWERSHELL_TABOOS = [
|
|
16
|
+
'',
|
|
17
|
+
'> - **PowerShell 禁忌**(Windows 用户必读):',
|
|
18
|
+
'> - 禁止 `| cat`(PowerShell 将 cat 解释为 Get-Content,不接受管道输入)→ 直接执行命令',
|
|
19
|
+
'> - 禁止 `node -e "..."` 内联脚本(PowerShell 对 `||`/`{}`/`()` 有特殊解析,导致 JS 语法被破坏)→ 用 `write_to_file` 创建临时 `.cjs` 脚本执行后删除',
|
|
20
|
+
'> - 禁止 cmd.exe 语法(如 `for /L %i in (1,1,N) do ...`)→ PowerShell 不支持,改用逐个 `execute_command` 调用或 PowerShell `foreach`',
|
|
21
|
+
'> - 警告 CLIXML:PowerShell 捕获 `node`/`npx` 子进程输出时可能包装为 `#< CLIXML <Objs...>` 格式 → 检测到 CLIXML 时以文件系统实际产物为准,忽略 stdout',
|
|
22
|
+
'> - 禁止 `curl`(PowerShell 将 curl 别名为 Invoke-WebRequest)→ HTTP 请求走 `context-client.cjs` / `kb-upload.cjs`,或用 `Invoke-RestMethod`',
|
|
23
|
+
'> - 禁止 `Get-Content` / `Out-File` 读写 JSON(PowerShell 默认写 BOM 导致 JSON.parse 失败)→ 用 write_to_file 工具或 Node.js `fs` 模块',
|
|
24
|
+
].join('\n');
|
|
25
|
+
|
|
10
26
|
const OPSX_SKILL_DIRS = [
|
|
11
27
|
'opsx-propose',
|
|
12
28
|
'opsx-spec',
|
|
@@ -20,8 +36,11 @@ const OPSX_SKILL_DIRS = [
|
|
|
20
36
|
'opsx-knowledge',
|
|
21
37
|
'opsx-rules',
|
|
22
38
|
// Engineering KB 依赖技能(随 init 部署;propose/spec/archive 硬依赖)
|
|
39
|
+
'opsx-kb-config',
|
|
23
40
|
'opsx-ontology-query',
|
|
24
41
|
'opsx-kb-ingest',
|
|
42
|
+
// Spec 一致性校验技能
|
|
43
|
+
'opsx-consistency-check',
|
|
25
44
|
];
|
|
26
45
|
|
|
27
46
|
const PKG_ROOT = path.resolve(__dirname, '..');
|
|
@@ -102,6 +121,8 @@ const KUNLUN_SLASH_COMMANDS = [
|
|
|
102
121
|
'explore',
|
|
103
122
|
'ontology-query',
|
|
104
123
|
'kb-ingest',
|
|
124
|
+
'kb-config',
|
|
125
|
+
'consistency-check',
|
|
105
126
|
];
|
|
106
127
|
|
|
107
128
|
function transformKunlunSlashCommands(content) {
|
|
@@ -125,7 +146,8 @@ function replaceProfileTokens(content, profile) {
|
|
|
125
146
|
return content
|
|
126
147
|
.replace(/\$\{SKILL_RUNTIME_DIR\}/g, vars.SKILL_RUNTIME_DIR || profile.skillsDir || '')
|
|
127
148
|
.replace(/\$\{HOOK_GATE_DESCRIPTION\}/g, vars.HOOK_GATE_DESCRIPTION || '')
|
|
128
|
-
.replace(/\$\{SHELL_GUIDANCE\}/g, vars.SHELL_GUIDANCE || '')
|
|
149
|
+
.replace(/\$\{SHELL_GUIDANCE\}/g, (vars.SHELL_GUIDANCE || '') + POWERSHELL_TABOOS)
|
|
150
|
+
.replace(/\$\{CHECK_REVIEW_DELEGATION_GUIDANCE\}/g, vars.CHECK_REVIEW_DELEGATION_GUIDANCE || '')
|
|
129
151
|
.replace(/\$\{AGENT_SKILL_DIR\}/g, profile.skillsDir || '');
|
|
130
152
|
}
|
|
131
153
|
|
package/lib/tool-profiles.js
CHANGED
|
@@ -24,6 +24,7 @@ const TOOL_PROFILES = Object.freeze({
|
|
|
24
24
|
SKILL_RUNTIME_DIR: '.cursor/skills',
|
|
25
25
|
HOOK_GATE_DESCRIPTION: '本项目未为此 Agent 部署自动 Hook 门禁;请遵循 Skill 自检与 CI/Git 证据门禁。',
|
|
26
26
|
SHELL_GUIDANCE: '在 Windows 上优先使用 PowerShell;如需 Bash 请使用 Git Bash。',
|
|
27
|
+
CHECK_REVIEW_DELEGATION_GUIDANCE: '使用 Cursor 当前可用的独立 Agent/子代理能力启动一个全新评审上下文;若能力不可用,按本 Skill 的受控回退规则由主 Agent 检查。',
|
|
27
28
|
},
|
|
28
29
|
},
|
|
29
30
|
claude: {
|
|
@@ -44,6 +45,7 @@ const TOOL_PROFILES = Object.freeze({
|
|
|
44
45
|
SKILL_RUNTIME_DIR: '.claude/skills',
|
|
45
46
|
HOOK_GATE_DESCRIPTION: 'Claude Code 已部署 SDD Hook Pack(.claude/hooks + settings.json),自动门禁与遥测增强已启用。',
|
|
46
47
|
SHELL_GUIDANCE: '在 Windows Bash / Git Bash / Claude Bash 中,禁止裸写 Windows 反斜杠绝对路径;如必须使用绝对路径,请写成正斜杠路径或加引号。',
|
|
48
|
+
CHECK_REVIEW_DELEGATION_GUIDANCE: '使用 Claude Code 的 Agent 能力启动一个全新、无作者上下文的评审子代理;不得让评审子代理再次委派。',
|
|
47
49
|
},
|
|
48
50
|
},
|
|
49
51
|
codebuddy: {
|
|
@@ -64,6 +66,7 @@ const TOOL_PROFILES = Object.freeze({
|
|
|
64
66
|
SKILL_RUNTIME_DIR: '.codebuddy/skills',
|
|
65
67
|
HOOK_GATE_DESCRIPTION: 'CodeBuddy CLI 已部署 SDD Hook Pack(.codebuddy/hooks + settings.json),初始化后自动生效。',
|
|
66
68
|
SHELL_GUIDANCE: 'CodeBuddy Hooks 在 Windows 上通过 Git Bash 执行;命令使用 node "$CODEBUDDY_PROJECT_DIR/..." 引用项目路径。',
|
|
69
|
+
CHECK_REVIEW_DELEGATION_GUIDANCE: '使用 CodeBuddy 当前可用的独立 Agent 能力启动一个评审子代理;若启动失败,记录标准回退原因后由主 Agent 检查。',
|
|
67
70
|
},
|
|
68
71
|
},
|
|
69
72
|
qoder: {
|
|
@@ -84,6 +87,7 @@ const TOOL_PROFILES = Object.freeze({
|
|
|
84
87
|
SKILL_RUNTIME_DIR: '.qoder/skills',
|
|
85
88
|
HOOK_GATE_DESCRIPTION: '本项目未为此 Agent 部署自动 Hook 门禁;请遵循 Skill 自检与 CI/Git 证据门禁。',
|
|
86
89
|
SHELL_GUIDANCE: '在 Windows 上优先使用 PowerShell;如需 Bash 请使用 Git Bash。',
|
|
90
|
+
CHECK_REVIEW_DELEGATION_GUIDANCE: '使用 Qoder 当前可用的独立 Agent/子代理能力启动一个全新评审上下文;能力不可用时才允许主 Agent 回退。',
|
|
87
91
|
},
|
|
88
92
|
},
|
|
89
93
|
opencode: {
|
|
@@ -107,6 +111,7 @@ const TOOL_PROFILES = Object.freeze({
|
|
|
107
111
|
SKILL_RUNTIME_DIR: '.opencode/skills',
|
|
108
112
|
HOOK_GATE_DESCRIPTION: '本项目未为此 Agent 部署自动 Hook 门禁;请遵循 Skill 自检与 CI/Git 证据门禁。',
|
|
109
113
|
SHELL_GUIDANCE: '在 Windows 上优先使用 PowerShell;OpenCode 可通过环境变量 OPENCODE_GIT_BASH_PATH 指定 Git Bash;如需 Bash 请使用 Git Bash。',
|
|
114
|
+
CHECK_REVIEW_DELEGATION_GUIDANCE: '使用 OpenCode 当前可用的独立 Agent 能力启动一个无作者上下文的评审子代理;不得递归委派。',
|
|
110
115
|
},
|
|
111
116
|
},
|
|
112
117
|
kunlunzhima: {
|
|
@@ -127,6 +132,7 @@ const TOOL_PROFILES = Object.freeze({
|
|
|
127
132
|
SKILL_RUNTIME_DIR: '.kunlunzhima/skills',
|
|
128
133
|
HOOK_GATE_DESCRIPTION: 'KunlunZhima IDE 不支持自动 Hook 门禁;请通过 Skill 自检、commands 入口与 CI/Git 证据门禁保障质量。',
|
|
129
134
|
SHELL_GUIDANCE: 'KunlunZhima IDE 通过 commands/skills 入口使用 SDD;相对引用以 Skill 目录解析。',
|
|
135
|
+
CHECK_REVIEW_DELEGATION_GUIDANCE: '使用 KunlunZhima 当前可用的独立智能体能力启动一个全新评审上下文;若不支持,记录标准回退原因后由主智能体检查。',
|
|
130
136
|
},
|
|
131
137
|
},
|
|
132
138
|
workbuddy: {
|
|
@@ -147,6 +153,7 @@ const TOOL_PROFILES = Object.freeze({
|
|
|
147
153
|
SKILL_RUNTIME_DIR: '.workbuddy/skills',
|
|
148
154
|
HOOK_GATE_DESCRIPTION: '本项目未为此 Agent 部署自动 Hook 门禁;请遵循 Skill 自检与 CI/Git 证据门禁。',
|
|
149
155
|
SHELL_GUIDANCE: '在 Windows 上优先使用 PowerShell;如需 Bash 请使用 Git Bash。',
|
|
156
|
+
CHECK_REVIEW_DELEGATION_GUIDANCE: '使用 WorkBuddy 当前可用的独立 Agent/子代理能力启动一个全新评审上下文;能力不可用时才允许主 Agent 回退。',
|
|
150
157
|
},
|
|
151
158
|
},
|
|
152
159
|
codex: {
|
|
@@ -167,6 +174,7 @@ const TOOL_PROFILES = Object.freeze({
|
|
|
167
174
|
SKILL_RUNTIME_DIR: '.agents/skills',
|
|
168
175
|
HOOK_GATE_DESCRIPTION: '本项目未为此 Agent 部署自动 Hook 门禁;请遵循 Skill 自检与 CI/Git 证据门禁。',
|
|
169
176
|
SHELL_GUIDANCE: '在 Windows 上优先使用 PowerShell;如需 Bash 请使用 Git Bash。',
|
|
177
|
+
CHECK_REVIEW_DELEGATION_GUIDANCE: '使用 Codex 的多代理能力启动恰好一个独立评审子代理,并传入所需文件而不是作者对话;不得让评审子代理再次委派。',
|
|
170
178
|
},
|
|
171
179
|
},
|
|
172
180
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kld-sdd",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.17",
|
|
4
4
|
"description": "KLD SDD OpenSpec 项目初始化工具 - 一键部署 SDD skills",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -36,8 +36,13 @@
|
|
|
36
36
|
"skywalk-sdd/runtime-metadata.cjs",
|
|
37
37
|
"skywalk-sdd/reporting/",
|
|
38
38
|
"skywalk-sdd/context-client.cjs",
|
|
39
|
+
"skywalk-sdd/openspec-shim.cjs",
|
|
39
40
|
"skywalk-sdd/ontology/",
|
|
40
41
|
"skywalk-sdd/apply-worktree-finish.cjs",
|
|
42
|
+
"skywalk-sdd/spec-root.cjs",
|
|
43
|
+
"skywalk-sdd/lib/",
|
|
44
|
+
"skywalk-sdd/kb-upload.cjs",
|
|
45
|
+
"skywalk-sdd/kb-sync-identity.cjs",
|
|
41
46
|
"README.md"
|
|
42
47
|
]
|
|
43
48
|
}
|
|
@@ -11,32 +11,11 @@
|
|
|
11
11
|
const fs = require('fs');
|
|
12
12
|
const path = require('path');
|
|
13
13
|
const { execFileSync } = require('child_process');
|
|
14
|
+
const { parseArgs } = require('./lib/shared.cjs');
|
|
14
15
|
|
|
15
16
|
const TEMP_DIR_NAMES = ['node_modules', 'dist', '.turbo', '.next', 'coverage', 'build'];
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
const args = {};
|
|
19
|
-
for (let i = 2; i < argv.length; i++) {
|
|
20
|
-
const token = argv[i];
|
|
21
|
-
if (token === '--') continue;
|
|
22
|
-
if (token.startsWith('--')) {
|
|
23
|
-
const eq = token.indexOf('=');
|
|
24
|
-
if (eq !== -1) {
|
|
25
|
-
args[token.slice(2, eq)] = token.slice(eq + 1);
|
|
26
|
-
} else {
|
|
27
|
-
const key = token.slice(2);
|
|
28
|
-
const next = argv[i + 1];
|
|
29
|
-
if (next && !next.startsWith('--')) {
|
|
30
|
-
args[key] = next;
|
|
31
|
-
i++;
|
|
32
|
-
} else {
|
|
33
|
-
args[key] = true;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
return args;
|
|
39
|
-
}
|
|
18
|
+
// parseArgs 已迁移至 lib/shared.cjs
|
|
40
19
|
|
|
41
20
|
function git(cwd, gitArgs, opts = {}) {
|
|
42
21
|
return execFileSync('git', gitArgs, {
|