kld-sdd 2.6.17 → 2.6.21
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 +10 -0
- package/kld-sdd-guide.html +22 -0
- package/lib/device-auth-cli.js +130 -0
- package/lib/device-auth.js +345 -0
- package/lib/init-account-binding.js +108 -0
- package/lib/init.js +182 -58
- package/package.json +3 -3
- package/skywalk-sdd/index.cjs +142 -25
- package/skywalk-sdd/lib/shared.cjs +150 -2
- package/skywalk-sdd/lib/usage-contract.cjs +276 -0
- package/skywalk-sdd/lib/usage-reporter.cjs +354 -0
- package/skywalk-sdd/lib/user-config.cjs +157 -0
- package/templates/git-hooks/pre-commit-consistency-check.cjs +271 -116
- package/templates/git-hooks/pre-push-consistency-check.cjs +252 -123
- package/templates/skills/kld-sdd/opsx-check/SKILL.md +1 -1
- package/templates/skills/kld-sdd/opsx-consistency-check/SKILL.md +187 -257
- package/templates/skills/kld-sdd/opsx-consistency-check/reference.md +129 -0
- package/templates/skills/kld-sdd/opsx-kb-config/SKILL.md +27 -6
- package/templates/skills/kld-sdd/opsx-kb-config/reference.md +12 -1
|
@@ -3,191 +3,346 @@
|
|
|
3
3
|
* pre-commit-consistency-check.cjs
|
|
4
4
|
*
|
|
5
5
|
* Pre-commit 门禁:检查活跃变更的 Spec 一致性校验报告。
|
|
6
|
-
*
|
|
6
|
+
* - 默认:缺少报告 / confidence=low → 阻止提交;strict 模式下 medium 也阻止
|
|
7
7
|
*
|
|
8
|
-
*
|
|
8
|
+
* 通过环境变量控制:
|
|
9
|
+
* - SDD_SKIP_CONSISTENCY_CHECK=1 → 跳过校验
|
|
10
|
+
* - SDD_STRICT_CONSISTENCY=1 → 严格模式(medium 也阻止)
|
|
11
|
+
* - SDD_WARN_ONLY=1 → 仅告警不拦截
|
|
12
|
+
* - 注意:三个环境变量互斥,只能启用一个
|
|
9
13
|
*/
|
|
10
|
-
|
|
11
14
|
'use strict';
|
|
12
15
|
|
|
13
16
|
const fs = require('fs');
|
|
14
17
|
const path = require('path');
|
|
18
|
+
const { execFileSync } = require('child_process');
|
|
15
19
|
|
|
16
|
-
|
|
20
|
+
const readArg = (name) => {
|
|
17
21
|
const prefix = `--${name}=`;
|
|
18
|
-
const found = process.argv.find(
|
|
22
|
+
const found = process.argv.find(a => a.startsWith(prefix));
|
|
19
23
|
return found ? found.slice(prefix.length) : '';
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// --- Spec 根路径解析 ---
|
|
27
|
+
|
|
28
|
+
function resolveSpecRoot() {
|
|
29
|
+
// .cjs 位于 <spec-root>/skywalk-sdd/git-hooks/,直接用 __dirname 推算
|
|
30
|
+
return path.resolve(__dirname, '..', '..');
|
|
20
31
|
}
|
|
21
32
|
|
|
22
|
-
function
|
|
23
|
-
|
|
33
|
+
function discoverActiveChanges(specRoot) {
|
|
34
|
+
const changesDir = path.join(specRoot, 'openspec', 'changes');
|
|
35
|
+
if (!fs.existsSync(changesDir)) return [];
|
|
36
|
+
|
|
37
|
+
return fs.readdirSync(changesDir).filter(name => {
|
|
38
|
+
const p = path.join(changesDir, name);
|
|
39
|
+
return fs.statSync(p).isDirectory() && !name.startsWith('.') && name !== 'archive';
|
|
40
|
+
});
|
|
24
41
|
}
|
|
25
42
|
|
|
26
|
-
|
|
27
|
-
* 从 .sdd-spec-root 文件读取 spec 包裹包相对路径(单仓 mono 布局)
|
|
28
|
-
*/
|
|
29
|
-
function resolveSpecRoot(projectRoot) {
|
|
30
|
-
// 1. 检查 .sdd-spec-root(单仓 mono 模式)
|
|
31
|
-
const hintPath = path.join(projectRoot, '.sdd-spec-root');
|
|
32
|
-
if (fs.existsSync(hintPath)) {
|
|
33
|
-
const rel = fs.readFileSync(hintPath, 'utf8').trim();
|
|
34
|
-
if (rel) {
|
|
35
|
-
const specAbs = path.resolve(projectRoot, rel);
|
|
36
|
-
if (fs.existsSync(specAbs)) {
|
|
37
|
-
return specAbs;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
43
|
+
// --- 报告查找与解析 ---
|
|
41
44
|
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
45
|
+
function findReport(specRoot, changeName, type) {
|
|
46
|
+
const names = type === 'json'
|
|
47
|
+
? ['consistency-report-result.json', 'consistency-report-self-review-result.json']
|
|
48
|
+
: ['consistency-report.md', 'consistency-report-self-review.md'];
|
|
49
|
+
|
|
50
|
+
const found = [];
|
|
51
|
+
for (const n of names) {
|
|
52
|
+
const p = path.join(specRoot, 'openspec', 'changes', changeName, n);
|
|
53
|
+
if (fs.existsSync(p)) found.push(p);
|
|
51
54
|
}
|
|
52
55
|
|
|
53
|
-
//
|
|
54
|
-
if (
|
|
55
|
-
return
|
|
56
|
+
// 如果两种模式的报告都存在,返回错误标记
|
|
57
|
+
if (found.length > 1) {
|
|
58
|
+
return { error: 'duplicate', paths: found };
|
|
56
59
|
}
|
|
57
60
|
|
|
58
|
-
return null;
|
|
61
|
+
return found.length === 1 ? found[0] : null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function parseReport(reportPath) {
|
|
65
|
+
try {
|
|
66
|
+
const data = JSON.parse(fs.readFileSync(reportPath, 'utf8'));
|
|
67
|
+
return {
|
|
68
|
+
confidence: (data.confidence || '').toLowerCase(),
|
|
69
|
+
reviewMode: data.reviewMode || 'independent-review',
|
|
70
|
+
humanConfirmed: data.humanConfirmed === true,
|
|
71
|
+
reportPath,
|
|
72
|
+
};
|
|
73
|
+
} catch (err) {
|
|
74
|
+
return { confidence: '', reviewMode: '', humanConfirmed: false, reportPath, parseError: err.message };
|
|
75
|
+
}
|
|
59
76
|
}
|
|
60
77
|
|
|
78
|
+
// --- 变更文件采集(基于配置文件精准定位) ---
|
|
79
|
+
|
|
61
80
|
/**
|
|
62
|
-
*
|
|
81
|
+
* 简易 YAML 解析器:仅提取 code_repos 列表
|
|
63
82
|
*/
|
|
64
|
-
function
|
|
65
|
-
|
|
66
|
-
|
|
83
|
+
function parseCodeRepos(yamlPath) {
|
|
84
|
+
try {
|
|
85
|
+
const content = fs.readFileSync(yamlPath, 'utf8');
|
|
86
|
+
const match = content.match(/code_repos:\s*\n((?:\s+-\s+\S+\n?)+)/);
|
|
87
|
+
if (!match) return null;
|
|
88
|
+
return match[1]
|
|
89
|
+
.split('\n')
|
|
90
|
+
.map(line => line.replace(/^\s+-\s+/, '').trim())
|
|
91
|
+
.filter(Boolean);
|
|
92
|
+
} catch {
|
|
93
|
+
return null;
|
|
67
94
|
}
|
|
95
|
+
}
|
|
68
96
|
|
|
69
|
-
|
|
70
|
-
|
|
97
|
+
function gitDiff(repoRoot) {
|
|
98
|
+
try {
|
|
99
|
+
return execFileSync('git', ['diff', 'HEAD', '--name-only'], {
|
|
100
|
+
cwd: repoRoot, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'],
|
|
101
|
+
}).split(/\r?\n/).filter(Boolean);
|
|
102
|
+
} catch {
|
|
71
103
|
return [];
|
|
72
104
|
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function getModifiedFiles(projectRoot) {
|
|
108
|
+
// 工作区模式:读 .sdd-workspace.yaml 的 code_repos
|
|
109
|
+
const wsYaml = path.join(projectRoot, '.sdd-workspace.yaml');
|
|
110
|
+
if (fs.existsSync(wsYaml)) {
|
|
111
|
+
const repos = parseCodeRepos(wsYaml);
|
|
112
|
+
if (repos && repos.length > 0) {
|
|
113
|
+
const files = [];
|
|
114
|
+
for (const repo of repos) {
|
|
115
|
+
const repoRoot = path.join(projectRoot, repo);
|
|
116
|
+
for (const f of gitDiff(repoRoot)) {
|
|
117
|
+
files.push(path.join(repo, f).replace(/\\/g, '/'));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return [...new Set(files)];
|
|
121
|
+
}
|
|
122
|
+
}
|
|
73
123
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const fullPath = path.join(changesDir, name);
|
|
77
|
-
return fs.statSync(fullPath).isDirectory()
|
|
78
|
-
&& !name.startsWith('.')
|
|
79
|
-
&& name !== 'archive';
|
|
80
|
-
});
|
|
124
|
+
// 单仓模式 / 兜底:直接对 projectRoot 做 diff
|
|
125
|
+
return gitDiff(projectRoot);
|
|
81
126
|
}
|
|
82
127
|
|
|
83
128
|
/**
|
|
84
|
-
*
|
|
129
|
+
* 获取报告生成时间:优先用 JSON 中的 generatedAt,降级用文件 mtime
|
|
85
130
|
*/
|
|
86
|
-
function
|
|
87
|
-
|
|
131
|
+
function getReportTime(jsonPath) {
|
|
132
|
+
try {
|
|
133
|
+
const data = JSON.parse(fs.readFileSync(jsonPath, 'utf8'));
|
|
134
|
+
if (data.generatedAt) {
|
|
135
|
+
const t = new Date(data.generatedAt).getTime();
|
|
136
|
+
if (!isNaN(t)) return t;
|
|
137
|
+
}
|
|
138
|
+
} catch {}
|
|
139
|
+
// 降级:用文件 mtime
|
|
140
|
+
return fs.statSync(jsonPath).mtimeMs;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function isReportStale(specRoot, changeName, modifiedFiles, projectRoot) {
|
|
144
|
+
const jsonPath = findReport(specRoot, changeName, 'json');
|
|
145
|
+
if (!jsonPath) return false;
|
|
146
|
+
|
|
147
|
+
const reportTime = getReportTime(jsonPath);
|
|
148
|
+
|
|
149
|
+
// 排除报告文件本身
|
|
150
|
+
const reportFiles = [
|
|
151
|
+
'consistency-report.md',
|
|
152
|
+
'consistency-report-self-review.md',
|
|
88
153
|
'consistency-report-result.json',
|
|
89
154
|
'consistency-report-self-review-result.json',
|
|
90
155
|
];
|
|
91
156
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
157
|
+
return modifiedFiles.some(file => {
|
|
158
|
+
if (reportFiles.includes(path.basename(file))) return false;
|
|
159
|
+
const fp = path.join(projectRoot, file);
|
|
160
|
+
return fs.existsSync(fp) && fs.statSync(fp).mtimeMs > reportTime;
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// --- 提示信息 ---
|
|
165
|
+
|
|
166
|
+
function generateInstructions(missing, parseError, lowConfidence, stale, needsConfirmation, warnOnly) {
|
|
167
|
+
const lines = ['', '=========================================='];
|
|
168
|
+
const needsRegenerate = [...new Set([...missing, ...parseError, ...lowConfidence, ...stale])];
|
|
169
|
+
|
|
170
|
+
if (warnOnly) {
|
|
171
|
+
lines.push('[SDD commit-consistency-check] ⚠️ 仅告警模式(SDD_WARN_ONLY=1),以下问题不会阻止提交:');
|
|
172
|
+
} else {
|
|
173
|
+
lines.push('[SDD commit-consistency-check] 以下变更存在问题,拒绝提交:');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
lines.push('');
|
|
177
|
+
|
|
178
|
+
if (missing.length) {
|
|
179
|
+
lines.push(' 缺少一致性报告:');
|
|
180
|
+
missing.forEach(c => lines.push(` - ${c}`));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (parseError.length) {
|
|
184
|
+
lines.push(' 报告解析失败(JSON 格式损坏):');
|
|
185
|
+
parseError.forEach(c => lines.push(` - ${c}`));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (lowConfidence.length) {
|
|
189
|
+
lines.push(' 报告置信度不达标:');
|
|
190
|
+
lowConfidence.forEach(c => lines.push(` - ${c}`));
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (stale.length) {
|
|
194
|
+
lines.push(' 报告已过期(代码在报告生成后被修改):');
|
|
195
|
+
stale.forEach(c => lines.push(` - ${c}`));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (needsConfirmation.length) {
|
|
199
|
+
lines.push(' 自审模式需人工确认:');
|
|
200
|
+
needsConfirmation.forEach(c => lines.push(` - ${c}`));
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// 只有需要重新生成报告时才显示 skill 执行建议
|
|
204
|
+
if (needsRegenerate.length > 0) {
|
|
205
|
+
lines.push('');
|
|
206
|
+
lines.push(warnOnly
|
|
207
|
+
? '建议执行 opsx-consistency-check skill 更新报告:'
|
|
208
|
+
: '请执行 opsx-consistency-check skill 更新报告后再提交:');
|
|
209
|
+
lines.push('');
|
|
210
|
+
|
|
211
|
+
if (needsRegenerate.length === 1) {
|
|
212
|
+
lines.push(` skill: opsx-consistency-check ${needsRegenerate[0]}`);
|
|
213
|
+
} else {
|
|
214
|
+
lines.push(' skill(逐个执行):');
|
|
215
|
+
needsRegenerate.forEach(c => lines.push(` opsx-consistency-check ${c}`));
|
|
96
216
|
}
|
|
217
|
+
|
|
218
|
+
lines.push(
|
|
219
|
+
'',
|
|
220
|
+
' 💡 建议:新开对话窗口执行校验,以获得独立、客观的审核结果',
|
|
221
|
+
);
|
|
97
222
|
}
|
|
98
223
|
|
|
99
|
-
|
|
100
|
-
|
|
224
|
+
// 自审模式人工确认指引
|
|
225
|
+
if (needsConfirmation.length > 0) {
|
|
226
|
+
lines.push('');
|
|
227
|
+
lines.push(' 人工确认步骤:');
|
|
228
|
+
lines.push(' 1. 打开 consistency-report-self-review.md,找到「人工确认(自审模式)」章节');
|
|
229
|
+
lines.push(' 2. 逐条核对断言,在「人工确认」列填写 ✅ 已确认 或 ❌ 有误');
|
|
230
|
+
lines.push(' 3. 确认无误后,编辑 consistency-report-self-review-result.json,将 humanConfirmed 从 false 改为 true');
|
|
231
|
+
lines.push('');
|
|
232
|
+
lines.push(' 报告路径: openspec/changes/<change-name>/consistency-report-self-review.md');
|
|
233
|
+
lines.push(' JSON 路径: openspec/changes/<change-name>/consistency-report-self-review-result.json');
|
|
234
|
+
}
|
|
101
235
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
return {
|
|
110
|
-
confidence: (data.confidence || '').toLowerCase(),
|
|
111
|
-
overallResult: (data.overallResult || '').toLowerCase(),
|
|
112
|
-
changeName: data.changeName || '',
|
|
113
|
-
reportPath,
|
|
114
|
-
};
|
|
115
|
-
} catch (err) {
|
|
116
|
-
return { confidence: '', overallResult: '', changeName: '', reportPath, parseError: err.message };
|
|
236
|
+
// 如果没有需要重新生成的报告,但有其他问题,显示默认报告路径
|
|
237
|
+
if (needsRegenerate.length > 0 && needsConfirmation.length === 0) {
|
|
238
|
+
lines.push(
|
|
239
|
+
'',
|
|
240
|
+
' 报告路径: openspec/changes/<change-name>/consistency-report.md',
|
|
241
|
+
' JSON 路径: openspec/changes/<change-name>/consistency-report-result.json',
|
|
242
|
+
);
|
|
117
243
|
}
|
|
244
|
+
|
|
245
|
+
lines.push('==========================================', '');
|
|
246
|
+
|
|
247
|
+
return lines.join('\n');
|
|
118
248
|
}
|
|
119
249
|
|
|
250
|
+
// --- 主流程 ---
|
|
251
|
+
|
|
120
252
|
function main() {
|
|
121
253
|
const projectRoot = path.resolve(readArg('project') || process.env.SDD_PROJECT || process.cwd());
|
|
122
|
-
const explicitChange = readArg('change') || process.env.SDD_CHANGE || process.env.OPENSPEC_CHANGE || '';
|
|
123
|
-
const strictMode = hasFlag('strict') || process.env.SDD_STRICT_CONSISTENCY === '1';
|
|
124
254
|
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
255
|
+
const skip = process.env.SDD_SKIP_CONSISTENCY_CHECK === '1';
|
|
256
|
+
const strict = process.env.SDD_STRICT_CONSISTENCY === '1';
|
|
257
|
+
const warnOnly = process.env.SDD_WARN_ONLY === '1';
|
|
258
|
+
|
|
259
|
+
// 互斥检查
|
|
260
|
+
const activeModes = [skip, strict, warnOnly].filter(Boolean).length;
|
|
261
|
+
if (activeModes > 1) {
|
|
262
|
+
console.error('[SDD commit-consistency-check] 错误:SDD_SKIP_CONSISTENCY_CHECK、SDD_STRICT_CONSISTENCY、SDD_WARN_ONLY 三个环境变量互斥,只能启用一个');
|
|
263
|
+
process.exit(1);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (skip) {
|
|
267
|
+
console.log('[SDD commit-consistency-check] 跳过校验');
|
|
268
|
+
process.exit(0);
|
|
129
269
|
}
|
|
130
270
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
271
|
+
if (warnOnly) console.log('[SDD commit-consistency-check] 仅告警模式');
|
|
272
|
+
|
|
273
|
+
const specRoot = resolveSpecRoot();
|
|
274
|
+
|
|
275
|
+
const changes = discoverActiveChanges(specRoot);
|
|
276
|
+
if (!changes.length) {
|
|
277
|
+
console.log('[SDD commit-consistency-check] 无活跃变更,跳过');
|
|
278
|
+
process.exit(0);
|
|
135
279
|
}
|
|
136
280
|
|
|
137
|
-
const
|
|
281
|
+
const modifiedFiles = getModifiedFiles(projectRoot);
|
|
138
282
|
const missing = [];
|
|
139
|
-
const
|
|
283
|
+
const parseError = [];
|
|
284
|
+
const lowConfidence = [];
|
|
285
|
+
const stale = [];
|
|
286
|
+
const needsConfirmation = [];
|
|
287
|
+
|
|
288
|
+
for (const cn of changes) {
|
|
289
|
+
const rp = findReport(specRoot, cn, 'json');
|
|
290
|
+
if (!rp) {
|
|
291
|
+
missing.push(cn);
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
140
294
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
295
|
+
// 检查是否存在两种模式的报告
|
|
296
|
+
if (rp.error === 'duplicate') {
|
|
297
|
+
console.warn(`[SDD commit-consistency-check] ⚠️ ${cn} 存在两种模式的报告,请删除其中一个:`);
|
|
298
|
+
rp.paths.forEach(p => console.warn(` - ${p}`));
|
|
299
|
+
parseError.push(cn);
|
|
145
300
|
continue;
|
|
146
301
|
}
|
|
147
302
|
|
|
148
|
-
const report = parseReport(
|
|
303
|
+
const report = parseReport(rp);
|
|
149
304
|
if (report.parseError) {
|
|
150
|
-
console.warn(`SDD consistency-check
|
|
151
|
-
|
|
305
|
+
console.warn(`[SDD commit-consistency-check] ⚠️ ${cn} 报告解析失败`);
|
|
306
|
+
parseError.push(cn);
|
|
152
307
|
continue;
|
|
153
308
|
}
|
|
154
309
|
|
|
155
|
-
if (
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
310
|
+
if (isReportStale(specRoot, cn, modifiedFiles, projectRoot)) {
|
|
311
|
+
console.log(`[SDD commit-consistency-check] ⚠️ ${cn}: 报告已过期`);
|
|
312
|
+
stale.push(cn);
|
|
313
|
+
continue;
|
|
159
314
|
}
|
|
160
|
-
}
|
|
161
315
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
316
|
+
// 自审模式需要人工确认
|
|
317
|
+
if (report.reviewMode === 'self-review' && !report.humanConfirmed) {
|
|
318
|
+
console.log(`[SDD commit-consistency-check] ⚠️ ${cn}: 自审模式需人工确认`);
|
|
319
|
+
needsConfirmation.push(cn);
|
|
320
|
+
continue;
|
|
166
321
|
}
|
|
167
|
-
}
|
|
168
322
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
323
|
+
const c = report.confidence;
|
|
324
|
+
if (c === 'low' || (c === 'medium' && strict)) {
|
|
325
|
+
console.log(`[SDD commit-consistency-check] ❌ ${cn}: 置信度 ${c}`);
|
|
326
|
+
lowConfidence.push(cn);
|
|
327
|
+
continue;
|
|
173
328
|
}
|
|
329
|
+
|
|
330
|
+
console.log(`[SDD commit-consistency-check] ✅ ${cn}: 置信度 ${c}`);
|
|
174
331
|
}
|
|
175
332
|
|
|
176
|
-
if (
|
|
177
|
-
console.
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
console.
|
|
333
|
+
if (missing.length || parseError.length || lowConfidence.length || stale.length || needsConfirmation.length) {
|
|
334
|
+
console.log(generateInstructions(missing, parseError, lowConfidence, stale, needsConfirmation, warnOnly));
|
|
335
|
+
|
|
336
|
+
if (warnOnly) {
|
|
337
|
+
console.log('[SDD commit-consistency-check] 仅告警模式,放行提交');
|
|
338
|
+
process.exit(0);
|
|
181
339
|
}
|
|
182
|
-
console.error('');
|
|
183
|
-
console.error('请修复不一致项后重新执行 opsx-consistency-check,或使用 --no-verify 跳过(不推荐)。');
|
|
184
|
-
process.exit(1);
|
|
185
|
-
}
|
|
186
340
|
|
|
187
|
-
if (strictMode && missing.length > 0) {
|
|
188
|
-
console.error('SDD consistency-check: ❌ strict 模式已启用,缺少报告的变更不允许提交。');
|
|
189
341
|
process.exit(1);
|
|
190
342
|
}
|
|
343
|
+
|
|
344
|
+
console.log('\n[SDD commit-consistency-check] ✅ 所有变更均已通过一致性校验,放行提交');
|
|
345
|
+
process.exit(0);
|
|
191
346
|
}
|
|
192
347
|
|
|
193
348
|
main();
|