@longzai-intelligence-issues/ledger 0.0.1
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/CHANGELOG.md +18 -0
- package/README.md +26 -0
- package/dist/index.d.ts +2123 -0
- package/dist/index.js +78 -0
- package/dist/rolldown-runtime-BqT_7tdF.js +1 -0
- package/lzi-builder.config.ts +15 -0
- package/lzi-bun.config.ts +8 -0
- package/oxlint.config.ts +3 -0
- package/package.json +39 -0
- package/src/__tests__/close/verify-close.commands.test.ts +560 -0
- package/src/__tests__/docs-refs/docs-refs.commands.test.ts +213 -0
- package/src/__tests__/fs/mini-glob.utils.test.ts +71 -0
- package/src/__tests__/fs/walk-surface.utils.test.ts +170 -0
- package/src/__tests__/health/health.commands.test.ts +131 -0
- package/src/__tests__/lint/lint.core.test.ts +332 -0
- package/src/__tests__/numbering/numbering.commands.test.ts +194 -0
- package/src/__tests__/numbering/numbering.core.test.ts +318 -0
- package/src/__tests__/parser/guide-fixture.utils.test.ts +45 -0
- package/src/__tests__/parser/registry.parser.test.ts +316 -0
- package/src/__tests__/prompts/prompts.commands.test.ts +47 -0
- package/src/__tests__/template/issue-template.renderer.test.ts +133 -0
- package/src/close/evidence-reader.utils.ts +201 -0
- package/src/close/green-flip.commands.ts +199 -0
- package/src/close/verify-close.commands.ts +676 -0
- package/src/docs-refs/docs-refs.commands.ts +693 -0
- package/src/freeze/freeze.commands.ts +261 -0
- package/src/fs/mini-glob.utils.ts +216 -0
- package/src/fs/walk-surface.utils.ts +298 -0
- package/src/health/health.commands.ts +327 -0
- package/src/index.ts +46 -0
- package/src/lint/lint-baseline.commands.ts +147 -0
- package/src/lint/lint.core.ts +584 -0
- package/src/normalize/normalize-header.commands.ts +361 -0
- package/src/numbering/numbering.commands.ts +375 -0
- package/src/numbering/numbering.core.ts +685 -0
- package/src/parser/format.utils.ts +279 -0
- package/src/parser/guide-fixture.utils.ts +117 -0
- package/src/parser/registry.parser.ts +679 -0
- package/src/prompts/prompts.commands.ts +211 -0
- package/src/template/issue-template.renderer.ts +351 -0
- package/src/triage/triage.classify.ts +93 -0
- package/src/vault/vault.commands.ts +434 -0
- package/tsconfig/.cache/build.tsbuildinfo +1 -0
- package/tsconfig/.cache/node.tsbuildinfo +1 -0
- package/tsconfig/.cache/test.tsbuildinfo +1 -0
- package/tsconfig/app.json +13 -0
- package/tsconfig/build.json +15 -0
- package/tsconfig/node.json +12 -0
- package/tsconfig/test.json +15 -0
- package/tsconfig.json +23 -0
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 编号门禁命令核(fs 编排层)
|
|
3
|
+
*
|
|
4
|
+
* 承接 next / check(全量与目录增量)/ audit 三命令的文件系统编排:扫描面收集
|
|
5
|
+
* 走 walk-surface 前缀定向,判定全部委托 numbering.core 纯函数。结果对象携带
|
|
6
|
+
* 退出语义(findings 非空即违规),报文渲染由 CLI 层承担。
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { LziIssuesConfig, ScopeEntry } from '@longzai-intelligence-issues/config';
|
|
10
|
+
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
|
|
13
|
+
import { walkFilesUnderPrefixes, walkFilesUnderRoots } from '@/fs/walk-surface.utils';
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
auditCoverage,
|
|
17
|
+
computeNextNumber,
|
|
18
|
+
type NumberingAllowlistEntry,
|
|
19
|
+
type NumberingAuditFinding,
|
|
20
|
+
type NumberingFinding,
|
|
21
|
+
type NumberingKeyRule,
|
|
22
|
+
scanScopeForFindings,
|
|
23
|
+
} from './numbering.core.js';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* issues 注册表键规则(数字前缀;v1 注册表固定形态)
|
|
27
|
+
*/
|
|
28
|
+
export const ISSUES_KEY_RULE: NumberingKeyRule = { kind: 'numericPrefix' };
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 取号命令结果
|
|
32
|
+
*/
|
|
33
|
+
export type NumberingNextResult =
|
|
34
|
+
| {
|
|
35
|
+
/**
|
|
36
|
+
* 取号成功标记
|
|
37
|
+
*/
|
|
38
|
+
ok: true;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 下一可用编号(零填充)
|
|
42
|
+
*/
|
|
43
|
+
number: string;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 取号目标描述(scope key 或目录)
|
|
47
|
+
*/
|
|
48
|
+
target: string;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 跳过的已占用号数
|
|
52
|
+
*/
|
|
53
|
+
skippedOccupied: number;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 跳过的保留号数
|
|
57
|
+
*/
|
|
58
|
+
skippedReserved: number;
|
|
59
|
+
}
|
|
60
|
+
| {
|
|
61
|
+
/**
|
|
62
|
+
* 取号成功标记
|
|
63
|
+
*/
|
|
64
|
+
ok: false;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 失败原因
|
|
68
|
+
*/
|
|
69
|
+
error: string;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 检查命令结果
|
|
74
|
+
*/
|
|
75
|
+
export type NumberingCheckResult = {
|
|
76
|
+
/**
|
|
77
|
+
* 违例列表(空 = 通过)
|
|
78
|
+
*/
|
|
79
|
+
findings: NumberingFinding[];
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 参与判定的 scope 数
|
|
83
|
+
*/
|
|
84
|
+
scopeCount: number;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 扫描面文件数
|
|
88
|
+
*/
|
|
89
|
+
scannedFiles: number;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* 审计命令结果
|
|
94
|
+
*/
|
|
95
|
+
export type NumberingAuditResult = {
|
|
96
|
+
/**
|
|
97
|
+
* 违例列表(空 = 通过)
|
|
98
|
+
*/
|
|
99
|
+
findings: NumberingAuditFinding[];
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* 全仓扫描面文件数
|
|
103
|
+
*/
|
|
104
|
+
scannedFiles: number;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* 收集注册表目录文件名列表(前缀定向,含非编号文件——键提取口径天然过滤)
|
|
109
|
+
*
|
|
110
|
+
* @param root - 仓库根绝对路径
|
|
111
|
+
* @param registryDir - 注册表目录(posix 相对)
|
|
112
|
+
* @returns 文件名列表
|
|
113
|
+
*/
|
|
114
|
+
function collectBasenames(root: string, registryDir: string): string[] {
|
|
115
|
+
return walkFilesUnderPrefixes(root, [registryDir]).map((file) => file.split('/').pop() ?? '');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* 取号命令入参
|
|
120
|
+
*/
|
|
121
|
+
export type NumberingNextCommandInput = {
|
|
122
|
+
/**
|
|
123
|
+
* 仓库根绝对路径
|
|
124
|
+
*/
|
|
125
|
+
root: string;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* 解析后配置
|
|
129
|
+
*/
|
|
130
|
+
config: LziIssuesConfig;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* 目标 scope key(与 dirRel 互斥)
|
|
134
|
+
*/
|
|
135
|
+
scopeKey?: string;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* 目标注册表目录(posix 相对,与 scopeKey 互斥)
|
|
139
|
+
*/
|
|
140
|
+
dirRel?: string;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* 取号命令核(只读不写;编号写入时刻确定——落盘前重跑取最新值)
|
|
145
|
+
*
|
|
146
|
+
* @param input - 命令入参
|
|
147
|
+
* @returns 取号结果
|
|
148
|
+
*/
|
|
149
|
+
export function runNumberingNext(input: NumberingNextCommandInput): NumberingNextResult {
|
|
150
|
+
/**
|
|
151
|
+
* 双目标同时提供标记
|
|
152
|
+
*/
|
|
153
|
+
const bothGiven = input.scopeKey !== undefined && input.dirRel !== undefined;
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* 双目标同时缺席标记
|
|
157
|
+
*/
|
|
158
|
+
const neitherGiven = input.scopeKey === undefined && input.dirRel === undefined;
|
|
159
|
+
|
|
160
|
+
if (bothGiven || neitherGiven) {
|
|
161
|
+
return { ok: false, error: '取号须且仅须指定 --scope <key> 或 --dir <目录> 之一' };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (input.scopeKey !== undefined) {
|
|
165
|
+
/**
|
|
166
|
+
* 目标 scope
|
|
167
|
+
*/
|
|
168
|
+
const scope = input.config.scopes.find((entry) => entry.key === input.scopeKey);
|
|
169
|
+
|
|
170
|
+
if (scope === undefined) {
|
|
171
|
+
return {
|
|
172
|
+
ok: false,
|
|
173
|
+
error: `scope「${input.scopeKey}」未登记(请在 lzi-issues.config.ts 注册)`,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* 取号结果
|
|
179
|
+
*/
|
|
180
|
+
const next = computeNextNumber({
|
|
181
|
+
basenames: collectBasenames(input.root, scope.registryDir),
|
|
182
|
+
rule: ISSUES_KEY_RULE,
|
|
183
|
+
reserved: scope.numbering.reserved.map((entry) => entry.number),
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
if (!next.ok) {
|
|
187
|
+
return next;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
ok: true,
|
|
192
|
+
number: next.number,
|
|
193
|
+
target: scope.key,
|
|
194
|
+
skippedOccupied: next.skippedOccupied,
|
|
195
|
+
skippedReserved: next.skippedReserved,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* 目录模式:覆盖 scope 查找(registryDir 精确等于目录)
|
|
201
|
+
*/
|
|
202
|
+
const covering = input.config.scopes.filter((scope) => scope.registryDir === input.dirRel);
|
|
203
|
+
|
|
204
|
+
if (covering.length === 0) {
|
|
205
|
+
return {
|
|
206
|
+
ok: false,
|
|
207
|
+
error: `目录「${input.dirRel}」未登记为任何 scope 的注册表(请在 lzi-issues.config.ts 注册后再取号)`,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* 保留号并集(保守防复用)
|
|
213
|
+
*/
|
|
214
|
+
const reservedUnion = [
|
|
215
|
+
...new Set(covering.flatMap((scope) => scope.numbering.reserved.map((r) => r.number))),
|
|
216
|
+
];
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* 目录模式取号结果
|
|
220
|
+
*/
|
|
221
|
+
const next = computeNextNumber({
|
|
222
|
+
basenames: collectBasenames(input.root, input.dirRel ?? ''),
|
|
223
|
+
rule: ISSUES_KEY_RULE,
|
|
224
|
+
reserved: reservedUnion,
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
if (!next.ok) {
|
|
228
|
+
return next;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return {
|
|
232
|
+
ok: true,
|
|
233
|
+
number: next.number,
|
|
234
|
+
target: input.dirRel ?? '',
|
|
235
|
+
skippedOccupied: next.skippedOccupied,
|
|
236
|
+
skippedReserved: next.skippedReserved,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* 检查命令入参
|
|
242
|
+
*/
|
|
243
|
+
export type NumberingCheckCommandInput = {
|
|
244
|
+
/**
|
|
245
|
+
* 仓库根绝对路径
|
|
246
|
+
*/
|
|
247
|
+
root: string;
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* 解析后配置
|
|
251
|
+
*/
|
|
252
|
+
config: LziIssuesConfig;
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* 目录增量档目标(缺省全量档)
|
|
256
|
+
*/
|
|
257
|
+
dirRel?: string;
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* 检查命令核(全量档 / 目录增量档)
|
|
262
|
+
*
|
|
263
|
+
* 目录增量档:只判覆盖 scope,豁免按目录过滤(防其他目录条目误判 stale),
|
|
264
|
+
* 不跑 slug 白名单失效对账(扫描面不完整防假阳性)。
|
|
265
|
+
*
|
|
266
|
+
* @param input - 命令入参
|
|
267
|
+
* @returns 检查结果
|
|
268
|
+
*/
|
|
269
|
+
export function runNumberingCheck(input: NumberingCheckCommandInput): NumberingCheckResult {
|
|
270
|
+
/**
|
|
271
|
+
* 参与判定的 scope 列表
|
|
272
|
+
*/
|
|
273
|
+
const scopes: ScopeEntry[] =
|
|
274
|
+
input.dirRel === undefined
|
|
275
|
+
? input.config.scopes
|
|
276
|
+
: input.config.scopes.filter((scope) => scope.registryDir === input.dirRel);
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* 违例收集器
|
|
280
|
+
*/
|
|
281
|
+
const findings: NumberingFinding[] = [];
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* 扫描面文件计数
|
|
285
|
+
*/
|
|
286
|
+
let scannedFiles = 0;
|
|
287
|
+
|
|
288
|
+
for (const scope of scopes) {
|
|
289
|
+
/**
|
|
290
|
+
* 注册表文件名列表
|
|
291
|
+
*/
|
|
292
|
+
const basenames = collectBasenames(input.root, scope.registryDir);
|
|
293
|
+
|
|
294
|
+
scannedFiles += basenames.length;
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* 全量档豁免(scope 维度过滤)
|
|
298
|
+
*/
|
|
299
|
+
const allowlist: NumberingAllowlistEntry[] =
|
|
300
|
+
input.dirRel === undefined
|
|
301
|
+
? input.config.numbering.allowlist
|
|
302
|
+
.filter((entry) => entry.scopeKey === scope.key)
|
|
303
|
+
.map((entry) => ({ ...entry, files: [...entry.files] }))
|
|
304
|
+
: input.config.numbering.allowlist
|
|
305
|
+
.filter(
|
|
306
|
+
(entry) =>
|
|
307
|
+
entry.scopeKey === scope.key &&
|
|
308
|
+
entry.files.some((file) => file.startsWith(`${input.dirRel}/`)),
|
|
309
|
+
)
|
|
310
|
+
.map((entry) => ({ ...entry, files: [...entry.files] }));
|
|
311
|
+
|
|
312
|
+
findings.push(
|
|
313
|
+
...scanScopeForFindings({
|
|
314
|
+
scopeKey: scope.key,
|
|
315
|
+
basenames,
|
|
316
|
+
rule: ISSUES_KEY_RULE,
|
|
317
|
+
reserved: scope.numbering.reserved,
|
|
318
|
+
allowlist,
|
|
319
|
+
slugDigitPolicy: scope.slugDigitPolicy,
|
|
320
|
+
slugDigitAllowlist: input.config.slugDigitAllowlist.map((entry) => entry.glob),
|
|
321
|
+
toRepoRelative: (basename) => `${scope.registryDir}/${basename}`,
|
|
322
|
+
fullMode: input.dirRel === undefined,
|
|
323
|
+
}),
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return { findings, scopeCount: scopes.length, scannedFiles };
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* 审计命令入参
|
|
332
|
+
*/
|
|
333
|
+
export type NumberingAuditCommandInput = {
|
|
334
|
+
/**
|
|
335
|
+
* 仓库根绝对路径
|
|
336
|
+
*/
|
|
337
|
+
root: string;
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* 解析后配置
|
|
341
|
+
*/
|
|
342
|
+
config: LziIssuesConfig;
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* 审计命令核(全仓严格遍历防逃逸;审计根缺失抛错防扫描面缩水)
|
|
347
|
+
*
|
|
348
|
+
* @param input - 命令入参
|
|
349
|
+
* @returns 审计结果
|
|
350
|
+
* @throws {@link Error} 审计根缺失
|
|
351
|
+
*/
|
|
352
|
+
export function runNumberingAudit(input: NumberingAuditCommandInput): NumberingAuditResult {
|
|
353
|
+
/**
|
|
354
|
+
* 全仓扫描面文件收集器(posix 相对根路径)
|
|
355
|
+
*/
|
|
356
|
+
const files: string[] = [];
|
|
357
|
+
|
|
358
|
+
for (const auditRoot of input.config.numbering.audit.roots) {
|
|
359
|
+
files.push(
|
|
360
|
+
...walkFilesUnderRoots(join(input.root, auditRoot), {
|
|
361
|
+
excludeGlobs: input.config.numbering.globalExclusions.map((entry) => entry.glob),
|
|
362
|
+
}).map((file) => (auditRoot === '' ? file : `${auditRoot}/${file}`)),
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return {
|
|
367
|
+
findings: auditCoverage({
|
|
368
|
+
files,
|
|
369
|
+
numberishPattern: input.config.numbering.audit.numberishPattern,
|
|
370
|
+
registryDirs: input.config.scopes.map((scope) => scope.registryDir),
|
|
371
|
+
pending: input.config.numbering.audit.pending,
|
|
372
|
+
}),
|
|
373
|
+
scannedFiles: files.length,
|
|
374
|
+
};
|
|
375
|
+
}
|