@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,134 @@
1
+ /**
2
+ * Generic Markdown Serializer
3
+ *
4
+ * Serializes APS plans to generic markdown format.
5
+ */
6
+
7
+ import type { APSPlan } from '@eddacraft/anvil-core';
8
+
9
+ /**
10
+ * Type guard to check if value is a string
11
+ */
12
+ function isString(value: unknown): value is string {
13
+ return typeof value === 'string';
14
+ }
15
+
16
+ /**
17
+ * Type guard to check if value is a string array
18
+ */
19
+ function isStringArray(value: unknown): value is string[] {
20
+ return Array.isArray(value) && value.every((item) => typeof item === 'string');
21
+ }
22
+
23
+ /**
24
+ * Serialize APS plan to generic markdown format
25
+ */
26
+ export function serializeToGeneric(plan: APSPlan): string {
27
+ const lines: string[] = [];
28
+
29
+ // Title (from intent or metadata) - safely check if title is a non-empty string
30
+ const metadataTitle = plan.metadata?.['title'];
31
+ const title = isString(metadataTitle) && metadataTitle ? metadataTitle : plan.intent;
32
+ lines.push(`# ${title}`);
33
+ lines.push('');
34
+
35
+ // Overview section - safely check if overview is a string
36
+ const metadataOverview = plan.metadata?.['overview'];
37
+ if (metadataOverview && isString(metadataOverview)) {
38
+ lines.push('## Overview');
39
+ lines.push('');
40
+ lines.push(metadataOverview);
41
+ lines.push('');
42
+ } else if (plan.intent) {
43
+ lines.push('## Purpose');
44
+ lines.push('');
45
+ lines.push(plan.intent);
46
+ lines.push('');
47
+ }
48
+
49
+ // Goals section - safely check if goals is a string array
50
+ const metadataGoals = plan.metadata?.['goals'];
51
+ if (metadataGoals && isStringArray(metadataGoals)) {
52
+ lines.push('## Goals');
53
+ lines.push('');
54
+ for (const goal of metadataGoals) {
55
+ lines.push(`- ${goal}`);
56
+ }
57
+ lines.push('');
58
+ }
59
+
60
+ // Changes section
61
+ if (plan.proposed_changes.length > 0) {
62
+ lines.push('## Changes');
63
+ lines.push('');
64
+
65
+ // Group changes by type
66
+ const creates = plan.proposed_changes.filter((c) => c.type === 'file_create');
67
+ const updates = plan.proposed_changes.filter((c) => c.type === 'file_update');
68
+ const deletes = plan.proposed_changes.filter((c) => c.type === 'file_delete');
69
+ const configs = plan.proposed_changes.filter((c) => c.type === 'config_update');
70
+
71
+ if (creates.length > 0) {
72
+ lines.push('### Files to Create');
73
+ lines.push('');
74
+ for (const change of creates) {
75
+ lines.push(`- **${change.path}**: ${change.description}`);
76
+ }
77
+ lines.push('');
78
+ }
79
+
80
+ if (updates.length > 0) {
81
+ lines.push('### Files to Update');
82
+ lines.push('');
83
+ for (const change of updates) {
84
+ lines.push(`- **${change.path}**: ${change.description}`);
85
+ }
86
+ lines.push('');
87
+ }
88
+
89
+ if (configs.length > 0) {
90
+ lines.push('### Configuration Changes');
91
+ lines.push('');
92
+ for (const change of configs) {
93
+ lines.push(`- **${change.path}**: ${change.description}`);
94
+ }
95
+ lines.push('');
96
+ }
97
+
98
+ if (deletes.length > 0) {
99
+ lines.push('### Files to Delete');
100
+ lines.push('');
101
+ for (const change of deletes) {
102
+ lines.push(`- **${change.path}**: ${change.description}`);
103
+ }
104
+ lines.push('');
105
+ }
106
+ }
107
+
108
+ // Validation requirements
109
+ if (plan.validations.required_checks.length > 0) {
110
+ lines.push('## Validation Requirements');
111
+ lines.push('');
112
+ for (const check of plan.validations.required_checks) {
113
+ lines.push(`- ${check}`);
114
+ }
115
+ lines.push('');
116
+ }
117
+
118
+ // Metadata section
119
+ if (plan.provenance) {
120
+ lines.push('## Metadata');
121
+ lines.push('');
122
+ lines.push(`- **Author**: ${plan.provenance.author || 'Unknown'}`);
123
+ lines.push(`- **Created**: ${plan.provenance.timestamp}`);
124
+ if (plan.provenance.repository) {
125
+ lines.push(`- **Repository**: ${plan.provenance.repository}`);
126
+ }
127
+ if (plan.provenance.branch) {
128
+ lines.push(`- **Branch**: ${plan.provenance.branch}`);
129
+ }
130
+ lines.push('');
131
+ }
132
+
133
+ return lines.join('\n');
134
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Generic Adapter Types
3
+ *
4
+ * Types for the generic markdown adapter that handles common planning document formats.
5
+ */
6
+
7
+ /**
8
+ * Parsed generic document structure
9
+ */
10
+ export interface GenericDocument {
11
+ /** Document title (from first # heading) */
12
+ title?: string;
13
+ /** Intent/purpose extracted from intro or objective sections */
14
+ intent?: string;
15
+ /** Overview/description */
16
+ overview?: string;
17
+ /** Goals or objectives */
18
+ goals?: string[];
19
+ /** Requirements extracted from various sections */
20
+ requirements?: string[];
21
+ /** Tasks or action items */
22
+ tasks?: string[];
23
+ /** Features or capabilities */
24
+ features?: string[];
25
+ /** Metadata from document */
26
+ metadata?: Record<string, unknown>;
27
+ /** Raw content */
28
+ raw: string;
29
+ }
30
+
31
+ /**
32
+ * Detection indicators for generic markdown
33
+ */
34
+ export interface GenericIndicators {
35
+ /** Has markdown headings */
36
+ hasHeadings: boolean;
37
+ /** Has lists (bullet or numbered) */
38
+ hasLists: boolean;
39
+ /** Has requirements-like content */
40
+ hasRequirementsSection: boolean;
41
+ /** Has goals/objectives section */
42
+ hasGoalsSection: boolean;
43
+ /** Has tasks/action items */
44
+ hasTasksSection: boolean;
45
+ /** Has features section */
46
+ hasFeaturesSection: boolean;
47
+ /** Has overview/description */
48
+ hasOverviewSection: boolean;
49
+ /** Document length */
50
+ wordCount: number;
51
+ /** Number of list items */
52
+ listItemCount: number;
53
+ }
@@ -0,0 +1,270 @@
1
+ /**
2
+ * Generic Adapter Utilities
3
+ *
4
+ * Helper functions for parsing generic markdown documents.
5
+ */
6
+
7
+ import type { GenericDocument, GenericIndicators } from './types.js';
8
+
9
+ /**
10
+ * Extract document title from first heading
11
+ */
12
+ export function extractTitle(content: string): string | undefined {
13
+ const match = content.match(/^#\s+(.+)$/m);
14
+ return match ? match[1].trim() : undefined;
15
+ }
16
+
17
+ /**
18
+ * Extract intent from common sections
19
+ */
20
+ export function extractIntent(content: string): string | undefined {
21
+ // Look for sections like: Purpose, Intent, Objective, Goal, Summary
22
+ const patterns = [
23
+ /##\s+(?:Purpose|Intent|Objective|Goal)\s*\n+([^\n#]+)/i,
24
+ /##\s+(?:Executive\s+)?Summary\s*\n+([^\n#]+)/i,
25
+ /^([^#\n]+?)(?=\n##|\n#|$)/m, // First paragraph as fallback
26
+ ];
27
+
28
+ for (const pattern of patterns) {
29
+ const match = content.match(pattern);
30
+ if (match && match[1]) {
31
+ return match[1].trim().substring(0, 500); // Limit length
32
+ }
33
+ }
34
+
35
+ return undefined;
36
+ }
37
+
38
+ /**
39
+ * Extract overview/description
40
+ */
41
+ export function extractOverview(content: string): string | undefined {
42
+ const patterns = [/##\s+(?:Overview|Description|Background|Context)\s*\n+([\s\S]+?)(?=\n##|$)/i];
43
+
44
+ for (const pattern of patterns) {
45
+ const match = content.match(pattern);
46
+ if (match && match[1]) {
47
+ return match[1].trim();
48
+ }
49
+ }
50
+
51
+ return undefined;
52
+ }
53
+
54
+ /**
55
+ * Extract goals from lists under Goals/Objectives sections
56
+ */
57
+ export function extractGoals(content: string): string[] {
58
+ const goals: string[] = [];
59
+
60
+ // Match Goals/Objectives section
61
+ const sectionMatch = content.match(/##\s+(?:Goals?|Objectives?)\s*\n+([\s\S]+?)(?=\n##|$)/i);
62
+
63
+ if (sectionMatch) {
64
+ const section = sectionMatch[1];
65
+ // Extract list items
66
+ const listItems = section.match(/^[-*]\s+(.+)$/gm);
67
+ if (listItems) {
68
+ goals.push(...listItems.map((item) => item.replace(/^[-*]\s+/, '').trim()));
69
+ }
70
+ }
71
+
72
+ return goals;
73
+ }
74
+
75
+ /**
76
+ * Extract requirements from various sections
77
+ */
78
+ export function extractRequirements(content: string): string[] {
79
+ const requirements: string[] = [];
80
+
81
+ // Match Requirements/Needs/Must Have sections
82
+ const patterns = [
83
+ /##\s+(?:Requirements?|Needs?|Must\s+Have)\s*\n+([\s\S]+?)(?=\n##|$)/i,
84
+ /##\s+(?:Functional\s+)?Requirements?\s*\n+([\s\S]+?)(?=\n##|$)/i,
85
+ ];
86
+
87
+ for (const pattern of patterns) {
88
+ const match = content.match(pattern);
89
+ if (match) {
90
+ const section = match[1];
91
+ // Extract list items
92
+ const listItems = section.match(/^[-*]\s+(.+)$/gm);
93
+ if (listItems) {
94
+ requirements.push(...listItems.map((item) => item.replace(/^[-*]\s+/, '').trim()));
95
+ }
96
+ // Extract numbered items
97
+ const numberedItems = section.match(/^\d+\.\s+(.+)$/gm);
98
+ if (numberedItems) {
99
+ requirements.push(...numberedItems.map((item) => item.replace(/^\d+\.\s+/, '').trim()));
100
+ }
101
+ }
102
+ }
103
+
104
+ return requirements;
105
+ }
106
+
107
+ /**
108
+ * Extract tasks from Tasks/Action Items sections
109
+ */
110
+ export function extractTasks(content: string): string[] {
111
+ const tasks: string[] = [];
112
+
113
+ const patterns = [/##\s+(?:Tasks?|Action\s+Items?|To\s+Do|TODO)\s*\n+([\s\S]+?)(?=\n##|$)/i];
114
+
115
+ for (const pattern of patterns) {
116
+ const match = content.match(pattern);
117
+ if (match) {
118
+ const section = match[1];
119
+ // Extract list items (including checkboxes)
120
+ const listItems = section.match(/^[-*]\s+(?:\[[ x]\]\s+)?(.+)$/gm);
121
+ if (listItems) {
122
+ tasks.push(...listItems.map((item) => item.replace(/^[-*]\s+(?:\[[ x]\]\s+)?/, '').trim()));
123
+ }
124
+ }
125
+ }
126
+
127
+ return tasks;
128
+ }
129
+
130
+ /**
131
+ * Extract features from Features/Capabilities sections
132
+ */
133
+ export function extractFeatures(content: string): string[] {
134
+ const features: string[] = [];
135
+
136
+ const patterns = [/##\s+(?:Features?|Capabilities?|Functionality)\s*\n+([\s\S]+?)(?=\n##|$)/i];
137
+
138
+ for (const pattern of patterns) {
139
+ const match = content.match(pattern);
140
+ if (match) {
141
+ const section = match[1];
142
+ const listItems = section.match(/^[-*]\s+(.+)$/gm);
143
+ if (listItems) {
144
+ features.push(...listItems.map((item) => item.replace(/^[-*]\s+/, '').trim()));
145
+ }
146
+ }
147
+ }
148
+
149
+ return features;
150
+ }
151
+
152
+ /**
153
+ * Analyze content for generic markdown indicators
154
+ */
155
+ export function analyzeContent(content: string): GenericIndicators {
156
+ const headings = content.match(/^#{1,6}\s+.+$/gm) || [];
157
+ const listItems = content.match(/^[-*]\s+.+$/gm) || [];
158
+ const words = content.split(/\s+/).filter((w) => w.length > 0);
159
+
160
+ return {
161
+ hasHeadings: headings.length > 0,
162
+ hasLists: listItems.length > 0,
163
+ hasRequirementsSection: /##\s+(?:Requirements?|Needs?)/i.test(content),
164
+ hasGoalsSection: /##\s+(?:Goals?|Objectives?)/i.test(content),
165
+ hasTasksSection: /##\s+(?:Tasks?|Action\s+Items?|To\s+Do)/i.test(content),
166
+ hasFeaturesSection: /##\s+(?:Features?|Capabilities?)/i.test(content),
167
+ hasOverviewSection: /##\s+(?:Overview|Description|Background)/i.test(content),
168
+ wordCount: words.length,
169
+ listItemCount: listItems.length,
170
+ };
171
+ }
172
+
173
+ /**
174
+ * Calculate confidence score for generic markdown detection
175
+ */
176
+ export function calculateConfidenceScore(indicators: GenericIndicators): number {
177
+ let score = 0;
178
+
179
+ // Basic markdown structure (10 points)
180
+ if (indicators.hasHeadings) {
181
+ score += 10;
182
+ }
183
+
184
+ // Has lists (10 points)
185
+ if (indicators.hasLists) {
186
+ score += 10;
187
+ }
188
+
189
+ // Planning-related sections (5 points each)
190
+ if (indicators.hasRequirementsSection) {
191
+ score += 5;
192
+ }
193
+ if (indicators.hasGoalsSection) {
194
+ score += 5;
195
+ }
196
+ if (indicators.hasTasksSection) {
197
+ score += 5;
198
+ }
199
+ if (indicators.hasFeaturesSection) {
200
+ score += 5;
201
+ }
202
+ if (indicators.hasOverviewSection) {
203
+ score += 5;
204
+ }
205
+
206
+ // Content density (up to 10 points)
207
+ if (indicators.wordCount >= 100) {
208
+ score += 5;
209
+ }
210
+ if (indicators.wordCount >= 500) {
211
+ score += 5;
212
+ }
213
+
214
+ // List items (up to 5 points)
215
+ if (indicators.listItemCount >= 3) {
216
+ score += 3;
217
+ }
218
+ if (indicators.listItemCount >= 10) {
219
+ score += 2;
220
+ }
221
+
222
+ return Math.min(100, score);
223
+ }
224
+
225
+ /**
226
+ * Build detection reason string
227
+ */
228
+ export function buildDetectionReason(indicators: GenericIndicators): string {
229
+ const reasons: string[] = [];
230
+
231
+ if (indicators.hasHeadings) {
232
+ reasons.push('markdown-headings');
233
+ }
234
+ if (indicators.hasLists) {
235
+ reasons.push(`${indicators.listItemCount} list-items`);
236
+ }
237
+ if (indicators.hasRequirementsSection) {
238
+ reasons.push('requirements-section');
239
+ }
240
+ if (indicators.hasGoalsSection) {
241
+ reasons.push('goals-section');
242
+ }
243
+ if (indicators.hasTasksSection) {
244
+ reasons.push('tasks-section');
245
+ }
246
+ if (indicators.hasFeaturesSection) {
247
+ reasons.push('features-section');
248
+ }
249
+ if (indicators.hasOverviewSection) {
250
+ reasons.push('overview-section');
251
+ }
252
+
253
+ return reasons.length > 0 ? reasons.join(', ') : 'generic-markdown';
254
+ }
255
+
256
+ /**
257
+ * Parse generic markdown document
258
+ */
259
+ export function parseGenericDocument(content: string): GenericDocument {
260
+ return {
261
+ title: extractTitle(content),
262
+ intent: extractIntent(content),
263
+ overview: extractOverview(content),
264
+ goals: extractGoals(content),
265
+ requirements: extractRequirements(content),
266
+ tasks: extractTasks(content),
267
+ features: extractFeatures(content),
268
+ raw: content,
269
+ };
270
+ }
package/src/index.ts ADDED
@@ -0,0 +1,48 @@
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
+
8
+ // Export base framework (types, registry, utils)
9
+ export * from './base/index.js';
10
+
11
+ // Export APS Markdown adapter (native format)
12
+ export * from './aps-markdown/index.js';
13
+
14
+ // Export SpecKit adapter
15
+ export * from './speckit/index.js';
16
+
17
+ // Export BMAD adapter
18
+ export * from './bmad/index.js';
19
+
20
+ // Export Generic adapter
21
+ export * from './generic/index.js';
22
+
23
+ // Export common types for backward compatibility
24
+ export type {
25
+ SpecContext,
26
+ ExternalSpec,
27
+ ConversionResult,
28
+ ConversionError,
29
+ ConversionWarning,
30
+ } from './common/types.js';
31
+
32
+ // Auto-register adapters when module is imported
33
+ import { registry as baseRegistry } from './base/index.js';
34
+ import { APSMarkdownAdapter } from './aps-markdown/index.js';
35
+ import { BMADFormatAdapter } from './bmad/index.js';
36
+ import { SpecKitFormatAdapter } from './speckit/index.js';
37
+ import { GenericMarkdownAdapter } from './generic/index.js';
38
+
39
+ // Register adapters in priority order
40
+ // APS adapter first (native format), then specific external formats
41
+ // Generic adapter is registered last as fallback
42
+ baseRegistry.register(new APSMarkdownAdapter());
43
+ baseRegistry.register(new BMADFormatAdapter());
44
+ baseRegistry.register(new SpecKitFormatAdapter());
45
+ baseRegistry.register(new GenericMarkdownAdapter());
46
+
47
+ // Export the registry instance as default export
48
+ export { baseRegistry as registry };