@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,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 推荐提示词(内置两模板 + extraDir 自定义加载)
|
|
3
|
+
*
|
|
4
|
+
* apex 两工作流提示词的泛化迁移:watch 域特有内容替换为 config 驱动的 scope
|
|
5
|
+
* 判定与 lzi-issues 命令面。CLI `prompts list / show` 消费;不动 MCP。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 提示词列举/展示选项
|
|
13
|
+
*/
|
|
14
|
+
export type PromptsOptions = {
|
|
15
|
+
/**
|
|
16
|
+
* 是否包含内置模板(默认 true)
|
|
17
|
+
*/
|
|
18
|
+
builtin?: boolean;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 自定义模板目录绝对路径
|
|
22
|
+
*/
|
|
23
|
+
extraDir?: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 提示词条目
|
|
28
|
+
*/
|
|
29
|
+
export type PromptEntry = {
|
|
30
|
+
/**
|
|
31
|
+
* 提示词 id(kebab-case)
|
|
32
|
+
*/
|
|
33
|
+
id: string;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 一句话描述
|
|
37
|
+
*/
|
|
38
|
+
description: string;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 正文全文(markdown)
|
|
42
|
+
*/
|
|
43
|
+
content: string;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 来源(builtin / extra)
|
|
47
|
+
*/
|
|
48
|
+
source: 'builtin' | 'extra';
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 内置提示词:登记 + 治理全流程
|
|
53
|
+
*/
|
|
54
|
+
const ISSUE_CREATE_AND_PROCESS = `# issue-create-and-process:登记新问题并完成治理
|
|
55
|
+
|
|
56
|
+
## 命令定位
|
|
57
|
+
|
|
58
|
+
输入:问题原文(用户观察/描述的现象、报错、异常)。**问题原文必须逐字保留进 issue 文档,禁止转述改写**;调用未附带问题原文时立即停止并说明缺少原文。输入是既有 issue 引用时改走 issue-process,本命令不重复登记。
|
|
59
|
+
|
|
60
|
+
两个职责顺序执行:**登记**(范围判定→查重→门禁取号→落盘骨架)→**治理**(核实→修复→测试→记账→回填)。
|
|
61
|
+
|
|
62
|
+
## 一、登记
|
|
63
|
+
|
|
64
|
+
- **范围判定先于建档**:按 lzi-issues.config.ts 的 scopes(paths 覆盖面)判定问题归属——跨项目/全局问题落全局 scope 注册表,项目/包专属问题落对应 scope;判定不了时列出全部 scope 请用户裁定。
|
|
65
|
+
- **查重先于建档**:在目标注册表目录检索主题关键词、现象特征、涉及文件名;已存在同语义 issue 时走既有更新路径(回填现象与证据),禁止重复建档。
|
|
66
|
+
- **门禁取号**:\`lzi-issues create --title <标题> --scope <key>\`(编号写入时刻确定,目录即编号命名空间);落盘后跑 \`lzi-issues numbering check --dir <注册表目录>\` 自检。禁止手工定号。
|
|
67
|
+
- 骨架即标准模板(\`lzi-issues template\`),正文四段:问题动机(原文逐字+客观证据)→现状与证据→处置方向(【1】备选方案和分析【2】推荐方案和分析)→处置结果/验证口径。
|
|
68
|
+
|
|
69
|
+
## 二、治理
|
|
70
|
+
|
|
71
|
+
- **证据先行**:现象在当前代码上复现或查证后才写入;根因经检索/调试/最小复现确认后才落盘;无法确认的如实标注「根因待治理时核实」。已被并行治理修复的,证据确认后归属写入并收口,不做重复修复。
|
|
72
|
+
- **处理原则**:长期治理、最大工作量、最佳实践——按根因根治与体系化改进取舍,不选「恰好让报错消失」的最小改动。
|
|
73
|
+
- **裁决门槛**:能自决的技术方案直接决定并执行;规格语义裁决、产品行为取舍、推翻既有裁决等重大分歧**停下**,正文提供【1】问题动机/背景【2】备选方案和分析【3】推荐方案和分析,等用户裁决。
|
|
74
|
+
- **TDD**:先写复现/定义预期的失败测试(Red),再实现(Green)、重构(Refactor)。
|
|
75
|
+
- **测试最小范围**:只运行与本次改动相关的测试文件;三层覆盖缺失则补齐。
|
|
76
|
+
- **CHANGELOG**:变更落定即时 \`bun run changelog:add -- --added|--changed|--fixed "描述" --package <pkg>\`;纯文档/注释变更不入账。
|
|
77
|
+
- **边界治理**:同类问题排查(rg 受影响链路);命中不在本期边界的、治理中新暴露的遗留——按范围**单独建档**(同样走门禁取号),禁止混入本 issue;本 issue「处置结果/验证口径」逐条写明去向。
|
|
78
|
+
- **单载体回填**:发现、决策、处置过程全部回填 issue 文档自身(状态行实时更新),禁止外写专题 records/findings。
|
|
79
|
+
- **收口**:判据全绿后 \`lzi-issues verify-close --issue <文档>\`(缺省零执行证据收口;确须真跑加 --rerun,重度命令须 --confirm-heavy,仅人类明令)。
|
|
80
|
+
- **git 全程只读**:仅 status/diff/log/show 服务于本任务;非本任务产出的文件不动。
|
|
81
|
+
|
|
82
|
+
## 收尾
|
|
83
|
+
|
|
84
|
+
状态行终态化(🟢 已处置 或如实保持 🔴/🟡 并写明原因);处置结果/验证口径回填检查清单、测试结果、遗留去向;全部变更留工作区由用户审阅提交。`;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 内置提示词:既有 issue 治理
|
|
88
|
+
*/
|
|
89
|
+
const ISSUE_PROCESS = `# issue-process:治理既有 issue
|
|
90
|
+
|
|
91
|
+
## 命令定位
|
|
92
|
+
|
|
93
|
+
输入:issue 引用(完整 NNNN-slug / 纯编号 / 不完整 slug 前缀)。**引用必须唯一定位**——编号命中不唯一(历史撞号)时停止并列出候选(含 scope 与完整 slug),要求重新指定;未指定 issue 时立即停止,禁止自行挑选。
|
|
94
|
+
|
|
95
|
+
三个阶段:**读题核实**→**治理落地**→**回填归档**。
|
|
96
|
+
|
|
97
|
+
## 一、读题核实
|
|
98
|
+
|
|
99
|
+
- **终态拦截**:状态行已 🟢 的默认不处理(复审须明确要求);⚪ 已冻结的先停下(解冻须「用户裁定」)。
|
|
100
|
+
- **开工标注**:确认处理对象后状态行更新为 🟡 处理中(括注挂起原因)。
|
|
101
|
+
- **现状优先于记载**:issue 是写入时刻快照——现象、根因、涉及文件逐项在当前代码上核实;漂移时先如实修订记载再治理;已被并行治理修复的,证据确认后归属写入并收口,不重复修复。
|
|
102
|
+
|
|
103
|
+
## 二、治理落地
|
|
104
|
+
|
|
105
|
+
- 处理原则、裁决门槛、TDD、测试最小范围、CHANGELOG、边界治理、单载体回填纪律与 issue-create-and-process 完全一致。
|
|
106
|
+
- 治理中发现原方案实现层缺陷:有实证时把证据与修正方案记入「处置结果/验证口径」后继续;需变更已裁决规格语义时停下等用户裁定。
|
|
107
|
+
|
|
108
|
+
## 三、回填归档
|
|
109
|
+
|
|
110
|
+
- 状态行终态化;「处置结果/验证口径」记裁决内容(含否决备选与理由)、落地清单、检查与测试结果、遗留去向。
|
|
111
|
+
- 落地形态与登记时不一致或根因被修正:证据与修正理由写进正文,保留排查链路,禁止静默偏离。
|
|
112
|
+
- 收口走 \`lzi-issues verify-close\`(判据块 + 证据锚点;阻塞引用未全 🟢 会被门禁拒收)。`;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* 内置提示词全集
|
|
116
|
+
*/
|
|
117
|
+
const BUILTIN_PROMPTS: readonly PromptEntry[] = [
|
|
118
|
+
{
|
|
119
|
+
id: 'issue-create-and-process',
|
|
120
|
+
description:
|
|
121
|
+
'登记新问题到注册表并完成治理:范围判定、查重、门禁取号、证据先行、裁决门槛、TDD、单载体回填、verify-close 收口',
|
|
122
|
+
content: ISSUE_CREATE_AND_PROCESS,
|
|
123
|
+
source: 'builtin',
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
id: 'issue-process',
|
|
127
|
+
description:
|
|
128
|
+
'治理既有 issue:引用唯一定位消歧、终态拦截、现状核实、治理与回填、verify-close 收口',
|
|
129
|
+
content: ISSUE_PROCESS,
|
|
130
|
+
source: 'builtin',
|
|
131
|
+
},
|
|
132
|
+
];
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* 加载 extraDir 自定义提示词(frontmatter name/description + 正文)
|
|
136
|
+
*
|
|
137
|
+
* @param extraDir - 自定义模板目录绝对路径
|
|
138
|
+
* @returns 提示词条目列表(目录缺席为空列表)
|
|
139
|
+
*/
|
|
140
|
+
function loadExtraPrompts(extraDir: string): PromptEntry[] {
|
|
141
|
+
if (!existsSync(extraDir)) {
|
|
142
|
+
return [];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return readdirSync(extraDir)
|
|
146
|
+
.filter((file) => file.endsWith('.md'))
|
|
147
|
+
.map((file) => {
|
|
148
|
+
/**
|
|
149
|
+
* 模板全文
|
|
150
|
+
*/
|
|
151
|
+
const raw = readFileSync(join(extraDir, file), 'utf8');
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* frontmatter 提取
|
|
155
|
+
*/
|
|
156
|
+
const nameMatch = /^name:\s*(.+)$/m.exec(raw);
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* description 提取命中
|
|
160
|
+
*/
|
|
161
|
+
const descMatch = /^description:\s*(.+)$/m.exec(raw);
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
id: nameMatch?.[1]?.trim() ?? file.replace(/\.md$/, ''),
|
|
165
|
+
description: descMatch?.[1]?.trim() ?? '',
|
|
166
|
+
content: raw,
|
|
167
|
+
source: 'extra',
|
|
168
|
+
};
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* 列出全部提示词(内置 + extra;builtin 关闭时仅 extra)
|
|
174
|
+
*
|
|
175
|
+
* @param options - 列举选项
|
|
176
|
+
* @param options.builtin - 是否包含内置(默认 true)
|
|
177
|
+
* @param options.extraDir - 自定义模板目录
|
|
178
|
+
* @returns 提示词条目列表(id 去重,extra 覆盖同 id 内置)
|
|
179
|
+
*/
|
|
180
|
+
export function listPrompts(options: PromptsOptions): PromptEntry[] {
|
|
181
|
+
/**
|
|
182
|
+
* 汇总收集器
|
|
183
|
+
*/
|
|
184
|
+
const collected: PromptEntry[] = [];
|
|
185
|
+
|
|
186
|
+
if (options.builtin !== false) {
|
|
187
|
+
collected.push(...BUILTIN_PROMPTS);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (options.extraDir !== undefined) {
|
|
191
|
+
collected.push(...loadExtraPrompts(options.extraDir));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* id 去重(后者覆盖前者——extra 优先)
|
|
196
|
+
*/
|
|
197
|
+
const byId = new Map(collected.map((entry) => [entry.id, entry]));
|
|
198
|
+
|
|
199
|
+
return [...byId.values()];
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* 展示单个提示词
|
|
204
|
+
*
|
|
205
|
+
* @param id - 提示词 id
|
|
206
|
+
* @param options - 列举选项(与 listPrompts 一致)
|
|
207
|
+
* @returns 提示词条目;未命中为 null
|
|
208
|
+
*/
|
|
209
|
+
export function showPrompt(id: string, options: PromptsOptions): PromptEntry | null {
|
|
210
|
+
return listPrompts(options).find((entry) => entry.id === id) ?? null;
|
|
211
|
+
}
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* issue 标准模板渲染器(唯一真源)
|
|
3
|
+
*
|
|
4
|
+
* 标准格式三路共用本渲染器:template 命令(skeleton 骨架展示)、create 建档
|
|
5
|
+
* (instance 实际值骨架)、init 注册表 README 存根。正文四段占位文本以常量形态
|
|
6
|
+
* 由两种模式共享,结构漂移在构造上不可能发生;指南活样板与骨架的逐字一致由
|
|
7
|
+
* guide-fixture 测试守卫(零漂移五环之一)。
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
BLOCKING_LINE_KEY,
|
|
12
|
+
CANONICAL_STATUS_LABELS,
|
|
13
|
+
HISTORY_LINE_KEY,
|
|
14
|
+
} from '@/parser/format.utils';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* issue 优先级(模板口径)
|
|
18
|
+
*/
|
|
19
|
+
export type IssueTemplatePriority = 'P1' | 'P2' | 'P3';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 模板引用形态(沿革 / 阻塞行的引用条目)
|
|
23
|
+
*/
|
|
24
|
+
export type IssueTemplateRef = {
|
|
25
|
+
/**
|
|
26
|
+
* 引用编号(3-4 位零填充)
|
|
27
|
+
*/
|
|
28
|
+
number: string;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 引用完整 slug(缺席时渲染为纯编号引用)
|
|
32
|
+
*/
|
|
33
|
+
slug?: string;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* instance 模式渲染入参
|
|
38
|
+
*/
|
|
39
|
+
export type IssueTemplateValues = {
|
|
40
|
+
/**
|
|
41
|
+
* 编号(4 位零填充,编号门禁取号分配)
|
|
42
|
+
*/
|
|
43
|
+
number: string;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* slug(kebab-case,默认禁数字)
|
|
47
|
+
*/
|
|
48
|
+
slug: string;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 一句话标题(问题是什么,禁笼统词)
|
|
52
|
+
*/
|
|
53
|
+
title: string;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 优先级
|
|
57
|
+
*/
|
|
58
|
+
priority: IssueTemplatePriority;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 所属域(config 登记的 scope title)
|
|
62
|
+
*/
|
|
63
|
+
scopeTitle: string;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 立案日期(YYYY-MM-DD)
|
|
67
|
+
*/
|
|
68
|
+
date: string;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 来源(问题原文出处 + 日期)
|
|
72
|
+
*/
|
|
73
|
+
source: string;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* 沿革引用(缺席时省略沿革行)
|
|
77
|
+
*/
|
|
78
|
+
historyRefs?: IssueTemplateRef[];
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* 阻塞引用(缺席时省略阻塞行;全部 🟢 后方可收口)
|
|
82
|
+
*/
|
|
83
|
+
blockedByRefs?: IssueTemplateRef[];
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 正文段「一、问题动机」模板(两模式共享)
|
|
88
|
+
*/
|
|
89
|
+
const MOTIVATION_SECTION = [
|
|
90
|
+
'## 一、问题动机',
|
|
91
|
+
'',
|
|
92
|
+
'> <问题原文逐字记录,禁止转述改写>',
|
|
93
|
+
'',
|
|
94
|
+
'客观证据(报错/复现步骤/涉及文件/日志路径):',
|
|
95
|
+
'- <证据>',
|
|
96
|
+
'',
|
|
97
|
+
].join('\n');
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* 正文段「二、现状与证据」模板(两模式共享)
|
|
101
|
+
*/
|
|
102
|
+
const CURRENT_STATE_SECTION = [
|
|
103
|
+
'## 二、现状与证据',
|
|
104
|
+
'',
|
|
105
|
+
'<排查链路与实证;无法确认根因时如实标注「根因待治理时核实」>',
|
|
106
|
+
'',
|
|
107
|
+
].join('\n');
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* 正文段「三、处置方向」模板(两模式共享)
|
|
111
|
+
*/
|
|
112
|
+
const DIRECTION_SECTION = [
|
|
113
|
+
'## 三、处置方向',
|
|
114
|
+
'',
|
|
115
|
+
'【1】备选方案和分析',
|
|
116
|
+
'- <备选>:<分析>',
|
|
117
|
+
'',
|
|
118
|
+
'【2】推荐方案和分析',
|
|
119
|
+
'- <推荐>:<分析;能自决的直接执行,裁决类停下等用户裁定>',
|
|
120
|
+
'',
|
|
121
|
+
].join('\n');
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* 正文段「四、处置结果/验证口径」模板(两模式共享)
|
|
125
|
+
*/
|
|
126
|
+
const DISPOSITION_SECTION = [
|
|
127
|
+
'## 四、处置结果/验证口径',
|
|
128
|
+
'',
|
|
129
|
+
'<随治理进展回填;判据以 criteria 围栏块声明;收口由 verify-close 回填证据锚点>',
|
|
130
|
+
'',
|
|
131
|
+
].join('\n');
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* 渲染引用行内容
|
|
135
|
+
*
|
|
136
|
+
* 有 slug 渲染为 markdown 相对链接(`[0035](./0035-slug.md)`),无 slug 渲染为
|
|
137
|
+
* 纯编号引用(`[0035]`);多条引用以「;」连接。
|
|
138
|
+
*
|
|
139
|
+
* @param refs - 引用列表
|
|
140
|
+
* @returns 引用行内容文本
|
|
141
|
+
*/
|
|
142
|
+
function renderRefList(refs: IssueTemplateRef[]): string {
|
|
143
|
+
/**
|
|
144
|
+
* 单条引用渲染结果列表
|
|
145
|
+
*/
|
|
146
|
+
const rendered = refs.map((ref) =>
|
|
147
|
+
ref.slug ? `[${ref.number}](./${ref.number}-${ref.slug}.md)` : `[${ref.number}]`,
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
return rendered.join(';');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* 渲染标准模板骨架(skeleton 模式)
|
|
155
|
+
*
|
|
156
|
+
* 占位符形态,供 template 命令展示与指南活样板消费。
|
|
157
|
+
*
|
|
158
|
+
* @returns 骨架全文(以单个换行结尾)
|
|
159
|
+
*/
|
|
160
|
+
export function renderIssueTemplateSkeleton(): string {
|
|
161
|
+
return [
|
|
162
|
+
'# <NNNN> - <一句话标题:问题是什么,禁笼统词>',
|
|
163
|
+
'',
|
|
164
|
+
'**优先级**: <P1|P2|P3>',
|
|
165
|
+
`**状态**: ${CANONICAL_STATUS_LABELS.red}`,
|
|
166
|
+
'**立案日期**: <YYYY-MM-DD>',
|
|
167
|
+
'**所属域**: <scope title>',
|
|
168
|
+
'**来源**: <问题原文出处(用户裁定/会话记录/自动巡检 + 日期)>',
|
|
169
|
+
'**沿革**: <可选:前序 issue 引用,如 [0035](./0035-<slug>.md)>',
|
|
170
|
+
'**阻塞**: <可选:被阻塞于哪些 issue;全部 🟢 后方可收口>',
|
|
171
|
+
'',
|
|
172
|
+
MOTIVATION_SECTION,
|
|
173
|
+
CURRENT_STATE_SECTION,
|
|
174
|
+
DIRECTION_SECTION,
|
|
175
|
+
DISPOSITION_SECTION,
|
|
176
|
+
].join('\n');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* 渲染标准模板实例(instance 模式)
|
|
181
|
+
*
|
|
182
|
+
* 头部元信息代入实际值;沿革 / 阻塞行按入参省略或渲染;正文四段保留占位指导
|
|
183
|
+
* 文本(建档即骨架,内容随治理进展回填——apex 同款语义)。
|
|
184
|
+
*
|
|
185
|
+
* @param values - 实际值入参
|
|
186
|
+
* @returns 可直接落盘的 issue 文档全文(以单个换行结尾)
|
|
187
|
+
*/
|
|
188
|
+
export function renderIssueTemplate(values: IssueTemplateValues): string {
|
|
189
|
+
/**
|
|
190
|
+
* 头部元信息行列表
|
|
191
|
+
*/
|
|
192
|
+
const headerLines: string[] = [
|
|
193
|
+
`# ${values.number} - ${values.title}`,
|
|
194
|
+
'',
|
|
195
|
+
`**优先级**: ${values.priority}`,
|
|
196
|
+
`**状态**: ${CANONICAL_STATUS_LABELS.red}`,
|
|
197
|
+
`**立案日期**: ${values.date}`,
|
|
198
|
+
`**所属域**: ${values.scopeTitle}`,
|
|
199
|
+
`**来源**: ${values.source}`,
|
|
200
|
+
];
|
|
201
|
+
|
|
202
|
+
if (values.historyRefs !== undefined && values.historyRefs.length > 0) {
|
|
203
|
+
headerLines.push(`**${HISTORY_LINE_KEY}**: ${renderRefList(values.historyRefs)}`);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (values.blockedByRefs !== undefined && values.blockedByRefs.length > 0) {
|
|
207
|
+
headerLines.push(`**${BLOCKING_LINE_KEY}**: ${renderRefList(values.blockedByRefs)}`);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
headerLines.push('');
|
|
211
|
+
|
|
212
|
+
return [
|
|
213
|
+
headerLines.join('\n'),
|
|
214
|
+
MOTIVATION_SECTION,
|
|
215
|
+
CURRENT_STATE_SECTION,
|
|
216
|
+
DIRECTION_SECTION,
|
|
217
|
+
DISPOSITION_SECTION,
|
|
218
|
+
].join('\n');
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* 渲染 criteria 判据块示例(template --with-criteria 附带)
|
|
223
|
+
*
|
|
224
|
+
* 示例本身为解析器合法形态(照抄即通过机检),字段说明以 `#` 注释行承载。
|
|
225
|
+
*
|
|
226
|
+
* @returns 判据块示例全文
|
|
227
|
+
*/
|
|
228
|
+
export function renderCriteriaBlockExample(): string {
|
|
229
|
+
return [
|
|
230
|
+
'```criteria',
|
|
231
|
+
'# 一行一判据:label|channel|cwd|command(四段竖线分隔,段内禁竖线)',
|
|
232
|
+
'# channel 限字母数字起首的 [A-Za-z0-9._-];cwd 相对仓库根;command 为退出码即判据的自动化命令',
|
|
233
|
+
'# summary 行唯一且非空——真跑收口 --criteria-from-issue 的机器可读载体',
|
|
234
|
+
'summary: 示例:单包测试全绿',
|
|
235
|
+
'单包测试|test|packages/demo|bun run test',
|
|
236
|
+
'```',
|
|
237
|
+
'',
|
|
238
|
+
].join('\n');
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* 渲染注册表 README 格式存根(init 命令落盘)
|
|
243
|
+
*
|
|
244
|
+
* 一页式格式速览:建档入口、状态行五态、正文四段;完整规范指向格式真源指南。
|
|
245
|
+
*
|
|
246
|
+
* @param scopeTitle - scope 人类可读名
|
|
247
|
+
* @param guideRelPath - 格式指南相对本注册表目录的路径(如 `../../docs/guides/issue-file-format.md`)
|
|
248
|
+
* @returns README 存根全文
|
|
249
|
+
*/
|
|
250
|
+
export function renderRegistryReadmeStub(scopeTitle: string, guideRelPath: string): string {
|
|
251
|
+
return [
|
|
252
|
+
`# ${scopeTitle} issue 注册表`,
|
|
253
|
+
'',
|
|
254
|
+
'本目录是 lzi-issues 注册表(`NNNN-<slug>.md`,目录即编号命名空间,编号独立序列)。',
|
|
255
|
+
'',
|
|
256
|
+
'## 建档',
|
|
257
|
+
'',
|
|
258
|
+
'- `lzi-issues create --title <标题> --scope <key>`(编号走门禁取号,禁止手工定号)',
|
|
259
|
+
'- 标准模板:`lzi-issues template`;完整格式规范:[issue-file-format](' + guideRelPath + ')',
|
|
260
|
+
'',
|
|
261
|
+
'## 状态行(处理情况第一入口)',
|
|
262
|
+
'',
|
|
263
|
+
`- ${CANONICAL_STATUS_LABELS.red}:待处理`,
|
|
264
|
+
`- ${CANONICAL_STATUS_LABELS.yellow}(须括注 ≥4 字挂起原因)`,
|
|
265
|
+
`- ${CANONICAL_STATUS_LABELS.green}(须处置段 + 证据锚点;收口走 \`lzi-issues verify-close\`)`,
|
|
266
|
+
`- ${CANONICAL_STATUS_LABELS.frozen}(仅经 \`lzi-issues freeze\`,理由须含「用户裁定」)`,
|
|
267
|
+
`- ${CANONICAL_STATUS_LABELS.canceled}(须括注取消原因)`,
|
|
268
|
+
'',
|
|
269
|
+
'## 正文四段',
|
|
270
|
+
'',
|
|
271
|
+
'一、问题动机 → 二、现状与证据 → 三、处置方向(【1】备选方案和分析 / 【2】推荐方案和分析)→ 四、处置结果/验证口径',
|
|
272
|
+
'',
|
|
273
|
+
].join('\n');
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* 模板字段说明条目(template --format json 载荷)
|
|
278
|
+
*/
|
|
279
|
+
export type TemplateFieldDoc = {
|
|
280
|
+
/**
|
|
281
|
+
* 字段名(渲染入参名或元信息键)
|
|
282
|
+
*/
|
|
283
|
+
name: string;
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* 是否必填
|
|
287
|
+
*/
|
|
288
|
+
required: boolean;
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* 字段说明
|
|
292
|
+
*/
|
|
293
|
+
description: string;
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* 标准模板字段说明全集(供 agent / 使用者程序化消费)
|
|
298
|
+
*/
|
|
299
|
+
export const TEMPLATE_FIELDS: readonly TemplateFieldDoc[] = [
|
|
300
|
+
{
|
|
301
|
+
name: 'number',
|
|
302
|
+
required: true,
|
|
303
|
+
description: 'scope 内编号,4 位零填充,由编号门禁取号分配(写入时刻确定)',
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
name: 'slug',
|
|
307
|
+
required: true,
|
|
308
|
+
description:
|
|
309
|
+
'ASCII kebab slug(小写字母/数字/连字符,禁中文),slug 段默认禁数字(slugDigitPolicy)',
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
name: 'title',
|
|
313
|
+
required: true,
|
|
314
|
+
description: '一句话标题:问题是什么,禁笼统词',
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
name: 'priority',
|
|
318
|
+
required: true,
|
|
319
|
+
description: '优先级,P1 | P2 | P3',
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
name: 'status',
|
|
323
|
+
required: true,
|
|
324
|
+
description: '状态行五态;建档固定 🔴 待处理,流转走工具(verify-close / freeze 等)',
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
name: '立案日期',
|
|
328
|
+
required: true,
|
|
329
|
+
description: 'YYYY-MM-DD',
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
name: '所属域',
|
|
333
|
+
required: true,
|
|
334
|
+
description: 'config 登记的 scope title',
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
name: '来源',
|
|
338
|
+
required: true,
|
|
339
|
+
description: '问题原文出处(用户裁定 / 会话记录 / 自动巡检 + 日期)',
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
name: '沿革',
|
|
343
|
+
required: false,
|
|
344
|
+
description: '前序 issue 引用,markdown 相对链接或纯编号形态',
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
name: '阻塞',
|
|
348
|
+
required: false,
|
|
349
|
+
description: '被阻塞于哪些 issue;全部 🟢 后方可收口(blockingMustBeGreen 门禁)',
|
|
350
|
+
},
|
|
351
|
+
] as const;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 黄灯分诊分类(B > C > A 优先级,全文关键词扫描)
|
|
3
|
+
*
|
|
4
|
+
* B = 判据依赖环境(真机 / 合作方 / 人工确认等)→ 诚实保持黄灯;
|
|
5
|
+
* C = 被并行在途线阻断(在途 / 并行线 / 待补跑等)→ 待落地后复跑;
|
|
6
|
+
* A = 判据全部机器可跑 → verify-close 补跑收口。
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { IssueDocRecord } from '@/parser/registry.parser';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 分诊类别
|
|
13
|
+
*/
|
|
14
|
+
export type TriageCategory = 'A' | 'B' | 'C';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 分诊结果
|
|
18
|
+
*/
|
|
19
|
+
export type TriageClassification = {
|
|
20
|
+
/**
|
|
21
|
+
* 类别
|
|
22
|
+
*/
|
|
23
|
+
category: TriageCategory;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 分类依据(中文说明)
|
|
27
|
+
*/
|
|
28
|
+
reason: string;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* B 类关键词(环境依赖)
|
|
33
|
+
*/
|
|
34
|
+
const CATEGORY_B_KEYWORDS = [
|
|
35
|
+
'真机',
|
|
36
|
+
'雪场',
|
|
37
|
+
'合作方',
|
|
38
|
+
'回执',
|
|
39
|
+
'技术支持',
|
|
40
|
+
'REAL_DEVICE',
|
|
41
|
+
'人工确认',
|
|
42
|
+
'环境不具备',
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* C 类关键词(并行在途)
|
|
47
|
+
*/
|
|
48
|
+
const CATEGORY_C_KEYWORDS = ['在途', '并行线', '待并行', '让锁', '待落地', '待补跑', '待复跑'];
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 黄灯分诊分类
|
|
52
|
+
*
|
|
53
|
+
* @param record - 解析产物
|
|
54
|
+
* @returns 分诊结果
|
|
55
|
+
*/
|
|
56
|
+
export function classifyIssue(record: IssueDocRecord): TriageClassification {
|
|
57
|
+
/**
|
|
58
|
+
* B 类命中关键词
|
|
59
|
+
*/
|
|
60
|
+
const bHit = CATEGORY_B_KEYWORDS.find((keyword) => record.rawContent.includes(keyword));
|
|
61
|
+
|
|
62
|
+
if (bHit !== undefined) {
|
|
63
|
+
return {
|
|
64
|
+
category: 'B',
|
|
65
|
+
reason: `判据依赖环境(命中「${bHit}」)——诚实保持黄灯,环境具备后再收口`,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* C 类命中关键词
|
|
71
|
+
*/
|
|
72
|
+
const cHit = CATEGORY_C_KEYWORDS.find((keyword) => record.rawContent.includes(keyword));
|
|
73
|
+
|
|
74
|
+
if (cHit !== undefined) {
|
|
75
|
+
return {
|
|
76
|
+
category: 'C',
|
|
77
|
+
reason: `被并行在途线阻断(命中「${cHit}」)——待在途线落地后复跑收口`,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 判据命令数(0 条提示补录)
|
|
83
|
+
*/
|
|
84
|
+
const criteriaCount = record.criteria?.criteria.length ?? 0;
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
category: 'A',
|
|
88
|
+
reason:
|
|
89
|
+
criteriaCount > 0
|
|
90
|
+
? `判据全部机器可跑(${criteriaCount} 条)——verify-close 补跑收口`
|
|
91
|
+
: '判据清单为空,需人工核对后补录 criteria 块再收口',
|
|
92
|
+
};
|
|
93
|
+
}
|