@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.
Files changed (50) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +26 -0
  3. package/dist/index.d.ts +2123 -0
  4. package/dist/index.js +78 -0
  5. package/dist/rolldown-runtime-BqT_7tdF.js +1 -0
  6. package/lzi-builder.config.ts +15 -0
  7. package/lzi-bun.config.ts +8 -0
  8. package/oxlint.config.ts +3 -0
  9. package/package.json +39 -0
  10. package/src/__tests__/close/verify-close.commands.test.ts +560 -0
  11. package/src/__tests__/docs-refs/docs-refs.commands.test.ts +213 -0
  12. package/src/__tests__/fs/mini-glob.utils.test.ts +71 -0
  13. package/src/__tests__/fs/walk-surface.utils.test.ts +170 -0
  14. package/src/__tests__/health/health.commands.test.ts +131 -0
  15. package/src/__tests__/lint/lint.core.test.ts +332 -0
  16. package/src/__tests__/numbering/numbering.commands.test.ts +194 -0
  17. package/src/__tests__/numbering/numbering.core.test.ts +318 -0
  18. package/src/__tests__/parser/guide-fixture.utils.test.ts +45 -0
  19. package/src/__tests__/parser/registry.parser.test.ts +316 -0
  20. package/src/__tests__/prompts/prompts.commands.test.ts +47 -0
  21. package/src/__tests__/template/issue-template.renderer.test.ts +133 -0
  22. package/src/close/evidence-reader.utils.ts +201 -0
  23. package/src/close/green-flip.commands.ts +199 -0
  24. package/src/close/verify-close.commands.ts +676 -0
  25. package/src/docs-refs/docs-refs.commands.ts +693 -0
  26. package/src/freeze/freeze.commands.ts +261 -0
  27. package/src/fs/mini-glob.utils.ts +216 -0
  28. package/src/fs/walk-surface.utils.ts +298 -0
  29. package/src/health/health.commands.ts +327 -0
  30. package/src/index.ts +46 -0
  31. package/src/lint/lint-baseline.commands.ts +147 -0
  32. package/src/lint/lint.core.ts +584 -0
  33. package/src/normalize/normalize-header.commands.ts +361 -0
  34. package/src/numbering/numbering.commands.ts +375 -0
  35. package/src/numbering/numbering.core.ts +685 -0
  36. package/src/parser/format.utils.ts +279 -0
  37. package/src/parser/guide-fixture.utils.ts +117 -0
  38. package/src/parser/registry.parser.ts +679 -0
  39. package/src/prompts/prompts.commands.ts +211 -0
  40. package/src/template/issue-template.renderer.ts +351 -0
  41. package/src/triage/triage.classify.ts +93 -0
  42. package/src/vault/vault.commands.ts +434 -0
  43. package/tsconfig/.cache/build.tsbuildinfo +1 -0
  44. package/tsconfig/.cache/node.tsbuildinfo +1 -0
  45. package/tsconfig/.cache/test.tsbuildinfo +1 -0
  46. package/tsconfig/app.json +13 -0
  47. package/tsconfig/build.json +15 -0
  48. package/tsconfig/node.json +12 -0
  49. package/tsconfig/test.json +15 -0
  50. package/tsconfig.json +23 -0
@@ -0,0 +1,279 @@
1
+ /**
2
+ * issue 注册表格式单一真源
3
+ *
4
+ * 全部格式口径(文件名、状态行、处置段、criteria 围栏块、引用形态)的常量与判定
5
+ * 函数集中于此模块。读侧(解析器 / lint / 收口)与写侧(模板渲染 / 翻绿回填 / 冻结
6
+ * 写入)共用同一真源,杜绝两套口径漂移——该纪律经 apex-energy-turbo
7
+ * issue-verify-ledger 长期验证有效后迁移(其 registry.parser.ts 同构实现)。
8
+ */
9
+
10
+ /**
11
+ * 注册表文件收集口径
12
+ *
13
+ * 3-4 位零填充数字前缀 + `-` + slug + `.md`;README 等不匹配文件天然不收编。
14
+ */
15
+ export const REGISTRY_FILE_PATTERN = /^[0-9]{3,4}-.+\.md$/;
16
+
17
+ /**
18
+ * slug 合法字符集(单一真源):仅 ASCII kebab——小写字母、数字、连字符,
19
+ * 禁中文与任何非 ASCII 字符(与契约层 SlugVO 口径一致)。
20
+ */
21
+ export const ASCII_KEBAB_SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
22
+
23
+ /**
24
+ * 状态行前缀正则源(读写共用单一真源)
25
+ *
26
+ * 「状态」+ 可选 0-2 个粗体星号 + 可选空白 + 全/半角冒号。以源字符串形态导出,
27
+ * 写侧改写正则由本源动态构造,保证读/写口径永不分裂。
28
+ */
29
+ export const STATUS_LINE_PREFIX_SOURCE = '状态\\*{0,2}\\s*[::]';
30
+
31
+ /**
32
+ * 状态行定位正则(读侧使用,由前缀源构造)
33
+ */
34
+ export const STATUS_LINE_LOCATE_PATTERN = new RegExp(STATUS_LINE_PREFIX_SOURCE);
35
+
36
+ /**
37
+ * 状态行定位扫描窗口(头部前 N 行)
38
+ */
39
+ export const STATUS_LINE_SCAN_LINES = 30;
40
+
41
+ /**
42
+ * 挂起原因括注提取模式:全/半角括号内至少 4 字符
43
+ */
44
+ export const PENDING_REASON_PATTERN = /[((]([^))]{4,})[))]/;
45
+
46
+ /**
47
+ * 处置段标题关键词
48
+ *
49
+ * `## ` 标题行包含本关键词即认定为处置段。
50
+ */
51
+ export const DISPOSITION_TITLE_KEYWORD = '处置结果';
52
+
53
+ /**
54
+ * 处置段占位判定模式(读写共用单一真源)
55
+ *
56
+ * 「处置中 / 待回填」后须紧跟右括号 / 标点 / 破折号 / 行尾才计占位——占位是终态
57
+ * 标签,正文状语(如「处置中新核实的…」后为继续字符)不计。
58
+ */
59
+ export const DISPOSITION_PLACEHOLDER_PATTERN = /(?:处置中|待回填)(?=[))::,,。;;、]|——|$)/;
60
+
61
+ /**
62
+ * 处置段占位判定窗口(处置段标题起 N 行)
63
+ */
64
+ export const DISPOSITION_PLACEHOLDER_WINDOW_LINES = 8;
65
+
66
+ /**
67
+ * 证据锚点块在位判定标记
68
+ *
69
+ * 两种收口形态的回填标题前缀:零执行证据收口 / 证书化真跑收口。
70
+ */
71
+ export const EVIDENCE_ANCHOR_MARKERS = ['证据锚点(末次通过零执行收口', '判据执行证据('] as const;
72
+
73
+ /**
74
+ * criteria 围栏块开栏行模式
75
+ */
76
+ export const CRITERIA_FENCE_OPEN_PATTERN = /^```criteria\s*$/;
77
+
78
+ /**
79
+ * criteria 围栏块闭栏行模式
80
+ */
81
+ export const CRITERIA_FENCE_CLOSE_PATTERN = /^```\s*$/;
82
+
83
+ /**
84
+ * criteria 判据行 channel 段合法形态(字母数字起首,限字母数字与 . _ -)
85
+ */
86
+ export const CRITERIA_CHANNEL_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
87
+
88
+ /**
89
+ * 元信息行「沿革」键:前序 issue 引用载体
90
+ */
91
+ export const HISTORY_LINE_KEY = '沿革';
92
+
93
+ /**
94
+ * 元信息行「阻塞」键:阻塞关系引用载体(全部 🟢 后方可收口)
95
+ */
96
+ export const BLOCKING_LINE_KEY = '阻塞';
97
+
98
+ /**
99
+ * 元信息行键定位前缀源
100
+ *
101
+ * 粗体标签(`**沿革**:`)与裸键(`沿革:`)两形态通吃;冒号全/半角均可。
102
+ */
103
+ export const META_LINE_PREFIX_SOURCE_TEMPLATE = '\\*{0,2}%s\\*{0,2}\\s*[::]';
104
+
105
+ /**
106
+ * issue 状态归类
107
+ *
108
+ * 规范五态(red / yellow / green / frozen / canceled)+ 旧格式变体
109
+ * (legacy-yellow / legacy-green,仅 --strict-legacy 计违规)+ 未知(unknown)。
110
+ */
111
+ export type IssueStatusKind =
112
+ | 'red'
113
+ | 'yellow'
114
+ | 'green'
115
+ | 'frozen'
116
+ | 'canceled'
117
+ | 'legacy-yellow'
118
+ | 'legacy-green'
119
+ | 'unknown';
120
+
121
+ /**
122
+ * 规范状态标签(写侧单一真源)
123
+ */
124
+ export const CANONICAL_STATUS_LABELS: Readonly<
125
+ Record<'red' | 'yellow' | 'green' | 'frozen' | 'canceled', string>
126
+ > = {
127
+ red: '🔴 待处理',
128
+ yellow: '🟡 处理中',
129
+ green: '🟢 已处置',
130
+ frozen: '⚪ 已冻结',
131
+ canceled: '⚫ 已取消',
132
+ };
133
+
134
+ /**
135
+ * issue 引用 token 形态
136
+ *
137
+ * `number` 为 3-4 位编号;`slug` 为可空的不完整 slug 段(纯编号引用无 slug)。
138
+ */
139
+ export type IssueRefToken = {
140
+ /**
141
+ * 引用编号(3-4 位零填充)
142
+ */
143
+ number: string;
144
+
145
+ /**
146
+ * 引用 slug 段(纯编号引用时为 null)
147
+ */
148
+ slug: string | null;
149
+ };
150
+
151
+ /**
152
+ * issue 引用 token 提取模式
153
+ *
154
+ * 命中 markdown 链接 label 与纯文本两种形态:`[0035](./0035-xxx.md)` 与 `[0035]`,
155
+ * 以及行内代码中的不完整 slug 前缀 `[0035-slug-pre]` 形态。链接目标(第三捕获组)
156
+ * 作为 slug 兜底来源——label 只有编号时从目标文件名 `NNNN-slug.md` 反解 slug。
157
+ */
158
+ export const ISSUE_REF_TOKEN_PATTERN = /\[([0-9]{3,4})(?:-([a-z0-9-]+))?\](?:\(([^)]*)\))?/g;
159
+
160
+ /**
161
+ * 链接目标文件名反解模式(slug 兜底来源)
162
+ */
163
+ const LINK_TARGET_SLUG_PATTERN = /([0-9]{3,4})-([a-z0-9-]+)\.md/;
164
+
165
+ /**
166
+ * 从任意文本行提取 issue 引用 token 列表
167
+ *
168
+ * slug 解析优先级:label 内 slug 段 > 链接目标文件名 slug;两者皆无时为 null
169
+ * (纯编号引用)。
170
+ *
171
+ * @param text - 待提取文本(沿革/阻塞行内容、正文段落等)
172
+ * @returns 按出现顺序去重后的引用 token 列表
173
+ */
174
+ export function extractIssueRefTokens(text: string): IssueRefToken[] {
175
+ /**
176
+ * 命中收集器(保序)
177
+ */
178
+ const hits: IssueRefToken[] = [];
179
+
180
+ /**
181
+ * 全局匹配游标(pattern 带 g 标志,须逐次 exec)
182
+ */
183
+ const pattern = new RegExp(ISSUE_REF_TOKEN_PATTERN.source, 'g');
184
+
185
+ /**
186
+ * 当前命中结果
187
+ */
188
+ let match: RegExpExecArray | null = pattern.exec(text);
189
+
190
+ while (match !== null) {
191
+ /**
192
+ * 当前命中编号
193
+ */
194
+ const number = match[1] ?? '';
195
+
196
+ /**
197
+ * label 内 slug 段
198
+ */
199
+ let slug = match[2] ?? null;
200
+
201
+ /**
202
+ * 链接目标(缺席为 null)
203
+ */
204
+ const target = match[3] ?? null;
205
+
206
+ if (slug === null && target !== null) {
207
+ /**
208
+ * 目标文件名 slug 反解结果
209
+ */
210
+ const targetMatch = LINK_TARGET_SLUG_PATTERN.exec(target);
211
+
212
+ slug = targetMatch?.[2] ?? null;
213
+ }
214
+
215
+ /**
216
+ * 去重判定:同 number+slug 已收集则跳过
217
+ */
218
+ const duplicated = hits.some((hit) => hit.number === number && hit.slug === slug);
219
+
220
+ if (!duplicated) {
221
+ hits.push({ number, slug });
222
+ }
223
+
224
+ match = pattern.exec(text);
225
+ }
226
+
227
+ return hits;
228
+ }
229
+
230
+ /**
231
+ * 构造元信息行键定位正则
232
+ *
233
+ * @param key - 元信息键(如「沿革」「阻塞」)
234
+ * @returns 行首锚定的键定位正则
235
+ */
236
+ export function buildMetaLineLocatePattern(key: string): RegExp {
237
+ return new RegExp(`^\\*{0,2}${key}\\*{0,2}\\s*[::]`);
238
+ }
239
+
240
+ /**
241
+ * 状态行归类
242
+ *
243
+ * 归类优先级:已冻结 > 🔴 > ⚫ > 🟡 处理中 > 🟡(旧格式)> 🟢 已处置 > 🟢/✅(旧格式)>
244
+ * 未知。未识别形态归 unknown(与「无状态行」同判,lint 由 status-line-missing 拦截)。
245
+ *
246
+ * @param line - 状态行全文
247
+ * @returns 状态归类
248
+ */
249
+ export function classifyStatusLine(line: string): IssueStatusKind {
250
+ if (line.includes('已冻结')) {
251
+ return 'frozen';
252
+ }
253
+
254
+ if (line.includes('🔴')) {
255
+ return 'red';
256
+ }
257
+
258
+ if (line.includes('⚫')) {
259
+ return 'canceled';
260
+ }
261
+
262
+ if (/🟡\s*处理中/.test(line)) {
263
+ return 'yellow';
264
+ }
265
+
266
+ if (line.includes('🟡')) {
267
+ return 'legacy-yellow';
268
+ }
269
+
270
+ if (/🟢\s*已处置/.test(line)) {
271
+ return 'green';
272
+ }
273
+
274
+ if (line.includes('🟢') || line.includes('✅')) {
275
+ return 'legacy-green';
276
+ }
277
+
278
+ return 'unknown';
279
+ }
@@ -0,0 +1,117 @@
1
+ /**
2
+ * 格式指南夹具工具(零漂移守卫的切片面)
3
+ *
4
+ * 格式真源指南(docs/guides/issue-file-format.md)内的活样板以 HTML 注释标记
5
+ * 圈定边界;本模块负责定位指南文件、切片活样板,供测试与渲染器比对——
6
+ * 「写指南即写测试,改解析器必改指南」(apex guide-fixture 同款机制)。
7
+ */
8
+
9
+ import { existsSync, readFileSync } from 'node:fs';
10
+ import { dirname, join, resolve } from 'node:path';
11
+
12
+ /**
13
+ * 活样板起点标记
14
+ */
15
+ export const GUIDE_FIXTURE_START_MARK = '<!-- issue-file-format-fixture:start -->';
16
+
17
+ /**
18
+ * 活样板终点标记
19
+ */
20
+ export const GUIDE_FIXTURE_END_MARK = '<!-- issue-file-format-fixture:end -->';
21
+
22
+ /**
23
+ * 格式指南相对项目根路径
24
+ */
25
+ export const ISSUE_FILE_FORMAT_GUIDE_RELATIVE = 'docs/guides/issue-file-format.md';
26
+
27
+ /**
28
+ * 指南向上查找最大层数
29
+ */
30
+ const GUIDE_LOCATE_MAX_DEPTH = 10;
31
+
32
+ /**
33
+ * 定位格式指南绝对路径
34
+ *
35
+ * 从本模块运行位置逐级向上查找 `docs/guides/issue-file-format.md`(源码运行与
36
+ * dist 构建产物两种位置均可命中;上限 10 层防失控上溯)。
37
+ *
38
+ * @returns 指南绝对路径
39
+ * @throws {@link Error} 超出查找上限仍未命中
40
+ */
41
+ export function locateIssueFileFormatGuide(): string {
42
+ /**
43
+ * 当前查找目录
44
+ */
45
+ let currentDir = dirname(resolve(import.meta.url.replace('file://', '')));
46
+
47
+ for (let depth = 0; depth < GUIDE_LOCATE_MAX_DEPTH; depth += 1) {
48
+ /**
49
+ * 当前层指南候选路径
50
+ */
51
+ const candidate = join(currentDir, ISSUE_FILE_FORMAT_GUIDE_RELATIVE);
52
+
53
+ if (existsSync(candidate)) {
54
+ return candidate;
55
+ }
56
+
57
+ /**
58
+ * 上一级目录(根目录判定:上溯不变即停)
59
+ */
60
+ const parent = dirname(currentDir);
61
+
62
+ if (parent === currentDir) {
63
+ break;
64
+ }
65
+
66
+ currentDir = parent;
67
+ }
68
+
69
+ throw new Error(
70
+ `未定位到格式指南:${ISSUE_FILE_FORMAT_GUIDE_RELATIVE}(自模块位置向上 ${GUIDE_LOCATE_MAX_DEPTH} 层内)`,
71
+ );
72
+ }
73
+
74
+ /**
75
+ * 读取格式指南全文
76
+ *
77
+ * @returns 指南全文
78
+ */
79
+ export function loadIssueFileFormatGuide(): string {
80
+ return readFileSync(locateIssueFileFormatGuide(), 'utf8');
81
+ }
82
+
83
+ /**
84
+ * 切片指南活样板
85
+ *
86
+ * @param guide - 指南全文
87
+ * @returns 起终点标记之间的活样板文本(去首尾空白行)
88
+ * @throws {@link Error} 标记缺席或次序颠倒(显式抛错,不静默返回空)
89
+ */
90
+ export function sliceGuideFixtureSample(guide: string): string {
91
+ /**
92
+ * 起点标记下标
93
+ */
94
+ const startIndex = guide.indexOf(GUIDE_FIXTURE_START_MARK);
95
+
96
+ /**
97
+ * 终点标记下标
98
+ */
99
+ const endIndex = guide.indexOf(GUIDE_FIXTURE_END_MARK);
100
+
101
+ if (startIndex === -1) {
102
+ throw new Error(`指南活样板起点标记缺席:${GUIDE_FIXTURE_START_MARK}`);
103
+ }
104
+
105
+ if (endIndex === -1) {
106
+ throw new Error(`指南活样板终点标记缺席:${GUIDE_FIXTURE_END_MARK}`);
107
+ }
108
+
109
+ if (endIndex < startIndex) {
110
+ throw new Error('指南活样板标记次序颠倒(终点在起点之前)');
111
+ }
112
+
113
+ return guide
114
+ .slice(startIndex + GUIDE_FIXTURE_START_MARK.length, endIndex)
115
+ .replace(/^\n+/, '')
116
+ .replace(/\n+$/, '');
117
+ }