@sudocode-ai/integration-speckit 0.1.14
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.
- package/dist/id-generator.d.ts +149 -0
- package/dist/id-generator.d.ts.map +1 -0
- package/dist/id-generator.js +197 -0
- package/dist/id-generator.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1017 -0
- package/dist/index.js.map +1 -0
- package/dist/parser/index.d.ts +11 -0
- package/dist/parser/index.d.ts.map +1 -0
- package/dist/parser/index.js +16 -0
- package/dist/parser/index.js.map +1 -0
- package/dist/parser/markdown-utils.d.ts +138 -0
- package/dist/parser/markdown-utils.d.ts.map +1 -0
- package/dist/parser/markdown-utils.js +283 -0
- package/dist/parser/markdown-utils.js.map +1 -0
- package/dist/parser/plan-parser.d.ts +97 -0
- package/dist/parser/plan-parser.d.ts.map +1 -0
- package/dist/parser/plan-parser.js +286 -0
- package/dist/parser/plan-parser.js.map +1 -0
- package/dist/parser/spec-parser.d.ts +95 -0
- package/dist/parser/spec-parser.d.ts.map +1 -0
- package/dist/parser/spec-parser.js +250 -0
- package/dist/parser/spec-parser.js.map +1 -0
- package/dist/parser/supporting-docs.d.ts +119 -0
- package/dist/parser/supporting-docs.d.ts.map +1 -0
- package/dist/parser/supporting-docs.js +324 -0
- package/dist/parser/supporting-docs.js.map +1 -0
- package/dist/parser/tasks-parser.d.ts +171 -0
- package/dist/parser/tasks-parser.d.ts.map +1 -0
- package/dist/parser/tasks-parser.js +281 -0
- package/dist/parser/tasks-parser.js.map +1 -0
- package/dist/relationship-mapper.d.ts +165 -0
- package/dist/relationship-mapper.d.ts.map +1 -0
- package/dist/relationship-mapper.js +238 -0
- package/dist/relationship-mapper.js.map +1 -0
- package/dist/watcher.d.ts +137 -0
- package/dist/watcher.d.ts.map +1 -0
- package/dist/watcher.js +599 -0
- package/dist/watcher.js.map +1 -0
- package/dist/writer/index.d.ts +8 -0
- package/dist/writer/index.d.ts.map +1 -0
- package/dist/writer/index.js +10 -0
- package/dist/writer/index.js.map +1 -0
- package/dist/writer/spec-writer.d.ts +70 -0
- package/dist/writer/spec-writer.d.ts.map +1 -0
- package/dist/writer/spec-writer.js +261 -0
- package/dist/writer/spec-writer.js.map +1 -0
- package/dist/writer/tasks-writer.d.ts +47 -0
- package/dist/writer/tasks-writer.d.ts.map +1 -0
- package/dist/writer/tasks-writer.js +161 -0
- package/dist/writer/tasks-writer.js.map +1 -0
- package/package.json +42 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plan Parser for Spec-Kit Integration
|
|
3
|
+
*
|
|
4
|
+
* Parses plan.md files from spec-kit and extracts structured data.
|
|
5
|
+
*
|
|
6
|
+
* Expected format:
|
|
7
|
+
* ```markdown
|
|
8
|
+
* # Implementation Plan: [FEATURE]
|
|
9
|
+
*
|
|
10
|
+
* **Branch**: feature/xxx
|
|
11
|
+
* **Spec**: [[s-001-spec]] or spec.md link
|
|
12
|
+
* **Status**: Draft
|
|
13
|
+
*
|
|
14
|
+
* ## Overview
|
|
15
|
+
* ...
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
import { readFileSync, existsSync } from "fs";
|
|
19
|
+
import { PATTERNS, extractMetadata, extractTitleWithPrefixRemoval, extractCrossReferences, findContentStartIndex, parseDate, normalizeStatus, } from "./markdown-utils.js";
|
|
20
|
+
/**
|
|
21
|
+
* Parse a plan.md file and extract structured data
|
|
22
|
+
*
|
|
23
|
+
* @param filePath - Absolute path to the plan.md file
|
|
24
|
+
* @param options - Parsing options
|
|
25
|
+
* @returns Parsed plan data or null if file doesn't exist
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* const plan = parsePlan("/project/.specify/specs/001-auth/plan.md");
|
|
29
|
+
* console.log(plan?.title); // "Authentication"
|
|
30
|
+
* console.log(plan?.specReference); // "spec.md" or "s-001-spec"
|
|
31
|
+
*/
|
|
32
|
+
export function parsePlan(filePath, options = {}) {
|
|
33
|
+
const { includeContent = true, extractReferences = true } = options;
|
|
34
|
+
if (!existsSync(filePath)) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
const rawContent = readFileSync(filePath, "utf-8");
|
|
39
|
+
const lines = rawContent.split("\n");
|
|
40
|
+
// Extract raw title
|
|
41
|
+
const rawTitle = extractRawTitle(lines);
|
|
42
|
+
if (!rawTitle) {
|
|
43
|
+
return null; // Invalid plan file without title
|
|
44
|
+
}
|
|
45
|
+
// Clean title by removing prefix
|
|
46
|
+
const title = extractTitleWithPrefixRemoval(lines, [
|
|
47
|
+
"Implementation Plan:",
|
|
48
|
+
"Implementation Plan",
|
|
49
|
+
]) || rawTitle;
|
|
50
|
+
// Extract metadata
|
|
51
|
+
const metadata = extractMetadata(lines);
|
|
52
|
+
// Extract specific metadata fields
|
|
53
|
+
const branch = extractBranch(lines, metadata);
|
|
54
|
+
const specReference = extractSpecReference(lines, metadata);
|
|
55
|
+
const status = extractStatus(metadata);
|
|
56
|
+
const createdAt = extractCreatedDate(metadata);
|
|
57
|
+
// Extract main content
|
|
58
|
+
let content = "";
|
|
59
|
+
if (includeContent) {
|
|
60
|
+
const contentStartIndex = findContentStartIndex(lines);
|
|
61
|
+
content = lines.slice(contentStartIndex).join("\n").trim();
|
|
62
|
+
}
|
|
63
|
+
// Extract cross-references
|
|
64
|
+
let crossReferences = [];
|
|
65
|
+
if (extractReferences) {
|
|
66
|
+
crossReferences = extractCrossReferences(rawContent);
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
title,
|
|
70
|
+
rawTitle,
|
|
71
|
+
branch,
|
|
72
|
+
specReference,
|
|
73
|
+
status,
|
|
74
|
+
createdAt,
|
|
75
|
+
metadata,
|
|
76
|
+
content,
|
|
77
|
+
crossReferences,
|
|
78
|
+
filePath,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
console.error(`[plan-parser] Failed to parse ${filePath}:`, error);
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Parse plan content from a string (for testing or in-memory parsing)
|
|
88
|
+
*
|
|
89
|
+
* @param content - Markdown content string
|
|
90
|
+
* @param filePath - Optional file path for reference
|
|
91
|
+
* @returns Parsed plan data or null
|
|
92
|
+
*/
|
|
93
|
+
export function parsePlanContent(content, filePath = "<string>") {
|
|
94
|
+
const lines = content.split("\n");
|
|
95
|
+
// Extract raw title
|
|
96
|
+
const rawTitle = extractRawTitle(lines);
|
|
97
|
+
if (!rawTitle) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
// Clean title by removing prefix
|
|
101
|
+
const title = extractTitleWithPrefixRemoval(lines, [
|
|
102
|
+
"Implementation Plan:",
|
|
103
|
+
"Implementation Plan",
|
|
104
|
+
]) || rawTitle;
|
|
105
|
+
// Extract metadata
|
|
106
|
+
const metadata = extractMetadata(lines);
|
|
107
|
+
// Extract specific metadata fields
|
|
108
|
+
const branch = extractBranch(lines, metadata);
|
|
109
|
+
const specReference = extractSpecReference(lines, metadata);
|
|
110
|
+
const status = extractStatus(metadata);
|
|
111
|
+
const createdAt = extractCreatedDate(metadata);
|
|
112
|
+
// Extract main content
|
|
113
|
+
const contentStartIndex = findContentStartIndex(lines);
|
|
114
|
+
const mainContent = lines.slice(contentStartIndex).join("\n").trim();
|
|
115
|
+
// Extract cross-references
|
|
116
|
+
const crossReferences = extractCrossReferences(content);
|
|
117
|
+
return {
|
|
118
|
+
title,
|
|
119
|
+
rawTitle,
|
|
120
|
+
branch,
|
|
121
|
+
specReference,
|
|
122
|
+
status,
|
|
123
|
+
createdAt,
|
|
124
|
+
metadata,
|
|
125
|
+
content: mainContent,
|
|
126
|
+
crossReferences,
|
|
127
|
+
filePath,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Extract the raw title from lines
|
|
132
|
+
*/
|
|
133
|
+
function extractRawTitle(lines) {
|
|
134
|
+
for (const line of lines) {
|
|
135
|
+
const match = line.match(PATTERNS.TITLE);
|
|
136
|
+
if (match) {
|
|
137
|
+
return match[1].trim();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Extract branch from metadata
|
|
144
|
+
*/
|
|
145
|
+
function extractBranch(lines, metadata) {
|
|
146
|
+
// Try "Branch" key first
|
|
147
|
+
const branch = metadata.get("Branch");
|
|
148
|
+
if (branch) {
|
|
149
|
+
return branch;
|
|
150
|
+
}
|
|
151
|
+
// Also try "Feature Branch" for consistency
|
|
152
|
+
const featureBranch = metadata.get("Feature Branch");
|
|
153
|
+
if (featureBranch) {
|
|
154
|
+
return featureBranch;
|
|
155
|
+
}
|
|
156
|
+
// Try direct regex match for flexibility
|
|
157
|
+
for (const line of lines) {
|
|
158
|
+
const branchMatch = line.match(PATTERNS.BRANCH);
|
|
159
|
+
if (branchMatch) {
|
|
160
|
+
return branchMatch[1].trim();
|
|
161
|
+
}
|
|
162
|
+
const featureBranchMatch = line.match(PATTERNS.FEATURE_BRANCH);
|
|
163
|
+
if (featureBranchMatch) {
|
|
164
|
+
return featureBranchMatch[1].trim();
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Extract spec reference from metadata
|
|
171
|
+
*/
|
|
172
|
+
function extractSpecReference(lines, metadata) {
|
|
173
|
+
// Try "Spec" key
|
|
174
|
+
const spec = metadata.get("Spec");
|
|
175
|
+
if (spec) {
|
|
176
|
+
// Clean up [[...]] if present
|
|
177
|
+
const refMatch = spec.match(/\[\[([^\]]+)\]\]/);
|
|
178
|
+
if (refMatch) {
|
|
179
|
+
return refMatch[1].trim();
|
|
180
|
+
}
|
|
181
|
+
return spec;
|
|
182
|
+
}
|
|
183
|
+
// Try direct regex match
|
|
184
|
+
for (const line of lines) {
|
|
185
|
+
const match = line.match(PATTERNS.SPEC_LINK);
|
|
186
|
+
if (match) {
|
|
187
|
+
return match[1].trim();
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Extract status from metadata
|
|
194
|
+
*/
|
|
195
|
+
function extractStatus(metadata) {
|
|
196
|
+
const status = metadata.get("Status") || metadata.get("status");
|
|
197
|
+
if (status) {
|
|
198
|
+
return normalizeStatus(status);
|
|
199
|
+
}
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Extract created date from metadata
|
|
204
|
+
*/
|
|
205
|
+
function extractCreatedDate(metadata) {
|
|
206
|
+
const created = metadata.get("Created") || metadata.get("created");
|
|
207
|
+
if (created) {
|
|
208
|
+
return parseDate(created);
|
|
209
|
+
}
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Check if a file appears to be a valid plan.md file
|
|
214
|
+
*
|
|
215
|
+
* @param filePath - Path to check
|
|
216
|
+
* @returns true if the file looks like a plan file
|
|
217
|
+
*/
|
|
218
|
+
export function isPlanFile(filePath) {
|
|
219
|
+
if (!existsSync(filePath)) {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
try {
|
|
223
|
+
const content = readFileSync(filePath, "utf-8");
|
|
224
|
+
const lines = content.split("\n").slice(0, 10); // Check first 10 lines
|
|
225
|
+
// Look for plan-like title
|
|
226
|
+
for (const line of lines) {
|
|
227
|
+
if (PATTERNS.TITLE.test(line)) {
|
|
228
|
+
// Check if it has "Implementation Plan" prefix or spec reference metadata
|
|
229
|
+
const hasPlanPrefix = /Implementation Plan/i.test(line);
|
|
230
|
+
const hasSpecRef = content.includes("**Spec**");
|
|
231
|
+
return hasPlanPrefix || hasSpecRef;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Extract just the title from a plan file (fast extraction)
|
|
242
|
+
*
|
|
243
|
+
* @param filePath - Path to the plan file
|
|
244
|
+
* @returns The title or null
|
|
245
|
+
*/
|
|
246
|
+
export function getPlanFileTitle(filePath) {
|
|
247
|
+
if (!existsSync(filePath)) {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
try {
|
|
251
|
+
const content = readFileSync(filePath, "utf-8");
|
|
252
|
+
const lines = content.split("\n").slice(0, 5); // Only check first 5 lines
|
|
253
|
+
const rawTitle = extractRawTitle(lines);
|
|
254
|
+
if (!rawTitle) {
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
return extractTitleWithPrefixRemoval(lines, [
|
|
258
|
+
"Implementation Plan:",
|
|
259
|
+
"Implementation Plan",
|
|
260
|
+
]) || rawTitle;
|
|
261
|
+
}
|
|
262
|
+
catch {
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Extract just the status from a plan file (fast extraction)
|
|
268
|
+
*
|
|
269
|
+
* @param filePath - Path to the plan file
|
|
270
|
+
* @returns The status or null
|
|
271
|
+
*/
|
|
272
|
+
export function getPlanFileStatus(filePath) {
|
|
273
|
+
if (!existsSync(filePath)) {
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
try {
|
|
277
|
+
const content = readFileSync(filePath, "utf-8");
|
|
278
|
+
const lines = content.split("\n").slice(0, 15); // Check first 15 lines for metadata
|
|
279
|
+
const metadata = extractMetadata(lines);
|
|
280
|
+
return extractStatus(metadata);
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
//# sourceMappingURL=plan-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-parser.js","sourceRoot":"","sources":["../../src/parser/plan-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spec Parser for Spec-Kit Integration
|
|
3
|
+
*
|
|
4
|
+
* Parses spec.md files from spec-kit and extracts structured data.
|
|
5
|
+
*
|
|
6
|
+
* Expected format:
|
|
7
|
+
* ```markdown
|
|
8
|
+
* # Feature Specification: [FEATURE NAME]
|
|
9
|
+
*
|
|
10
|
+
* **Feature Branch**: feature/xxx
|
|
11
|
+
* **Status**: Draft
|
|
12
|
+
* **Created**: 2024-01-01
|
|
13
|
+
*
|
|
14
|
+
* ## Overview
|
|
15
|
+
* ...
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Parsed result from a spec.md file
|
|
20
|
+
*/
|
|
21
|
+
export interface ParsedSpecKitSpec {
|
|
22
|
+
/** The feature/spec title (without "Feature Specification:" prefix) */
|
|
23
|
+
title: string;
|
|
24
|
+
/** Full raw title as it appears in the file */
|
|
25
|
+
rawTitle: string;
|
|
26
|
+
/** Feature branch name if specified */
|
|
27
|
+
featureBranch: string | null;
|
|
28
|
+
/** Status of the spec (e.g., "Draft", "In Progress", "Complete") */
|
|
29
|
+
status: string | null;
|
|
30
|
+
/** Creation date */
|
|
31
|
+
createdAt: Date | null;
|
|
32
|
+
/** All metadata key-value pairs */
|
|
33
|
+
metadata: Map<string, string>;
|
|
34
|
+
/** Main content (everything after metadata section) */
|
|
35
|
+
content: string;
|
|
36
|
+
/** Cross-references found in the content */
|
|
37
|
+
crossReferences: Array<{
|
|
38
|
+
id: string;
|
|
39
|
+
displayText?: string;
|
|
40
|
+
}>;
|
|
41
|
+
/** Source file path */
|
|
42
|
+
filePath: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Options for parsing spec files
|
|
46
|
+
*/
|
|
47
|
+
export interface ParseSpecOptions {
|
|
48
|
+
/** Whether to include full content (default: true) */
|
|
49
|
+
includeContent?: boolean;
|
|
50
|
+
/** Whether to extract cross-references (default: true) */
|
|
51
|
+
extractReferences?: boolean;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Parse a spec.md file and extract structured data
|
|
55
|
+
*
|
|
56
|
+
* @param filePath - Absolute path to the spec.md file
|
|
57
|
+
* @param options - Parsing options
|
|
58
|
+
* @returns Parsed spec data or null if file doesn't exist
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* const spec = parseSpec("/project/.specify/specs/001-auth/spec.md");
|
|
62
|
+
* console.log(spec?.title); // "Authentication"
|
|
63
|
+
* console.log(spec?.status); // "Draft"
|
|
64
|
+
*/
|
|
65
|
+
export declare function parseSpec(filePath: string, options?: ParseSpecOptions): ParsedSpecKitSpec | null;
|
|
66
|
+
/**
|
|
67
|
+
* Parse spec content from a string (for testing or in-memory parsing)
|
|
68
|
+
*
|
|
69
|
+
* @param content - Markdown content string
|
|
70
|
+
* @param filePath - Optional file path for reference
|
|
71
|
+
* @returns Parsed spec data or null
|
|
72
|
+
*/
|
|
73
|
+
export declare function parseSpecContent(content: string, filePath?: string): ParsedSpecKitSpec | null;
|
|
74
|
+
/**
|
|
75
|
+
* Check if a file appears to be a valid spec.md file
|
|
76
|
+
*
|
|
77
|
+
* @param filePath - Path to check
|
|
78
|
+
* @returns true if the file looks like a spec file
|
|
79
|
+
*/
|
|
80
|
+
export declare function isSpecFile(filePath: string): boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Extract just the title from a spec file (fast extraction)
|
|
83
|
+
*
|
|
84
|
+
* @param filePath - Path to the spec file
|
|
85
|
+
* @returns The title or null
|
|
86
|
+
*/
|
|
87
|
+
export declare function getSpecFileTitle(filePath: string): string | null;
|
|
88
|
+
/**
|
|
89
|
+
* Extract just the status from a spec file (fast extraction)
|
|
90
|
+
*
|
|
91
|
+
* @param filePath - Path to the spec file
|
|
92
|
+
* @returns The status or null
|
|
93
|
+
*/
|
|
94
|
+
export declare function getSpecFileStatus(filePath: string): string | null;
|
|
95
|
+
//# sourceMappingURL=spec-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec-parser.d.ts","sourceRoot":"","sources":["../../src/parser/spec-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAaH;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,oEAAoE;IACpE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,oBAAoB;IACpB,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,mCAAmC;IACnC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,uDAAuD;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,eAAe,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7D,uBAAuB;IACvB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,sDAAsD;IACtD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,0DAA0D;IAC1D,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,gBAAqB,GAC7B,iBAAiB,GAAG,IAAI,CA2D1B;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,MAAmB,GAC5B,iBAAiB,GAAG,IAAI,CAyC1B;AA6DD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAuBpD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAqBhE;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAajE"}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spec Parser for Spec-Kit Integration
|
|
3
|
+
*
|
|
4
|
+
* Parses spec.md files from spec-kit and extracts structured data.
|
|
5
|
+
*
|
|
6
|
+
* Expected format:
|
|
7
|
+
* ```markdown
|
|
8
|
+
* # Feature Specification: [FEATURE NAME]
|
|
9
|
+
*
|
|
10
|
+
* **Feature Branch**: feature/xxx
|
|
11
|
+
* **Status**: Draft
|
|
12
|
+
* **Created**: 2024-01-01
|
|
13
|
+
*
|
|
14
|
+
* ## Overview
|
|
15
|
+
* ...
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
import { readFileSync, existsSync } from "fs";
|
|
19
|
+
import { PATTERNS, extractMetadata, extractTitleWithPrefixRemoval, extractCrossReferences, findContentStartIndex, parseDate, normalizeStatus, } from "./markdown-utils.js";
|
|
20
|
+
/**
|
|
21
|
+
* Parse a spec.md file and extract structured data
|
|
22
|
+
*
|
|
23
|
+
* @param filePath - Absolute path to the spec.md file
|
|
24
|
+
* @param options - Parsing options
|
|
25
|
+
* @returns Parsed spec data or null if file doesn't exist
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* const spec = parseSpec("/project/.specify/specs/001-auth/spec.md");
|
|
29
|
+
* console.log(spec?.title); // "Authentication"
|
|
30
|
+
* console.log(spec?.status); // "Draft"
|
|
31
|
+
*/
|
|
32
|
+
export function parseSpec(filePath, options = {}) {
|
|
33
|
+
const { includeContent = true, extractReferences = true } = options;
|
|
34
|
+
if (!existsSync(filePath)) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
const rawContent = readFileSync(filePath, "utf-8");
|
|
39
|
+
const lines = rawContent.split("\n");
|
|
40
|
+
// Extract raw title
|
|
41
|
+
const rawTitle = extractRawTitle(lines);
|
|
42
|
+
if (!rawTitle) {
|
|
43
|
+
return null; // Invalid spec file without title
|
|
44
|
+
}
|
|
45
|
+
// Clean title by removing prefix
|
|
46
|
+
const title = extractTitleWithPrefixRemoval(lines, [
|
|
47
|
+
"Feature Specification:",
|
|
48
|
+
"Feature Specification",
|
|
49
|
+
]) || rawTitle;
|
|
50
|
+
// Extract metadata
|
|
51
|
+
const metadata = extractMetadata(lines);
|
|
52
|
+
// Extract specific metadata fields
|
|
53
|
+
const featureBranch = extractFeatureBranch(lines, metadata);
|
|
54
|
+
const status = extractStatus(metadata);
|
|
55
|
+
const createdAt = extractCreatedDate(metadata);
|
|
56
|
+
// Extract main content
|
|
57
|
+
let content = "";
|
|
58
|
+
if (includeContent) {
|
|
59
|
+
const contentStartIndex = findContentStartIndex(lines);
|
|
60
|
+
content = lines.slice(contentStartIndex).join("\n").trim();
|
|
61
|
+
}
|
|
62
|
+
// Extract cross-references
|
|
63
|
+
let crossReferences = [];
|
|
64
|
+
if (extractReferences) {
|
|
65
|
+
crossReferences = extractCrossReferences(rawContent);
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
title,
|
|
69
|
+
rawTitle,
|
|
70
|
+
featureBranch,
|
|
71
|
+
status,
|
|
72
|
+
createdAt,
|
|
73
|
+
metadata,
|
|
74
|
+
content,
|
|
75
|
+
crossReferences,
|
|
76
|
+
filePath,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
console.error(`[spec-parser] Failed to parse ${filePath}:`, error);
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Parse spec content from a string (for testing or in-memory parsing)
|
|
86
|
+
*
|
|
87
|
+
* @param content - Markdown content string
|
|
88
|
+
* @param filePath - Optional file path for reference
|
|
89
|
+
* @returns Parsed spec data or null
|
|
90
|
+
*/
|
|
91
|
+
export function parseSpecContent(content, filePath = "<string>") {
|
|
92
|
+
const lines = content.split("\n");
|
|
93
|
+
// Extract raw title
|
|
94
|
+
const rawTitle = extractRawTitle(lines);
|
|
95
|
+
if (!rawTitle) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
// Clean title by removing prefix
|
|
99
|
+
const title = extractTitleWithPrefixRemoval(lines, [
|
|
100
|
+
"Feature Specification:",
|
|
101
|
+
"Feature Specification",
|
|
102
|
+
]) || rawTitle;
|
|
103
|
+
// Extract metadata
|
|
104
|
+
const metadata = extractMetadata(lines);
|
|
105
|
+
// Extract specific metadata fields
|
|
106
|
+
const featureBranch = extractFeatureBranch(lines, metadata);
|
|
107
|
+
const status = extractStatus(metadata);
|
|
108
|
+
const createdAt = extractCreatedDate(metadata);
|
|
109
|
+
// Extract main content
|
|
110
|
+
const contentStartIndex = findContentStartIndex(lines);
|
|
111
|
+
const mainContent = lines.slice(contentStartIndex).join("\n").trim();
|
|
112
|
+
// Extract cross-references
|
|
113
|
+
const crossReferences = extractCrossReferences(content);
|
|
114
|
+
return {
|
|
115
|
+
title,
|
|
116
|
+
rawTitle,
|
|
117
|
+
featureBranch,
|
|
118
|
+
status,
|
|
119
|
+
createdAt,
|
|
120
|
+
metadata,
|
|
121
|
+
content: mainContent,
|
|
122
|
+
crossReferences,
|
|
123
|
+
filePath,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Extract the raw title from lines
|
|
128
|
+
*/
|
|
129
|
+
function extractRawTitle(lines) {
|
|
130
|
+
for (const line of lines) {
|
|
131
|
+
const match = line.match(PATTERNS.TITLE);
|
|
132
|
+
if (match) {
|
|
133
|
+
return match[1].trim();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Extract feature branch from metadata
|
|
140
|
+
*/
|
|
141
|
+
function extractFeatureBranch(lines, metadata) {
|
|
142
|
+
// Try "Feature Branch" key first
|
|
143
|
+
const featureBranch = metadata.get("Feature Branch");
|
|
144
|
+
if (featureBranch) {
|
|
145
|
+
return featureBranch;
|
|
146
|
+
}
|
|
147
|
+
// Try direct regex match for flexibility
|
|
148
|
+
for (const line of lines) {
|
|
149
|
+
const match = line.match(PATTERNS.FEATURE_BRANCH);
|
|
150
|
+
if (match) {
|
|
151
|
+
return match[1].trim();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Extract status from metadata
|
|
158
|
+
*/
|
|
159
|
+
function extractStatus(metadata) {
|
|
160
|
+
const status = metadata.get("Status") || metadata.get("status");
|
|
161
|
+
if (status) {
|
|
162
|
+
return normalizeStatus(status);
|
|
163
|
+
}
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Extract created date from metadata
|
|
168
|
+
*/
|
|
169
|
+
function extractCreatedDate(metadata) {
|
|
170
|
+
const created = metadata.get("Created") || metadata.get("created");
|
|
171
|
+
if (created) {
|
|
172
|
+
return parseDate(created);
|
|
173
|
+
}
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Check if a file appears to be a valid spec.md file
|
|
178
|
+
*
|
|
179
|
+
* @param filePath - Path to check
|
|
180
|
+
* @returns true if the file looks like a spec file
|
|
181
|
+
*/
|
|
182
|
+
export function isSpecFile(filePath) {
|
|
183
|
+
if (!existsSync(filePath)) {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
const content = readFileSync(filePath, "utf-8");
|
|
188
|
+
const lines = content.split("\n").slice(0, 10); // Check first 10 lines
|
|
189
|
+
// Look for spec-like title
|
|
190
|
+
for (const line of lines) {
|
|
191
|
+
if (PATTERNS.TITLE.test(line)) {
|
|
192
|
+
// Check if it has "Feature Specification" prefix or feature branch metadata
|
|
193
|
+
const hasSpecPrefix = /Feature Specification/i.test(line);
|
|
194
|
+
const hasFeatureBranch = content.includes("**Feature Branch**");
|
|
195
|
+
return hasSpecPrefix || hasFeatureBranch;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Extract just the title from a spec file (fast extraction)
|
|
206
|
+
*
|
|
207
|
+
* @param filePath - Path to the spec file
|
|
208
|
+
* @returns The title or null
|
|
209
|
+
*/
|
|
210
|
+
export function getSpecFileTitle(filePath) {
|
|
211
|
+
if (!existsSync(filePath)) {
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
try {
|
|
215
|
+
const content = readFileSync(filePath, "utf-8");
|
|
216
|
+
const lines = content.split("\n").slice(0, 5); // Only check first 5 lines
|
|
217
|
+
const rawTitle = extractRawTitle(lines);
|
|
218
|
+
if (!rawTitle) {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
return extractTitleWithPrefixRemoval(lines, [
|
|
222
|
+
"Feature Specification:",
|
|
223
|
+
"Feature Specification",
|
|
224
|
+
]) || rawTitle;
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Extract just the status from a spec file (fast extraction)
|
|
232
|
+
*
|
|
233
|
+
* @param filePath - Path to the spec file
|
|
234
|
+
* @returns The status or null
|
|
235
|
+
*/
|
|
236
|
+
export function getSpecFileStatus(filePath) {
|
|
237
|
+
if (!existsSync(filePath)) {
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
try {
|
|
241
|
+
const content = readFileSync(filePath, "utf-8");
|
|
242
|
+
const lines = content.split("\n").slice(0, 15); // Check first 15 lines for metadata
|
|
243
|
+
const metadata = extractMetadata(lines);
|
|
244
|
+
return extractStatus(metadata);
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
//# sourceMappingURL=spec-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec-parser.js","sourceRoot":"","sources":["../../src/parser/spec-parser.ts"],"names":[],"mappings":"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"}
|