@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,232 @@
1
+ /**
2
+ * Generic Adapter Utilities
3
+ *
4
+ * Helper functions for parsing generic markdown documents.
5
+ */
6
+ /**
7
+ * Extract document title from first heading
8
+ */
9
+ export function extractTitle(content) {
10
+ const match = content.match(/^#\s+(.+)$/m);
11
+ return match ? match[1].trim() : undefined;
12
+ }
13
+ /**
14
+ * Extract intent from common sections
15
+ */
16
+ export function extractIntent(content) {
17
+ // Look for sections like: Purpose, Intent, Objective, Goal, Summary
18
+ const patterns = [
19
+ /##\s+(?:Purpose|Intent|Objective|Goal)\s*\n+([^\n#]+)/i,
20
+ /##\s+(?:Executive\s+)?Summary\s*\n+([^\n#]+)/i,
21
+ /^([^#\n]+?)(?=\n##|\n#|$)/m, // First paragraph as fallback
22
+ ];
23
+ for (const pattern of patterns) {
24
+ const match = content.match(pattern);
25
+ if (match && match[1]) {
26
+ return match[1].trim().substring(0, 500); // Limit length
27
+ }
28
+ }
29
+ return undefined;
30
+ }
31
+ /**
32
+ * Extract overview/description
33
+ */
34
+ export function extractOverview(content) {
35
+ const patterns = [/##\s+(?:Overview|Description|Background|Context)\s*\n+([\s\S]+?)(?=\n##|$)/i];
36
+ for (const pattern of patterns) {
37
+ const match = content.match(pattern);
38
+ if (match && match[1]) {
39
+ return match[1].trim();
40
+ }
41
+ }
42
+ return undefined;
43
+ }
44
+ /**
45
+ * Extract goals from lists under Goals/Objectives sections
46
+ */
47
+ export function extractGoals(content) {
48
+ const goals = [];
49
+ // Match Goals/Objectives section
50
+ const sectionMatch = content.match(/##\s+(?:Goals?|Objectives?)\s*\n+([\s\S]+?)(?=\n##|$)/i);
51
+ if (sectionMatch) {
52
+ const section = sectionMatch[1];
53
+ // Extract list items
54
+ const listItems = section.match(/^[-*]\s+(.+)$/gm);
55
+ if (listItems) {
56
+ goals.push(...listItems.map((item) => item.replace(/^[-*]\s+/, '').trim()));
57
+ }
58
+ }
59
+ return goals;
60
+ }
61
+ /**
62
+ * Extract requirements from various sections
63
+ */
64
+ export function extractRequirements(content) {
65
+ const requirements = [];
66
+ // Match Requirements/Needs/Must Have sections
67
+ const patterns = [
68
+ /##\s+(?:Requirements?|Needs?|Must\s+Have)\s*\n+([\s\S]+?)(?=\n##|$)/i,
69
+ /##\s+(?:Functional\s+)?Requirements?\s*\n+([\s\S]+?)(?=\n##|$)/i,
70
+ ];
71
+ for (const pattern of patterns) {
72
+ const match = content.match(pattern);
73
+ if (match) {
74
+ const section = match[1];
75
+ // Extract list items
76
+ const listItems = section.match(/^[-*]\s+(.+)$/gm);
77
+ if (listItems) {
78
+ requirements.push(...listItems.map((item) => item.replace(/^[-*]\s+/, '').trim()));
79
+ }
80
+ // Extract numbered items
81
+ const numberedItems = section.match(/^\d+\.\s+(.+)$/gm);
82
+ if (numberedItems) {
83
+ requirements.push(...numberedItems.map((item) => item.replace(/^\d+\.\s+/, '').trim()));
84
+ }
85
+ }
86
+ }
87
+ return requirements;
88
+ }
89
+ /**
90
+ * Extract tasks from Tasks/Action Items sections
91
+ */
92
+ export function extractTasks(content) {
93
+ const tasks = [];
94
+ const patterns = [/##\s+(?:Tasks?|Action\s+Items?|To\s+Do|TODO)\s*\n+([\s\S]+?)(?=\n##|$)/i];
95
+ for (const pattern of patterns) {
96
+ const match = content.match(pattern);
97
+ if (match) {
98
+ const section = match[1];
99
+ // Extract list items (including checkboxes)
100
+ const listItems = section.match(/^[-*]\s+(?:\[[ x]\]\s+)?(.+)$/gm);
101
+ if (listItems) {
102
+ tasks.push(...listItems.map((item) => item.replace(/^[-*]\s+(?:\[[ x]\]\s+)?/, '').trim()));
103
+ }
104
+ }
105
+ }
106
+ return tasks;
107
+ }
108
+ /**
109
+ * Extract features from Features/Capabilities sections
110
+ */
111
+ export function extractFeatures(content) {
112
+ const features = [];
113
+ const patterns = [/##\s+(?:Features?|Capabilities?|Functionality)\s*\n+([\s\S]+?)(?=\n##|$)/i];
114
+ for (const pattern of patterns) {
115
+ const match = content.match(pattern);
116
+ if (match) {
117
+ const section = match[1];
118
+ const listItems = section.match(/^[-*]\s+(.+)$/gm);
119
+ if (listItems) {
120
+ features.push(...listItems.map((item) => item.replace(/^[-*]\s+/, '').trim()));
121
+ }
122
+ }
123
+ }
124
+ return features;
125
+ }
126
+ /**
127
+ * Analyze content for generic markdown indicators
128
+ */
129
+ export function analyzeContent(content) {
130
+ const headings = content.match(/^#{1,6}\s+.+$/gm) || [];
131
+ const listItems = content.match(/^[-*]\s+.+$/gm) || [];
132
+ const words = content.split(/\s+/).filter((w) => w.length > 0);
133
+ return {
134
+ hasHeadings: headings.length > 0,
135
+ hasLists: listItems.length > 0,
136
+ hasRequirementsSection: /##\s+(?:Requirements?|Needs?)/i.test(content),
137
+ hasGoalsSection: /##\s+(?:Goals?|Objectives?)/i.test(content),
138
+ hasTasksSection: /##\s+(?:Tasks?|Action\s+Items?|To\s+Do)/i.test(content),
139
+ hasFeaturesSection: /##\s+(?:Features?|Capabilities?)/i.test(content),
140
+ hasOverviewSection: /##\s+(?:Overview|Description|Background)/i.test(content),
141
+ wordCount: words.length,
142
+ listItemCount: listItems.length,
143
+ };
144
+ }
145
+ /**
146
+ * Calculate confidence score for generic markdown detection
147
+ */
148
+ export function calculateConfidenceScore(indicators) {
149
+ let score = 0;
150
+ // Basic markdown structure (10 points)
151
+ if (indicators.hasHeadings) {
152
+ score += 10;
153
+ }
154
+ // Has lists (10 points)
155
+ if (indicators.hasLists) {
156
+ score += 10;
157
+ }
158
+ // Planning-related sections (5 points each)
159
+ if (indicators.hasRequirementsSection) {
160
+ score += 5;
161
+ }
162
+ if (indicators.hasGoalsSection) {
163
+ score += 5;
164
+ }
165
+ if (indicators.hasTasksSection) {
166
+ score += 5;
167
+ }
168
+ if (indicators.hasFeaturesSection) {
169
+ score += 5;
170
+ }
171
+ if (indicators.hasOverviewSection) {
172
+ score += 5;
173
+ }
174
+ // Content density (up to 10 points)
175
+ if (indicators.wordCount >= 100) {
176
+ score += 5;
177
+ }
178
+ if (indicators.wordCount >= 500) {
179
+ score += 5;
180
+ }
181
+ // List items (up to 5 points)
182
+ if (indicators.listItemCount >= 3) {
183
+ score += 3;
184
+ }
185
+ if (indicators.listItemCount >= 10) {
186
+ score += 2;
187
+ }
188
+ return Math.min(100, score);
189
+ }
190
+ /**
191
+ * Build detection reason string
192
+ */
193
+ export function buildDetectionReason(indicators) {
194
+ const reasons = [];
195
+ if (indicators.hasHeadings) {
196
+ reasons.push('markdown-headings');
197
+ }
198
+ if (indicators.hasLists) {
199
+ reasons.push(`${indicators.listItemCount} list-items`);
200
+ }
201
+ if (indicators.hasRequirementsSection) {
202
+ reasons.push('requirements-section');
203
+ }
204
+ if (indicators.hasGoalsSection) {
205
+ reasons.push('goals-section');
206
+ }
207
+ if (indicators.hasTasksSection) {
208
+ reasons.push('tasks-section');
209
+ }
210
+ if (indicators.hasFeaturesSection) {
211
+ reasons.push('features-section');
212
+ }
213
+ if (indicators.hasOverviewSection) {
214
+ reasons.push('overview-section');
215
+ }
216
+ return reasons.length > 0 ? reasons.join(', ') : 'generic-markdown';
217
+ }
218
+ /**
219
+ * Parse generic markdown document
220
+ */
221
+ export function parseGenericDocument(content) {
222
+ return {
223
+ title: extractTitle(content),
224
+ intent: extractIntent(content),
225
+ overview: extractOverview(content),
226
+ goals: extractGoals(content),
227
+ requirements: extractRequirements(content),
228
+ tasks: extractTasks(content),
229
+ features: extractFeatures(content),
230
+ raw: content,
231
+ };
232
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Anvil Adapters Package
3
+ *
4
+ * Provides format adapters for converting between external planning formats
5
+ * (SpecKit, BMAD, etc.) and the Anvil Plan Spec (APS).
6
+ */
7
+ export * from './base/index.js';
8
+ export * from './aps-markdown/index.js';
9
+ export * from './speckit/index.js';
10
+ export * from './bmad/index.js';
11
+ export * from './generic/index.js';
12
+ export type { SpecContext, ExternalSpec, ConversionResult, ConversionError, ConversionWarning, } from './common/types.js';
13
+ import { registry as baseRegistry } from './base/index.js';
14
+ export { baseRegistry as registry };
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,cAAc,iBAAiB,CAAC;AAGhC,cAAc,yBAAyB,CAAC;AAGxC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,iBAAiB,CAAC;AAGhC,cAAc,oBAAoB,CAAC;AAGnC,YAAY,EACV,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,eAAe,EACf,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,QAAQ,IAAI,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAe3D,OAAO,EAAE,YAAY,IAAI,QAAQ,EAAE,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Anvil Adapters Package
3
+ *
4
+ * Provides format adapters for converting between external planning formats
5
+ * (SpecKit, BMAD, etc.) and the Anvil Plan Spec (APS).
6
+ */
7
+ // Export base framework (types, registry, utils)
8
+ export * from './base/index.js';
9
+ // Export APS Markdown adapter (native format)
10
+ export * from './aps-markdown/index.js';
11
+ // Export SpecKit adapter
12
+ export * from './speckit/index.js';
13
+ // Export BMAD adapter
14
+ export * from './bmad/index.js';
15
+ // Export Generic adapter
16
+ export * from './generic/index.js';
17
+ // Auto-register adapters when module is imported
18
+ import { registry as baseRegistry } from './base/index.js';
19
+ import { APSMarkdownAdapter } from './aps-markdown/index.js';
20
+ import { BMADFormatAdapter } from './bmad/index.js';
21
+ import { SpecKitFormatAdapter } from './speckit/index.js';
22
+ import { GenericMarkdownAdapter } from './generic/index.js';
23
+ // Register adapters in priority order
24
+ // APS adapter first (native format), then specific external formats
25
+ // Generic adapter is registered last as fallback
26
+ baseRegistry.register(new APSMarkdownAdapter());
27
+ baseRegistry.register(new BMADFormatAdapter());
28
+ baseRegistry.register(new SpecKitFormatAdapter());
29
+ baseRegistry.register(new GenericMarkdownAdapter());
30
+ // Export the registry instance as default export
31
+ export { baseRegistry as registry };
@@ -0,0 +1,22 @@
1
+ import type { APSPlan, ValidationResult } from '@eddacraft/anvil-core';
2
+ import type { ConversionResult, ExternalSpec, SpecContext } from '../common/types.js';
3
+ import { BaseAdapter } from '../common/types.js';
4
+ export declare class SpecKitExportAdapter extends BaseAdapter {
5
+ readonly name = "speckit-export";
6
+ readonly version = "1.0.0";
7
+ readonly supportedFormats: readonly ["speckit", "spec.md"];
8
+ generateSpec(_intent: string, _context: SpecContext): Promise<APSPlan>;
9
+ validateSpec(spec: APSPlan): Promise<ValidationResult>;
10
+ convertToAPS(_spec: ExternalSpec): Promise<ConversionResult<APSPlan>>;
11
+ convertFromAPS(spec: APSPlan): Promise<ConversionResult<ExternalSpec>>;
12
+ private generateSpecMarkdown;
13
+ private generatePlanMarkdown;
14
+ private generateTasksMarkdown;
15
+ private groupChangesByType;
16
+ private formatChangeType;
17
+ private getFileExtension;
18
+ private filterCustomMetadata;
19
+ private convertChangesToSteps;
20
+ private convertChangesToTasks;
21
+ }
22
+ //# sourceMappingURL=export.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../src/speckit/export.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAU,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC/E,OAAO,KAAK,EAEV,gBAAgB,EAEhB,YAAY,EACZ,WAAW,EACZ,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,qBAAa,oBAAqB,SAAQ,WAAW;IACnD,QAAQ,CAAC,IAAI,oBAAoB;IACjC,QAAQ,CAAC,OAAO,WAAW;IAC3B,QAAQ,CAAC,gBAAgB,kCAAmC;IAEtD,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;IAItE,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAyCtD,YAAY,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAYrE,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC;IAyC5E,OAAO,CAAC,oBAAoB;IA0F5B,OAAO,CAAC,oBAAoB;IA0C5B,OAAO,CAAC,qBAAqB;IAsE7B,OAAO,CAAC,kBAAkB;IAa1B,OAAO,CAAC,gBAAgB;IAexB,OAAO,CAAC,gBAAgB;IAwCxB,OAAO,CAAC,oBAAoB;IAa5B,OAAO,CAAC,qBAAqB;IAmC7B,OAAO,CAAC,qBAAqB;CAyD9B"}
@@ -0,0 +1,384 @@
1
+ import { BaseAdapter } from '../common/types.js';
2
+ export class SpecKitExportAdapter extends BaseAdapter {
3
+ name = 'speckit-export';
4
+ version = '1.0.0';
5
+ supportedFormats = ['speckit', 'spec.md'];
6
+ async generateSpec(_intent, _context) {
7
+ throw new Error('Use speckit-import adapter for generating new specs');
8
+ }
9
+ async validateSpec(spec) {
10
+ const issues = [];
11
+ if (!spec.intent || spec.intent.length < 10) {
12
+ issues.push({
13
+ path: 'intent',
14
+ message: 'Intent is required and must be at least 10 characters',
15
+ code: 'INVALID_INTENT',
16
+ severity: 'error',
17
+ });
18
+ }
19
+ if (spec.proposed_changes.length === 0) {
20
+ issues.push({
21
+ path: 'proposed_changes',
22
+ message: 'No changes to export',
23
+ code: 'EMPTY_CHANGES',
24
+ severity: 'warning',
25
+ });
26
+ }
27
+ // Only errors make validation invalid, warnings are ok
28
+ const hasErrors = issues.some((issue) => issue.severity === 'error');
29
+ return {
30
+ valid: !hasErrors,
31
+ data: spec,
32
+ issues: issues.length > 0 ? issues : undefined,
33
+ summary: hasErrors
34
+ ? `Found ${issues.filter((i) => i.severity === 'error').length} error(s)`
35
+ : issues.length > 0
36
+ ? `Validation passed with ${issues.length} warning(s)`
37
+ : 'Validation passed',
38
+ };
39
+ }
40
+ async convertToAPS(_spec) {
41
+ return {
42
+ success: false,
43
+ errors: [
44
+ {
45
+ code: 'NOT_IMPLEMENTED',
46
+ message: 'Import from SpecKit format is handled by speckit-import adapter',
47
+ },
48
+ ],
49
+ };
50
+ }
51
+ async convertFromAPS(spec) {
52
+ const errors = [];
53
+ const warnings = [];
54
+ try {
55
+ const specContent = this.generateSpecMarkdown(spec);
56
+ const planContent = this.generatePlanMarkdown(spec);
57
+ const tasksContent = this.generateTasksMarkdown(spec);
58
+ const result = {
59
+ format: 'speckit',
60
+ version: '1.0.0',
61
+ content: {
62
+ specContent,
63
+ planContent,
64
+ tasksContent,
65
+ metadata: spec.metadata,
66
+ },
67
+ metadata: {
68
+ generated_at: new Date().toISOString(),
69
+ generator: this.name,
70
+ generator_version: this.version,
71
+ aps_id: spec.id,
72
+ aps_hash: spec.hash,
73
+ },
74
+ };
75
+ return {
76
+ success: true,
77
+ data: result,
78
+ warnings: warnings.length > 0 ? warnings : undefined,
79
+ };
80
+ }
81
+ catch (error) {
82
+ errors.push({
83
+ code: 'EXPORT_ERROR',
84
+ message: error instanceof Error ? error.message : 'Failed to export to SpecKit format',
85
+ });
86
+ return { success: false, errors };
87
+ }
88
+ }
89
+ generateSpecMarkdown(spec) {
90
+ const sections = [];
91
+ sections.push(`# Specification`);
92
+ sections.push('');
93
+ sections.push(`## Intent`);
94
+ sections.push('');
95
+ sections.push(spec.intent);
96
+ sections.push('');
97
+ if (spec.metadata?.['overview']) {
98
+ sections.push(`## Overview`);
99
+ sections.push('');
100
+ sections.push(spec.metadata['overview']);
101
+ sections.push('');
102
+ }
103
+ if (spec.metadata?.['goals'] && Array.isArray(spec.metadata['goals'])) {
104
+ sections.push(`## Goals`);
105
+ sections.push('');
106
+ for (const goal of spec.metadata['goals']) {
107
+ sections.push(`- ${goal}`);
108
+ }
109
+ sections.push('');
110
+ }
111
+ if (spec.metadata?.['requirements'] && Array.isArray(spec.metadata['requirements'])) {
112
+ sections.push(`## Requirements`);
113
+ sections.push('');
114
+ for (const req of spec.metadata['requirements']) {
115
+ sections.push(`- ${req}`);
116
+ }
117
+ sections.push('');
118
+ }
119
+ if (spec.proposed_changes.length > 0) {
120
+ sections.push(`## Changes`);
121
+ sections.push('');
122
+ const changesByType = this.groupChangesByType(spec.proposed_changes);
123
+ for (const [type, changes] of Object.entries(changesByType)) {
124
+ if (changes.length > 0) {
125
+ sections.push(`### ${this.formatChangeType(type)}`);
126
+ sections.push('');
127
+ for (const change of changes) {
128
+ sections.push(`#### ${change.description}`);
129
+ sections.push('');
130
+ if (change.path) {
131
+ sections.push(`Path: \`${change.path}\``);
132
+ sections.push('');
133
+ }
134
+ if (change.content) {
135
+ const extension = this.getFileExtension(change.path || '');
136
+ sections.push(`\`\`\`${extension}`);
137
+ sections.push(change.content);
138
+ sections.push(`\`\`\``);
139
+ sections.push('');
140
+ }
141
+ if (change.diff) {
142
+ sections.push(`\`\`\`diff`);
143
+ sections.push(change.diff);
144
+ sections.push(`\`\`\``);
145
+ sections.push('');
146
+ }
147
+ }
148
+ }
149
+ }
150
+ }
151
+ if (spec.metadata) {
152
+ const customMetadata = this.filterCustomMetadata(spec.metadata);
153
+ if (Object.keys(customMetadata).length > 0) {
154
+ sections.push(`## Metadata`);
155
+ sections.push('');
156
+ sections.push(`\`\`\`json`);
157
+ sections.push(JSON.stringify(customMetadata, null, 2));
158
+ sections.push(`\`\`\``);
159
+ sections.push('');
160
+ }
161
+ }
162
+ return sections.join('\n');
163
+ }
164
+ generatePlanMarkdown(spec) {
165
+ const sections = [];
166
+ sections.push(`# Implementation Plan`);
167
+ sections.push('');
168
+ sections.push(`Generated from APS: ${spec.id}`);
169
+ sections.push('');
170
+ sections.push(`## Summary`);
171
+ sections.push('');
172
+ sections.push(spec.intent);
173
+ sections.push('');
174
+ sections.push(`## Implementation Steps`);
175
+ sections.push('');
176
+ const steps = this.convertChangesToSteps(spec.proposed_changes);
177
+ for (let i = 0; i < steps.length; i++) {
178
+ const step = steps[i];
179
+ sections.push(`${i + 1}. **${step.title}**`);
180
+ if (step.details) {
181
+ sections.push(` - ${step.details}`);
182
+ }
183
+ if (step.dependencies.length > 0) {
184
+ sections.push(` - Dependencies: ${step.dependencies.join(', ')}`);
185
+ }
186
+ sections.push('');
187
+ }
188
+ if (spec.validations) {
189
+ sections.push(`## Validation Requirements`);
190
+ sections.push('');
191
+ sections.push(`- Required checks: ${spec.validations.required_checks.join(', ')}`);
192
+ if (spec.validations.skip_checks.length > 0) {
193
+ sections.push(`- Skipped checks: ${spec.validations.skip_checks.join(', ')}`);
194
+ }
195
+ sections.push('');
196
+ }
197
+ return sections.join('\n');
198
+ }
199
+ generateTasksMarkdown(spec) {
200
+ const sections = [];
201
+ sections.push(`# Tasks`);
202
+ sections.push('');
203
+ sections.push(`Generated from APS: ${spec.id}`);
204
+ sections.push(`Last updated: ${new Date().toISOString()}`);
205
+ sections.push('');
206
+ sections.push(`## Task List`);
207
+ sections.push('');
208
+ const tasks = this.convertChangesToTasks(spec.proposed_changes);
209
+ let completedCount = 0;
210
+ for (const task of tasks) {
211
+ const status = task.completed ? 'x' : ' ';
212
+ const statusEmoji = task.completed ? '✅' : '⏳';
213
+ sections.push(`- [${status}] ${statusEmoji} ${task.description}`);
214
+ if (task.subtasks.length > 0) {
215
+ for (const subtask of task.subtasks) {
216
+ const subStatus = subtask.completed ? 'x' : ' ';
217
+ sections.push(` - [${subStatus}] ${subtask.description}`);
218
+ }
219
+ }
220
+ if (task.completed) {
221
+ completedCount++;
222
+ }
223
+ }
224
+ sections.push('');
225
+ sections.push(`## Progress`);
226
+ sections.push('');
227
+ sections.push(`- Total tasks: ${tasks.length}`);
228
+ sections.push(`- Completed: ${completedCount}`);
229
+ sections.push(`- Remaining: ${tasks.length - completedCount}`);
230
+ sections.push(`- Progress: ${Math.round((completedCount / tasks.length) * 100)}%`);
231
+ sections.push('');
232
+ if (spec.executions && spec.executions.length > 0) {
233
+ sections.push(`## Execution History`);
234
+ sections.push('');
235
+ for (const execution of spec.executions) {
236
+ sections.push(`### ${new Date(execution.timestamp).toLocaleString()}`);
237
+ sections.push(`- Operation: ${execution.operation}`);
238
+ sections.push(`- Status: ${execution.status}`);
239
+ if (execution.executed_by) {
240
+ sections.push(`- Executed by: ${execution.executed_by}`);
241
+ }
242
+ if (execution.changes_applied && execution.changes_applied.length > 0) {
243
+ sections.push(`- Applied changes: ${execution.changes_applied.join(', ')}`);
244
+ }
245
+ if (execution.changes_failed && execution.changes_failed.length > 0) {
246
+ sections.push(`- Failed changes: ${execution.changes_failed.join(', ')}`);
247
+ }
248
+ if (execution.logs && execution.logs.length > 0) {
249
+ for (const log of execution.logs) {
250
+ sections.push(`- ${log}`);
251
+ }
252
+ }
253
+ sections.push('');
254
+ }
255
+ }
256
+ return sections.join('\n');
257
+ }
258
+ groupChangesByType(changes) {
259
+ const groups = {};
260
+ for (const change of changes) {
261
+ if (!groups[change.type]) {
262
+ groups[change.type] = [];
263
+ }
264
+ groups[change.type].push(change);
265
+ }
266
+ return groups;
267
+ }
268
+ formatChangeType(type) {
269
+ const typeMap = {
270
+ file_create: 'Files to Create',
271
+ file_update: 'Files to Update',
272
+ file_delete: 'Files to Delete',
273
+ config_update: 'Configuration Changes',
274
+ dependency_add: 'Dependencies to Add',
275
+ dependency_remove: 'Dependencies to Remove',
276
+ dependency_update: 'Dependencies to Update',
277
+ script_execute: 'Scripts to Execute',
278
+ };
279
+ return typeMap[type] || type.replace(/_/g, ' ').replace(/\b\w/g, (l) => l.toUpperCase());
280
+ }
281
+ getFileExtension(path) {
282
+ const ext = path.split('.').pop()?.toLowerCase();
283
+ const extMap = {
284
+ js: 'javascript',
285
+ ts: 'typescript',
286
+ jsx: 'javascript',
287
+ tsx: 'typescript',
288
+ py: 'python',
289
+ rb: 'ruby',
290
+ go: 'go',
291
+ rs: 'rust',
292
+ java: 'java',
293
+ cpp: 'cpp',
294
+ c: 'c',
295
+ h: 'c',
296
+ hpp: 'cpp',
297
+ cs: 'csharp',
298
+ php: 'php',
299
+ swift: 'swift',
300
+ kt: 'kotlin',
301
+ scala: 'scala',
302
+ r: 'r',
303
+ m: 'matlab',
304
+ sh: 'bash',
305
+ ps1: 'powershell',
306
+ sql: 'sql',
307
+ md: 'markdown',
308
+ yml: 'yaml',
309
+ yaml: 'yaml',
310
+ json: 'json',
311
+ xml: 'xml',
312
+ html: 'html',
313
+ css: 'css',
314
+ scss: 'scss',
315
+ less: 'less',
316
+ };
317
+ return extMap[ext || ''] || ext || 'text';
318
+ }
319
+ filterCustomMetadata(metadata) {
320
+ const standardKeys = ['overview', 'goals', 'requirements', 'source_format'];
321
+ const custom = {};
322
+ for (const [key, value] of Object.entries(metadata)) {
323
+ if (!standardKeys.includes(key)) {
324
+ custom[key] = value;
325
+ }
326
+ }
327
+ return custom;
328
+ }
329
+ convertChangesToSteps(changes) {
330
+ const steps = [];
331
+ const pathDependencies = new Map();
332
+ for (let i = 0; i < changes.length; i++) {
333
+ const change = changes[i];
334
+ const deps = [];
335
+ if (change.path) {
336
+ const previousIndex = pathDependencies.get(change.path);
337
+ if (previousIndex !== undefined) {
338
+ deps.push(`Step ${previousIndex + 1}`);
339
+ }
340
+ pathDependencies.set(change.path, i);
341
+ }
342
+ steps.push({
343
+ title: change.description,
344
+ details: change.path ? `Target: ${change.path}` : '',
345
+ dependencies: deps,
346
+ });
347
+ }
348
+ return steps;
349
+ }
350
+ convertChangesToTasks(changes) {
351
+ const tasks = [];
352
+ for (const change of changes) {
353
+ const task = {
354
+ description: change.description,
355
+ completed: false,
356
+ subtasks: [],
357
+ };
358
+ if (change.type === 'file_create' || change.type === 'file_update') {
359
+ task.subtasks.push({
360
+ description: `Edit ${change.path || 'file'}`,
361
+ completed: false,
362
+ });
363
+ if (this.config.preserveMetadata) {
364
+ task.subtasks.push({
365
+ description: 'Add file metadata',
366
+ completed: false,
367
+ });
368
+ }
369
+ }
370
+ if (change.type.startsWith('dependency_')) {
371
+ task.subtasks.push({
372
+ description: 'Update package manifest',
373
+ completed: false,
374
+ });
375
+ task.subtasks.push({
376
+ description: 'Run package manager',
377
+ completed: false,
378
+ });
379
+ }
380
+ tasks.push(task);
381
+ }
382
+ return tasks;
383
+ }
384
+ }