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,128 @@
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 { existsSync, readdirSync } from 'node:fs';
8
+ import { join } from 'node:path';
9
+ import { readFrontmatter, writeFrontmatterAtomic } from './frontmatter.js';
10
+ import { RULE_ID_RE } from './audit.js';
11
+ import { ID_PAD_WIDTH } from './constants.js';
12
+ const VALID_SEVERITY = ['low', 'medium', 'high', 'critical'];
13
+ const VALID_STATUS = ['open', 'mitigated', 'resolved', 'closed'];
14
+ export function listIncidents(paths, opts) {
15
+ if (!existsSync(paths.incidentsDir))
16
+ return [];
17
+ const out = [];
18
+ for (const f of readdirSync(paths.incidentsDir)) {
19
+ if (!f.endsWith('.md') || f.startsWith('.'))
20
+ continue;
21
+ const filePath = join(paths.incidentsDir, f);
22
+ const { data, content } = readFrontmatter(filePath);
23
+ const fm = data;
24
+ if (opts?.status && fm.status !== opts.status)
25
+ continue;
26
+ if (opts?.relatedDecision && !(fm.relatedDecisions ?? []).includes(opts.relatedDecision))
27
+ continue;
28
+ out.push({ id: fm.id, fm, content, filePath });
29
+ }
30
+ return out.sort((a, b) => b.fm.created.localeCompare(a.fm.created));
31
+ }
32
+ export function findIncident(paths, id) {
33
+ for (const i of listIncidents(paths)) {
34
+ if (i.id === id || i.fm.id === id)
35
+ return i;
36
+ }
37
+ return null;
38
+ }
39
+ export function nextIncidentId(paths) {
40
+ if (!existsSync(paths.incidentsDir)) {
41
+ const today = new Date().toISOString().slice(0, 10).replace(/-/g, '');
42
+ return `INC-${today}-001`;
43
+ }
44
+ const today = new Date().toISOString().slice(0, 10).replace(/-/g, '');
45
+ const prefix = `INC-${today}-`;
46
+ const existing = readdirSync(paths.incidentsDir)
47
+ .map(f => f.match(/^INC-\d{8}-(\d+)\.md$/))
48
+ .filter((m) => m !== null)
49
+ .map(m => Number(m[1]));
50
+ const max = existing.length > 0 ? Math.max(...existing) : 0;
51
+ return `${prefix}${String(max + 1).padStart(ID_PAD_WIDTH, '0')}`;
52
+ }
53
+ export function createIncident(input) {
54
+ if (!RULE_ID_RE.test(input.ruleId)) {
55
+ throw new Error(`ruleId 格式非法: ${input.ruleId}`);
56
+ }
57
+ if (!VALID_SEVERITY.includes(input.severity)) {
58
+ throw new Error(`severity 非法: ${input.severity}(必须 ${VALID_SEVERITY.join('|')})`);
59
+ }
60
+ if (!input.title.trim()) {
61
+ throw new Error('title 必填');
62
+ }
63
+ const id = nextIncidentId(input.paths);
64
+ const now = new Date().toISOString();
65
+ const fm = {
66
+ id,
67
+ ruleId: input.ruleId,
68
+ severity: input.severity,
69
+ status: 'open',
70
+ title: input.title,
71
+ specCode: input.specCode,
72
+ taskCode: input.taskCode,
73
+ relatedDecisions: input.relatedDecisions && input.relatedDecisions.length > 0
74
+ ? input.relatedDecisions
75
+ : undefined,
76
+ created: now,
77
+ updated: now,
78
+ };
79
+ const content = renderContent(fm, input.description ?? '');
80
+ const filePath = join(input.paths.incidentsDir, `${id}.md`);
81
+ writeFrontmatterAtomic(filePath, fm, content);
82
+ return { id, fm, content, filePath };
83
+ }
84
+ export function updateIncidentStatus(paths, id, status, note) {
85
+ const inc = findIncident(paths, id);
86
+ if (!inc)
87
+ throw new Error(`Incident not found: ${id}`);
88
+ if (!VALID_STATUS.includes(status)) {
89
+ throw new Error(`status 非法: ${status}(必须 ${VALID_STATUS.join('|')})`);
90
+ }
91
+ const updated = {
92
+ ...inc,
93
+ fm: { ...inc.fm, status, updated: new Date().toISOString() },
94
+ content: inc.content + (note ? `\n\n## ${status} (${new Date().toISOString()})\n${note}\n` : ''),
95
+ };
96
+ writeFrontmatterAtomic(inc.filePath, updated.fm, updated.content);
97
+ return updated;
98
+ }
99
+ function renderContent(fm, description) {
100
+ const lines = [];
101
+ lines.push(`# ${fm.id} — ${fm.title}`);
102
+ lines.push('');
103
+ lines.push(`> 规则: **${fm.ruleId}** | 严重度: **${fm.severity}** | 状态: **${fm.status}**${fm.specCode ? ` | spec: ${fm.specCode}` : ''}${fm.taskCode ? ` | task: ${fm.taskCode}` : ''}`);
104
+ lines.push('');
105
+ lines.push(`> 创建: ${fm.created}`);
106
+ lines.push('');
107
+ lines.push('## 触发场景');
108
+ lines.push(description || '(请补充)');
109
+ lines.push('');
110
+ lines.push('## 影响');
111
+ lines.push('(请补充:影响范围 + 用户痛点)');
112
+ lines.push('');
113
+ lines.push('## 临时修复');
114
+ lines.push('(请补充:紧急止血)');
115
+ lines.push('');
116
+ lines.push('## 永久方案');
117
+ lines.push('(请补充:根因 + 长期修复)');
118
+ lines.push('');
119
+ lines.push('## 关联');
120
+ lines.push('- spec: ' + (fm.specCode ?? '-'));
121
+ lines.push('- task: ' + (fm.taskCode ?? '-'));
122
+ lines.push('- 规则: ' + fm.ruleId);
123
+ if (fm.relatedDecisions && fm.relatedDecisions.length > 0) {
124
+ lines.push('- 决策: ' + fm.relatedDecisions.join(', '));
125
+ }
126
+ return lines.join('\n');
127
+ }
128
+ //# sourceMappingURL=incident.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"incident.js","sourceRoot":"","sources":["../../src/core/incident.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * 解析项目根目录。
3
+ * 策略:优先用 SPEC_MANAGER_ROOT 环境变量,否则从 cwd 向上找 .spec-manager/,
4
+ * 找到为止;找不到就用 cwd(让首次 init 有合理默认值)。
5
+ */
6
+ export declare function resolveProjectRoot(cwd?: string): string;
7
+ export interface ProjectPaths {
8
+ root: string;
9
+ configDir: string;
10
+ configFile: string;
11
+ auditFile: string;
12
+ incidentsDir: string;
13
+ dictFile: string;
14
+ specsDir: string;
15
+ changesDir: string;
16
+ archiveDir: string;
17
+ isInitialized: boolean;
18
+ }
19
+ export declare function getPaths(root?: string): ProjectPaths;
20
+ /**
21
+ * 平铺布局下的 spec 文件路径。
22
+ *
23
+ * 布局:specs/<topic>/<code>.md
24
+ * 点分编号已编码层级关系,无需嵌套目录。
25
+ *
26
+ * @param parentFilePath 保留参数(向后兼容),平铺布局下忽略
27
+ * @param code spec code(如 auth-L1、auth-L2.1、auth-L3.1.1)
28
+ * @param topic topic 名
29
+ * @param date 保留参数(向后兼容),canonical 路径下忽略
30
+ */
31
+ export declare function specFilePath(paths: ProjectPaths, _parentFilePath: string | null, code: string, topic?: string, _date?: string): string;
32
+ export declare function assertSafeTopic(topic: string): void;
33
+ export declare function assertSafeSpecCode(code: string): void;
34
+ /**
35
+ * 平铺布局下,元数据目录在 topic 级别:
36
+ * specs/<topic>/decisions/、specs/<topic>/tasks/
37
+ */
38
+ export declare function siblingMetaDir(specFilePath: string, name: 'decisions' | 'tasks'): string;
39
+ export interface SpecFileEntry {
40
+ topic: string;
41
+ code: string;
42
+ filePath: string;
43
+ }
44
+ export interface ParsedSpecFilename {
45
+ code: string;
46
+ format: 'canonical' | 'legacy-date';
47
+ legacyDate?: string;
48
+ }
49
+ export interface SpecPathMigration {
50
+ topic: string;
51
+ code: string;
52
+ from: string;
53
+ to: string;
54
+ legacyDate: string;
55
+ }
56
+ /**
57
+ * 平铺布局:直接扫描 specs/<topic>/ 下的 .md 文件。
58
+ * 跳过元数据目录(decisions/ tasks/ 等)。
59
+ */
60
+ export declare function listSpecFiles(paths: ProjectPaths): SpecFileEntry[];
61
+ export declare function listSpecPathMigrations(paths: ProjectPaths): SpecPathMigration[];
62
+ /**
63
+ * 从文件名提取 code。
64
+ * canonical: <code>.md
65
+ * legacy: <code>-<YYYYMMDD>.md
66
+ */
67
+ export declare function parseSpecFilename(filename: string): ParsedSpecFilename | null;
68
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/core/paths.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,GAAE,MAAsB,GAAG,MAAM,CAYtE;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,wBAAgB,QAAQ,CAAC,IAAI,GAAE,MAA6B,GAAG,YAAY,CAe1E;AAUD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,YAAY,EACnB,eAAe,EAAE,MAAM,GAAG,IAAI,EAC9B,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,MAAM,GACb,MAAM,CAKR;AASD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAInD;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAIrD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,GAAG,MAAM,CAExF;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,GAAG,aAAa,CAAC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,YAAY,GAAG,aAAa,EAAE,CAqBlE;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,YAAY,GAAG,iBAAiB,EAAE,CAoB/E;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CAU7E"}
@@ -0,0 +1,162 @@
1
+ import { existsSync, readdirSync, statSync } from 'node:fs';
2
+ import { dirname, join, resolve } from 'node:path';
3
+ import { SPEC_CODE_RE, TOPIC_RE } from './constants.js';
4
+ /**
5
+ * 解析项目根目录。
6
+ * 策略:优先用 SPEC_MANAGER_ROOT 环境变量,否则从 cwd 向上找 .spec-manager/,
7
+ * 找到为止;找不到就用 cwd(让首次 init 有合理默认值)。
8
+ */
9
+ export function resolveProjectRoot(cwd = process.cwd()) {
10
+ const envRoot = process.env.SPEC_MANAGER_ROOT;
11
+ if (envRoot)
12
+ return resolve(envRoot);
13
+ let dir = resolve(cwd);
14
+ for (let i = 0; i < 8; i++) {
15
+ if (existsSync(join(dir, '.spec-manager')))
16
+ return dir;
17
+ const parent = dirname(dir);
18
+ if (parent === dir)
19
+ break;
20
+ dir = parent;
21
+ }
22
+ return resolve(cwd);
23
+ }
24
+ export function getPaths(root = resolveProjectRoot()) {
25
+ const configDir = join(root, '.spec-manager');
26
+ const isInitialized = statSafe(configDir)?.isDirectory() ?? false;
27
+ return {
28
+ root,
29
+ configDir,
30
+ configFile: join(configDir, 'config.yaml'),
31
+ auditFile: join(configDir, 'audit.json'),
32
+ incidentsDir: join(configDir, 'incidents'),
33
+ dictFile: join(configDir, 'dict.yaml'),
34
+ specsDir: join(root, 'specs'),
35
+ changesDir: join(root, 'changes'),
36
+ archiveDir: join(root, 'archive'),
37
+ isInitialized,
38
+ };
39
+ }
40
+ function statSafe(p) {
41
+ try {
42
+ return statSync(p);
43
+ }
44
+ catch {
45
+ return null;
46
+ }
47
+ }
48
+ /**
49
+ * 平铺布局下的 spec 文件路径。
50
+ *
51
+ * 布局:specs/<topic>/<code>.md
52
+ * 点分编号已编码层级关系,无需嵌套目录。
53
+ *
54
+ * @param parentFilePath 保留参数(向后兼容),平铺布局下忽略
55
+ * @param code spec code(如 auth-L1、auth-L2.1、auth-L3.1.1)
56
+ * @param topic topic 名
57
+ * @param date 保留参数(向后兼容),canonical 路径下忽略
58
+ */
59
+ export function specFilePath(paths, _parentFilePath, code, topic, _date) {
60
+ assertSafeSpecCode(code);
61
+ const t = topic ?? extractTopicFromCode(code);
62
+ assertSafeTopic(t);
63
+ return join(paths.specsDir, t, `${code}.md`);
64
+ }
65
+ function extractTopicFromCode(code) {
66
+ // spec-manager-ai-ux-L1 → spec-manager-ai-ux
67
+ const match = code.match(/^(.+)-L\d/);
68
+ if (match)
69
+ return match[1];
70
+ throw new Error(`specFilePath: 无法从 code 推断 topic,请传 topic 参数`);
71
+ }
72
+ export function assertSafeTopic(topic) {
73
+ if (!TOPIC_RE.test(topic)) {
74
+ throw new Error(`topic 非法: ${topic}(必须匹配 ${TOPIC_RE.source},且不能包含路径分隔符)`);
75
+ }
76
+ }
77
+ export function assertSafeSpecCode(code) {
78
+ if (!SPEC_CODE_RE.test(code)) {
79
+ throw new Error(`spec code 非法: ${code}(必须匹配 ${SPEC_CODE_RE.source},且不能包含路径分隔符)`);
80
+ }
81
+ }
82
+ /**
83
+ * 平铺布局下,元数据目录在 topic 级别:
84
+ * specs/<topic>/decisions/、specs/<topic>/tasks/
85
+ */
86
+ export function siblingMetaDir(specFilePath, name) {
87
+ return join(dirname(specFilePath), name);
88
+ }
89
+ /**
90
+ * 平铺布局:直接扫描 specs/<topic>/ 下的 .md 文件。
91
+ * 跳过元数据目录(decisions/ tasks/ 等)。
92
+ */
93
+ export function listSpecFiles(paths) {
94
+ const out = [];
95
+ if (!existsSync(paths.specsDir))
96
+ return out;
97
+ for (const topicEntry of readdirSync(paths.specsDir, { withFileTypes: true })) {
98
+ if (!topicEntry.isDirectory() || topicEntry.name.startsWith('.'))
99
+ continue;
100
+ const topicDir = join(paths.specsDir, topicEntry.name);
101
+ for (const file of readdirSync(topicDir, { withFileTypes: true })) {
102
+ if (file.isDirectory())
103
+ continue;
104
+ if (!file.name.endsWith('.md'))
105
+ continue;
106
+ if (file.name.startsWith('.'))
107
+ continue;
108
+ const parsed = parseSpecFilename(file.name);
109
+ if (!parsed)
110
+ continue;
111
+ const filePath = join(topicDir, file.name);
112
+ const dup = out.find(e => e.topic === topicEntry.name && e.code === parsed.code);
113
+ if (dup) {
114
+ throw new Error(`重复 spec code: ${parsed.code} (${dup.filePath}, ${filePath})`);
115
+ }
116
+ out.push({ topic: topicEntry.name, code: parsed.code, filePath });
117
+ }
118
+ }
119
+ return out.sort((a, b) => a.code.localeCompare(b.code));
120
+ }
121
+ export function listSpecPathMigrations(paths) {
122
+ const out = [];
123
+ if (!existsSync(paths.specsDir))
124
+ return out;
125
+ for (const topicEntry of readdirSync(paths.specsDir, { withFileTypes: true })) {
126
+ if (!topicEntry.isDirectory() || topicEntry.name.startsWith('.'))
127
+ continue;
128
+ const topicDir = join(paths.specsDir, topicEntry.name);
129
+ for (const file of readdirSync(topicDir, { withFileTypes: true })) {
130
+ if (file.isDirectory() || file.name.startsWith('.') || !file.name.endsWith('.md'))
131
+ continue;
132
+ const parsed = parseSpecFilename(file.name);
133
+ if (!parsed || parsed.format !== 'legacy-date' || !parsed.legacyDate)
134
+ continue;
135
+ out.push({
136
+ topic: topicEntry.name,
137
+ code: parsed.code,
138
+ from: join(topicDir, file.name),
139
+ to: join(topicDir, `${parsed.code}.md`),
140
+ legacyDate: parsed.legacyDate,
141
+ });
142
+ }
143
+ }
144
+ return out.sort((a, b) => a.code.localeCompare(b.code));
145
+ }
146
+ /**
147
+ * 从文件名提取 code。
148
+ * canonical: <code>.md
149
+ * legacy: <code>-<YYYYMMDD>.md
150
+ */
151
+ export function parseSpecFilename(filename) {
152
+ const legacy = filename.match(/^(.+)-(\d{8})\.md$/);
153
+ if (legacy && SPEC_CODE_RE.test(legacy[1])) {
154
+ return { code: legacy[1], format: 'legacy-date', legacyDate: legacy[2] };
155
+ }
156
+ const old = filename.match(/^(.+)\.md$/);
157
+ if (old && SPEC_CODE_RE.test(old[1])) {
158
+ return { code: old[1], format: 'canonical' };
159
+ }
160
+ return null;
161
+ }
162
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/core/paths.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { ProjectPaths } from './paths.js';
2
+ export type TopicMetaDir = 'tasks' | 'decisions';
3
+ export interface TopicMetaFile {
4
+ topic: string;
5
+ fileName: string;
6
+ filePath: string;
7
+ }
8
+ export declare function listTopicMetaFiles(paths: ProjectPaths, dirName: TopicMetaDir, opts?: {
9
+ topic?: string;
10
+ extension?: string;
11
+ filePrefix?: string;
12
+ }): TopicMetaFile[];
13
+ //# sourceMappingURL=repository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../../src/core/repository.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,WAAW,CAAC;AAEjD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,YAAY,EACrB,IAAI,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GACjE,aAAa,EAAE,CAuBjB"}
@@ -0,0 +1,29 @@
1
+ import { existsSync, readdirSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ export function listTopicMetaFiles(paths, dirName, opts) {
4
+ const out = [];
5
+ if (!existsSync(paths.specsDir))
6
+ return out;
7
+ for (const topicEntry of readdirSync(paths.specsDir, { withFileTypes: true })) {
8
+ if (!topicEntry.isDirectory() || topicEntry.name.startsWith('.'))
9
+ continue;
10
+ if (opts?.topic && topicEntry.name !== opts.topic)
11
+ continue;
12
+ const metaDir = join(paths.specsDir, topicEntry.name, dirName);
13
+ if (!existsSync(metaDir))
14
+ continue;
15
+ for (const fileName of readdirSync(metaDir)) {
16
+ if (opts?.extension && !fileName.endsWith(opts.extension))
17
+ continue;
18
+ if (opts?.filePrefix && !fileName.startsWith(opts.filePrefix))
19
+ continue;
20
+ out.push({
21
+ topic: topicEntry.name,
22
+ fileName,
23
+ filePath: join(metaDir, fileName),
24
+ });
25
+ }
26
+ }
27
+ return out.sort((a, b) => a.filePath.localeCompare(b.filePath));
28
+ }
29
+ //# sourceMappingURL=repository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository.js","sourceRoot":"","sources":["../../src/core/repository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAWjC,MAAM,UAAU,kBAAkB,CAChC,KAAmB,EACnB,OAAqB,EACrB,IAAkE;IAElE,MAAM,GAAG,GAAoB,EAAE,CAAC;IAChC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC;QAAE,OAAO,GAAG,CAAC;IAE5C,KAAK,MAAM,UAAU,IAAI,WAAW,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QAC9E,IAAI,CAAC,UAAU,CAAC,WAAW,EAAE,IAAI,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QAC3E,IAAI,IAAI,EAAE,KAAK,IAAI,UAAU,CAAC,IAAI,KAAK,IAAI,CAAC,KAAK;YAAE,SAAS;QAE5D,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC/D,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;YAAE,SAAS;QAEnC,KAAK,MAAM,QAAQ,IAAI,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;YAC5C,IAAI,IAAI,EAAE,SAAS,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC;gBAAE,SAAS;YACpE,IAAI,IAAI,EAAE,UAAU,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC;gBAAE,SAAS;YACxE,GAAG,CAAC,IAAI,CAAC;gBACP,KAAK,EAAE,UAAU,CAAC,IAAI;gBACtB,QAAQ;gBACR,QAAQ,EAAE,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC;aAClC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;AAClE,CAAC"}
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Spec 文件的原子读写。
3
+ *
4
+ * 写策略:写临时文件 → rename,保证并发下不会读到半截内容。
5
+ * 读策略:先解析 frontmatter,找不到时返回 null(由调用方决定 fallback)。
6
+ */
7
+ import type { SpecLevel } from './validate.js';
8
+ import { type ProjectPaths } from './paths.js';
9
+ import { type AuditSink } from './audit-events.js';
10
+ export interface SpecFrontmatter {
11
+ id?: string;
12
+ code: string;
13
+ level: SpecLevel;
14
+ title: string;
15
+ topic: string;
16
+ parentCode: string | null;
17
+ status: 'draft' | 'confirmed' | 'frozen' | 'implemented' | 'archived';
18
+ milestone?: string;
19
+ aiSummary?: string;
20
+ coveredTasks?: string[];
21
+ steps?: StepFrontmatter[];
22
+ relations?: Array<{
23
+ type: string;
24
+ target: string;
25
+ }>;
26
+ created?: string;
27
+ updated?: string;
28
+ changeSummary?: string;
29
+ }
30
+ export interface StepFrontmatter {
31
+ stepNo: number | string;
32
+ stepType: 'llm_call' | 'mcp_tool' | 'human_gate';
33
+ name: string;
34
+ status: 'pending' | 'running' | 'succeeded' | 'failed' | 'skipped';
35
+ toolName?: string;
36
+ inputJson?: string;
37
+ outputJson?: string;
38
+ errorCode?: string;
39
+ errorMessage?: string;
40
+ latencyMs?: number;
41
+ reportedAt?: string;
42
+ }
43
+ export interface SpecRecord {
44
+ fm: SpecFrontmatter;
45
+ content: string;
46
+ filePath: string;
47
+ }
48
+ export declare const DESC_MAX_LEN = 15;
49
+ /**
50
+ * 生成 spec code: 点分编号,层级自文档化。
51
+ * - L1: <topic>-L1
52
+ * - L2: <topic>-L2.1(第 1 个 L2 子 spec)
53
+ * - L3: <topic>-L3.1.1[-desc](可选描述后缀,≤15 字符)
54
+ *
55
+ * parentCode 为空时生成 <topic>-<level>;
56
+ * 非空时生成 <topic>-<level>.<N>[.M...][-desc]。
57
+ */
58
+ export declare function generateSpecCode(topic: string, level: SpecLevel, parentCode?: string, siblingCount?: number, desc?: string): string;
59
+ export declare function readSpec(filePath: string): SpecRecord | null;
60
+ export declare function findSpecByCode(paths: ProjectPaths, code: string): SpecRecord | null;
61
+ /**
62
+ * 带 mtime 失效的 listAllSpecs。文件未变时复用内存里的解析结果,
63
+ * 避免每次 decision/task/archive/delta 全扫都重新 parse frontmatter。
64
+ *
65
+ * 注意:单进程内有效;多进程或显式调 invalidateSpecCache() 失效。
66
+ */
67
+ export declare function listAllSpecs(paths: ProjectPaths): SpecRecord[];
68
+ export declare function invalidateSpecCache(filePath?: string): void;
69
+ /**
70
+ * 原子写:先写临时文件,再 rename。
71
+ */
72
+ export declare function writeSpec(record: SpecRecord): void;
73
+ export declare function createSpec(args: {
74
+ paths: ProjectPaths;
75
+ code: string;
76
+ level: SpecLevel;
77
+ title: string;
78
+ topic: string;
79
+ parentCode: string | null;
80
+ parentRecord?: SpecRecord | null;
81
+ milestone?: string;
82
+ auditSink?: AuditSink;
83
+ }): SpecRecord;
84
+ /**
85
+ * 更新 spec:传 partial 覆盖 frontmatter,更新 content,更新 updated 时间戳。
86
+ * 自动截断 aiSummary 到 300 字符(带 warning)。
87
+ */
88
+ export interface UpdateResult {
89
+ record: SpecRecord;
90
+ warnings: string[];
91
+ }
92
+ export declare function updateSpec(paths: ProjectPaths, code: string, patch: {
93
+ content?: string;
94
+ aiSummary?: string;
95
+ changeSummary?: string;
96
+ status?: SpecFrontmatter['status'];
97
+ appendStep?: StepFrontmatter;
98
+ replaceStep?: {
99
+ no: number | string;
100
+ step: StepFrontmatter;
101
+ };
102
+ addRelation?: {
103
+ type: string;
104
+ target: string;
105
+ };
106
+ }, opts?: {
107
+ auditSink?: AuditSink;
108
+ }): UpdateResult;
109
+ /**
110
+ * R22: contentTemplate 是不是只剩 createSpec 写出的占位?
111
+ * 占位 = 文件里有 marker 行,且去掉 marker 后正文长度 < PLACEHOLDER_CONTENT_MAX。
112
+ */
113
+ export declare function isPlaceholderContent(content: string): boolean;
114
+ export interface SpecPathMigrationResult {
115
+ dryRun: boolean;
116
+ migrated: Array<{
117
+ code: string;
118
+ from: string;
119
+ to: string;
120
+ }>;
121
+ }
122
+ export declare function migrateSpecPaths(paths: ProjectPaths, opts?: {
123
+ dryRun?: boolean;
124
+ }): SpecPathMigrationResult;
125
+ //# sourceMappingURL=spec-io.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec-io.d.ts","sourceRoot":"","sources":["../../src/core/spec-io.ts"],"names":[],"mappings":"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"}