@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,361 @@
1
+ /**
2
+ * SpecKit Import Adapter v2 - Official Format
3
+ *
4
+ * Supports official GitHub spec-kit format:
5
+ * - spec.md: Requirements and user scenarios (WHAT and WHY)
6
+ * - plan.md: Technical implementation details (HOW)
7
+ * - tasks.md: Executable task breakdown
8
+ *
9
+ * Can import individual files or complete feature directories
10
+ */
11
+ import { createPlan } from '@eddacraft/anvil-core';
12
+ import { BaseAdapter } from '../common/types.js';
13
+ import { SpecParser } from './parsers/spec-parser.js';
14
+ import { PlanParser } from './parsers/plan-parser.js';
15
+ import { TasksParser } from './parsers/tasks-parser.js';
16
+ export class SpecKitImportAdapterV2 extends BaseAdapter {
17
+ name = 'speckit-import-v2';
18
+ version = '2.0.0';
19
+ supportedFormats = ['speckit', 'spec-kit', 'spec.md', 'plan.md', 'tasks.md'];
20
+ specParser;
21
+ planParser;
22
+ tasksParser;
23
+ constructor(config = {}) {
24
+ super(config);
25
+ this.specParser = new SpecParser();
26
+ this.planParser = new PlanParser();
27
+ this.tasksParser = new TasksParser();
28
+ }
29
+ async generateSpec(intent, context) {
30
+ const provenance = {
31
+ timestamp: new Date().toISOString(),
32
+ source: 'cli',
33
+ version: this.version,
34
+ author: context.author,
35
+ repository: context.repositoryPath,
36
+ branch: context.branch,
37
+ commit: context.commit,
38
+ };
39
+ // Generate a simple spec.md file creation change
40
+ const changes = [
41
+ {
42
+ type: 'file_create',
43
+ path: 'specs/new-feature/spec.md',
44
+ description: 'Create specification file following spec-kit format',
45
+ content: this.generateSpecTemplate(intent),
46
+ },
47
+ ];
48
+ const planId = `aps-${Date.now().toString(16).substring(0, 8)}`;
49
+ return {
50
+ ...createPlan({
51
+ id: planId,
52
+ intent,
53
+ provenance,
54
+ changes,
55
+ }),
56
+ schema_version: '0.1.0',
57
+ hash: '0'.repeat(64),
58
+ };
59
+ }
60
+ async validateSpec(spec) {
61
+ const errors = [];
62
+ const warnings = [];
63
+ if (spec.proposed_changes.length === 0) {
64
+ warnings.push({
65
+ field: 'proposed_changes',
66
+ message: 'No changes specified in the plan',
67
+ });
68
+ }
69
+ const issues = [
70
+ ...errors.map((e) => ({
71
+ path: e.field,
72
+ message: e.message,
73
+ code: 'VALIDATION_ERROR',
74
+ severity: 'error',
75
+ })),
76
+ ...warnings.map((w) => ({
77
+ path: w.field,
78
+ message: w.message,
79
+ code: 'VALIDATION_WARNING',
80
+ severity: 'warning',
81
+ })),
82
+ ];
83
+ return {
84
+ valid: errors.length === 0,
85
+ data: spec,
86
+ issues: issues.length > 0 ? issues : undefined,
87
+ summary: errors.length === 0 ? 'Validation passed' : `Found ${errors.length} error(s)`,
88
+ };
89
+ }
90
+ async convertToAPS(spec) {
91
+ const errors = [];
92
+ const warnings = [];
93
+ if (!this.canImport(spec.format)) {
94
+ return {
95
+ success: false,
96
+ errors: [
97
+ {
98
+ code: 'UNSUPPORTED_FORMAT',
99
+ message: `Format '${spec.format}' is not supported by this adapter`,
100
+ },
101
+ ],
102
+ };
103
+ }
104
+ try {
105
+ const docs = spec.content;
106
+ // Parse all available documents
107
+ if (docs.spec?.content) {
108
+ docs.spec.parsed = this.specParser.parseSpec(docs.spec.content);
109
+ }
110
+ if (docs.plan?.content) {
111
+ docs.plan.parsed = this.planParser.parsePlan(docs.plan.content);
112
+ }
113
+ if (docs.tasks?.content) {
114
+ docs.tasks.parsed = this.tasksParser.parseTasks(docs.tasks.content);
115
+ }
116
+ // At minimum, we need spec.md
117
+ if (!docs.spec?.parsed) {
118
+ return {
119
+ success: false,
120
+ errors: [
121
+ {
122
+ code: 'MISSING_SPEC',
123
+ message: 'spec.md content is required',
124
+ },
125
+ ],
126
+ };
127
+ }
128
+ // Build APS plan from parsed documents
129
+ const apsResult = this.buildAPSFromDocs(docs, spec.metadata, errors, warnings);
130
+ if (errors.length > 0) {
131
+ return { success: false, errors, warnings };
132
+ }
133
+ return {
134
+ success: true,
135
+ data: apsResult,
136
+ warnings: warnings.length > 0 ? warnings : undefined,
137
+ };
138
+ }
139
+ catch (error) {
140
+ errors.push({
141
+ code: 'CONVERSION_ERROR',
142
+ message: error instanceof Error ? error.message : 'Unknown conversion error',
143
+ });
144
+ return { success: false, errors };
145
+ }
146
+ }
147
+ async convertFromAPS(_spec) {
148
+ return {
149
+ success: false,
150
+ errors: [
151
+ {
152
+ code: 'NOT_IMPLEMENTED',
153
+ message: 'Export to SpecKit format is handled by speckit-export adapter',
154
+ },
155
+ ],
156
+ };
157
+ }
158
+ buildAPSFromDocs(docs, metadata, errors, warnings) {
159
+ const parsedSpec = docs.spec.parsed;
160
+ const parsedPlan = docs.plan?.parsed;
161
+ const parsedTasks = docs.tasks?.parsed;
162
+ // Build intent from spec user scenarios and metadata
163
+ const intent = this.buildIntent(parsedSpec);
164
+ // Build proposed changes from user scenarios + plan details + tasks
165
+ const changes = this.buildProposedChanges(parsedSpec, parsedPlan, parsedTasks, warnings);
166
+ // Build provenance
167
+ const provenance = {
168
+ timestamp: metadata?.['timestamp'] || new Date().toISOString(),
169
+ source: 'cli',
170
+ version: this.version,
171
+ author: metadata?.['author'] || parsedSpec.metadata.branch,
172
+ repository: metadata?.['repository'],
173
+ branch: parsedSpec.metadata.branch,
174
+ commit: metadata?.['commit'],
175
+ };
176
+ const planId = `aps-${Date.now().toString(16).substring(0, 8)}`;
177
+ return {
178
+ ...createPlan({
179
+ id: planId,
180
+ intent,
181
+ provenance,
182
+ changes,
183
+ }),
184
+ schema_version: '0.1.0',
185
+ hash: '0'.repeat(64),
186
+ metadata: {
187
+ source_format: 'speckit-v2',
188
+ feature: parsedSpec.metadata.feature,
189
+ // From spec.md
190
+ userScenarios: parsedSpec.userScenarios,
191
+ requirements: parsedSpec.requirements,
192
+ successCriteria: parsedSpec.successCriteria,
193
+ clarifications: parsedSpec.clarifications,
194
+ // From plan.md
195
+ technicalContext: parsedPlan?.technicalContext,
196
+ constitutionCheck: parsedPlan?.constitutionCheck,
197
+ projectStructure: parsedPlan?.projectStructure,
198
+ implementationDetails: parsedPlan?.implementationDetails
199
+ ? Object.fromEntries(parsedPlan.implementationDetails)
200
+ : undefined,
201
+ complexityDecisions: parsedPlan?.complexityDecisions,
202
+ // From tasks.md
203
+ phases: parsedTasks?.phases,
204
+ taskDependencies: parsedTasks?.dependencies,
205
+ implementationStrategies: parsedTasks?.strategies,
206
+ },
207
+ };
208
+ }
209
+ buildIntent(parsedSpec) {
210
+ // Build intent from feature name and P1 user scenarios
211
+ const feature = parsedSpec.metadata.feature || 'Feature';
212
+ const p1Scenarios = parsedSpec.userScenarios.filter((s) => s.priority === 'P1');
213
+ if (p1Scenarios.length === 0) {
214
+ return `Implement ${feature}`;
215
+ }
216
+ const scenarioDescriptions = p1Scenarios
217
+ .map((s) => `${s.asA} wants to ${s.iWantTo} so that ${s.soThat}`)
218
+ .join('. ');
219
+ return `${feature}: ${scenarioDescriptions}`.substring(0, 500);
220
+ }
221
+ buildProposedChanges(parsedSpec, parsedPlan, parsedTasks, warnings) {
222
+ const changes = [];
223
+ // Strategy: Convert user scenarios to proposed changes
224
+ // Each scenario represents a feature to implement
225
+ for (const scenario of parsedSpec.userScenarios) {
226
+ // Create a change for each P1/P2 user scenario
227
+ if (scenario.priority === 'P1' || scenario.priority === 'P2') {
228
+ const change = {
229
+ type: 'file_create',
230
+ path: this.inferPathFromScenario(scenario, parsedPlan),
231
+ description: `Implement ${scenario.title}: ${scenario.iWantTo}`,
232
+ metadata: {
233
+ priority: scenario.priority,
234
+ userStory: {
235
+ asA: scenario.asA,
236
+ iWantTo: scenario.iWantTo,
237
+ soThat: scenario.soThat,
238
+ },
239
+ acceptanceScenarios: scenario.acceptanceScenarios,
240
+ edgeCases: scenario.edgeCases,
241
+ },
242
+ };
243
+ changes.push(change);
244
+ }
245
+ }
246
+ // If we have plan details, add implementation-specific changes
247
+ if (parsedPlan) {
248
+ // Add database migrations if mentioned
249
+ if (parsedPlan.implementationDetails.has('Database Schema')) {
250
+ changes.push({
251
+ type: 'file_create',
252
+ path: 'database/migrations/',
253
+ description: 'Create database migrations for required tables',
254
+ metadata: {
255
+ section: 'Database Schema',
256
+ },
257
+ });
258
+ }
259
+ // Add API endpoints if mentioned
260
+ if (parsedPlan.implementationDetails.has('API Endpoints')) {
261
+ changes.push({
262
+ type: 'file_create',
263
+ path: this.inferAPIPath(parsedPlan),
264
+ description: 'Implement API endpoints',
265
+ metadata: {
266
+ section: 'API Endpoints',
267
+ },
268
+ });
269
+ }
270
+ }
271
+ // Add dependency installation if we have technical context
272
+ if (parsedPlan?.technicalContext.dependencies &&
273
+ parsedPlan.technicalContext.dependencies.length > 0) {
274
+ changes.push({
275
+ type: 'dependency_add',
276
+ path: 'package.json',
277
+ description: `Install dependencies: ${parsedPlan.technicalContext.dependencies.slice(0, 3).join(', ')}${parsedPlan.technicalContext.dependencies.length > 3 ? '...' : ''}`,
278
+ metadata: {
279
+ dependencies: parsedPlan.technicalContext.dependencies,
280
+ },
281
+ });
282
+ }
283
+ if (changes.length === 0) {
284
+ warnings.push({
285
+ code: 'NO_CHANGES',
286
+ message: 'No actionable changes could be derived from spec',
287
+ });
288
+ }
289
+ return changes;
290
+ }
291
+ inferPathFromScenario(scenario, plan) {
292
+ const title = scenario.title.toLowerCase().replace(/\s+/g, '-');
293
+ // Use plan structure if available
294
+ if (plan?.projectStructure.sourceCode) {
295
+ // Try to extract common patterns
296
+ if (plan.projectStructure.sourceCode.includes('src/modules/')) {
297
+ return `src/modules/${title}/`;
298
+ }
299
+ if (plan.projectStructure.sourceCode.includes('src/features/')) {
300
+ return `src/features/${title}/`;
301
+ }
302
+ }
303
+ return `src/${title}/`;
304
+ }
305
+ inferAPIPath(plan) {
306
+ if (plan.projectStructure.sourceCode?.includes('controllers')) {
307
+ return 'src/controllers/';
308
+ }
309
+ if (plan.projectStructure.sourceCode?.includes('routes')) {
310
+ return 'src/routes/';
311
+ }
312
+ return 'src/api/';
313
+ }
314
+ generateSpecTemplate(intent) {
315
+ return `# Feature: ${intent}
316
+
317
+ **Branch**: \`feature/xxx-feature-name\`
318
+ **Date**: ${new Date().toISOString().split('T')[0]}
319
+ **Status**: Draft
320
+
321
+ ## User Scenarios & Testing
322
+
323
+ ### P1: [User Scenario Title]
324
+ **As a** [user type]
325
+ **I want to** [action]
326
+ **So that** [benefit]
327
+
328
+ **Acceptance Scenarios:**
329
+ - [Scenario 1]
330
+ - [Scenario 2]
331
+
332
+ **Edge Cases:**
333
+ - [Edge case 1]
334
+ - [NEEDS CLARIFICATION: Question?]
335
+
336
+ ## Requirements
337
+
338
+ ### Functional Requirements
339
+
340
+ **FR-001**: System MUST [requirement]
341
+ **FR-002**: [NEEDS CLARIFICATION: What should happen when...]
342
+
343
+ ### Key Entities
344
+
345
+ **EntityName**
346
+ - Represents: [What this entity represents]
347
+ - Key Attributes: [attribute1, attribute2]
348
+ - Relationships: [relationships to other entities]
349
+
350
+ ## Success Criteria
351
+
352
+ ### Quantitative Metrics
353
+ - [Metric 1]
354
+ - [Metric 2]
355
+
356
+ ### Qualitative Metrics
357
+ - [Metric 1]
358
+ - [Metric 2]
359
+ `;
360
+ }
361
+ }
@@ -0,0 +1,16 @@
1
+ import { type APSPlan } from '@eddacraft/anvil-core';
2
+ import type { AdapterConfig, ConversionResult, ExternalSpec, SpecContext } from '../common/types.js';
3
+ import { BaseAdapter } from '../common/types.js';
4
+ export declare class SpecKitImportAdapter extends BaseAdapter {
5
+ readonly name = "speckit-import";
6
+ readonly version = "1.0.0";
7
+ readonly supportedFormats: readonly ["speckit", "spec.md"];
8
+ private parser;
9
+ constructor(config?: AdapterConfig);
10
+ generateSpec(intent: string, context: SpecContext): Promise<APSPlan>;
11
+ validateSpec(spec: APSPlan): Promise<import('@eddacraft/anvil-core').ValidationResult>;
12
+ convertToAPS(spec: ExternalSpec): Promise<ConversionResult<APSPlan>>;
13
+ convertFromAPS(_spec: APSPlan): Promise<ConversionResult<ExternalSpec>>;
14
+ private convertChangesToAPS;
15
+ }
16
+ //# sourceMappingURL=import.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import.d.ts","sourceRoot":"","sources":["../../src/speckit/import.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,OAAO,EAAgC,MAAM,uBAAuB,CAAC;AAC/F,OAAO,KAAK,EACV,aAAa,EAEb,gBAAgB,EAEhB,YAAY,EACZ,WAAW,EACZ,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAUjD,qBAAa,oBAAqB,SAAQ,WAAW;IACnD,QAAQ,CAAC,IAAI,oBAAoB;IACjC,QAAQ,CAAC,OAAO,WAAW;IAC3B,QAAQ,CAAC,gBAAgB,kCAAmC;IAE5D,OAAO,CAAC,MAAM,CAAgB;gBAElB,MAAM,GAAE,aAAkB;IAKhC,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;IAmCpE,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,uBAAuB,EAAE,gBAAgB,CAAC;IAwDtF,YAAY,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAoGpE,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC;IAY7E,OAAO,CAAC,mBAAmB;CAsE5B"}
@@ -0,0 +1,247 @@
1
+ import { createPlan } from '@eddacraft/anvil-core';
2
+ import { BaseAdapter } from '../common/types.js';
3
+ import { SpecKitParser } from './parser.js';
4
+ export class SpecKitImportAdapter extends BaseAdapter {
5
+ name = 'speckit-import';
6
+ version = '1.0.0';
7
+ supportedFormats = ['speckit', 'spec.md'];
8
+ parser;
9
+ constructor(config = {}) {
10
+ super(config);
11
+ this.parser = new SpecKitParser();
12
+ }
13
+ async generateSpec(intent, context) {
14
+ const provenance = {
15
+ timestamp: new Date().toISOString(),
16
+ source: 'cli',
17
+ version: this.version,
18
+ author: context.author,
19
+ repository: context.repositoryPath,
20
+ branch: context.branch,
21
+ commit: context.commit,
22
+ };
23
+ const changes = [
24
+ {
25
+ type: 'file_create',
26
+ path: 'spec.md',
27
+ description: 'Create initial specification file',
28
+ content: `# Specification\n\n## Intent\n\n${intent}\n\n## Overview\n\n[Describe the overall approach]\n\n## Requirements\n\n- [List prerequisites]\n\n## Changes\n\n- [List proposed changes]\n`,
29
+ },
30
+ ];
31
+ const planId = `aps-${Date.now().toString(16).substring(0, 8)}`;
32
+ const plan = {
33
+ ...createPlan({
34
+ id: planId,
35
+ intent,
36
+ provenance,
37
+ changes,
38
+ }),
39
+ schema_version: '0.1.0',
40
+ hash: '0'.repeat(64), // Placeholder hash
41
+ };
42
+ return plan;
43
+ }
44
+ async validateSpec(spec) {
45
+ const errors = [];
46
+ const warnings = [];
47
+ if (spec.proposed_changes.length === 0) {
48
+ warnings.push({
49
+ field: 'proposed_changes',
50
+ message: 'No changes specified in the plan',
51
+ });
52
+ }
53
+ for (let i = 0; i < spec.proposed_changes.length; i++) {
54
+ const change = spec.proposed_changes[i];
55
+ if (!change.description || change.description.length < 10) {
56
+ warnings.push({
57
+ field: `proposed_changes[${i}].description`,
58
+ message: 'Change description is too short or missing',
59
+ });
60
+ }
61
+ if (!change.path && change.type !== 'script_execute') {
62
+ errors.push({
63
+ field: `proposed_changes[${i}].path`,
64
+ message: `Path is required for change type '${change.type}'`,
65
+ });
66
+ }
67
+ }
68
+ const issues = [
69
+ ...errors.map((e) => ({
70
+ path: e.field,
71
+ message: e.message,
72
+ code: 'VALIDATION_ERROR',
73
+ severity: 'error',
74
+ })),
75
+ ...warnings.map((w) => ({
76
+ path: w.field,
77
+ message: w.message,
78
+ code: 'VALIDATION_WARNING',
79
+ severity: 'warning',
80
+ })),
81
+ ];
82
+ return {
83
+ valid: errors.length === 0,
84
+ data: spec,
85
+ issues: issues.length > 0 ? issues : undefined,
86
+ summary: errors.length === 0 ? 'Validation passed' : `Found ${errors.length} error(s)`,
87
+ };
88
+ }
89
+ async convertToAPS(spec) {
90
+ const errors = [];
91
+ const warnings = [];
92
+ if (!this.canImport(spec.format)) {
93
+ return {
94
+ success: false,
95
+ errors: [
96
+ {
97
+ code: 'UNSUPPORTED_FORMAT',
98
+ message: `Format '${spec.format}' is not supported by this adapter`,
99
+ },
100
+ ],
101
+ };
102
+ }
103
+ try {
104
+ const specKitSpec = spec.content;
105
+ if (!specKitSpec.specContent) {
106
+ errors.push({
107
+ code: 'MISSING_SPEC_CONTENT',
108
+ message: 'spec.md content is required',
109
+ });
110
+ return { success: false, errors };
111
+ }
112
+ const parsed = this.parser.parseSpecMarkdown(specKitSpec.specContent);
113
+ if (!parsed.intent && !parsed.overview) {
114
+ errors.push({
115
+ code: 'MISSING_INTENT',
116
+ message: 'No intent or overview section found in spec.md',
117
+ });
118
+ }
119
+ else if (!parsed.intent) {
120
+ warnings.push({
121
+ code: 'MISSING_INTENT',
122
+ message: 'No intent section found, using overview as fallback',
123
+ });
124
+ }
125
+ const changes = this.convertChangesToAPS(parsed.changes || [], errors, warnings);
126
+ if (errors.length > 0) {
127
+ return { success: false, errors, warnings };
128
+ }
129
+ const intent = parsed.intent || parsed.overview || 'Specification from SpecKit';
130
+ const provenance = {
131
+ timestamp: spec.metadata?.['timestamp'] || new Date().toISOString(),
132
+ source: 'cli',
133
+ version: this.version,
134
+ author: spec.metadata?.['author'],
135
+ repository: spec.metadata?.['repository'],
136
+ branch: spec.metadata?.['branch'],
137
+ commit: spec.metadata?.['commit'],
138
+ };
139
+ const planId = `aps-${Date.now().toString(16).substring(0, 8)}`;
140
+ try {
141
+ const plan = {
142
+ ...createPlan({
143
+ id: planId,
144
+ intent: intent.substring(0, 500),
145
+ provenance,
146
+ changes,
147
+ }),
148
+ schema_version: '0.1.0',
149
+ hash: '0'.repeat(64), // Placeholder hash
150
+ metadata: {
151
+ ...parsed.metadata,
152
+ source_format: 'speckit',
153
+ goals: parsed.goals,
154
+ requirements: parsed.requirements,
155
+ overview: parsed.overview,
156
+ },
157
+ };
158
+ return {
159
+ success: true,
160
+ data: plan,
161
+ warnings: warnings.length > 0 ? warnings : undefined,
162
+ };
163
+ }
164
+ catch (error) {
165
+ errors.push({
166
+ code: 'APS_CREATION_FAILED',
167
+ message: error instanceof Error ? error.message : 'Failed to create APS plan',
168
+ });
169
+ return { success: false, errors };
170
+ }
171
+ }
172
+ catch (error) {
173
+ errors.push({
174
+ code: 'CONVERSION_ERROR',
175
+ message: error instanceof Error ? error.message : 'Unknown conversion error',
176
+ });
177
+ return { success: false, errors };
178
+ }
179
+ }
180
+ async convertFromAPS(_spec) {
181
+ return {
182
+ success: false,
183
+ errors: [
184
+ {
185
+ code: 'NOT_IMPLEMENTED',
186
+ message: 'Export to SpecKit format is handled by speckit-export adapter',
187
+ },
188
+ ],
189
+ };
190
+ }
191
+ convertChangesToAPS(changes, errors, warnings) {
192
+ const apsChanges = [];
193
+ for (let i = 0; i < changes.length; i++) {
194
+ const change = changes[i];
195
+ if (!change.description) {
196
+ warnings.push({
197
+ code: 'EMPTY_DESCRIPTION',
198
+ message: `Change ${i + 1} has no description`,
199
+ path: `changes[${i}]`,
200
+ });
201
+ continue;
202
+ }
203
+ const validTypes = [
204
+ 'file_create',
205
+ 'file_update',
206
+ 'file_delete',
207
+ 'config_update',
208
+ 'dependency_add',
209
+ 'dependency_remove',
210
+ 'dependency_update',
211
+ 'script_execute',
212
+ ];
213
+ if (!validTypes.includes(change.type)) {
214
+ warnings.push({
215
+ code: 'UNKNOWN_CHANGE_TYPE',
216
+ message: `Unknown change type '${change.type}', defaulting to 'script_execute'`,
217
+ path: `changes[${i}].type`,
218
+ });
219
+ change.type = 'script_execute';
220
+ }
221
+ const apsChange = {
222
+ type: change.type,
223
+ path: change.path || '',
224
+ description: change.description,
225
+ };
226
+ if (change.content) {
227
+ apsChange.content = change.content;
228
+ }
229
+ if (!apsChange.path && apsChange.type !== 'script_execute') {
230
+ warnings.push({
231
+ code: 'MISSING_PATH',
232
+ message: `Path not specified for ${apsChange.type}, using placeholder`,
233
+ path: `changes[${i}].path`,
234
+ });
235
+ apsChange.path = '<path-to-be-specified>';
236
+ }
237
+ apsChanges.push(apsChange);
238
+ }
239
+ if (apsChanges.length === 0) {
240
+ warnings.push({
241
+ code: 'NO_CHANGES',
242
+ message: 'No valid changes found in specification',
243
+ });
244
+ }
245
+ return apsChanges;
246
+ }
247
+ }
@@ -0,0 +1,5 @@
1
+ export { SpecKitImportAdapter } from './import.js';
2
+ export { SpecKitExportAdapter } from './export.js';
3
+ export { SpecKitParser } from './parser.js';
4
+ export { SpecKitFormatAdapter, createSpecKitAdapter } from './format-adapter.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/speckit/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { SpecKitImportAdapter } from './import.js';
2
+ export { SpecKitExportAdapter } from './export.js';
3
+ export { SpecKitParser } from './parser.js';
4
+ export { SpecKitFormatAdapter, createSpecKitAdapter } from './format-adapter.js';
@@ -0,0 +1,28 @@
1
+ interface ParsedSpecKit {
2
+ intent?: string;
3
+ overview?: string;
4
+ goals?: string[];
5
+ requirements?: string[];
6
+ changes?: Array<{
7
+ type: string;
8
+ description: string;
9
+ path?: string;
10
+ content?: string;
11
+ }>;
12
+ metadata?: Record<string, unknown>;
13
+ }
14
+ export declare class SpecKitParser {
15
+ private static readonly SPEC_SECTIONS;
16
+ /** Maximum input size for SpecKit parsing (2MB) */
17
+ private static readonly MAX_INPUT_SIZE;
18
+ parseSpecMarkdown(content: string): ParsedSpecKit;
19
+ private parseMarkdownSections;
20
+ private extractSectionData;
21
+ private extractParagraphText;
22
+ private parseListItems;
23
+ private parseChanges;
24
+ private parseChangeSection;
25
+ private parseChangeFromListItem;
26
+ }
27
+ export {};
28
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/speckit/parser.ts"],"names":[],"mappings":"AASA,UAAU,aAAa;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,OAAO,CAAC,EAAE,KAAK,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAeD,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAM1B;IAEX,mDAAmD;IACnD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAmB;IAEzD,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa;IAgBjD,OAAO,CAAC,qBAAqB;IAgD7B,OAAO,CAAC,kBAAkB;IA6C1B,OAAO,CAAC,oBAAoB;IA0B5B,OAAO,CAAC,cAAc;IA0BtB,OAAO,CAAC,YAAY;IAsDpB,OAAO,CAAC,kBAAkB;IA+C1B,OAAO,CAAC,uBAAuB;CAwChC"}