@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,181 @@
1
+ /**
2
+ * BMAD Parser
3
+ *
4
+ * Parses BMAD format documents (PRD, Architecture, Epics, Stories) into APS plans.
5
+ */
6
+ import { validateRelativePath } from '@eddacraft/anvil-core';
7
+ import { RequirementType } from './types.js';
8
+ import { extractFrontMatter, extractRequirements, extractUserStories, extractChangeLog, identifyDocumentType, extractTitle, extractIntent, } from './utils.js';
9
+ import { createError, createWarning, generateDeterministicPlanId } from '../base/utils.js';
10
+ /**
11
+ * Parse BMAD document into internal structure
12
+ *
13
+ * @param content - BMAD markdown content
14
+ * @returns Parsed document
15
+ */
16
+ /**
17
+ * Validate and sanitize a file path to prevent path traversal attacks.
18
+ * Falls back to stripping special characters if validation fails.
19
+ */
20
+ function safePath(raw) {
21
+ try {
22
+ return validateRelativePath(raw);
23
+ }
24
+ catch {
25
+ return raw.replace(/[^a-z0-9/._-]/gi, '').replace(/\.{2,}/g, '');
26
+ }
27
+ }
28
+ /** Maximum input size for BMAD parsing (2MB) */
29
+ const MAX_INPUT_SIZE = 2 * 1024 * 1024;
30
+ export function parseBMADDocument(content) {
31
+ if (content.length > MAX_INPUT_SIZE) {
32
+ throw new Error(`Input exceeds maximum size of ${MAX_INPUT_SIZE} bytes`);
33
+ }
34
+ const frontMatter = extractFrontMatter(content);
35
+ const docType = identifyDocumentType(content, frontMatter);
36
+ const requirements = extractRequirements(content);
37
+ const userStories = extractUserStories(content);
38
+ const changeLog = extractChangeLog(content);
39
+ const title = extractTitle(content);
40
+ const intent = extractIntent(content, docType);
41
+ return {
42
+ type: docType,
43
+ frontMatter: frontMatter || undefined,
44
+ title: title || undefined,
45
+ intent,
46
+ requirements,
47
+ userStories,
48
+ changeLog,
49
+ sections: new Map(),
50
+ raw: content,
51
+ };
52
+ }
53
+ /**
54
+ * Convert BMAD requirement to APS change
55
+ *
56
+ * @param requirement - BMAD requirement
57
+ * @returns APS change
58
+ */
59
+ function requirementToChange(requirement) {
60
+ // Map requirement type to change type
61
+ switch (requirement.type) {
62
+ case RequirementType.FUNCTIONAL:
63
+ // FR typically means creating or updating files
64
+ return {
65
+ type: 'file_create',
66
+ path: safePath(`features/${requirement.id.toLowerCase()}.ts`),
67
+ description: `${requirement.id}: ${requirement.description}`,
68
+ };
69
+ case RequirementType.NON_FUNCTIONAL:
70
+ // NFR typically means configuration or validation
71
+ return {
72
+ type: 'config_update',
73
+ path: 'config/requirements.json',
74
+ description: `${requirement.id}: ${requirement.description}`,
75
+ };
76
+ case RequirementType.USER_STORY:
77
+ // US typically means creating feature files
78
+ return {
79
+ type: 'file_create',
80
+ path: safePath(`features/stories/${requirement.id.toLowerCase()}.ts`),
81
+ description: `${requirement.id}: ${requirement.description}`,
82
+ };
83
+ default:
84
+ return {
85
+ type: 'file_create',
86
+ path: safePath(`requirements/${requirement.id.toLowerCase()}.md`),
87
+ description: requirement.description,
88
+ };
89
+ }
90
+ }
91
+ /**
92
+ * Convert BMAD user story to APS change
93
+ *
94
+ * @param story - BMAD user story
95
+ * @returns APS change
96
+ */
97
+ function userStoryToChange(story) {
98
+ let description = `${story.id}: ${story.title}`;
99
+ if (story.userType && story.action && story.benefit) {
100
+ description += ` (As a ${story.userType}, I want ${story.action}, so that ${story.benefit})`;
101
+ }
102
+ if (story.acceptanceCriteria && story.acceptanceCriteria.length > 0) {
103
+ description += ` - Acceptance criteria: ${story.acceptanceCriteria.join('; ')}`;
104
+ }
105
+ return {
106
+ type: 'file_create',
107
+ path: safePath(`features/stories/${story.id.toLowerCase()}.ts`),
108
+ description,
109
+ };
110
+ }
111
+ export function bmadToAPS(document, context, originalContent) {
112
+ const planId = context?.planId ??
113
+ (originalContent
114
+ ? generateDeterministicPlanId(originalContent)
115
+ : `aps-${Date.now().toString(16).substring(0, 8)}`);
116
+ // Convert requirements and stories to changes
117
+ const changes = [];
118
+ const errors = [];
119
+ const warnings = [];
120
+ // Add requirements as changes
121
+ for (const requirement of document.requirements) {
122
+ try {
123
+ const change = requirementToChange(requirement);
124
+ changes.push(change);
125
+ }
126
+ catch (error) {
127
+ errors.push(createError('REQUIREMENT_CONVERSION_ERROR', `Failed to convert requirement ${requirement.id}`, {
128
+ details: error,
129
+ }));
130
+ }
131
+ }
132
+ // Add user stories as changes
133
+ for (const story of document.userStories) {
134
+ try {
135
+ const change = userStoryToChange(story);
136
+ changes.push(change);
137
+ }
138
+ catch (error) {
139
+ errors.push(createError('STORY_CONVERSION_ERROR', `Failed to convert story ${story.id}`, {
140
+ details: error,
141
+ }));
142
+ }
143
+ }
144
+ // If no changes found, add warning
145
+ if (changes.length === 0) {
146
+ warnings.push(createWarning('NO_CHANGES', 'No requirements or user stories found in document', {
147
+ details: { type: document.type },
148
+ }));
149
+ }
150
+ // Extract provenance from front-matter or context
151
+ const timestamp = document.frontMatter?.date || context?.timestamp || new Date().toISOString();
152
+ const author = document.frontMatter?.author || context?.author || 'unknown';
153
+ const version = document.frontMatter?.version || '1.0.0';
154
+ // Build APS plan
155
+ const plan = {
156
+ id: planId,
157
+ schema_version: '0.1.0',
158
+ intent: document.intent || 'BMAD document conversion',
159
+ proposed_changes: changes,
160
+ provenance: {
161
+ timestamp,
162
+ author,
163
+ source: 'cli',
164
+ version,
165
+ repository: context?.repositoryPath,
166
+ branch: context?.branch,
167
+ commit: context?.commit,
168
+ },
169
+ validations: {
170
+ required_checks: ['lint', 'test', 'coverage'],
171
+ skip_checks: [],
172
+ },
173
+ // Note: hash will be generated by the adapter after plan creation
174
+ hash: '0000000000000000000000000000000000000000000000000000000000000000',
175
+ };
176
+ return plan;
177
+ }
178
+ export function parseBMAD(content, context) {
179
+ const document = parseBMADDocument(content);
180
+ return bmadToAPS(document, context, content);
181
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * BMAD Serializer
3
+ *
4
+ * Serializes APS plans to BMAD format documents.
5
+ */
6
+ import type { APSPlan } from '@eddacraft/anvil-core';
7
+ /**
8
+ * Serialize APS plan to BMAD format
9
+ *
10
+ * Generates a BMAD PRD document from an APS plan.
11
+ *
12
+ * @param plan - APS plan to serialize
13
+ * @returns BMAD markdown content
14
+ */
15
+ export declare function serializeToBMAD(plan: APSPlan): string;
16
+ //# sourceMappingURL=serializer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../../src/bmad/serializer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAU,MAAM,uBAAuB,CAAC;AAE7D;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CA6LrD"}
@@ -0,0 +1,170 @@
1
+ /**
2
+ * BMAD Serializer
3
+ *
4
+ * Serializes APS plans to BMAD format documents.
5
+ */
6
+ /**
7
+ * Serialize APS plan to BMAD format
8
+ *
9
+ * Generates a BMAD PRD document from an APS plan.
10
+ *
11
+ * @param plan - APS plan to serialize
12
+ * @returns BMAD markdown content
13
+ */
14
+ export function serializeToBMAD(plan) {
15
+ const lines = [];
16
+ // Generate YAML front-matter
17
+ lines.push('---');
18
+ lines.push('name: "Product Requirements Document"');
19
+ lines.push(`version: "${plan.provenance.version || '1.0.0'}"`);
20
+ lines.push(`date: "${plan.provenance.timestamp}"`);
21
+ lines.push(`author: "${plan.provenance.author || 'unknown'}"`);
22
+ lines.push('description: "Generated from Anvil Plan Specification"');
23
+ lines.push('---');
24
+ lines.push('');
25
+ // Generate document header
26
+ const projectName = plan.metadata?.['projectName'] || 'Project';
27
+ lines.push(`# ${projectName} - Product Requirements Document`);
28
+ lines.push('');
29
+ lines.push(`**Author:** ${plan.provenance.author || 'unknown'}`);
30
+ lines.push(`**Date:** ${plan.provenance.timestamp}`);
31
+ lines.push(`**Version:** ${plan.provenance.version || '1.0.0'}`);
32
+ lines.push('');
33
+ // Generate change log
34
+ lines.push('## Change Log');
35
+ lines.push('');
36
+ lines.push('| Date | Version | Description | Author |');
37
+ lines.push('| :--- | :------ | :---------- | :----- |');
38
+ const date = new Date(plan.provenance.timestamp).toISOString().split('T')[0];
39
+ const version = plan.provenance.version || '1.0.0';
40
+ const author = plan.provenance.author || 'unknown';
41
+ lines.push(`| ${date} | ${version} | Initial version | ${author} |`);
42
+ lines.push('');
43
+ // Generate overview/intent section
44
+ lines.push('## Overview');
45
+ lines.push('');
46
+ lines.push(plan.intent);
47
+ lines.push('');
48
+ // Categorize changes
49
+ const functionalRequirements = [];
50
+ const nonFunctionalRequirements = [];
51
+ const userStories = [];
52
+ for (const change of plan.proposed_changes) {
53
+ // Categorize based on change type and path
54
+ if (change.path.includes('stories/') || change.description.match(/^US-\d{2}/)) {
55
+ userStories.push(change);
56
+ }
57
+ else if (change.type === 'config_update' ||
58
+ change.type === 'dependency_add' ||
59
+ change.type === 'dependency_update' ||
60
+ change.description.match(/^NFR-\d{2}/)) {
61
+ nonFunctionalRequirements.push(change);
62
+ }
63
+ else {
64
+ functionalRequirements.push(change);
65
+ }
66
+ }
67
+ // Generate Functional Requirements section
68
+ if (functionalRequirements.length > 0) {
69
+ lines.push('## Functional Requirements');
70
+ lines.push('');
71
+ functionalRequirements.forEach((change, index) => {
72
+ const reqNum = String(index + 1).padStart(2, '0');
73
+ const reqId = `FR-${reqNum}`;
74
+ // Extract description (remove existing FR-XX if present)
75
+ let description = change.description.replace(/^FR-\d{2}:\s*/, '');
76
+ // Add path context if not already in description
77
+ if (!description.includes(change.path)) {
78
+ description += ` (${change.path})`;
79
+ }
80
+ lines.push(`${reqId}: ${description}`);
81
+ });
82
+ lines.push('');
83
+ }
84
+ // Generate Non-Functional Requirements section
85
+ if (nonFunctionalRequirements.length > 0) {
86
+ lines.push('## Non-Functional Requirements');
87
+ lines.push('');
88
+ nonFunctionalRequirements.forEach((change, index) => {
89
+ const reqNum = String(index + 1).padStart(2, '0');
90
+ const reqId = `NFR-${reqNum}`;
91
+ // Extract description
92
+ const description = change.description.replace(/^NFR-\d{2}:\s*/, '');
93
+ lines.push(`${reqId}: ${description}`);
94
+ });
95
+ lines.push('');
96
+ }
97
+ // Add validation requirements as NFRs
98
+ if (plan.validations.required_checks.length > 0) {
99
+ if (nonFunctionalRequirements.length === 0) {
100
+ lines.push('## Non-Functional Requirements');
101
+ lines.push('');
102
+ }
103
+ const startIndex = nonFunctionalRequirements.length + 1;
104
+ plan.validations.required_checks.forEach((check, index) => {
105
+ const reqNum = String(startIndex + index).padStart(2, '0');
106
+ const reqId = `NFR-${reqNum}`;
107
+ lines.push(`${reqId}: Must pass ${check} validation`);
108
+ });
109
+ lines.push('');
110
+ }
111
+ // Generate User Stories section
112
+ if (userStories.length > 0) {
113
+ lines.push('## User Stories');
114
+ lines.push('');
115
+ userStories.forEach((story, index) => {
116
+ const storyNum = String(index + 1).padStart(2, '0');
117
+ const storyId = `US-${storyNum}`;
118
+ // Extract story title
119
+ let title = story.description.replace(/^US-\d{2}:\s*/, '');
120
+ // Check if description contains "As a... I want... so that..." pattern
121
+ const storyMatch = title.match(/\(As a (.+?), I want (.+?), so that (.+?)\)/);
122
+ if (storyMatch) {
123
+ const [, userType, action, benefit] = storyMatch;
124
+ title = title.replace(/\(As a .+?\)/, '').trim();
125
+ lines.push(`### ${storyId}: ${title}`);
126
+ lines.push('');
127
+ lines.push(`As a ${userType},`);
128
+ lines.push(`I want ${action},`);
129
+ lines.push(`so that ${benefit}.`);
130
+ lines.push('');
131
+ // Extract acceptance criteria if present
132
+ const criteriaMatch = title.match(/- Acceptance criteria: (.+)$/);
133
+ if (criteriaMatch) {
134
+ const criteria = criteriaMatch[1].split(';').map((c) => c.trim());
135
+ lines.push('**Acceptance Criteria:**');
136
+ lines.push('');
137
+ criteria.forEach((criterion, i) => {
138
+ lines.push(`${i + 1}. ${criterion}`);
139
+ });
140
+ lines.push('');
141
+ }
142
+ }
143
+ else {
144
+ lines.push(`### ${storyId}: ${title}`);
145
+ lines.push('');
146
+ }
147
+ });
148
+ }
149
+ // Add repository information if available
150
+ if (plan.provenance.repository || plan.provenance.branch) {
151
+ lines.push('## Repository Information');
152
+ lines.push('');
153
+ if (plan.provenance.repository) {
154
+ lines.push(`**Repository:** ${plan.provenance.repository}`);
155
+ }
156
+ if (plan.provenance.branch) {
157
+ lines.push(`**Branch:** ${plan.provenance.branch}`);
158
+ }
159
+ if (plan.provenance.commit) {
160
+ lines.push(`**Commit:** ${plan.provenance.commit}`);
161
+ }
162
+ lines.push('');
163
+ }
164
+ // Add footer
165
+ lines.push('---');
166
+ lines.push('');
167
+ lines.push('*Generated by Anvil - https://github.com/EddaCraft/anvil-001*');
168
+ lines.push('');
169
+ return lines.join('\n');
170
+ }
@@ -0,0 +1,127 @@
1
+ /**
2
+ * BMAD Format Types
3
+ *
4
+ * Type definitions for BMAD (Breakthrough Method for Agile AI-Driven Development) format.
5
+ */
6
+ /**
7
+ * BMAD v6 folder structure constants
8
+ *
9
+ * v6 changed `.bmad` → `_bmad` and `_cfg` → `_config`.
10
+ * We support both legacy and new paths for backward compatibility.
11
+ */
12
+ export declare const BMAD_FOLDERS: {
13
+ /** New v6 project folder */
14
+ readonly PROJECT: "_bmad";
15
+ /** Legacy project folder */
16
+ readonly PROJECT_LEGACY: ".bmad";
17
+ /** New v6 config folder */
18
+ readonly CONFIG: "_config";
19
+ /** Legacy config folder */
20
+ readonly CONFIG_LEGACY: "_cfg";
21
+ /** Agent memory folder (v6) */
22
+ readonly MEMORY: "_memory";
23
+ /** Module config file (v6) */
24
+ readonly MODULE_CONFIG: "module.yaml";
25
+ };
26
+ /**
27
+ * YAML front-matter metadata
28
+ */
29
+ export interface BMADFrontMatter {
30
+ name?: string;
31
+ version?: string;
32
+ description?: string;
33
+ output_file?: string;
34
+ variables?: Record<string, string>;
35
+ template?: string;
36
+ date?: string;
37
+ author?: string;
38
+ /** v6: Whether the agent document has a sidecar config */
39
+ hasSidecar?: boolean;
40
+ }
41
+ /**
42
+ * BMAD document types
43
+ */
44
+ export declare enum BMADDocumentType {
45
+ PRD = "prd",
46
+ ARCHITECTURE = "architecture",
47
+ EPIC = "epic",
48
+ STORY = "story",
49
+ /** v6: Agent persona/configuration document */
50
+ AGENT = "agent",
51
+ UNKNOWN = "unknown"
52
+ }
53
+ /**
54
+ * Requirement types
55
+ */
56
+ export declare enum RequirementType {
57
+ FUNCTIONAL = "FR",
58
+ NON_FUNCTIONAL = "NFR",
59
+ USER_STORY = "US"
60
+ }
61
+ /**
62
+ * Parsed requirement
63
+ */
64
+ export interface BMADRequirement {
65
+ type: RequirementType;
66
+ id: string;
67
+ number: number;
68
+ description: string;
69
+ line?: number;
70
+ }
71
+ /**
72
+ * User story structure
73
+ */
74
+ export interface BMADUserStory {
75
+ id: string;
76
+ title: string;
77
+ userType?: string;
78
+ action?: string;
79
+ benefit?: string;
80
+ acceptanceCriteria?: string[];
81
+ line?: number;
82
+ }
83
+ /**
84
+ * Change log entry
85
+ */
86
+ export interface BMADChangeLogEntry {
87
+ date: string;
88
+ version: string;
89
+ description: string;
90
+ author: string;
91
+ }
92
+ /**
93
+ * Parsed BMAD document
94
+ */
95
+ export interface BMADDocument {
96
+ type: BMADDocumentType;
97
+ frontMatter?: BMADFrontMatter;
98
+ title?: string;
99
+ intent?: string;
100
+ requirements: BMADRequirement[];
101
+ userStories: BMADUserStory[];
102
+ changeLog: BMADChangeLogEntry[];
103
+ sections: Map<string, string>;
104
+ raw: string;
105
+ }
106
+ /**
107
+ * Detection indicators for confidence scoring
108
+ */
109
+ export interface DetectionIndicators {
110
+ hasYamlFrontMatter: boolean;
111
+ hasFunctionalRequirements: boolean;
112
+ hasNonFunctionalRequirements: boolean;
113
+ hasUserStories: boolean;
114
+ hasUserStoryFormat: boolean;
115
+ hasChangeLogTable: boolean;
116
+ hasDocumentTitle: boolean;
117
+ requirementCount: number;
118
+ /** v6: Path is inside a BMAD project folder */
119
+ hasBmadFolderPath: boolean;
120
+ /** v6: Path is inside a BMAD config folder */
121
+ hasBmadConfigPath: boolean;
122
+ /** v6: Document has hasSidecar field */
123
+ hasHasSidecar: boolean;
124
+ /** v6: Document uses hyphenated variable syntax */
125
+ hasHyphenatedVariables: boolean;
126
+ }
127
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/bmad/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * BMAD Format Types
3
+ *
4
+ * Type definitions for BMAD (Breakthrough Method for Agile AI-Driven Development) format.
5
+ */
6
+ /**
7
+ * BMAD v6 folder structure constants
8
+ *
9
+ * v6 changed `.bmad` → `_bmad` and `_cfg` → `_config`.
10
+ * We support both legacy and new paths for backward compatibility.
11
+ */
12
+ export const BMAD_FOLDERS = {
13
+ /** New v6 project folder */
14
+ PROJECT: '_bmad',
15
+ /** Legacy project folder */
16
+ PROJECT_LEGACY: '.bmad',
17
+ /** New v6 config folder */
18
+ CONFIG: '_config',
19
+ /** Legacy config folder */
20
+ CONFIG_LEGACY: '_cfg',
21
+ /** Agent memory folder (v6) */
22
+ MEMORY: '_memory',
23
+ /** Module config file (v6) */
24
+ MODULE_CONFIG: 'module.yaml',
25
+ };
26
+ /**
27
+ * BMAD document types
28
+ */
29
+ export var BMADDocumentType;
30
+ (function (BMADDocumentType) {
31
+ BMADDocumentType["PRD"] = "prd";
32
+ BMADDocumentType["ARCHITECTURE"] = "architecture";
33
+ BMADDocumentType["EPIC"] = "epic";
34
+ BMADDocumentType["STORY"] = "story";
35
+ /** v6: Agent persona/configuration document */
36
+ BMADDocumentType["AGENT"] = "agent";
37
+ BMADDocumentType["UNKNOWN"] = "unknown";
38
+ })(BMADDocumentType || (BMADDocumentType = {}));
39
+ /**
40
+ * Requirement types
41
+ */
42
+ export var RequirementType;
43
+ (function (RequirementType) {
44
+ RequirementType["FUNCTIONAL"] = "FR";
45
+ RequirementType["NON_FUNCTIONAL"] = "NFR";
46
+ RequirementType["USER_STORY"] = "US";
47
+ })(RequirementType || (RequirementType = {}));
@@ -0,0 +1,120 @@
1
+ /**
2
+ * BMAD Adapter Utilities
3
+ *
4
+ * Helper functions for BMAD format parsing and serialization.
5
+ */
6
+ import { BMADFrontMatter, BMADRequirement, BMADUserStory, BMADChangeLogEntry, BMADDocumentType, DetectionIndicators } from './types.js';
7
+ import type { PathDetectionHint } from '../base/types.js';
8
+ /**
9
+ * Analyze a file path for BMAD folder structure indicators
10
+ *
11
+ * Detects both v6 (`_bmad`, `_config`) and legacy (`.bmad`, `_cfg`) paths.
12
+ *
13
+ * @param hint - Path detection hint with file path and directory info
14
+ * @returns Object indicating which BMAD path patterns were found
15
+ */
16
+ export declare function analyzePath(hint: PathDetectionHint): {
17
+ isBmadFolder: boolean;
18
+ isConfigFolder: boolean;
19
+ };
20
+ /**
21
+ * Expand BMAD template variables in content
22
+ *
23
+ * Supports both legacy underscore syntax `{project_root}` and
24
+ * v6 hyphenated syntax `{project-root}`.
25
+ *
26
+ * @param content - Content with variable placeholders
27
+ * @param variables - Variable values to substitute
28
+ * @returns Content with variables expanded
29
+ */
30
+ export declare function expandVariables(content: string, variables: Record<string, string>): string;
31
+ /**
32
+ * Check if content contains hyphenated variable syntax (v6)
33
+ *
34
+ * @param content - Content to check
35
+ * @returns True if hyphenated variables are found
36
+ */
37
+ export declare function hasHyphenatedVariables(content: string): boolean;
38
+ /**
39
+ * Parse a YAML boolean value
40
+ *
41
+ * Handles YAML 1.1 boolean forms: true/false, yes/no, on/off.
42
+ *
43
+ * @param value - String value from YAML
44
+ * @returns Boolean or undefined if not a boolean value
45
+ */
46
+ export declare function parseYamlBoolean(value: string): boolean | undefined;
47
+ /**
48
+ * Extract YAML front-matter from markdown content
49
+ *
50
+ * @param content - Markdown content
51
+ * @returns Parsed front-matter or null
52
+ */
53
+ export declare function extractFrontMatter(content: string): BMADFrontMatter | null;
54
+ /**
55
+ * Extract requirements from content
56
+ *
57
+ * @param content - Document content
58
+ * @returns Array of requirements
59
+ */
60
+ export declare function extractRequirements(content: string): BMADRequirement[];
61
+ /**
62
+ * Extract user stories from content
63
+ *
64
+ * @param content - Document content
65
+ * @returns Array of user stories
66
+ */
67
+ export declare function extractUserStories(content: string): BMADUserStory[];
68
+ /**
69
+ * Extract change log entries from content
70
+ *
71
+ * @param content - Document content
72
+ * @returns Array of change log entries
73
+ */
74
+ export declare function extractChangeLog(content: string): BMADChangeLogEntry[];
75
+ /**
76
+ * Identify document type from content
77
+ *
78
+ * @param content - Document content
79
+ * @param frontMatter - Parsed front-matter
80
+ * @returns Document type
81
+ */
82
+ export declare function identifyDocumentType(content: string, frontMatter?: BMADFrontMatter | null): BMADDocumentType;
83
+ /**
84
+ * Analyze content for detection indicators
85
+ *
86
+ * @param content - Document content
87
+ * @param hint - Optional path detection hint
88
+ * @returns Detection indicators
89
+ */
90
+ export declare function analyzeContent(content: string, hint?: PathDetectionHint): DetectionIndicators;
91
+ /**
92
+ * Calculate detection confidence score
93
+ *
94
+ * @param indicators - Detection indicators
95
+ * @returns Confidence score (0-100)
96
+ */
97
+ export declare function calculateConfidenceScore(indicators: DetectionIndicators): number;
98
+ /**
99
+ * Build detection reason message
100
+ *
101
+ * @param indicators - Detection indicators
102
+ * @returns Reason message
103
+ */
104
+ export declare function buildDetectionReason(indicators: DetectionIndicators): string;
105
+ /**
106
+ * Extract document title from content
107
+ *
108
+ * @param content - Document content
109
+ * @returns Title or null
110
+ */
111
+ export declare function extractTitle(content: string): string | null;
112
+ /**
113
+ * Extract intent/summary from document
114
+ *
115
+ * @param content - Document content
116
+ * @param docType - Document type
117
+ * @returns Intent description
118
+ */
119
+ export declare function extractIntent(content: string, docType: BMADDocumentType): string;
120
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/bmad/utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,eAAe,EACf,eAAe,EACf,aAAa,EACb,kBAAkB,EAElB,gBAAgB,EAChB,mBAAmB,EAEpB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,iBAAiB,GAAG;IACpD,YAAY,EAAE,OAAO,CAAC;IACtB,cAAc,EAAE,OAAO,CAAC;CACzB,CAqBA;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAc1F;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE/D;AAMD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAKnE;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CA2C1E;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,EAAE,CAuBtE;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,EAAE,CAiEnE;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAoCtE;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,WAAW,CAAC,EAAE,eAAe,GAAG,IAAI,GACnC,gBAAgB,CAkClB;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,iBAAiB,GAAG,mBAAmB,CA0B7F;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,mBAAmB,GAAG,MAAM,CAqDhF;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,mBAAmB,GAAG,MAAM,CAoC5E;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAa3D;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAsDhF"}