@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,254 @@
1
+ import { createHash } from 'node:crypto';
2
+ export function generateDeterministicPlanId(content) {
3
+ const hash = createHash('sha256').update(content, 'utf8').digest('hex');
4
+ return `aps-${hash.substring(0, 8)}`;
5
+ }
6
+ /**
7
+ * Create a detection result
8
+ *
9
+ * @param detected - Whether format was detected
10
+ * @param confidence - Confidence score (0-100)
11
+ * @param reason - Optional reason
12
+ * @returns Detection result
13
+ */
14
+ export function createDetection(detected, confidence, reason) {
15
+ return {
16
+ detected,
17
+ confidence: Math.max(0, Math.min(100, confidence)),
18
+ reason,
19
+ };
20
+ }
21
+ /**
22
+ * Create an adapter error
23
+ *
24
+ * @param code - Error code
25
+ * @param message - Error message
26
+ * @param options - Additional options
27
+ * @returns Adapter error
28
+ */
29
+ export function createError(code, message, options) {
30
+ return {
31
+ code,
32
+ message,
33
+ ...options,
34
+ };
35
+ }
36
+ /**
37
+ * Create an adapter warning
38
+ *
39
+ * @param code - Warning code
40
+ * @param message - Warning message
41
+ * @param options - Additional options
42
+ * @returns Adapter warning
43
+ */
44
+ export function createWarning(code, message, options) {
45
+ return {
46
+ code,
47
+ message,
48
+ ...options,
49
+ };
50
+ }
51
+ /**
52
+ * Check if content matches a pattern
53
+ *
54
+ * Useful for format detection.
55
+ *
56
+ * @param content - Content to check
57
+ * @param patterns - Array of regex patterns or strings
58
+ * @returns Number of patterns matched (0 to patterns.length)
59
+ */
60
+ export function matchesPatterns(content, patterns) {
61
+ let matches = 0;
62
+ for (const pattern of patterns) {
63
+ if (typeof pattern === 'string') {
64
+ if (content.includes(pattern)) {
65
+ matches++;
66
+ }
67
+ }
68
+ else {
69
+ if (pattern.test(content)) {
70
+ matches++;
71
+ }
72
+ }
73
+ }
74
+ return matches;
75
+ }
76
+ /**
77
+ * Calculate confidence score based on pattern matches
78
+ *
79
+ * @param matchCount - Number of patterns matched
80
+ * @param totalPatterns - Total number of patterns checked
81
+ * @param requiredMatches - Minimum matches for 100% confidence
82
+ * @returns Confidence score (0-100)
83
+ */
84
+ export function calculateConfidence(matchCount, totalPatterns, requiredMatches = totalPatterns) {
85
+ if (matchCount === 0)
86
+ return 0;
87
+ if (matchCount >= requiredMatches)
88
+ return 100;
89
+ // Linear interpolation
90
+ return Math.round((matchCount / requiredMatches) * 100);
91
+ }
92
+ /**
93
+ * Extract file extension from filename or path
94
+ *
95
+ * @param filename - Filename or path
96
+ * @returns Extension (including dot) or empty string
97
+ */
98
+ export function getExtension(filename) {
99
+ const match = filename.match(/\.[^.]+$/);
100
+ return match ? match[0] : '';
101
+ }
102
+ /**
103
+ * Normalize format identifier
104
+ *
105
+ * Removes leading dot and converts to lowercase.
106
+ *
107
+ * @param format - Format identifier or extension
108
+ * @returns Normalized format
109
+ */
110
+ export function normalizeFormat(format) {
111
+ return format.toLowerCase().replace(/^\./, '');
112
+ }
113
+ /**
114
+ * Format errors for display
115
+ *
116
+ * @param errors - Array of errors
117
+ * @returns Formatted error string
118
+ */
119
+ export function formatErrors(errors) {
120
+ return errors
121
+ .map((error) => {
122
+ let msg = `[${error.code}] ${error.message}`;
123
+ if (error.path) {
124
+ msg += ` (at ${error.path}`;
125
+ if (error.line !== undefined) {
126
+ msg += `:${error.line}`;
127
+ if (error.column !== undefined) {
128
+ msg += `:${error.column}`;
129
+ }
130
+ }
131
+ msg += ')';
132
+ }
133
+ return msg;
134
+ })
135
+ .join('\n');
136
+ }
137
+ /**
138
+ * Format warnings for display
139
+ *
140
+ * @param warnings - Array of warnings
141
+ * @returns Formatted warning string
142
+ */
143
+ export function formatWarnings(warnings) {
144
+ return warnings
145
+ .map((warning) => {
146
+ let msg = `[${warning.code}] ${warning.message}`;
147
+ if (warning.path) {
148
+ msg += ` (at ${warning.path}`;
149
+ if (warning.line !== undefined) {
150
+ msg += `:${warning.line}`;
151
+ if (warning.column !== undefined) {
152
+ msg += `:${warning.column}`;
153
+ }
154
+ }
155
+ msg += ')';
156
+ }
157
+ return msg;
158
+ })
159
+ .join('\n');
160
+ }
161
+ /**
162
+ * Merge parse results
163
+ *
164
+ * Useful when parsing composite formats.
165
+ *
166
+ * @param results - Array of parse results
167
+ * @returns Merged result
168
+ */
169
+ export function mergeParseResults(results) {
170
+ if (results.length === 0) {
171
+ return {
172
+ success: false,
173
+ errors: [createError('NO_RESULTS', 'No parse results to merge')],
174
+ };
175
+ }
176
+ if (results.length === 1) {
177
+ return results[0];
178
+ }
179
+ const allErrors = [];
180
+ const allWarnings = [];
181
+ let finalData = results[0].data;
182
+ for (const result of results) {
183
+ if (!result.success) {
184
+ if (result.errors) {
185
+ allErrors.push(...result.errors);
186
+ }
187
+ }
188
+ if (result.warnings) {
189
+ allWarnings.push(...result.warnings);
190
+ }
191
+ if (result.data) {
192
+ finalData = result.data;
193
+ }
194
+ }
195
+ if (allErrors.length > 0) {
196
+ return {
197
+ success: false,
198
+ errors: allErrors,
199
+ warnings: allWarnings.length > 0 ? allWarnings : undefined,
200
+ };
201
+ }
202
+ return {
203
+ success: true,
204
+ data: finalData,
205
+ warnings: allWarnings.length > 0 ? allWarnings : undefined,
206
+ };
207
+ }
208
+ /**
209
+ * Check if result has errors
210
+ *
211
+ * @param result - Parse or serialize result
212
+ * @returns True if result has errors
213
+ */
214
+ export function hasErrors(result) {
215
+ return !result.success || (result.errors !== undefined && result.errors.length > 0);
216
+ }
217
+ /**
218
+ * Check if result has warnings
219
+ *
220
+ * @param result - Parse or serialize result
221
+ * @returns True if result has warnings
222
+ */
223
+ export function hasWarnings(result) {
224
+ return result.warnings !== undefined && result.warnings.length > 0;
225
+ }
226
+ /**
227
+ * Extract first line from content (useful for format detection)
228
+ *
229
+ * @param content - Content string
230
+ * @returns First non-empty line or empty string
231
+ */
232
+ export function getFirstLine(content) {
233
+ const lines = content.split('\n');
234
+ for (const line of lines) {
235
+ const trimmed = line.trim();
236
+ if (trimmed) {
237
+ return trimmed;
238
+ }
239
+ }
240
+ return '';
241
+ }
242
+ /**
243
+ * Count occurrences of pattern in content
244
+ *
245
+ * @param content - Content to search
246
+ * @param pattern - Pattern to count
247
+ * @returns Number of occurrences
248
+ */
249
+ export function countOccurrences(content, pattern) {
250
+ if (typeof pattern === 'string') {
251
+ return (content.match(new RegExp(pattern, 'g')) || []).length;
252
+ }
253
+ return (content.match(new RegExp(pattern.source, 'g')) || []).length;
254
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * BMAD Format Adapter
3
+ *
4
+ * FormatAdapter implementation for BMAD (Breakthrough Method for Agile AI-Driven Development) format.
5
+ * Handles PRD, Architecture, Epic, Story, and Agent documents.
6
+ */
7
+ import { type APSPlan, type ValidationResult } from '@eddacraft/anvil-core';
8
+ import { BaseFormatAdapter, type AdapterMetadata, type DetectionResult, type ParseResult, type SerializeResult, type ParseContext, type AdapterOptions, type PathDetectionHint } from '../base/types.js';
9
+ /**
10
+ * BMAD FormatAdapter implementation
11
+ *
12
+ * Converts between BMAD format documents and APS plans.
13
+ */
14
+ export declare class BMADFormatAdapter extends BaseFormatAdapter {
15
+ readonly metadata: AdapterMetadata;
16
+ /**
17
+ * Detect if content is BMAD format
18
+ *
19
+ * Uses confidence scoring based on multiple indicators:
20
+ * - YAML front-matter (30 points)
21
+ * - Requirement identifiers FR/NFR/US (25 points)
22
+ * - User story format (20 points)
23
+ * - Change log table (15 points)
24
+ * - Document title (10 points)
25
+ *
26
+ * @param content - Document content to analyze
27
+ * @returns Detection result with confidence score
28
+ */
29
+ detect(content: string): DetectionResult;
30
+ /**
31
+ * Detect with file path hints for improved accuracy
32
+ *
33
+ * Uses folder structure (e.g., `_bmad/`, `.bmad/`, `_config/`)
34
+ * to boost detection confidence.
35
+ *
36
+ * @param content - Document content to analyze
37
+ * @param hint - Path and directory information
38
+ * @returns Detection result with confidence score
39
+ */
40
+ detectWithPath(content: string, hint: PathDetectionHint): DetectionResult;
41
+ /**
42
+ * Parse BMAD content to APS plan
43
+ *
44
+ * @param content - BMAD markdown content
45
+ * @param context - Parse context for provenance
46
+ * @param options - Adapter options
47
+ * @returns Parse result with APS plan
48
+ */
49
+ parse(content: string, context?: ParseContext, _options?: AdapterOptions): Promise<ParseResult>;
50
+ /**
51
+ * Serialize APS plan to BMAD format
52
+ *
53
+ * @param plan - APS plan to serialize
54
+ * @param options - Adapter options
55
+ * @returns Serialize result with BMAD markdown
56
+ */
57
+ serialize(plan: APSPlan, _options?: AdapterOptions): Promise<SerializeResult>;
58
+ /**
59
+ * Validate BMAD content
60
+ *
61
+ * Checks for required BMAD elements without full conversion.
62
+ *
63
+ * @param content - BMAD content to validate
64
+ * @param options - Validation options
65
+ * @returns Validation result
66
+ */
67
+ validate(content: string, _options?: AdapterOptions): Promise<ValidationResult>;
68
+ }
69
+ /**
70
+ * Create a new BMAD format adapter instance
71
+ *
72
+ * @param options - Adapter options
73
+ * @returns BMAD adapter instance
74
+ */
75
+ export declare function createBMADAdapter(options?: AdapterOptions): BMADFormatAdapter;
76
+ //# sourceMappingURL=format-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-adapter.d.ts","sourceRoot":"","sources":["../../src/bmad/format-adapter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAgB,KAAK,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC1F,OAAO,EACL,iBAAiB,EACjB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACvB,MAAM,kBAAkB,CAAC;AAa1B;;;;GAIG;AACH,qBAAa,iBAAkB,SAAQ,iBAAiB;IACtD,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAOhC;IAEF;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe;IASxC;;;;;;;;;OASG;IACH,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,eAAe;IAQzE;;;;;;;OAOG;IACG,KAAK,CACT,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,YAAY,EACtB,QAAQ,CAAC,EAAE,cAAc,GACxB,OAAO,CAAC,WAAW,CAAC;IAuBvB;;;;;;OAMG;IACG,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC;IAenF;;;;;;;;OAQG;IACG,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAiEtF;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,iBAAiB,CAE7E"}
@@ -0,0 +1,186 @@
1
+ /**
2
+ * BMAD Format Adapter
3
+ *
4
+ * FormatAdapter implementation for BMAD (Breakthrough Method for Agile AI-Driven Development) format.
5
+ * Handles PRD, Architecture, Epic, Story, and Agent documents.
6
+ */
7
+ import { generateHash } from '@eddacraft/anvil-core';
8
+ import { BaseFormatAdapter, } from '../base/types.js';
9
+ import { createDetection } from '../base/utils.js';
10
+ import { analyzeContent, calculateConfidenceScore, buildDetectionReason, extractFrontMatter, identifyDocumentType, } from './utils.js';
11
+ import { BMADDocumentType } from './types.js';
12
+ import { parseBMAD } from './parser.js';
13
+ import { serializeToBMAD } from './serializer.js';
14
+ /**
15
+ * BMAD FormatAdapter implementation
16
+ *
17
+ * Converts between BMAD format documents and APS plans.
18
+ */
19
+ export class BMADFormatAdapter extends BaseFormatAdapter {
20
+ metadata = {
21
+ name: 'bmad',
22
+ version: '1.0.0',
23
+ displayName: 'BMAD (Breakthrough Method for Agile AI-Driven Development)',
24
+ description: 'BMAD PRD and architecture document adapter',
25
+ formats: ['bmad', 'prd', 'architecture'],
26
+ extensions: ['.md'],
27
+ };
28
+ /**
29
+ * Detect if content is BMAD format
30
+ *
31
+ * Uses confidence scoring based on multiple indicators:
32
+ * - YAML front-matter (30 points)
33
+ * - Requirement identifiers FR/NFR/US (25 points)
34
+ * - User story format (20 points)
35
+ * - Change log table (15 points)
36
+ * - Document title (10 points)
37
+ *
38
+ * @param content - Document content to analyze
39
+ * @returns Detection result with confidence score
40
+ */
41
+ detect(content) {
42
+ const indicators = analyzeContent(content);
43
+ const confidence = calculateConfidenceScore(indicators);
44
+ const reason = buildDetectionReason(indicators);
45
+ // Detection threshold: 50% confidence
46
+ return createDetection(confidence >= 50, confidence, reason);
47
+ }
48
+ /**
49
+ * Detect with file path hints for improved accuracy
50
+ *
51
+ * Uses folder structure (e.g., `_bmad/`, `.bmad/`, `_config/`)
52
+ * to boost detection confidence.
53
+ *
54
+ * @param content - Document content to analyze
55
+ * @param hint - Path and directory information
56
+ * @returns Detection result with confidence score
57
+ */
58
+ detectWithPath(content, hint) {
59
+ const indicators = analyzeContent(content, hint);
60
+ const confidence = calculateConfidenceScore(indicators);
61
+ const reason = buildDetectionReason(indicators);
62
+ return createDetection(confidence >= 50, confidence, reason);
63
+ }
64
+ /**
65
+ * Parse BMAD content to APS plan
66
+ *
67
+ * @param content - BMAD markdown content
68
+ * @param context - Parse context for provenance
69
+ * @param options - Adapter options
70
+ * @returns Parse result with APS plan
71
+ */
72
+ async parse(content, context, _options) {
73
+ try {
74
+ // Parse content to APS plan
75
+ const plan = parseBMAD(content, context);
76
+ // Generate hash for the plan
77
+ const planWithHash = {
78
+ ...plan,
79
+ hash: generateHash(plan),
80
+ };
81
+ return this.createParseSuccess(planWithHash);
82
+ }
83
+ catch (error) {
84
+ return this.createParseError([
85
+ {
86
+ code: 'PARSE_ERROR',
87
+ message: error instanceof Error ? error.message : 'Failed to parse BMAD content',
88
+ details: error,
89
+ },
90
+ ]);
91
+ }
92
+ }
93
+ /**
94
+ * Serialize APS plan to BMAD format
95
+ *
96
+ * @param plan - APS plan to serialize
97
+ * @param options - Adapter options
98
+ * @returns Serialize result with BMAD markdown
99
+ */
100
+ async serialize(plan, _options) {
101
+ try {
102
+ const content = serializeToBMAD(plan);
103
+ return this.createSerializeSuccess(content);
104
+ }
105
+ catch (error) {
106
+ return this.createSerializeError([
107
+ {
108
+ code: 'SERIALIZE_ERROR',
109
+ message: error instanceof Error ? error.message : 'Failed to serialize to BMAD format',
110
+ details: error,
111
+ },
112
+ ]);
113
+ }
114
+ }
115
+ /**
116
+ * Validate BMAD content
117
+ *
118
+ * Checks for required BMAD elements without full conversion.
119
+ *
120
+ * @param content - BMAD content to validate
121
+ * @param options - Validation options
122
+ * @returns Validation result
123
+ */
124
+ async validate(content, _options) {
125
+ const issues = [];
126
+ // Check for minimum content length
127
+ if (content.trim().length < 100) {
128
+ issues.push({
129
+ code: 'CONTENT_TOO_SHORT',
130
+ path: 'content',
131
+ message: 'Content is too short to be a valid BMAD document',
132
+ severity: 'error',
133
+ });
134
+ }
135
+ // Analyze content for BMAD indicators
136
+ const indicators = analyzeContent(content);
137
+ const confidence = calculateConfidenceScore(indicators);
138
+ // Low confidence suggests invalid BMAD format
139
+ if (confidence < 50) {
140
+ issues.push({
141
+ code: 'LOW_CONFIDENCE',
142
+ path: 'content',
143
+ message: `Content does not appear to be a valid BMAD document (confidence: ${confidence}%)`,
144
+ severity: 'error',
145
+ });
146
+ }
147
+ // Check for at least some requirements or stories
148
+ if (indicators.requirementCount === 0) {
149
+ issues.push({
150
+ code: 'NO_REQUIREMENTS',
151
+ path: 'content',
152
+ message: 'No requirements (FR/NFR/US) found in document',
153
+ severity: 'warning',
154
+ });
155
+ }
156
+ // v6: Warn on agent docs missing hasSidecar
157
+ const frontMatter = extractFrontMatter(content);
158
+ const docType = identifyDocumentType(content, frontMatter);
159
+ if (docType === BMADDocumentType.AGENT && frontMatter?.hasSidecar === undefined) {
160
+ issues.push({
161
+ code: 'MISSING_HAS_SIDECAR',
162
+ path: 'frontMatter.hasSidecar',
163
+ message: 'Agent document is missing the hasSidecar field (expected for BMAD v6)',
164
+ severity: 'warning',
165
+ });
166
+ }
167
+ return {
168
+ valid: issues.filter((i) => i.severity === 'error').length === 0,
169
+ issues: issues.length > 0 ? issues : undefined,
170
+ summary: issues.filter((i) => i.severity === 'error').length === 0 && issues.length === 0
171
+ ? 'BMAD document is valid'
172
+ : issues.filter((i) => i.severity === 'error').length === 0
173
+ ? `Valid with ${issues.length} warning${issues.length > 1 ? 's' : ''}`
174
+ : `Found ${issues.filter((i) => i.severity === 'error').length} error${issues.filter((i) => i.severity === 'error').length > 1 ? 's' : ''}`,
175
+ };
176
+ }
177
+ }
178
+ /**
179
+ * Create a new BMAD format adapter instance
180
+ *
181
+ * @param options - Adapter options
182
+ * @returns BMAD adapter instance
183
+ */
184
+ export function createBMADAdapter(options) {
185
+ return new BMADFormatAdapter(options);
186
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * BMAD Adapter Module
3
+ *
4
+ * Export all BMAD adapter functionality.
5
+ */
6
+ export { BMADFormatAdapter, createBMADAdapter } from './format-adapter.js';
7
+ export { BMAD_FOLDERS } from './types.js';
8
+ export type { BMADDocument, BMADDocumentType, BMADRequirement, BMADUserStory, BMADFrontMatter, BMADChangeLogEntry, RequirementType, DetectionIndicators, } from './types.js';
9
+ export { parseBMAD, parseBMADDocument, bmadToAPS } from './parser.js';
10
+ export { serializeToBMAD } from './serializer.js';
11
+ export * from './utils.js';
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/bmad/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC3E,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,YAAY,EACV,YAAY,EACZ,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,mBAAmB,GACpB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACtE,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,cAAc,YAAY,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * BMAD Adapter Module
3
+ *
4
+ * Export all BMAD adapter functionality.
5
+ */
6
+ export { BMADFormatAdapter, createBMADAdapter } from './format-adapter.js';
7
+ export { BMAD_FOLDERS } from './types.js';
8
+ export { parseBMAD, parseBMADDocument, bmadToAPS } from './parser.js';
9
+ export { serializeToBMAD } from './serializer.js';
10
+ export * from './utils.js';
@@ -0,0 +1,12 @@
1
+ /**
2
+ * BMAD Parser
3
+ *
4
+ * Parses BMAD format documents (PRD, Architecture, Epics, Stories) into APS plans.
5
+ */
6
+ import { type APSPlan } from '@eddacraft/anvil-core';
7
+ import type { ParseContext } from '../base/types.js';
8
+ import { BMADDocument } from './types.js';
9
+ export declare function parseBMADDocument(content: string): BMADDocument;
10
+ export declare function bmadToAPS(document: BMADDocument, context?: ParseContext, originalContent?: string): APSPlan;
11
+ export declare function parseBMAD(content: string, context?: ParseContext): APSPlan;
12
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/bmad/parser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,KAAK,OAAO,EAAqC,MAAM,uBAAuB,CAAC;AACxF,OAAO,KAAK,EAAE,YAAY,EAAgC,MAAM,kBAAkB,CAAC;AACnF,OAAO,EAAE,YAAY,EAAmD,MAAM,YAAY,CAAC;AAiC3F,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,CAuB/D;AAoED,wBAAgB,SAAS,CACvB,QAAQ,EAAE,YAAY,EACtB,OAAO,CAAC,EAAE,YAAY,EACtB,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAkFT;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAG1E"}