kld-sdd 2.6.10 → 2.6.11
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
CHANGED
|
@@ -862,7 +862,7 @@ function deployTelemetryDataDir(targetCwd = process.cwd()) {
|
|
|
862
862
|
return true;
|
|
863
863
|
}
|
|
864
864
|
|
|
865
|
-
function installGitHookShim(cwd, hookName, scriptPath) {
|
|
865
|
+
function installGitHookShim(cwd, hookName, scriptPath, options = {}) {
|
|
866
866
|
const hooksDir = path.join(cwd, '.git', 'hooks');
|
|
867
867
|
if (!fs.existsSync(hooksDir)) {
|
|
868
868
|
return;
|
|
@@ -870,11 +870,20 @@ function installGitHookShim(cwd, hookName, scriptPath) {
|
|
|
870
870
|
|
|
871
871
|
const hookPath = path.join(hooksDir, hookName);
|
|
872
872
|
const marker = 'KLD SDD quality gate';
|
|
873
|
+
// Git 把 commit-msg 文件路径放在 $1;--project 等额外参数必须跟在 "$@" 后面,
|
|
874
|
+
// 否则 trailer 脚本会把 --project=… 误当成 message 文件(argv[2])。
|
|
875
|
+
const extraArgs = options.extraArgs || '';
|
|
876
|
+
const nodeLine = extraArgs
|
|
877
|
+
? ` node "${scriptPath}" "$@" ${extraArgs}`
|
|
878
|
+
: ` node "${scriptPath}" "$@"`;
|
|
873
879
|
const shim = [
|
|
874
880
|
'#!/bin/sh',
|
|
875
881
|
`# ${marker}`,
|
|
876
882
|
`if [ -f "${scriptPath}" ]; then`,
|
|
877
|
-
|
|
883
|
+
nodeLine,
|
|
884
|
+
'else',
|
|
885
|
+
` echo "[kld-sdd] Hook 脚本不存在: ${scriptPath}" >&2`,
|
|
886
|
+
' exit 1',
|
|
878
887
|
'fi',
|
|
879
888
|
'',
|
|
880
889
|
].join('\n');
|
|
@@ -882,10 +891,21 @@ function installGitHookShim(cwd, hookName, scriptPath) {
|
|
|
882
891
|
if (fs.existsSync(hookPath)) {
|
|
883
892
|
const existing = fs.readFileSync(hookPath, 'utf8');
|
|
884
893
|
if (existing.includes(marker)) {
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
894
|
+
// 已是 SDD shim:若指向路径变化(如改为引用 spec 仓),允许覆盖升级
|
|
895
|
+
if (existing.includes(scriptPath) && (!extraArgs || existing.includes(extraArgs.trim()))) {
|
|
896
|
+
console.log(` ✓ .git/hooks/${hookName} 已包含 SDD 质量门禁`);
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
899
|
+
fs.writeFileSync(hookPath, shim, 'utf8');
|
|
900
|
+
try {
|
|
901
|
+
fs.chmodSync(hookPath, 0o755);
|
|
902
|
+
} catch {
|
|
903
|
+
// ignore
|
|
904
|
+
}
|
|
905
|
+
console.log(` ✓ 更新 .git/hooks/${hookName} SDD 质量门禁`);
|
|
906
|
+
return;
|
|
888
907
|
}
|
|
908
|
+
console.log(` ℹ️ .git/hooks/${hookName} 已存在,已保留不覆盖;可手动调用 ${scriptPath}`);
|
|
889
909
|
return;
|
|
890
910
|
}
|
|
891
911
|
|
|
@@ -898,14 +918,52 @@ function installGitHookShim(cwd, hookName, scriptPath) {
|
|
|
898
918
|
console.log(` ✓ 安装 .git/hooks/${hookName} SDD 质量门禁`);
|
|
899
919
|
}
|
|
900
920
|
|
|
921
|
+
/**
|
|
922
|
+
* 确保 spec 包裹包内有 commit-msg 脚本(供代码仓 Hook 远程引用)。
|
|
923
|
+
*/
|
|
924
|
+
function ensureSpecCommitMsgScript(specRepoRoot, pkgPath = getPackagePath()) {
|
|
925
|
+
const specRoot = path.resolve(specRepoRoot);
|
|
926
|
+
const scriptPath = path.join(specRoot, 'skywalk-sdd', 'git-hooks', 'commit-msg-sdd-trailer.cjs');
|
|
927
|
+
if (fs.existsSync(scriptPath)) return { ok: true, scriptPath };
|
|
928
|
+
|
|
929
|
+
const src = path.join(pkgPath, 'templates', 'git-hooks', 'commit-msg-sdd-trailer.cjs');
|
|
930
|
+
if (!fs.existsSync(src)) {
|
|
931
|
+
return { ok: false, message: `commit-msg 模板缺失: ${src}` };
|
|
932
|
+
}
|
|
933
|
+
const ontologySrc = path.join(pkgPath, 'skywalk-sdd', 'ontology');
|
|
934
|
+
const ontologyDst = path.join(specRoot, 'skywalk-sdd', 'ontology');
|
|
935
|
+
if (fs.existsSync(ontologySrc) && !fs.existsSync(path.join(ontologyDst, 'change-key.cjs'))) {
|
|
936
|
+
copyDir(ontologySrc, ontologyDst);
|
|
937
|
+
}
|
|
938
|
+
fs.mkdirSync(path.dirname(scriptPath), { recursive: true });
|
|
939
|
+
fs.copyFileSync(src, scriptPath);
|
|
940
|
+
return { ok: true, scriptPath, deployed: true };
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
/**
|
|
944
|
+
* 代码仓 commit-msg:只写 .git/hooks,脚本与 ontology 一律用 spec 仓那一份。
|
|
945
|
+
*/
|
|
946
|
+
function installCodeRepoCommitMsgHook(codeRepoRoot, specRepoRoot, options = {}) {
|
|
947
|
+
const cwd = path.resolve(codeRepoRoot);
|
|
948
|
+
const ensured = ensureSpecCommitMsgScript(specRepoRoot, options.pkgPath || getPackagePath());
|
|
949
|
+
if (!ensured.ok) {
|
|
950
|
+
console.log(` ⚠️ ${ensured.message}`);
|
|
951
|
+
return false;
|
|
952
|
+
}
|
|
953
|
+
installGitHookShim(cwd, 'commit-msg', ensured.scriptPath, {
|
|
954
|
+
extraArgs: `--project=${cwd}`,
|
|
955
|
+
});
|
|
956
|
+
return true;
|
|
957
|
+
}
|
|
958
|
+
|
|
901
959
|
/**
|
|
902
960
|
* 部署 Git hooks / CI 兜底模板
|
|
903
961
|
* 这些脚本只做质量门禁和 CI 补充记录,不替代 OPSX 主采集链路。
|
|
904
962
|
*
|
|
905
963
|
* @param {string} [targetCwd]
|
|
906
964
|
* @param {{ mode?: 'full'|'commit-msg-only' }} [options]
|
|
907
|
-
* full: spec
|
|
908
|
-
* commit-msg-only:
|
|
965
|
+
* full: spec 包裹包(pre-commit/pre-push/commit-msg + CI)
|
|
966
|
+
* commit-msg-only: 单仓 Git 根(本仓落 skywalk-sdd/git-hooks;多仓代码仓请用 installCodeRepoCommitMsgHook)
|
|
909
967
|
*/
|
|
910
968
|
function deployQualityGateTemplates(targetCwd = process.cwd(), options = {}) {
|
|
911
969
|
const pkgPath = getPackagePath();
|
|
@@ -946,12 +1004,12 @@ function deployQualityGateTemplates(targetCwd = process.cwd(), options = {}) {
|
|
|
946
1004
|
}
|
|
947
1005
|
|
|
948
1006
|
/**
|
|
949
|
-
*
|
|
1007
|
+
* 代码仓轻量接入:只写 .sdd.yaml + sdd.specPath + .git/hooks/commit-msg。
|
|
1008
|
+
* 不在代码仓落 skywalk-sdd/——Hook 脚本与 ontology 引用 spec 包裹包那一份。
|
|
950
1009
|
*/
|
|
951
1010
|
function attachCodeRepoLite(codeRepoRoot, specRepoRoot, options = {}) {
|
|
952
1011
|
const cwd = path.resolve(codeRepoRoot);
|
|
953
1012
|
const specRoot = path.resolve(specRepoRoot);
|
|
954
|
-
const pkgPath = options.pkgPath || getPackagePath();
|
|
955
1013
|
const label = path.basename(cwd);
|
|
956
1014
|
|
|
957
1015
|
if (!workspaceLayout.isGitRepo(cwd)) {
|
|
@@ -961,16 +1019,7 @@ function attachCodeRepoLite(codeRepoRoot, specRepoRoot, options = {}) {
|
|
|
961
1019
|
return { ok: false, repo: label, message: `spec 路径不是 Git 仓库: ${specRoot}` };
|
|
962
1020
|
}
|
|
963
1021
|
|
|
964
|
-
console.log(`🔗 轻量接入代码仓(无 skills): ${label}`);
|
|
965
|
-
|
|
966
|
-
// Hook 依赖 ontology + git-hooks;不部署完整 telemetry/skills
|
|
967
|
-
const dataDir = path.join(cwd, 'skywalk-sdd');
|
|
968
|
-
const ontologySrc = path.join(pkgPath, 'skywalk-sdd', 'ontology');
|
|
969
|
-
const ontologyDst = path.join(dataDir, 'ontology');
|
|
970
|
-
if (fs.existsSync(ontologySrc)) {
|
|
971
|
-
copyDir(ontologySrc, ontologyDst);
|
|
972
|
-
}
|
|
973
|
-
deployQualityGateTemplates(cwd, { mode: 'commit-msg-only' });
|
|
1022
|
+
console.log(`🔗 轻量接入代码仓(无 skywalk-sdd/、无 skills): ${label}`);
|
|
974
1023
|
|
|
975
1024
|
const remote = sddConfig.gitRemoteUrl(specRoot) || 'git@gitlab.example.com:biz/xxx-sdd-specs.git';
|
|
976
1025
|
const sddYamlPath = path.join(cwd, '.sdd.yaml');
|
|
@@ -996,6 +1045,9 @@ function attachCodeRepoLite(codeRepoRoot, specRepoRoot, options = {}) {
|
|
|
996
1045
|
}
|
|
997
1046
|
console.log(` ✓ sdd.specPath = ${linked.path}`);
|
|
998
1047
|
|
|
1048
|
+
installCodeRepoCommitMsgHook(cwd, specRoot, options);
|
|
1049
|
+
console.log(' ✓ commit-msg → spec 仓 skywalk-sdd/git-hooks/(代码仓不落 skywalk-sdd/)');
|
|
1050
|
+
|
|
999
1051
|
return { ok: true, repo: label, path: cwd, specPath: linked.path };
|
|
1000
1052
|
}
|
|
1001
1053
|
|
|
@@ -1155,6 +1207,98 @@ function writeSddSpecRootHint(anchorDir, specPackageAbs) {
|
|
|
1155
1207
|
return filePath;
|
|
1156
1208
|
}
|
|
1157
1209
|
|
|
1210
|
+
/**
|
|
1211
|
+
* 工作区 / 单仓 Git 根的 skywalk-sdd/ 改为指向包裹包内唯一实体(符号链接)。
|
|
1212
|
+
* skills 仍可用 `node skywalk-sdd/log.cjs`,实体文件只在 *-sdd-specs/skywalk-sdd/。
|
|
1213
|
+
* 链接失败时回退为薄转发脚本(不复制 ontology)。
|
|
1214
|
+
*/
|
|
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) {
|
|
1255
|
+
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' };
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
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);
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
} catch (err) {
|
|
1287
|
+
console.log(` ⚠️ 清理旧 skywalk-sdd/ 失败: ${err.message}`);
|
|
1288
|
+
return deploySkywalkForwarders(anchor, specPackageAbs);
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
const rel = path.relative(anchor, targetAbs);
|
|
1292
|
+
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 };
|
|
1296
|
+
} catch (err) {
|
|
1297
|
+
console.log(` ⚠️ 符号链接失败 (${err.message}),回退为转发脚本`);
|
|
1298
|
+
return deploySkywalkForwarders(anchor, specPackageAbs);
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1158
1302
|
/**
|
|
1159
1303
|
* 检测当前项目已初始化的 AI 编辑器
|
|
1160
1304
|
*/
|
|
@@ -1185,6 +1329,8 @@ function updateGitignore() {
|
|
|
1185
1329
|
'.agents/commands/personal-*',
|
|
1186
1330
|
'skywalk-sdd/events/',
|
|
1187
1331
|
'skywalk-sdd/state/',
|
|
1332
|
+
'**/skywalk-sdd/events/',
|
|
1333
|
+
'**/skywalk-sdd/state/',
|
|
1188
1334
|
'.worktrees/'
|
|
1189
1335
|
];
|
|
1190
1336
|
|
|
@@ -1198,9 +1344,11 @@ function updateGitignore() {
|
|
|
1198
1344
|
.workbuddy/commands/personal-*
|
|
1199
1345
|
.agents/commands/personal-*
|
|
1200
1346
|
|
|
1201
|
-
# SDD Telemetry
|
|
1347
|
+
# SDD Telemetry 数据(实体在 *-sdd-specs/skywalk-sdd/;工作区可能是 symlink)
|
|
1202
1348
|
skywalk-sdd/events/
|
|
1203
1349
|
skywalk-sdd/state/
|
|
1350
|
+
**/skywalk-sdd/events/
|
|
1351
|
+
**/skywalk-sdd/state/
|
|
1204
1352
|
|
|
1205
1353
|
# SDD Apply 隔离 worktree(本地临时目录)
|
|
1206
1354
|
.worktrees/
|
|
@@ -1360,8 +1508,6 @@ async function main() {
|
|
|
1360
1508
|
deployProfileArtifacts(selectedTools, workspaceRoot, getPackagePath());
|
|
1361
1509
|
|
|
1362
1510
|
if (layout.isWorkspace) {
|
|
1363
|
-
deployTelemetryDataDir(workspaceRoot);
|
|
1364
|
-
|
|
1365
1511
|
if (!specPackage) {
|
|
1366
1512
|
console.log('📦 未发现 spec 仓,正在创建统一 SDD 包裹包(独立 Git)...');
|
|
1367
1513
|
specPackage = workspaceLayout.ensureSpecPackage(workspaceRoot, { initGit: true });
|
|
@@ -1373,6 +1519,8 @@ async function main() {
|
|
|
1373
1519
|
asIndependentGit: true,
|
|
1374
1520
|
});
|
|
1375
1521
|
writeSddSpecRootHint(workspaceRoot, specPackage.abs);
|
|
1522
|
+
// 工作区不复制 skywalk-sdd 实体,只链到包裹包(skills 路径仍可用)
|
|
1523
|
+
linkSkywalkToSpecPackage(workspaceRoot, specPackage.abs);
|
|
1376
1524
|
|
|
1377
1525
|
// 重新探测,刷新 layout.specRepo / codeRepos
|
|
1378
1526
|
layout = workspaceLayout.detectWorkspaceLayout(workspaceRoot);
|
|
@@ -1390,7 +1538,6 @@ async function main() {
|
|
|
1390
1538
|
} else {
|
|
1391
1539
|
// 单仓:Git 根只放代码 + skills + Hook;SDD 内容进统一包裹子目录
|
|
1392
1540
|
const isGit = workspaceLayout.isGitRepo(workspaceRoot);
|
|
1393
|
-
deployTelemetryDataDir(workspaceRoot);
|
|
1394
1541
|
|
|
1395
1542
|
if (isGit) {
|
|
1396
1543
|
console.log('📦 单仓模式:创建/复用仓内统一 SDD 包裹目录...');
|
|
@@ -1402,9 +1549,10 @@ async function main() {
|
|
|
1402
1549
|
asIndependentGit: false,
|
|
1403
1550
|
});
|
|
1404
1551
|
writeSddSpecRootHint(workspaceRoot, specPackage.abs);
|
|
1552
|
+
linkSkywalkToSpecPackage(workspaceRoot, specPackage.abs);
|
|
1405
1553
|
|
|
1406
1554
|
deployMonoSddYaml(workspaceRoot, { specPathRelative: specPackage.name });
|
|
1407
|
-
// Hook 装在 Git
|
|
1555
|
+
// Hook 装在 Git 根;经 symlink 写入包裹包内的 git-hooks
|
|
1408
1556
|
deployQualityGateTemplates(workspaceRoot, { mode: 'commit-msg-only' });
|
|
1409
1557
|
const linked = sddConfig.setSpecPath(workspaceRoot, specPackage.abs, {
|
|
1410
1558
|
allowNestedPackage: true,
|
|
@@ -1422,6 +1570,8 @@ async function main() {
|
|
|
1422
1570
|
skipOpenspec,
|
|
1423
1571
|
asIndependentGit: false,
|
|
1424
1572
|
});
|
|
1573
|
+
writeSddSpecRootHint(workspaceRoot, specPackage.abs);
|
|
1574
|
+
linkSkywalkToSpecPackage(workspaceRoot, specPackage.abs);
|
|
1425
1575
|
deployDotSddYaml(workspaceRoot);
|
|
1426
1576
|
}
|
|
1427
1577
|
}
|
|
@@ -1466,12 +1616,13 @@ async function main() {
|
|
|
1466
1616
|
console.log('工作区部署结果:');
|
|
1467
1617
|
console.log(' 🎯 工作目录 .*/skills/opsx-*/ # skills 只装这一份');
|
|
1468
1618
|
console.log(' 📋 .sdd-workspace.yaml # 子仓编排清单');
|
|
1619
|
+
console.log(' 🔗 skywalk-sdd/ → *-sdd-specs/skywalk-sdd/ # 符号链接,实体只在包裹包');
|
|
1469
1620
|
if (specPackage) {
|
|
1470
1621
|
console.log(` 📄 ${specPackage.name}/ # 统一 SDD 包裹包(独立 Git)`);
|
|
1471
1622
|
console.log(' modules.yaml / sdd.config.yaml / openspec / openspec-templates / skywalk-sdd');
|
|
1472
1623
|
}
|
|
1473
1624
|
for (const repo of layout.codeRepos) {
|
|
1474
|
-
console.log(` 🔗 ${repo.name}/ ← commit-msg Hook + .sdd.yaml(无 skills)`);
|
|
1625
|
+
console.log(` 🔗 ${repo.name}/ ← commit-msg Hook + .sdd.yaml(无 skywalk-sdd/、无 skills)`);
|
|
1475
1626
|
}
|
|
1476
1627
|
console.log();
|
|
1477
1628
|
console.log('新代码仓加入后执行:');
|
|
@@ -1485,6 +1636,7 @@ async function main() {
|
|
|
1485
1636
|
console.log(' modules.yaml / sdd.config.yaml / openspec / openspec-templates / skywalk-sdd');
|
|
1486
1637
|
}
|
|
1487
1638
|
console.log(' 🔗 .sdd.yaml (layout: mono) + commit-msg Hook');
|
|
1639
|
+
console.log(' 🔗 skywalk-sdd/ → *-sdd-specs/skywalk-sdd/ # 符号链接,实体只在包裹包');
|
|
1488
1640
|
}
|
|
1489
1641
|
if (selectedTools.includes('kunlunzhima')) {
|
|
1490
1642
|
console.log(' 📎 .kunlunzhima/commands/opsx/ # KunlunZhima OPSX command bridge(11 个)');
|
|
@@ -1496,10 +1648,6 @@ async function main() {
|
|
|
1496
1648
|
if (selectedTools.includes('claude')) {
|
|
1497
1649
|
console.log(' 🪝 .claude/hooks/ # Claude Code SDD Hook Pack(可选增强)');
|
|
1498
1650
|
}
|
|
1499
|
-
if (!layout.isWorkspace) {
|
|
1500
|
-
console.log(' 📊 skywalk-sdd/ # Hook 运行时 ontology(Git 根)');
|
|
1501
|
-
console.log(' 🧰 skywalk-sdd/git-hooks/ # commit-msg Trailer Hook');
|
|
1502
|
-
}
|
|
1503
1651
|
console.log();
|
|
1504
1652
|
|
|
1505
1653
|
// 统一的 skill 格式说明
|
|
@@ -1564,6 +1712,8 @@ module.exports = {
|
|
|
1564
1712
|
deployMonoSddYaml,
|
|
1565
1713
|
populateSpecPackage,
|
|
1566
1714
|
writeSddSpecRootHint,
|
|
1715
|
+
linkSkywalkToSpecPackage,
|
|
1716
|
+
deploySkywalkForwarders,
|
|
1567
1717
|
deployQualityGateTemplates,
|
|
1568
1718
|
deployTelemetryDataDir,
|
|
1569
1719
|
attachCodeRepoLite,
|
package/package.json
CHANGED
|
@@ -67,17 +67,40 @@ function detectEntryMode(projectRoot) {
|
|
|
67
67
|
return 'unknown';
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
function resolveCommitMsgScriptPath(projectRoot) {
|
|
71
|
+
const localScript = path.join(projectRoot, 'skywalk-sdd', 'git-hooks', 'commit-msg-sdd-trailer.cjs');
|
|
72
|
+
if (fs.existsSync(localScript)) return localScript;
|
|
73
|
+
// 多仓代码仓:脚本在 spec 包裹包,不在本仓 skywalk-sdd/
|
|
74
|
+
try {
|
|
75
|
+
const context = sddConfig.resolveCodeRepoContext(projectRoot);
|
|
76
|
+
if (context && context.specPath) {
|
|
77
|
+
const remote = path.join(context.specPath, 'skywalk-sdd', 'git-hooks', 'commit-msg-sdd-trailer.cjs');
|
|
78
|
+
if (fs.existsSync(remote)) return remote;
|
|
79
|
+
}
|
|
80
|
+
} catch {
|
|
81
|
+
// ignore
|
|
82
|
+
}
|
|
83
|
+
if (sddConfig.isMonoLayout(projectRoot)) {
|
|
84
|
+
const monoSpec = sddConfig.resolveMonoSpecRoot(projectRoot);
|
|
85
|
+
if (monoSpec) {
|
|
86
|
+
const nested = path.join(monoSpec, 'skywalk-sdd', 'git-hooks', 'commit-msg-sdd-trailer.cjs');
|
|
87
|
+
if (fs.existsSync(nested)) return nested;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
|
|
70
93
|
function checkHookInstalled(projectRoot) {
|
|
71
94
|
const hookPath = path.join(projectRoot, '.git', 'hooks', 'commit-msg');
|
|
72
|
-
const scriptPath =
|
|
95
|
+
const scriptPath = resolveCommitMsgScriptPath(projectRoot);
|
|
73
96
|
if (!fs.existsSync(path.join(projectRoot, '.git'))) {
|
|
74
97
|
return { status: STATUS.SKIP, message: '非 Git 仓库,跳过 Hook 检查' };
|
|
75
98
|
}
|
|
76
|
-
if (!
|
|
99
|
+
if (!scriptPath) {
|
|
77
100
|
return {
|
|
78
101
|
status: STATUS.FIXABLE,
|
|
79
102
|
code: 'HOOK_SCRIPT_MISSING',
|
|
80
|
-
message: 'commit-msg
|
|
103
|
+
message: 'commit-msg 脚本未找到(本仓或 sdd.specPath 下均无 skywalk-sdd/git-hooks/),可重新执行 kld-sdd-init',
|
|
81
104
|
fix: { type: 'deploy-hook-script' },
|
|
82
105
|
};
|
|
83
106
|
}
|
|
@@ -169,8 +169,18 @@ function upsertTrailers(message, trailers) {
|
|
|
169
169
|
return body;
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
+
function resolveMsgFile(argv = process.argv) {
|
|
173
|
+
// Git 传入的 message 文件路径;跳过 --project= 等可选参数
|
|
174
|
+
for (let i = 2; i < argv.length; i += 1) {
|
|
175
|
+
const arg = argv[i];
|
|
176
|
+
if (!arg || arg.startsWith('--')) continue;
|
|
177
|
+
return arg;
|
|
178
|
+
}
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
|
|
172
182
|
function main(env = process.env) {
|
|
173
|
-
const msgFile =
|
|
183
|
+
const msgFile = resolveMsgFile();
|
|
174
184
|
if (!msgFile) {
|
|
175
185
|
fail('缺少 commit message 文件参数');
|
|
176
186
|
}
|