spec-manager 0.1.0

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 (159) hide show
  1. package/AGENTS.md +27 -0
  2. package/CODEBUDDY.md +19 -0
  3. package/LICENSE +21 -0
  4. package/README.md +531 -0
  5. package/dist/cli/audit.d.ts +10 -0
  6. package/dist/cli/audit.d.ts.map +1 -0
  7. package/dist/cli/audit.js +62 -0
  8. package/dist/cli/audit.js.map +1 -0
  9. package/dist/cli/change.d.ts +10 -0
  10. package/dist/cli/change.d.ts.map +1 -0
  11. package/dist/cli/change.js +103 -0
  12. package/dist/cli/change.js.map +1 -0
  13. package/dist/cli/common.d.ts +5 -0
  14. package/dist/cli/common.d.ts.map +1 -0
  15. package/dist/cli/common.js +17 -0
  16. package/dist/cli/common.js.map +1 -0
  17. package/dist/cli/decision.d.ts +13 -0
  18. package/dist/cli/decision.d.ts.map +1 -0
  19. package/dist/cli/decision.js +185 -0
  20. package/dist/cli/decision.js.map +1 -0
  21. package/dist/cli/dict.d.ts +10 -0
  22. package/dist/cli/dict.d.ts.map +1 -0
  23. package/dist/cli/dict.js +73 -0
  24. package/dist/cli/dict.js.map +1 -0
  25. package/dist/cli/incident.d.ts +10 -0
  26. package/dist/cli/incident.d.ts.map +1 -0
  27. package/dist/cli/incident.js +119 -0
  28. package/dist/cli/incident.js.map +1 -0
  29. package/dist/cli/index.d.ts +3 -0
  30. package/dist/cli/index.d.ts.map +1 -0
  31. package/dist/cli/index.js +30 -0
  32. package/dist/cli/index.js.map +1 -0
  33. package/dist/cli/project.d.ts +3 -0
  34. package/dist/cli/project.d.ts.map +1 -0
  35. package/dist/cli/project.js +144 -0
  36. package/dist/cli/project.js.map +1 -0
  37. package/dist/cli/spec.d.ts +3 -0
  38. package/dist/cli/spec.d.ts.map +1 -0
  39. package/dist/cli/spec.js +289 -0
  40. package/dist/cli/spec.js.map +1 -0
  41. package/dist/cli/task.d.ts +14 -0
  42. package/dist/cli/task.d.ts.map +1 -0
  43. package/dist/cli/task.js +287 -0
  44. package/dist/cli/task.js.map +1 -0
  45. package/dist/cli/usability.d.ts +3 -0
  46. package/dist/cli/usability.d.ts.map +1 -0
  47. package/dist/cli/usability.js +153 -0
  48. package/dist/cli/usability.js.map +1 -0
  49. package/dist/core/agents.d.ts +43 -0
  50. package/dist/core/agents.d.ts.map +1 -0
  51. package/dist/core/agents.js +194 -0
  52. package/dist/core/agents.js.map +1 -0
  53. package/dist/core/archive.d.ts +35 -0
  54. package/dist/core/archive.d.ts.map +1 -0
  55. package/dist/core/archive.js +360 -0
  56. package/dist/core/archive.js.map +1 -0
  57. package/dist/core/audit-events.d.ts +12 -0
  58. package/dist/core/audit-events.d.ts.map +1 -0
  59. package/dist/core/audit-events.js +16 -0
  60. package/dist/core/audit-events.js.map +1 -0
  61. package/dist/core/audit.d.ts +78 -0
  62. package/dist/core/audit.d.ts.map +1 -0
  63. package/dist/core/audit.js +157 -0
  64. package/dist/core/audit.js.map +1 -0
  65. package/dist/core/constants.d.ts +28 -0
  66. package/dist/core/constants.d.ts.map +1 -0
  67. package/dist/core/constants.js +30 -0
  68. package/dist/core/constants.js.map +1 -0
  69. package/dist/core/decision.d.ts +69 -0
  70. package/dist/core/decision.d.ts.map +1 -0
  71. package/dist/core/decision.js +210 -0
  72. package/dist/core/decision.js.map +1 -0
  73. package/dist/core/delta.d.ts +85 -0
  74. package/dist/core/delta.d.ts.map +1 -0
  75. package/dist/core/delta.js +264 -0
  76. package/dist/core/delta.js.map +1 -0
  77. package/dist/core/dict.d.ts +28 -0
  78. package/dist/core/dict.d.ts.map +1 -0
  79. package/dist/core/dict.js +57 -0
  80. package/dist/core/dict.js.map +1 -0
  81. package/dist/core/frontmatter.d.ts +19 -0
  82. package/dist/core/frontmatter.d.ts.map +1 -0
  83. package/dist/core/frontmatter.js +57 -0
  84. package/dist/core/frontmatter.js.map +1 -0
  85. package/dist/core/incident.d.ts +45 -0
  86. package/dist/core/incident.d.ts.map +1 -0
  87. package/dist/core/incident.js +128 -0
  88. package/dist/core/incident.js.map +1 -0
  89. package/dist/core/paths.d.ts +68 -0
  90. package/dist/core/paths.d.ts.map +1 -0
  91. package/dist/core/paths.js +162 -0
  92. package/dist/core/paths.js.map +1 -0
  93. package/dist/core/repository.d.ts +13 -0
  94. package/dist/core/repository.d.ts.map +1 -0
  95. package/dist/core/repository.js +29 -0
  96. package/dist/core/repository.js.map +1 -0
  97. package/dist/core/spec-io.d.ts +125 -0
  98. package/dist/core/spec-io.d.ts.map +1 -0
  99. package/dist/core/spec-io.js +260 -0
  100. package/dist/core/spec-io.js.map +1 -0
  101. package/dist/core/status.d.ts +22 -0
  102. package/dist/core/status.d.ts.map +1 -0
  103. package/dist/core/status.js +54 -0
  104. package/dist/core/status.js.map +1 -0
  105. package/dist/core/task.d.ts +118 -0
  106. package/dist/core/task.d.ts.map +1 -0
  107. package/dist/core/task.js +340 -0
  108. package/dist/core/task.js.map +1 -0
  109. package/dist/core/usability.d.ts +25 -0
  110. package/dist/core/usability.d.ts.map +1 -0
  111. package/dist/core/usability.js +136 -0
  112. package/dist/core/usability.js.map +1 -0
  113. package/dist/core/validate.d.ts +34 -0
  114. package/dist/core/validate.d.ts.map +1 -0
  115. package/dist/core/validate.js +195 -0
  116. package/dist/core/validate.js.map +1 -0
  117. package/dist/index.d.ts +11 -0
  118. package/dist/index.d.ts.map +1 -0
  119. package/dist/index.js +12 -0
  120. package/dist/index.js.map +1 -0
  121. package/dist/schemas/change.d.ts +138 -0
  122. package/dist/schemas/change.d.ts.map +1 -0
  123. package/dist/schemas/change.js +38 -0
  124. package/dist/schemas/change.js.map +1 -0
  125. package/dist/schemas/spec.d.ts +233 -0
  126. package/dist/schemas/spec.d.ts.map +1 -0
  127. package/dist/schemas/spec.js +56 -0
  128. package/dist/schemas/spec.js.map +1 -0
  129. package/package.json +66 -0
  130. package/rules/_TEMPLATE.md +20 -0
  131. package/rules/code-discipline.md +47 -0
  132. package/rules/codebase-survey.md +37 -0
  133. package/rules/delta.md +42 -0
  134. package/rules/doc-governance.md +195 -0
  135. package/rules/flow-control.md +65 -0
  136. package/rules/quality-gate.md +73 -0
  137. package/skill/SKILL.md +90 -0
  138. package/skill/subskills/adr.md +73 -0
  139. package/skill/subskills/change.md +118 -0
  140. package/skill/subskills/design.md +53 -0
  141. package/skill/subskills/impl.md +100 -0
  142. package/skill/subskills/plan.md +46 -0
  143. package/skill/subskills/postmortem.md +77 -0
  144. package/skill/subskills/prd.md +72 -0
  145. package/skill/subskills/quick.md +25 -0
  146. package/skill/subskills/release.md +50 -0
  147. package/skill/subskills/research.md +35 -0
  148. package/skill/subskills/runbook.md +44 -0
  149. package/skill/subskills/testplan.md +48 -0
  150. package/templates/L0-prd.md +43 -0
  151. package/templates/L1-prd.md +130 -0
  152. package/templates/L2-design.md +135 -0
  153. package/templates/L3-impl.md +125 -0
  154. package/templates/agent-plan.json +17 -0
  155. package/templates/agents/AGENTS.md +27 -0
  156. package/templates/agents/CLAUDE.md +11 -0
  157. package/templates/agents/CODEBUDDY.md +23 -0
  158. package/templates/agents/codebuddy-skill/SKILL.md +46 -0
  159. package/templates/decision.md +42 -0
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Delta Spec 解析器
3
+ *
4
+ * 一个 change 提案 = changes/<name>/
5
+ * ├── proposal.md ← 为什么 + 范围
6
+ * ├── specs/<topic>/<code>/<code>.md ← ADDED 时主 spec 的占位(带 frontmatter)
7
+ * └── deltas/<code>.md ← ADDED/MODIFIED/REMOVED/RENAMED 列表
8
+ *
9
+ * 本文件:解析 deltas/<code>.md 或内联在 spec 文件 ## DELTA 段中的变更。
10
+ *
11
+ * 文件格式(每个 delta 文件):
12
+ * ---
13
+ * code: 2026-06-04-a1b2c3
14
+ * ---
15
+ *
16
+ * ## ADDED Requirements
17
+ * ### Requirement: Two-Factor Authentication
18
+ * The system **SHALL** ...(完整 SHALL/MUST 表述)
19
+ *
20
+ * ## MODIFIED Requirements
21
+ * ### Requirement: ...
22
+ * **旧文**:...
23
+ * **新文**:...
24
+ *
25
+ * ## REMOVED Requirements
26
+ * ### Requirement: ...
27
+ * 说明删除原因
28
+ *
29
+ * ## RENAMED Requirements
30
+ * - FROM: 2026-06-04-a1b2c3-old TO: 2026-06-04-a1b2c3-new
31
+ *
32
+ * Archive 时按 RENAMED→REMOVED→MODIFIED→ADDED 顺序应用:
33
+ * - RENAMED: spec 文件改名 + frontmatter code 改写
34
+ * - REMOVED: spec 文件移到 archive
35
+ * - MODIFIED: 替换 section 内容
36
+ * - ADDED: 创建新 spec
37
+ */
38
+ import { type ChangeEntry, type DeltaSpec } from '../schemas/change.js';
39
+ import type { ProjectPaths } from './paths.js';
40
+ export interface ChangeDir {
41
+ root: string;
42
+ proposal: string;
43
+ deltaFiles: string[];
44
+ specFiles: string[];
45
+ }
46
+ export declare function changeDir(paths: ProjectPaths, name: string): string;
47
+ export declare function getChangeDir(paths: ProjectPaths, name: string): ChangeDir | null;
48
+ export declare function listChanges(paths: ProjectPaths): Array<{
49
+ name: string;
50
+ root: string;
51
+ created: string;
52
+ }>;
53
+ /**
54
+ * 解析一个 delta 文件为 ChangeEntry 列表。
55
+ * 一个 delta 文件可以含多个 ## ADDED/MODIFIED/REMOVED/RENAMED 段。
56
+ */
57
+ export declare function parseDeltaFile(filePath: string): {
58
+ specCode: string;
59
+ entries: ChangeEntry[];
60
+ };
61
+ /**
62
+ * 解析整个 change 目录,返回结构化 DeltaSpec。
63
+ * 校验:每个 delta 文件的 specCode 必须和文件名一致;entry.code 必填。
64
+ */
65
+ export declare function parseDeltaSpec(paths: ProjectPaths, name: string): DeltaSpec;
66
+ /**
67
+ * 创建一个新 change 目录(scaffold)。
68
+ * 包含 proposal.md 模板 + deltas/ + specs/ 空目录。
69
+ */
70
+ export interface CreateChangeInput {
71
+ paths: ProjectPaths;
72
+ name: string;
73
+ description?: string;
74
+ }
75
+ export declare function createChange(input: CreateChangeInput): {
76
+ name: string;
77
+ root: string;
78
+ proposalFile: string;
79
+ };
80
+ /**
81
+ * 把 delta entry 渲染成可写的 delta 文件格式。
82
+ * 用户可以直接 edit 这个文件,然后再 archive。
83
+ */
84
+ export declare function renderDeltaFile(specCode: string, entries: ChangeEntry[]): string;
85
+ //# sourceMappingURL=delta.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delta.d.ts","sourceRoot":"","sources":["../../src/core/delta.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAKH,OAAO,EAAmB,KAAK,WAAW,EAAkB,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACzG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAwBhF;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAevG;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,WAAW,EAAE,CAAA;CAAE,CA+D7F;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,CAoB3E;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,YAAY,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CA0B3G;AAqBD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,MAAM,CA2BhF"}
@@ -0,0 +1,264 @@
1
+ /**
2
+ * Delta Spec 解析器
3
+ *
4
+ * 一个 change 提案 = changes/<name>/
5
+ * ├── proposal.md ← 为什么 + 范围
6
+ * ├── specs/<topic>/<code>/<code>.md ← ADDED 时主 spec 的占位(带 frontmatter)
7
+ * └── deltas/<code>.md ← ADDED/MODIFIED/REMOVED/RENAMED 列表
8
+ *
9
+ * 本文件:解析 deltas/<code>.md 或内联在 spec 文件 ## DELTA 段中的变更。
10
+ *
11
+ * 文件格式(每个 delta 文件):
12
+ * ---
13
+ * code: 2026-06-04-a1b2c3
14
+ * ---
15
+ *
16
+ * ## ADDED Requirements
17
+ * ### Requirement: Two-Factor Authentication
18
+ * The system **SHALL** ...(完整 SHALL/MUST 表述)
19
+ *
20
+ * ## MODIFIED Requirements
21
+ * ### Requirement: ...
22
+ * **旧文**:...
23
+ * **新文**:...
24
+ *
25
+ * ## REMOVED Requirements
26
+ * ### Requirement: ...
27
+ * 说明删除原因
28
+ *
29
+ * ## RENAMED Requirements
30
+ * - FROM: 2026-06-04-a1b2c3-old TO: 2026-06-04-a1b2c3-new
31
+ *
32
+ * Archive 时按 RENAMED→REMOVED→MODIFIED→ADDED 顺序应用:
33
+ * - RENAMED: spec 文件改名 + frontmatter code 改写
34
+ * - REMOVED: spec 文件移到 archive
35
+ * - MODIFIED: 替换 section 内容
36
+ * - ADDED: 创建新 spec
37
+ */
38
+ import { existsSync, writeFileSync, readdirSync, mkdirSync } from 'node:fs';
39
+ import { join } from 'node:path';
40
+ import { readFrontmatter, writeFrontmatter } from './frontmatter.js';
41
+ import { DeltaSpecSchema } from '../schemas/change.js';
42
+ export function changeDir(paths, name) {
43
+ return join(paths.changesDir, name);
44
+ }
45
+ export function getChangeDir(paths, name) {
46
+ const root = changeDir(paths, name);
47
+ if (!existsSync(root))
48
+ return null;
49
+ const proposal = join(root, 'proposal.md');
50
+ const deltasDir = join(root, 'deltas');
51
+ const specsDir = join(root, 'specs');
52
+ const deltaFiles = existsSync(deltasDir)
53
+ ? readdirSync(deltasDir).filter(f => f.endsWith('.md')).map(f => join(deltasDir, f))
54
+ : [];
55
+ // 树形布局下,占位文件位置: changes/<name>/specs/<topic>/<code>/<code>.md
56
+ // 扁平,不在 change 内部再嵌套父链 — placeholder 只用于携带 level/parentCode
57
+ const specFiles = [];
58
+ if (existsSync(specsDir)) {
59
+ for (const topicEntry of readdirSync(specsDir, { withFileTypes: true })) {
60
+ if (!topicEntry.isDirectory() || topicEntry.name.startsWith('.'))
61
+ continue;
62
+ const topicDir = join(specsDir, topicEntry.name);
63
+ for (const specEntry of readdirSync(topicDir, { withFileTypes: true })) {
64
+ if (!specEntry.isDirectory() || specEntry.name.startsWith('.') || specEntry.name.startsWith('_'))
65
+ continue;
66
+ const f = join(topicDir, specEntry.name, `${specEntry.name}.md`);
67
+ if (existsSync(f))
68
+ specFiles.push(f);
69
+ }
70
+ }
71
+ }
72
+ return { root, proposal, deltaFiles, specFiles };
73
+ }
74
+ export function listChanges(paths) {
75
+ if (!existsSync(paths.changesDir))
76
+ return [];
77
+ return readdirSync(paths.changesDir)
78
+ .filter(f => !f.startsWith('.'))
79
+ .map(f => {
80
+ const root = join(paths.changesDir, f);
81
+ const proposalPath = join(root, 'proposal.md');
82
+ let created = '';
83
+ if (existsSync(proposalPath)) {
84
+ const { data } = readFrontmatter(proposalPath);
85
+ created = data.created ?? '';
86
+ }
87
+ return { name: f, root, created };
88
+ })
89
+ .sort((a, b) => a.name.localeCompare(b.name));
90
+ }
91
+ /**
92
+ * 解析一个 delta 文件为 ChangeEntry 列表。
93
+ * 一个 delta 文件可以含多个 ## ADDED/MODIFIED/REMOVED/RENAMED 段。
94
+ */
95
+ export function parseDeltaFile(filePath) {
96
+ const { data, content } = readFrontmatter(filePath);
97
+ const fm = data;
98
+ if (!fm.code)
99
+ throw new Error(`Delta 文件 ${filePath} 缺 frontmatter code 字段`);
100
+ const entries = [];
101
+ // 按 ## XXX 段切分
102
+ const sections = content.split(/^## (ADDED|MODIFIED|REMOVED|RENAMED) Requirements$/m);
103
+ // sections[0] = 前言;sections[1] = 第一个 op 名;sections[2] = 第一个 op 内容;...
104
+ for (let i = 1; i < sections.length; i += 2) {
105
+ const op = sections[i];
106
+ const body = sections[i + 1] ?? '';
107
+ if (op === 'RENAMED') {
108
+ // 形如: - FROM: <old> TO: <new>
109
+ const matches = body.matchAll(/FROM:\s*(\S+)\s+TO:\s*(\S+)/g);
110
+ for (const m of matches) {
111
+ entries.push({ op, code: m[1], newCode: m[2] });
112
+ }
113
+ }
114
+ else if (op === 'REMOVED') {
115
+ // 形如: ### Requirement: <code>
116
+ // 或 - <code>: <reason>
117
+ const reqMatches = body.matchAll(/### Requirement:\s*(\S+)/g);
118
+ for (const m of reqMatches) {
119
+ entries.push({ op, code: m[1] });
120
+ }
121
+ }
122
+ else if (op === 'ADDED' || op === 'MODIFIED') {
123
+ // 形如: ### Requirement: <code>\n<content>\n
124
+ // 用 indexOf 找每个 ### Requirement: 起点和下一个段头终点
125
+ const reqRegex = /^### Requirement:\s*(\S+)$/gm;
126
+ let m;
127
+ const reqStarts = [];
128
+ while ((m = reqRegex.exec(body)) !== null) {
129
+ const code = m[1];
130
+ const bodyStart = m.index + m[0].length; // 跳过整行(含 \n)
131
+ reqStarts.push({ code, lineStart: m.index, bodyStart });
132
+ }
133
+ for (let i = 0; i < reqStarts.length; i++) {
134
+ const { code, bodyStart } = reqStarts[i];
135
+ const nextStart = i + 1 < reqStarts.length ? reqStarts[i + 1].lineStart : body.length;
136
+ const reqBody = body.slice(bodyStart, nextStart).trim();
137
+ if (op === 'ADDED') {
138
+ // ADDED 必须含 title + content
139
+ const titleMatch = reqBody.match(/^#+\s+(.+?)$/m);
140
+ entries.push({
141
+ op,
142
+ code,
143
+ title: titleMatch?.[1]?.trim(),
144
+ content: reqBody,
145
+ changeSummary: 'delta ADDED',
146
+ });
147
+ }
148
+ else {
149
+ // MODIFIED: 必有 content(新文)
150
+ entries.push({
151
+ op,
152
+ code,
153
+ content: reqBody,
154
+ changeSummary: 'delta MODIFIED',
155
+ });
156
+ }
157
+ }
158
+ }
159
+ }
160
+ return { specCode: fm.code, entries };
161
+ }
162
+ /**
163
+ * 解析整个 change 目录,返回结构化 DeltaSpec。
164
+ * 校验:每个 delta 文件的 specCode 必须和文件名一致;entry.code 必填。
165
+ */
166
+ export function parseDeltaSpec(paths, name) {
167
+ const dir = getChangeDir(paths, name);
168
+ if (!dir)
169
+ throw new Error(`Change not found: ${name}`);
170
+ const allEntries = [];
171
+ for (const f of dir.deltaFiles) {
172
+ const { entries } = parseDeltaFile(f);
173
+ allEntries.push(...entries);
174
+ }
175
+ // 校验:entry.code 必填
176
+ for (const e of allEntries) {
177
+ if (!e.code)
178
+ throw new Error(`Delta entry 缺 code 字段:op=${e.op}`);
179
+ }
180
+ const result = {
181
+ name,
182
+ changes: allEntries,
183
+ };
184
+ return DeltaSpecSchema.parse(result);
185
+ }
186
+ export function createChange(input) {
187
+ if (!/^[a-z0-9-]+$/.test(input.name)) {
188
+ throw new Error(`change name 非法: ${input.name}(必须 ^[a-z0-9-]+$)`);
189
+ }
190
+ const root = changeDir(input.paths, input.name);
191
+ if (existsSync(root))
192
+ throw new Error(`Change 已存在: ${input.name}`);
193
+ mkdirSync(join(root, 'deltas'), { recursive: true });
194
+ mkdirSync(join(root, 'specs'), { recursive: true });
195
+ const proposalFile = join(root, 'proposal.md');
196
+ const now = new Date().toISOString();
197
+ const fm = {
198
+ name: input.name,
199
+ why: '',
200
+ scope: '',
201
+ created: now,
202
+ };
203
+ const content = renderProposalTemplate(input.name, input.description ?? '');
204
+ writeFileSync(proposalFile, writeFrontmatter(fm, content), 'utf8');
205
+ // 写 README 占位
206
+ const readme = join(root, 'README.md');
207
+ writeFileSync(readme, `# Change: ${input.name}\n\n创建于 ${now}\n`, 'utf8');
208
+ return { name: input.name, root, proposalFile };
209
+ }
210
+ function renderProposalTemplate(name, description) {
211
+ return `# ${name}
212
+
213
+ > ${description || '(请填写 change 描述)'}
214
+
215
+ ## 为什么
216
+ <!-- 这次变更的动机?要解决的痛点? -->
217
+
218
+ ## 范围
219
+ <!-- 影响哪些 topic / L1 / L2 / L3? -->
220
+
221
+ ## 风险与回滚
222
+ <!-- 失败时如何回滚?影响哪些 AC? -->
223
+
224
+ ## 影响的需求
225
+ <!-- 列出 affectedCriteria -->
226
+ `;
227
+ }
228
+ /**
229
+ * 把 delta entry 渲染成可写的 delta 文件格式。
230
+ * 用户可以直接 edit 这个文件,然后再 archive。
231
+ */
232
+ export function renderDeltaFile(specCode, entries) {
233
+ const fm = { code: specCode };
234
+ const body = [];
235
+ const grouped = {
236
+ ADDED: [], MODIFIED: [], REMOVED: [], RENAMED: [],
237
+ };
238
+ for (const e of entries)
239
+ grouped[e.op].push(e);
240
+ for (const op of ['ADDED', 'MODIFIED', 'REMOVED', 'RENAMED']) {
241
+ if (grouped[op].length === 0)
242
+ continue;
243
+ body.push(`## ${op} Requirements`);
244
+ body.push('');
245
+ for (const e of grouped[op]) {
246
+ if (op === 'RENAMED') {
247
+ body.push(`- FROM: ${e.code} TO: ${e.newCode}`);
248
+ }
249
+ else if (op === 'REMOVED') {
250
+ body.push(`### Requirement: ${e.code}`);
251
+ if (e.changeSummary)
252
+ body.push(e.changeSummary);
253
+ }
254
+ else {
255
+ body.push(`### Requirement: ${e.code}`);
256
+ body.push('');
257
+ body.push(e.content ?? '(待填写)');
258
+ }
259
+ body.push('');
260
+ }
261
+ }
262
+ return writeFrontmatter(fm, body.join('\n'));
263
+ }
264
+ //# sourceMappingURL=delta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delta.js","sourceRoot":"","sources":["../../src/core/delta.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * 数据字典 — 外部系统元数据(表.字段 → 业务含义)
3
+ * 存储位置:.spec-manager/dict.yaml
4
+ * 格式:
5
+ * <table>:
6
+ * <field>:
7
+ * meaning: <string>
8
+ * updated: <iso>
9
+ */
10
+ import type { ProjectPaths } from './paths.js';
11
+ export interface DictEntry {
12
+ table: string;
13
+ field: string;
14
+ meaning: string;
15
+ updated: string;
16
+ }
17
+ export interface DictData {
18
+ [table: string]: {
19
+ [field: string]: Omit<DictEntry, 'table' | 'field'>;
20
+ };
21
+ }
22
+ export declare function readDict(paths: ProjectPaths): DictData;
23
+ export declare function writeDict(paths: ProjectPaths, data: DictData): void;
24
+ export declare function registerField(paths: ProjectPaths, table: string, field: string, meaning: string): DictData;
25
+ export declare function queryField(paths: ProjectPaths, table: string, field: string): DictEntry | null;
26
+ export declare function listFieldsByTable(paths: ProjectPaths, table: string): DictEntry[];
27
+ export declare function listAllTables(paths: ProjectPaths): string[];
28
+ //# sourceMappingURL=dict.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dict.d.ts","sourceRoot":"","sources":["../../src/core/dict.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,QAAQ;IACvB,CAAC,KAAK,EAAE,MAAM,GAAG;QACf,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC;KACrD,CAAC;CACH;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,QAAQ,CAItD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,QAAQ,GAAG,IAAI,CAInE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,QAAQ,CAS1G;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAO9F;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,CAKjF;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,EAAE,CAG3D"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * 数据字典 — 外部系统元数据(表.字段 → 业务含义)
3
+ * 存储位置:.spec-manager/dict.yaml
4
+ * 格式:
5
+ * <table>:
6
+ * <field>:
7
+ * meaning: <string>
8
+ * updated: <iso>
9
+ */
10
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
11
+ import { dirname } from 'node:path';
12
+ import YAML from 'yaml';
13
+ export function readDict(paths) {
14
+ if (!existsSync(paths.dictFile))
15
+ return {};
16
+ const raw = readFileSync(paths.dictFile, 'utf8');
17
+ return YAML.parse(raw) ?? {};
18
+ }
19
+ export function writeDict(paths, data) {
20
+ const dir = dirname(paths.dictFile);
21
+ if (!existsSync(dir))
22
+ mkdirSync(dir, { recursive: true });
23
+ writeFileSync(paths.dictFile, YAML.stringify(data), 'utf8');
24
+ }
25
+ export function registerField(paths, table, field, meaning) {
26
+ const data = readDict(paths);
27
+ if (!data[table])
28
+ data[table] = {};
29
+ data[table][field] = {
30
+ meaning,
31
+ updated: new Date().toISOString(),
32
+ };
33
+ writeDict(paths, data);
34
+ return data;
35
+ }
36
+ export function queryField(paths, table, field) {
37
+ const data = readDict(paths);
38
+ const t = data[table];
39
+ if (!t)
40
+ return null;
41
+ const f = t[field];
42
+ if (!f)
43
+ return null;
44
+ return { table, field, ...f };
45
+ }
46
+ export function listFieldsByTable(paths, table) {
47
+ const data = readDict(paths);
48
+ const t = data[table];
49
+ if (!t)
50
+ return [];
51
+ return Object.entries(t).map(([field, v]) => ({ table, field, ...v }));
52
+ }
53
+ export function listAllTables(paths) {
54
+ const data = readDict(paths);
55
+ return Object.keys(data).sort();
56
+ }
57
+ //# sourceMappingURL=dict.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dict.js","sourceRoot":"","sources":["../../src/core/dict.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,IAAI,MAAM,MAAM,CAAC;AAgBxB,MAAM,UAAU,QAAQ,CAAC,KAAmB;IAC1C,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,CAAC;IAC3C,MAAM,GAAG,GAAG,YAAY,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACjD,OAAQ,IAAI,CAAC,KAAK,CAAC,GAAG,CAAc,IAAI,EAAE,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAmB,EAAE,IAAc;IAC3D,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACpC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,aAAa,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAmB,EAAE,KAAa,EAAE,KAAa,EAAE,OAAe;IAC9F,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC7B,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;IACnC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG;QACnB,OAAO;QACP,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KAClC,CAAC;IACF,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACvB,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAmB,EAAE,KAAa,EAAE,KAAa;IAC1E,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC7B,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;IACtB,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;IACnB,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAmB,EAAE,KAAa;IAClE,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC7B,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;IACtB,IAAI,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAClB,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;AACzE,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAmB;IAC/C,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC7B,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;AAClC,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * 读取文件,返回 { data, content }。
3
+ * data 是 YAML frontmatter,content 是正文(已剥离 frontmatter 标记)。
4
+ */
5
+ export declare function readFrontmatter(filePath: string): {
6
+ data: Record<string, unknown>;
7
+ content: string;
8
+ };
9
+ /**
10
+ * 序列化:data + content → 带 frontmatter 的 markdown 字符串。
11
+ * gray-matter 不接受 undefined 值;递归剔除后再 dump。
12
+ */
13
+ export declare function writeFrontmatter(data: Record<string, unknown>, content: string): string;
14
+ /**
15
+ * 原子写:先写临时文件,再 rename。确保并发下不会读到半截内容。
16
+ */
17
+ export declare function writeAtomic(filePath: string, raw: string): void;
18
+ export declare function writeFrontmatterAtomic(filePath: string, data: Record<string, unknown>, content: string): void;
19
+ //# sourceMappingURL=frontmatter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../../src/core/frontmatter.ts"],"names":[],"mappings":"AAKA;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAGpG;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEvF;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAM/D;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAE7G"}
@@ -0,0 +1,57 @@
1
+ import matter from 'gray-matter';
2
+ import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
3
+ import { dirname, join } from 'node:path';
4
+ import { randomBytes } from 'node:crypto';
5
+ /**
6
+ * 读取文件,返回 { data, content }。
7
+ * data 是 YAML frontmatter,content 是正文(已剥离 frontmatter 标记)。
8
+ */
9
+ export function readFrontmatter(filePath) {
10
+ const raw = readFileSync(filePath, 'utf8');
11
+ return matter(raw);
12
+ }
13
+ /**
14
+ * 序列化:data + content → 带 frontmatter 的 markdown 字符串。
15
+ * gray-matter 不接受 undefined 值;递归剔除后再 dump。
16
+ */
17
+ export function writeFrontmatter(data, content) {
18
+ return matter.stringify(content, stripUndefined(data));
19
+ }
20
+ /**
21
+ * 原子写:先写临时文件,再 rename。确保并发下不会读到半截内容。
22
+ */
23
+ export function writeAtomic(filePath, raw) {
24
+ const dir = dirname(filePath);
25
+ mkdirSync(dir, { recursive: true });
26
+ const tmp = join(dir, `.${randomBytes(6).toString('hex')}.tmp`);
27
+ writeFileSync(tmp, raw, 'utf8');
28
+ renameSync(tmp, filePath);
29
+ }
30
+ export function writeFrontmatterAtomic(filePath, data, content) {
31
+ writeAtomic(filePath, writeFrontmatter(data, content));
32
+ }
33
+ /** 序列化时跳过的字段(已废弃或由外部系统注入) */
34
+ const FM_SKIP_KEYS = new Set(['project']);
35
+ function stripUndefined(v) {
36
+ if (Array.isArray(v))
37
+ return v.map(stripUndefined);
38
+ if (v && typeof v === 'object') {
39
+ const out = {};
40
+ for (const [k, val] of Object.entries(v)) {
41
+ if (val === undefined)
42
+ continue;
43
+ if (FM_SKIP_KEYS.has(k))
44
+ continue;
45
+ const cleaned = stripUndefined(val);
46
+ // 跳过空数组和空字符串
47
+ if (Array.isArray(cleaned) && cleaned.length === 0)
48
+ continue;
49
+ if (cleaned === '')
50
+ continue;
51
+ out[k] = cleaned;
52
+ }
53
+ return out;
54
+ }
55
+ return v;
56
+ }
57
+ //# sourceMappingURL=frontmatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontmatter.js","sourceRoot":"","sources":["../../src/core/frontmatter.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC3C,OAAO,MAAM,CAAC,GAAG,CAA8B,CAAC;AAClD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAA6B,EAAE,OAAe;IAC7E,OAAO,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,cAAc,CAAC,IAAI,CAA4B,CAAC,CAAC;AACpF,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,QAAgB,EAAE,GAAW;IACvD,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC9B,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAChE,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;IAChC,UAAU,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,QAAgB,EAAE,IAA6B,EAAE,OAAe;IACrG,WAAW,CAAC,QAAQ,EAAE,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;AACzD,CAAC;AAED,6BAA6B;AAC7B,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;AAE1C,SAAS,cAAc,CAAC,CAAU;IAChC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IACnD,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC/B,MAAM,GAAG,GAA4B,EAAE,CAAC;QACxC,KAAK,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAA4B,CAAC,EAAE,CAAC;YACpE,IAAI,GAAG,KAAK,SAAS;gBAAE,SAAS;YAChC,IAAI,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;gBAAE,SAAS;YAClC,MAAM,OAAO,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;YACpC,aAAa;YACb,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YAC7D,IAAI,OAAO,KAAK,EAAE;gBAAE,SAAS;YAC7B,GAAG,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC;QACnB,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Incident 记录
3
+ * 存储位置:.spec-manager/incidents/INC-YYYYMMDD-NNN.md
4
+ * frontmatter:id, ruleId, severity, status, created, specCode?, taskCode?
5
+ * 正文:触发场景、影响、临时修复、永久方案、关联 spec/task
6
+ */
7
+ import type { ProjectPaths } from './paths.js';
8
+ export type Severity = 'low' | 'medium' | 'high' | 'critical';
9
+ export type IncidentStatus = 'open' | 'mitigated' | 'resolved' | 'closed';
10
+ export interface IncidentRecord {
11
+ id: string;
12
+ fm: {
13
+ id: string;
14
+ ruleId: string;
15
+ severity: Severity;
16
+ status: IncidentStatus;
17
+ title: string;
18
+ specCode?: string;
19
+ taskCode?: string;
20
+ relatedDecisions?: string[];
21
+ created: string;
22
+ updated: string;
23
+ };
24
+ content: string;
25
+ filePath: string;
26
+ }
27
+ export declare function listIncidents(paths: ProjectPaths, opts?: {
28
+ status?: IncidentStatus;
29
+ relatedDecision?: string;
30
+ }): IncidentRecord[];
31
+ export declare function findIncident(paths: ProjectPaths, id: string): IncidentRecord | null;
32
+ export declare function nextIncidentId(paths: ProjectPaths): string;
33
+ export interface CreateIncidentInput {
34
+ paths: ProjectPaths;
35
+ ruleId: string;
36
+ severity: Severity;
37
+ title: string;
38
+ description?: string;
39
+ specCode?: string;
40
+ taskCode?: string;
41
+ relatedDecisions?: string[];
42
+ }
43
+ export declare function createIncident(input: CreateIncidentInput): IncidentRecord;
44
+ export declare function updateIncidentStatus(paths: ProjectPaths, id: string, status: IncidentStatus, note?: string): IncidentRecord;
45
+ //# sourceMappingURL=incident.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"incident.d.ts","sourceRoot":"","sources":["../../src/core/incident.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;AAC9D,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,WAAW,GAAG,UAAU,GAAG,QAAQ,CAAC;AAE1E,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE;QACF,EAAE,EAAE,MAAM,CAAC;QACX,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,QAAQ,CAAC;QACnB,MAAM,EAAE,cAAc,CAAC;QACvB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;QAC5B,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAKD,wBAAgB,aAAa,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,cAAc,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,cAAc,EAAE,CAajI;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAKnF;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAa1D;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,YAAY,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,cAAc,CA8BzE;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,cAAc,CAa3H"}