@eddacraft/anvil-adapters 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 (183) hide show
  1. package/AGENTS.md +180 -0
  2. package/BMAD_ADAPTER_SPEC.md +489 -0
  3. package/LICENSE +14 -0
  4. package/README.md +500 -0
  5. package/dist/aps-markdown/adapter.d.ts +102 -0
  6. package/dist/aps-markdown/adapter.d.ts.map +1 -0
  7. package/dist/aps-markdown/adapter.js +351 -0
  8. package/dist/aps-markdown/index.d.ts +8 -0
  9. package/dist/aps-markdown/index.d.ts.map +1 -0
  10. package/dist/aps-markdown/index.js +7 -0
  11. package/dist/base/file-discovery.d.ts +63 -0
  12. package/dist/base/file-discovery.d.ts.map +1 -0
  13. package/dist/base/file-discovery.js +246 -0
  14. package/dist/base/index.d.ts +10 -0
  15. package/dist/base/index.d.ts.map +1 -0
  16. package/dist/base/index.js +9 -0
  17. package/dist/base/registry.d.ts +155 -0
  18. package/dist/base/registry.d.ts.map +1 -0
  19. package/dist/base/registry.js +227 -0
  20. package/dist/base/testing.d.ts +102 -0
  21. package/dist/base/testing.d.ts.map +1 -0
  22. package/dist/base/testing.js +221 -0
  23. package/dist/base/types.d.ts +255 -0
  24. package/dist/base/types.d.ts.map +1 -0
  25. package/dist/base/types.js +78 -0
  26. package/dist/base/utils.d.ts +127 -0
  27. package/dist/base/utils.d.ts.map +1 -0
  28. package/dist/base/utils.js +254 -0
  29. package/dist/bmad/format-adapter.d.ts +76 -0
  30. package/dist/bmad/format-adapter.d.ts.map +1 -0
  31. package/dist/bmad/format-adapter.js +186 -0
  32. package/dist/bmad/index.d.ts +12 -0
  33. package/dist/bmad/index.d.ts.map +1 -0
  34. package/dist/bmad/index.js +10 -0
  35. package/dist/bmad/parser.d.ts +12 -0
  36. package/dist/bmad/parser.d.ts.map +1 -0
  37. package/dist/bmad/parser.js +181 -0
  38. package/dist/bmad/serializer.d.ts +16 -0
  39. package/dist/bmad/serializer.d.ts.map +1 -0
  40. package/dist/bmad/serializer.js +170 -0
  41. package/dist/bmad/types.d.ts +127 -0
  42. package/dist/bmad/types.d.ts.map +1 -0
  43. package/dist/bmad/types.js +47 -0
  44. package/dist/bmad/utils.d.ts +120 -0
  45. package/dist/bmad/utils.d.ts.map +1 -0
  46. package/dist/bmad/utils.js +480 -0
  47. package/dist/common/index.d.ts +3 -0
  48. package/dist/common/index.d.ts.map +1 -0
  49. package/dist/common/index.js +2 -0
  50. package/dist/common/registry.d.ts +18 -0
  51. package/dist/common/registry.d.ts.map +1 -0
  52. package/dist/common/registry.js +58 -0
  53. package/dist/common/types.d.ts +68 -0
  54. package/dist/common/types.d.ts.map +1 -0
  55. package/dist/common/types.js +12 -0
  56. package/dist/generic/format-adapter.d.ts +64 -0
  57. package/dist/generic/format-adapter.d.ts.map +1 -0
  58. package/dist/generic/format-adapter.js +159 -0
  59. package/dist/generic/index.d.ts +10 -0
  60. package/dist/generic/index.d.ts.map +1 -0
  61. package/dist/generic/index.js +9 -0
  62. package/dist/generic/parser.d.ts +11 -0
  63. package/dist/generic/parser.d.ts.map +1 -0
  64. package/dist/generic/parser.js +106 -0
  65. package/dist/generic/serializer.d.ts +11 -0
  66. package/dist/generic/serializer.d.ts.map +1 -0
  67. package/dist/generic/serializer.js +118 -0
  68. package/dist/generic/types.d.ts +52 -0
  69. package/dist/generic/types.d.ts.map +1 -0
  70. package/dist/generic/types.js +6 -0
  71. package/dist/generic/utils.d.ts +51 -0
  72. package/dist/generic/utils.d.ts.map +1 -0
  73. package/dist/generic/utils.js +232 -0
  74. package/dist/index.d.ts +15 -0
  75. package/dist/index.d.ts.map +1 -0
  76. package/dist/index.js +31 -0
  77. package/dist/speckit/export.d.ts +22 -0
  78. package/dist/speckit/export.d.ts.map +1 -0
  79. package/dist/speckit/export.js +384 -0
  80. package/dist/speckit/format-adapter.d.ts +104 -0
  81. package/dist/speckit/format-adapter.d.ts.map +1 -0
  82. package/dist/speckit/format-adapter.js +488 -0
  83. package/dist/speckit/import-v2.d.ts +33 -0
  84. package/dist/speckit/import-v2.d.ts.map +1 -0
  85. package/dist/speckit/import-v2.js +361 -0
  86. package/dist/speckit/import.d.ts +16 -0
  87. package/dist/speckit/import.d.ts.map +1 -0
  88. package/dist/speckit/import.js +247 -0
  89. package/dist/speckit/index.d.ts +5 -0
  90. package/dist/speckit/index.d.ts.map +1 -0
  91. package/dist/speckit/index.js +4 -0
  92. package/dist/speckit/parser.d.ts +28 -0
  93. package/dist/speckit/parser.d.ts.map +1 -0
  94. package/dist/speckit/parser.js +283 -0
  95. package/dist/speckit/parsers/plan-parser.d.ts +71 -0
  96. package/dist/speckit/parsers/plan-parser.d.ts.map +1 -0
  97. package/dist/speckit/parsers/plan-parser.js +216 -0
  98. package/dist/speckit/parsers/spec-parser.d.ts +67 -0
  99. package/dist/speckit/parsers/spec-parser.d.ts.map +1 -0
  100. package/dist/speckit/parsers/spec-parser.js +255 -0
  101. package/dist/speckit/parsers/tasks-parser.d.ts +57 -0
  102. package/dist/speckit/parsers/tasks-parser.d.ts.map +1 -0
  103. package/dist/speckit/parsers/tasks-parser.js +157 -0
  104. package/package.json +23 -0
  105. package/project.json +29 -0
  106. package/src/__tests__/adapter-edge-cases.test.ts +937 -0
  107. package/src/__tests__/bmad-format-adapter.test.ts +1470 -0
  108. package/src/__tests__/fixtures/aps/expected-output.json +83 -0
  109. package/src/__tests__/fixtures/bmad/invalid-malformed-yaml.md +16 -0
  110. package/src/__tests__/fixtures/bmad/invalid-no-requirements.md +23 -0
  111. package/src/__tests__/fixtures/bmad/invalid-only-yaml.md +16 -0
  112. package/src/__tests__/fixtures/bmad/invalid-too-short.md +3 -0
  113. package/src/__tests__/fixtures/bmad/invalid-wrong-format.md +40 -0
  114. package/src/__tests__/fixtures/bmad/valid-agent.md +27 -0
  115. package/src/__tests__/fixtures/bmad/valid-architecture.md +116 -0
  116. package/src/__tests__/fixtures/bmad/valid-complex-prd.md +161 -0
  117. package/src/__tests__/fixtures/bmad/valid-epic.md +73 -0
  118. package/src/__tests__/fixtures/bmad/valid-minimal-prd.md +19 -0
  119. package/src/__tests__/fixtures/bmad/valid-prd.md +107 -0
  120. package/src/__tests__/fixtures/bmad/valid-story.md +107 -0
  121. package/src/__tests__/fixtures/bmad/valid-task.md +79 -0
  122. package/src/__tests__/fixtures/bmad/valid-v6-prd.md +35 -0
  123. package/src/__tests__/fixtures/generic/plan-detailed.md +39 -0
  124. package/src/__tests__/fixtures/generic/prd-simple.md +27 -0
  125. package/src/__tests__/fixtures/generic/rfc-example.md +26 -0
  126. package/src/__tests__/fixtures/generic/todo-list.md +23 -0
  127. package/src/__tests__/fixtures/speckit/sample-plan.md +63 -0
  128. package/src/__tests__/fixtures/speckit/sample-spec-namespaced.md +50 -0
  129. package/src/__tests__/fixtures/speckit/sample-spec.md +105 -0
  130. package/src/__tests__/fixtures/speckit/sample-tasks.md +87 -0
  131. package/src/__tests__/fixtures/speckit-official/auth-feature/plan.md +272 -0
  132. package/src/__tests__/fixtures/speckit-official/auth-feature/spec.md +149 -0
  133. package/src/__tests__/fixtures/speckit-official/auth-feature/tasks.md +169 -0
  134. package/src/__tests__/generic-format-adapter.test.ts +398 -0
  135. package/src/__tests__/speckit-export.test.ts +233 -0
  136. package/src/__tests__/speckit-format-adapter.test.ts +832 -0
  137. package/src/__tests__/speckit-import-v2.test.ts +253 -0
  138. package/src/__tests__/speckit-import.test.ts +209 -0
  139. package/src/__tests__/speckit-parser.test.ts +219 -0
  140. package/src/__tests__/speckit-spec-parser.test.ts +120 -0
  141. package/src/aps-markdown/__tests__/__fixtures__/simple-leaf.aps.md +17 -0
  142. package/src/aps-markdown/__tests__/adapter.test.ts +393 -0
  143. package/src/aps-markdown/adapter.ts +455 -0
  144. package/src/aps-markdown/index.ts +8 -0
  145. package/src/base/__tests__/registry.test.ts +515 -0
  146. package/src/base/file-discovery.ts +305 -0
  147. package/src/base/index.ts +10 -0
  148. package/src/base/registry.ts +263 -0
  149. package/src/base/testing.ts +334 -0
  150. package/src/base/types.ts +342 -0
  151. package/src/base/utils.ts +306 -0
  152. package/src/bmad/format-adapter.ts +227 -0
  153. package/src/bmad/index.ts +21 -0
  154. package/src/bmad/parser.ts +224 -0
  155. package/src/bmad/serializer.ts +206 -0
  156. package/src/bmad/types.ts +135 -0
  157. package/src/bmad/utils.ts +575 -0
  158. package/src/common/index.ts +2 -0
  159. package/src/common/registry.ts +72 -0
  160. package/src/common/types.ts +84 -0
  161. package/src/generic/__tests__/serializer.test.ts +167 -0
  162. package/src/generic/format-adapter.ts +200 -0
  163. package/src/generic/index.ts +11 -0
  164. package/src/generic/parser.ts +129 -0
  165. package/src/generic/serializer.ts +134 -0
  166. package/src/generic/types.ts +53 -0
  167. package/src/generic/utils.ts +270 -0
  168. package/src/index.ts +48 -0
  169. package/src/speckit/export.ts +489 -0
  170. package/src/speckit/format-adapter.ts +595 -0
  171. package/src/speckit/import-v2.ts +445 -0
  172. package/src/speckit/import.ts +305 -0
  173. package/src/speckit/index.ts +4 -0
  174. package/src/speckit/parser.ts +351 -0
  175. package/src/speckit/parsers/plan-parser.ts +342 -0
  176. package/src/speckit/parsers/spec-parser.ts +379 -0
  177. package/src/speckit/parsers/tasks-parser.ts +246 -0
  178. package/tsconfig.json +26 -0
  179. package/tsconfig.lib.json +21 -0
  180. package/tsconfig.lib.tsbuildinfo +1 -0
  181. package/tsconfig.spec.json +9 -0
  182. package/tsconfig.tsbuildinfo +1 -0
  183. package/vitest.config.ts +14 -0
@@ -0,0 +1,255 @@
1
+ /**
2
+ * Parser for official GitHub Spec-Kit spec.md format
3
+ *
4
+ * Spec.md focuses on WHAT and WHY (not HOW):
5
+ * - Feature metadata
6
+ * - User Scenarios & Testing (prioritized user stories)
7
+ * - Requirements (functional requirements, key entities)
8
+ * - Success Criteria (measurable outcomes)
9
+ */
10
+ export class SpecParser {
11
+ parseSpec(content) {
12
+ const result = {
13
+ metadata: {},
14
+ userScenarios: [],
15
+ requirements: {
16
+ functional: [],
17
+ entities: [],
18
+ },
19
+ successCriteria: {
20
+ quantitative: [],
21
+ qualitative: [],
22
+ },
23
+ clarifications: [],
24
+ };
25
+ // Extract metadata from first section (before ## headers)
26
+ result.metadata = this.extractMetadata(content);
27
+ // Parse user scenarios section
28
+ result.userScenarios = this.parseUserScenarios(content);
29
+ // Parse requirements section
30
+ result.requirements = this.parseRequirements(content);
31
+ // Parse success criteria
32
+ result.successCriteria = this.parseSuccessCriteria(content);
33
+ // Extract all clarifications
34
+ result.clarifications = this.extractClarifications(content);
35
+ return result;
36
+ }
37
+ extractMetadata(content) {
38
+ const metadata = {};
39
+ // Extract title (# Feature: ...)
40
+ const titleMatch = content.match(/^#\s+Feature:\s+(.+)$/m);
41
+ if (titleMatch) {
42
+ metadata.feature = titleMatch[1].trim();
43
+ }
44
+ // Extract bold key-value pairs (** Key **: value)
45
+ // Stop at next ** or newline to handle multiple metadata on same line
46
+ const metadataRegex = /\*\*([^*]+)\*\*:\s*`?([^`*\n]+?)`?\s*(?=\*\*|\n|$)/g;
47
+ let match;
48
+ while ((match = metadataRegex.exec(content)) !== null) {
49
+ const key = match[1].trim().toLowerCase().replace(/\s+/g, '_');
50
+ const value = match[2].trim();
51
+ metadata[key] = value;
52
+ }
53
+ return metadata;
54
+ }
55
+ parseUserScenarios(content) {
56
+ const scenarios = [];
57
+ // Find "User Scenarios & Testing" section
58
+ const scenarioSectionMatch = content.match(/##\s+User Scenarios?\s*(?:&|and)?\s*Testing([\s\S]*?)(?=\n##\s|\n#\s|$)/i);
59
+ if (!scenarioSectionMatch) {
60
+ return scenarios;
61
+ }
62
+ const scenarioSection = scenarioSectionMatch[1];
63
+ // Split by ### headers (each scenario)
64
+ const scenarioBlocks = scenarioSection.split(/###\s+/).slice(1);
65
+ for (const block of scenarioBlocks) {
66
+ const scenario = this.parseUserScenario(block);
67
+ if (scenario) {
68
+ scenarios.push(scenario);
69
+ }
70
+ }
71
+ return scenarios;
72
+ }
73
+ parseUserScenario(block) {
74
+ // Extract priority and title from first line (P1: Title)
75
+ const titleMatch = block.match(/^(P\d+):\s+(.+)$/m);
76
+ if (!titleMatch) {
77
+ return null;
78
+ }
79
+ const priority = titleMatch[1];
80
+ const title = titleMatch[2].trim();
81
+ // Extract user story components (handle multiline with line breaks)
82
+ const asAMatch = block.match(/\*\*As a\*\*\s+(.+?)(?=\s*\*\*|\n\n|$)/is);
83
+ const iWantToMatch = block.match(/\*\*I want(?:\s+to)?\*\*\s+(.+?)(?=\s*\*\*|\n\n|$)/is);
84
+ const soThatMatch = block.match(/\*\*So(?:\s+|\n)that\*\*\s+(.+?)(?=\s*\n\n|$)/is);
85
+ // Extract acceptance scenarios
86
+ const acceptanceScenarios = [];
87
+ const acceptanceMatch = block.match(/\*\*Acceptance Scenarios:\*\*([\s\S]*?)(?=\*\*Edge Cases:|\*\*\[NEEDS|###|$)/i);
88
+ if (acceptanceMatch) {
89
+ const scenarios = acceptanceMatch[1]
90
+ .split(/\n[-*]\s+/)
91
+ .map((s) => s.trim())
92
+ .filter((s) => s.length > 0);
93
+ acceptanceScenarios.push(...scenarios);
94
+ }
95
+ // Extract edge cases
96
+ const edgeCases = [];
97
+ const edgeCaseMatch = block.match(/\*\*Edge Cases:\*\*([\s\S]*?)(?=###|$)/i);
98
+ if (edgeCaseMatch) {
99
+ const cases = edgeCaseMatch[1]
100
+ .split(/\n[-*]\s+/)
101
+ .map((s) => s.trim())
102
+ .filter((s) => s.length > 0);
103
+ edgeCases.push(...cases);
104
+ }
105
+ return {
106
+ priority,
107
+ title,
108
+ asA: asAMatch?.[1]?.trim() || '',
109
+ iWantTo: iWantToMatch?.[1]?.trim() || '',
110
+ soThat: soThatMatch?.[1]?.trim() || '',
111
+ acceptanceScenarios,
112
+ edgeCases,
113
+ };
114
+ }
115
+ parseRequirements(content) {
116
+ const requirements = {
117
+ functional: [],
118
+ entities: [],
119
+ };
120
+ // Find "Requirements" section
121
+ const reqSectionMatch = content.match(/##\s+Requirements([\s\S]*?)(?=\n##\s|\n#\s|$)/i);
122
+ if (!reqSectionMatch) {
123
+ return requirements;
124
+ }
125
+ const reqSection = reqSectionMatch[1];
126
+ // Parse functional requirements
127
+ requirements.functional = this.parseFunctionalRequirements(reqSection);
128
+ // Parse entity definitions
129
+ requirements.entities = this.parseEntities(reqSection);
130
+ return requirements;
131
+ }
132
+ parseFunctionalRequirements(section) {
133
+ const requirements = [];
134
+ // Find "Functional Requirements" subsection
135
+ const functionalMatch = section.match(/###\s+Functional Requirements([\s\S]*?)(?=\n###|$)/i);
136
+ if (!functionalMatch) {
137
+ return requirements;
138
+ }
139
+ const functionalSection = functionalMatch[1];
140
+ // Match FR-XXX: Description or [NEEDS CLARIFICATION: question]
141
+ const reqRegex = /\*\*(FR-\d+)\*\*:\s+(.+?)(?=\n\*\*FR-|\n###|$)/gs;
142
+ let match;
143
+ while ((match = reqRegex.exec(functionalSection)) !== null) {
144
+ const code = match[1];
145
+ const description = match[2].trim();
146
+ // Check if this is a clarification request
147
+ const clarificationMatch = description.match(/\[NEEDS CLARIFICATION:\s*(.+?)\]/);
148
+ requirements.push({
149
+ code,
150
+ description: clarificationMatch ? description : description,
151
+ needsClarification: !!clarificationMatch,
152
+ clarificationQuestion: clarificationMatch?.[1]?.trim(),
153
+ });
154
+ }
155
+ return requirements;
156
+ }
157
+ parseEntities(section) {
158
+ const entities = [];
159
+ // Find "Key Entities" subsection
160
+ const entitiesMatch = section.match(/###\s+Key Entities([\s\S]*?)(?=\n##|$)/i);
161
+ if (!entitiesMatch) {
162
+ return entities;
163
+ }
164
+ const entitiesSection = entitiesMatch[1];
165
+ // Split by ** EntityName **
166
+ const entityBlocks = entitiesSection.split(/\*\*([^*]+)\*\*/g).slice(1);
167
+ for (let i = 0; i < entityBlocks.length; i += 2) {
168
+ const entityName = entityBlocks[i].trim();
169
+ const entityContent = entityBlocks[i + 1] || '';
170
+ if (!entityName || !entityContent.trim()) {
171
+ continue;
172
+ }
173
+ const entity = this.parseEntity(entityName, entityContent);
174
+ if (entity) {
175
+ entities.push(entity);
176
+ }
177
+ }
178
+ return entities;
179
+ }
180
+ parseEntity(name, content) {
181
+ const representsMatch = content.match(/[-*]\s+Represents:\s+(.+)/i);
182
+ const attributesMatch = content.match(/[-*]\s+Key Attributes:\s+(.+)/i);
183
+ const relationshipsMatch = content.match(/[-*]\s+Relationships:\s+(.+)/i);
184
+ return {
185
+ name,
186
+ represents: representsMatch?.[1]?.trim() || '',
187
+ keyAttributes: attributesMatch?.[1]
188
+ ?.split(',')
189
+ .map((a) => a.trim())
190
+ .filter((a) => a.length > 0) || [],
191
+ relationships: relationshipsMatch?.[1]
192
+ ?.split(',')
193
+ .map((r) => r.trim())
194
+ .filter((r) => r.length > 0) || [],
195
+ };
196
+ }
197
+ parseSuccessCriteria(content) {
198
+ const criteria = {
199
+ quantitative: [],
200
+ qualitative: [],
201
+ };
202
+ // Find "Success Criteria" section
203
+ const criteriaSectionMatch = content.match(/##\s+Success Criteria([\s\S]*?)(?=\n##\s|\n#\s|$)/i);
204
+ if (!criteriaSectionMatch) {
205
+ return criteria;
206
+ }
207
+ const criteriaSection = criteriaSectionMatch[1];
208
+ // Parse quantitative metrics
209
+ const quantMatch = criteriaSection.match(/###\s+Quantitative Metrics([\s\S]*?)(?=\n###|$)/i);
210
+ if (quantMatch) {
211
+ const metrics = quantMatch[1]
212
+ .split(/\n[-*]\s+/)
213
+ .map((m) => m.trim())
214
+ .filter((m) => m.length > 0);
215
+ criteria.quantitative = metrics;
216
+ }
217
+ // Parse qualitative metrics
218
+ const qualMatch = criteriaSection.match(/###\s+Qualitative Metrics([\s\S]*?)(?=\n###|$)/i);
219
+ if (qualMatch) {
220
+ const metrics = qualMatch[1]
221
+ .split(/\n[-*]\s+/)
222
+ .map((m) => m.trim())
223
+ .filter((m) => m.length > 0);
224
+ criteria.qualitative = metrics;
225
+ }
226
+ // Parse security metrics (if present)
227
+ const securityMatch = criteriaSection.match(/###\s+Security Metrics([\s\S]*?)(?=\n###|$)/i);
228
+ if (securityMatch) {
229
+ const metrics = securityMatch[1]
230
+ .split(/\n[-*]\s+/)
231
+ .map((m) => m.trim())
232
+ .filter((m) => m.length > 0);
233
+ criteria.security = metrics;
234
+ }
235
+ // Parse performance metrics (if present)
236
+ const perfMatch = criteriaSection.match(/###\s+Performance Metrics([\s\S]*?)(?=\n###|$)/i);
237
+ if (perfMatch) {
238
+ const metrics = perfMatch[1]
239
+ .split(/\n[-*]\s+/)
240
+ .map((m) => m.trim())
241
+ .filter((m) => m.length > 0);
242
+ criteria.performance = metrics;
243
+ }
244
+ return criteria;
245
+ }
246
+ extractClarifications(content) {
247
+ const clarifications = [];
248
+ const clarificationRegex = /\[NEEDS CLARIFICATION:\s*([^\]]+)\]/g;
249
+ let match;
250
+ while ((match = clarificationRegex.exec(content)) !== null) {
251
+ clarifications.push(match[1].trim());
252
+ }
253
+ return clarifications;
254
+ }
255
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Parser for official GitHub Spec-Kit tasks.md format
3
+ *
4
+ * Tasks.md breaks down work into executable tasks:
5
+ * - Tasks organized by phases
6
+ * - Task IDs for tracking
7
+ * - Parallel execution markers
8
+ * - Dependencies and execution order
9
+ * - Implementation strategies
10
+ */
11
+ interface TasksMetadata {
12
+ feature?: string;
13
+ branch?: string;
14
+ date?: string;
15
+ spec?: string;
16
+ plan?: string;
17
+ [key: string]: unknown;
18
+ }
19
+ interface Task {
20
+ id: string;
21
+ parallel: boolean;
22
+ userStory?: string;
23
+ description: string;
24
+ }
25
+ interface Phase {
26
+ name: string;
27
+ order: number;
28
+ tasks: Task[];
29
+ checkpoint?: string;
30
+ }
31
+ interface Dependency {
32
+ description: string;
33
+ requiredBefore?: string[];
34
+ canRunInParallel?: boolean;
35
+ }
36
+ interface ImplementationStrategy {
37
+ name: string;
38
+ description: string;
39
+ recommended?: boolean;
40
+ }
41
+ export interface ParsedTasks {
42
+ metadata: TasksMetadata;
43
+ phases: Phase[];
44
+ dependencies: Dependency[];
45
+ strategies: ImplementationStrategy[];
46
+ }
47
+ export declare class TasksParser {
48
+ parseTasks(content: string): ParsedTasks;
49
+ private extractMetadata;
50
+ private parsePhases;
51
+ private parseTaskItems;
52
+ private extractCheckpoint;
53
+ private parseDependencies;
54
+ private parseStrategies;
55
+ }
56
+ export {};
57
+ //# sourceMappingURL=tasks-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tasks-parser.d.ts","sourceRoot":"","sources":["../../../src/speckit/parsers/tasks-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,UAAU,aAAa;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,UAAU,IAAI;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,UAAU,KAAK;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,UAAU,UAAU;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,UAAU,sBAAsB;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,aAAa,CAAC;IACxB,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,YAAY,EAAE,UAAU,EAAE,CAAC;IAC3B,UAAU,EAAE,sBAAsB,EAAE,CAAC;CACtC;AAED,qBAAa,WAAW;IACtB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW;IAuBxC,OAAO,CAAC,eAAe;IAqBvB,OAAO,CAAC,WAAW;IA2BnB,OAAO,CAAC,cAAc;IA8BtB,OAAO,CAAC,iBAAiB;IAKzB,OAAO,CAAC,iBAAiB;IAyCzB,OAAO,CAAC,eAAe;CA4CxB"}
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Parser for official GitHub Spec-Kit tasks.md format
3
+ *
4
+ * Tasks.md breaks down work into executable tasks:
5
+ * - Tasks organized by phases
6
+ * - Task IDs for tracking
7
+ * - Parallel execution markers
8
+ * - Dependencies and execution order
9
+ * - Implementation strategies
10
+ */
11
+ export class TasksParser {
12
+ parseTasks(content) {
13
+ const result = {
14
+ metadata: {},
15
+ phases: [],
16
+ dependencies: [],
17
+ strategies: [],
18
+ };
19
+ // Extract metadata
20
+ result.metadata = this.extractMetadata(content);
21
+ // Parse phases and tasks
22
+ result.phases = this.parsePhases(content);
23
+ // Parse dependencies section
24
+ result.dependencies = this.parseDependencies(content);
25
+ // Parse implementation strategies
26
+ result.strategies = this.parseStrategies(content);
27
+ return result;
28
+ }
29
+ extractMetadata(content) {
30
+ const metadata = {};
31
+ // Extract title (# Tasks: ...)
32
+ const titleMatch = content.match(/^#\s+Tasks:\s+(.+)$/m);
33
+ if (titleMatch) {
34
+ metadata.feature = titleMatch[1].trim();
35
+ }
36
+ // Extract bold key-value pairs with links
37
+ const metadataRegex = /\*\*([^*]+)\*\*:\s*(?:`([^`\n]+)`|\[([^\]]+)\]\(([^)]+)\)|([^\n|]+))/g;
38
+ let match;
39
+ while ((match = metadataRegex.exec(content)) !== null) {
40
+ const key = match[1].trim().toLowerCase().replace(/\s+/g, '_');
41
+ const value = match[2] || match[3] || match[5] || '';
42
+ metadata[key] = value.trim();
43
+ }
44
+ return metadata;
45
+ }
46
+ parsePhases(content) {
47
+ const phases = [];
48
+ // Match ## Phase N: Name sections
49
+ const phaseRegex = /##\s+Phase\s+(\d+):\s+([^\n]+)([\s\S]*?)(?=\n##\s+(?:Phase|Dependencies)|$)/gi;
50
+ let match;
51
+ while ((match = phaseRegex.exec(content)) !== null) {
52
+ const order = parseInt(match[1], 10);
53
+ const name = match[2].trim();
54
+ const phaseContent = match[3];
55
+ const tasks = this.parseTaskItems(phaseContent);
56
+ const checkpoint = this.extractCheckpoint(phaseContent);
57
+ phases.push({
58
+ name,
59
+ order,
60
+ tasks,
61
+ checkpoint,
62
+ });
63
+ }
64
+ return phases;
65
+ }
66
+ parseTaskItems(content) {
67
+ const tasks = [];
68
+ // Match task lines: - [ID] [P?] [Story?] Description
69
+ // Format: - [001] Description
70
+ // or: - [001] [P] Description
71
+ // or: - [001] [P] [Story] Description
72
+ const taskRegex = /[-*]\s+\[(\d+)\](?:\s+\[P\])?(?:\s+\[([^\]]+)\])?\s+(.+)/g;
73
+ let match;
74
+ while ((match = taskRegex.exec(content)) !== null) {
75
+ const id = match[1];
76
+ const userStory = match[2]?.trim();
77
+ const description = match[3].trim();
78
+ // Check if [P] marker exists (for parallel execution)
79
+ const lineMatch = content.match(new RegExp(`\\[${id}\\]\\s+\\[P\\]`, 'i'));
80
+ const parallel = !!lineMatch;
81
+ tasks.push({
82
+ id,
83
+ parallel,
84
+ userStory,
85
+ description,
86
+ });
87
+ }
88
+ return tasks;
89
+ }
90
+ extractCheckpoint(content) {
91
+ const checkpointMatch = content.match(/\*\*Checkpoint\*\*:\s+(.+)/i);
92
+ return checkpointMatch?.[1]?.trim();
93
+ }
94
+ parseDependencies(content) {
95
+ const dependencies = [];
96
+ // Find "Dependencies & Execution Order" section
97
+ const depsMatch = content.match(/##\s+Dependencies\s*(?:&|and)?\s*Execution Order([\s\S]*?)(?=\n##\s|$)/i);
98
+ if (!depsMatch) {
99
+ return dependencies;
100
+ }
101
+ const depsSection = depsMatch[1];
102
+ // Parse subsections (### Required Sequential Order, ### Parallel Work Opportunities)
103
+ const subsections = depsSection.split(/###\s+/).slice(1);
104
+ for (const subsection of subsections) {
105
+ const lines = subsection.split('\n');
106
+ const title = lines[0].trim();
107
+ const items = [];
108
+ for (const line of lines.slice(1)) {
109
+ const itemMatch = line.match(/^[-*]\s+(.+)/);
110
+ if (itemMatch) {
111
+ items.push(itemMatch[1].trim());
112
+ }
113
+ }
114
+ if (items.length > 0) {
115
+ dependencies.push({
116
+ description: title,
117
+ requiredBefore: title.toLowerCase().includes('sequential') ? items : undefined,
118
+ canRunInParallel: title.toLowerCase().includes('parallel'),
119
+ });
120
+ }
121
+ }
122
+ return dependencies;
123
+ }
124
+ parseStrategies(content) {
125
+ const strategies = [];
126
+ // Find "Implementation Strategies" section
127
+ const strategiesMatch = content.match(/##\s+Implementation Strategies([\s\S]*?)$/i);
128
+ if (!strategiesMatch) {
129
+ return strategies;
130
+ }
131
+ const strategiesSection = strategiesMatch[1];
132
+ // Parse ### Strategy N: Name
133
+ const strategyRegex = /###\s+Strategy\s+\d+:\s+([^\n]+)([\s\S]*?)(?=\n###|$)/gi;
134
+ let match;
135
+ while ((match = strategyRegex.exec(strategiesSection)) !== null) {
136
+ const name = match[1].trim();
137
+ const description = match[2].trim();
138
+ // Check if recommended
139
+ const recommended = strategiesSection.toLowerCase().includes(`recommended`);
140
+ strategies.push({
141
+ name,
142
+ description,
143
+ recommended: recommended && match[0].toLowerCase().includes('recommended'),
144
+ });
145
+ }
146
+ // Extract recommended strategy
147
+ const recommendedMatch = strategiesSection.match(/\*\*Recommended\*\*:\s+([^.]+)/i);
148
+ if (recommendedMatch) {
149
+ const recommendedName = recommendedMatch[1].trim();
150
+ const strategy = strategies.find((s) => recommendedName.toLowerCase().includes(s.name.toLowerCase()));
151
+ if (strategy) {
152
+ strategy.recommended = true;
153
+ }
154
+ }
155
+ return strategies;
156
+ }
157
+ }
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@eddacraft/anvil-adapters",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": "./dist/index.js"
9
+ },
10
+ "dependencies": {
11
+ "@eddacraft/anvil-aps": "0.1.0",
12
+ "@eddacraft/anvil-core": "0.1.0"
13
+ },
14
+ "devDependencies": {
15
+ "vitest": "^4.0.18"
16
+ },
17
+ "scripts": {
18
+ "build": "tsc -p tsconfig.lib.json",
19
+ "test": "vitest run",
20
+ "test:watch": "vitest",
21
+ "typecheck": "tsc --noEmit"
22
+ }
23
+ }
package/project.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@eddacraft/anvil-adapters",
3
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
4
+ "sourceRoot": "packages/adapters/src",
5
+ "projectType": "library",
6
+ "release": {
7
+ "version": {
8
+ "manifestRootsToUpdate": ["{projectRoot}/dist"],
9
+ "currentVersionResolver": "git-tag",
10
+ "fallbackCurrentVersionResolver": "disk"
11
+ }
12
+ },
13
+ "tags": [],
14
+ "targets": {
15
+ "build": {
16
+ "executor": "nx:run-script",
17
+ "outputs": ["{projectRoot}/dist"],
18
+ "options": {
19
+ "script": "build"
20
+ },
21
+ "dependsOn": ["^build"]
22
+ },
23
+ "nx-release-publish": {
24
+ "options": {
25
+ "packageRoot": "{projectRoot}/dist"
26
+ }
27
+ }
28
+ }
29
+ }