kld-sdd 2.6.7 → 2.6.8
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/bin/kld-sdd-init.js +39 -3
- package/lib/init.js +320 -45
- package/lib/workspace-layout.js +2 -0
- package/package.json +2 -2
- package/skywalk-sdd/ontology/active-changes.cjs +297 -0
- package/skywalk-sdd/ontology/change-key.cjs +241 -0
- package/skywalk-sdd/ontology/cli.cjs +135 -0
- package/skywalk-sdd/ontology/list-changes.cjs +110 -0
- package/skywalk-sdd/ontology/modules.cjs +167 -0
- package/skywalk-sdd/ontology/naming-diagnose.cjs +594 -0
- package/skywalk-sdd/ontology/sdd-config.cjs +335 -0
- package/skywalk-sdd/ontology/workspace-layout.cjs +194 -0
- package/templates/dot-sdd.yaml +8 -0
- package/templates/git-hooks/commit-msg-sdd-trailer.cjs +224 -0
- package/templates/modules.yaml +13 -0
- package/templates/openspec/proposal.md +7 -1
- package/templates/sdd.config.yaml +12 -0
- package/templates/skills/kld-sdd/opsx-apply/SKILL.md +3 -3
- package/templates/skills/kld-sdd/opsx-apply/checklist.md +1 -1
- package/templates/skills/kld-sdd/opsx-archive/SKILL.md +10 -0
- package/templates/skills/kld-sdd/opsx-check/SKILL.md +39 -2
- package/templates/skills/kld-sdd/opsx-explore/SKILL.md +37 -17
- package/templates/skills/kld-sdd/opsx-propose/SKILL.md +42 -13
- package/templates/skills/kld-sdd/opsx-task/SKILL.md +1 -0
- package/templates/skills/kld-sdd/opsx-tdd-core/checklist.md +1 -1
package/bin/kld-sdd-init.js
CHANGED
|
@@ -3,17 +3,53 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* KLD SDD 项目初始化工具
|
|
5
5
|
* 一键配置团队标准化开发环境,内置 openSpec 模版
|
|
6
|
-
*
|
|
6
|
+
*
|
|
7
7
|
* 子命令:
|
|
8
|
-
* (无参数)
|
|
9
|
-
* log
|
|
8
|
+
* (无参数) 运行项目初始化
|
|
9
|
+
* log SDD Telemetry CLI(记录阶段事件、查询指标)
|
|
10
|
+
* link-spec 将代码仓关联到本地 spec clone(git config sdd.specPath)
|
|
11
|
+
* sync-repos 工作目录下发现新增代码仓并轻量接入(不装 skills)
|
|
10
12
|
*/
|
|
11
13
|
|
|
14
|
+
const path = require('path');
|
|
12
15
|
const args = process.argv.slice(2);
|
|
13
16
|
|
|
14
17
|
if (args[0] === 'log') {
|
|
15
18
|
// Telemetry CLI 模式
|
|
16
19
|
require('../skywalk-sdd/index.js').main();
|
|
20
|
+
} else if (args[0] === 'link-spec') {
|
|
21
|
+
const sddConfig = require('../skywalk-sdd/ontology/sdd-config.cjs');
|
|
22
|
+
const pathArg = args.find((item) => item.startsWith('--path='));
|
|
23
|
+
const projectArg = args.find((item) => item.startsWith('--project='));
|
|
24
|
+
const positional = args.slice(1).find((item) => !item.startsWith('--'));
|
|
25
|
+
const specPath = pathArg ? pathArg.slice('--path='.length) : positional;
|
|
26
|
+
const projectRoot = path.resolve(projectArg ? projectArg.slice('--project='.length) : '.');
|
|
27
|
+
if (!specPath) {
|
|
28
|
+
console.error('用法: kld-sdd link-spec --path=<spec-clone> [--project=.]');
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
const result = sddConfig.setSpecPath(projectRoot, specPath);
|
|
32
|
+
if (!result.ok) {
|
|
33
|
+
console.error(`❌ ${result.message}`);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
console.log(`✅ 已设置 git config --local sdd.specPath = ${result.path}`);
|
|
37
|
+
} else if (args[0] === 'sync-repos') {
|
|
38
|
+
const { syncWorkspaceRepos } = require('../lib/init');
|
|
39
|
+
const projectArg = args.find((item) => item.startsWith('--project='));
|
|
40
|
+
const workspaceRoot = path.resolve(projectArg ? projectArg.slice('--project='.length) : '.');
|
|
41
|
+
const all = args.includes('--all');
|
|
42
|
+
const result = syncWorkspaceRepos(workspaceRoot, { onlyNew: !all });
|
|
43
|
+
if (!result.ok && result.attached.length === 0 && (!result.layout || !result.layout.specRepo)) {
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
if (result.failed && result.failed.length) {
|
|
47
|
+
for (const item of result.failed) {
|
|
48
|
+
console.error(`❌ ${item.repo}: ${item.message}`);
|
|
49
|
+
}
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
console.log(`✅ sync-repos 完成,接入 ${result.attached.length} 个代码仓`);
|
|
17
53
|
} else {
|
|
18
54
|
// 正常初始化模式
|
|
19
55
|
const { main } = require('../lib/init');
|
package/lib/init.js
CHANGED
|
@@ -28,6 +28,8 @@ const {
|
|
|
28
28
|
BRIDGE_MARKER,
|
|
29
29
|
} = require('./command-bridge');
|
|
30
30
|
const { deployCodebuddyHookPack } = require('./deploy-codebuddy-hooks');
|
|
31
|
+
const workspaceLayout = require('./workspace-layout');
|
|
32
|
+
const sddConfig = require('../skywalk-sdd/ontology/sdd-config.cjs');
|
|
31
33
|
|
|
32
34
|
const rl = readline.createInterface({
|
|
33
35
|
input: process.stdin,
|
|
@@ -797,11 +799,11 @@ function deploySddGuideManual(cwd = process.cwd(), pkgPath = getPackagePath()) {
|
|
|
797
799
|
* 部署 SDD Telemetry 数据目录
|
|
798
800
|
* 创建本地数据目录用于存储事件和活跃状态(最终报告落 openspec/changes/<change>/reports/,不再写 skywalk-sdd/reports/)
|
|
799
801
|
*/
|
|
800
|
-
function deployTelemetryDataDir() {
|
|
802
|
+
function deployTelemetryDataDir(targetCwd = process.cwd()) {
|
|
801
803
|
console.log('📊 正在初始化 SDD Telemetry 数据目录...');
|
|
802
804
|
|
|
803
805
|
const pkgPath = getPackagePath();
|
|
804
|
-
const cwd =
|
|
806
|
+
const cwd = path.resolve(targetCwd);
|
|
805
807
|
const dataDir = path.join(cwd, 'skywalk-sdd');
|
|
806
808
|
|
|
807
809
|
if (!fs.existsSync(dataDir)) {
|
|
@@ -901,16 +903,22 @@ function installGitHookShim(cwd, hookName, scriptPath) {
|
|
|
901
903
|
/**
|
|
902
904
|
* 部署 Git hooks / CI 兜底模板
|
|
903
905
|
* 这些脚本只做质量门禁和 CI 补充记录,不替代 OPSX 主采集链路。
|
|
906
|
+
*
|
|
907
|
+
* @param {string} [targetCwd]
|
|
908
|
+
* @param {{ mode?: 'full'|'commit-msg-only' }} [options]
|
|
909
|
+
* full: spec 仓(pre-commit/pre-push/commit-msg + CI)
|
|
910
|
+
* commit-msg-only: 代码仓(只装 Trailer Hook,不装 skills、不装文档门禁)
|
|
904
911
|
*/
|
|
905
|
-
function deployQualityGateTemplates() {
|
|
912
|
+
function deployQualityGateTemplates(targetCwd = process.cwd(), options = {}) {
|
|
906
913
|
const pkgPath = getPackagePath();
|
|
907
|
-
const cwd =
|
|
914
|
+
const cwd = path.resolve(targetCwd);
|
|
915
|
+
const mode = options.mode || 'full';
|
|
908
916
|
// U4: 非 Git 项目跳过 Git hooks / CI 兜底模板(按需部署,不污染非 Git 项目根)
|
|
909
917
|
if (!fs.existsSync(path.join(cwd, '.git'))) {
|
|
910
918
|
console.log('🧰 非 Git 项目,跳过 Git hooks / CI 兜底模板部署');
|
|
911
919
|
return false;
|
|
912
920
|
}
|
|
913
|
-
console.log(
|
|
921
|
+
console.log(`🧰 正在部署 SDD Git hooks(${mode}): ${cwd}`);
|
|
914
922
|
const dataDir = path.join(cwd, 'skywalk-sdd');
|
|
915
923
|
const gitHooksTemplateDir = path.join(pkgPath, 'templates', 'git-hooks');
|
|
916
924
|
const ciTemplateDir = path.join(pkgPath, 'templates', 'ci');
|
|
@@ -924,17 +932,162 @@ function deployQualityGateTemplates() {
|
|
|
924
932
|
console.log(` ⚠️ Git hooks 模板缺失: ${gitHooksTemplateDir}`);
|
|
925
933
|
}
|
|
926
934
|
|
|
927
|
-
if (fs.existsSync(ciTemplateDir)) {
|
|
935
|
+
if (mode === 'full' && fs.existsSync(ciTemplateDir)) {
|
|
928
936
|
copyDir(ciTemplateDir, targetCiDir);
|
|
929
937
|
console.log(' ✓ 部署 skywalk-sdd/ci/');
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
if (mode === 'full') {
|
|
941
|
+
installGitHookShim(cwd, 'pre-commit', 'skywalk-sdd/git-hooks/pre-commit-sdd-check.cjs');
|
|
942
|
+
installGitHookShim(cwd, 'pre-push', 'skywalk-sdd/git-hooks/pre-push-sdd-check.cjs');
|
|
943
|
+
}
|
|
944
|
+
installGitHookShim(cwd, 'commit-msg', 'skywalk-sdd/git-hooks/commit-msg-sdd-trailer.cjs');
|
|
945
|
+
|
|
946
|
+
console.log('✅ Git hooks 已就绪');
|
|
947
|
+
return true;
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
/**
|
|
951
|
+
* 代码仓轻量接入:只部署 Hook 运行所需 ontology + commit-msg,不部署 skills。
|
|
952
|
+
*/
|
|
953
|
+
function attachCodeRepoLite(codeRepoRoot, specRepoRoot, options = {}) {
|
|
954
|
+
const cwd = path.resolve(codeRepoRoot);
|
|
955
|
+
const specRoot = path.resolve(specRepoRoot);
|
|
956
|
+
const pkgPath = options.pkgPath || getPackagePath();
|
|
957
|
+
const label = path.basename(cwd);
|
|
958
|
+
|
|
959
|
+
if (!workspaceLayout.isGitRepo(cwd)) {
|
|
960
|
+
return { ok: false, repo: label, message: '不是 Git 仓库' };
|
|
961
|
+
}
|
|
962
|
+
if (!workspaceLayout.isGitRepo(specRoot)) {
|
|
963
|
+
return { ok: false, repo: label, message: `spec 路径不是 Git 仓库: ${specRoot}` };
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
console.log(`🔗 轻量接入代码仓(无 skills): ${label}`);
|
|
967
|
+
|
|
968
|
+
// Hook 依赖 ontology + git-hooks;不部署完整 telemetry/skills
|
|
969
|
+
const dataDir = path.join(cwd, 'skywalk-sdd');
|
|
970
|
+
const ontologySrc = path.join(pkgPath, 'skywalk-sdd', 'ontology');
|
|
971
|
+
const ontologyDst = path.join(dataDir, 'ontology');
|
|
972
|
+
if (fs.existsSync(ontologySrc)) {
|
|
973
|
+
copyDir(ontologySrc, ontologyDst);
|
|
974
|
+
}
|
|
975
|
+
deployQualityGateTemplates(cwd, { mode: 'commit-msg-only' });
|
|
976
|
+
|
|
977
|
+
const remote = sddConfig.gitRemoteUrl(specRoot) || 'git@gitlab.example.com:biz/xxx-sdd-specs.git';
|
|
978
|
+
const sddYamlPath = path.join(cwd, '.sdd.yaml');
|
|
979
|
+
if (!fs.existsSync(sddYamlPath)) {
|
|
980
|
+
fs.writeFileSync(
|
|
981
|
+
sddYamlPath,
|
|
982
|
+
[
|
|
983
|
+
'# 代码仓库关联团队 spec Git 仓库(个人绝对路径勿写入本文件)',
|
|
984
|
+
'version: 1',
|
|
985
|
+
`spec_repository: ${remote}`,
|
|
986
|
+
'',
|
|
987
|
+
].join('\n'),
|
|
988
|
+
'utf8',
|
|
989
|
+
);
|
|
990
|
+
console.log(` ✓ 写入 .sdd.yaml → ${remote}`);
|
|
930
991
|
} else {
|
|
931
|
-
console.log(
|
|
992
|
+
console.log(' ✓ .sdd.yaml 已存在,跳过覆盖');
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
const linked = sddConfig.setSpecPath(cwd, specRoot);
|
|
996
|
+
if (!linked.ok) {
|
|
997
|
+
return { ok: false, repo: label, message: linked.message };
|
|
932
998
|
}
|
|
999
|
+
console.log(` ✓ sdd.specPath = ${linked.path}`);
|
|
933
1000
|
|
|
934
|
-
|
|
935
|
-
|
|
1001
|
+
return { ok: true, repo: label, path: cwd, specPath: linked.path };
|
|
1002
|
+
}
|
|
936
1003
|
|
|
937
|
-
|
|
1004
|
+
/**
|
|
1005
|
+
* 部署 modules.yaml 到 spec 仓库根目录(若不存在)
|
|
1006
|
+
*/
|
|
1007
|
+
function deployModulesYaml(cwd = process.cwd(), pkgPath = getPackagePath()) {
|
|
1008
|
+
console.log('📦 正在检查 modules.yaml...');
|
|
1009
|
+
const source = path.join(pkgPath, 'templates', 'modules.yaml');
|
|
1010
|
+
const target = path.join(cwd, 'modules.yaml');
|
|
1011
|
+
if (!fs.existsSync(source)) {
|
|
1012
|
+
console.log(`⚠️ modules.yaml 模板缺失: ${source}`);
|
|
1013
|
+
return false;
|
|
1014
|
+
}
|
|
1015
|
+
if (fs.existsSync(target)) {
|
|
1016
|
+
console.log(' ✓ modules.yaml 已存在,跳过');
|
|
1017
|
+
return true;
|
|
1018
|
+
}
|
|
1019
|
+
fs.copyFileSync(source, target);
|
|
1020
|
+
console.log(`✅ 已创建 modules.yaml: ${target}`);
|
|
1021
|
+
return true;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
/**
|
|
1025
|
+
* 部署 sdd.config.yaml(活动变更登记表)到 spec 仓库根目录(若不存在)
|
|
1026
|
+
* propose 隐式写入、archive 隐式移除;代码仓 AI 与 commit-msg Hook 读取
|
|
1027
|
+
*/
|
|
1028
|
+
function deploySddConfigYaml(cwd = process.cwd(), pkgPath = getPackagePath()) {
|
|
1029
|
+
console.log('📦 正在检查 sdd.config.yaml...');
|
|
1030
|
+
const source = path.join(pkgPath, 'templates', 'sdd.config.yaml');
|
|
1031
|
+
const target = path.join(cwd, 'sdd.config.yaml');
|
|
1032
|
+
if (!fs.existsSync(source)) {
|
|
1033
|
+
console.log(`⚠️ sdd.config.yaml 模板缺失: ${source}`);
|
|
1034
|
+
return false;
|
|
1035
|
+
}
|
|
1036
|
+
if (fs.existsSync(target)) {
|
|
1037
|
+
console.log(' ✓ sdd.config.yaml 已存在,跳过');
|
|
1038
|
+
return true;
|
|
1039
|
+
}
|
|
1040
|
+
fs.copyFileSync(source, target);
|
|
1041
|
+
console.log(`✅ 已创建 sdd.config.yaml: ${target}`);
|
|
1042
|
+
return true;
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
/**
|
|
1046
|
+
* 部署 .sdd.yaml 示例到代码仓库(若不存在且不像 spec 仓)
|
|
1047
|
+
* spec 仓特征:存在 openspec/changes 或 modules.yaml
|
|
1048
|
+
*/
|
|
1049
|
+
function deployDotSddYaml(cwd = process.cwd(), pkgPath = getPackagePath()) {
|
|
1050
|
+
const hasSpecLayout =
|
|
1051
|
+
fs.existsSync(path.join(cwd, 'openspec', 'changes')) ||
|
|
1052
|
+
fs.existsSync(path.join(cwd, 'modules.yaml'));
|
|
1053
|
+
if (hasSpecLayout) {
|
|
1054
|
+
return false;
|
|
1055
|
+
}
|
|
1056
|
+
const source = path.join(pkgPath, 'templates', 'dot-sdd.yaml');
|
|
1057
|
+
const target = path.join(cwd, '.sdd.yaml');
|
|
1058
|
+
if (!fs.existsSync(source) || fs.existsSync(target)) {
|
|
1059
|
+
return false;
|
|
1060
|
+
}
|
|
1061
|
+
console.log('🔗 检测到代码仓库布局,部署 .sdd.yaml 示例...');
|
|
1062
|
+
fs.copyFileSync(source, target);
|
|
1063
|
+
console.log(`✅ 已创建 .sdd.yaml(请修改 spec_repository): ${target}`);
|
|
1064
|
+
return true;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
/**
|
|
1068
|
+
* 单仓(openspec 与代码同仓)声明 layout: mono。
|
|
1069
|
+
* commit-msg Hook 靠它跳过「spec 工作区干净」检查并只写 Spec-Change。
|
|
1070
|
+
*/
|
|
1071
|
+
function deployMonoSddYaml(cwd = process.cwd()) {
|
|
1072
|
+
const target = path.join(cwd, '.sdd.yaml');
|
|
1073
|
+
if (fs.existsSync(target)) {
|
|
1074
|
+
const existing = fs.readFileSync(target, 'utf8');
|
|
1075
|
+
if (/^layout:\s*mono\s*$/m.test(existing)) {
|
|
1076
|
+
console.log(' ✓ .sdd.yaml 已声明 layout: mono,跳过');
|
|
1077
|
+
return true;
|
|
1078
|
+
}
|
|
1079
|
+
console.log('⚠️ .sdd.yaml 已存在但未声明 layout: mono;单仓请手动补一行 `layout: mono`');
|
|
1080
|
+
return false;
|
|
1081
|
+
}
|
|
1082
|
+
const lines = [
|
|
1083
|
+
'# 单仓布局:openspec 文档与代码在同一个仓库',
|
|
1084
|
+
'# commit-msg 只写 Spec-Change(spec 与代码同属一次 commit,无需 Spec-Revision 指针)',
|
|
1085
|
+
'version: 1',
|
|
1086
|
+
'layout: mono',
|
|
1087
|
+
'',
|
|
1088
|
+
];
|
|
1089
|
+
fs.writeFileSync(target, lines.join('\n'), 'utf8');
|
|
1090
|
+
console.log(`✅ 已创建 .sdd.yaml(layout: mono): ${target}`);
|
|
938
1091
|
return true;
|
|
939
1092
|
}
|
|
940
1093
|
|
|
@@ -1017,6 +1170,7 @@ KLD SDD 项目初始化工具
|
|
|
1017
1170
|
用法:
|
|
1018
1171
|
kld-sdd-init [选项]
|
|
1019
1172
|
npx kld-sdd [选项]
|
|
1173
|
+
kld-sdd sync-repos # 工作目录下发现新增代码仓并轻量接入
|
|
1020
1174
|
|
|
1021
1175
|
选项:
|
|
1022
1176
|
-h, --help 显示帮助信息
|
|
@@ -1025,13 +1179,61 @@ KLD SDD 项目初始化工具
|
|
|
1025
1179
|
--skip-template 跳过复制内置模版
|
|
1026
1180
|
--tool <name> 指定编辑器,可用值: cursor, claude, codebuddy, qoder, opencode, kunlunzhima, workbuddy, codex, all
|
|
1027
1181
|
|
|
1182
|
+
工作区模式(在个人工作目录执行):
|
|
1183
|
+
- skills / 编辑器配置只部署一次到当前工作目录
|
|
1184
|
+
- 自动发现子目录 Git 仓库
|
|
1185
|
+
- spec 仓:modules.yaml / openspec 模板 / 文档门禁
|
|
1186
|
+
- 代码仓:仅 commit-msg Hook + .sdd.yaml + link-spec(不装 skills)
|
|
1187
|
+
- Trailer 数据仍在代码 commit 时由 Hook 写入
|
|
1188
|
+
|
|
1028
1189
|
示例:
|
|
1029
1190
|
kld-sdd-init # 完整初始化流程
|
|
1030
1191
|
kld-sdd-init --skip-openspec # 跳过 openspec,直接部署 skills
|
|
1031
1192
|
kld-sdd-init --tool codex # 仅部署 Codex 配置
|
|
1193
|
+
kld-sdd sync-repos # 新代码仓加入后同步接入
|
|
1032
1194
|
`);
|
|
1033
1195
|
}
|
|
1034
1196
|
|
|
1197
|
+
/**
|
|
1198
|
+
* 工作区:为已发现的代码仓做轻量接入,并刷新 .sdd-workspace.yaml
|
|
1199
|
+
*/
|
|
1200
|
+
function syncWorkspaceRepos(workspaceRoot = process.cwd(), options = {}) {
|
|
1201
|
+
const root = path.resolve(workspaceRoot);
|
|
1202
|
+
const { layout, newcomers, saved } = workspaceLayout.findNewCodeRepos(root);
|
|
1203
|
+
const onlyNew = options.onlyNew !== false;
|
|
1204
|
+
|
|
1205
|
+
if (!layout.specRepo) {
|
|
1206
|
+
console.log('❌ 未发现 spec 仓库(需子目录含 modules.yaml / openspec/changes / *-sdd-specs)');
|
|
1207
|
+
return { ok: false, attached: [], layout };
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
console.log(`📂 工作目录: ${root}`);
|
|
1211
|
+
console.log(`📄 Spec 仓: ${layout.specRepo.name}`);
|
|
1212
|
+
console.log(`🔎 已发现代码仓: ${layout.codeRepos.map((c) => c.name).join(', ') || '(无)'}`);
|
|
1213
|
+
|
|
1214
|
+
const targets = onlyNew && saved.ok
|
|
1215
|
+
? newcomers
|
|
1216
|
+
: layout.codeRepos;
|
|
1217
|
+
|
|
1218
|
+
if (onlyNew && saved.ok) {
|
|
1219
|
+
console.log(`🆕 待接入新仓: ${newcomers.map((c) => c.name).join(', ') || '(无)'}`);
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
const attached = [];
|
|
1223
|
+
const failed = [];
|
|
1224
|
+
for (const repo of targets) {
|
|
1225
|
+
const result = attachCodeRepoLite(repo.abs, layout.specRepo.abs);
|
|
1226
|
+
if (result.ok) attached.push(result);
|
|
1227
|
+
else failed.push(result);
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
// 刷新清单(包含本次发现的全部代码仓)
|
|
1231
|
+
const filePath = workspaceLayout.writeWorkspaceFile(root, layout);
|
|
1232
|
+
console.log(`✅ 已更新 ${path.basename(filePath)}`);
|
|
1233
|
+
|
|
1234
|
+
return { ok: failed.length === 0, attached, failed, layout, filePath };
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1035
1237
|
/**
|
|
1036
1238
|
* 主函数
|
|
1037
1239
|
*/
|
|
@@ -1071,44 +1273,87 @@ async function main() {
|
|
|
1071
1273
|
process.exit(1);
|
|
1072
1274
|
}
|
|
1073
1275
|
|
|
1074
|
-
|
|
1276
|
+
const workspaceRoot = process.cwd();
|
|
1277
|
+
const layout = workspaceLayout.detectWorkspaceLayout(workspaceRoot);
|
|
1278
|
+
if (layout.isWorkspace) {
|
|
1279
|
+
console.log('📂 检测到个人工作目录布局(子目录 Git 仓库)');
|
|
1280
|
+
console.log(` Spec: ${layout.specRepo ? layout.specRepo.name : '(未发现)'}`);
|
|
1281
|
+
console.log(` 代码仓: ${layout.codeRepos.map((c) => c.name).join(', ') || '(无)'}`);
|
|
1282
|
+
console.log(' → skills 只部署到本工作目录;代码仓仅轻量接入 Hook/关联');
|
|
1283
|
+
console.log();
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
// 1. openspec init:工作区模式下若有 spec 仓则在其内执行
|
|
1075
1287
|
if (!skipOpenspec) {
|
|
1076
|
-
|
|
1288
|
+
if (layout.isWorkspace && layout.specRepo) {
|
|
1289
|
+
const prev = process.cwd();
|
|
1290
|
+
try {
|
|
1291
|
+
process.chdir(layout.specRepo.abs);
|
|
1292
|
+
await runOpenspecInit(selectedTools);
|
|
1293
|
+
} finally {
|
|
1294
|
+
process.chdir(prev);
|
|
1295
|
+
}
|
|
1296
|
+
} else {
|
|
1297
|
+
await runOpenspecInit(selectedTools);
|
|
1298
|
+
}
|
|
1077
1299
|
}
|
|
1078
1300
|
|
|
1079
1301
|
if (!skipTemplate) {
|
|
1080
|
-
// 2.
|
|
1302
|
+
// 2/3. skills 与编辑器产物:永远只装在当前工作目录一次
|
|
1081
1303
|
cleanupNativeOpenspecCommands(selectedTools);
|
|
1082
|
-
|
|
1083
|
-
// 3. 清理旧版嵌套 skills/kld-sdd/ bundle(Claude Code 无法识别二层目录)
|
|
1084
1304
|
cleanupLegacyBundledOpsxSkills(selectedTools);
|
|
1085
|
-
|
|
1086
|
-
// 3.1 部署 SDD opsx skills(扁平到 .claude/skills/opsx-*/)
|
|
1087
1305
|
deployOpsxSkills(selectedTools);
|
|
1088
|
-
|
|
1089
|
-
// 3.2 清理原生 openspec-* skills(防止残留)
|
|
1090
1306
|
cleanupNativeOpenspecSkills(selectedTools);
|
|
1307
|
+
deployProfileArtifacts(selectedTools, workspaceRoot, getPackagePath());
|
|
1308
|
+
|
|
1309
|
+
if (layout.isWorkspace) {
|
|
1310
|
+
// 工作目录本身保留一份 telemetry,供 skills 调用
|
|
1311
|
+
deployTelemetryDataDir(workspaceRoot);
|
|
1312
|
+
|
|
1313
|
+
if (layout.specRepo) {
|
|
1314
|
+
const prev = process.cwd();
|
|
1315
|
+
try {
|
|
1316
|
+
process.chdir(layout.specRepo.abs);
|
|
1317
|
+
deployTelemetryDataDir(layout.specRepo.abs);
|
|
1318
|
+
deployQualityGateTemplates(layout.specRepo.abs, { mode: 'full' });
|
|
1319
|
+
deployModulesYaml(layout.specRepo.abs);
|
|
1320
|
+
deploySddConfigYaml(layout.specRepo.abs);
|
|
1321
|
+
copyTemplatesToProject();
|
|
1322
|
+
initGlobalOverview();
|
|
1323
|
+
deploySddGuideManual(layout.specRepo.abs);
|
|
1324
|
+
} finally {
|
|
1325
|
+
process.chdir(prev);
|
|
1326
|
+
}
|
|
1091
1327
|
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1328
|
+
console.log();
|
|
1329
|
+
console.log('🔗 正在为子代码仓做轻量接入(不安装 skills)...');
|
|
1330
|
+
for (const repo of layout.codeRepos) {
|
|
1331
|
+
attachCodeRepoLite(repo.abs, layout.specRepo.abs);
|
|
1332
|
+
}
|
|
1333
|
+
const wsFile = workspaceLayout.writeWorkspaceFile(workspaceRoot, layout);
|
|
1334
|
+
console.log(`✅ 工作区清单: ${wsFile}`);
|
|
1335
|
+
} else {
|
|
1336
|
+
console.log('⚠️ 未发现 spec 仓,已跳过代码仓 Hook 接入。请先准备 *-sdd-specs 子目录后执行: kld-sdd sync-repos');
|
|
1337
|
+
}
|
|
1338
|
+
} else {
|
|
1339
|
+
// 单仓模式(旧行为):当前目录既是项目根
|
|
1340
|
+
deployTelemetryDataDir(workspaceRoot);
|
|
1341
|
+
deployQualityGateTemplates(workspaceRoot, { mode: 'full' });
|
|
1342
|
+
deployModulesYaml(workspaceRoot);
|
|
1343
|
+
deploySddConfigYaml(workspaceRoot);
|
|
1344
|
+
if (workspaceLayout.isGitRepo(workspaceRoot)) {
|
|
1345
|
+
// openspec 与代码同仓:显式声明单仓,Hook 才会写 Spec-Change
|
|
1346
|
+
deployMonoSddYaml(workspaceRoot);
|
|
1347
|
+
} else {
|
|
1348
|
+
deployDotSddYaml(workspaceRoot);
|
|
1349
|
+
}
|
|
1350
|
+
copyTemplatesToProject();
|
|
1351
|
+
initGlobalOverview();
|
|
1352
|
+
deploySddGuideManual(workspaceRoot);
|
|
1353
|
+
}
|
|
1109
1354
|
}
|
|
1110
1355
|
|
|
1111
|
-
// 7.
|
|
1356
|
+
// 7. 更新工作目录 .gitignore
|
|
1112
1357
|
updateGitignore();
|
|
1113
1358
|
|
|
1114
1359
|
const selectedNames = selectedTools.map(t => TOOL_CONFIGS[t].name).join(', ');
|
|
@@ -1120,11 +1365,28 @@ async function main() {
|
|
|
1120
1365
|
console.log();
|
|
1121
1366
|
console.log(`已为以下编辑器生成配置: ${selectedNames}`);
|
|
1122
1367
|
console.log();
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1368
|
+
if (layout.isWorkspace) {
|
|
1369
|
+
console.log('工作区部署结果:');
|
|
1370
|
+
console.log(' 🎯 工作目录 .*/skills/opsx-*/ # skills 只装这一份');
|
|
1371
|
+
console.log(' 📋 .sdd-workspace.yaml # 子仓编排清单');
|
|
1372
|
+
if (layout.specRepo) {
|
|
1373
|
+
console.log(` 📄 ${layout.specRepo.name}/modules.yaml + openspec-templates`);
|
|
1374
|
+
}
|
|
1375
|
+
for (const repo of layout.codeRepos) {
|
|
1376
|
+
console.log(` 🔗 ${repo.name}/ ← commit-msg Hook + .sdd.yaml(无 skills)`);
|
|
1377
|
+
}
|
|
1378
|
+
console.log();
|
|
1379
|
+
console.log('新代码仓加入后执行:');
|
|
1380
|
+
console.log(' kld-sdd sync-repos');
|
|
1381
|
+
console.log('Trailer 仍在各代码仓 git commit 时由 Hook 写入。');
|
|
1382
|
+
} else {
|
|
1383
|
+
console.log('已生成/覆盖:');
|
|
1384
|
+
console.log(' 🌐 openspec/specs/overview.md # 全局架构约束(数据字典、接口规范)');
|
|
1385
|
+
console.log(' 📖 openspec/kld-sdd操作手册.html # SDD 操作手册(HTML)');
|
|
1386
|
+
console.log(' 📄 openspec-templates/ # 标准文档模版(参考用)');
|
|
1387
|
+
console.log(' 📦 modules.yaml / .sdd.yaml # 多仓库命名与关联配置');
|
|
1388
|
+
console.log(' 🎯 .*/skills/opsx-*/ # SDD skills(扁平一层)');
|
|
1389
|
+
}
|
|
1128
1390
|
if (selectedTools.includes('kunlunzhima')) {
|
|
1129
1391
|
console.log(' 📎 .kunlunzhima/commands/opsx/ # KunlunZhima OPSX command bridge(11 个)');
|
|
1130
1392
|
console.log(' ℹ️ KunlunZhima 通过 commands/skills 入口使用 SDD,未启用自动 Hook');
|
|
@@ -1135,9 +1397,11 @@ async function main() {
|
|
|
1135
1397
|
if (selectedTools.includes('claude')) {
|
|
1136
1398
|
console.log(' 🪝 .claude/hooks/ # Claude Code SDD Hook Pack(可选增强)');
|
|
1137
1399
|
}
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1400
|
+
if (!layout.isWorkspace) {
|
|
1401
|
+
console.log(' 📊 skywalk-sdd/ # SDD Telemetry 数据目录');
|
|
1402
|
+
console.log(' 🧰 skywalk-sdd/git-hooks/ # Git hooks 质量门禁模板');
|
|
1403
|
+
console.log(' 🧪 skywalk-sdd/ci/ # CI 兜底采集模板');
|
|
1404
|
+
}
|
|
1141
1405
|
console.log();
|
|
1142
1406
|
|
|
1143
1407
|
// 统一的 skill 格式说明
|
|
@@ -1156,6 +1420,9 @@ async function main() {
|
|
|
1156
1420
|
|
|
1157
1421
|
console.log();
|
|
1158
1422
|
console.log('后续步骤:');
|
|
1423
|
+
if (layout.isWorkspace && layout.specRepo) {
|
|
1424
|
+
console.log(` 0. 文档类命令请以 spec 仓为 --project(例: --project=${layout.specRepo.name})`);
|
|
1425
|
+
}
|
|
1159
1426
|
console.log(' 1. 激活 opsx-propose skill,输入变更名称开始创建文档');
|
|
1160
1427
|
console.log(' 2. 按顺序执行 propose → spec → design → task');
|
|
1161
1428
|
console.log(' 3. 激活 opsx-check 验证文档质量');
|
|
@@ -1187,6 +1454,14 @@ module.exports = {
|
|
|
1187
1454
|
deployOpsxSkills,
|
|
1188
1455
|
deployProfileArtifacts,
|
|
1189
1456
|
deploySddGuideManual,
|
|
1457
|
+
deployModulesYaml,
|
|
1458
|
+
deploySddConfigYaml,
|
|
1459
|
+
deployDotSddYaml,
|
|
1460
|
+
deployMonoSddYaml,
|
|
1461
|
+
deployQualityGateTemplates,
|
|
1462
|
+
deployTelemetryDataDir,
|
|
1463
|
+
attachCodeRepoLite,
|
|
1464
|
+
syncWorkspaceRepos,
|
|
1190
1465
|
copyDirRendered,
|
|
1191
1466
|
parseSelectedTools,
|
|
1192
1467
|
TOOL_CONFIGS,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kld-sdd",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.8",
|
|
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/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/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"
|
|
12
12
|
},
|
|
13
13
|
"keywords": [
|
|
14
14
|
"kld",
|