@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,104 @@
1
+ /**
2
+ * SpecKit Format Adapter
3
+ *
4
+ * FormatAdapter implementation for GitHub spec-kit format.
5
+ * Handles simple SpecKit specification documents with Intent, Overview, Goals,
6
+ * Requirements, and Changes sections.
7
+ *
8
+ * Uses SpecKitParser to parse markdown with ## sections and convert to/from APS format.
9
+ */
10
+ import { type APSPlan, type ValidationResult } from '@eddacraft/anvil-core';
11
+ import { BaseFormatAdapter, type AdapterMetadata, type DetectionResult, type ParseResult, type SerializeResult, type ParseContext, type AdapterOptions, type PathDetectionHint } from '../base/types.js';
12
+ /**
13
+ * SpecKit FormatAdapter implementation
14
+ *
15
+ * Converts between SpecKit format documents and APS plans.
16
+ */
17
+ export declare class SpecKitFormatAdapter extends BaseFormatAdapter {
18
+ readonly metadata: AdapterMetadata;
19
+ private parser;
20
+ constructor(options?: AdapterOptions);
21
+ /**
22
+ * Detect if content is SpecKit format
23
+ *
24
+ * Uses confidence scoring based on multiple indicators:
25
+ * - Specification header (20 points)
26
+ * - Intent section (15 points)
27
+ * - Overview section (10 points)
28
+ * - Goals section (10 points)
29
+ * - Requirements section (10 points)
30
+ * - Changes section (20 points)
31
+ * - Files to Create/Update sections (10 points)
32
+ * - Code blocks (5 points)
33
+ *
34
+ * @param content - Document content to analyze
35
+ * @returns Detection result with confidence score
36
+ */
37
+ detect(content: string): DetectionResult;
38
+ /**
39
+ * Detect with file path hints for improved accuracy
40
+ *
41
+ * Uses sibling file information (e.g., AGENTS.md) and content
42
+ * namespace patterns (e.g., `speckit.*`) to boost detection.
43
+ *
44
+ * @param content - Document content to analyze
45
+ * @param hint - Path and directory information
46
+ * @returns Detection result with confidence score
47
+ */
48
+ detectWithPath(content: string, hint: PathDetectionHint): DetectionResult;
49
+ /**
50
+ * Parse SpecKit content to APS plan
51
+ *
52
+ * @param content - SpecKit markdown content
53
+ * @param context - Parse context for provenance
54
+ * @param options - Adapter options
55
+ * @returns Parse result with APS plan
56
+ */
57
+ parse(content: string, context?: ParseContext, _options?: AdapterOptions): Promise<ParseResult>;
58
+ /**
59
+ * Infer APS change type from SpecKit change type
60
+ */
61
+ private inferChangeType;
62
+ /**
63
+ * Infer file path from change description
64
+ */
65
+ private inferPathFromDescription;
66
+ /**
67
+ * Serialize APS plan to SpecKit format
68
+ *
69
+ * @param plan - APS plan to serialize
70
+ * @param options - Adapter options
71
+ * @returns Serialize result with SpecKit markdown
72
+ */
73
+ serialize(plan: APSPlan, _options?: AdapterOptions): Promise<SerializeResult>;
74
+ /**
75
+ * Validate SpecKit content
76
+ *
77
+ * Checks for required SpecKit elements without full conversion.
78
+ *
79
+ * @param content - SpecKit content to validate
80
+ * @param options - Validation options
81
+ * @returns Validation result
82
+ */
83
+ validate(content: string, _options?: AdapterOptions): Promise<ValidationResult>;
84
+ /**
85
+ * Analyze content for SpecKit indicators
86
+ */
87
+ private analyzeContent;
88
+ /**
89
+ * Calculate confidence score
90
+ */
91
+ private calculateConfidence;
92
+ /**
93
+ * Build detection reason message
94
+ */
95
+ private buildDetectionReason;
96
+ }
97
+ /**
98
+ * Create a new SpecKit format adapter instance
99
+ *
100
+ * @param options - Adapter options
101
+ * @returns SpecKit adapter instance
102
+ */
103
+ export declare function createSpecKitAdapter(options?: AdapterOptions): SpecKitFormatAdapter;
104
+ //# sourceMappingURL=format-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-adapter.d.ts","sourceRoot":"","sources":["../../src/speckit/format-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAEL,KAAK,OAAO,EACZ,KAAK,gBAAgB,EAItB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,iBAAiB,EACjB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACvB,MAAM,kBAAkB,CAAC;AAwB1B;;;;GAIG;AACH,qBAAa,oBAAqB,SAAQ,iBAAiB;IACzD,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAOhC;IAEF,OAAO,CAAC,MAAM,CAAgB;gBAElB,OAAO,CAAC,EAAE,cAAc;IAKpC;;;;;;;;;;;;;;;OAeG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe;IAUxC;;;;;;;;;OASG;IACH,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,eAAe;IAQzE;;;;;;;OAOG;IACG,KAAK,CACT,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,YAAY,EACtB,QAAQ,CAAC,EAAE,cAAc,GACxB,OAAO,CAAC,WAAW,CAAC;IA+EvB;;OAEG;IACH,OAAO,CAAC,eAAe;IAQvB;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAehC;;;;;;OAMG;IACG,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC;IAsInF;;;;;;;;OAQG;IACG,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA8DrF;;OAEG;IACH,OAAO,CAAC,cAAc;IA4BtB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IA8D3B;;OAEG;IACH,OAAO,CAAC,oBAAoB;CAoC7B;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,oBAAoB,CAEnF"}
@@ -0,0 +1,488 @@
1
+ /**
2
+ * SpecKit Format Adapter
3
+ *
4
+ * FormatAdapter implementation for GitHub spec-kit format.
5
+ * Handles simple SpecKit specification documents with Intent, Overview, Goals,
6
+ * Requirements, and Changes sections.
7
+ *
8
+ * Uses SpecKitParser to parse markdown with ## sections and convert to/from APS format.
9
+ */
10
+ import { generateHash, createPlan, validateRelativePath, } from '@eddacraft/anvil-core';
11
+ import { BaseFormatAdapter, } from '../base/types.js';
12
+ import { createDetection, generateDeterministicPlanId } from '../base/utils.js';
13
+ import { SpecKitParser } from './parser.js';
14
+ /**
15
+ * SpecKit FormatAdapter implementation
16
+ *
17
+ * Converts between SpecKit format documents and APS plans.
18
+ */
19
+ export class SpecKitFormatAdapter extends BaseFormatAdapter {
20
+ metadata = {
21
+ name: 'speckit',
22
+ version: '2.0.0',
23
+ displayName: 'GitHub SpecKit',
24
+ description: 'GitHub spec-kit format adapter (spec.md, plan.md, tasks.md)',
25
+ formats: ['speckit', 'spec-kit', 'spec.md', 'plan.md', 'tasks.md'],
26
+ extensions: ['.md'],
27
+ };
28
+ parser;
29
+ constructor(options) {
30
+ super(options);
31
+ this.parser = new SpecKitParser();
32
+ }
33
+ /**
34
+ * Detect if content is SpecKit format
35
+ *
36
+ * Uses confidence scoring based on multiple indicators:
37
+ * - Specification header (20 points)
38
+ * - Intent section (15 points)
39
+ * - Overview section (10 points)
40
+ * - Goals section (10 points)
41
+ * - Requirements section (10 points)
42
+ * - Changes section (20 points)
43
+ * - Files to Create/Update sections (10 points)
44
+ * - Code blocks (5 points)
45
+ *
46
+ * @param content - Document content to analyze
47
+ * @returns Detection result with confidence score
48
+ */
49
+ detect(content) {
50
+ const indicators = this.analyzeContent(content);
51
+ const confidence = this.calculateConfidence(indicators);
52
+ const reason = this.buildDetectionReason(indicators);
53
+ // Detection threshold: 50% confidence
54
+ // Lower threshold than BMAD to accommodate minimal SpecKit documents
55
+ return createDetection(confidence >= 50, confidence, reason);
56
+ }
57
+ /**
58
+ * Detect with file path hints for improved accuracy
59
+ *
60
+ * Uses sibling file information (e.g., AGENTS.md) and content
61
+ * namespace patterns (e.g., `speckit.*`) to boost detection.
62
+ *
63
+ * @param content - Document content to analyze
64
+ * @param hint - Path and directory information
65
+ * @returns Detection result with confidence score
66
+ */
67
+ detectWithPath(content, hint) {
68
+ const indicators = this.analyzeContent(content, hint);
69
+ const confidence = this.calculateConfidence(indicators);
70
+ const reason = this.buildDetectionReason(indicators);
71
+ return createDetection(confidence >= 50, confidence, reason);
72
+ }
73
+ /**
74
+ * Parse SpecKit content to APS plan
75
+ *
76
+ * @param content - SpecKit markdown content
77
+ * @param context - Parse context for provenance
78
+ * @param options - Adapter options
79
+ * @returns Parse result with APS plan
80
+ */
81
+ async parse(content, context, _options) {
82
+ try {
83
+ // Parse SpecKit markdown using SpecKitParser
84
+ const parsed = this.parser.parseSpecMarkdown(content);
85
+ // Build intent from parsed content
86
+ const intent = parsed.intent || 'Implement Feature';
87
+ // Build proposed changes from parsed changes
88
+ const changes = [];
89
+ if (parsed.changes && parsed.changes.length > 0) {
90
+ for (const change of parsed.changes) {
91
+ const changeType = this.inferChangeType(change.type);
92
+ const rawPath = change.path || this.inferPathFromDescription(change.description);
93
+ let safePath;
94
+ try {
95
+ safePath = validateRelativePath(rawPath);
96
+ }
97
+ catch {
98
+ continue;
99
+ }
100
+ changes.push({
101
+ type: changeType,
102
+ path: safePath,
103
+ description: change.description,
104
+ content: change.content,
105
+ });
106
+ }
107
+ }
108
+ // Build provenance
109
+ const provenance = {
110
+ timestamp: context?.timestamp || new Date().toISOString(),
111
+ source: 'cli',
112
+ version: this.metadata.version,
113
+ author: context?.author,
114
+ repository: context?.repositoryPath,
115
+ branch: context?.branch,
116
+ commit: context?.commit,
117
+ };
118
+ const planId = context?.planId ?? generateDeterministicPlanId(content);
119
+ // Create APS plan
120
+ const plan = {
121
+ ...createPlan({
122
+ id: planId,
123
+ intent,
124
+ provenance,
125
+ changes,
126
+ }),
127
+ schema_version: '0.1.0',
128
+ hash: '0'.repeat(64), // Temporary, will be replaced
129
+ metadata: {
130
+ source_format: 'speckit',
131
+ overview: parsed.overview,
132
+ goals: parsed.goals,
133
+ requirements: parsed.requirements,
134
+ ...parsed.metadata,
135
+ },
136
+ };
137
+ // Generate hash for the plan
138
+ const planWithHash = {
139
+ ...plan,
140
+ hash: generateHash(plan),
141
+ };
142
+ return this.createParseSuccess(planWithHash);
143
+ }
144
+ catch (error) {
145
+ return this.createParseError([
146
+ {
147
+ code: 'PARSE_ERROR',
148
+ message: error instanceof Error ? error.message : 'Failed to parse SpecKit content',
149
+ details: error,
150
+ },
151
+ ]);
152
+ }
153
+ }
154
+ /**
155
+ * Infer APS change type from SpecKit change type
156
+ */
157
+ inferChangeType(type) {
158
+ const typeLower = type.toLowerCase();
159
+ if (typeLower.includes('create'))
160
+ return 'file_create';
161
+ if (typeLower.includes('update') || typeLower.includes('modify'))
162
+ return 'file_update';
163
+ if (typeLower.includes('delete') || typeLower.includes('remove'))
164
+ return 'file_delete';
165
+ return 'file_update'; // Default to update
166
+ }
167
+ /**
168
+ * Infer file path from change description
169
+ */
170
+ inferPathFromDescription(description) {
171
+ // Try to extract path from common patterns like "at path/to/file" or "`path/to/file`"
172
+ const pathMatch = description.match(/at\s+`([^`]+)`/) ||
173
+ description.match(/at\s+(\S+\.\w+)/) ||
174
+ description.match(/`([^`]+\.\w+)`/);
175
+ if (pathMatch) {
176
+ return pathMatch[1];
177
+ }
178
+ // Fallback: generate a generic path
179
+ return 'src/generated-file.ts';
180
+ }
181
+ /**
182
+ * Serialize APS plan to SpecKit format
183
+ *
184
+ * @param plan - APS plan to serialize
185
+ * @param options - Adapter options
186
+ * @returns Serialize result with SpecKit markdown
187
+ */
188
+ async serialize(plan, _options) {
189
+ try {
190
+ const sections = [];
191
+ // Header
192
+ sections.push('# Specification');
193
+ sections.push('');
194
+ // Intent section
195
+ sections.push('## Intent');
196
+ sections.push('');
197
+ sections.push(plan.intent);
198
+ sections.push('');
199
+ // Overview section (if available in metadata)
200
+ const overview = plan.metadata?.['overview'];
201
+ if (overview) {
202
+ sections.push('## Overview');
203
+ sections.push('');
204
+ sections.push(overview);
205
+ sections.push('');
206
+ }
207
+ // Goals section (if available in metadata)
208
+ const goals = plan.metadata?.['goals'];
209
+ if (goals && Array.isArray(goals)) {
210
+ sections.push('## Goals');
211
+ sections.push('');
212
+ for (const goal of goals) {
213
+ sections.push(`- ${goal}`);
214
+ }
215
+ sections.push('');
216
+ }
217
+ // Requirements section (if available in metadata)
218
+ const requirements = plan.metadata?.['requirements'];
219
+ if (requirements && Array.isArray(requirements)) {
220
+ sections.push('## Requirements');
221
+ sections.push('');
222
+ for (const req of requirements) {
223
+ sections.push(`- ${req}`);
224
+ }
225
+ sections.push('');
226
+ }
227
+ // Changes section
228
+ if (plan.proposed_changes.length > 0) {
229
+ sections.push('## Changes');
230
+ sections.push('');
231
+ // Group changes by type
232
+ const fileCreates = plan.proposed_changes.filter((c) => c.type === 'file_create');
233
+ const fileUpdates = plan.proposed_changes.filter((c) => c.type === 'file_update');
234
+ const fileDeletes = plan.proposed_changes.filter((c) => c.type === 'file_delete');
235
+ // Files to Create
236
+ if (fileCreates.length > 0) {
237
+ sections.push('### Files to Create');
238
+ sections.push('');
239
+ for (const change of fileCreates) {
240
+ sections.push(`#### Create ${change.path}`);
241
+ sections.push('');
242
+ sections.push(change.description || 'No description provided');
243
+ sections.push('');
244
+ if (change.content) {
245
+ sections.push('```typescript');
246
+ sections.push(change.content);
247
+ sections.push('```');
248
+ sections.push('');
249
+ }
250
+ }
251
+ }
252
+ // Files to Update
253
+ if (fileUpdates.length > 0) {
254
+ sections.push('### Files to Update');
255
+ sections.push('');
256
+ for (const change of fileUpdates) {
257
+ sections.push(`#### Update ${change.path}`);
258
+ sections.push('');
259
+ sections.push(change.description || 'No description provided');
260
+ sections.push('');
261
+ if (change.content) {
262
+ sections.push('```typescript');
263
+ sections.push(change.content);
264
+ sections.push('```');
265
+ sections.push('');
266
+ }
267
+ }
268
+ }
269
+ // Files to Delete
270
+ if (fileDeletes.length > 0) {
271
+ sections.push('### Files to Delete');
272
+ sections.push('');
273
+ for (const change of fileDeletes) {
274
+ sections.push(`#### Delete ${change.path}`);
275
+ sections.push('');
276
+ sections.push(change.description || 'No description provided');
277
+ sections.push('');
278
+ }
279
+ }
280
+ }
281
+ // Metadata section (if additional metadata exists)
282
+ const metadataKeys = Object.keys(plan.metadata || {}).filter((k) => !['source_format', 'overview', 'goals', 'requirements'].includes(k));
283
+ if (metadataKeys.length > 0) {
284
+ sections.push('## Metadata');
285
+ sections.push('');
286
+ sections.push('```json');
287
+ const filteredMetadata = {};
288
+ for (const key of metadataKeys) {
289
+ filteredMetadata[key] = plan.metadata?.[key];
290
+ }
291
+ sections.push(JSON.stringify(filteredMetadata, null, 2));
292
+ sections.push('```');
293
+ sections.push('');
294
+ }
295
+ const content = sections.join('\n');
296
+ return this.createSerializeSuccess(content);
297
+ }
298
+ catch (error) {
299
+ return this.createSerializeError([
300
+ {
301
+ code: 'SERIALIZE_ERROR',
302
+ message: error instanceof Error ? error.message : 'Failed to serialize to SpecKit format',
303
+ details: error,
304
+ },
305
+ ]);
306
+ }
307
+ }
308
+ /**
309
+ * Validate SpecKit content
310
+ *
311
+ * Checks for required SpecKit elements without full conversion.
312
+ *
313
+ * @param content - SpecKit content to validate
314
+ * @param options - Validation options
315
+ * @returns Validation result
316
+ */
317
+ async validate(content, _options) {
318
+ const issues = [];
319
+ // Check for minimum content length
320
+ if (content.trim().length < 100) {
321
+ issues.push({
322
+ code: 'CONTENT_TOO_SHORT',
323
+ path: 'content',
324
+ message: 'Content is too short to be a valid SpecKit document',
325
+ severity: 'error',
326
+ });
327
+ }
328
+ // Analyze content for SpecKit indicators
329
+ const indicators = this.analyzeContent(content);
330
+ const confidence = this.calculateConfidence(indicators);
331
+ // Low confidence suggests invalid SpecKit format
332
+ if (confidence < 50) {
333
+ issues.push({
334
+ code: 'LOW_CONFIDENCE',
335
+ path: 'content',
336
+ message: `Content does not appear to be a valid SpecKit document (confidence: ${confidence}%)`,
337
+ severity: 'error',
338
+ });
339
+ }
340
+ // Check for required sections
341
+ if (!indicators.hasSpecificationHeader && !indicators.hasChangesSection) {
342
+ issues.push({
343
+ code: 'MISSING_REQUIRED_SECTIONS',
344
+ path: 'content',
345
+ message: 'Missing required sections (Specification header or Changes section)',
346
+ severity: 'error',
347
+ });
348
+ }
349
+ // Warn if missing recommended sections
350
+ if (!indicators.hasIntentSection) {
351
+ issues.push({
352
+ code: 'MISSING_INTENT',
353
+ path: 'content',
354
+ message: 'Missing recommended Intent section',
355
+ severity: 'warning',
356
+ });
357
+ }
358
+ return {
359
+ valid: issues.filter((i) => i.severity === 'error').length === 0,
360
+ issues: issues.length > 0 ? issues : undefined,
361
+ summary: issues.length === 0
362
+ ? 'SpecKit document is valid'
363
+ : `Found ${issues.length} validation issue${issues.length > 1 ? 's' : ''}`,
364
+ };
365
+ }
366
+ /**
367
+ * Analyze content for SpecKit indicators
368
+ */
369
+ analyzeContent(content, hint) {
370
+ const lowerContent = content.toLowerCase();
371
+ // Detect speckit.* namespace commands (e.g., /speckit.clarify, /speckit.analyze)
372
+ const hasSpeckitNamespace = /\b\/?speckit\.\w+\b/i.test(content);
373
+ // Check for AGENTS.md sibling
374
+ const hasAgentsMdSibling = hint?.siblingFiles?.some((f) => f.toLowerCase() === 'agents.md') ?? false;
375
+ return {
376
+ hasSpecificationHeader: /^#\s+(specification|spec)\s*$/im.test(content),
377
+ hasIntentSection: /^##\s+intent\s*$/im.test(content),
378
+ hasOverviewSection: /^##\s+overview\s*$/im.test(content),
379
+ hasGoalsSection: /^##\s+goals?\s*$/im.test(content),
380
+ hasRequirementsSection: /^##\s+requirements?\s*$/im.test(content),
381
+ hasChangesSection: /^##\s+changes?\s*$/im.test(content),
382
+ hasFilesToCreateSection: lowerContent.includes('files to create') || lowerContent.includes('create file'),
383
+ hasFilesToUpdateSection: lowerContent.includes('files to update') || lowerContent.includes('update file'),
384
+ hasCodeBlocks: /```[\s\S]*?```/.test(content),
385
+ sectionCount: (content.match(/^##\s+/gim) || []).length,
386
+ hasSpeckitNamespace,
387
+ hasAgentsMdSibling,
388
+ };
389
+ }
390
+ /**
391
+ * Calculate confidence score
392
+ */
393
+ calculateConfidence(indicators) {
394
+ let score = 0;
395
+ // Specification header (20 points)
396
+ if (indicators.hasSpecificationHeader) {
397
+ score += 20;
398
+ }
399
+ // Intent section (15 points)
400
+ if (indicators.hasIntentSection) {
401
+ score += 15;
402
+ }
403
+ // Overview section (10 points)
404
+ if (indicators.hasOverviewSection) {
405
+ score += 10;
406
+ }
407
+ // Goals section (10 points)
408
+ if (indicators.hasGoalsSection) {
409
+ score += 10;
410
+ }
411
+ // Requirements section (10 points)
412
+ if (indicators.hasRequirementsSection) {
413
+ score += 10;
414
+ }
415
+ // Changes section (20 points)
416
+ if (indicators.hasChangesSection) {
417
+ score += 20;
418
+ }
419
+ // Files to Create/Update sections (10 points)
420
+ if (indicators.hasFilesToCreateSection || indicators.hasFilesToUpdateSection) {
421
+ score += 10;
422
+ }
423
+ // Code blocks (5 points)
424
+ if (indicators.hasCodeBlocks) {
425
+ score += 5;
426
+ }
427
+ // speckit.* namespace commands (10 points)
428
+ if (indicators.hasSpeckitNamespace) {
429
+ score += 10;
430
+ }
431
+ // AGENTS.md sibling (15 points)
432
+ if (indicators.hasAgentsMdSibling) {
433
+ score += 15;
434
+ }
435
+ // Bonus: If has both Specification header AND Intent section, ensure at least 50% confidence
436
+ // This accommodates minimal but valid SpecKit documents
437
+ if (indicators.hasSpecificationHeader && indicators.hasIntentSection && score < 50) {
438
+ score = 50;
439
+ }
440
+ return Math.min(100, score);
441
+ }
442
+ /**
443
+ * Build detection reason message
444
+ */
445
+ buildDetectionReason(indicators) {
446
+ const reasons = [];
447
+ if (indicators.hasSpecificationHeader) {
448
+ reasons.push('specification-header');
449
+ }
450
+ if (indicators.hasIntentSection) {
451
+ reasons.push('intent-section');
452
+ }
453
+ if (indicators.hasGoalsSection) {
454
+ reasons.push('goals-section');
455
+ }
456
+ if (indicators.hasRequirementsSection) {
457
+ reasons.push('requirements-section');
458
+ }
459
+ if (indicators.hasChangesSection) {
460
+ reasons.push('changes-section');
461
+ }
462
+ if (indicators.hasFilesToCreateSection || indicators.hasFilesToUpdateSection) {
463
+ reasons.push('file-changes');
464
+ }
465
+ if (indicators.hasCodeBlocks) {
466
+ reasons.push('code-blocks');
467
+ }
468
+ if (indicators.hasSpeckitNamespace) {
469
+ reasons.push('speckit-namespace');
470
+ }
471
+ if (indicators.hasAgentsMdSibling) {
472
+ reasons.push('agents-md');
473
+ }
474
+ if (indicators.sectionCount >= 3) {
475
+ reasons.push(`${indicators.sectionCount} sections`);
476
+ }
477
+ return reasons.length > 0 ? reasons.join(', ') : 'no strong indicators';
478
+ }
479
+ }
480
+ /**
481
+ * Create a new SpecKit format adapter instance
482
+ *
483
+ * @param options - Adapter options
484
+ * @returns SpecKit adapter instance
485
+ */
486
+ export function createSpecKitAdapter(options) {
487
+ return new SpecKitFormatAdapter(options);
488
+ }
@@ -0,0 +1,33 @@
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 { type APSPlan } from '@eddacraft/anvil-core';
12
+ import type { AdapterConfig, ConversionResult, ExternalSpec, SpecContext } from '../common/types.js';
13
+ import { BaseAdapter } from '../common/types.js';
14
+ export declare class SpecKitImportAdapterV2 extends BaseAdapter {
15
+ readonly name = "speckit-import-v2";
16
+ readonly version = "2.0.0";
17
+ readonly supportedFormats: readonly ["speckit", "spec-kit", "spec.md", "plan.md", "tasks.md"];
18
+ private specParser;
19
+ private planParser;
20
+ private tasksParser;
21
+ constructor(config?: AdapterConfig);
22
+ generateSpec(intent: string, context: SpecContext): Promise<APSPlan>;
23
+ validateSpec(spec: APSPlan): Promise<import('@eddacraft/anvil-core').ValidationResult>;
24
+ convertToAPS(spec: ExternalSpec): Promise<ConversionResult<APSPlan>>;
25
+ convertFromAPS(_spec: APSPlan): Promise<ConversionResult<ExternalSpec>>;
26
+ private buildAPSFromDocs;
27
+ private buildIntent;
28
+ private buildProposedChanges;
29
+ private inferPathFromScenario;
30
+ private inferAPIPath;
31
+ private generateSpecTemplate;
32
+ }
33
+ //# sourceMappingURL=import-v2.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import-v2.d.ts","sourceRoot":"","sources":["../../src/speckit/import-v2.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,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;AAqBjD,qBAAa,sBAAuB,SAAQ,WAAW;IACrD,QAAQ,CAAC,IAAI,uBAAuB;IACpC,QAAQ,CAAC,OAAO,WAAW;IAC3B,QAAQ,CAAC,gBAAgB,qEAAsE;IAE/F,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,WAAW,CAAc;gBAErB,MAAM,GAAE,aAAkB;IAOhC,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;IAuCtF,YAAY,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAgEpE,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC;IAY7E,OAAO,CAAC,gBAAgB;IA8DxB,OAAO,CAAC,WAAW;IAgBnB,OAAO,CAAC,oBAAoB;IAqF5B,OAAO,CAAC,qBAAqB;IAiB7B,OAAO,CAAC,YAAY;IAUpB,OAAO,CAAC,oBAAoB;CA+C7B"}