lean-spec 0.2.9 โ†’ 0.2.15-dev.21022397862

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 (38) hide show
  1. package/README.md +101 -9
  2. package/bin/lean-spec-rust.js +116 -0
  3. package/bin/lean-spec.js +9 -1
  4. package/binaries/darwin-arm64/lean-spec +0 -0
  5. package/binaries/darwin-arm64/package.json +20 -0
  6. package/binaries/darwin-x64/lean-spec +0 -0
  7. package/binaries/darwin-x64/package.json +20 -0
  8. package/binaries/linux-arm64/lean-spec +0 -0
  9. package/binaries/linux-arm64/package.json +20 -0
  10. package/binaries/linux-x64/lean-spec +0 -0
  11. package/binaries/linux-x64/package.json +20 -0
  12. package/binaries/windows-x64/lean-spec.exe +0 -0
  13. package/binaries/windows-x64/package.json +20 -0
  14. package/package.json +9 -43
  15. package/templates/detailed/AGENTS.md +72 -101
  16. package/templates/standard/AGENTS.md +72 -101
  17. package/dist/backfill-WYW47B42.js +0 -5
  18. package/dist/backfill-WYW47B42.js.map +0 -1
  19. package/dist/chunk-ENX5NYTE.js +0 -297
  20. package/dist/chunk-ENX5NYTE.js.map +0 -1
  21. package/dist/chunk-FILG4YF3.js +0 -2990
  22. package/dist/chunk-FILG4YF3.js.map +0 -1
  23. package/dist/chunk-JX4PXHNC.js +0 -7945
  24. package/dist/chunk-JX4PXHNC.js.map +0 -1
  25. package/dist/chunk-K4VTB6BF.js +0 -298
  26. package/dist/chunk-K4VTB6BF.js.map +0 -1
  27. package/dist/chunk-SZNMHOHN.js +0 -444
  28. package/dist/chunk-SZNMHOHN.js.map +0 -1
  29. package/dist/cli.d.ts +0 -2
  30. package/dist/cli.js +0 -129
  31. package/dist/cli.js.map +0 -1
  32. package/dist/frontmatter-Q5UJOZLR.js +0 -3
  33. package/dist/frontmatter-Q5UJOZLR.js.map +0 -1
  34. package/dist/mcp-server.d.ts +0 -16
  35. package/dist/mcp-server.js +0 -8
  36. package/dist/mcp-server.js.map +0 -1
  37. package/dist/validate-LMCR5JTN.js +0 -5
  38. package/dist/validate-LMCR5JTN.js.map +0 -1
@@ -12,132 +12,103 @@
12
12
 
13
13
  > **Why?** Skipping discovery creates duplicate work. Manual file creation breaks LeanSpec tooling.
14
14
 
15
- ## ๐Ÿ”ง How to Manage Specs
16
-
17
- ### Primary Method: MCP Tools (Recommended)
18
-
19
- If you have LeanSpec MCP tools available, **ALWAYS use them**:
20
-
21
- | Action | MCP Tool | Description |
22
- |--------|----------|-------------|
23
- | See project status | `board` | Kanban view + project health metrics |
24
- | List all specs | `list` | Filterable list with metadata |
25
- | Search specs | `search` | Semantic search across all content |
26
- | View a spec | `view` | Full content with formatting |
27
- | Create new spec | `create` | Auto-sequences, proper structure |
28
- | Update spec | `update` | Validates transitions, timestamps |
29
- | Link specs | `link` | Add dependencies (depends_on) |
30
- | Unlink specs | `unlink` | Remove dependencies |
31
- | Check dependencies | `deps` | Visual dependency graph |
32
-
33
- **Why MCP over CLI?**
34
- - โœ… Direct tool integration (no shell execution needed)
35
- - โœ… Structured responses (better for AI reasoning)
36
- - โœ… Real-time validation (immediate feedback)
37
- - โœ… Context-aware (understands project state)
38
-
39
- ### Fallback: CLI Commands
40
-
41
- If MCP tools are not available, use CLI commands:
42
-
43
- ```bash
44
- lean-spec board # Project overview
45
- lean-spec list # See all specs
46
- lean-spec search "query" # Find relevant specs
47
- lean-spec create <name> # Create new spec
48
- lean-spec update <spec> --status <status> # Update status
49
- lean-spec link <spec> --depends-on <other> # Add dependencies
50
- lean-spec unlink <spec> --depends-on <other> # Remove dependencies
51
- lean-spec deps <spec> # Show dependencies
52
- ```
53
-
54
- **Tip:** Check if you have LeanSpec MCP tools available before using CLI.
55
-
56
- ## โš ๏ธ SDD Workflow Checkpoints
57
-
58
- ### Before Starting ANY Task
59
-
60
- 1. ๐Ÿ“‹ **Run `board`** - What's the current project state?
61
- 2. ๐Ÿ” **Run `search`** - Are there related specs already?
62
- 3. ๐Ÿ“ **Check existing specs** - Is there one for this work?
63
-
64
- ### During Implementation
65
-
66
- 4. ๐Ÿ“Š **Update status to `in-progress`** BEFORE coding
67
- 5. ๐Ÿ“ **Document decisions** in the spec as you work
68
- 6. ๐Ÿ”— **Link dependencies** if you discover blocking relationships
69
-
70
- ### After Completing Work
71
-
72
- 7. โœ… **Update status to `complete`** when done
73
- 8. ๐Ÿ“„ **Document what you learned** in the spec
74
- 9. ๐Ÿค” **Create follow-up specs** if needed
75
-
76
- ### ๐Ÿšซ Common Mistakes to Avoid
15
+ ## ๐Ÿ”ง Managing Specs
16
+
17
+ ### MCP Tools (Preferred) with CLI Fallback
18
+
19
+ | Action | MCP Tool | CLI Fallback |
20
+ |--------|----------|--------------|
21
+ | Project status | `board` | `lean-spec board` |
22
+ | List specs | `list` | `lean-spec list` |
23
+ | Search specs | `search` | `lean-spec search "query"` |
24
+ | View spec | `view` | `lean-spec view <spec>` |
25
+ | Create spec | `create` | `lean-spec create <name>` |
26
+ | Update spec | `update` | `lean-spec update <spec> --status <status>` |
27
+ | Link specs | `link` | `lean-spec link <spec> --depends-on <other>` |
28
+ | Unlink specs | `unlink` | `lean-spec unlink <spec> --depends-on <other>` |
29
+ | Dependencies | `deps` | `lean-spec deps <spec>` |
30
+ | Token count | `tokens` | `lean-spec tokens <spec>` |
31
+ | Validate specs | `validate` | `lean-spec validate` |
32
+
33
+ ## โš ๏ธ Core Rules
34
+
35
+ | Rule | Details |
36
+ |------|---------|
37
+ | **NEVER edit frontmatter manually** | Use `update`, `link`, `unlink` for: `status`, `priority`, `tags`, `assignee`, `transitions`, timestamps, `depends_on` |
38
+ | **ALWAYS link spec references** | Content mentions another spec โ†’ `lean-spec link <spec> --depends-on <other>` |
39
+ | **Track status transitions** | `planned` โ†’ `in-progress` (before coding) โ†’ `complete` (after done) |
40
+ | **Keep specs current** | Document progress, decisions, and learnings as work happens. Obsolete specs mislead both humans and AI |
41
+ | **No nested code blocks** | Use indentation instead |
42
+
43
+ ### ๐Ÿšซ Common Mistakes
77
44
 
78
45
  | โŒ Don't | โœ… Do Instead |
79
46
  |----------|---------------|
80
47
  | Create spec files manually | Use `create` tool |
81
- | Skip discovery before new work | Run `board` and `search` first |
82
- | Leave status as "planned" after starting | Update to `in-progress` immediately |
83
- | Finish work without updating spec | Document decisions, update status |
48
+ | Skip discovery | Run `board` and `search` first |
49
+ | Leave status as "planned" | Update to `in-progress` before coding |
84
50
  | Edit frontmatter manually | Use `update` tool |
85
- | Forget about specs mid-conversation | Check spec status periodically |
86
-
87
- ## Core Rules
88
-
89
- 1. **Read README.md first** - Understand project context
90
- 2. **Check specs/** - Review existing specs before starting
91
- 3. **Use MCP tools** - Prefer MCP over CLI when available
92
- 4. **Follow LeanSpec principles** - Clarity over documentation
93
- 5. **Keep it minimal** - If it doesn't add clarity, cut it
94
- 6. **NEVER manually edit frontmatter** - Use `update`, `link`, `unlink` tools
95
- 7. **Track progress in specs** - Update status and document decisions
51
+ | Complete spec without documentation | Document progress, prompts, learnings first |
96
52
 
97
- ## When to Use Specs
53
+ ## ๐Ÿ“‹ SDD Workflow
98
54
 
99
- **Write a spec for:**
100
- - Features affecting multiple parts of the system
101
- - Breaking changes or significant refactors
102
- - Design decisions needing team alignment
55
+ ```
56
+ BEFORE: board โ†’ search โ†’ check existing specs
57
+ DURING: update status to in-progress โ†’ code โ†’ document decisions โ†’ link dependencies
58
+ AFTER: document completion โ†’ update status to complete
59
+ ```
103
60
 
104
- **Skip specs for:**
105
- - Bug fixes
106
- - Trivial changes
107
- - Self-explanatory refactors
61
+ **Status tracks implementation, NOT spec writing.**
108
62
 
109
63
  ## Spec Dependencies
110
64
 
111
- ### `depends_on` - Blocking Dependency
112
- Directional dependency - this spec cannot start until dependencies are complete.
113
- **Use when:** True blocking dependency, work order matters, one spec builds on another.
65
+ Use `depends_on` to express blocking relationships between specs:
66
+ - **`depends_on`** = True blocker, work order matters, directional (A depends on B)
114
67
 
68
+ Link dependencies when one spec builds on another:
115
69
  ```bash
116
70
  lean-spec link <spec> --depends-on <other-spec>
117
71
  ```
118
72
 
119
- **Note:** Only use `depends_on` for actual blocking dependencies. Don't link specs just because they're "related" - link them when one truly blocks the other.
120
-
121
- ## Quality Standards
73
+ ## When to Use Specs
122
74
 
123
- - **Status tracking is mandatory:**
124
- - `planned` โ†’ after creation
125
- - `in-progress` โ†’ BEFORE starting implementation
126
- - `complete` โ†’ AFTER finishing implementation
127
- - Specs stay in sync with implementation
128
- - Never leave specs with stale status
75
+ | โœ… Write spec | โŒ Skip spec |
76
+ |---------------|--------------|
77
+ | Multi-part features | Bug fixes |
78
+ | Breaking changes | Trivial changes |
79
+ | Design decisions | Self-explanatory refactors |
129
80
 
130
- ## Spec Complexity Guidelines
81
+ ## Token Thresholds
131
82
 
132
83
  | Tokens | Status |
133
84
  |--------|--------|
134
85
  | <2,000 | โœ… Optimal |
135
86
  | 2,000-3,500 | โœ… Good |
136
87
  | 3,500-5,000 | โš ๏ธ Consider splitting |
137
- | >5,000 | ๐Ÿ”ด Should split |
88
+ | >5,000 | ๐Ÿ”ด Must split |
89
+
90
+ ## Quality Validation
91
+
92
+ Before completing work, validate spec quality:
93
+ ```bash
94
+ lean-spec validate # Check structure and quality
95
+ lean-spec validate --check-deps # Verify dependency alignment
96
+ ```
97
+
98
+ Validation checks:
99
+ - Missing required sections
100
+ - Excessive length (>400 lines)
101
+ - Content/frontmatter dependency misalignment
102
+ - Invalid frontmatter fields
103
+
104
+ ## First Principles (Priority Order)
138
105
 
139
- Use `tokens` tool to check spec size.
106
+ 1. **Context Economy** - <2,000 tokens optimal, >3,500 needs splitting
107
+ 2. **Signal-to-Noise** - Every word must inform a decision
108
+ 3. **Intent Over Implementation** - Capture why, let how emerge
109
+ 4. **Bridge the Gap** - Both human and AI must understand
110
+ 5. **Progressive Disclosure** - Add complexity only when pain is felt
140
111
 
141
112
  ---
142
113
 
143
- **Remember:** LeanSpec tracks what you're building. Keep specs in sync with your work!
114
+ **Remember:** LeanSpec tracks what you're building. Keep specs in sync with your work!
@@ -1,5 +0,0 @@
1
- export { backfillCommand, backfillTimestamps } from './chunk-SZNMHOHN.js';
2
- import './chunk-K4VTB6BF.js';
3
- import './chunk-ENX5NYTE.js';
4
- //# sourceMappingURL=backfill-WYW47B42.js.map
5
- //# sourceMappingURL=backfill-WYW47B42.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"backfill-WYW47B42.js"}
@@ -1,297 +0,0 @@
1
- import * as fs from 'fs/promises';
2
- import * as path from 'path';
3
- import matter from 'gray-matter';
4
- import yaml from 'js-yaml';
5
- import dayjs from 'dayjs';
6
-
7
- // src/frontmatter.ts
8
- function normalizeDateFields(data) {
9
- const dateFields = ["created", "completed", "updated", "due"];
10
- for (const field of dateFields) {
11
- if (data[field] instanceof Date) {
12
- data[field] = data[field].toISOString().split("T")[0];
13
- }
14
- }
15
- }
16
- function enrichWithTimestamps(data, previousData) {
17
- const now = (/* @__PURE__ */ new Date()).toISOString();
18
- if (!data.created_at) {
19
- data.created_at = now;
20
- }
21
- if (previousData) {
22
- data.updated_at = now;
23
- }
24
- if (data.status === "complete" && previousData?.status !== "complete" && !data.completed_at) {
25
- data.completed_at = now;
26
- if (!data.completed) {
27
- data.completed = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
28
- }
29
- }
30
- if (previousData && data.status !== previousData.status) {
31
- if (!Array.isArray(data.transitions)) {
32
- data.transitions = [];
33
- }
34
- data.transitions.push({
35
- status: data.status,
36
- at: now
37
- });
38
- }
39
- }
40
- function normalizeTagsField(data) {
41
- if (data.tags && typeof data.tags === "string") {
42
- try {
43
- const parsed = JSON.parse(data.tags);
44
- if (Array.isArray(parsed)) {
45
- data.tags = parsed;
46
- }
47
- } catch {
48
- data.tags = data.tags.split(",").map((t) => t.trim());
49
- }
50
- }
51
- }
52
- function validateCustomField(value, expectedType) {
53
- switch (expectedType) {
54
- case "string":
55
- if (typeof value === "string") {
56
- return { valid: true, coerced: value };
57
- }
58
- return { valid: true, coerced: String(value) };
59
- case "number":
60
- if (typeof value === "number") {
61
- return { valid: true, coerced: value };
62
- }
63
- const num = Number(value);
64
- if (!isNaN(num)) {
65
- return { valid: true, coerced: num };
66
- }
67
- return { valid: false, error: `Cannot convert '${value}' to number` };
68
- case "boolean":
69
- if (typeof value === "boolean") {
70
- return { valid: true, coerced: value };
71
- }
72
- if (value === "true" || value === "yes" || value === "1") {
73
- return { valid: true, coerced: true };
74
- }
75
- if (value === "false" || value === "no" || value === "0") {
76
- return { valid: true, coerced: false };
77
- }
78
- return { valid: false, error: `Cannot convert '${value}' to boolean` };
79
- case "array":
80
- if (Array.isArray(value)) {
81
- return { valid: true, coerced: value };
82
- }
83
- return { valid: false, error: `Expected array but got ${typeof value}` };
84
- default:
85
- return { valid: false, error: `Unknown type: ${expectedType}` };
86
- }
87
- }
88
- function validateCustomFields(frontmatter, config) {
89
- if (!config?.frontmatter?.custom) {
90
- return frontmatter;
91
- }
92
- const result = { ...frontmatter };
93
- for (const [fieldName, expectedType] of Object.entries(config.frontmatter.custom)) {
94
- if (fieldName in result) {
95
- const validation = validateCustomField(result[fieldName], expectedType);
96
- if (validation.valid) {
97
- result[fieldName] = validation.coerced;
98
- } else {
99
- console.warn(`Warning: Invalid custom field '${fieldName}': ${validation.error}`);
100
- }
101
- }
102
- }
103
- return result;
104
- }
105
- async function parseFrontmatter(filePath, config) {
106
- try {
107
- const content = await fs.readFile(filePath, "utf-8");
108
- const parsed = matter(content, {
109
- engines: {
110
- yaml: (str) => yaml.load(str, { schema: yaml.FAILSAFE_SCHEMA })
111
- }
112
- });
113
- if (!parsed.data || Object.keys(parsed.data).length === 0) {
114
- return parseFallbackFields(content);
115
- }
116
- if (!parsed.data.status) {
117
- console.warn(`Warning: Missing required field 'status' in ${filePath}`);
118
- return null;
119
- }
120
- if (!parsed.data.created) {
121
- console.warn(`Warning: Missing required field 'created' in ${filePath}`);
122
- return null;
123
- }
124
- const validStatuses = ["planned", "in-progress", "complete", "archived"];
125
- if (!validStatuses.includes(parsed.data.status)) {
126
- console.warn(`Warning: Invalid status '${parsed.data.status}' in ${filePath}. Valid values: ${validStatuses.join(", ")}`);
127
- }
128
- if (parsed.data.priority) {
129
- const validPriorities = ["low", "medium", "high", "critical"];
130
- if (!validPriorities.includes(parsed.data.priority)) {
131
- console.warn(`Warning: Invalid priority '${parsed.data.priority}' in ${filePath}. Valid values: ${validPriorities.join(", ")}`);
132
- }
133
- }
134
- normalizeTagsField(parsed.data);
135
- const knownFields = [
136
- "status",
137
- "created",
138
- "tags",
139
- "priority",
140
- "depends_on",
141
- "updated",
142
- "completed",
143
- "assignee",
144
- "reviewer",
145
- "issue",
146
- "pr",
147
- "epic",
148
- "breaking",
149
- "due",
150
- "created_at",
151
- "updated_at",
152
- "completed_at",
153
- "transitions"
154
- ];
155
- const customFields = config?.frontmatter?.custom ? Object.keys(config.frontmatter.custom) : [];
156
- const allKnownFields = [...knownFields, ...customFields];
157
- const unknownFields = Object.keys(parsed.data).filter((k) => !allKnownFields.includes(k));
158
- if (unknownFields.length > 0) {
159
- console.warn(`Info: Unknown fields in ${filePath}: ${unknownFields.join(", ")}`);
160
- }
161
- const validatedData = validateCustomFields(parsed.data, config);
162
- return validatedData;
163
- } catch (error) {
164
- console.error(`Error parsing frontmatter from ${filePath}:`, error);
165
- return null;
166
- }
167
- }
168
- function parseFallbackFields(content) {
169
- const statusMatch = content.match(/\*\*Status\*\*:\s*(?:๐Ÿ“…\s*)?(\w+(?:-\w+)?)/i);
170
- const createdMatch = content.match(/\*\*Created\*\*:\s*(\d{4}-\d{2}-\d{2})/);
171
- if (statusMatch && createdMatch) {
172
- const status = statusMatch[1].toLowerCase().replace(/\s+/g, "-");
173
- const created = createdMatch[1];
174
- return {
175
- status,
176
- created
177
- };
178
- }
179
- return null;
180
- }
181
- async function updateFrontmatter(filePath, updates) {
182
- const content = await fs.readFile(filePath, "utf-8");
183
- const parsed = matter(content, {
184
- engines: {
185
- yaml: (str) => yaml.load(str, { schema: yaml.FAILSAFE_SCHEMA })
186
- }
187
- });
188
- const previousData = { ...parsed.data };
189
- const newData = { ...parsed.data, ...updates };
190
- normalizeDateFields(newData);
191
- enrichWithTimestamps(newData, previousData);
192
- if (updates.status === "complete" && !newData.completed) {
193
- newData.completed = dayjs().format("YYYY-MM-DD");
194
- }
195
- if ("updated" in parsed.data) {
196
- newData.updated = dayjs().format("YYYY-MM-DD");
197
- }
198
- let updatedContent = parsed.content;
199
- updatedContent = updateVisualMetadata(updatedContent, newData);
200
- const newContent = matter.stringify(updatedContent, newData);
201
- await fs.writeFile(filePath, newContent, "utf-8");
202
- }
203
- function updateVisualMetadata(content, frontmatter) {
204
- const statusEmoji = getStatusEmojiPlain(frontmatter.status);
205
- const statusLabel = frontmatter.status.charAt(0).toUpperCase() + frontmatter.status.slice(1).replace("-", " ");
206
- const created = dayjs(frontmatter.created).format("YYYY-MM-DD");
207
- let metadataLine = `> **Status**: ${statusEmoji} ${statusLabel}`;
208
- if (frontmatter.priority) {
209
- const priorityLabel = frontmatter.priority.charAt(0).toUpperCase() + frontmatter.priority.slice(1);
210
- metadataLine += ` \xB7 **Priority**: ${priorityLabel}`;
211
- }
212
- metadataLine += ` \xB7 **Created**: ${created}`;
213
- if (frontmatter.tags && frontmatter.tags.length > 0) {
214
- metadataLine += ` \xB7 **Tags**: ${frontmatter.tags.join(", ")}`;
215
- }
216
- let secondLine = "";
217
- if (frontmatter.assignee || frontmatter.reviewer) {
218
- const assignee = frontmatter.assignee || "TBD";
219
- const reviewer = frontmatter.reviewer || "TBD";
220
- secondLine = `
221
- > **Assignee**: ${assignee} \xB7 **Reviewer**: ${reviewer}`;
222
- }
223
- const metadataPattern = /^>\s+\*\*Status\*\*:.*(?:\n>\s+\*\*Assignee\*\*:.*)?/m;
224
- if (metadataPattern.test(content)) {
225
- return content.replace(metadataPattern, metadataLine + secondLine);
226
- } else {
227
- const titleMatch = content.match(/^#\s+.+$/m);
228
- if (titleMatch) {
229
- const insertPos = titleMatch.index + titleMatch[0].length;
230
- return content.slice(0, insertPos) + "\n\n" + metadataLine + secondLine + "\n" + content.slice(insertPos);
231
- }
232
- }
233
- return content;
234
- }
235
- function getStatusEmojiPlain(status) {
236
- switch (status) {
237
- case "planned":
238
- return "\u{1F5D3}\uFE0F";
239
- case "in-progress":
240
- return "\u23F3";
241
- case "complete":
242
- return "\u2705";
243
- case "archived":
244
- return "\u{1F4E6}";
245
- default:
246
- return "\u{1F4C4}";
247
- }
248
- }
249
- async function getSpecFile(specDir, defaultFile = "README.md") {
250
- const specFile = path.join(specDir, defaultFile);
251
- try {
252
- await fs.access(specFile);
253
- return specFile;
254
- } catch {
255
- return null;
256
- }
257
- }
258
- function matchesFilter(frontmatter, filter) {
259
- if (filter.status) {
260
- const statuses = Array.isArray(filter.status) ? filter.status : [filter.status];
261
- if (!statuses.includes(frontmatter.status)) {
262
- return false;
263
- }
264
- }
265
- if (filter.tags && filter.tags.length > 0) {
266
- if (!frontmatter.tags || frontmatter.tags.length === 0) {
267
- return false;
268
- }
269
- const hasAllTags = filter.tags.every((tag) => frontmatter.tags.includes(tag));
270
- if (!hasAllTags) {
271
- return false;
272
- }
273
- }
274
- if (filter.priority) {
275
- const priorities = Array.isArray(filter.priority) ? filter.priority : [filter.priority];
276
- if (!frontmatter.priority || !priorities.includes(frontmatter.priority)) {
277
- return false;
278
- }
279
- }
280
- if (filter.assignee) {
281
- if (frontmatter.assignee !== filter.assignee) {
282
- return false;
283
- }
284
- }
285
- if (filter.customFields) {
286
- for (const [key, value] of Object.entries(filter.customFields)) {
287
- if (frontmatter[key] !== value) {
288
- return false;
289
- }
290
- }
291
- }
292
- return true;
293
- }
294
-
295
- export { enrichWithTimestamps, getSpecFile, matchesFilter, normalizeDateFields, normalizeTagsField, parseFrontmatter, updateFrontmatter, validateCustomField, validateCustomFields };
296
- //# sourceMappingURL=chunk-ENX5NYTE.js.map
297
- //# sourceMappingURL=chunk-ENX5NYTE.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/frontmatter.ts"],"names":[],"mappings":"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","file":"chunk-ENX5NYTE.js","sourcesContent":["import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport matter from 'gray-matter';\nimport yaml from 'js-yaml';\nimport dayjs from 'dayjs';\nimport type { LeanSpecConfig } from './config.js';\n\n// Valid status values\nexport type SpecStatus = 'planned' | 'in-progress' | 'complete' | 'archived';\n\n// Valid priority values\nexport type SpecPriority = 'low' | 'medium' | 'high' | 'critical';\n\n// Status transition record\nexport interface StatusTransition {\n status: SpecStatus;\n at: string; // ISO 8601 timestamp\n}\n\n// Core frontmatter fields\nexport interface SpecFrontmatter {\n // Required fields\n status: SpecStatus;\n created: string; // YYYY-MM-DD format\n\n // Recommended fields\n tags?: string[];\n priority?: SpecPriority;\n\n // Power user fields\n depends_on?: string[];\n updated?: string;\n completed?: string;\n assignee?: string;\n reviewer?: string;\n issue?: string;\n pr?: string;\n epic?: string;\n breaking?: boolean;\n due?: string; // YYYY-MM-DD format\n\n // Timestamp fields (for velocity tracking)\n created_at?: string; // ISO 8601 timestamp\n updated_at?: string; // ISO 8601 timestamp\n completed_at?: string; // ISO 8601 timestamp\n transitions?: StatusTransition[]; // Status change history\n\n // Allow any additional fields (for extensibility)\n [key: string]: unknown;\n}\n\n/**\n * Convert Date objects to YYYY-MM-DD string format\n * (gray-matter auto-parses YYYY-MM-DD strings as Date objects)\n */\nexport function normalizeDateFields(data: Record<string, unknown>): void {\n const dateFields = ['created', 'completed', 'updated', 'due'];\n \n for (const field of dateFields) {\n if (data[field] instanceof Date) {\n data[field] = (data[field] as Date).toISOString().split('T')[0];\n }\n }\n}\n\n/**\n * Enrich frontmatter with timestamps for velocity tracking\n * Auto-generates timestamps when missing and tracks status transitions\n */\nexport function enrichWithTimestamps(\n data: Record<string, unknown>,\n previousData?: Record<string, unknown>\n): void {\n const now = new Date().toISOString();\n\n // Set created_at if missing - always use current timestamp\n // Do NOT infer from created date field since that's just YYYY-MM-DD without time\n if (!data.created_at) {\n data.created_at = now;\n }\n\n // Update updated_at on any change (if previousData exists)\n if (previousData) {\n data.updated_at = now;\n }\n\n // Set completed_at when status changes to complete\n if (\n data.status === 'complete' &&\n previousData?.status !== 'complete' &&\n !data.completed_at\n ) {\n data.completed_at = now;\n // Also set the completed date field\n if (!data.completed) {\n data.completed = new Date().toISOString().split('T')[0];\n }\n }\n\n // Track status transition (optional)\n if (previousData && data.status !== previousData.status) {\n if (!Array.isArray(data.transitions)) {\n data.transitions = [];\n }\n (data.transitions as StatusTransition[]).push({\n status: data.status as SpecStatus,\n at: now,\n });\n }\n}\n\n/**\n * Normalize tags field - parse JSON strings into arrays\n * Handles cases where AI accidentally creates tags as '[\"..\",\"..\"]' strings\n */\nexport function normalizeTagsField(data: Record<string, unknown>): void {\n if (data.tags && typeof data.tags === 'string') {\n try {\n // Try to parse as JSON array\n const parsed = JSON.parse(data.tags as string);\n if (Array.isArray(parsed)) {\n data.tags = parsed;\n }\n } catch {\n // If not valid JSON, treat as comma-separated string\n data.tags = (data.tags as string).split(',').map(t => t.trim());\n }\n }\n}\n\n/**\n * Validate and coerce custom field types\n */\nexport function validateCustomField(\n value: unknown,\n expectedType: 'string' | 'number' | 'boolean' | 'array'\n): { valid: boolean; coerced?: unknown; error?: string } {\n switch (expectedType) {\n case 'string':\n if (typeof value === 'string') {\n return { valid: true, coerced: value };\n }\n // Coerce to string\n return { valid: true, coerced: String(value) };\n \n case 'number':\n if (typeof value === 'number') {\n return { valid: true, coerced: value };\n }\n // Try to coerce to number\n const num = Number(value);\n if (!isNaN(num)) {\n return { valid: true, coerced: num };\n }\n return { valid: false, error: `Cannot convert '${value}' to number` };\n \n case 'boolean':\n if (typeof value === 'boolean') {\n return { valid: true, coerced: value };\n }\n // Coerce string to boolean\n if (value === 'true' || value === 'yes' || value === '1') {\n return { valid: true, coerced: true };\n }\n if (value === 'false' || value === 'no' || value === '0') {\n return { valid: true, coerced: false };\n }\n return { valid: false, error: `Cannot convert '${value}' to boolean` };\n \n case 'array':\n if (Array.isArray(value)) {\n return { valid: true, coerced: value };\n }\n return { valid: false, error: `Expected array but got ${typeof value}` };\n \n default:\n return { valid: false, error: `Unknown type: ${expectedType}` };\n }\n}\n\n/**\n * Validate custom fields according to config\n */\nexport function validateCustomFields(\n frontmatter: Record<string, unknown>,\n config?: LeanSpecConfig\n): Record<string, unknown> {\n if (!config?.frontmatter?.custom) {\n return frontmatter;\n }\n \n const result = { ...frontmatter };\n \n for (const [fieldName, expectedType] of Object.entries(config.frontmatter.custom)) {\n if (fieldName in result) {\n const validation = validateCustomField(result[fieldName], expectedType);\n if (validation.valid) {\n result[fieldName] = validation.coerced;\n } else {\n console.warn(`Warning: Invalid custom field '${fieldName}': ${validation.error}`);\n }\n }\n }\n \n return result;\n}\n\n// Parse frontmatter from a spec file\nexport async function parseFrontmatter(\n filePath: string,\n config?: LeanSpecConfig\n): Promise<SpecFrontmatter | null> {\n try {\n const content = await fs.readFile(filePath, 'utf-8');\n const parsed = matter(content, {\n engines: {\n yaml: (str) => yaml.load(str, { schema: yaml.FAILSAFE_SCHEMA }) as Record<string, unknown>\n }\n });\n\n if (!parsed.data || Object.keys(parsed.data).length === 0) {\n // No frontmatter found, try fallback to inline fields\n return parseFallbackFields(content);\n }\n\n // Validate required fields\n if (!parsed.data.status) {\n console.warn(`Warning: Missing required field 'status' in ${filePath}`);\n return null;\n }\n\n if (!parsed.data.created) {\n console.warn(`Warning: Missing required field 'created' in ${filePath}`);\n return null;\n }\n\n // Validate status enum\n const validStatuses: SpecStatus[] = ['planned', 'in-progress', 'complete', 'archived'];\n if (!validStatuses.includes(parsed.data.status)) {\n console.warn(`Warning: Invalid status '${parsed.data.status}' in ${filePath}. Valid values: ${validStatuses.join(', ')}`);\n }\n\n // Validate priority enum if present\n if (parsed.data.priority) {\n const validPriorities: SpecPriority[] = ['low', 'medium', 'high', 'critical'];\n if (!validPriorities.includes(parsed.data.priority)) {\n console.warn(`Warning: Invalid priority '${parsed.data.priority}' in ${filePath}. Valid values: ${validPriorities.join(', ')}`);\n }\n }\n\n // Normalize tags field (parse JSON strings to arrays)\n normalizeTagsField(parsed.data);\n \n // Warn about unknown fields (informational only)\n const knownFields = [\n 'status', 'created', 'tags', 'priority', 'depends_on',\n 'updated', 'completed', 'assignee', 'reviewer', 'issue', 'pr', 'epic', 'breaking', 'due',\n 'created_at', 'updated_at', 'completed_at', 'transitions'\n ];\n \n // Add custom fields from config to known fields\n const customFields = config?.frontmatter?.custom ? Object.keys(config.frontmatter.custom) : [];\n const allKnownFields = [...knownFields, ...customFields];\n \n const unknownFields = Object.keys(parsed.data).filter(k => !allKnownFields.includes(k));\n if (unknownFields.length > 0) {\n console.warn(`Info: Unknown fields in ${filePath}: ${unknownFields.join(', ')}`);\n }\n \n // Validate and coerce custom fields\n const validatedData = validateCustomFields(parsed.data, config);\n\n return validatedData as SpecFrontmatter;\n } catch (error) {\n console.error(`Error parsing frontmatter from ${filePath}:`, error);\n return null;\n }\n}\n\n// Fallback: Parse inline fields from older specs\nfunction parseFallbackFields(content: string): SpecFrontmatter | null {\n const statusMatch = content.match(/\\*\\*Status\\*\\*:\\s*(?:๐Ÿ“…\\s*)?(\\w+(?:-\\w+)?)/i);\n const createdMatch = content.match(/\\*\\*Created\\*\\*:\\s*(\\d{4}-\\d{2}-\\d{2})/);\n\n if (statusMatch && createdMatch) {\n const status = statusMatch[1].toLowerCase().replace(/\\s+/g, '-') as SpecStatus;\n const created = createdMatch[1];\n\n return {\n status,\n created,\n };\n }\n\n return null;\n}\n\n// Update frontmatter in a spec file\nexport async function updateFrontmatter(\n filePath: string,\n updates: Partial<SpecFrontmatter>\n): Promise<void> {\n const content = await fs.readFile(filePath, 'utf-8');\n const parsed = matter(content, {\n engines: {\n yaml: (str) => yaml.load(str, { schema: yaml.FAILSAFE_SCHEMA }) as Record<string, unknown>\n }\n });\n\n // Store previous data for timestamp enrichment\n const previousData = { ...parsed.data };\n\n // Merge updates with existing data\n const newData = { ...parsed.data, ...updates };\n\n // Ensure date fields remain as strings (gray-matter auto-parses YYYY-MM-DD as Date objects)\n normalizeDateFields(newData);\n\n // Enrich with timestamps\n enrichWithTimestamps(newData, previousData);\n\n // Auto-update timestamps if fields exist (legacy behavior)\n if (updates.status === 'complete' && !newData.completed) {\n newData.completed = dayjs().format('YYYY-MM-DD');\n }\n\n if ('updated' in parsed.data) {\n newData.updated = dayjs().format('YYYY-MM-DD');\n }\n\n // Update visual metadata badges in content\n let updatedContent = parsed.content;\n updatedContent = updateVisualMetadata(updatedContent, newData as SpecFrontmatter);\n\n // Stringify back to file\n const newContent = matter.stringify(updatedContent, newData);\n await fs.writeFile(filePath, newContent, 'utf-8');\n}\n\n// Update visual metadata badges in content\nfunction updateVisualMetadata(content: string, frontmatter: SpecFrontmatter): string {\n const statusEmoji = getStatusEmojiPlain(frontmatter.status);\n const statusLabel = frontmatter.status.charAt(0).toUpperCase() + frontmatter.status.slice(1).replace('-', ' ');\n \n // Parse created date with dayjs - handles all formats consistently\n const created = dayjs(frontmatter.created).format('YYYY-MM-DD');\n \n // Build metadata line\n let metadataLine = `> **Status**: ${statusEmoji} ${statusLabel}`;\n \n if (frontmatter.priority) {\n const priorityLabel = frontmatter.priority.charAt(0).toUpperCase() + frontmatter.priority.slice(1);\n metadataLine += ` ยท **Priority**: ${priorityLabel}`;\n }\n \n metadataLine += ` ยท **Created**: ${created}`;\n \n if (frontmatter.tags && frontmatter.tags.length > 0) {\n metadataLine += ` ยท **Tags**: ${frontmatter.tags.join(', ')}`;\n }\n \n // For enterprise template with assignee/reviewer\n let secondLine = '';\n if (frontmatter.assignee || frontmatter.reviewer) {\n const assignee = frontmatter.assignee || 'TBD';\n const reviewer = frontmatter.reviewer || 'TBD';\n secondLine = `\\n> **Assignee**: ${assignee} ยท **Reviewer**: ${reviewer}`;\n }\n \n // Replace existing metadata block or add after title\n const metadataPattern = /^>\\s+\\*\\*Status\\*\\*:.*(?:\\n>\\s+\\*\\*Assignee\\*\\*:.*)?/m;\n \n if (metadataPattern.test(content)) {\n // Replace existing metadata\n return content.replace(metadataPattern, metadataLine + secondLine);\n } else {\n // Add after title (# title)\n const titleMatch = content.match(/^#\\s+.+$/m);\n if (titleMatch) {\n const insertPos = titleMatch.index! + titleMatch[0].length;\n return content.slice(0, insertPos) + '\\n\\n' + metadataLine + secondLine + '\\n' + content.slice(insertPos);\n }\n }\n \n return content;\n}\n\nfunction getStatusEmojiPlain(status: string): string {\n switch (status) {\n case 'planned': return '๐Ÿ—“๏ธ';\n case 'in-progress': return 'โณ';\n case 'complete': return 'โœ…';\n case 'archived': return '๐Ÿ“ฆ';\n default: return '๐Ÿ“„';\n }\n}\n\n// Get spec file path from spec directory\nexport async function getSpecFile(specDir: string, defaultFile: string = 'README.md'): Promise<string | null> {\n const specFile = path.join(specDir, defaultFile);\n \n try {\n await fs.access(specFile);\n return specFile;\n } catch {\n return null;\n }\n}\n\n// Filter specs by criteria\nexport interface SpecFilterOptions {\n status?: SpecStatus | SpecStatus[];\n tags?: string[];\n priority?: SpecPriority | SpecPriority[];\n assignee?: string;\n customFields?: Record<string, unknown>;\n}\n\nexport function matchesFilter(frontmatter: SpecFrontmatter, filter: SpecFilterOptions): boolean {\n // Status filter\n if (filter.status) {\n const statuses = Array.isArray(filter.status) ? filter.status : [filter.status];\n if (!statuses.includes(frontmatter.status)) {\n return false;\n }\n }\n\n // Tags filter (spec must have ALL specified tags)\n if (filter.tags && filter.tags.length > 0) {\n if (!frontmatter.tags || frontmatter.tags.length === 0) {\n return false;\n }\n const hasAllTags = filter.tags.every(tag => frontmatter.tags!.includes(tag));\n if (!hasAllTags) {\n return false;\n }\n }\n\n // Priority filter\n if (filter.priority) {\n const priorities = Array.isArray(filter.priority) ? filter.priority : [filter.priority];\n if (!frontmatter.priority || !priorities.includes(frontmatter.priority)) {\n return false;\n }\n }\n\n // Assignee filter\n if (filter.assignee) {\n if (frontmatter.assignee !== filter.assignee) {\n return false;\n }\n }\n \n // Custom fields filter\n if (filter.customFields) {\n for (const [key, value] of Object.entries(filter.customFields)) {\n if (frontmatter[key] !== value) {\n return false;\n }\n }\n }\n\n return true;\n}\n"]}