lean-spec 0.2.7 → 0.2.9-dev.20251205030455

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/git-timestamps.ts","../src/utils/path-helpers.ts","../src/utils/bootstrap-helpers.ts","../src/commands/backfill.ts"],"names":["result","fs2","path2","path3","matter","fs3"],"mappings":"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","file":"chunk-H5MCUMBK.js","sourcesContent":["import { execSync } from 'node:child_process';\nimport * as path from 'node:path';\nimport type { SpecStatus, StatusTransition } from '../frontmatter.js';\n\nexport interface GitTimestampData {\n created_at?: string;\n updated_at?: string;\n completed_at?: string;\n assignee?: string;\n transitions?: StatusTransition[];\n}\n\n/**\n * Check if the current directory is a git repository\n */\nexport function isGitRepository(): boolean {\n try {\n execSync('git rev-parse --is-inside-work-tree', { \n stdio: 'ignore',\n encoding: 'utf-8' \n });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Get the timestamp of the first commit that created a file\n */\nexport function getFirstCommitTimestamp(filePath: string): string | null {\n try {\n // Use --follow to track file renames\n // Use --diff-filter=A to find the commit that added the file\n // Format as ISO 8601 timestamp\n const timestamp = execSync(\n `git log --follow --format=\"%aI\" --diff-filter=A -- \"${filePath}\" | tail -1`,\n { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] }\n ).trim();\n \n return timestamp || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Get the timestamp of the most recent commit that modified a file\n */\nexport function getLastCommitTimestamp(filePath: string): string | null {\n try {\n const timestamp = execSync(\n `git log --format=\"%aI\" -n 1 -- \"${filePath}\"`,\n { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] }\n ).trim();\n \n return timestamp || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Get the timestamp when a spec was marked as complete\n * Searches git history for status changes to \"complete\"\n */\nexport function getCompletionTimestamp(filePath: string): string | null {\n try {\n // Get all commits that modified the file, with patch output\n const gitLog = execSync(\n `git log --format=\"%H|%aI\" -p -- \"${filePath}\"`,\n { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] }\n );\n \n // Parse commits to find status change to complete\n const commits = gitLog.split('\\ndiff --git').map(section => section.trim());\n \n for (const commit of commits) {\n if (!commit) continue;\n \n // Extract commit hash and timestamp from header\n const headerMatch = commit.match(/^([a-f0-9]{40})\\|([^\\n]+)/);\n if (!headerMatch) continue;\n \n const [, , timestamp] = headerMatch;\n \n // Look for status: complete in the diff\n // Check for both YAML frontmatter and inline status changes\n if (\n /^\\+status:\\s*['\"]?complete['\"]?/m.test(commit) ||\n /^\\+\\*\\*Status\\*\\*:.*complete/mi.test(commit)\n ) {\n return timestamp;\n }\n }\n \n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Get the author of the first commit (for assignee inference)\n */\nexport function getFirstCommitAuthor(filePath: string): string | null {\n try {\n const author = execSync(\n `git log --follow --format=\"%an\" --diff-filter=A -- \"${filePath}\" | tail -1`,\n { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] }\n ).trim();\n \n return author || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Parse all status transitions from git history\n * Reconstructs the full status change timeline\n */\nexport function parseStatusTransitions(filePath: string): StatusTransition[] {\n const transitions: StatusTransition[] = [];\n \n try {\n // Get all commits that modified the file, with patch output\n const gitLog = execSync(\n `git log --format=\"%H|%aI\" -p --reverse -- \"${filePath}\"`,\n { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] }\n );\n \n // Parse commits in chronological order (--reverse)\n const commits = gitLog.split('\\ndiff --git').map(section => section.trim());\n \n const validStatuses: SpecStatus[] = ['planned', 'in-progress', 'complete', 'archived'];\n \n for (const commit of commits) {\n if (!commit) continue;\n \n // Extract commit hash and timestamp from header\n const headerMatch = commit.match(/^([a-f0-9]{40})\\|([^\\n]+)/);\n if (!headerMatch) continue;\n \n const [, , timestamp] = headerMatch;\n \n // Look for status changes in the diff\n // Match: +status: complete or +status: 'complete'\n const statusMatch = commit.match(/^\\+status:\\s*['\"]?(\\w+(?:-\\w+)?)['\"]?/m);\n if (statusMatch) {\n const status = statusMatch[1] as SpecStatus;\n \n // Only record valid status values\n if (validStatuses.includes(status)) {\n // Avoid duplicate consecutive transitions\n const lastTransition = transitions[transitions.length - 1];\n if (!lastTransition || lastTransition.status !== status) {\n transitions.push({ status, at: timestamp });\n }\n }\n }\n }\n \n return transitions;\n } catch {\n return [];\n }\n}\n\n/**\n * Extract all git timestamp data for a spec file\n */\nexport function extractGitTimestamps(\n filePath: string,\n options: {\n includeAssignee?: boolean;\n includeTransitions?: boolean;\n } = {}\n): GitTimestampData {\n const data: GitTimestampData = {};\n \n // Core timestamps (always extracted)\n data.created_at = getFirstCommitTimestamp(filePath) ?? undefined;\n data.updated_at = getLastCommitTimestamp(filePath) ?? undefined;\n data.completed_at = getCompletionTimestamp(filePath) ?? undefined;\n \n // Optional fields\n if (options.includeAssignee) {\n const author = getFirstCommitAuthor(filePath);\n if (author) {\n data.assignee = author;\n }\n }\n \n if (options.includeTransitions) {\n const transitions = parseStatusTransitions(filePath);\n if (transitions.length > 0) {\n data.transitions = transitions;\n }\n }\n \n return data;\n}\n\n/**\n * Validate that a file exists in git history\n */\nexport function fileExistsInGit(filePath: string): boolean {\n try {\n execSync(\n `git log -n 1 -- \"${filePath}\"`,\n { stdio: 'ignore', encoding: 'utf-8' }\n );\n return true;\n } catch {\n return false;\n }\n}\n","import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\n\n/**\n * Create a regex pattern to match spec directories with sequence numbers\n * Handles optional date prefixes like 20251103-001-name\n */\nexport function createSpecDirPattern(): RegExp {\n // Match spec directories, handling optional date prefix\n // Patterns:\n // - 001-name (simple sequence)\n // - 20251103-001-name (date prefix + sequence)\n // - spec-001-name (custom prefix + sequence)\n // - 001-测试 (Unicode/Chinese name)\n // We look for: optional-prefix + NNN + dash + name\n // The sequence is 2-4 digits (to avoid matching 8-digit dates as sequences)\n // Requires dash followed by non-digit/non-dash character to ensure this is a spec directory name\n // Uses [^0-9-] instead of [a-z] to support Unicode characters (Chinese, Japanese, etc.)\n return /(?:^|\\D)(\\d{2,4})-[^0-9-]/i;\n}\n\n/**\n * Get next global sequence number across entire specs directory\n */\nexport async function getGlobalNextSeq(specsDir: string, digits: number): Promise<string> {\n try {\n // Recursively find all spec directories with sequence numbers\n const seqNumbers: number[] = [];\n const specPattern = createSpecDirPattern();\n \n async function scanDirectory(dir: string): Promise<void> {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n \n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n \n // Check if this is a spec directory (NNN-name format)\n const match = entry.name.match(specPattern);\n if (match) {\n const seqNum = parseInt(match[1], 10);\n if (!isNaN(seqNum) && seqNum > 0) {\n seqNumbers.push(seqNum);\n }\n }\n \n // Skip archived directory to avoid confusion\n if (entry.name === 'archived') continue;\n \n // Recursively scan subdirectories (for custom pattern grouping)\n const subDir = path.join(dir, entry.name);\n await scanDirectory(subDir);\n }\n } catch {\n // Directory doesn't exist or can't be read\n }\n }\n \n await scanDirectory(specsDir);\n \n if (seqNumbers.length === 0) {\n return '1'.padStart(digits, '0');\n }\n \n const maxSeq = Math.max(...seqNumbers);\n return String(maxSeq + 1).padStart(digits, '0');\n } catch {\n return '1'.padStart(digits, '0');\n }\n}\n\n/**\n * Get next sequence number for a date directory (legacy, kept for backward compatibility)\n */\nexport async function getNextSeq(dateDir: string, digits: number): Promise<string> {\n try {\n const specPattern = createSpecDirPattern();\n const entries = await fs.readdir(dateDir, { withFileTypes: true });\n const seqNumbers = entries\n .filter((e) => e.isDirectory() && specPattern.test(e.name))\n .map((e) => {\n const match = e.name.match(specPattern);\n return match ? parseInt(match[1], 10) : NaN;\n })\n .filter((n) => !isNaN(n));\n\n if (seqNumbers.length === 0) {\n return '1'.padStart(digits, '0');\n }\n\n const maxSeq = Math.max(...seqNumbers);\n return String(maxSeq + 1).padStart(digits, '0');\n } catch {\n return '1'.padStart(digits, '0');\n }\n}\n\n/**\n * Resolve spec path in multiple ways:\n * 1. Absolute path as given\n * 2. Relative to current directory\n * 3. Relative to specs directory\n * 4. Search by spec name in all subdirectories (flat or grouped)\n * 5. Search by sequence number only\n */\nexport async function resolveSpecPath(\n specPath: string,\n cwd: string,\n specsDir: string\n): Promise<string | null> {\n // Try absolute path\n if (path.isAbsolute(specPath)) {\n try {\n await fs.access(specPath);\n return specPath;\n } catch {\n return null;\n }\n }\n\n // Try relative to cwd\n const cwdPath = path.resolve(cwd, specPath);\n try {\n await fs.access(cwdPath);\n return cwdPath;\n } catch {\n // Continue to next method\n }\n\n // Try relative to specs directory\n const specsPath = path.join(specsDir, specPath);\n try {\n await fs.access(specsPath);\n return specsPath;\n } catch {\n // Continue to next method\n }\n\n // Search by sequence number only (e.g., \"5\" or \"005\")\n const seqMatch = specPath.match(/^0*(\\d+)$/);\n if (seqMatch) {\n const seqNum = parseInt(seqMatch[1], 10);\n const result = await searchBySequence(specsDir, seqNum);\n if (result) return result;\n }\n\n // Last resort: search for spec name in all subdirectories\n const specName = specPath.replace(/^.*\\//, ''); // Get last part\n const result = await searchInAllDirectories(specsDir, specName);\n return result;\n}\n\n/**\n * Search for a spec by sequence number across all directories\n */\nasync function searchBySequence(specsDir: string, seqNum: number): Promise<string | null> {\n const specPattern = createSpecDirPattern();\n \n async function scanDirectory(dir: string): Promise<string | null> {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n \n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n \n // Check if this matches the sequence number\n const match = entry.name.match(specPattern);\n if (match) {\n const entrySeq = parseInt(match[1], 10);\n if (entrySeq === seqNum) {\n return path.join(dir, entry.name);\n }\n }\n \n // Recursively search subdirectories (including archived)\n const subDir = path.join(dir, entry.name);\n const result = await scanDirectory(subDir);\n if (result) return result;\n }\n } catch {\n // Directory doesn't exist or can't be read\n }\n \n return null;\n }\n \n return scanDirectory(specsDir);\n}\n\n/**\n * Search for a spec by name in all subdirectories\n */\nasync function searchInAllDirectories(specsDir: string, specName: string): Promise<string | null> {\n async function scanDirectory(dir: string): Promise<string | null> {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n \n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n \n // Check if this matches the spec name\n if (entry.name === specName) {\n return path.join(dir, entry.name);\n }\n \n // Recursively search subdirectories (including archived)\n const subDir = path.join(dir, entry.name);\n const result = await scanDirectory(subDir);\n if (result) return result;\n }\n } catch {\n // Directory doesn't exist or can't be read\n }\n \n return null;\n }\n \n return scanDirectory(specsDir);\n}\n","import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport matter from 'gray-matter';\nimport { loadConfig } from '../config.js';\nimport type { SpecStatus, SpecFrontmatter } from '../frontmatter.js';\nimport { \n getFirstCommitTimestamp, \n fileExistsInGit,\n parseStatusTransitions,\n} from './git-timestamps.js';\n\n/**\n * Information about a spec file that may or may not have valid frontmatter\n */\nexport interface BootstrapSpecInfo {\n path: string; // Relative path to spec directory\n fullPath: string; // Absolute path to spec directory\n filePath: string; // Absolute path to spec file (README.md)\n name: string; // Spec directory name\n content: string; // File content\n hasFrontmatter: boolean; // Does file have YAML frontmatter?\n hasValidFrontmatter: boolean; // Does frontmatter have required fields?\n existingFrontmatter?: Partial<SpecFrontmatter>; // Existing frontmatter data\n inferredStatus?: SpecStatus; // Status inferred from content/git\n inferredCreated?: string; // Created date inferred from content/git\n}\n\n/**\n * Load all spec directories, including those without valid frontmatter\n */\nexport async function loadSpecsForBootstrap(\n specsDir: string,\n options: {\n includeArchived?: boolean;\n targetSpecs?: string[];\n } = {}\n): Promise<BootstrapSpecInfo[]> {\n const specs: BootstrapSpecInfo[] = [];\n const config = await loadConfig();\n \n // Pattern to match spec directories (2 or more digits followed by dash)\n const specPattern = /^(\\d{2,})-/;\n \n async function scanDirectory(dir: string, relativePath: string = ''): Promise<void> {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n \n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n \n // Skip archived directory in main scan (handle separately)\n if (entry.name === 'archived' && relativePath === '') continue;\n \n const entryPath = path.join(dir, entry.name);\n const entryRelativePath = relativePath ? `${relativePath}/${entry.name}` : entry.name;\n \n // Check if this is a spec directory (NNN-name format)\n if (specPattern.test(entry.name)) {\n // Filter by target specs if specified\n if (options.targetSpecs && options.targetSpecs.length > 0) {\n const matches = options.targetSpecs.some(target => {\n // Match by name, number, or path\n const targetNum = target.match(/^0*(\\d+)$/)?.[1];\n const entryNum = entry.name.match(/^(\\d+)/)?.[1];\n return entry.name === target || \n entry.name.includes(target) || \n (targetNum && entryNum && parseInt(targetNum, 10) === parseInt(entryNum, 10));\n });\n if (!matches) continue;\n }\n \n const specFile = path.join(entryPath, config.structure.defaultFile);\n \n try {\n await fs.access(specFile);\n const specInfo = await analyzeSpecFile(specFile, entryPath, entryRelativePath, entry.name);\n specs.push(specInfo);\n } catch {\n // No spec file found, skip\n }\n } else {\n // Not a spec directory, scan recursively\n await scanDirectory(entryPath, entryRelativePath);\n }\n }\n } catch {\n // Directory doesn't exist or can't be read\n }\n }\n \n await scanDirectory(specsDir);\n \n // Load archived specs if requested\n if (options.includeArchived) {\n const archivedPath = path.join(specsDir, 'archived');\n await scanDirectory(archivedPath, 'archived');\n }\n \n // Sort by spec number descending\n specs.sort((a, b) => {\n const aNum = parseInt(a.name.match(/^(\\d+)/)?.[1] || '0', 10);\n const bNum = parseInt(b.name.match(/^(\\d+)/)?.[1] || '0', 10);\n return bNum - aNum;\n });\n \n return specs;\n}\n\n/**\n * Analyze a spec file and extract/infer frontmatter data\n */\nasync function analyzeSpecFile(\n filePath: string,\n fullPath: string,\n relativePath: string,\n name: string\n): Promise<BootstrapSpecInfo> {\n const content = await fs.readFile(filePath, 'utf-8');\n \n // Try to parse existing frontmatter\n let hasFrontmatter = false;\n let hasValidFrontmatter = false;\n let existingFrontmatter: Partial<SpecFrontmatter> | undefined;\n \n try {\n const parsed = matter(content);\n if (parsed.data && Object.keys(parsed.data).length > 0) {\n hasFrontmatter = true;\n existingFrontmatter = parsed.data as Partial<SpecFrontmatter>;\n \n // Check if required fields exist\n hasValidFrontmatter = !!(parsed.data.status && parsed.data.created);\n }\n } catch {\n // Failed to parse frontmatter\n }\n \n // Infer status from content or git\n const inferredStatus = inferStatusFromContent(content) || \n inferStatusFromGit(filePath) || \n 'planned';\n \n // Infer created date from content or git\n const inferredCreated = inferCreatedFromContent(content) || \n inferCreatedFromGit(filePath) ||\n new Date().toISOString().split('T')[0];\n \n return {\n path: relativePath,\n fullPath,\n filePath,\n name,\n content,\n hasFrontmatter,\n hasValidFrontmatter,\n existingFrontmatter,\n inferredStatus: existingFrontmatter?.status || inferredStatus,\n inferredCreated: existingFrontmatter?.created || inferredCreated,\n };\n}\n\n/**\n * Infer status from markdown content using common patterns\n */\nexport function inferStatusFromContent(content: string): SpecStatus | null {\n // Pattern 1: **Status**: Complete (inline metadata)\n const inlineMatch = content.match(/\\*\\*Status\\*\\*:\\s*(?:✅\\s*|📅\\s*|⏳\\s*|📦\\s*)?(\\w+(?:-\\w+)?)/i);\n if (inlineMatch) {\n return normalizeStatus(inlineMatch[1]);\n }\n \n // Pattern 2: Status: Complete (simple format)\n const simpleMatch = content.match(/^Status:\\s*(\\w+(?:-\\w+)?)/mi);\n if (simpleMatch) {\n return normalizeStatus(simpleMatch[1]);\n }\n \n // Pattern 3: [x] Done / [ ] In Progress (task list format)\n if (/^\\s*-\\s*\\[x\\]\\s*(done|complete|finished)/mi.test(content)) {\n return 'complete';\n }\n \n // Pattern 4: ADR format - \"Status: Accepted\"\n const adrMatch = content.match(/^##?\\s*Status\\s*\\n+\\s*(\\w+)/mi);\n if (adrMatch) {\n const adrStatus = adrMatch[1].toLowerCase();\n if (['accepted', 'approved', 'done'].includes(adrStatus)) {\n return 'complete';\n }\n if (['proposed', 'pending', 'draft'].includes(adrStatus)) {\n return 'planned';\n }\n if (['superseded', 'deprecated', 'rejected'].includes(adrStatus)) {\n return 'archived';\n }\n }\n \n return null;\n}\n\n/**\n * Infer status from git history (check if ever marked complete)\n */\nexport function inferStatusFromGit(filePath: string): SpecStatus | null {\n if (!fileExistsInGit(filePath)) {\n return null;\n }\n \n try {\n const transitions = parseStatusTransitions(filePath);\n if (transitions.length > 0) {\n // Return the last known status from git history\n return transitions[transitions.length - 1].status;\n }\n } catch {\n // Git parsing failed\n }\n \n return null;\n}\n\n/**\n * Normalize various status strings to valid SpecStatus\n */\nfunction normalizeStatus(status: string): SpecStatus {\n const normalized = status.toLowerCase().trim().replace(/\\s+/g, '-');\n \n const statusMap: Record<string, SpecStatus> = {\n // Standard statuses\n 'planned': 'planned',\n 'in-progress': 'in-progress',\n 'inprogress': 'in-progress',\n 'in_progress': 'in-progress',\n 'wip': 'in-progress',\n 'working': 'in-progress',\n 'active': 'in-progress',\n 'complete': 'complete',\n 'completed': 'complete',\n 'done': 'complete',\n 'finished': 'complete',\n 'implemented': 'complete',\n 'archived': 'archived',\n 'deprecated': 'archived',\n 'superseded': 'archived',\n 'rejected': 'archived',\n // ADR statuses\n 'accepted': 'complete',\n 'approved': 'complete',\n 'proposed': 'planned',\n 'pending': 'planned',\n 'draft': 'planned',\n };\n \n return statusMap[normalized] || 'planned';\n}\n\n/**\n * Infer created date from markdown content\n */\nexport function inferCreatedFromContent(content: string): string | null {\n // Pattern 1: **Created**: 2025-01-15 (inline metadata)\n const inlineMatch = content.match(/\\*\\*Created\\*\\*:\\s*(\\d{4}-\\d{2}-\\d{2})/);\n if (inlineMatch) {\n return inlineMatch[1];\n }\n \n // Pattern 2: Created: 2025-01-15 (simple format)\n const simpleMatch = content.match(/^Created:\\s*(\\d{4}-\\d{2}-\\d{2})/mi);\n if (simpleMatch) {\n return simpleMatch[1];\n }\n \n // Pattern 3: Date: 2025-01-15\n const dateMatch = content.match(/^Date:\\s*(\\d{4}-\\d{2}-\\d{2})/mi);\n if (dateMatch) {\n return dateMatch[1];\n }\n \n // Pattern 4: ADR format - \"Date: January 15, 2025\"\n const adrMatch = content.match(/^##?\\s*Date\\s*\\n+\\s*(\\w+\\s+\\d{1,2},?\\s+\\d{4})/mi);\n if (adrMatch) {\n try {\n const date = new Date(adrMatch[1]);\n if (!isNaN(date.getTime())) {\n return date.toISOString().split('T')[0];\n }\n } catch {\n // Failed to parse date\n }\n }\n \n // Pattern 5: Look for dates in common formats anywhere\n const anyDateMatch = content.match(/(\\d{4}-\\d{2}-\\d{2})/);\n if (anyDateMatch) {\n return anyDateMatch[1];\n }\n \n return null;\n}\n\n/**\n * Infer created date from git history (first commit date)\n */\nexport function inferCreatedFromGit(filePath: string): string | null {\n const timestamp = getFirstCommitTimestamp(filePath);\n if (timestamp) {\n // Extract date part from ISO timestamp\n return timestamp.split('T')[0];\n }\n return null;\n}\n","import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { Command } from 'commander';\nimport { loadAllSpecs, getSpec, type SpecInfo } from '../spec-loader.js';\nimport { updateFrontmatter, type SpecFrontmatter, type SpecStatus } from '../frontmatter.js';\nimport { loadConfig } from '../config.js';\nimport { \n isGitRepository,\n extractGitTimestamps,\n fileExistsInGit,\n getFirstCommitTimestamp,\n type GitTimestampData,\n} from '../utils/git-timestamps.js';\nimport { resolveSpecPath } from '../utils/path-helpers.js';\nimport { \n loadSpecsForBootstrap, \n type BootstrapSpecInfo,\n inferStatusFromContent,\n inferCreatedFromContent,\n} from '../utils/bootstrap-helpers.js';\n\nexport interface BackfillResult {\n specPath: string;\n specName: string;\n created_at?: string;\n updated_at?: string;\n completed_at?: string;\n assignee?: string;\n transitionsCount?: number;\n source: 'git' | 'existing' | 'skipped' | 'bootstrapped';\n reason?: string;\n bootstrapped?: boolean; // Was frontmatter created from scratch?\n}\n\nexport interface BackfillOptions {\n dryRun?: boolean;\n force?: boolean;\n includeAssignee?: boolean;\n includeTransitions?: boolean;\n specs?: string[]; // specific specs to target\n json?: boolean;\n bootstrap?: boolean; // Create frontmatter for files without it\n}\n\n/**\n * Backfill command - backfill timestamps from git history\n */\nexport function backfillCommand(): Command {\n return new Command('backfill')\n .description('Backfill timestamps from git history')\n .argument('[specs...]', 'Specific specs to backfill (optional)')\n .option('--dry-run', 'Show what would be updated without making changes')\n .option('--force', 'Overwrite existing timestamp values')\n .option('--assignee', 'Include assignee from first commit author')\n .option('--transitions', 'Include full status transition history')\n .option('--all', 'Include all optional fields (assignee + transitions)')\n .option('--bootstrap', 'Create frontmatter for files without valid frontmatter')\n .option('--json', 'Output as JSON')\n .action(async (specs: string[] | undefined, options: {\n dryRun?: boolean;\n force?: boolean;\n assignee?: boolean;\n transitions?: boolean;\n all?: boolean;\n json?: boolean;\n bootstrap?: boolean;\n }) => {\n await backfillTimestamps({\n dryRun: options.dryRun,\n force: options.force,\n includeAssignee: options.assignee || options.all,\n includeTransitions: options.transitions || options.all,\n specs: specs && specs.length > 0 ? specs : undefined,\n json: options.json,\n bootstrap: options.bootstrap,\n });\n });\n}\n\n/**\n * Backfill timestamps from git history for all or specific specs\n */\nexport async function backfillTimestamps(options: BackfillOptions = {}): Promise<BackfillResult[]> {\n const results: BackfillResult[] = [];\n \n // Check if we're in a git repository\n if (!isGitRepository()) {\n console.error('\\x1b[31mError:\\x1b[0m Not in a git repository');\n console.error('Git history is required for backfilling timestamps');\n process.exit(1);\n }\n \n const config = await loadConfig();\n const cwd = process.cwd();\n const specsDir = path.join(cwd, config.specsDir);\n \n // Bootstrap mode: also load specs without valid frontmatter\n if (options.bootstrap) {\n console.log('\\x1b[36m🔧 Bootstrap mode - will create frontmatter for files without it\\x1b[0m\\n');\n \n const bootstrapSpecs = await loadSpecsForBootstrap(specsDir, {\n includeArchived: true,\n targetSpecs: options.specs,\n });\n \n if (bootstrapSpecs.length === 0) {\n console.log('No specs found to bootstrap');\n return results;\n }\n \n console.log(`Analyzing git history for ${bootstrapSpecs.length} spec${bootstrapSpecs.length === 1 ? '' : 's'}...\\n`);\n \n for (const spec of bootstrapSpecs) {\n const result = await bootstrapSpec(spec, options);\n results.push(result);\n }\n \n printSummary(results, options);\n return results;\n }\n \n // Normal mode: load specs with valid frontmatter only\n let specs: SpecInfo[];\n \n if (options.specs && options.specs.length > 0) {\n // Load specific specs\n specs = [];\n \n for (const specPath of options.specs) {\n const resolved = await resolveSpecPath(specPath, cwd, specsDir);\n if (!resolved) {\n console.warn(`\\x1b[33mWarning:\\x1b[0m Spec not found: ${specPath}`);\n continue;\n }\n const spec = await getSpec(resolved);\n if (spec) {\n specs.push(spec);\n }\n }\n } else {\n // Load all specs (including archived)\n specs = await loadAllSpecs({ includeArchived: true });\n }\n \n if (specs.length === 0) {\n console.log('No specs found to backfill');\n console.log('\\x1b[36mℹ\\x1b[0m Use --bootstrap to create frontmatter for files without it');\n return results;\n }\n \n // Show what we're doing\n if (options.dryRun) {\n console.log('\\x1b[36m🔍 Dry run mode - no changes will be made\\x1b[0m\\n');\n }\n \n console.log(`Analyzing git history for ${specs.length} spec${specs.length === 1 ? '' : 's'}...\\n`);\n \n // Process each spec\n for (const spec of specs) {\n const result = await backfillSpecTimestamps(spec, options);\n results.push(result);\n }\n \n // Print summary\n printSummary(results, options);\n \n return results;\n}\n\n/**\n * Backfill timestamps for a single spec\n */\nasync function backfillSpecTimestamps(\n spec: SpecInfo,\n options: BackfillOptions\n): Promise<BackfillResult> {\n const result: BackfillResult = {\n specPath: spec.path,\n specName: spec.name,\n source: 'skipped',\n };\n \n // Skip specs without valid frontmatter (missing required fields)\n if (!spec.frontmatter.status || !spec.frontmatter.created) {\n result.reason = 'Missing required frontmatter (status or created)';\n console.log(`\\x1b[33m⊘\\x1b[0m ${spec.name} - Missing required frontmatter`);\n return result;\n }\n \n // Check if file exists in git history\n if (!fileExistsInGit(spec.filePath)) {\n result.reason = 'Not in git history';\n console.log(`\\x1b[33m⊘\\x1b[0m ${spec.name} - Not in git history`);\n return result;\n }\n \n // Extract git timestamps\n const gitData = extractGitTimestamps(spec.filePath, {\n includeAssignee: options.includeAssignee,\n includeTransitions: options.includeTransitions,\n });\n \n // Determine what needs to be updated\n const updates: Partial<SpecFrontmatter> = {};\n let hasUpdates = false;\n \n // Check created_at\n if (gitData.created_at && (options.force || !spec.frontmatter.created_at)) {\n updates.created_at = gitData.created_at;\n result.created_at = gitData.created_at;\n result.source = 'git';\n hasUpdates = true;\n } else if (spec.frontmatter.created_at) {\n result.created_at = spec.frontmatter.created_at;\n result.source = 'existing';\n }\n \n // Check updated_at\n if (gitData.updated_at && (options.force || !spec.frontmatter.updated_at)) {\n updates.updated_at = gitData.updated_at;\n result.updated_at = gitData.updated_at;\n result.source = 'git';\n hasUpdates = true;\n } else if (spec.frontmatter.updated_at) {\n result.updated_at = spec.frontmatter.updated_at;\n result.source = 'existing';\n }\n \n // Check completed_at\n if (gitData.completed_at && (options.force || !spec.frontmatter.completed_at)) {\n updates.completed_at = gitData.completed_at;\n result.completed_at = gitData.completed_at;\n result.source = 'git';\n hasUpdates = true;\n } else if (spec.frontmatter.completed_at) {\n result.completed_at = spec.frontmatter.completed_at;\n result.source = 'existing';\n }\n \n // Check assignee (optional)\n if (options.includeAssignee && gitData.assignee && (options.force || !spec.frontmatter.assignee)) {\n updates.assignee = gitData.assignee;\n result.assignee = gitData.assignee;\n result.source = 'git';\n hasUpdates = true;\n } else if (spec.frontmatter.assignee) {\n result.assignee = spec.frontmatter.assignee;\n }\n \n // Check transitions (optional)\n if (options.includeTransitions && gitData.transitions && gitData.transitions.length > 0) {\n if (options.force || !spec.frontmatter.transitions || spec.frontmatter.transitions.length === 0) {\n updates.transitions = gitData.transitions;\n result.transitionsCount = gitData.transitions.length;\n result.source = 'git';\n hasUpdates = true;\n } else {\n // Merge with existing transitions (optional: could implement smart merge)\n result.transitionsCount = spec.frontmatter.transitions.length;\n }\n }\n \n // Sync updated date field with updated_at timestamp\n if (updates.updated_at && !updates.updated) {\n updates.updated = updates.updated_at.split('T')[0];\n }\n \n if (!hasUpdates) {\n result.reason = 'Already has complete data';\n console.log(`\\x1b[90m✓\\x1b[0m ${spec.name} - Already complete`);\n return result;\n }\n \n // Apply updates (unless dry run)\n if (!options.dryRun) {\n try {\n await updateFrontmatter(spec.filePath, updates);\n console.log(`\\x1b[32m✓\\x1b[0m ${spec.name} - Updated`);\n } catch (error) {\n result.source = 'skipped';\n result.reason = `Error: ${error instanceof Error ? error.message : String(error)}`;\n console.log(`\\x1b[31m✗\\x1b[0m ${spec.name} - Failed: ${result.reason}`);\n }\n } else {\n console.log(`\\x1b[36m→\\x1b[0m ${spec.name} - Would update`);\n // Show what would be updated\n if (updates.created_at) console.log(` created_at: ${updates.created_at} (git)`);\n if (updates.updated_at) console.log(` updated_at: ${updates.updated_at} (git)`);\n if (updates.completed_at) console.log(` completed_at: ${updates.completed_at} (git)`);\n if (updates.assignee) console.log(` assignee: ${updates.assignee} (git)`);\n if (updates.transitions) console.log(` transitions: ${updates.transitions.length} status changes (git)`);\n }\n \n return result;\n}\n\n/**\n * Bootstrap a spec file with missing or incomplete frontmatter\n */\nasync function bootstrapSpec(\n spec: BootstrapSpecInfo,\n options: BackfillOptions\n): Promise<BackfillResult> {\n const result: BackfillResult = {\n specPath: spec.path,\n specName: spec.name,\n source: 'skipped',\n bootstrapped: false,\n };\n \n // Skip specs that already have valid frontmatter (unless --force)\n if (spec.hasValidFrontmatter && !options.force) {\n // Still run normal backfill logic for timestamps\n result.reason = 'Already has valid frontmatter';\n console.log(`\\x1b[90m✓\\x1b[0m ${spec.name} - Already valid`);\n return result;\n }\n \n // Build frontmatter from existing + inferred data\n const frontmatter: Partial<SpecFrontmatter> = {\n ...(spec.existingFrontmatter || {}),\n status: spec.inferredStatus || 'planned',\n created: spec.inferredCreated || new Date().toISOString().split('T')[0],\n };\n \n // Get git timestamps\n if (fileExistsInGit(spec.filePath)) {\n const gitData = extractGitTimestamps(spec.filePath, {\n includeAssignee: options.includeAssignee,\n includeTransitions: options.includeTransitions,\n });\n \n if (gitData.created_at) {\n frontmatter.created_at = gitData.created_at;\n result.created_at = gitData.created_at;\n }\n if (gitData.updated_at) {\n frontmatter.updated_at = gitData.updated_at;\n result.updated_at = gitData.updated_at;\n }\n if (gitData.completed_at) {\n frontmatter.completed_at = gitData.completed_at;\n result.completed_at = gitData.completed_at;\n }\n if (options.includeAssignee && gitData.assignee) {\n frontmatter.assignee = gitData.assignee;\n result.assignee = gitData.assignee;\n }\n if (options.includeTransitions && gitData.transitions && gitData.transitions.length > 0) {\n frontmatter.transitions = gitData.transitions;\n result.transitionsCount = gitData.transitions.length;\n }\n }\n \n // Determine source labels for output\n const statusSource = spec.existingFrontmatter?.status ? 'existing' : \n (spec.content.match(/\\*\\*Status\\*\\*/i) ? 'content' : 'default');\n const createdSource = spec.existingFrontmatter?.created ? 'existing' :\n (spec.content.match(/\\*\\*Created\\*\\*/i) ? 'content' : \n (fileExistsInGit(spec.filePath) ? 'git' : 'default'));\n \n if (!options.dryRun) {\n try {\n await writeBootstrapFrontmatter(spec.filePath, spec.content, frontmatter);\n result.source = 'bootstrapped';\n result.bootstrapped = true;\n console.log(`\\x1b[32m✓\\x1b[0m ${spec.name} - Bootstrapped`);\n console.log(` status: ${frontmatter.status} (${statusSource})`);\n console.log(` created: ${frontmatter.created} (${createdSource})`);\n } catch (error) {\n result.source = 'skipped';\n result.reason = `Error: ${error instanceof Error ? error.message : String(error)}`;\n console.log(`\\x1b[31m✗\\x1b[0m ${spec.name} - Failed: ${result.reason}`);\n }\n } else {\n result.source = 'bootstrapped';\n result.bootstrapped = true;\n console.log(`\\x1b[36m→\\x1b[0m ${spec.name} - Would bootstrap`);\n console.log(` status: ${frontmatter.status} (${statusSource})`);\n console.log(` created: ${frontmatter.created} (${createdSource})`);\n if (frontmatter.created_at) console.log(` created_at: ${frontmatter.created_at} (git)`);\n if (frontmatter.updated_at) console.log(` updated_at: ${frontmatter.updated_at} (git)`);\n if (frontmatter.completed_at) console.log(` completed_at: ${frontmatter.completed_at} (git)`);\n if (frontmatter.assignee) console.log(` assignee: ${frontmatter.assignee} (git)`);\n if (frontmatter.transitions) console.log(` transitions: ${frontmatter.transitions.length} (git)`);\n }\n \n return result;\n}\n\n/**\n * Write or update frontmatter in a spec file\n */\nasync function writeBootstrapFrontmatter(\n filePath: string,\n originalContent: string,\n frontmatter: Partial<SpecFrontmatter>\n): Promise<void> {\n const matter = await import('gray-matter');\n \n // Parse existing content (may or may not have frontmatter)\n const parsed = matter.default(originalContent);\n \n // Merge new frontmatter with any existing\n const newData = { ...parsed.data, ...frontmatter };\n \n // Write back\n const newContent = matter.default.stringify(parsed.content, newData);\n await fs.writeFile(filePath, newContent, 'utf-8');\n}\n\n/**\n * Print summary of backfill results\n */\nfunction printSummary(results: BackfillResult[], options: BackfillOptions): void {\n console.log('\\n' + '─'.repeat(60));\n console.log('\\x1b[1mSummary:\\x1b[0m\\n');\n \n const total = results.length;\n const updated = results.filter(r => r.source === 'git').length;\n const bootstrapped = results.filter(r => r.source === 'bootstrapped').length;\n const existing = results.filter(r => r.source === 'existing').length;\n const skipped = results.filter(r => r.source === 'skipped').length;\n \n const timestampUpdates = results.filter(r => \n (r.source === 'git' || r.source === 'bootstrapped') && (r.created_at || r.updated_at || r.completed_at)\n ).length;\n \n const assigneeUpdates = results.filter(r => \n (r.source === 'git' || r.source === 'bootstrapped') && r.assignee\n ).length;\n \n const transitionUpdates = results.filter(r => \n (r.source === 'git' || r.source === 'bootstrapped') && r.transitionsCount\n ).length;\n \n console.log(` ${total} specs analyzed`);\n \n if (options.dryRun) {\n if (options.bootstrap) {\n console.log(` ${bootstrapped} would be bootstrapped`);\n }\n console.log(` ${updated} would be updated`);\n if (timestampUpdates > 0) {\n console.log(` └─ ${timestampUpdates} with timestamps`);\n }\n if (options.includeAssignee && assigneeUpdates > 0) {\n console.log(` └─ ${assigneeUpdates} with assignee`);\n }\n if (options.includeTransitions && transitionUpdates > 0) {\n console.log(` └─ ${transitionUpdates} with transitions`);\n }\n } else {\n if (options.bootstrap) {\n console.log(` ${bootstrapped} bootstrapped`);\n }\n console.log(` ${updated} updated`);\n }\n \n console.log(` ${existing} already complete`);\n console.log(` ${skipped} skipped`);\n \n // Show reasons for skipped specs\n const skipReasons = results\n .filter(r => r.source === 'skipped' && r.reason)\n .map(r => r.reason);\n \n if (skipReasons.length > 0) {\n console.log('\\n\\x1b[33mSkipped reasons:\\x1b[0m');\n const uniqueReasons = [...new Set(skipReasons)];\n for (const reason of uniqueReasons) {\n const count = skipReasons.filter(r => r === reason).length;\n console.log(` - ${reason} (${count})`);\n }\n }\n \n // Guidance\n if (options.dryRun) {\n console.log('\\n\\x1b[36mℹ\\x1b[0m Run without --dry-run to apply changes');\n \n if (!options.includeAssignee || !options.includeTransitions) {\n console.log('\\x1b[36mℹ\\x1b[0m Use --all to include optional fields (assignee, transitions)');\n }\n \n if (!options.bootstrap && skipped > 0) {\n console.log('\\x1b[36mℹ\\x1b[0m Use --bootstrap to create frontmatter for files without it');\n }\n } else if (updated > 0 || bootstrapped > 0) {\n console.log('\\n\\x1b[32m✓\\x1b[0m Backfill complete!');\n console.log(' Run \\x1b[36mlean-spec stats\\x1b[0m to see velocity metrics');\n }\n}\n"]}
@@ -0,0 +1,298 @@
1
+ import { getSpecFile, parseFrontmatter, matchesFilter } from './chunk-VN5BUHTV.js';
2
+ import * as fs2 from 'fs/promises';
3
+ import * as path2 from 'path';
4
+
5
+ var DEFAULT_CONFIG = {
6
+ template: "spec-template.md",
7
+ templates: {
8
+ default: "spec-template.md"
9
+ },
10
+ specsDir: "specs",
11
+ structure: {
12
+ pattern: "flat",
13
+ // Default to flat for new projects
14
+ prefix: "",
15
+ // No prefix by default - global sequence numbers only
16
+ dateFormat: "YYYYMMDD",
17
+ sequenceDigits: 3,
18
+ defaultFile: "README.md"
19
+ },
20
+ features: {
21
+ aiAgents: true,
22
+ examples: true
23
+ }
24
+ };
25
+ async function loadConfig(cwd = process.cwd()) {
26
+ const configPath = path2.join(cwd, ".lean-spec", "config.json");
27
+ try {
28
+ const content = await fs2.readFile(configPath, "utf-8");
29
+ const userConfig = JSON.parse(content);
30
+ const merged = { ...DEFAULT_CONFIG, ...userConfig };
31
+ normalizeLegacyPattern(merged);
32
+ return merged;
33
+ } catch {
34
+ return DEFAULT_CONFIG;
35
+ }
36
+ }
37
+ async function saveConfig(config, cwd = process.cwd()) {
38
+ const configDir = path2.join(cwd, ".lean-spec");
39
+ const configPath = path2.join(configDir, "config.json");
40
+ await fs2.mkdir(configDir, { recursive: true });
41
+ await fs2.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
42
+ }
43
+ function getToday(format = "YYYYMMDD") {
44
+ const now = /* @__PURE__ */ new Date();
45
+ const year = now.getFullYear();
46
+ const month = String(now.getMonth() + 1).padStart(2, "0");
47
+ const day = String(now.getDate()).padStart(2, "0");
48
+ switch (format) {
49
+ case "YYYYMMDD":
50
+ return `${year}${month}${day}`;
51
+ case "YYYY-MM-DD":
52
+ return `${year}-${month}-${day}`;
53
+ case "YYYY-MM":
54
+ return `${year}-${month}`;
55
+ case "YYYY/MM":
56
+ return `${year}/${month}`;
57
+ case "YYYY":
58
+ return String(year);
59
+ case "MM":
60
+ return month;
61
+ case "DD":
62
+ return day;
63
+ default:
64
+ return `${year}${month}${day}`;
65
+ }
66
+ }
67
+ function normalizeLegacyPattern(config) {
68
+ const pattern = config.structure.pattern;
69
+ if (pattern && pattern.includes("{date}") && pattern.includes("{seq}") && pattern.includes("{name}")) {
70
+ config.structure.pattern = "custom";
71
+ config.structure.groupExtractor = `{${config.structure.dateFormat}}`;
72
+ }
73
+ }
74
+ function resolvePrefix(prefix, dateFormat = "YYYYMMDD") {
75
+ const dateReplacements = {
76
+ "{YYYYMMDD}": () => getToday("YYYYMMDD"),
77
+ "{YYYY-MM-DD}": () => getToday("YYYY-MM-DD"),
78
+ "{YYYY-MM}": () => getToday("YYYY-MM"),
79
+ "{YYYY}": () => getToday("YYYY"),
80
+ "{MM}": () => getToday("MM"),
81
+ "{DD}": () => getToday("DD")
82
+ };
83
+ let result = prefix;
84
+ for (const [pattern, fn] of Object.entries(dateReplacements)) {
85
+ result = result.replace(pattern, fn());
86
+ }
87
+ return result;
88
+ }
89
+ function extractGroup(extractor, dateFormat = "YYYYMMDD", fields, fallback) {
90
+ const dateReplacements = {
91
+ "{YYYYMMDD}": () => getToday("YYYYMMDD"),
92
+ "{YYYY-MM-DD}": () => getToday("YYYY-MM-DD"),
93
+ "{YYYY-MM}": () => getToday("YYYY-MM"),
94
+ "{YYYY}": () => getToday("YYYY"),
95
+ "{MM}": () => getToday("MM"),
96
+ "{DD}": () => getToday("DD")
97
+ };
98
+ let result = extractor;
99
+ for (const [pattern, fn] of Object.entries(dateReplacements)) {
100
+ result = result.replace(pattern, fn());
101
+ }
102
+ const fieldMatches = result.match(/\{([^}]+)\}/g);
103
+ if (fieldMatches) {
104
+ for (const match of fieldMatches) {
105
+ const fieldName = match.slice(1, -1);
106
+ const fieldValue = fields?.[fieldName];
107
+ if (fieldValue === void 0) {
108
+ if (!fallback) {
109
+ throw new Error(`Custom field '${fieldName}' required but not provided. Set structure.groupFallback in config or provide --field ${fieldName}=<value>`);
110
+ }
111
+ return fallback;
112
+ }
113
+ result = result.replace(match, String(fieldValue));
114
+ }
115
+ }
116
+ return result;
117
+ }
118
+ async function loadSubFiles(specDir, options = {}) {
119
+ const subFiles = [];
120
+ try {
121
+ const entries = await fs2.readdir(specDir, { withFileTypes: true });
122
+ for (const entry of entries) {
123
+ if (entry.name === "README.md") continue;
124
+ if (entry.isDirectory()) continue;
125
+ const filePath = path2.join(specDir, entry.name);
126
+ const stat2 = await fs2.stat(filePath);
127
+ const ext = path2.extname(entry.name).toLowerCase();
128
+ const isDocument = ext === ".md";
129
+ const subFile = {
130
+ name: entry.name,
131
+ path: filePath,
132
+ size: stat2.size,
133
+ type: isDocument ? "document" : "asset"
134
+ };
135
+ if (isDocument && options.includeContent) {
136
+ subFile.content = await fs2.readFile(filePath, "utf-8");
137
+ }
138
+ subFiles.push(subFile);
139
+ }
140
+ } catch (error) {
141
+ return [];
142
+ }
143
+ return subFiles.sort((a, b) => {
144
+ if (a.type !== b.type) {
145
+ return a.type === "document" ? -1 : 1;
146
+ }
147
+ return a.name.localeCompare(b.name);
148
+ });
149
+ }
150
+ async function loadAllSpecs(options = {}) {
151
+ const config = await loadConfig();
152
+ const cwd = process.cwd();
153
+ const specsDir = path2.join(cwd, config.specsDir);
154
+ const specs = [];
155
+ try {
156
+ await fs2.access(specsDir);
157
+ } catch {
158
+ return [];
159
+ }
160
+ const specPattern = /^(\d{2,})-/;
161
+ async function loadSpecsFromDir(dir, relativePath = "") {
162
+ try {
163
+ const entries = await fs2.readdir(dir, { withFileTypes: true });
164
+ for (const entry of entries) {
165
+ if (!entry.isDirectory()) continue;
166
+ if (entry.name === "archived" && relativePath === "") continue;
167
+ const entryPath = path2.join(dir, entry.name);
168
+ const entryRelativePath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
169
+ if (specPattern.test(entry.name)) {
170
+ const specFile = await getSpecFile(entryPath, config.structure.defaultFile);
171
+ if (specFile) {
172
+ const frontmatter = await parseFrontmatter(specFile, config);
173
+ if (frontmatter) {
174
+ if (options.filter && !matchesFilter(frontmatter, options.filter)) {
175
+ continue;
176
+ }
177
+ const dateMatch = entryRelativePath.match(/(\d{8})/);
178
+ let date;
179
+ if (dateMatch) {
180
+ date = dateMatch[1];
181
+ } else if (typeof frontmatter.created === "string") {
182
+ date = frontmatter.created;
183
+ } else if (frontmatter.created) {
184
+ date = String(frontmatter.created);
185
+ } else {
186
+ date = "";
187
+ }
188
+ const specInfo = {
189
+ path: entryRelativePath,
190
+ fullPath: entryPath,
191
+ filePath: specFile,
192
+ name: entry.name,
193
+ date,
194
+ frontmatter
195
+ };
196
+ if (options.includeContent) {
197
+ specInfo.content = await fs2.readFile(specFile, "utf-8");
198
+ }
199
+ if (options.includeSubFiles) {
200
+ specInfo.subFiles = await loadSubFiles(entryPath, {
201
+ includeContent: options.includeContent
202
+ });
203
+ }
204
+ specs.push(specInfo);
205
+ }
206
+ }
207
+ } else {
208
+ await loadSpecsFromDir(entryPath, entryRelativePath);
209
+ }
210
+ }
211
+ } catch {
212
+ }
213
+ }
214
+ await loadSpecsFromDir(specsDir);
215
+ if (options.includeArchived) {
216
+ const archivedPath = path2.join(specsDir, "archived");
217
+ await loadSpecsFromDir(archivedPath, "archived");
218
+ }
219
+ const sortBy = options.sortBy || "id";
220
+ const sortOrder = options.sortOrder || "desc";
221
+ specs.sort((a, b) => {
222
+ let comparison = 0;
223
+ switch (sortBy) {
224
+ case "id":
225
+ case "number": {
226
+ const aNum = parseInt(a.name.match(/^(\d+)/)?.[1] || "0", 10);
227
+ const bNum = parseInt(b.name.match(/^(\d+)/)?.[1] || "0", 10);
228
+ comparison = aNum - bNum;
229
+ break;
230
+ }
231
+ case "created": {
232
+ const aDate2 = String(a.frontmatter.created || "");
233
+ const bDate2 = String(b.frontmatter.created || "");
234
+ comparison = aDate2.localeCompare(bDate2);
235
+ break;
236
+ }
237
+ case "name": {
238
+ comparison = a.name.localeCompare(b.name);
239
+ break;
240
+ }
241
+ case "status": {
242
+ comparison = a.frontmatter.status.localeCompare(b.frontmatter.status);
243
+ break;
244
+ }
245
+ case "priority": {
246
+ const priorityOrder = { critical: 4, high: 3, medium: 2, low: 1 };
247
+ const aPriority = a.frontmatter.priority ? priorityOrder[a.frontmatter.priority] : 0;
248
+ const bPriority = b.frontmatter.priority ? priorityOrder[b.frontmatter.priority] : 0;
249
+ comparison = aPriority - bPriority;
250
+ break;
251
+ }
252
+ default:
253
+ const aDate = String(a.frontmatter.created || "");
254
+ const bDate = String(b.frontmatter.created || "");
255
+ comparison = aDate.localeCompare(bDate);
256
+ }
257
+ return sortOrder === "desc" ? -comparison : comparison;
258
+ });
259
+ return specs;
260
+ }
261
+ async function getSpec(specPath) {
262
+ const config = await loadConfig();
263
+ const cwd = process.cwd();
264
+ const specsDir = path2.join(cwd, config.specsDir);
265
+ let fullPath;
266
+ if (path2.isAbsolute(specPath)) {
267
+ fullPath = specPath;
268
+ } else {
269
+ fullPath = path2.join(specsDir, specPath);
270
+ }
271
+ try {
272
+ await fs2.access(fullPath);
273
+ } catch {
274
+ return null;
275
+ }
276
+ const specFile = await getSpecFile(fullPath, config.structure.defaultFile);
277
+ if (!specFile) return null;
278
+ const frontmatter = await parseFrontmatter(specFile, config);
279
+ if (!frontmatter) return null;
280
+ const content = await fs2.readFile(specFile, "utf-8");
281
+ const relativePath = path2.relative(specsDir, fullPath);
282
+ const parts = relativePath.split(path2.sep);
283
+ const date = parts[0] === "archived" ? parts[1] : parts[0];
284
+ const name = parts[parts.length - 1];
285
+ return {
286
+ path: relativePath,
287
+ fullPath,
288
+ filePath: specFile,
289
+ name,
290
+ date,
291
+ frontmatter,
292
+ content
293
+ };
294
+ }
295
+
296
+ export { extractGroup, getSpec, loadAllSpecs, loadConfig, loadSubFiles, resolvePrefix, saveConfig };
297
+ //# sourceMappingURL=chunk-RF5PKL6L.js.map
298
+ //# sourceMappingURL=chunk-RF5PKL6L.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config.ts","../src/spec-loader.ts"],"names":["path","fs","stat","aDate","bDate"],"mappings":"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","file":"chunk-RF5PKL6L.js","sourcesContent":["import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\n\nexport interface LeanSpecConfig {\n template: string;\n templates?: Record<string, string>; // Maps template name to filename\n specsDir: string;\n autoCheck?: boolean; // Enable/disable auto-check for sequence conflicts (default: true)\n structure: {\n pattern: 'flat' | 'custom' | string; // 'flat' or 'custom', or legacy pattern string\n dateFormat: string;\n sequenceDigits: number;\n defaultFile: string;\n prefix?: string; // For flat pattern: \"{YYYYMMDD}-\" or \"spec-\" (optional, default: empty for global numbering)\n groupExtractor?: string; // For custom pattern: \"{YYYYMMDD}\" or \"milestone-{milestone}\"\n groupFallback?: string; // Fallback folder if field missing (only for non-date extractors)\n };\n features?: {\n aiAgents?: boolean;\n examples?: boolean;\n collaboration?: boolean;\n compliance?: boolean;\n approvals?: boolean;\n apiDocs?: boolean;\n };\n frontmatter?: {\n required?: string[];\n optional?: string[];\n custom?: Record<string, 'string' | 'number' | 'boolean' | 'array'>;\n };\n variables?: Record<string, string>;\n}\n\nconst DEFAULT_CONFIG: LeanSpecConfig = {\n template: 'spec-template.md',\n templates: {\n default: 'spec-template.md',\n },\n specsDir: 'specs',\n structure: {\n pattern: 'flat', // Default to flat for new projects\n prefix: '', // No prefix by default - global sequence numbers only\n dateFormat: 'YYYYMMDD',\n sequenceDigits: 3,\n defaultFile: 'README.md',\n },\n features: {\n aiAgents: true,\n examples: true,\n },\n};\n\nexport async function loadConfig(cwd: string = process.cwd()): Promise<LeanSpecConfig> {\n const configPath = path.join(cwd, '.lean-spec', 'config.json');\n\n try {\n const content = await fs.readFile(configPath, 'utf-8');\n const userConfig = JSON.parse(content);\n const merged = { ...DEFAULT_CONFIG, ...userConfig };\n \n // Normalize legacy pattern format\n normalizeLegacyPattern(merged);\n \n return merged;\n } catch {\n // No config file, use defaults\n return DEFAULT_CONFIG;\n }\n}\n\nexport async function saveConfig(\n config: LeanSpecConfig,\n cwd: string = process.cwd(),\n): Promise<void> {\n const configDir = path.join(cwd, '.lean-spec');\n const configPath = path.join(configDir, 'config.json');\n\n await fs.mkdir(configDir, { recursive: true });\n await fs.writeFile(configPath, JSON.stringify(config, null, 2), 'utf-8');\n}\n\nexport function getToday(format: string = 'YYYYMMDD'): string {\n const now = new Date();\n const year = now.getFullYear();\n const month = String(now.getMonth() + 1).padStart(2, '0');\n const day = String(now.getDate()).padStart(2, '0');\n\n switch (format) {\n case 'YYYYMMDD':\n return `${year}${month}${day}`;\n case 'YYYY-MM-DD':\n return `${year}-${month}-${day}`;\n case 'YYYY-MM':\n return `${year}-${month}`;\n case 'YYYY/MM':\n return `${year}/${month}`;\n case 'YYYY':\n return String(year);\n case 'MM':\n return month;\n case 'DD':\n return day;\n default:\n return `${year}${month}${day}`;\n }\n}\n\n/**\n * Detect if a config uses legacy pattern format and convert it\n */\nexport function normalizeLegacyPattern(config: LeanSpecConfig): void {\n const pattern = config.structure.pattern;\n \n // If pattern contains {date}/{seq}-{name}/, convert to custom with date grouping\n if (pattern && pattern.includes('{date}') && pattern.includes('{seq}') && pattern.includes('{name}')) {\n config.structure.pattern = 'custom';\n config.structure.groupExtractor = `{${config.structure.dateFormat}}`;\n }\n}\n\n/**\n * Resolve prefix string for flat pattern (e.g., \"{YYYYMMDD}-\" becomes \"20251103-\")\n */\nexport function resolvePrefix(\n prefix: string,\n dateFormat: string = 'YYYYMMDD'\n): string {\n const dateReplacements: Record<string, () => string> = {\n '{YYYYMMDD}': () => getToday('YYYYMMDD'),\n '{YYYY-MM-DD}': () => getToday('YYYY-MM-DD'),\n '{YYYY-MM}': () => getToday('YYYY-MM'),\n '{YYYY}': () => getToday('YYYY'),\n '{MM}': () => getToday('MM'),\n '{DD}': () => getToday('DD'),\n };\n\n let result = prefix;\n for (const [pattern, fn] of Object.entries(dateReplacements)) {\n result = result.replace(pattern, fn());\n }\n\n return result;\n}\n\n/**\n * Extract group folder from extractor pattern\n */\nexport function extractGroup(\n extractor: string,\n dateFormat: string = 'YYYYMMDD',\n fields?: Record<string, unknown>,\n fallback?: string\n): string {\n const dateReplacements: Record<string, () => string> = {\n '{YYYYMMDD}': () => getToday('YYYYMMDD'),\n '{YYYY-MM-DD}': () => getToday('YYYY-MM-DD'),\n '{YYYY-MM}': () => getToday('YYYY-MM'),\n '{YYYY}': () => getToday('YYYY'),\n '{MM}': () => getToday('MM'),\n '{DD}': () => getToday('DD'),\n };\n\n let result = extractor;\n\n // Replace date functions first\n for (const [pattern, fn] of Object.entries(dateReplacements)) {\n result = result.replace(pattern, fn());\n }\n\n // Replace frontmatter fields: {fieldname}\n const fieldMatches = result.match(/\\{([^}]+)\\}/g);\n if (fieldMatches) {\n for (const match of fieldMatches) {\n const fieldName = match.slice(1, -1); // Remove { }\n const fieldValue = fields?.[fieldName];\n\n if (fieldValue === undefined) {\n if (!fallback) {\n throw new Error(`Custom field '${fieldName}' required but not provided. Set structure.groupFallback in config or provide --field ${fieldName}=<value>`);\n }\n return fallback;\n }\n\n result = result.replace(match, String(fieldValue));\n }\n }\n\n return result;\n}\n","import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { loadConfig } from './config.js';\nimport { parseFrontmatter, getSpecFile, matchesFilter, type SpecFrontmatter, type SpecFilterOptions } from './frontmatter.js';\n\nexport interface SpecInfo {\n path: string; // Relative path like \"20251101/003-pm-visualization-tools\"\n fullPath: string; // Absolute path to spec directory\n filePath: string; // Absolute path to spec file (README.md)\n name: string; // Just the spec name like \"003-pm-visualization-tools\"\n date?: string; // Date folder like \"20251101\" (optional for flat/nested patterns)\n frontmatter: SpecFrontmatter;\n content?: string; // Full file content (optional, for search)\n subFiles?: SubFileInfo[]; // Sub-documents and assets\n}\n\nexport interface SubFileInfo {\n name: string; // e.g., \"TESTING.md\" or \"diagram.png\"\n path: string; // Absolute path to the file\n size: number; // File size in bytes\n type: 'document' | 'asset'; // Classification based on file type\n content?: string; // Optional content for documents\n}\n\n// Load sub-files for a spec (all files except README.md)\nexport async function loadSubFiles(\n specDir: string,\n options: { includeContent?: boolean } = {}\n): Promise<SubFileInfo[]> {\n const subFiles: SubFileInfo[] = [];\n\n try {\n const entries = await fs.readdir(specDir, { withFileTypes: true });\n\n for (const entry of entries) {\n // Skip README.md (main spec file)\n if (entry.name === 'README.md') continue;\n\n // Skip directories for now (could be assets folder)\n if (entry.isDirectory()) continue;\n\n const filePath = path.join(specDir, entry.name);\n const stat = await fs.stat(filePath);\n\n // Determine type based on extension\n const ext = path.extname(entry.name).toLowerCase();\n const isDocument = ext === '.md';\n\n const subFile: SubFileInfo = {\n name: entry.name,\n path: filePath,\n size: stat.size,\n type: isDocument ? 'document' : 'asset',\n };\n\n // Load content for documents if requested\n if (isDocument && options.includeContent) {\n subFile.content = await fs.readFile(filePath, 'utf-8');\n }\n\n subFiles.push(subFile);\n }\n } catch (error) {\n // Directory doesn't exist or can't be read - return empty array\n // This is expected for specs without sub-files\n return [];\n }\n\n // Sort: documents first, then alphabetically\n return subFiles.sort((a, b) => {\n if (a.type !== b.type) {\n return a.type === 'document' ? -1 : 1;\n }\n return a.name.localeCompare(b.name);\n });\n}\n\n// Load all specs from the specs directory\nexport async function loadAllSpecs(options: {\n includeArchived?: boolean;\n includeContent?: boolean;\n includeSubFiles?: boolean;\n filter?: SpecFilterOptions;\n sortBy?: string;\n sortOrder?: 'asc' | 'desc';\n} = {}): Promise<SpecInfo[]> {\n const config = await loadConfig();\n const cwd = process.cwd();\n const specsDir = path.join(cwd, config.specsDir);\n\n const specs: SpecInfo[] = [];\n\n // Check if specs directory exists\n try {\n await fs.access(specsDir);\n } catch {\n return [];\n }\n\n // Pattern to match spec directories (2 or more digits followed by dash)\n const specPattern = /^(\\d{2,})-/;\n\n // Recursively load all specs from the directory structure\n async function loadSpecsFromDir(dir: string, relativePath: string = ''): Promise<void> {\n try {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n \n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n \n // Skip archived directory in main scan (handle separately)\n if (entry.name === 'archived' && relativePath === '') continue;\n \n const entryPath = path.join(dir, entry.name);\n const entryRelativePath = relativePath ? `${relativePath}/${entry.name}` : entry.name;\n \n // Check if this is a spec directory (NNN-name format)\n if (specPattern.test(entry.name)) {\n const specFile = await getSpecFile(entryPath, config.structure.defaultFile);\n \n if (specFile) {\n const frontmatter = await parseFrontmatter(specFile, config);\n \n if (frontmatter) {\n // Apply filter if provided\n if (options.filter && !matchesFilter(frontmatter, options.filter)) {\n continue;\n }\n \n // Extract date from path or frontmatter\n const dateMatch = entryRelativePath.match(/(\\d{8})/);\n let date: string;\n \n if (dateMatch) {\n date = dateMatch[1];\n } else if (typeof frontmatter.created === 'string') {\n date = frontmatter.created;\n } else if (frontmatter.created) {\n date = String(frontmatter.created);\n } else {\n date = '';\n }\n \n const specInfo: SpecInfo = {\n path: entryRelativePath,\n fullPath: entryPath,\n filePath: specFile,\n name: entry.name,\n date: date,\n frontmatter,\n };\n \n // Load content if requested\n if (options.includeContent) {\n specInfo.content = await fs.readFile(specFile, 'utf-8');\n }\n \n // Load sub-files if requested\n if (options.includeSubFiles) {\n specInfo.subFiles = await loadSubFiles(entryPath, {\n includeContent: options.includeContent,\n });\n }\n \n specs.push(specInfo);\n }\n }\n } else {\n // Not a spec directory, scan recursively for nested structure\n await loadSpecsFromDir(entryPath, entryRelativePath);\n }\n }\n } catch {\n // Directory doesn't exist or can't be read\n }\n }\n \n // Load active specs\n await loadSpecsFromDir(specsDir);\n\n // Load archived specs if requested\n if (options.includeArchived) {\n const archivedPath = path.join(specsDir, 'archived');\n await loadSpecsFromDir(archivedPath, 'archived');\n }\n\n // Sort specs based on options (default: id desc)\n const sortBy = options.sortBy || 'id';\n const sortOrder = options.sortOrder || 'desc';\n \n specs.sort((a, b) => {\n let comparison = 0;\n \n switch (sortBy) {\n case 'id':\n case 'number': { // Keep 'number' for backwards compatibility\n // Extract leading digits from spec name\n const aNum = parseInt(a.name.match(/^(\\d+)/)?.[1] || '0', 10);\n const bNum = parseInt(b.name.match(/^(\\d+)/)?.[1] || '0', 10);\n comparison = aNum - bNum;\n break;\n }\n case 'created': {\n // Sort by created date from frontmatter\n const aDate = String(a.frontmatter.created || '');\n const bDate = String(b.frontmatter.created || '');\n comparison = aDate.localeCompare(bDate);\n break;\n }\n case 'name': {\n comparison = a.name.localeCompare(b.name);\n break;\n }\n case 'status': {\n comparison = a.frontmatter.status.localeCompare(b.frontmatter.status);\n break;\n }\n case 'priority': {\n // Priority order: critical > high > medium > low > (none)\n const priorityOrder = { critical: 4, high: 3, medium: 2, low: 1 };\n const aPriority = a.frontmatter.priority ? priorityOrder[a.frontmatter.priority] : 0;\n const bPriority = b.frontmatter.priority ? priorityOrder[b.frontmatter.priority] : 0;\n comparison = aPriority - bPriority;\n break;\n }\n default:\n // Default to created date\n const aDate = String(a.frontmatter.created || '');\n const bDate = String(b.frontmatter.created || '');\n comparison = aDate.localeCompare(bDate);\n }\n \n // Apply sort order\n return sortOrder === 'desc' ? -comparison : comparison;\n });\n\n return specs;\n}\n\n// Get a specific spec by path\nexport async function getSpec(specPath: string): Promise<SpecInfo | null> {\n const config = await loadConfig();\n const cwd = process.cwd();\n const specsDir = path.join(cwd, config.specsDir);\n\n // Resolve the full path\n let fullPath: string;\n if (path.isAbsolute(specPath)) {\n fullPath = specPath;\n } else {\n fullPath = path.join(specsDir, specPath);\n }\n\n // Check if directory exists\n try {\n await fs.access(fullPath);\n } catch {\n return null;\n }\n\n const specFile = await getSpecFile(fullPath, config.structure.defaultFile);\n if (!specFile) return null;\n\n const frontmatter = await parseFrontmatter(specFile, config);\n if (!frontmatter) return null;\n\n const content = await fs.readFile(specFile, 'utf-8');\n\n // Parse path components\n const relativePath = path.relative(specsDir, fullPath);\n const parts = relativePath.split(path.sep);\n const date = parts[0] === 'archived' ? parts[1] : parts[0];\n const name = parts[parts.length - 1];\n\n return {\n path: relativePath,\n fullPath,\n filePath: specFile,\n name,\n date,\n frontmatter,\n content,\n };\n}\n"]}
@@ -137,7 +137,6 @@ async function parseFrontmatter(filePath, config) {
137
137
  "created",
138
138
  "tags",
139
139
  "priority",
140
- "related",
141
140
  "depends_on",
142
141
  "updated",
143
142
  "completed",
@@ -202,6 +201,9 @@ async function updateFrontmatter(filePath, updates) {
202
201
  await fs.writeFile(filePath, newContent, "utf-8");
203
202
  }
204
203
  function updateVisualMetadata(content, frontmatter) {
204
+ if (!frontmatter.status || !frontmatter.created) {
205
+ return content;
206
+ }
205
207
  const statusEmoji = getStatusEmojiPlain(frontmatter.status);
206
208
  const statusLabel = frontmatter.status.charAt(0).toUpperCase() + frontmatter.status.slice(1).replace("-", " ");
207
209
  const created = dayjs(frontmatter.created).format("YYYY-MM-DD");
@@ -294,5 +296,5 @@ function matchesFilter(frontmatter, filter) {
294
296
  }
295
297
 
296
298
  export { enrichWithTimestamps, getSpecFile, matchesFilter, normalizeDateFields, normalizeTagsField, parseFrontmatter, updateFrontmatter, validateCustomField, validateCustomFields };
297
- //# sourceMappingURL=chunk-LVD7ZAVZ.js.map
298
- //# sourceMappingURL=chunk-LVD7ZAVZ.js.map
299
+ //# sourceMappingURL=chunk-VN5BUHTV.js.map
300
+ //# sourceMappingURL=chunk-VN5BUHTV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/frontmatter.ts"],"names":[],"mappings":"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","file":"chunk-VN5BUHTV.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 // Skip visual metadata update if required fields are missing\n if (!frontmatter.status || !frontmatter.created) {\n return content;\n }\n \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"]}