@outcomeeng/spx 0.1.0 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -25
- package/dist/cli.js +4 -1
- package/dist/cli.js.map +1 -1
- package/package.json +44 -26
- package/dist/.eslintcache +0 -1
- package/dist/.validation-timings.json +0 -50
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/commands/claude/init.ts","../src/commands/claude/settings/consolidate.ts","../src/lib/claude/permissions/discovery.ts","../src/lib/claude/permissions/parser.ts","../src/scanner/walk.ts","../src/lib/claude/permissions/subsumption.ts","../src/lib/claude/permissions/merger.ts","../src/lib/claude/settings/backup.ts","../src/lib/claude/settings/reporter.ts","../src/lib/claude/settings/writer.ts","../src/domains/claude/index.ts","../src/commands/session/archive.ts","../src/session/errors.ts","../src/session/show.ts","../src/config/defaults.ts","../src/session/list.ts","../src/session/timestamp.ts","../src/session/types.ts","../src/commands/session/delete.ts","../src/session/delete.ts","../src/commands/session/handoff.ts","../src/git/root.ts","../src/session/create.ts","../src/commands/session/list.ts","../src/commands/session/pickup.ts","../src/session/pickup.ts","../src/commands/session/prune.ts","../src/commands/session/release.ts","../src/session/release.ts","../src/commands/session/show.ts","../src/domains/session/help.ts","../src/domains/session/index.ts","../src/scanner/scanner.ts","../src/status/state.ts","../src/tree/build.ts","../src/commands/spec/next.ts","../src/reporter/json.ts","../src/reporter/markdown.ts","../src/reporter/table.ts","../src/reporter/text.ts","../src/commands/spec/status.ts","../src/domains/spec/index.ts","../node_modules/.pnpm/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/scanner.js","../node_modules/.pnpm/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/string-intern.js","../node_modules/.pnpm/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/parser.js","../node_modules/.pnpm/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/main.js","../src/validation/config/scope.ts","../src/validation/discovery/tool-finder.ts","../src/validation/discovery/constants.ts","../src/validation/steps/circular.ts","../src/validation/steps/constants.ts","../src/commands/validation/circular.ts","../src/commands/validation/format.ts","../src/validation/steps/eslint.ts","../src/validation/types.ts","../src/validation/steps/knip.ts","../src/commands/validation/knip.ts","../src/commands/validation/lint.ts","../src/validation/steps/typescript.ts","../src/commands/validation/typescript.ts","../src/commands/validation/all.ts","../src/domains/validation/index.ts"],"sourcesContent":["/**\n * CLI entry point for spx\n */\nimport { Command } from \"commander\";\nimport { claudeDomain } from \"./domains/claude\";\nimport { sessionDomain } from \"./domains/session\";\nimport { specDomain } from \"./domains/spec\";\nimport { validationDomain } from \"./domains/validation\";\n\nconst program = new Command();\n\nprogram\n .name(\"spx\")\n .description(\"Fast, deterministic CLI tool for spec workflow management\")\n .version(\"0.2.0\");\n\n// Register domains\nclaudeDomain.register(program);\nsessionDomain.register(program);\nspecDomain.register(program);\nvalidationDomain.register(program);\n\nprogram.parse();\n","/**\n * Init command implementation\n *\n * Wrapper around `claude plugin marketplace` to install/update spx-claude marketplace\n */\nimport { execa } from \"execa\";\n\n/**\n * Options for init command\n */\nexport interface InitOptions {\n /** Working directory (for testing) */\n cwd?: string;\n}\n\n/**\n * Execute claude init command\n *\n * Wraps the Claude CLI's `plugin marketplace` commands to manage\n * the spx-claude marketplace installation.\n *\n * Behavior:\n * 1. Check if spx-claude marketplace exists via `claude plugin marketplace list`\n * 2. If missing: shell `claude plugin marketplace add simonheimlicher/spx-claude`\n * 3. If exists: shell `claude plugin marketplace update spx-claude`\n * 4. Parse output and return status message\n *\n * @param options - Command options\n * @returns Status message\n * @throws Error if claude CLI not available or commands fail\n *\n * @example\n * ```typescript\n * const output = await initCommand();\n * console.log(output);\n * // Output: \"✓ spx-claude marketplace installed successfully\"\n * // or: \"✓ spx-claude marketplace updated successfully\"\n * ```\n */\nexport async function initCommand(\n options: InitOptions = {},\n): Promise<string> {\n const cwd = options.cwd || process.cwd();\n\n try {\n // Step 1: Check if spx-claude marketplace exists\n const { stdout: listOutput } = await execa(\n \"claude\",\n [\"plugin\", \"marketplace\", \"list\"],\n { cwd },\n );\n\n const exists = listOutput.includes(\"spx-claude\");\n\n // Step 2: Add or update based on existence\n if (!exists) {\n // Add marketplace\n await execa(\n \"claude\",\n [\"plugin\", \"marketplace\", \"add\", \"simonheimlicher/spx-claude\"],\n { cwd },\n );\n\n return \"✓ spx-claude marketplace installed successfully\\n\\nRun 'claude plugin marketplace list' to view all marketplaces.\";\n } else {\n // Update marketplace\n await execa(\"claude\", [\"plugin\", \"marketplace\", \"update\", \"spx-claude\"], {\n cwd,\n });\n\n return \"✓ spx-claude marketplace updated successfully\\n\\nThe marketplace is now up to date.\";\n }\n } catch (error) {\n if (error instanceof Error) {\n // Check for specific error conditions\n if (\n error.message.includes(\"ENOENT\")\n || error.message.includes(\"command not found\")\n ) {\n throw new Error(\n \"Claude CLI not found. Please install Claude Code first.\\n\\nVisit: https://docs.anthropic.com/claude-code\",\n );\n }\n\n throw new Error(`Failed to initialize marketplace: ${error.message}`);\n }\n throw error;\n }\n}\n","/**\n * Consolidate command implementation\n *\n * Orchestrates the full consolidation pipeline:\n * 1. Discovery - Find all settings.local.json files\n * 2. Parsing - Extract permissions from each file\n * 3. Merging - Combine with subsumption and conflict resolution\n * 4. Backup - Create timestamped backup (if not dry-run)\n * 5. Writing - Atomically write merged settings (if not dry-run)\n * 6. Reporting - Format and return result summary\n */\nimport os from \"os\";\nimport path from \"path\";\nimport { findSettingsFiles } from \"../../../lib/claude/permissions/discovery.js\";\nimport { mergePermissions } from \"../../../lib/claude/permissions/merger.js\";\nimport { parseAllSettings, parseSettingsFile } from \"../../../lib/claude/permissions/parser.js\";\nimport { createBackup } from \"../../../lib/claude/settings/backup.js\";\nimport { formatReport } from \"../../../lib/claude/settings/reporter.js\";\nimport { writeSettings } from \"../../../lib/claude/settings/writer.js\";\n\n/**\n * Options for consolidate command\n */\nexport interface ConsolidateOptions {\n /** Root directory to scan for settings files (default: ~/Code) */\n root?: string;\n /** Write changes to global settings file (default: false = preview only) */\n write?: boolean;\n /** Write merged settings to specified file instead of global settings */\n outputFile?: string;\n /** Path to global settings file (for testing; default: ~/.claude/settings.json) */\n globalSettings?: string;\n}\n\n/**\n * Execute settings consolidate command\n *\n * Consolidates permissions from project-local settings files into\n * the global Claude Code settings file.\n *\n * Features:\n * - Discovers all `.claude/settings.local.json` files recursively\n * - Applies subsumption to remove narrower permissions\n * - Resolves conflicts (deny wins over allow)\n * - Deduplicates and sorts permissions\n * - Creates backup before modifications\n * - Supports dry-run mode for preview\n *\n * @param options - Command options\n * @returns Formatted report string\n * @throws Error if discovery, parsing, or writing fails\n *\n * @example\n * ```typescript\n * // Normal consolidation\n * const output = await consolidateCommand({ root: \"~/Code\" });\n * console.log(output);\n *\n * // Dry-run preview\n * const preview = await consolidateCommand({ root: \"~/Code\", dryRun: true });\n * console.log(preview);\n * ```\n */\nexport async function consolidateCommand(\n options: ConsolidateOptions = {},\n): Promise<string> {\n // Resolve paths\n const root = options.root\n ? path.resolve(options.root.replace(/^~/, os.homedir()))\n : path.join(os.homedir(), \"Code\");\n\n const globalSettingsPath = options.globalSettings\n || path.join(os.homedir(), \".claude\", \"settings.json\");\n\n const shouldWrite = options.write || false;\n const outputFile = options.outputFile;\n const previewOnly = !shouldWrite && !outputFile;\n\n // Step 1: Discovery - find all settings.local.json files\n const settingsFiles = await findSettingsFiles(root);\n\n if (settingsFiles.length === 0) {\n return `No settings files found in ${root}\\n\\nSearched for: **/.claude/settings.local.json`;\n }\n\n // Step 2: Parsing - extract permissions from each file\n const localPermissions = await parseAllSettings(settingsFiles);\n\n // Step 3: Read global settings\n let globalSettings = await parseSettingsFile(globalSettingsPath);\n\n // If global settings doesn't exist, create empty structure\n if (!globalSettings) {\n globalSettings = {\n permissions: {\n allow: [],\n deny: [],\n ask: [],\n },\n };\n }\n\n // Ensure permissions object exists\n if (!globalSettings.permissions) {\n globalSettings.permissions = {\n allow: [],\n deny: [],\n ask: [],\n };\n }\n\n // Step 4: Merge with subsumption and conflict resolution\n const { merged, result } = mergePermissions(\n globalSettings.permissions,\n localPermissions,\n );\n\n // Step 5: Backup (only when writing to global settings)\n if (shouldWrite) {\n try {\n result.backupPath = await createBackup(globalSettingsPath);\n } catch (error) {\n // If backup fails because file doesn't exist, that's okay (first time)\n if (error instanceof Error && !error.message.includes(\"not found\")) {\n throw error;\n }\n }\n }\n\n // Step 6: Write (if --write or --output-file specified)\n if (shouldWrite) {\n const updatedSettings = {\n ...globalSettings,\n permissions: merged,\n };\n await writeSettings(globalSettingsPath, updatedSettings);\n } else if (outputFile) {\n const updatedSettings = {\n ...globalSettings,\n permissions: merged,\n };\n const resolvedOutputPath = path.resolve(outputFile.replace(/^~/, os.homedir()));\n await writeSettings(resolvedOutputPath, updatedSettings);\n result.outputPath = resolvedOutputPath;\n }\n\n // Step 7: Report\n return formatReport(result, previewOnly, globalSettingsPath, outputFile);\n}\n","/**\n * Discovery of Claude Code settings files across project directories\n */\nimport fs from \"fs/promises\";\nimport path from \"path\";\n\n/**\n * Recursively find all .claude/settings.local.json files under a root directory\n *\n * Walks the directory tree looking for files matching the pattern:\n * `**\\/.claude/settings.local.json`\n *\n * @param root - Root directory path to start searching from\n * @param visited - Set of visited paths to avoid symlink loops (internal use)\n * @returns Promise resolving to array of absolute paths to settings.local.json files\n * @throws Error if root directory doesn't exist or permission denied\n *\n * @example\n * ```typescript\n * const files = await findSettingsFiles(\"~/Code\");\n * // Returns: [\n * // \"/Users/shz/Code/project-a/.claude/settings.local.json\",\n * // \"/Users/shz/Code/project-b/.claude/settings.local.json\"\n * // ]\n * ```\n */\nexport async function findSettingsFiles(\n root: string,\n visited: Set<string> = new Set(),\n): Promise<string[]> {\n // Normalize and resolve path to handle symlinks and ~ expansion\n const normalizedRoot = path.resolve(root.replace(/^~/, process.env.HOME || \"~\"));\n\n // Check for symlink loops\n if (visited.has(normalizedRoot)) {\n return []; // Skip already visited directories\n }\n visited.add(normalizedRoot);\n\n try {\n // Stat the root to verify it exists and is a directory\n const stats = await fs.stat(normalizedRoot);\n if (!stats.isDirectory()) {\n throw new Error(`Path is not a directory: ${normalizedRoot}`);\n }\n\n // Read directory contents\n const entries = await fs.readdir(normalizedRoot, { withFileTypes: true });\n const results: string[] = [];\n\n for (const entry of entries) {\n const fullPath = path.join(normalizedRoot, entry.name);\n\n // If this is a .claude directory, check for settings.local.json\n if (entry.isDirectory() && entry.name === \".claude\") {\n const settingsPath = path.join(fullPath, \"settings.local.json\");\n if (await isValidSettingsFile(settingsPath)) {\n results.push(settingsPath);\n }\n }\n\n // Recursively search subdirectories (skip .claude to avoid double-checking)\n if (entry.isDirectory() && entry.name !== \".claude\") {\n const subFiles = await findSettingsFiles(fullPath, visited);\n results.push(...subFiles);\n }\n }\n\n return results;\n } catch (error) {\n // Re-throw with more context\n if (error instanceof Error) {\n if (error.message.includes(\"ENOENT\")) {\n throw new Error(`Directory not found: ${normalizedRoot}`);\n }\n if (error.message.includes(\"EACCES\")) {\n throw new Error(`Permission denied: ${normalizedRoot}`);\n }\n throw new Error(\n `Failed to search directory \"${normalizedRoot}\": ${error.message}`,\n );\n }\n throw error;\n }\n}\n\n/**\n * Check if a given path is a valid settings.local.json file\n *\n * Validates that:\n * - File exists\n * - File is readable\n * - File has .json extension\n *\n * @param filePath - Absolute path to check\n * @returns Promise resolving to true if valid settings file, false otherwise\n *\n * @example\n * ```typescript\n * const isValid = await isValidSettingsFile(\"/path/to/.claude/settings.local.json\");\n * // Returns: true or false\n * ```\n */\nexport async function isValidSettingsFile(filePath: string): Promise<boolean> {\n try {\n // Check if file exists and is readable\n await fs.access(filePath, fs.constants.R_OK);\n\n // Check if it's actually a file (not a directory)\n const stats = await fs.stat(filePath);\n if (!stats.isFile()) {\n return false;\n }\n\n // Validate it has .json extension\n return path.extname(filePath) === \".json\";\n } catch {\n // File doesn't exist or isn't readable\n return false;\n }\n}\n","/**\n * Parser for Claude Code settings files and permissions\n */\nimport fs from \"fs/promises\";\nimport type { ClaudeSettings, Permission, PermissionCategory, Permissions } from \"./types.js\";\n\n/**\n * Parse a settings.json file and extract permissions\n *\n * Handles:\n * - Malformed JSON (returns null)\n * - Missing permissions object (returns empty permissions)\n * - Validates basic structure\n *\n * @param filePath - Absolute path to settings.json file\n * @returns Promise resolving to ClaudeSettings object, or null if malformed\n *\n * @example\n * ```typescript\n * const settings = await parseSettingsFile(\"/path/to/.claude/settings.json\");\n * if (settings) {\n * console.log(settings.permissions?.allow);\n * }\n * ```\n */\nexport async function parseSettingsFile(\n filePath: string,\n): Promise<ClaudeSettings | null> {\n try {\n // Read file contents\n const content = await fs.readFile(filePath, \"utf-8\");\n\n // Parse JSON\n const parsed = JSON.parse(content);\n\n // Basic validation: should be an object\n if (typeof parsed !== \"object\" || parsed === null) {\n return null;\n }\n\n return parsed as ClaudeSettings;\n } catch {\n // JSON parse error or file read error\n return null;\n }\n}\n\n/**\n * Parse a permission string into structured components\n *\n * Permission format: \"Type(scope)\"\n * Examples:\n * - \"Bash(git:*)\" => { type: \"Bash\", scope: \"git:*\" }\n * - \"Read(file_path:/Users/shz/Code/**)\" => { type: \"Read\", scope: \"file_path:/Users/shz/Code/**\" }\n * - \"WebFetch(domain:github.com)\" => { type: \"WebFetch\", scope: \"domain:github.com\" }\n *\n * @param raw - Raw permission string\n * @param category - Permission category (allow/deny/ask)\n * @returns Parsed Permission object\n * @throws Error if permission string is malformed\n *\n * @example\n * ```typescript\n * const perm = parsePermission(\"Bash(git:*)\", \"allow\");\n * // Returns: { raw: \"Bash(git:*)\", type: \"Bash\", scope: \"git:*\", category: \"allow\" }\n * ```\n */\nexport function parsePermission(\n raw: string,\n category: PermissionCategory,\n): Permission {\n // Match pattern: Type(scope)\n const match = raw.match(/^([^(]+)\\((.+)\\)$/);\n\n if (!match) {\n throw new Error(`Malformed permission string: \"${raw}\"`);\n }\n\n const [, type, scope] = match;\n\n return {\n raw,\n type: type.trim(),\n scope: scope.trim(),\n category,\n };\n}\n\n/**\n * Parse all permissions from a Permissions object\n *\n * Converts permission strings to structured Permission objects,\n * grouped by category (allow/deny/ask).\n *\n * @param permissions - Permissions object from settings.json\n * @returns Array of parsed Permission objects\n *\n * @example\n * ```typescript\n * const permissions = {\n * allow: [\"Bash(git:*)\", \"Bash(npm:*)\"],\n * deny: [\"Bash(rm -rf:*)\"]\n * };\n * const parsed = parseAllPermissions(permissions);\n * // Returns array of Permission objects with category set\n * ```\n */\nexport function parseAllPermissions(permissions: Permissions): Permission[] {\n const result: Permission[] = [];\n\n // Parse allow permissions\n if (permissions.allow) {\n for (const perm of permissions.allow) {\n try {\n result.push(parsePermission(perm, \"allow\"));\n } catch {\n // Skip malformed permissions\n continue;\n }\n }\n }\n\n // Parse deny permissions\n if (permissions.deny) {\n for (const perm of permissions.deny) {\n try {\n result.push(parsePermission(perm, \"deny\"));\n } catch {\n // Skip malformed permissions\n continue;\n }\n }\n }\n\n // Parse ask permissions\n if (permissions.ask) {\n for (const perm of permissions.ask) {\n try {\n result.push(parsePermission(perm, \"ask\"));\n } catch {\n // Skip malformed permissions\n continue;\n }\n }\n }\n\n return result;\n}\n\n/**\n * Read and parse multiple settings files\n *\n * Processes an array of file paths, reading and parsing each one.\n * Skips files that can't be read or parsed.\n *\n * @param filePaths - Array of absolute paths to settings files\n * @returns Promise resolving to array of Permissions objects (one per valid file)\n *\n * @example\n * ```typescript\n * const files = [\n * \"/Users/shz/Code/project-a/.claude/settings.local.json\",\n * \"/Users/shz/Code/project-b/.claude/settings.local.json\"\n * ];\n * const allPermissions = await parseAllSettings(files);\n * // Returns: [{ allow: [...], deny: [...] }, { allow: [...] }]\n * ```\n */\nexport async function parseAllSettings(\n filePaths: string[],\n): Promise<Permissions[]> {\n const results: Permissions[] = [];\n\n for (const filePath of filePaths) {\n const settings = await parseSettingsFile(filePath);\n if (settings?.permissions) {\n results.push(settings.permissions);\n }\n }\n\n return results;\n}\n","/**\n * Directory walking and filesystem traversal\n */\nimport fs from \"fs/promises\";\nimport path from \"path\";\nimport type { DirectoryEntry, WorkItem } from \"../types.js\";\nimport { parseWorkItemName } from \"./patterns.js\";\n\n/**\n * Recursively walk a directory tree and return all subdirectories\n *\n * @param root - Root directory path to start walking from\n * @param visited - Set of visited paths to avoid symlink loops (internal use)\n * @returns Promise resolving to array of directory entries\n * @throws Error if root directory doesn't exist or permission denied\n *\n * @example\n * ```typescript\n * const entries = await walkDirectory(\"/path/to/specs\");\n * // Returns: [{ name: \"capability-21_test\", path: \"/path/to/specs/capability-21_test\", isDirectory: true }, ...]\n * ```\n */\nexport async function walkDirectory(\n root: string,\n visited: Set<string> = new Set()\n): Promise<DirectoryEntry[]> {\n // Normalize and resolve path to handle symlinks\n const normalizedRoot = path.resolve(root);\n\n // Check for symlink loops\n if (visited.has(normalizedRoot)) {\n return []; // Skip already visited directories\n }\n visited.add(normalizedRoot);\n\n try {\n // Read directory contents\n const entries = await fs.readdir(normalizedRoot, { withFileTypes: true });\n const results: DirectoryEntry[] = [];\n\n for (const entry of entries) {\n const fullPath = path.join(normalizedRoot, entry.name);\n\n // Only process directories\n if (entry.isDirectory()) {\n // Add current directory\n results.push({\n name: entry.name,\n path: fullPath,\n isDirectory: true,\n });\n\n // Recursively walk subdirectories\n const subEntries = await walkDirectory(fullPath, visited);\n results.push(...subEntries);\n }\n }\n\n return results;\n } catch (error) {\n // Re-throw with more context\n if (error instanceof Error) {\n throw new Error(\n `Failed to walk directory \"${normalizedRoot}\": ${error.message}`\n );\n }\n throw error;\n }\n}\n\n/**\n * Filter directory entries to include only work item directories\n *\n * Uses parseWorkItemName() to validate directory names match work item patterns.\n * Excludes directories that don't match capability/feature/story patterns.\n *\n * @param entries - Array of directory entries to filter\n * @returns Filtered array containing only valid work item directories\n *\n * @example\n * ```typescript\n * const entries = [\n * { name: \"capability-21_test\", path: \"/specs/capability-21_test\", isDirectory: true },\n * { name: \"node_modules\", path: \"/specs/node_modules\", isDirectory: true },\n * ];\n * const filtered = filterWorkItemDirectories(entries);\n * // Returns: [{ name: \"capability-21_test\", ... }]\n * ```\n */\nexport function filterWorkItemDirectories(\n entries: DirectoryEntry[]\n): DirectoryEntry[] {\n return entries.filter((entry) => {\n try {\n // Try to parse the directory name as a work item\n parseWorkItemName(entry.name);\n return true; // Valid work item pattern\n } catch {\n return false; // Not a work item pattern\n }\n });\n}\n\n/**\n * Convert directory entries to WorkItem objects\n *\n * Parses each directory entry name to extract work item metadata (kind, number, slug)\n * and combines it with the full filesystem path.\n *\n * @param entries - Filtered directory entries (work items only)\n * @returns Array of WorkItem objects with full metadata\n * @throws Error if any entry has invalid work item pattern\n *\n * @example\n * ```typescript\n * const entries = [\n * { name: \"capability-21_core-cli\", path: \"/specs/capability-21_core-cli\", isDirectory: true },\n * ];\n * const workItems = buildWorkItemList(entries);\n * // Returns: [{ kind: \"capability\", number: 20, slug: \"core-cli\", path: \"/specs/capability-21_core-cli\" }]\n * ```\n */\nexport function buildWorkItemList(entries: DirectoryEntry[]): WorkItem[] {\n return entries.map((entry) => ({\n ...parseWorkItemName(entry.name),\n path: entry.path,\n }));\n}\n\n/**\n * Normalize path separators for cross-platform consistency\n *\n * Converts Windows backslashes to forward slashes for consistent path handling\n * across different operating systems.\n *\n * @param filepath - Path to normalize\n * @returns Normalized path with forward slashes\n *\n * @example\n * ```typescript\n * normalizePath(\"C:\\\\Users\\\\test\\\\specs\"); // Returns: \"C:/Users/test/specs\"\n * normalizePath(\"/home/user/specs\"); // Returns: \"/home/user/specs\"\n * ```\n */\nexport function normalizePath(filepath: string): string {\n // Replace all backslashes with forward slashes for cross-platform consistency\n return filepath.replace(/\\\\/g, \"/\");\n}\n","/**\n * Permission subsumption detection\n *\n * Detects when broader permissions subsume narrower ones:\n * - Bash(git:*) subsumes Bash(git log:*), Bash(git worktree:*), etc.\n * - Read(file_path:/Users/shz/Code/**) subsumes Read(file_path:/Users/shz/Code/project-a/**)\n */\nimport { normalizePath } from \"../../../scanner/walk.js\";\nimport { parsePermission } from \"./parser.js\";\nimport type { Permission, PermissionCategory, ScopePattern, SubsumptionResult } from \"./types.js\";\n\n/**\n * Parse a scope string to extract pattern type and value\n *\n * Determines whether the scope is a command pattern (e.g., \"git:*\")\n * or a path pattern (e.g., \"file_path:/Users/shz/Code/**\").\n *\n * @param scope - Scope string from permission\n * @returns ScopePattern with type and pattern\n *\n * @example\n * ```typescript\n * parseScopePattern(\"git:*\")\n * // Returns: { type: \"command\", pattern: \"git:*\" }\n *\n * parseScopePattern(\"file_path:/Users/shz/Code/**\")\n * // Returns: { type: \"path\", pattern: \"/Users/shz/Code/**\" }\n * ```\n */\nexport function parseScopePattern(scope: string): ScopePattern {\n // Check if scope contains path indicators\n if (\n scope.includes(\"file_path:\")\n || scope.includes(\"directory_path:\")\n || scope.includes(\"path:\")\n ) {\n // Extract path after the colon\n const colonIndex = scope.indexOf(\":\");\n const pattern = colonIndex >= 0 ? scope.substring(colonIndex + 1) : scope;\n return { type: \"path\", pattern };\n }\n\n // Default to command pattern (e.g., \"git:*\", \"npm:*\")\n return { type: \"command\", pattern: scope };\n}\n\n/**\n * Check if permission A subsumes permission B\n *\n * Subsumption rules:\n * 1. Same type required (Bash subsumes Bash, not Read)\n * 2. Identical scopes = not subsumption (exact match)\n * 3. Broader scope subsumes narrower scope:\n * - Command: \"git:*\" subsumes \"git log:*\", \"git worktree:*\"\n * - Path: \"/Users/shz/Code/**\" subsumes \"/Users/shz/Code/project-a/**\"\n *\n * @param broader - Permission that might subsume the other\n * @param narrower - Permission that might be subsumed\n * @returns true if broader subsumes narrower, false otherwise\n *\n * @example\n * ```typescript\n * subsumes(\n * parsePermission(\"Bash(git:*)\", \"allow\"),\n * parsePermission(\"Bash(git log:*)\", \"allow\")\n * )\n * // Returns: true\n *\n * subsumes(\n * parsePermission(\"Read(file_path:/Users/shz/Code/**)\", \"allow\"),\n * parsePermission(\"Read(file_path:/Users/shz/Code/project-a/**)\", \"allow\")\n * )\n * // Returns: true\n *\n * subsumes(\n * parsePermission(\"Bash(git:*)\", \"allow\"),\n * parsePermission(\"Read(file_path:/Users/shz/Code/**)\", \"allow\")\n * )\n * // Returns: false (different types)\n * ```\n */\nexport function subsumes(broader: Permission, narrower: Permission): boolean {\n // 1. Type must match\n if (broader.type !== narrower.type) {\n return false;\n }\n\n // 2. Identical = not subsumption (this is just a duplicate)\n if (broader.scope === narrower.scope) {\n return false;\n }\n\n // 3. Parse scope patterns\n const broaderScope = parseScopePattern(broader.scope);\n const narrowerScope = parseScopePattern(narrower.scope);\n\n // 4. Handle command patterns (e.g., \"git:*\")\n if (broaderScope.type === \"command\" && narrowerScope.type === \"command\") {\n // Extract base command by removing :* suffix\n const broaderBase = broaderScope.pattern.replace(/:?\\*+$/, \"\");\n const narrowerFull = narrowerScope.pattern.replace(/:?\\*+$/, \"\");\n\n // Check if narrower starts with broader prefix\n // \"git:*\" subsumes \"git log:*\" if:\n // - narrowerFull starts with broaderBase\n // - narrowerFull is longer (more specific)\n if (narrowerFull.startsWith(broaderBase)) {\n // Additional specificity check: narrower must add more detail\n // e.g., \"git\" subsumes \"git log\", but not \"git\" subsumes \"git\"\n return narrowerFull.length > broaderBase.length;\n }\n }\n\n // 5. Handle path patterns (e.g., \"/Users/shz/Code/**\")\n if (broaderScope.type === \"path\" && narrowerScope.type === \"path\") {\n // Normalize paths for comparison\n const broaderPath = normalizePath(broaderScope.pattern.replace(/\\/?\\*+$/, \"\"));\n const narrowerPath = normalizePath(narrowerScope.pattern.replace(/\\/?\\*+$/, \"\"));\n\n // Check if narrower is a sub-path of broader\n // \"/Users/shz/Code\" subsumes \"/Users/shz/Code/project-a\" if:\n // - narrowerPath starts with broaderPath + \"/\"\n return narrowerPath.startsWith(broaderPath + \"/\");\n }\n\n // 6. Mixed pattern types don't subsume each other\n return false;\n}\n\n/**\n * Find all subsumption relationships in a permission list\n *\n * Returns a map of broader permissions to the narrower permissions\n * they subsume. This allows identifying which permissions can be\n * removed from the list.\n *\n * @param permissions - Array of permissions to analyze\n * @returns Array of subsumption results\n *\n * @example\n * ```typescript\n * const permissions = [\n * parsePermission(\"Bash(git:*)\", \"allow\"),\n * parsePermission(\"Bash(git log:*)\", \"allow\"),\n * parsePermission(\"Bash(git worktree:*)\", \"allow\"),\n * parsePermission(\"Bash(npm:*)\", \"allow\"),\n * ];\n *\n * const results = detectSubsumptions(permissions);\n * // Returns: [\n * // {\n * // broader: { raw: \"Bash(git:*)\", ... },\n * // narrower: [\n * // { raw: \"Bash(git log:*)\", ... },\n * // { raw: \"Bash(git worktree:*)\", ... }\n * // ]\n * // }\n * // ]\n * ```\n */\nexport function detectSubsumptions(\n permissions: Permission[],\n): SubsumptionResult[] {\n const results: SubsumptionResult[] = [];\n const processedBroader = new Set<string>();\n\n for (let i = 0; i < permissions.length; i++) {\n const broader = permissions[i];\n\n // Skip if already processed as a broader permission\n if (processedBroader.has(broader.raw)) {\n continue;\n }\n\n const narrowerPerms: Permission[] = [];\n\n // Check all other permissions to see if this one subsumes them\n for (let j = 0; j < permissions.length; j++) {\n if (i === j) continue; // Skip comparing with self\n\n const narrower = permissions[j];\n\n if (subsumes(broader, narrower)) {\n narrowerPerms.push(narrower);\n }\n }\n\n // If this permission subsumes others, record it\n if (narrowerPerms.length > 0) {\n results.push({\n broader,\n narrower: narrowerPerms,\n });\n processedBroader.add(broader.raw);\n }\n }\n\n return results;\n}\n\n/**\n * Remove subsumed permissions from a list, keeping only the broadest ones\n *\n * This is the main function for consolidating permissions.\n * It finds all subsumption relationships and removes narrower\n * permissions, leaving only the broadest ones.\n *\n * @param permissionStrings - Array of raw permission strings\n * @param category - Permission category (for parsing)\n * @returns Array of permission strings with subsumed ones removed\n *\n * @example\n * ```typescript\n * const permissions = [\n * \"Bash(git:*)\",\n * \"Bash(git log:*)\",\n * \"Bash(git worktree:*)\",\n * \"Bash(npm:*)\",\n * ];\n *\n * const result = removeSubsumed(permissions, \"allow\");\n * // Returns: [\"Bash(git:*)\", \"Bash(npm:*)\"]\n * ```\n */\nexport function removeSubsumed(\n permissionStrings: string[],\n category: PermissionCategory,\n): string[] {\n // Parse all permission strings\n const permissions = permissionStrings\n .map((raw) => {\n try {\n return parsePermission(raw, category);\n } catch {\n // Keep malformed permissions as-is (don't filter them out)\n return null;\n }\n })\n .filter((p): p is Permission => p !== null);\n\n // Detect subsumptions\n const subsumptions = detectSubsumptions(permissions);\n\n // Build set of narrower permissions to remove\n const toRemove = new Set<string>();\n for (const result of subsumptions) {\n for (const narrower of result.narrower) {\n toRemove.add(narrower.raw);\n }\n }\n\n // Filter out subsumed permissions\n return permissionStrings.filter((perm) => !toRemove.has(perm));\n}\n","/**\n * Permission merging with subsumption and conflict resolution\n */\nimport { parsePermission } from \"./parser.js\";\nimport { removeSubsumed, subsumes } from \"./subsumption.js\";\nimport type { ConsolidationResult, Permissions, PermissionsAdded } from \"./types.js\";\n\n/**\n * Merge permissions from global settings and multiple local settings files\n *\n * Process:\n * 1. Combine all permissions by category (allow/deny/ask)\n * 2. Apply subsumption to remove narrower permissions\n * 3. Resolve conflicts (deny wins over allow)\n * 4. Deduplicate using Sets\n * 5. Sort alphabetically\n *\n * @param global - Global settings permissions (baseline)\n * @param local - Array of local settings permissions to merge in\n * @returns Merged permissions and consolidation statistics\n *\n * @example\n * ```typescript\n * const global = { allow: [\"Bash(ls:*)\"] };\n * const local = [\n * { allow: [\"Bash(git:*)\", \"Bash(git log:*)\"] },\n * { deny: [\"Bash(rm:*)\"] }\n * ];\n *\n * const { merged, result } = mergePermissions(global, local);\n * // merged.allow: [\"Bash(git:*)\", \"Bash(ls:*)\"] // git log:* removed by subsumption\n * // merged.deny: [\"Bash(rm:*)\"]\n * // result.subsumed: [\"Bash(git log:*)\"]\n * ```\n */\nexport function mergePermissions(\n global: Permissions,\n local: Permissions[],\n): { merged: Permissions; result: ConsolidationResult } {\n // Track original global permissions for computing added\n const originalGlobal = {\n allow: new Set(global.allow || []),\n deny: new Set(global.deny || []),\n ask: new Set(global.ask || []),\n };\n\n // Step 1: Combine all permissions by category\n const combined: Permissions = {\n allow: [...(global.allow || [])],\n deny: [...(global.deny || [])],\n ask: [...(global.ask || [])],\n };\n\n let filesProcessed = 0;\n let filesSkipped = 0;\n\n for (const localPerms of local) {\n let hasPerms = false;\n\n if (localPerms.allow && localPerms.allow.length > 0) {\n combined.allow?.push(...localPerms.allow);\n hasPerms = true;\n }\n if (localPerms.deny && localPerms.deny.length > 0) {\n combined.deny?.push(...localPerms.deny);\n hasPerms = true;\n }\n if (localPerms.ask && localPerms.ask.length > 0) {\n combined.ask?.push(...localPerms.ask);\n hasPerms = true;\n }\n\n if (hasPerms) {\n filesProcessed++;\n } else {\n filesSkipped++;\n }\n }\n\n // Step 2: Apply subsumption to each category\n const allSubsumed: string[] = [];\n\n const afterSubsumption: Permissions = {};\n\n if (combined.allow && combined.allow.length > 0) {\n const before = new Set(combined.allow);\n combined.allow = removeSubsumed(combined.allow, \"allow\");\n const after = new Set(combined.allow);\n\n // Track what was removed\n for (const perm of before) {\n if (!after.has(perm)) {\n allSubsumed.push(perm);\n }\n }\n }\n\n if (combined.deny && combined.deny.length > 0) {\n const before = new Set(combined.deny);\n combined.deny = removeSubsumed(combined.deny, \"deny\");\n const after = new Set(combined.deny);\n\n // Track what was removed\n for (const perm of before) {\n if (!after.has(perm)) {\n allSubsumed.push(perm);\n }\n }\n }\n\n if (combined.ask && combined.ask.length > 0) {\n const before = new Set(combined.ask);\n combined.ask = removeSubsumed(combined.ask, \"ask\");\n const after = new Set(combined.ask);\n\n // Track what was removed\n for (const perm of before) {\n if (!after.has(perm)) {\n allSubsumed.push(perm);\n }\n }\n }\n\n afterSubsumption.allow = combined.allow;\n afterSubsumption.deny = combined.deny;\n afterSubsumption.ask = combined.ask;\n\n // Step 3: Resolve conflicts (deny wins over allow)\n const {\n resolved,\n conflictCount,\n subsumed: conflictSubsumed,\n } = resolveConflicts(afterSubsumption);\n\n // Combine subsumptions from step 2 and step 3\n const subsumed = [...allSubsumed, ...conflictSubsumed];\n\n // Step 4: Deduplicate using Sets and sort\n const merged: Permissions = {};\n\n if (resolved.allow && resolved.allow.length > 0) {\n merged.allow = Array.from(new Set(resolved.allow)).sort();\n }\n\n if (resolved.deny && resolved.deny.length > 0) {\n merged.deny = Array.from(new Set(resolved.deny)).sort();\n }\n\n if (resolved.ask && resolved.ask.length > 0) {\n merged.ask = Array.from(new Set(resolved.ask)).sort();\n }\n\n // Step 5: Compute what was added\n const added: PermissionsAdded = {\n allow: [],\n deny: [],\n ask: [],\n };\n\n for (const perm of merged.allow || []) {\n if (!originalGlobal.allow.has(perm)) {\n added.allow.push(perm);\n }\n }\n\n for (const perm of merged.deny || []) {\n if (!originalGlobal.deny.has(perm)) {\n added.deny.push(perm);\n }\n }\n\n for (const perm of merged.ask || []) {\n if (!originalGlobal.ask.has(perm)) {\n added.ask.push(perm);\n }\n }\n\n // Build result\n const result: ConsolidationResult = {\n filesScanned: local.length,\n filesProcessed,\n filesSkipped,\n added,\n subsumed,\n conflictsResolved: conflictCount,\n };\n\n return { merged, result };\n}\n\n/**\n * Resolve conflicts between allow and deny permissions\n *\n * Rules:\n * - If exact match in both allow and deny: keep in deny, remove from allow\n * - If deny has broader permission that subsumes allow: remove from allow\n *\n * This implements a security-first approach: deny always wins.\n *\n * @param permissions - Permissions with potential conflicts\n * @returns Resolved permissions with conflicts removed, count of conflicts, and list of subsumed permissions\n *\n * @example\n * ```typescript\n * const permissions = {\n * allow: [\"Bash(git log:*)\", \"Bash(npm:*)\"],\n * deny: [\"Bash(git:*)\"]\n * };\n *\n * const { resolved, conflictCount } = resolveConflicts(permissions);\n * // resolved.allow: [\"Bash(npm:*)\"] // git log:* removed (subsumed by deny git:*)\n * // resolved.deny: [\"Bash(git:*)\"]\n * // conflictCount: 1\n * ```\n */\nexport function resolveConflicts(permissions: Permissions): {\n resolved: Permissions;\n conflictCount: number;\n subsumed: string[];\n} {\n const allow = permissions.allow || [];\n const deny = permissions.deny || [];\n const ask = permissions.ask || [];\n\n const denySet = new Set(deny);\n const subsumed: string[] = [];\n let conflictCount = 0;\n\n // Check each allow permission against deny permissions\n const allowToRemove = new Set<string>();\n\n for (const allowPerm of allow) {\n // Exact match: move to deny\n if (denySet.has(allowPerm)) {\n allowToRemove.add(allowPerm);\n conflictCount++;\n continue;\n }\n\n // Check if any deny permission subsumes this allow permission\n for (const denyPerm of deny) {\n try {\n const allowParsed = parsePermission(allowPerm, \"allow\");\n const denyParsed = parsePermission(denyPerm, \"deny\");\n\n if (subsumes(denyParsed, allowParsed)) {\n // Deny subsumes allow - remove from allow\n allowToRemove.add(allowPerm);\n subsumed.push(allowPerm);\n conflictCount++;\n break;\n }\n } catch {\n // Skip malformed permissions\n continue;\n }\n }\n }\n\n // Build resolved permissions\n const resolved: Permissions = {\n allow: allow.filter((p) => !allowToRemove.has(p)),\n deny,\n ask,\n };\n\n return { resolved, conflictCount, subsumed };\n}\n","/**\n * Backup management for Claude Code settings files\n */\nimport fs from \"fs/promises\";\n\n/**\n * Create a timestamped backup of a settings file\n *\n * Backup format: `<original-path>.backup.YYYY-MM-DD-HHmmss`\n *\n * Example: `settings.json.backup.2026-01-08-143022`\n *\n * @param settingsPath - Absolute path to settings file to back up\n * @returns Promise resolving to backup file path\n * @throws Error if source file doesn't exist or backup fails\n *\n * @example\n * ```typescript\n * const backupPath = await createBackup(\"/Users/shz/.claude/settings.json\");\n * // Returns: \"/Users/shz/.claude/settings.json.backup.2026-01-08-143022\"\n * ```\n */\nexport async function createBackup(settingsPath: string): Promise<string> {\n try {\n // Verify source file exists\n await fs.access(settingsPath, fs.constants.R_OK);\n\n // Generate timestamp: YYYY-MM-DD-HHmmss\n const now = new Date();\n const timestamp = [\n now.getFullYear(),\n String(now.getMonth() + 1).padStart(2, \"0\"),\n String(now.getDate()).padStart(2, \"0\"),\n ].join(\"-\") + \"-\" + [\n String(now.getHours()).padStart(2, \"0\"),\n String(now.getMinutes()).padStart(2, \"0\"),\n String(now.getSeconds()).padStart(2, \"0\"),\n ].join(\"\");\n\n // Build backup path\n const backupPath = `${settingsPath}.backup.${timestamp}`;\n\n // Copy file to backup location\n await fs.copyFile(settingsPath, backupPath);\n\n return backupPath;\n } catch (error) {\n if (error instanceof Error) {\n if (error.message.includes(\"ENOENT\")) {\n throw new Error(`Settings file not found: ${settingsPath}`);\n }\n if (error.message.includes(\"EACCES\")) {\n throw new Error(`Permission denied: ${settingsPath}`);\n }\n throw new Error(`Failed to create backup: ${error.message}`);\n }\n throw error;\n }\n}\n","/**\n * Formatting and reporting of consolidation results\n */\nimport type { ConsolidationResult } from \"../permissions/types.js\";\n\n/**\n * Format consolidation result as user-friendly text report\n *\n * Shows:\n * - Files scanned/processed/skipped\n * - Permissions added by category (allow/deny/ask)\n * - Conflicts resolved\n * - Subsumed permissions removed\n * - Backup path (if created)\n * - Instructions (if preview mode) or confirmation (if written)\n *\n * @param result - Consolidation result data\n * @param previewOnly - Whether this is preview-only mode (default behavior)\n * @param globalSettingsPath - Path to global settings file\n * @param outputFile - Optional output file path\n * @returns Formatted report string\n *\n * @example\n * ```typescript\n * const result = {\n * filesScanned: 12,\n * filesProcessed: 10,\n * filesSkipped: 2,\n * added: {\n * allow: [\"Bash(git:*)\", \"Bash(npm:*)\"],\n * deny: [\"Bash(rm:*)\"],\n * ask: []\n * },\n * subsumed: [\"Bash(git log:*)\", \"Bash(git worktree:*)\"],\n * conflictsResolved: 1,\n * backupPath: \"/Users/shz/.claude/settings.json.backup.2026-01-08-143022\"\n * };\n *\n * console.log(formatReport(result, true, \"/Users/shz/.claude/settings.json\"));\n * // Outputs formatted report with instructions\n * ```\n */\nexport function formatReport(\n result: ConsolidationResult,\n previewOnly: boolean,\n globalSettingsPath?: string,\n outputFile?: string,\n): string {\n const lines: string[] = [];\n\n // Header\n lines.push(\"Scanning for Claude Code settings files...\");\n lines.push(\"\");\n\n // Files summary\n lines.push(`Found ${result.filesScanned} settings files`);\n lines.push(` Processed: ${result.filesProcessed}`);\n if (result.filesSkipped > 0) {\n lines.push(` Skipped: ${result.filesSkipped} (no permissions)`);\n }\n lines.push(\"\");\n\n // Permissions added\n const totalAdded = result.added.allow.length\n + result.added.deny.length\n + result.added.ask.length;\n\n if (totalAdded > 0) {\n lines.push(`Permissions to add: ${totalAdded}`);\n\n if (result.added.allow.length > 0) {\n lines.push(\"\");\n lines.push(\" allow:\");\n for (const perm of result.added.allow) {\n lines.push(` + ${perm}`);\n }\n }\n\n if (result.added.deny.length > 0) {\n lines.push(\"\");\n lines.push(\" deny:\");\n for (const perm of result.added.deny) {\n lines.push(` + ${perm}`);\n }\n }\n\n if (result.added.ask.length > 0) {\n lines.push(\"\");\n lines.push(\" ask:\");\n for (const perm of result.added.ask) {\n lines.push(` + ${perm}`);\n }\n }\n } else {\n lines.push(\"No new permissions to add (all permissions already in global settings)\");\n }\n\n lines.push(\"\");\n\n // Subsumption results\n if (result.subsumed.length > 0) {\n lines.push(`Subsumed permissions removed: ${result.subsumed.length}`);\n lines.push(\" (narrower permissions replaced by broader ones)\");\n for (const perm of result.subsumed) {\n lines.push(` - ${perm}`);\n }\n lines.push(\"\");\n }\n\n // Conflicts\n if (result.conflictsResolved > 0) {\n lines.push(`Conflicts resolved: ${result.conflictsResolved}`);\n lines.push(\" (permissions moved from allow to deny)\");\n lines.push(\"\");\n }\n\n // Backup\n if (result.backupPath) {\n lines.push(`Backup created: ${result.backupPath}`);\n lines.push(\"\");\n }\n\n // Summary\n lines.push(\"Summary:\");\n lines.push(` Files scanned: ${result.filesScanned}`);\n lines.push(\n ` Permissions added: ${result.added.allow.length} allow, ${result.added.deny.length} deny, ${result.added.ask.length} ask`,\n );\n if (result.subsumed.length > 0) {\n lines.push(` Subsumed removed: ${result.subsumed.length}`);\n }\n if (result.conflictsResolved > 0) {\n lines.push(` Conflicts resolved: ${result.conflictsResolved}`);\n }\n\n // Final status message\n lines.push(\"\");\n if (previewOnly) {\n lines.push(\"ℹ️ Preview mode: No changes written\");\n lines.push(\"\");\n lines.push(\"To apply changes:\");\n lines.push(` • Modify global settings: spx claude settings consolidate --write`);\n lines.push(` • Write to file: spx claude settings consolidate --output-file /path/to/file`);\n } else if (outputFile) {\n lines.push(`✓ Settings written to: ${result.outputPath || outputFile}`);\n lines.push(\"\");\n lines.push(\"To apply to your global settings:\");\n lines.push(` • Review the file, then copy to: ${globalSettingsPath || \"~/.claude/settings.json\"}`);\n lines.push(` • Or run: spx claude settings consolidate --write`);\n } else {\n lines.push(`✓ Global settings updated: ${globalSettingsPath || \"~/.claude/settings.json\"}`);\n }\n\n return lines.join(\"\\n\");\n}\n","/**\n * Atomic file writing for Claude Code settings\n */\nimport fs from \"fs/promises\";\nimport os from \"os\";\nimport path from \"path\";\nimport type { ClaudeSettings } from \"../permissions/types.js\";\n\n/**\n * Filesystem abstraction for dependency injection\n *\n * Enables testing error paths without mocking.\n */\nexport interface FileSystem {\n writeFile(path: string, content: string): Promise<void>;\n rename(oldPath: string, newPath: string): Promise<void>;\n unlink(path: string): Promise<void>;\n mkdir(path: string, options?: { recursive?: boolean }): Promise<void>;\n}\n\n/**\n * Production filesystem implementation\n */\nconst realFs: FileSystem = {\n writeFile: (path, content) => fs.writeFile(path, content, \"utf-8\"),\n rename: fs.rename,\n unlink: fs.unlink,\n mkdir: async (path, options) => {\n await fs.mkdir(path, options);\n },\n};\n\n/**\n * Atomically write settings to a file\n *\n * Uses temp file + rename pattern for atomicity:\n * 1. Write to temporary file\n * 2. Rename to target (atomic operation on most filesystems)\n *\n * Preserves JSON formatting with 2-space indentation.\n *\n * @param filePath - Absolute path to settings file\n * @param settings - Settings object to write\n * @param deps - Dependencies (for testing)\n * @throws Error if write fails\n *\n * @example\n * ```typescript\n * const settings = {\n * permissions: {\n * allow: [\"Bash(git:*)\", \"Bash(npm:*)\"]\n * }\n * };\n * await writeSettings(\"/Users/shz/.claude/settings.json\", settings);\n * ```\n */\nexport async function writeSettings(\n filePath: string,\n settings: ClaudeSettings,\n deps: { fs: FileSystem } = { fs: realFs },\n): Promise<void> {\n // Ensure directory exists\n const dir = path.dirname(filePath);\n await deps.fs.mkdir(dir, { recursive: true });\n\n // Generate temporary file path\n const tempPath = path.join(\n os.tmpdir(),\n `settings-${Date.now()}-${Math.random().toString(36).substring(7)}.json`,\n );\n\n try {\n // Format JSON with 2-space indentation and trailing newline\n const content = JSON.stringify(settings, null, 2) + \"\\n\";\n\n // Write to temp file\n await deps.fs.writeFile(tempPath, content);\n\n // Atomic rename\n await deps.fs.rename(tempPath, filePath);\n } catch (error) {\n // Cleanup temp file on failure\n try {\n await deps.fs.unlink(tempPath);\n } catch {\n // Ignore cleanup errors\n }\n\n // Re-throw original error\n if (error instanceof Error) {\n throw new Error(`Failed to write settings: ${error.message}`);\n }\n throw error;\n }\n}\n","/**\n * Claude domain - Manage Claude Code settings and plugins\n */\nimport type { Command } from \"commander\";\nimport { initCommand } from \"../../commands/claude/init.js\";\nimport { consolidateCommand } from \"../../commands/claude/settings/consolidate.js\";\nimport type { Domain } from \"../types.js\";\n\n/**\n * Register claude domain commands\n *\n * @param claudeCmd - Commander.js claude domain command\n */\nfunction registerClaudeCommands(claudeCmd: Command): void {\n // init command\n claudeCmd\n .command(\"init\")\n .description(\"Initialize or update spx-claude marketplace plugin\")\n .action(async () => {\n try {\n const output = await initCommand({ cwd: process.cwd() });\n console.log(output);\n } catch (error) {\n console.error(\n \"Error:\",\n error instanceof Error ? error.message : String(error),\n );\n process.exit(1);\n }\n });\n\n // settings subcommand group\n const settingsCmd = claudeCmd\n .command(\"settings\")\n .description(\"Manage Claude Code settings\");\n\n // settings consolidate command\n settingsCmd\n .command(\"consolidate\")\n .description(\n \"Consolidate permissions from project-specific settings into global settings\",\n )\n .option(\"--write\", \"Write changes to global settings file (default: preview only)\")\n .option(\n \"--output-file <path>\",\n \"Write merged settings to specified file instead of global settings\",\n )\n .option(\n \"--root <path>\",\n \"Root directory to scan for settings files (default: ~/Code)\",\n )\n .option(\n \"--global-settings <path>\",\n \"Path to global settings file (default: ~/.claude/settings.json)\",\n )\n .action(\n async (options: {\n write?: boolean;\n outputFile?: string;\n root?: string;\n globalSettings?: string;\n }) => {\n try {\n // Validate mutually exclusive options\n if (options.write && options.outputFile) {\n console.error(\n \"Error: --write and --output-file are mutually exclusive\\n\"\n + \"Use --write to modify global settings, or --output-file to write to a different location\",\n );\n process.exit(1);\n }\n\n const output = await consolidateCommand({\n write: options.write,\n outputFile: options.outputFile,\n root: options.root,\n globalSettings: options.globalSettings,\n });\n console.log(output);\n } catch (error) {\n console.error(\n \"Error:\",\n error instanceof Error ? error.message : String(error),\n );\n process.exit(1);\n }\n },\n );\n}\n\n/**\n * Claude domain - Manage Claude Code settings and plugins\n */\nexport const claudeDomain: Domain = {\n name: \"claude\",\n description: \"Manage Claude Code settings and plugins\",\n register: (program: Command) => {\n const claudeCmd = program\n .command(\"claude\")\n .description(\"Manage Claude Code settings and plugins\");\n\n registerClaudeCommands(claudeCmd);\n },\n};\n","/**\n * Session archive CLI command handler.\n *\n * @module commands/session/archive\n */\n\nimport { mkdir, rename, stat } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\n\nimport { SessionNotFoundError } from \"../../session/errors.js\";\nimport { DEFAULT_SESSION_CONFIG, type SessionDirectoryConfig } from \"../../session/show.js\";\n\n/**\n * Options for the archive command.\n */\nexport interface ArchiveOptions {\n /** Session ID to archive */\n sessionId: string;\n /** Custom sessions directory */\n sessionsDir?: string;\n}\n\n/**\n * Error thrown when a session is already archived.\n */\nexport class SessionAlreadyArchivedError extends Error {\n /** The session ID that is already archived */\n readonly sessionId: string;\n\n constructor(sessionId: string) {\n super(`Session already archived: ${sessionId}.`);\n this.name = \"SessionAlreadyArchivedError\";\n this.sessionId = sessionId;\n }\n}\n\n/**\n * Finds the source path for a session to archive.\n *\n * @param sessionId - Session ID to find\n * @param config - Directory configuration\n * @returns Source path and target path for archiving\n * @throws {SessionNotFoundError} When session is not found in todo or doing\n * @throws {SessionAlreadyArchivedError} When session is already in archive\n */\nexport async function resolveArchivePaths(\n sessionId: string,\n config: SessionDirectoryConfig,\n): Promise<{ source: string; target: string }> {\n const filename = `${sessionId}.md`;\n const todoPath = join(config.todoDir, filename);\n const doingPath = join(config.doingDir, filename);\n const archivePath = join(config.archiveDir, filename);\n\n // Check if already archived\n try {\n const archiveStats = await stat(archivePath);\n if (archiveStats.isFile()) {\n throw new SessionAlreadyArchivedError(sessionId);\n }\n } catch (error) {\n // ENOENT is expected - session not in archive\n if (error instanceof Error && \"code\" in error && error.code !== \"ENOENT\") {\n throw error;\n }\n // Rethrow SessionAlreadyArchivedError\n if (error instanceof SessionAlreadyArchivedError) {\n throw error;\n }\n }\n\n // Check todo directory first\n try {\n const todoStats = await stat(todoPath);\n if (todoStats.isFile()) {\n return { source: todoPath, target: archivePath };\n }\n } catch {\n // File not in todo, continue to check doing\n }\n\n // Check doing directory\n try {\n const doingStats = await stat(doingPath);\n if (doingStats.isFile()) {\n return { source: doingPath, target: archivePath };\n }\n } catch {\n // File not in doing either\n }\n\n // Session not found in either directory\n throw new SessionNotFoundError(sessionId);\n}\n\n/**\n * Executes the archive command.\n *\n * @param options - Command options\n * @returns Formatted output for display\n * @throws {SessionNotFoundError} When session not found\n * @throws {SessionAlreadyArchivedError} When session is already archived\n */\nexport async function archiveCommand(options: ArchiveOptions): Promise<string> {\n // Build config from options\n const config: SessionDirectoryConfig = options.sessionsDir\n ? {\n todoDir: join(options.sessionsDir, \"todo\"),\n doingDir: join(options.sessionsDir, \"doing\"),\n archiveDir: join(options.sessionsDir, \"archive\"),\n }\n : DEFAULT_SESSION_CONFIG;\n\n // Resolve source and target paths\n const { source, target } = await resolveArchivePaths(options.sessionId, config);\n\n // Ensure archive directory exists (FR2: create if missing)\n await mkdir(dirname(target), { recursive: true });\n\n // Move to archive\n await rename(source, target);\n\n return `Archived session: ${options.sessionId}\\nArchive location: ${target}`;\n}\n","/**\n * Session-specific error types.\n *\n * @module session/errors\n */\n\n/**\n * Base class for session errors.\n */\nexport class SessionError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"SessionError\";\n }\n}\n\n/**\n * Error thrown when a session cannot be found.\n */\nexport class SessionNotFoundError extends SessionError {\n /** The session ID that was not found */\n readonly sessionId: string;\n\n constructor(sessionId: string) {\n super(`Session not found: ${sessionId}. Check the session ID and try again.`);\n this.name = \"SessionNotFoundError\";\n this.sessionId = sessionId;\n }\n}\n\n/**\n * Error thrown when a session is not available for claiming (already claimed).\n */\nexport class SessionNotAvailableError extends SessionError {\n /** The session ID that is not available */\n readonly sessionId: string;\n\n constructor(sessionId: string) {\n super(`Session not available: ${sessionId}. It may have been claimed by another agent.`);\n this.name = \"SessionNotAvailableError\";\n this.sessionId = sessionId;\n }\n}\n\n/**\n * Error thrown when session content is invalid.\n */\nexport class SessionInvalidContentError extends SessionError {\n constructor(reason: string) {\n super(`Invalid session content: ${reason}`);\n this.name = \"SessionInvalidContentError\";\n }\n}\n\n/**\n * Error thrown when trying to release a session that is not currently claimed.\n */\nexport class SessionNotClaimedError extends SessionError {\n /** The session ID that is not claimed */\n readonly sessionId: string;\n\n constructor(sessionId: string) {\n super(`Session not claimed: ${sessionId}. The session is not in the doing directory.`);\n this.name = \"SessionNotClaimedError\";\n this.sessionId = sessionId;\n }\n}\n\n/**\n * Error thrown when no sessions are available for auto-pickup.\n */\nexport class NoSessionsAvailableError extends SessionError {\n constructor() {\n super(\"No sessions available. The todo directory is empty.\");\n this.name = \"NoSessionsAvailableError\";\n }\n}\n","/**\n * Session display utilities for showing session content without claiming.\n *\n * @module session/show\n */\n\nimport { join } from \"node:path\";\n\nimport { DEFAULT_CONFIG } from \"../config/defaults.js\";\nimport { parseSessionMetadata } from \"./list.js\";\nimport type { SessionStatus } from \"./types.js\";\n\n/**\n * Configuration for session directory paths.\n */\nexport interface SessionDirectoryConfig {\n /** Path to todo directory */\n todoDir: string;\n /** Path to doing directory */\n doingDir: string;\n /** Path to archive directory */\n archiveDir: string;\n}\n\n/**\n * Default session directory configuration.\n *\n * Derived from DEFAULT_CONFIG to ensure single source of truth for all path components.\n * NEVER hardcode path strings like \".spx\", \"sessions\", \"todo\" - always derive from config.\n */\nconst { dir: sessionsBaseDir, statusDirs } = DEFAULT_CONFIG.sessions;\n\nexport const DEFAULT_SESSION_CONFIG: SessionDirectoryConfig = {\n todoDir: join(sessionsBaseDir, statusDirs.todo),\n doingDir: join(sessionsBaseDir, statusDirs.doing),\n archiveDir: join(sessionsBaseDir, statusDirs.archive),\n};\n\n/**\n * Order to search directories (matches priority: todo first, then doing, then archive).\n */\nexport const SEARCH_ORDER: SessionStatus[] = [\"todo\", \"doing\", \"archive\"];\n\n/**\n * Options for formatting show output.\n */\nexport interface ShowOutputOptions {\n /** Current status of the session */\n status: SessionStatus;\n}\n\n/**\n * Resolves possible file paths for a session ID across all status directories.\n *\n * @param id - Session ID (timestamp format)\n * @param config - Directory configuration\n * @returns Array of possible file paths in search order\n *\n * @example\n * ```typescript\n * const paths = resolveSessionPaths('2026-01-13_08-01-05', {\n * todoDir: '.spx/sessions/todo',\n * doingDir: '.spx/sessions/doing',\n * archiveDir: '.spx/sessions/archive',\n * });\n * // => [\n * // '.spx/sessions/todo/2026-01-13_08-01-05.md',\n * // '.spx/sessions/doing/2026-01-13_08-01-05.md',\n * // '.spx/sessions/archive/2026-01-13_08-01-05.md',\n * // ]\n * ```\n */\nexport function resolveSessionPaths(\n id: string,\n config: SessionDirectoryConfig = DEFAULT_SESSION_CONFIG,\n): string[] {\n const filename = `${id}.md`;\n\n return [\n `${config.todoDir}/${filename}`,\n `${config.doingDir}/${filename}`,\n `${config.archiveDir}/${filename}`,\n ];\n}\n\n/**\n * Formats session content for display with metadata header.\n *\n * @param content - Raw session file content\n * @param options - Display options including status\n * @returns Formatted output string with metadata header\n *\n * @example\n * ```typescript\n * const output = formatShowOutput(sessionContent, { status: 'todo' });\n * // => \"Status: todo\\nPriority: high\\n---\\n# Session Content...\"\n * ```\n */\nexport function formatShowOutput(\n content: string,\n options: ShowOutputOptions,\n): string {\n const metadata = parseSessionMetadata(content);\n\n // Build header with extracted metadata\n const headerLines: string[] = [\n `Status: ${options.status}`,\n `Priority: ${metadata.priority}`,\n ];\n\n // Add optional metadata if present\n if (metadata.id) {\n headerLines.unshift(`ID: ${metadata.id}`);\n }\n if (metadata.branch) {\n headerLines.push(`Branch: ${metadata.branch}`);\n }\n if (metadata.tags.length > 0) {\n headerLines.push(`Tags: ${metadata.tags.join(\", \")}`);\n }\n if (metadata.createdAt) {\n headerLines.push(`Created: ${metadata.createdAt}`);\n }\n\n // Combine header with separator and original content\n const header = headerLines.join(\"\\n\");\n const separator = \"\\n\" + \"─\".repeat(40) + \"\\n\\n\";\n\n return header + separator + content;\n}\n","/**\n * Default configuration for spx CLI\n *\n * This module defines the default directory structure and configuration\n * constants used throughout the spx CLI. All directory paths should reference\n * this configuration instead of using hardcoded strings.\n *\n * @module config/defaults\n */\n\n/**\n * Configuration schema for spx CLI directory structure\n */\nexport interface SpxConfig {\n /**\n * Specifications directory configuration\n */\n specs: {\n /**\n * Base directory for all specification files\n * @default \"specs\"\n */\n root: string;\n\n /**\n * Work items organization\n */\n work: {\n /**\n * Container directory for all work items\n * @default \"work\"\n */\n dir: string;\n\n /**\n * Status-based subdirectories for work items\n */\n statusDirs: {\n /**\n * Active work directory\n * @default \"doing\"\n */\n doing: string;\n\n /**\n * Future work directory\n * @default \"backlog\"\n */\n backlog: string;\n\n /**\n * Completed work directory\n * @default \"archive\"\n */\n done: string;\n };\n };\n\n /**\n * Product-level architecture decision records directory\n * @default \"decisions\"\n */\n decisions: string;\n\n /**\n * Templates directory (optional)\n * @default \"templates\"\n */\n templates?: string;\n };\n\n /**\n * Session handoff files configuration\n */\n sessions: {\n /**\n * Directory for session handoff files\n * @default \".spx/sessions\"\n */\n dir: string;\n\n /**\n * Status-based subdirectories for sessions\n */\n statusDirs: {\n /**\n * Available sessions directory\n * @default \"todo\"\n */\n todo: string;\n\n /**\n * Claimed sessions directory\n * @default \"doing\"\n */\n doing: string;\n\n /**\n * Archived sessions directory\n * @default \"archive\"\n */\n archive: string;\n };\n };\n}\n\n/**\n * Default configuration constant\n *\n * This is the embedded default configuration that spx uses when no\n * .spx/config.json file exists in the product.\n *\n * DO NOT modify this constant at runtime - it should remain immutable.\n */\nexport const DEFAULT_CONFIG = {\n specs: {\n root: \"specs\",\n work: {\n dir: \"work\",\n statusDirs: {\n doing: \"doing\",\n backlog: \"backlog\",\n done: \"archive\",\n },\n },\n decisions: \"decisions\",\n templates: \"templates\",\n },\n sessions: {\n dir: \".spx/sessions\",\n statusDirs: {\n todo: \"todo\",\n doing: \"doing\",\n archive: \"archive\",\n },\n },\n} as const satisfies SpxConfig;\n","/**\n * Session listing and sorting utilities.\n *\n * @module session/list\n */\n\nimport { parse as parseYaml } from \"yaml\";\n\nimport { parseSessionId } from \"./timestamp\";\nimport { DEFAULT_PRIORITY, PRIORITY_ORDER, type Session, type SessionMetadata, type SessionPriority } from \"./types\";\n\n/**\n * Regular expression to match YAML front matter.\n * Matches content between opening `---` and closing `---` or `...`\n */\nconst FRONT_MATTER_PATTERN = /^---\\r?\\n([\\s\\S]*?)\\r?\\n(?:---|\\.\\.\\.)\\r?\\n?/;\n\n/**\n * Validates if a value is a valid priority.\n */\nfunction isValidPriority(value: unknown): value is SessionPriority {\n return value === \"high\" || value === \"medium\" || value === \"low\";\n}\n\n/**\n * Parses YAML front matter from session content to extract metadata.\n *\n * @param content - Full session file content\n * @returns Extracted metadata with defaults for missing fields\n *\n * @example\n * ```typescript\n * const metadata = parseSessionMetadata(`---\n * priority: high\n * tags: [bug, urgent]\n * ---\n * # Session content`);\n * // => { priority: 'high', tags: ['bug', 'urgent'] }\n * ```\n */\nexport function parseSessionMetadata(content: string): SessionMetadata {\n const match = FRONT_MATTER_PATTERN.exec(content);\n\n if (!match) {\n return {\n priority: DEFAULT_PRIORITY,\n tags: [],\n };\n }\n\n try {\n const parsed = parseYaml(match[1]) as Record<string, unknown>;\n\n if (!parsed || typeof parsed !== \"object\") {\n return {\n priority: DEFAULT_PRIORITY,\n tags: [],\n };\n }\n\n // Extract priority with validation\n const priority = isValidPriority(parsed.priority)\n ? parsed.priority\n : DEFAULT_PRIORITY;\n\n // Extract tags, ensuring it's an array of strings\n let tags: string[] = [];\n if (Array.isArray(parsed.tags)) {\n tags = parsed.tags.filter((t): t is string => typeof t === \"string\");\n }\n\n // Build metadata object\n const metadata: SessionMetadata = {\n priority,\n tags,\n };\n\n // Add optional fields if present\n if (typeof parsed.id === \"string\") {\n metadata.id = parsed.id;\n }\n if (typeof parsed.branch === \"string\") {\n metadata.branch = parsed.branch;\n }\n if (typeof parsed.created_at === \"string\") {\n metadata.createdAt = parsed.created_at;\n }\n if (typeof parsed.working_directory === \"string\") {\n metadata.workingDirectory = parsed.working_directory;\n }\n if (Array.isArray(parsed.specs)) {\n metadata.specs = parsed.specs.filter(\n (s): s is string => typeof s === \"string\",\n );\n }\n if (Array.isArray(parsed.files)) {\n metadata.files = parsed.files.filter(\n (f): f is string => typeof f === \"string\",\n );\n }\n\n return metadata;\n } catch {\n // Malformed YAML, return defaults\n return {\n priority: DEFAULT_PRIORITY,\n tags: [],\n };\n }\n}\n\n/**\n * Sorts sessions by priority (high first) then by timestamp (newest first).\n *\n * @param sessions - Array of sessions to sort\n * @returns New sorted array (does not mutate input)\n *\n * @example\n * ```typescript\n * const sorted = sortSessions([\n * { id: 'a', metadata: { priority: 'low' } },\n * { id: 'b', metadata: { priority: 'high' } },\n * ]);\n * // => [{ id: 'b', ... }, { id: 'a', ... }]\n * ```\n */\nexport function sortSessions(sessions: Session[]): Session[] {\n return [...sessions].sort((a, b) => {\n // First: sort by priority (high = 0, medium = 1, low = 2)\n const priorityA = PRIORITY_ORDER[a.metadata.priority];\n const priorityB = PRIORITY_ORDER[b.metadata.priority];\n\n if (priorityA !== priorityB) {\n return priorityA - priorityB;\n }\n\n // Second: sort by timestamp (newest first = descending)\n const dateA = parseSessionId(a.id);\n const dateB = parseSessionId(b.id);\n\n // Handle invalid session IDs by treating them as oldest\n if (!dateA && !dateB) return 0;\n if (!dateA) return 1; // a goes after b\n if (!dateB) return -1; // b goes after a\n\n return dateB.getTime() - dateA.getTime();\n });\n}\n","/**\n * Session timestamp utilities for generating and parsing session IDs.\n *\n * Session IDs use the format YYYY-MM-DD_HH-mm-ss as specified in\n * ADR-32 (Timestamp Format).\n *\n * @module session/timestamp\n */\n\n/**\n * Regular expression pattern for validating session IDs.\n * Format: YYYY-MM-DD_HH-mm-ss (all components zero-padded)\n *\n * Exported for use in validation and testing.\n */\nexport const SESSION_ID_PATTERN = /^(\\d{4})-(\\d{2})-(\\d{2})_(\\d{2})-(\\d{2})-(\\d{2})$/;\n\n/**\n * Separator between date and time components in session IDs.\n */\nexport const SESSION_ID_SEPARATOR = \"_\";\n\n/**\n * Options for generating session IDs.\n */\nexport interface GenerateSessionIdOptions {\n /**\n * Function that returns the current time.\n * Defaults to `() => new Date()` for production use.\n * Injectable for deterministic testing.\n */\n now?: () => Date;\n}\n\n/**\n * Generates a session ID from the current (or injected) time.\n *\n * @param options - Optional configuration including time source\n * @returns Session ID in format YYYY-MM-DD_HH-mm-ss\n *\n * @example\n * ```typescript\n * // Production usage\n * const id = generateSessionId();\n * // => \"2026-01-13_08-01-05\"\n *\n * // Testing with injected time\n * const id = generateSessionId({ now: () => new Date('2026-01-13T08:01:05') });\n * // => \"2026-01-13_08-01-05\"\n * ```\n */\nexport function generateSessionId(options: GenerateSessionIdOptions = {}): string {\n const now = options.now ?? (() => new Date());\n const date = now();\n\n const year = date.getFullYear();\n const month = String(date.getMonth() + 1).padStart(2, \"0\");\n const day = String(date.getDate()).padStart(2, \"0\");\n const hours = String(date.getHours()).padStart(2, \"0\");\n const minutes = String(date.getMinutes()).padStart(2, \"0\");\n const seconds = String(date.getSeconds()).padStart(2, \"0\");\n\n return `${year}-${month}-${day}${SESSION_ID_SEPARATOR}${hours}-${minutes}-${seconds}`;\n}\n\n/**\n * Parses a session ID string back into a Date object.\n *\n * @param id - Session ID string to parse\n * @returns Date object if valid, null if invalid format\n *\n * @example\n * ```typescript\n * const date = parseSessionId('2026-01-13_08-01-05');\n * // => Date representing 2026-01-13T08:01:05 (local time)\n *\n * const invalid = parseSessionId('invalid-format');\n * // => null\n * ```\n */\nexport function parseSessionId(id: string): Date | null {\n const match = SESSION_ID_PATTERN.exec(id);\n\n if (!match) {\n return null;\n }\n\n const [, yearStr, monthStr, dayStr, hoursStr, minutesStr, secondsStr] = match;\n\n const year = parseInt(yearStr, 10);\n const month = parseInt(monthStr, 10) - 1; // Date months are 0-indexed\n const day = parseInt(dayStr, 10);\n const hours = parseInt(hoursStr, 10);\n const minutes = parseInt(minutesStr, 10);\n const seconds = parseInt(secondsStr, 10);\n\n // Validate component ranges\n if (month < 0 || month > 11) return null;\n if (day < 1 || day > 31) return null;\n if (hours < 0 || hours > 23) return null;\n if (minutes < 0 || minutes > 59) return null;\n if (seconds < 0 || seconds > 59) return null;\n\n return new Date(year, month, day, hours, minutes, seconds);\n}\n","/**\n * Session type definitions for the session management domain.\n *\n * @module session/types\n */\n\n/**\n * Priority levels for session ordering.\n * Sessions are sorted: high → medium → low\n */\nexport type SessionPriority = \"high\" | \"medium\" | \"low\";\n\n/**\n * Status derived from directory location per ADR-21.\n */\nexport type SessionStatus = \"todo\" | \"doing\" | \"archive\";\n\n/**\n * Priority sort order (lower number = higher priority).\n */\nexport const PRIORITY_ORDER: Record<SessionPriority, number> = {\n high: 0,\n medium: 1,\n low: 2,\n} as const;\n\n/**\n * Default priority when not specified in YAML front matter.\n */\nexport const DEFAULT_PRIORITY: SessionPriority = \"medium\";\n\n/**\n * Metadata extracted from session YAML front matter.\n */\nexport interface SessionMetadata {\n /** Session ID (from filename or YAML) */\n id?: string;\n /** Priority level for sorting */\n priority: SessionPriority;\n /** Free-form tags for filtering */\n tags: string[];\n /** Git branch associated with session */\n branch?: string;\n /** Spec files to auto-inject on pickup */\n specs?: string[];\n /** Code files to auto-inject on pickup */\n files?: string[];\n /** ISO 8601 timestamp when session was created */\n createdAt?: string;\n /** Working directory path */\n workingDirectory?: string;\n}\n\n/**\n * Complete session information including status and metadata.\n */\nexport interface Session {\n /** Session ID (timestamp format: YYYY-MM-DD_HH-mm-ss) */\n id: string;\n /** Status derived from directory location */\n status: SessionStatus;\n /** Metadata from YAML front matter */\n metadata: SessionMetadata;\n /** Full path to session file */\n path: string;\n}\n","/**\n * Session delete CLI command handler.\n *\n * @module commands/session/delete\n */\n\nimport { stat, unlink } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { resolveDeletePath } from \"../../session/delete.js\";\nimport { DEFAULT_SESSION_CONFIG, resolveSessionPaths, type SessionDirectoryConfig } from \"../../session/show.js\";\n\n/**\n * Options for the delete command.\n */\nexport interface DeleteOptions {\n /** Session ID to delete */\n sessionId: string;\n /** Custom sessions directory */\n sessionsDir?: string;\n}\n\n/**\n * Checks which paths exist.\n */\nasync function findExistingPaths(paths: string[]): Promise<string[]> {\n const existing: string[] = [];\n\n for (const path of paths) {\n try {\n const stats = await stat(path);\n if (stats.isFile()) {\n existing.push(path);\n }\n } catch {\n // File doesn't exist, skip\n }\n }\n\n return existing;\n}\n\n/**\n * Executes the delete command.\n *\n * @param options - Command options\n * @returns Formatted output for display\n * @throws {SessionNotFoundError} When session not found\n */\nexport async function deleteCommand(options: DeleteOptions): Promise<string> {\n // Build config from options\n const config: SessionDirectoryConfig = options.sessionsDir\n ? {\n todoDir: join(options.sessionsDir, \"todo\"),\n doingDir: join(options.sessionsDir, \"doing\"),\n archiveDir: join(options.sessionsDir, \"archive\"),\n }\n : DEFAULT_SESSION_CONFIG;\n\n // Resolve possible paths\n const paths = resolveSessionPaths(options.sessionId, config);\n\n // Find existing paths\n const existingPaths = await findExistingPaths(paths);\n\n // Resolve the path to delete\n const pathToDelete = resolveDeletePath(options.sessionId, existingPaths);\n\n // Delete the file\n await unlink(pathToDelete);\n\n return `Deleted session: ${options.sessionId}`;\n}\n","/**\n * Session deletion utilities.\n *\n * @module session/delete\n */\n\nimport { SessionNotFoundError } from \"./errors\";\n\n/**\n * Resolves the path to delete for a session ID.\n *\n * Given a session ID and a list of existing paths (checked by caller),\n * returns the first path that contains the session ID.\n *\n * @param sessionId - Session ID to find\n * @param existingPaths - Paths that were found to exist\n * @returns The path to the session file\n * @throws {SessionNotFoundError} When session is not found in any directory\n *\n * @example\n * ```typescript\n * // Caller checks which paths exist, passes only existing ones\n * const existingPaths = ['.spx/sessions/doing/2026-01-13_08-01-05.md'];\n * const path = resolveDeletePath('2026-01-13_08-01-05', existingPaths);\n * // => '.spx/sessions/doing/2026-01-13_08-01-05.md'\n * ```\n */\nexport function resolveDeletePath(\n sessionId: string,\n existingPaths: string[],\n): string {\n // Find the first existing path that matches the session ID\n const matchingPath = existingPaths.find((path) => path.includes(sessionId));\n\n if (!matchingPath) {\n throw new SessionNotFoundError(sessionId);\n }\n\n return matchingPath;\n}\n","/**\n * Session handoff CLI command handler.\n *\n * Creates a new session for handoff to another agent context.\n * Metadata (priority, tags) should be included in the content as YAML frontmatter.\n *\n * @module commands/session/handoff\n */\n\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport { join, resolve } from \"node:path\";\n\nimport { DEFAULT_CONFIG } from \"../../config/defaults.js\";\nimport { buildSessionPathFromRoot, detectGitRoot } from \"../../git/root.js\";\nimport { validateSessionContent } from \"../../session/create.js\";\nimport { SessionInvalidContentError } from \"../../session/errors.js\";\nimport { DEFAULT_SESSION_CONFIG, type SessionDirectoryConfig } from \"../../session/show.js\";\nimport { generateSessionId } from \"../../session/timestamp.js\";\n\nconst { statusDirs } = DEFAULT_CONFIG.sessions;\n\n/**\n * Regex to detect YAML frontmatter presence.\n * Matches opening `---` at start of content.\n */\nconst FRONT_MATTER_START = /^---\\r?\\n/;\n\n/**\n * Options for the handoff command.\n */\nexport interface HandoffOptions {\n /** Session content (from stdin). Should include YAML frontmatter with priority/tags. */\n content?: string;\n /** Custom sessions directory */\n sessionsDir?: string;\n}\n\n/**\n * Checks if content has YAML frontmatter.\n *\n * @param content - Raw session content\n * @returns True if content starts with frontmatter delimiter\n */\nexport function hasFrontmatter(content: string): boolean {\n return FRONT_MATTER_START.test(content);\n}\n\n/**\n * Builds session content, adding default frontmatter only if not present.\n *\n * If content already has frontmatter, returns as-is (preserves agent-provided metadata).\n * If content lacks frontmatter, adds default frontmatter with medium priority.\n *\n * @param content - Raw content from stdin\n * @returns Content ready to be written to session file\n */\nexport function buildSessionContent(content: string | undefined): string {\n // Default content if none provided\n if (!content || content.trim().length === 0) {\n return `---\npriority: medium\n---\n\n# New Session\n\nDescribe your task here.`;\n }\n\n // If content already has frontmatter, preserve it as-is\n if (hasFrontmatter(content)) {\n return content;\n }\n\n // Add default frontmatter to content without it\n return `---\npriority: medium\n---\n\n${content}`;\n}\n\n/**\n * Executes the handoff command.\n *\n * Creates a new session in the todo directory for pickup by another context.\n * Output includes `<HANDOFF_ID>` tag for easy parsing by automation tools.\n *\n * When no --sessions-dir is provided, sessions are created at the git repository root\n * (if in a git repo) to ensure consistent location across subdirectories.\n *\n * Metadata (priority, tags) should be included in the content as YAML frontmatter:\n * ```\n * ---\n * priority: high\n * tags: [feature, api]\n * ---\n * # Session content...\n * ```\n *\n * @param options - Command options\n * @returns Formatted output for display with parseable session ID\n * @throws {SessionInvalidContentError} When content validation fails\n */\nexport async function handoffCommand(options: HandoffOptions): Promise<string> {\n // Build config from options\n const config: SessionDirectoryConfig = options.sessionsDir\n ? {\n todoDir: join(options.sessionsDir, statusDirs.todo),\n doingDir: join(options.sessionsDir, statusDirs.doing),\n archiveDir: join(options.sessionsDir, statusDirs.archive),\n }\n : DEFAULT_SESSION_CONFIG;\n\n // Detect git root when no explicit sessions directory provided\n let baseDir: string;\n let warningMessage: string | undefined;\n\n if (options.sessionsDir) {\n // Explicit directory provided - use as-is\n baseDir = options.sessionsDir;\n } else {\n // No explicit directory - detect git root\n const gitResult = await detectGitRoot();\n baseDir = gitResult.root;\n warningMessage = gitResult.warning;\n }\n\n // Generate session ID\n const sessionId = generateSessionId();\n\n // Build content - preserves existing frontmatter or adds defaults\n const fullContent = buildSessionContent(options.content);\n\n // Validate content\n const validation = validateSessionContent(fullContent);\n if (!validation.valid) {\n throw new SessionInvalidContentError(validation.error ?? \"Unknown validation error\");\n }\n\n // Build path to session file\n const filename = `${sessionId}.md`;\n const sessionPath = options.sessionsDir\n ? join(config.todoDir, filename)\n : buildSessionPathFromRoot(baseDir, sessionId, config);\n const absolutePath = resolve(sessionPath);\n\n // Ensure directory exists\n const todoDir = options.sessionsDir\n ? config.todoDir\n : join(baseDir, config.todoDir);\n await mkdir(todoDir, { recursive: true });\n\n // Write file\n await writeFile(sessionPath, fullContent, \"utf-8\");\n\n // Build output message\n let output =\n `Created handoff session <HANDOFF_ID>${sessionId}</HANDOFF_ID>\\n<SESSION_FILE>${absolutePath}</SESSION_FILE>`;\n\n // Emit warning to stderr if not in git repo\n if (warningMessage) {\n // Write warning to stderr (not included in returned string)\n process.stderr.write(`${warningMessage}\\n`);\n }\n\n return output;\n}\n","/**\n * Git repository root detection utilities.\n *\n * Provides git root detection with dependency injection for testability.\n * Sessions should be created at the git repository root, not relative to cwd.\n *\n * @module git/root\n */\n\nimport { execa, type ResultPromise } from \"execa\";\nimport { join } from \"node:path\";\n\nimport type { SessionDirectoryConfig } from \"../session/show.js\";\n\n/**\n * Result from git root detection.\n */\nexport interface GitRootResult {\n /** Absolute path to git root (or cwd if not in git repo) */\n root: string;\n /** Whether the directory is inside a git repository */\n isGitRepo: boolean;\n /** Warning message when not in a git repo (undefined if in repo) */\n warning?: string;\n}\n\n/**\n * Dependencies for git operations (injectable for testing).\n */\nexport interface GitDependencies {\n /**\n * Execute a command (typically execa).\n *\n * @param command - Command to execute\n * @param args - Command arguments\n * @param options - Execution options\n * @returns Result promise with stdout/stderr\n */\n execa: (\n command: string,\n args: string[],\n options?: { cwd?: string; reject?: boolean },\n ) => ResultPromise;\n}\n\n/**\n * Default dependencies using real execa.\n */\nconst defaultDeps: GitDependencies = {\n execa: (command, args, options) => execa(command, args, options),\n};\n\n/**\n * Warning message emitted when not in a git repository.\n */\nconst NOT_GIT_REPO_WARNING =\n \"Warning: Not in a git repository. Sessions will be created relative to current directory.\";\n\n/**\n * Detects the git repository root directory.\n *\n * Uses `git rev-parse --show-toplevel` to find the repository root.\n * If not in a git repository, returns the current working directory with a warning.\n *\n * @param cwd - Current working directory (defaults to process.cwd())\n * @param deps - Injectable dependencies for testing\n * @returns GitRootResult with root path, git status, and optional warning\n *\n * @example\n * ```typescript\n * // In a git repo subdirectory\n * const result = await detectGitRoot('/repo/src/components');\n * // => { root: '/repo', isGitRepo: true }\n *\n * // Not in a git repo\n * const result = await detectGitRoot('/tmp/random');\n * // => { root: '/tmp/random', isGitRepo: false, warning: '...' }\n * ```\n */\nexport async function detectGitRoot(\n cwd: string = process.cwd(),\n deps: GitDependencies = defaultDeps,\n): Promise<GitRootResult> {\n try {\n const result = await deps.execa(\n \"git\",\n [\"rev-parse\", \"--show-toplevel\"],\n { cwd, reject: false },\n );\n\n // Git command succeeded - we're in a repo\n if (result.exitCode === 0 && result.stdout) {\n // Trim whitespace and normalize path (remove trailing slashes)\n const stdout = typeof result.stdout === \"string\" ? result.stdout : result.stdout.toString();\n const gitRoot = stdout.trim().replace(/\\/+$/, \"\");\n\n return {\n root: gitRoot,\n isGitRepo: true,\n };\n }\n\n // Git command failed - not in a repo\n return {\n root: cwd,\n isGitRepo: false,\n warning: NOT_GIT_REPO_WARNING,\n };\n } catch {\n // Command execution failed (git not installed, permission error, etc.)\n return {\n root: cwd,\n isGitRepo: false,\n warning: NOT_GIT_REPO_WARNING,\n };\n }\n}\n\n/**\n * Builds an absolute session file path from git root and session ID.\n *\n * Pure function that constructs the path without I/O.\n * All path components come from the config parameter (single source of truth).\n *\n * @param gitRoot - Absolute path to git repository root\n * @param sessionId - Session timestamp ID (e.g., \"2026-01-13_08-01-05\")\n * @param config - Session directory configuration\n * @returns Absolute path to session file in todo directory\n *\n * @example\n * ```typescript\n * const path = buildSessionPathFromRoot(\n * '/Users/dev/myproject',\n * '2026-01-13_08-01-05',\n * DEFAULT_SESSION_CONFIG,\n * );\n * // => '/Users/dev/myproject/.spx/sessions/todo/2026-01-13_08-01-05.md'\n * ```\n */\nexport function buildSessionPathFromRoot(\n gitRoot: string,\n sessionId: string,\n config: SessionDirectoryConfig,\n): string {\n const filename = `${sessionId}.md`;\n\n // Build absolute path: git root + todo dir + filename\n // All components come from config (no hardcoded strings)\n return join(gitRoot, config.todoDir, filename);\n}\n","/**\n * Session creation utilities.\n *\n * @module session/create\n */\n\nimport type { SessionDirectoryConfig } from \"./show\";\n\n/**\n * Minimum content length for a valid session.\n */\nexport const MIN_CONTENT_LENGTH = 1;\n\n/**\n * Configuration subset needed for session creation.\n */\nexport type CreateSessionConfig = Pick<SessionDirectoryConfig, \"todoDir\">;\n\n/**\n * Result of session content validation.\n */\nexport interface ValidationResult {\n /** Whether the content is valid */\n valid: boolean;\n /** Error message if invalid */\n error?: string;\n}\n\n/**\n * Builds the full file path for a new session in the todo directory.\n *\n * @param sessionId - Session ID (timestamp format)\n * @param config - Directory configuration with todoDir\n * @returns Full path to session file\n *\n * @example\n * ```typescript\n * const path = buildSessionPath('2026-01-13_08-01-05', { todoDir: '.spx/sessions/todo' });\n * // => '.spx/sessions/todo/2026-01-13_08-01-05.md'\n * ```\n */\nexport function buildSessionPath(\n sessionId: string,\n config: CreateSessionConfig,\n): string {\n return `${config.todoDir}/${sessionId}.md`;\n}\n\n/**\n * Validates session content before creation.\n *\n * @param content - Raw session content\n * @returns Validation result with valid flag and optional error\n *\n * @example\n * ```typescript\n * const result = validateSessionContent('# My Session');\n * // => { valid: true }\n *\n * const result = validateSessionContent('');\n * // => { valid: false, error: 'Session content cannot be empty' }\n * ```\n */\nexport function validateSessionContent(content: string): ValidationResult {\n // Check for empty content\n if (!content || content.trim().length < MIN_CONTENT_LENGTH) {\n return {\n valid: false,\n error: \"Session content cannot be empty\",\n };\n }\n\n // Check for only whitespace\n if (content.trim().length === 0) {\n return {\n valid: false,\n error: \"Session content cannot be only whitespace\",\n };\n }\n\n return { valid: true };\n}\n","/**\n * Session list CLI command handler.\n *\n * @module commands/session/list\n */\n\nimport { readdir, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { parseSessionMetadata, sortSessions } from \"../../session/list.js\";\nimport { DEFAULT_SESSION_CONFIG, type SessionDirectoryConfig } from \"../../session/show.js\";\nimport type { Session, SessionStatus } from \"../../session/types.js\";\n\n/**\n * Options for the list command.\n */\nexport interface ListOptions {\n /** Filter by status */\n status?: SessionStatus;\n /** Custom sessions directory */\n sessionsDir?: string;\n /** Output format */\n format?: \"text\" | \"json\";\n}\n\n/**\n * Loads sessions from a specific directory.\n */\nasync function loadSessionsFromDir(\n dir: string,\n status: SessionStatus,\n): Promise<Session[]> {\n try {\n const files = await readdir(dir);\n const sessions: Session[] = [];\n\n for (const file of files) {\n if (!file.endsWith(\".md\")) continue;\n\n const id = file.replace(\".md\", \"\");\n const filePath = join(dir, file);\n const content = await readFile(filePath, \"utf-8\");\n const metadata = parseSessionMetadata(content);\n\n sessions.push({\n id,\n status,\n path: filePath,\n metadata,\n });\n }\n\n return sessions;\n } catch (error) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") {\n return [];\n }\n throw error;\n }\n}\n\n/**\n * Formats sessions for text output.\n */\nfunction formatTextOutput(sessions: Session[], _status: SessionStatus): string {\n if (sessions.length === 0) {\n return ` (no sessions)`;\n }\n\n return sessions\n .map((s) => {\n const priority = s.metadata.priority !== \"medium\" ? ` [${s.metadata.priority}]` : \"\";\n const tags = s.metadata.tags.length > 0 ? ` (${s.metadata.tags.join(\", \")})` : \"\";\n return ` ${s.id}${priority}${tags}`;\n })\n .join(\"\\n\");\n}\n\n/**\n * Executes the list command.\n *\n * @param options - Command options\n * @returns Formatted output for display\n */\nexport async function listCommand(options: ListOptions): Promise<string> {\n // Build config from options\n const config: SessionDirectoryConfig = options.sessionsDir\n ? {\n todoDir: join(options.sessionsDir, \"todo\"),\n doingDir: join(options.sessionsDir, \"doing\"),\n archiveDir: join(options.sessionsDir, \"archive\"),\n }\n : DEFAULT_SESSION_CONFIG;\n\n // Load sessions based on filter\n const statuses: SessionStatus[] = options.status\n ? [options.status]\n : [\"todo\", \"doing\", \"archive\"];\n\n const allSessions: Record<SessionStatus, Session[]> = {\n todo: [],\n doing: [],\n archive: [],\n };\n\n for (const status of statuses) {\n const dir = status === \"todo\"\n ? config.todoDir\n : status === \"doing\"\n ? config.doingDir\n : config.archiveDir;\n\n const sessions = await loadSessionsFromDir(dir, status);\n allSessions[status] = sortSessions(sessions);\n }\n\n // Format output\n if (options.format === \"json\") {\n return JSON.stringify(allSessions, null, 2);\n }\n\n // Text format\n const lines: string[] = [];\n\n if (statuses.includes(\"doing\")) {\n lines.push(\"DOING:\");\n lines.push(formatTextOutput(allSessions.doing, \"doing\"));\n lines.push(\"\");\n }\n\n if (statuses.includes(\"todo\")) {\n lines.push(\"TODO:\");\n lines.push(formatTextOutput(allSessions.todo, \"todo\"));\n lines.push(\"\");\n }\n\n if (statuses.includes(\"archive\")) {\n lines.push(\"ARCHIVE:\");\n lines.push(formatTextOutput(allSessions.archive, \"archive\"));\n }\n\n return lines.join(\"\\n\").trim();\n}\n","/**\n * Session pickup CLI command handler.\n *\n * @module commands/session/pickup\n */\n\nimport { mkdir, readdir, readFile, rename } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { NoSessionsAvailableError } from \"../../session/errors.js\";\nimport { parseSessionMetadata } from \"../../session/list.js\";\nimport { buildClaimPaths, classifyClaimError, selectBestSession } from \"../../session/pickup.js\";\nimport { DEFAULT_SESSION_CONFIG, formatShowOutput, type SessionDirectoryConfig } from \"../../session/show.js\";\nimport type { Session, SessionStatus } from \"../../session/types.js\";\n\n/**\n * Options for the pickup command.\n */\nexport interface PickupOptions {\n /** Session ID to pickup (mutually exclusive with auto) */\n sessionId?: string;\n /** Auto-select highest priority session */\n auto?: boolean;\n /** Custom sessions directory */\n sessionsDir?: string;\n}\n\n/**\n * Loads sessions from the todo directory.\n */\nasync function loadTodoSessions(config: SessionDirectoryConfig): Promise<Session[]> {\n try {\n const files = await readdir(config.todoDir);\n const sessions: Session[] = [];\n\n for (const file of files) {\n if (!file.endsWith(\".md\")) continue;\n\n const id = file.replace(\".md\", \"\");\n const filePath = join(config.todoDir, file);\n const content = await readFile(filePath, \"utf-8\");\n const metadata = parseSessionMetadata(content);\n\n sessions.push({\n id,\n status: \"todo\" as SessionStatus,\n path: filePath,\n metadata,\n });\n }\n\n return sessions;\n } catch (error) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") {\n return [];\n }\n throw error;\n }\n}\n\n/**\n * Executes the pickup command.\n *\n * Claims a session from the todo queue and moves it to doing.\n * Output includes `<PICKUP_ID>` tag for easy parsing by automation tools.\n *\n * @param options - Command options\n * @returns Formatted output for display with parseable session ID\n * @throws {NoSessionsAvailableError} When no sessions in todo for auto mode\n * @throws {SessionNotAvailableError} When session already claimed\n */\nexport async function pickupCommand(options: PickupOptions): Promise<string> {\n // Build config from options\n const config: SessionDirectoryConfig = options.sessionsDir\n ? {\n todoDir: join(options.sessionsDir, \"todo\"),\n doingDir: join(options.sessionsDir, \"doing\"),\n archiveDir: join(options.sessionsDir, \"archive\"),\n }\n : DEFAULT_SESSION_CONFIG;\n\n let sessionId: string;\n\n if (options.auto) {\n // Auto mode: select best session\n const sessions = await loadTodoSessions(config);\n const selected = selectBestSession(sessions);\n\n if (!selected) {\n throw new NoSessionsAvailableError();\n }\n\n sessionId = selected.id;\n } else if (options.sessionId) {\n sessionId = options.sessionId;\n } else {\n throw new Error(\"Either session ID or --auto flag is required\");\n }\n\n // Build paths and ensure doing directory exists\n const paths = buildClaimPaths(sessionId, config);\n await mkdir(config.doingDir, { recursive: true });\n\n // Perform atomic claim\n try {\n await rename(paths.source, paths.target);\n } catch (error) {\n throw classifyClaimError(error, sessionId);\n }\n\n // Read and format content\n const content = await readFile(paths.target, \"utf-8\");\n const output = formatShowOutput(content, { status: \"doing\" });\n\n // Output with parseable PICKUP_ID tag\n return `Claimed session <PICKUP_ID>${sessionId}</PICKUP_ID>\\n\\n${output}`;\n}\n","/**\n * Session pickup/claiming utilities.\n *\n * This module provides pure functions for atomic session claiming:\n * - Path construction for claim operations\n * - Error classification for claim failures\n * - Session selection for auto-pickup\n *\n * @module session/pickup\n */\n\nimport { SessionNotAvailableError } from \"./errors\";\nimport { parseSessionId } from \"./timestamp\";\nimport { PRIORITY_ORDER, type Session } from \"./types\";\n\n/**\n * Configuration for session claim paths.\n */\nexport interface ClaimPathConfig {\n /** Directory containing sessions to be claimed */\n todoDir: string;\n /** Directory for claimed sessions */\n doingDir: string;\n}\n\n/**\n * Result of building claim paths.\n */\nexport interface ClaimPaths {\n /** Source path (session in todo) */\n source: string;\n /** Target path (session in doing) */\n target: string;\n}\n\n/**\n * Builds source and target paths for claiming a session.\n *\n * @param sessionId - The session ID (timestamp format)\n * @param config - Directory configuration\n * @returns Source and target paths for the claim operation\n *\n * @example\n * ```typescript\n * const paths = buildClaimPaths(\"2026-01-13_08-01-05\", {\n * todoDir: \".spx/sessions/todo\",\n * doingDir: \".spx/sessions/doing\",\n * });\n * // => { source: \".spx/sessions/todo/2026-01-13_08-01-05.md\", target: \".spx/sessions/doing/2026-01-13_08-01-05.md\" }\n * ```\n */\nexport function buildClaimPaths(sessionId: string, config: ClaimPathConfig): ClaimPaths {\n return {\n source: `${config.todoDir}/${sessionId}.md`,\n target: `${config.doingDir}/${sessionId}.md`,\n };\n}\n\n/**\n * Classifies a filesystem error that occurred during claiming.\n *\n * When a claim operation fails, this function maps the raw filesystem\n * error to an appropriate domain error:\n * - ENOENT: Session was claimed by another agent (SessionNotAvailable)\n * - Other errors: Rethrown as-is\n *\n * @param error - The error from the claim operation\n * @param sessionId - The session ID being claimed\n * @returns A SessionNotAvailableError if ENOENT\n * @throws The original error if not ENOENT\n *\n * @example\n * ```typescript\n * const err = Object.assign(new Error(\"ENOENT\"), { code: \"ENOENT\" });\n * const classified = classifyClaimError(err, \"test-id\");\n * // => SessionNotAvailableError\n * ```\n */\nexport function classifyClaimError(error: unknown, sessionId: string): SessionNotAvailableError {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") {\n return new SessionNotAvailableError(sessionId);\n }\n throw error;\n}\n\n/**\n * Selects the best session for auto-pickup based on priority and timestamp.\n *\n * Selection criteria:\n * 1. Highest priority (high > medium > low)\n * 2. Oldest timestamp (FIFO) for sessions with equal priority\n *\n * @param sessions - Available sessions to choose from\n * @returns The best session to claim, or null if no sessions available\n *\n * @example\n * ```typescript\n * const sessions = [\n * { id: \"low-1\", metadata: { priority: \"low\" } },\n * { id: \"high-1\", metadata: { priority: \"high\" } },\n * ];\n * const best = selectBestSession(sessions);\n * // => { id: \"high-1\", ... }\n * ```\n */\nexport function selectBestSession(sessions: Session[]): Session | null {\n if (sessions.length === 0) {\n return null;\n }\n\n // Sort by priority (high first) then by timestamp (oldest first for FIFO)\n const sorted = [...sessions].sort((a, b) => {\n // First: sort by priority (high = 0, medium = 1, low = 2)\n const priorityA = PRIORITY_ORDER[a.metadata.priority];\n const priorityB = PRIORITY_ORDER[b.metadata.priority];\n\n if (priorityA !== priorityB) {\n return priorityA - priorityB;\n }\n\n // Second: sort by timestamp (oldest first = FIFO = ascending)\n const dateA = parseSessionId(a.id);\n const dateB = parseSessionId(b.id);\n\n // Handle invalid session IDs by treating them as newest (go last)\n if (!dateA && !dateB) return 0;\n if (!dateA) return 1; // a goes after b\n if (!dateB) return -1; // b goes after a\n\n return dateA.getTime() - dateB.getTime();\n });\n\n return sorted[0];\n}\n","/**\n * Session prune CLI command handler.\n *\n * @module commands/session/prune\n */\n\nimport { readdir, readFile, unlink } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { parseSessionMetadata, sortSessions } from \"../../session/list.js\";\nimport { DEFAULT_SESSION_CONFIG, type SessionDirectoryConfig } from \"../../session/show.js\";\nimport type { Session } from \"../../session/types.js\";\n\n/**\n * Default number of sessions to keep when pruning.\n */\nexport const DEFAULT_KEEP_COUNT = 5;\n\n/**\n * Options for the prune command.\n */\nexport interface PruneOptions {\n /** Number of sessions to keep (default: 5) */\n keep?: number;\n /** Show what would be deleted without actually deleting */\n dryRun?: boolean;\n /** Custom sessions directory */\n sessionsDir?: string;\n}\n\n/**\n * Error thrown when prune options are invalid.\n */\nexport class PruneValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"PruneValidationError\";\n }\n}\n\n/**\n * Validates prune options.\n *\n * @param options - Options to validate\n * @throws {PruneValidationError} When options are invalid\n */\nexport function validatePruneOptions(options: PruneOptions): void {\n if (options.keep !== undefined) {\n if (!Number.isInteger(options.keep) || options.keep < 1) {\n throw new PruneValidationError(\n `Invalid --keep value: ${options.keep}. Must be a positive integer.`,\n );\n }\n }\n}\n\n/**\n * Loads sessions from the archive directory.\n */\nasync function loadArchiveSessions(config: SessionDirectoryConfig): Promise<Session[]> {\n try {\n const files = await readdir(config.archiveDir);\n const sessions: Session[] = [];\n\n for (const file of files) {\n if (!file.endsWith(\".md\")) continue;\n\n const id = file.replace(\".md\", \"\");\n const filePath = join(config.archiveDir, file);\n const content = await readFile(filePath, \"utf-8\");\n const metadata = parseSessionMetadata(content);\n\n sessions.push({\n id,\n status: \"archive\",\n path: filePath,\n metadata,\n });\n }\n\n return sessions;\n } catch (error) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") {\n return [];\n }\n throw error;\n }\n}\n\n/**\n * Determines which sessions to prune.\n *\n * Sessions are sorted by priority (high first) then by timestamp (newest first).\n * The first `keep` sessions are retained, the rest are marked for deletion.\n *\n * @param sessions - All todo sessions\n * @param keep - Number of sessions to keep\n * @returns Sessions to delete (oldest/lowest priority first)\n */\nexport function selectSessionsToPrune(sessions: Session[], keep: number): Session[] {\n // Sort sessions by priority and timestamp\n const sorted = sortSessions(sessions);\n\n // Keep the top N sessions, prune the rest\n if (sorted.length <= keep) {\n return [];\n }\n\n return sorted.slice(keep);\n}\n\n/**\n * Executes the prune command.\n *\n * @param options - Command options\n * @returns Formatted output for display\n * @throws {PruneValidationError} When options are invalid\n */\nexport async function pruneCommand(options: PruneOptions): Promise<string> {\n // Validate options\n validatePruneOptions(options);\n\n const keep = options.keep ?? DEFAULT_KEEP_COUNT;\n const dryRun = options.dryRun ?? false;\n\n // Build config from options\n const config: SessionDirectoryConfig = options.sessionsDir\n ? {\n todoDir: join(options.sessionsDir, \"todo\"),\n doingDir: join(options.sessionsDir, \"doing\"),\n archiveDir: join(options.sessionsDir, \"archive\"),\n }\n : DEFAULT_SESSION_CONFIG;\n\n // Load and sort sessions\n const sessions = await loadArchiveSessions(config);\n const toPrune = selectSessionsToPrune(sessions, keep);\n\n if (toPrune.length === 0) {\n return `No sessions to prune. ${sessions.length} sessions kept.`;\n }\n\n // Dry run mode\n if (dryRun) {\n const lines = [\n `Would delete ${toPrune.length} sessions:`,\n ...toPrune.map((s) => ` - ${s.id}`),\n \"\",\n `${sessions.length - toPrune.length} sessions would be kept.`,\n ];\n return lines.join(\"\\n\");\n }\n\n // Delete sessions\n for (const session of toPrune) {\n await unlink(session.path);\n }\n\n const lines = [\n `Deleted ${toPrune.length} sessions:`,\n ...toPrune.map((s) => ` - ${s.id}`),\n \"\",\n `${sessions.length - toPrune.length} sessions kept.`,\n ];\n return lines.join(\"\\n\");\n}\n","/**\n * Session release CLI command handler.\n *\n * @module commands/session/release\n */\n\nimport { readdir, rename } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { SessionNotClaimedError } from \"../../session/errors.js\";\nimport { buildReleasePaths, findCurrentSession } from \"../../session/release.js\";\nimport { DEFAULT_SESSION_CONFIG, type SessionDirectoryConfig } from \"../../session/show.js\";\n\n/**\n * Options for the release command.\n */\nexport interface ReleaseOptions {\n /** Session ID to release (optional, defaults to most recent in doing) */\n sessionId?: string;\n /** Custom sessions directory */\n sessionsDir?: string;\n}\n\n/**\n * Loads session refs from the doing directory.\n */\nasync function loadDoingSessions(config: SessionDirectoryConfig): Promise<Array<{ id: string }>> {\n try {\n const files = await readdir(config.doingDir);\n return files\n .filter((file) => file.endsWith(\".md\"))\n .map((file) => ({ id: file.replace(\".md\", \"\") }));\n } catch (error) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") {\n return [];\n }\n throw error;\n }\n}\n\n/**\n * Executes the release command.\n *\n * @param options - Command options\n * @returns Formatted output for display\n * @throws {SessionNotClaimedError} When session not in doing directory\n */\nexport async function releaseCommand(options: ReleaseOptions): Promise<string> {\n // Build config from options\n const config: SessionDirectoryConfig = options.sessionsDir\n ? {\n todoDir: join(options.sessionsDir, \"todo\"),\n doingDir: join(options.sessionsDir, \"doing\"),\n archiveDir: join(options.sessionsDir, \"archive\"),\n }\n : DEFAULT_SESSION_CONFIG;\n\n let sessionId: string;\n\n if (options.sessionId) {\n sessionId = options.sessionId;\n } else {\n // Find most recent session in doing\n const sessions = await loadDoingSessions(config);\n const current = findCurrentSession(sessions);\n\n if (!current) {\n throw new SessionNotClaimedError(\"(none)\");\n }\n\n sessionId = current.id;\n }\n\n // Build paths and perform release\n const paths = buildReleasePaths(sessionId, config);\n\n try {\n await rename(paths.source, paths.target);\n } catch (error) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") {\n throw new SessionNotClaimedError(sessionId);\n }\n throw error;\n }\n\n return `Released session: ${sessionId}\\nSession returned to todo directory.`;\n}\n","/**\n * Session release utilities.\n *\n * This module provides pure functions for releasing claimed sessions:\n * - Path construction for release operations\n * - Finding the current session in doing directory\n *\n * @module session/release\n */\n\nimport { parseSessionId } from \"./timestamp\";\n\n/**\n * Configuration for session release paths.\n */\nexport interface ReleasePathConfig {\n /** Directory containing claimed sessions */\n doingDir: string;\n /** Directory for released sessions */\n todoDir: string;\n}\n\n/**\n * Result of building release paths.\n */\nexport interface ReleasePaths {\n /** Source path (session in doing) */\n source: string;\n /** Target path (session in todo) */\n target: string;\n}\n\n/**\n * Session reference with minimal required fields.\n */\nexport interface SessionRef {\n id: string;\n}\n\n/**\n * Builds source and target paths for releasing a session.\n *\n * @param sessionId - The session ID (timestamp format)\n * @param config - Directory configuration\n * @returns Source and target paths for the release operation\n *\n * @example\n * ```typescript\n * const paths = buildReleasePaths(\"2026-01-13_08-01-05\", {\n * doingDir: \".spx/sessions/doing\",\n * todoDir: \".spx/sessions/todo\",\n * });\n * // => { source: \".spx/sessions/doing/2026-01-13_08-01-05.md\", target: \".spx/sessions/todo/2026-01-13_08-01-05.md\" }\n * ```\n */\nexport function buildReleasePaths(sessionId: string, config: ReleasePathConfig): ReleasePaths {\n return {\n source: `${config.doingDir}/${sessionId}.md`,\n target: `${config.todoDir}/${sessionId}.md`,\n };\n}\n\n/**\n * Finds the most recent session in the doing directory.\n *\n * When no session ID is provided to release, this function\n * selects the most recently claimed session (newest timestamp).\n *\n * @param doingSessions - Sessions currently in doing directory\n * @returns The most recent session, or null if empty\n *\n * @example\n * ```typescript\n * const sessions = [\n * { id: \"2026-01-10_08-00-00\" },\n * { id: \"2026-01-13_08-00-00\" },\n * ];\n * const current = findCurrentSession(sessions);\n * // => { id: \"2026-01-13_08-00-00\" }\n * ```\n */\nexport function findCurrentSession(doingSessions: SessionRef[]): SessionRef | null {\n if (doingSessions.length === 0) {\n return null;\n }\n\n // Sort by timestamp descending (newest first)\n const sorted = [...doingSessions].sort((a, b) => {\n const dateA = parseSessionId(a.id);\n const dateB = parseSessionId(b.id);\n\n // Handle invalid session IDs by treating them as oldest\n if (!dateA && !dateB) return 0;\n if (!dateA) return 1; // a goes after b\n if (!dateB) return -1; // b goes after a\n\n return dateB.getTime() - dateA.getTime();\n });\n\n return sorted[0];\n}\n","/**\n * Session show CLI command handler.\n *\n * @module commands/session/show\n */\n\nimport { readFile, stat } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { SessionNotFoundError } from \"../../session/errors.js\";\nimport {\n DEFAULT_SESSION_CONFIG,\n formatShowOutput,\n resolveSessionPaths,\n SEARCH_ORDER,\n type SessionDirectoryConfig,\n} from \"../../session/show.js\";\nimport type { SessionStatus } from \"../../session/types.js\";\n\n/**\n * Options for the show command.\n */\nexport interface ShowOptions {\n /** Session ID to show */\n sessionId: string;\n /** Custom sessions directory */\n sessionsDir?: string;\n}\n\n/**\n * Finds the first existing path and its status.\n */\nasync function findExistingPath(\n paths: string[],\n _config: SessionDirectoryConfig,\n): Promise<{ path: string; status: SessionStatus } | null> {\n for (let i = 0; i < paths.length; i++) {\n const filePath = paths[i];\n try {\n const stats = await stat(filePath);\n if (stats.isFile()) {\n return { path: filePath, status: SEARCH_ORDER[i] };\n }\n } catch {\n // File doesn't exist, continue\n }\n }\n return null;\n}\n\n/**\n * Executes the show command.\n *\n * @param options - Command options\n * @returns Formatted output for display\n * @throws {SessionNotFoundError} When session not found\n */\nexport async function showCommand(options: ShowOptions): Promise<string> {\n // Build config from options\n const config: SessionDirectoryConfig = options.sessionsDir\n ? {\n todoDir: join(options.sessionsDir, \"todo\"),\n doingDir: join(options.sessionsDir, \"doing\"),\n archiveDir: join(options.sessionsDir, \"archive\"),\n }\n : DEFAULT_SESSION_CONFIG;\n\n // Resolve possible paths\n const paths = resolveSessionPaths(options.sessionId, config);\n\n // Find the existing file\n const found = await findExistingPath(paths, config);\n\n if (!found) {\n throw new SessionNotFoundError(options.sessionId);\n }\n\n // Read and format content\n const content = await readFile(found.path, \"utf-8\");\n return formatShowOutput(content, { status: found.status });\n}\n","/**\n * Shared help text for session commands.\n *\n * Centralizes help text to ensure consistency across subcommands.\n *\n * @module domains/session/help\n */\n\n/**\n * Session file format description for the main session help.\n */\nexport const SESSION_FORMAT_HELP = `\nSession File Format:\n Sessions are markdown files with YAML frontmatter for metadata.\n\n ---\n priority: high | medium | low\n tags: [tag1, tag2]\n ---\n # Session Title\n\n Session content...\n\nWorkflow:\n 1. handoff - Create session (todo)\n 2. pickup - Claim session (todo -> doing)\n 3. release - Return session (doing -> todo)\n 4. delete - Remove session\n`;\n\n/**\n * Frontmatter details for handoff command.\n */\nexport const HANDOFF_FRONTMATTER_HELP = `\nUsage:\n Option 1: Pipe content with frontmatter via stdin\n Option 2: Run without stdin, then edit the created file directly\n\nFrontmatter Format:\n ---\n priority: high # high | medium | low (default: medium)\n tags: [feat, api] # optional labels for categorization\n ---\n # Your session content here...\n\nOutput Tags (for automation):\n <HANDOFF_ID>session-id</HANDOFF_ID> - Session identifier\n <SESSION_FILE>/path/to/file</SESSION_FILE> - Absolute path to edit\n\nExamples:\n # With stdin content:\n echo '---\n priority: high\n ---\n # Fix login' | spx session handoff\n\n # Without stdin (creates empty session, edit file directly):\n spx session handoff\n`;\n\n/**\n * Selection logic for pickup command.\n */\nexport const PICKUP_SELECTION_HELP = `\nSelection Logic (--auto):\n Sessions are selected by priority, then age (FIFO):\n 1. high priority first\n 2. medium priority second\n 3. low priority last\n 4. Within same priority: oldest session first\n\nOutput:\n <PICKUP_ID>session-id</PICKUP_ID> tag for automation parsing\n`;\n","/**\n * Session domain - Manage session workflow\n */\nimport type { Command } from \"commander\";\n\nimport {\n archiveCommand,\n deleteCommand,\n handoffCommand,\n listCommand,\n pickupCommand,\n pruneCommand,\n PruneValidationError,\n releaseCommand,\n SessionAlreadyArchivedError,\n showCommand,\n} from \"../../commands/session/index.js\";\nimport type { Domain } from \"../types.js\";\nimport { HANDOFF_FRONTMATTER_HELP, PICKUP_SELECTION_HELP, SESSION_FORMAT_HELP } from \"./help.js\";\n\n/**\n * Reads content from stdin if available (piped input).\n * Returns undefined if stdin is a TTY (interactive terminal).\n */\nasync function readStdin(): Promise<string | undefined> {\n // Check if stdin is a TTY (interactive) - if so, don't wait for input\n if (process.stdin.isTTY) {\n return undefined;\n }\n\n return new Promise((resolve) => {\n let data = \"\";\n process.stdin.setEncoding(\"utf-8\");\n process.stdin.on(\"data\", (chunk) => {\n data += chunk;\n });\n process.stdin.on(\"end\", () => {\n resolve(data.trim() || undefined);\n });\n // Handle case where stdin closes without data\n process.stdin.on(\"error\", () => {\n resolve(undefined);\n });\n });\n}\n\n/**\n * Handles command errors with consistent formatting.\n */\nfunction handleError(error: unknown): never {\n console.error(\"Error:\", error instanceof Error ? error.message : String(error));\n process.exit(1);\n}\n\n/**\n * Register session domain commands\n *\n * @param sessionCmd - Commander.js session domain command\n */\nfunction registerSessionCommands(sessionCmd: Command): void {\n // list command\n sessionCmd\n .command(\"list\")\n .description(\"List all sessions\")\n .option(\"--status <status>\", \"Filter by status (todo|doing|archive)\")\n .option(\"--json\", \"Output as JSON\")\n .option(\"--sessions-dir <path>\", \"Custom sessions directory\")\n .action(async (options: { status?: string; json?: boolean; sessionsDir?: string }) => {\n try {\n const output = await listCommand({\n status: options.status as \"todo\" | \"doing\" | \"archive\" | undefined,\n format: options.json ? \"json\" : \"text\",\n sessionsDir: options.sessionsDir,\n });\n console.log(output);\n } catch (error) {\n handleError(error);\n }\n });\n\n // show command\n sessionCmd\n .command(\"show <id>\")\n .description(\"Show session content\")\n .option(\"--sessions-dir <path>\", \"Custom sessions directory\")\n .action(async (id: string, options: { sessionsDir?: string }) => {\n try {\n const output = await showCommand({\n sessionId: id,\n sessionsDir: options.sessionsDir,\n });\n console.log(output);\n } catch (error) {\n handleError(error);\n }\n });\n\n // pickup command\n sessionCmd\n .command(\"pickup [id]\")\n .description(\"Claim a session (move from todo to doing)\")\n .option(\"--auto\", \"Auto-select highest priority session\")\n .option(\"--sessions-dir <path>\", \"Custom sessions directory\")\n .addHelpText(\"after\", PICKUP_SELECTION_HELP)\n .action(async (id: string | undefined, options: { auto?: boolean; sessionsDir?: string }) => {\n try {\n if (!id && !options.auto) {\n console.error(\"Error: Either session ID or --auto flag is required\");\n process.exit(1);\n }\n const output = await pickupCommand({\n sessionId: id,\n auto: options.auto,\n sessionsDir: options.sessionsDir,\n });\n console.log(output);\n } catch (error) {\n handleError(error);\n }\n });\n\n // release command\n sessionCmd\n .command(\"release [id]\")\n .description(\"Release a session (move from doing to todo)\")\n .option(\"--sessions-dir <path>\", \"Custom sessions directory\")\n .action(async (id: string | undefined, options: { sessionsDir?: string }) => {\n try {\n const output = await releaseCommand({\n sessionId: id,\n sessionsDir: options.sessionsDir,\n });\n console.log(output);\n } catch (error) {\n handleError(error);\n }\n });\n\n // handoff command\n // Note: --priority and --tags removed. Metadata should be in content frontmatter.\n // This makes the command deterministic for Claude Code permission pre-approval.\n sessionCmd\n .command(\"handoff\")\n .description(\"Create a handoff session (reads content with frontmatter from stdin)\")\n .option(\"--sessions-dir <path>\", \"Custom sessions directory\")\n .addHelpText(\"after\", HANDOFF_FRONTMATTER_HELP)\n .action(async (options: { sessionsDir?: string }) => {\n try {\n // Read content from stdin if available\n const content = await readStdin();\n\n const output = await handoffCommand({\n content,\n sessionsDir: options.sessionsDir,\n });\n console.log(output);\n } catch (error) {\n handleError(error);\n }\n });\n\n // delete command\n sessionCmd\n .command(\"delete <id>\")\n .description(\"Delete a session\")\n .option(\"--sessions-dir <path>\", \"Custom sessions directory\")\n .action(async (id: string, options: { sessionsDir?: string }) => {\n try {\n const output = await deleteCommand({\n sessionId: id,\n sessionsDir: options.sessionsDir,\n });\n console.log(output);\n } catch (error) {\n handleError(error);\n }\n });\n\n // prune command\n sessionCmd\n .command(\"prune\")\n .description(\"Remove old todo sessions, keeping the most recent N\")\n .option(\"--keep <count>\", \"Number of sessions to keep (default: 5)\", \"5\")\n .option(\"--dry-run\", \"Show what would be deleted without deleting\")\n .option(\"--sessions-dir <path>\", \"Custom sessions directory\")\n .action(async (options: { keep?: string; dryRun?: boolean; sessionsDir?: string }) => {\n try {\n const keep = options.keep ? Number.parseInt(options.keep, 10) : undefined;\n const output = await pruneCommand({\n keep,\n dryRun: options.dryRun,\n sessionsDir: options.sessionsDir,\n });\n console.log(output);\n } catch (error) {\n if (error instanceof PruneValidationError) {\n console.error(\"Error:\", error.message);\n process.exit(1);\n }\n handleError(error);\n }\n });\n\n // archive command\n sessionCmd\n .command(\"archive <id>\")\n .description(\"Move a session to the archive directory\")\n .option(\"--sessions-dir <path>\", \"Custom sessions directory\")\n .action(async (id: string, options: { sessionsDir?: string }) => {\n try {\n const output = await archiveCommand({\n sessionId: id,\n sessionsDir: options.sessionsDir,\n });\n console.log(output);\n } catch (error) {\n if (error instanceof SessionAlreadyArchivedError) {\n console.error(\"Error:\", error.message);\n process.exit(1);\n }\n handleError(error);\n }\n });\n}\n\n/**\n * Session domain - Manage session workflow\n */\nexport const sessionDomain: Domain = {\n name: \"session\",\n description: \"Manage session workflow\",\n register: (program: Command) => {\n const sessionCmd = program\n .command(\"session\")\n .description(\"Manage session workflow\")\n .addHelpText(\"after\", SESSION_FORMAT_HELP);\n\n registerSessionCommands(sessionCmd);\n },\n};\n","/**\n * Scanner class for discovering work items in a project\n *\n * Encapsulates directory walking, filtering, and work item building with\n * configurable paths via dependency injection.\n *\n * @module scanner/scanner\n */\nimport path from \"node:path\";\nimport type { SpxConfig } from \"../config/defaults.js\";\nimport type { WorkItem } from \"../types.js\";\nimport { buildWorkItemList, filterWorkItemDirectories, walkDirectory } from \"./walk.js\";\n\n/**\n * Scanner for discovering work items in a project\n *\n * Uses dependency injection for configuration, eliminating hardcoded paths.\n * All directory paths are derived from the injected SpxConfig.\n *\n * @example\n * ```typescript\n * import { DEFAULT_CONFIG } from \"@/config/defaults\";\n *\n * const scanner = new Scanner(\"/path/to/project\", DEFAULT_CONFIG);\n * const workItems = await scanner.scan();\n * ```\n */\nexport class Scanner {\n /**\n * Create a new Scanner instance\n *\n * @param projectRoot - Absolute path to the project root directory\n * @param config - Configuration object defining directory structure\n */\n constructor(\n private readonly projectRoot: string,\n private readonly config: SpxConfig,\n ) {}\n\n /**\n * Scan the project for work items in the \"doing\" status directory\n *\n * Walks the configured specs/work/doing directory, filters for valid\n * work item directories, and returns structured work item data.\n *\n * @returns Array of work items found in the doing directory\n * @throws Error if the directory doesn't exist or is inaccessible\n */\n async scan(): Promise<WorkItem[]> {\n const doingPath = this.getDoingPath();\n\n // Walk directory to find all subdirectories\n const allEntries = await walkDirectory(doingPath);\n\n // Filter to only valid work item directories\n const workItemEntries = filterWorkItemDirectories(allEntries);\n\n // Build work item list with metadata\n return buildWorkItemList(workItemEntries);\n }\n\n /**\n * Get the full path to the \"doing\" status directory\n *\n * Constructs path from config: {projectRoot}/{specs.root}/{work.dir}/{statusDirs.doing}\n *\n * @returns Absolute path to the doing directory\n */\n getDoingPath(): string {\n return path.join(\n this.projectRoot,\n this.config.specs.root,\n this.config.specs.work.dir,\n this.config.specs.work.statusDirs.doing,\n );\n }\n\n /**\n * Get the full path to the \"backlog\" status directory\n *\n * @returns Absolute path to the backlog directory\n */\n getBacklogPath(): string {\n return path.join(\n this.projectRoot,\n this.config.specs.root,\n this.config.specs.work.dir,\n this.config.specs.work.statusDirs.backlog,\n );\n }\n\n /**\n * Get the full path to the \"done\" status directory\n *\n * @returns Absolute path to the done/archive directory\n */\n getDonePath(): string {\n return path.join(\n this.projectRoot,\n this.config.specs.root,\n this.config.specs.work.dir,\n this.config.specs.work.statusDirs.done,\n );\n }\n\n /**\n * Get the full path to the specs root directory\n *\n * @returns Absolute path to the specs root\n */\n getSpecsRootPath(): string {\n return path.join(this.projectRoot, this.config.specs.root);\n }\n\n /**\n * Get the full path to the work directory\n *\n * @returns Absolute path to the work directory\n */\n getWorkPath(): string {\n return path.join(\n this.projectRoot,\n this.config.specs.root,\n this.config.specs.work.dir,\n );\n }\n}\n","/**\n * Status determination state machine for work items\n */\nimport type { WorkItemStatus } from \"../types\";\nimport { access, readdir, stat } from \"fs/promises\";\nimport path from \"path\";\n\n/**\n * Input flags for status determination\n */\nexport interface StatusFlags {\n /** Whether tests/ directory exists */\n hasTestsDir: boolean;\n /** Whether tests/DONE.md exists */\n hasDoneMd: boolean;\n /** Whether tests/ directory is empty (excluding DONE.md) */\n testsIsEmpty: boolean;\n}\n\n/**\n * Determines work item status based on tests/ directory state\n *\n * Truth Table:\n * | hasTestsDir | testsIsEmpty | hasDoneMd | Status |\n * |-------------|--------------|-----------|-------------|\n * | false | N/A | N/A | OPEN |\n * | true | true | false | OPEN |\n * | true | true | true | DONE |\n * | true | false | false | IN_PROGRESS |\n * | true | false | true | DONE |\n *\n * @param flags - Status determination flags\n * @returns Work item status as OPEN, IN_PROGRESS, or DONE\n *\n * @example\n * ```typescript\n * // No tests directory\n * determineStatus({ hasTestsDir: false, hasDoneMd: false, testsIsEmpty: true })\n * // => \"OPEN\"\n *\n * // Tests directory with files, no DONE.md\n * determineStatus({ hasTestsDir: true, hasDoneMd: false, testsIsEmpty: false })\n * // => \"IN_PROGRESS\"\n *\n * // Tests directory with DONE.md\n * determineStatus({ hasTestsDir: true, hasDoneMd: true, testsIsEmpty: false })\n * // => \"DONE\"\n * ```\n */\nexport function determineStatus(flags: StatusFlags): WorkItemStatus {\n // No tests directory → OPEN\n if (!flags.hasTestsDir) {\n return \"OPEN\";\n }\n\n // Has DONE.md → DONE (regardless of other files)\n if (flags.hasDoneMd) {\n return \"DONE\";\n }\n\n // Has tests directory but empty → OPEN\n if (flags.testsIsEmpty) {\n return \"OPEN\";\n }\n\n // Has tests directory with files, no DONE.md → IN_PROGRESS\n return \"IN_PROGRESS\";\n}\n\n/**\n * Checks if a work item has a tests/ directory\n *\n * @param workItemPath - Absolute path to the work item directory\n * @returns Promise resolving to true if tests/ exists, false otherwise\n *\n * @example\n * ```typescript\n * const hasTests = await hasTestsDirectory('/path/to/story-21');\n * // => true if /path/to/story-21/tests exists\n * ```\n */\nexport async function hasTestsDirectory(\n workItemPath: string\n): Promise<boolean> {\n try {\n const testsPath = path.join(workItemPath, \"tests\");\n await access(testsPath);\n return true;\n } catch (error) {\n // ENOENT means directory doesn't exist → return false\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n return false;\n }\n // Re-throw permission errors and other failures\n throw error;\n }\n}\n\n/**\n * Checks if a tests/ directory is empty (has no test files)\n *\n * A directory is considered empty if it contains no files, or only contains:\n * - DONE.md (completion marker, not a test)\n * - Dotfiles like .gitkeep (version control artifacts, not tests)\n *\n * @param testsPath - Absolute path to the tests/ directory\n * @returns Promise resolving to true if empty, false if has test files\n *\n * @example\n * ```typescript\n * // Directory with only DONE.md\n * await isTestsDirectoryEmpty('/path/to/tests');\n * // => true (DONE.md doesn't count as a test)\n *\n * // Directory with test files\n * await isTestsDirectoryEmpty('/path/to/tests');\n * // => false\n * ```\n */\nexport async function isTestsDirectoryEmpty(\n testsPath: string\n): Promise<boolean> {\n try {\n const entries = await readdir(testsPath);\n\n // Filter out DONE.md and dotfiles\n const testFiles = entries.filter((entry) => {\n // Exclude DONE.md\n if (entry === \"DONE.md\") {\n return false;\n }\n // Exclude dotfiles (.gitkeep, .DS_Store, etc.)\n if (entry.startsWith(\".\")) {\n return false;\n }\n return true;\n });\n\n // Empty if no test files remain after filtering\n return testFiles.length === 0;\n } catch (error) {\n // ENOENT means directory doesn't exist → treat as empty\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n return true;\n }\n // Re-throw permission errors and other failures\n throw error;\n }\n}\n\n/**\n * Checks if a tests/ directory contains a DONE.md file\n *\n * Verifies that DONE.md exists and is a regular file (not a directory).\n * The check is case-sensitive: only \"DONE.md\" is accepted.\n *\n * @param testsPath - Absolute path to the tests/ directory\n * @returns Promise resolving to true if DONE.md exists, false otherwise\n *\n * @example\n * ```typescript\n * // Tests directory with DONE.md\n * await hasDoneMd('/path/to/tests');\n * // => true\n *\n * // Tests directory without DONE.md\n * await hasDoneMd('/path/to/tests');\n * // => false\n * ```\n */\nexport async function hasDoneMd(testsPath: string): Promise<boolean> {\n try {\n // Case-sensitive check: read directory and verify exact filename\n const entries = await readdir(testsPath);\n\n // Check if \"DONE.md\" exists in the directory listing (case-sensitive)\n if (!entries.includes(\"DONE.md\")) {\n return false;\n }\n\n // Verify it's a regular file, not a directory\n const donePath = path.join(testsPath, \"DONE.md\");\n const stats = await stat(donePath);\n return stats.isFile();\n } catch (error) {\n // ENOENT means directory doesn't exist → return false\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n return false;\n }\n // Re-throw permission errors and other failures\n throw error;\n }\n}\n\n/**\n * Custom error for status determination failures\n */\nexport class StatusDeterminationError extends Error {\n constructor(\n public readonly workItemPath: string,\n public readonly cause: unknown\n ) {\n const errorMessage =\n cause instanceof Error ? cause.message : String(cause);\n super(`Failed to determine status for ${workItemPath}: ${errorMessage}`);\n this.name = \"StatusDeterminationError\";\n }\n}\n\n/**\n * Determines the status of a work item by checking its tests/ directory\n *\n * This is the main orchestration function that combines all status checks:\n * 1. Checks if tests/ directory exists\n * 2. Checks if tests/ has DONE.md\n * 3. Checks if tests/ is empty (excluding DONE.md)\n * 4. Determines final status based on these flags\n *\n * Performance: Uses caching strategy to minimize filesystem calls.\n * All checks for a single work item are performed in one pass.\n *\n * @param workItemPath - Absolute path to the work item directory\n * @returns Promise resolving to OPEN, IN_PROGRESS, or DONE\n * @throws {StatusDeterminationError} If work item doesn't exist or has permission errors\n *\n * @example\n * ```typescript\n * // Work item with no tests directory\n * await getWorkItemStatus('/path/to/story-21');\n * // => \"OPEN\"\n *\n * // Work item with tests but no DONE.md\n * await getWorkItemStatus('/path/to/story-32');\n * // => \"IN_PROGRESS\"\n *\n * // Work item with DONE.md\n * await getWorkItemStatus('/path/to/story-43');\n * // => \"DONE\"\n * ```\n */\nexport async function getWorkItemStatus(\n workItemPath: string\n): Promise<WorkItemStatus> {\n try {\n // Step 0: Verify work item path exists\n try {\n await access(workItemPath);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n throw new Error(`Work item not found: ${workItemPath}`);\n }\n // Permission error or other failure\n throw error;\n }\n\n // Step 1: Check if tests/ directory exists\n const testsPath = path.join(workItemPath, \"tests\");\n let hasTests: boolean;\n try {\n await access(testsPath);\n hasTests = true;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n hasTests = false;\n } else {\n // Permission error or other failure\n throw error;\n }\n }\n\n // Early return if no tests directory\n if (!hasTests) {\n return determineStatus({\n hasTestsDir: false,\n hasDoneMd: false,\n testsIsEmpty: true,\n });\n }\n\n // Step 2: Read tests/ directory once (caching strategy)\n // This single readdir call gives us all the data we need\n const entries = await readdir(testsPath);\n\n // Step 3: Check for DONE.md (from cached entries)\n const hasDone = entries.includes(\"DONE.md\");\n if (hasDone) {\n // Verify it's a file, not a directory\n const donePath = path.join(testsPath, \"DONE.md\");\n const stats = await stat(donePath);\n if (!stats.isFile()) {\n // DONE.md is a directory, treat as no DONE.md\n return determineStatus({\n hasTestsDir: true,\n hasDoneMd: false,\n testsIsEmpty: isEmptyFromEntries(entries),\n });\n }\n }\n\n // Step 4: Check if empty (from cached entries)\n const isEmpty = isEmptyFromEntries(entries);\n\n // Step 5: Determine final status\n return determineStatus({\n hasTestsDir: true,\n hasDoneMd: hasDone,\n testsIsEmpty: isEmpty,\n });\n } catch (error) {\n // Wrap all errors with work item context\n throw new StatusDeterminationError(workItemPath, error);\n }\n}\n\n/**\n * Helper: Check if directory is empty from readdir entries\n * @param entries - Directory entries from readdir\n * @returns true if no test files (excluding DONE.md and dotfiles)\n */\nfunction isEmptyFromEntries(entries: string[]): boolean {\n const testFiles = entries.filter((entry) => {\n // Exclude DONE.md\n if (entry === \"DONE.md\") {\n return false;\n }\n // Exclude dotfiles (.gitkeep, .DS_Store, etc.)\n if (entry.startsWith(\".\")) {\n return false;\n }\n return true;\n });\n return testFiles.length === 0;\n}\n","/**\n * Tree building functions for converting flat work item lists to hierarchical trees\n *\n * Part of Feature 54 (Tree Building)\n */\nimport { getWorkItemStatus } from \"../status/state.js\";\nimport type { WorkItem } from \"../types.js\";\nimport type { TreeNode, WorkItemTree } from \"./types.js\";\n\n/**\n * Dependencies for tree building (for testing)\n */\nexport interface TreeBuildDeps {\n getStatus?: (path: string) => Promise<string>;\n}\n\n/**\n * Build hierarchical tree from flat list of work items\n *\n * Creates parent-child relationships based on directory paths.\n * Per ADR-002, children are BSP-sorted and status is rolled up from children.\n *\n * @param workItems - Flat list of work items from scanner\n * @param deps - Optional dependencies (for testing)\n * @returns Hierarchical tree structure\n * @throws Error if orphan work items detected (items without valid parents)\n *\n * @example\n * ```typescript\n * const workItems = await walkSpecs(\"/specs\");\n * const tree = await buildTree(workItems);\n * // tree.nodes contains top-level capabilities with nested children\n * ```\n */\nexport async function buildTree(\n workItems: WorkItem[],\n deps: TreeBuildDeps = {},\n): Promise<WorkItemTree> {\n const getStatus = deps.getStatus || getWorkItemStatus;\n\n // Step 1: Determine status for each work item\n const itemsWithStatus = await Promise.all(\n workItems.map(async (item) => ({\n ...item,\n status: await getStatus(item.path),\n })),\n );\n\n // Step 2: Separate work items by kind\n const capabilities = itemsWithStatus.filter(\n (item) => item.kind === \"capability\",\n );\n const features = itemsWithStatus.filter((item) => item.kind === \"feature\");\n const stories = itemsWithStatus.filter((item) => item.kind === \"story\");\n\n // Step 3: Build tree nodes for each level (with BSP sorting)\n const storyNodes = stories.map((item) => createTreeNode(item, []));\n const featureNodes = features.map((item) => {\n const children = storyNodes\n .filter((story) => isChildOf(story.path, item.path))\n .sort((a, b) => a.number - b.number); // Sort by BSP number\n return createTreeNode(item, children);\n });\n const capabilityNodes = capabilities.map((item) => {\n const children = featureNodes\n .filter((feature) => isChildOf(feature.path, item.path))\n .sort((a, b) => a.number - b.number); // Sort by BSP number\n return createTreeNode(item, children);\n });\n\n // Step 4: Detect orphans\n detectOrphans(stories, featureNodes);\n detectOrphans(features, capabilityNodes);\n\n // Step 5: Sort top-level capabilities by BSP number\n const sortedCapabilities = capabilityNodes.sort((a, b) => a.number - b.number);\n\n // Step 6: Roll up status from children to parents\n rollupStatus(sortedCapabilities);\n\n return {\n nodes: sortedCapabilities,\n };\n}\n\n/**\n * Create a TreeNode from a work item with status and children\n */\nfunction createTreeNode(\n item: WorkItem & { status: string },\n children: TreeNode[],\n): TreeNode {\n return {\n kind: item.kind,\n number: item.number,\n slug: item.slug,\n path: item.path,\n status: item.status as \"OPEN\" | \"IN_PROGRESS\" | \"DONE\",\n children,\n };\n}\n\n/**\n * Check if childPath is a direct child of parentPath\n *\n * A path is a child if it starts with the parent path followed by a separator.\n *\n * @param childPath - Potential child path\n * @param parentPath - Potential parent path\n * @returns true if childPath is a direct child of parentPath\n */\nfunction isChildOf(childPath: string, parentPath: string): boolean {\n // Normalize paths to ensure consistent comparison\n const normalizedChild = childPath.replace(/\\/$/, \"\");\n const normalizedParent = parentPath.replace(/\\/$/, \"\");\n\n // Check if child starts with parent followed by path separator\n if (!normalizedChild.startsWith(normalizedParent + \"/\")) {\n return false;\n }\n\n // Ensure it's a direct child (not a grandchild)\n const relativePath = normalizedChild.slice(normalizedParent.length + 1);\n return !relativePath.includes(\"/\");\n}\n\n/**\n * Detect orphan work items (items without valid parents)\n *\n * @param items - Work items to check\n * @param potentialParents - Potential parent nodes\n * @throws Error if orphans detected\n */\nfunction detectOrphans(\n items: (WorkItem & { status: string })[],\n potentialParents: TreeNode[],\n): void {\n for (const item of items) {\n const hasParent = potentialParents.some((parent) => isChildOf(item.path, parent.path));\n\n if (!hasParent) {\n throw new Error(\n `Orphan work item detected: ${item.kind} \"${item.slug}\" at ${item.path} has no valid parent`,\n );\n }\n }\n}\n\n/**\n * Roll up status from children to parents\n *\n * Recursively aggregates status from child nodes to parent nodes.\n * Parent status requires BOTH own tests/DONE.md AND all children complete.\n *\n * Rollup rules (ownStatus = status from parent's own tests/DONE.md):\n * - DONE: ownStatus is DONE AND all children are DONE\n * - OPEN: ownStatus is OPEN AND all children are OPEN\n * - IN_PROGRESS: everything else (any mismatch between own and child status)\n *\n * @param nodes - Tree nodes to process (modified in place)\n */\nfunction rollupStatus(nodes: TreeNode[]): void {\n for (const node of nodes) {\n if (node.children.length > 0) {\n // First, recursively roll up status for children\n rollupStatus(node.children);\n\n // Capture node's own status (from its tests/DONE.md)\n const ownStatus = node.status;\n\n // Then, aggregate status from children\n const childStatuses = node.children.map((child) => child.status);\n const allChildrenDone = childStatuses.every(\n (status) => status === \"DONE\",\n );\n const allChildrenOpen = childStatuses.every(\n (status) => status === \"OPEN\",\n );\n\n // DONE: own status is DONE AND all children are DONE\n if (ownStatus === \"DONE\" && allChildrenDone) {\n node.status = \"DONE\";\n } // OPEN: own status is OPEN AND all children are OPEN\n else if (ownStatus === \"OPEN\" && allChildrenOpen) {\n node.status = \"OPEN\";\n } // IN_PROGRESS: everything else\n else {\n node.status = \"IN_PROGRESS\";\n }\n }\n // Leaf nodes (stories) keep their original status\n }\n}\n","/**\n * Next command implementation\n *\n * Finds the next work item to work on based on BSP order:\n * - BSP order is absolute - lower number must complete first\n * - Status (IN_PROGRESS vs OPEN) is irrelevant to priority\n * - Returns first non-DONE item in BSP order\n */\nimport { DEFAULT_CONFIG } from \"../../config/defaults.js\";\nimport { Scanner } from \"../../scanner/scanner.js\";\nimport { buildTree } from \"../../tree/build.js\";\nimport type { TreeNode, WorkItemTree } from \"../../tree/types.js\";\nimport { LEAF_KIND } from \"../../types.js\";\n\n/**\n * Options for next command\n */\nexport interface NextOptions {\n /** Working directory (defaults to current directory) */\n cwd?: string;\n}\n\n/**\n * Find the next work item to work on\n *\n * Priority order:\n * - BSP order is absolute - lower number must complete first\n * - Status (IN_PROGRESS vs OPEN) is irrelevant to priority\n * - Returns first non-DONE leaf in BSP order\n *\n * Tree is already BSP-sorted at each level (per ADR-002).\n * Traverses depth-first in BSP order to find first non-DONE leaf.\n *\n * @param tree - Work item tree\n * @returns Next leaf node to work on, or null if all done\n *\n * @example\n * ```typescript\n * const tree = buildTreeWithMixedStatus();\n * const next = findNextWorkItem(tree);\n * // => { kind: \"story\", number: 21, slug: \"lowest-bsp-story\", status: \"OPEN\", ... }\n * ```\n */\nexport function findNextWorkItem(tree: WorkItemTree): TreeNode | null {\n // Tree is already BSP-sorted at each level (per ADR-002)\n // Traverse depth-first in BSP order to find first non-DONE leaf\n return findFirstNonDoneLeaf(tree.nodes);\n}\n\n/**\n * Recursively find first non-DONE story node in BSP order\n *\n * Tree children are assumed to be pre-sorted by BSP number (per ADR-002).\n * Only story nodes are considered actionable work items.\n *\n * @param nodes - Tree nodes to traverse (assumed BSP-sorted)\n * @returns First non-DONE story node, or null if all done\n */\nfunction findFirstNonDoneLeaf(nodes: TreeNode[]): TreeNode | null {\n for (const node of nodes) {\n if (node.kind === LEAF_KIND) {\n // Leaf kind is an actionable work item - check status\n if (node.status !== \"DONE\") {\n return node;\n }\n } else {\n // Non-leaf - recurse into children (already BSP-sorted)\n const found = findFirstNonDoneLeaf(node.children);\n if (found) {\n return found;\n }\n }\n }\n return null;\n}\n\n/**\n * Format work item name for display\n *\n * @param node - Tree node to format\n * @returns Formatted name (e.g., \"story-32_next-command\")\n */\nfunction formatWorkItemName(node: TreeNode): string {\n // Display number (capability needs +1, others as-is)\n const displayNum = node.kind === \"capability\" ? node.number + 1 : node.number;\n return `${node.kind}-${displayNum}_${node.slug}`;\n}\n\n/**\n * Execute next command\n *\n * Finds and displays the next work item to work on.\n * Shows full path information for context.\n *\n * @param options - Command options\n * @returns Formatted output\n * @throws Error if specs directory doesn't exist or is inaccessible\n *\n * @example\n * ```typescript\n * const output = await nextCommand({ cwd: \"/path/to/project\" });\n * console.log(output);\n * ```\n */\nexport async function nextCommand(options: NextOptions = {}): Promise<string> {\n const cwd = options.cwd || process.cwd();\n\n // Step 1-3: Use Scanner with config-driven paths\n const scanner = new Scanner(cwd, DEFAULT_CONFIG);\n const workItems = await scanner.scan();\n\n // Handle empty project\n if (workItems.length === 0) {\n return `No work items found in ${DEFAULT_CONFIG.specs.root}/${DEFAULT_CONFIG.specs.work.dir}/${DEFAULT_CONFIG.specs.work.statusDirs.doing}`;\n }\n\n // Step 4: Build hierarchical tree with status\n const tree = await buildTree(workItems);\n\n // Step 5: Find next work item\n const next = findNextWorkItem(tree);\n\n if (!next) {\n return \"All work items are complete! 🎉\";\n }\n\n // Step 6: Find parent feature and capability for context\n const parents = findParents(tree.nodes, next);\n\n // Step 7: Format output with context\n const lines: string[] = [];\n lines.push(\"Next work item:\");\n lines.push(\"\");\n\n if (parents.capability && parents.feature) {\n lines.push(\n ` ${formatWorkItemName(parents.capability)} > ${formatWorkItemName(parents.feature)} > ${\n formatWorkItemName(next)\n }`,\n );\n } else {\n lines.push(` ${formatWorkItemName(next)}`);\n }\n\n lines.push(\"\");\n lines.push(` Status: ${next.status}`);\n lines.push(` Path: ${next.path}`);\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Find parent capability and feature for a story\n *\n * @param nodes - Tree nodes to search\n * @param target - Story node to find parents for\n * @returns Parent capability and feature, or empty object if not found\n */\nfunction findParents(\n nodes: TreeNode[],\n target: TreeNode,\n): { capability?: TreeNode; feature?: TreeNode } {\n for (const capability of nodes) {\n for (const feature of capability.children) {\n for (const story of feature.children) {\n if (story.path === target.path) {\n return { capability, feature };\n }\n }\n }\n }\n return {};\n}\n","/**\n * JSON formatter for work item trees\n *\n * Produces structured JSON with summary statistics and full tree data.\n * Part of Feature 65 (Output Formatting), Story 32.\n */\nimport type { SpxConfig } from \"@/config/defaults\";\nimport type { TreeNode, WorkItemTree } from \"@/tree/types\";\n\n/** JSON indentation (2 spaces per ADR-002 and user requirements) */\nconst JSON_INDENT = 2;\n\n/**\n * Summary counts for capabilities and features only (NOT stories)\n */\ninterface Summary {\n done: number;\n inProgress: number;\n open: number;\n}\n\n/**\n * JSON output structure\n */\ninterface JSONOutput {\n config: {\n specs: SpxConfig[\"specs\"];\n sessions: SpxConfig[\"sessions\"];\n };\n summary: Summary;\n capabilities: unknown[];\n}\n\n/**\n * Format tree as JSON with summary statistics\n *\n * Summary counts capabilities + features only (per user requirement).\n * Display numbers (per ADR-002):\n * - Capabilities: internal + 1\n * - Features/Stories: as-is\n *\n * @param tree - Work item tree to format\n * @param config - SpxConfig used for path resolution\n * @returns JSON string with 2-space indentation\n */\nexport function formatJSON(tree: WorkItemTree, config: SpxConfig): string {\n const capabilities = tree.nodes.map((node) => nodeToJSON(node));\n const summary = calculateSummary(tree);\n\n const output: JSONOutput = {\n config: {\n specs: config.specs,\n sessions: config.sessions,\n },\n summary,\n capabilities,\n };\n\n return JSON.stringify(output, null, JSON_INDENT);\n}\n\n/**\n * Convert tree node to JSON structure with display numbers\n *\n * @param node - Tree node to convert\n * @returns JSON-serializable object\n */\nfunction nodeToJSON(node: TreeNode): unknown {\n const displayNumber = getDisplayNumber(node);\n\n const base = {\n kind: node.kind,\n number: displayNumber,\n slug: node.slug,\n status: node.status,\n };\n\n // Add children based on kind\n if (node.kind === \"capability\") {\n return {\n ...base,\n features: node.children.map((child) => nodeToJSON(child)),\n };\n } else if (node.kind === \"feature\") {\n return {\n ...base,\n stories: node.children.map((child) => nodeToJSON(child)),\n };\n } else {\n // Stories have no children\n return base;\n }\n}\n\n/**\n * Calculate summary statistics\n *\n * Counts capabilities + features only (NOT stories per user requirement)\n *\n * @param tree - Work item tree\n * @returns Summary counts\n */\nfunction calculateSummary(tree: WorkItemTree): Summary {\n const summary: Summary = {\n done: 0,\n inProgress: 0,\n open: 0,\n };\n\n for (const capability of tree.nodes) {\n // Count capability\n countNode(capability, summary);\n\n // Count features (but NOT stories)\n for (const feature of capability.children) {\n countNode(feature, summary);\n }\n }\n\n return summary;\n}\n\n/**\n * Count a single node in summary\n *\n * @param node - Node to count\n * @param summary - Summary object to update\n */\nfunction countNode(node: TreeNode, summary: Summary): void {\n switch (node.status) {\n case \"DONE\":\n summary.done++;\n break;\n case \"IN_PROGRESS\":\n summary.inProgress++;\n break;\n case \"OPEN\":\n summary.open++;\n break;\n }\n}\n\n/**\n * Get display number for a work item\n *\n * Per ADR-002:\n * - Capabilities: internal + 1 (dir capability-21 has internal 20)\n * - Features/Stories: as-is\n */\nfunction getDisplayNumber(node: TreeNode): number {\n return node.kind === \"capability\" ? node.number + 1 : node.number;\n}\n","/**\n * Markdown formatter for work item trees\n *\n * Renders trees with heading hierarchy and status lines.\n * Part of Feature 65 (Output Formatting), Story 43.\n */\nimport type { TreeNode, WorkItemTree } from \"../tree/types.js\";\n\n/**\n * Format tree as markdown with heading hierarchy\n *\n * Heading levels:\n * - Capabilities: #\n * - Features: ##\n * - Stories: ###\n *\n * Display numbers (per ADR-002):\n * - Capabilities: internal + 1\n * - Features/Stories: as-is\n *\n * @param tree - Work item tree to format\n * @returns Formatted markdown\n *\n * @example\n * ```typescript\n * const tree = buildTreeWithStories();\n * const output = formatMarkdown(tree);\n * // => \"# capability-21_test\\n\\nStatus: DONE\\n\\n## feature-21_test\\n...\"\n * ```\n */\nexport function formatMarkdown(tree: WorkItemTree): string {\n const sections: string[] = [];\n\n for (const node of tree.nodes) {\n formatNode(node, 1, sections);\n }\n\n return sections.join(\"\\n\\n\");\n}\n\n/**\n * Recursively format a tree node as markdown\n *\n * @param node - Current node to format\n * @param level - Heading level (1 = #, 2 = ##, 3 = ###)\n * @param sections - Output sections array\n */\nfunction formatNode(\n node: TreeNode,\n level: number,\n sections: string[]\n): void {\n const displayNumber = getDisplayNumber(node);\n const name = `${node.kind}-${displayNumber}_${node.slug}`;\n const heading = \"#\".repeat(level);\n\n sections.push(`${heading} ${name}`);\n sections.push(`Status: ${node.status}`);\n\n // Recurse for children\n for (const child of node.children) {\n formatNode(child, level + 1, sections);\n }\n}\n\n/**\n * Get display number for a work item\n *\n * Per ADR-002:\n * - Capabilities: internal + 1 (dir capability-21 has internal 20)\n * - Features/Stories: as-is\n */\nfunction getDisplayNumber(node: TreeNode): number {\n return node.kind === \"capability\" ? node.number + 1 : node.number;\n}\n","/**\n * Table formatter for work item trees\n *\n * Renders trees as aligned tables with dynamic column widths.\n * Part of Feature 65 (Output Formatting), Story 54.\n */\nimport type { TreeNode, WorkItemTree } from \"../tree/types.js\";\n\n/**\n * Table row data\n */\ninterface TableRow {\n level: string;\n number: string;\n name: string;\n status: string;\n}\n\n/**\n * Format tree as aligned table with dynamic column widths\n *\n * Columns: Level | Number | Name | Status\n * Level shows indented hierarchy:\n * - \"Capability\" (no indent)\n * - \" Feature\" (2-space indent)\n * - \" Story\" (4-space indent)\n *\n * Display numbers (per ADR-002):\n * - Capabilities: internal + 1\n * - Features/Stories: as-is\n *\n * @param tree - Work item tree to format\n * @returns Formatted table with aligned columns\n */\nexport function formatTable(tree: WorkItemTree): string {\n const rows: TableRow[] = [];\n\n // Collect all rows\n for (const node of tree.nodes) {\n collectRows(node, 0, rows);\n }\n\n // Calculate column widths\n const widths = calculateColumnWidths(rows);\n\n // Format table\n const lines: string[] = [];\n\n // Header row\n lines.push(\n formatRow(\n {\n level: \"Level\",\n number: \"Number\",\n name: \"Name\",\n status: \"Status\",\n },\n widths\n )\n );\n\n // Separator row\n lines.push(\n `|${\"-\".repeat(widths.level + 2)}|${\"-\".repeat(widths.number + 2)}|${\"-\".repeat(widths.name + 2)}|${\"-\".repeat(widths.status + 2)}|`\n );\n\n // Data rows\n for (const row of rows) {\n lines.push(formatRow(row, widths));\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Recursively collect table rows from tree\n *\n * @param node - Current node\n * @param depth - Current depth (0 = capability, 1 = feature, 2 = story)\n * @param rows - Output rows array\n */\nfunction collectRows(node: TreeNode, depth: number, rows: TableRow[]): void {\n const indent = \" \".repeat(depth);\n const levelName = getLevelName(node.kind);\n const displayNumber = getDisplayNumber(node);\n\n rows.push({\n level: `${indent}${levelName}`,\n number: String(displayNumber),\n name: node.slug,\n status: node.status,\n });\n\n // Recurse for children\n for (const child of node.children) {\n collectRows(child, depth + 1, rows);\n }\n}\n\n/**\n * Get level name with proper capitalization\n *\n * @param kind - Work item kind\n * @returns Capitalized level name\n */\nfunction getLevelName(kind: string): string {\n return kind.charAt(0).toUpperCase() + kind.slice(1);\n}\n\n/**\n * Get display number for a work item\n *\n * Per ADR-002:\n * - Capabilities: internal + 1 (dir capability-21 has internal 20)\n * - Features/Stories: as-is\n */\nfunction getDisplayNumber(node: TreeNode): number {\n return node.kind === \"capability\" ? node.number + 1 : node.number;\n}\n\n/**\n * Calculate maximum width for each column\n *\n * @param rows - All table rows including header\n * @returns Column widths\n */\nfunction calculateColumnWidths(rows: TableRow[]): {\n level: number;\n number: number;\n name: number;\n status: number;\n} {\n const widths = {\n level: \"Level\".length,\n number: \"Number\".length,\n name: \"Name\".length,\n status: \"Status\".length,\n };\n\n for (const row of rows) {\n widths.level = Math.max(widths.level, row.level.length);\n widths.number = Math.max(widths.number, row.number.length);\n widths.name = Math.max(widths.name, row.name.length);\n widths.status = Math.max(widths.status, row.status.length);\n }\n\n return widths;\n}\n\n/**\n * Format a single table row with proper padding\n *\n * @param row - Row data\n * @param widths - Column widths\n * @returns Formatted row with | delimiters\n */\nfunction formatRow(\n row: TableRow,\n widths: { level: number; number: number; name: number; status: number }\n): string {\n return `| ${row.level.padEnd(widths.level)} | ${row.number.padEnd(widths.number)} | ${row.name.padEnd(widths.name)} | ${row.status.padEnd(widths.status)} |`;\n}\n","/**\n * Text formatter for work item trees\n *\n * Renders hierarchical tree with indentation and colored status indicators.\n * Output format:\n * capability-21_name [STATUS] (colored)\n * feature-32_name [STATUS] (colored)\n * story-21_name [STATUS] (colored)\n */\nimport chalk from \"chalk\";\nimport type { TreeNode, WorkItemTree } from \"../tree/types.js\";\nimport type { WorkItemKind } from \"../types.js\";\n\n/**\n * Format work item tree as text with hierarchical indentation\n *\n * @param tree - Work item tree to format\n * @returns Formatted text output with indentation and status\n *\n * @example\n * ```typescript\n * const tree = buildTreeWithStories();\n * const output = formatText(tree);\n * // => \"capability-21_test [DONE]\\n feature-21_test [DONE]\\n story-21_test [DONE]\"\n * ```\n */\nexport function formatText(tree: WorkItemTree): string {\n const lines: string[] = [];\n\n for (const node of tree.nodes) {\n lines.push(formatNode(node, 0));\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Format a single tree node with indentation and colored status\n *\n * @param node - Tree node to format\n * @param indent - Indentation level (0 = no indent, 2 = feature, 4 = story)\n * @returns Formatted node and all children\n */\nfunction formatNode(node: TreeNode, indent: number): string {\n const lines: string[] = [];\n\n // Format current node\n const name = formatWorkItemName(node.kind, node.number, node.slug);\n const prefix = \" \".repeat(indent);\n const status = formatStatus(node.status);\n const line = `${prefix}${name} ${status}`;\n lines.push(line);\n\n // Recursively format children with increased indentation\n for (const child of node.children) {\n lines.push(formatNode(child, indent + 2));\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Format work item name for display\n *\n * Converts internal number to display number for capabilities.\n *\n * @param kind - Work item type\n * @param number - Internal BSP number\n * @param slug - URL-safe identifier\n * @returns Formatted name (e.g., \"capability-21_core-cli\")\n */\nfunction formatWorkItemName(\n kind: WorkItemKind,\n number: number,\n slug: string\n): string {\n // Capabilities: display = internal + 1\n // Features/Stories: display = internal\n const displayNumber = kind === \"capability\" ? number + 1 : number;\n return `${kind}-${displayNumber}_${slug}`;\n}\n\n/**\n * Format status with color\n *\n * Colors:\n * - DONE: green\n * - IN_PROGRESS: yellow\n * - OPEN: gray\n *\n * @param status - Work item status\n * @returns Colored status string with brackets\n */\nfunction formatStatus(status: string): string {\n switch (status) {\n case \"DONE\":\n return chalk.green(`[${status}]`);\n case \"IN_PROGRESS\":\n return chalk.yellow(`[${status}]`);\n case \"OPEN\":\n return chalk.gray(`[${status}]`);\n default:\n return `[${status}]`;\n }\n}\n","/**\n * Status command implementation\n *\n * Orchestrates all features to display project status:\n * 1. Scanner (Feature 32) - Walk specs directory\n * 2. Status (Feature 43) - Determine status for each work item\n * 3. Tree Building (Feature 54) - Build hierarchical tree\n * 4. Output Formatting (Feature 65) - Format in specified format\n */\nimport { DEFAULT_CONFIG } from \"../../config/defaults.js\";\nimport { formatJSON } from \"../../reporter/json.js\";\nimport { formatMarkdown } from \"../../reporter/markdown.js\";\nimport { formatTable } from \"../../reporter/table.js\";\nimport { formatText } from \"../../reporter/text.js\";\nimport { Scanner } from \"../../scanner/scanner.js\";\nimport { buildTree } from \"../../tree/build.js\";\n\n/**\n * Supported output formats\n */\nexport type OutputFormat = \"text\" | \"json\" | \"markdown\" | \"table\";\n\n/**\n * Options for status command\n */\nexport interface StatusOptions {\n /** Working directory (defaults to current directory) */\n cwd?: string;\n /** Output format (defaults to text) */\n format?: OutputFormat;\n}\n\n/**\n * Execute status command\n *\n * Displays current project status by:\n * - Walking specs/doing directory to find work items\n * - Determining status for each work item\n * - Building hierarchical tree structure\n * - Formatting and outputting in specified format\n *\n * @param options - Command options\n * @returns Formatted status output\n *\n * @example\n * ```typescript\n * const output = await statusCommand({ cwd: \"/path/to/project\", format: \"json\" });\n * console.log(output);\n * ```\n */\nexport async function statusCommand(\n options: StatusOptions = {},\n): Promise<string> {\n const cwd = options.cwd || process.cwd();\n const format = options.format || \"text\";\n\n // Step 1-3: Use Scanner with config-driven paths\n const scanner = new Scanner(cwd, DEFAULT_CONFIG);\n let workItems;\n try {\n workItems = await scanner.scan();\n } catch (error) {\n // Handle missing directory gracefully\n if (error instanceof Error && error.message.includes(\"ENOENT\")) {\n const doingPath =\n `${DEFAULT_CONFIG.specs.root}/${DEFAULT_CONFIG.specs.work.dir}/${DEFAULT_CONFIG.specs.work.statusDirs.doing}`;\n throw new Error(\n `Directory ${doingPath} not found.\\n\\nThis command is for legacy specs/ projects. For CODE framework projects, check the spx/ directory for specifications.`,\n );\n }\n throw error;\n }\n\n // Handle empty project\n if (workItems.length === 0) {\n return `No work items found in ${DEFAULT_CONFIG.specs.root}/${DEFAULT_CONFIG.specs.work.dir}/${DEFAULT_CONFIG.specs.work.statusDirs.doing}`;\n }\n\n // Step 4: Build hierarchical tree with status\n const tree = await buildTree(workItems);\n\n // Step 5: Format based on requested format\n switch (format) {\n case \"json\":\n return formatJSON(tree, DEFAULT_CONFIG);\n case \"markdown\":\n return formatMarkdown(tree);\n case \"table\":\n return formatTable(tree);\n case \"text\":\n default:\n return formatText(tree);\n }\n}\n","/**\n * Spec domain - Manage spec workflow\n */\nimport type { Command } from \"commander\";\nimport { nextCommand } from \"../../commands/spec/next.js\";\nimport { type OutputFormat, statusCommand } from \"../../commands/spec/status.js\";\nimport type { Domain } from \"../types.js\";\n\n/**\n * Register spec domain commands\n *\n * @param specCmd - Commander.js spec domain command\n */\nfunction registerSpecCommands(specCmd: Command): void {\n // status command\n specCmd\n .command(\"status\")\n .description(\"Get project status\")\n .option(\"--json\", \"Output as JSON\")\n .option(\"--format <format>\", \"Output format (text|json|markdown|table)\")\n .action(async (options: { json?: boolean; format?: string }) => {\n try {\n // Determine format: --json flag overrides --format option\n let format: OutputFormat = \"text\";\n if (options.json) {\n format = \"json\";\n } else if (options.format) {\n const validFormats = [\"text\", \"json\", \"markdown\", \"table\"];\n if (validFormats.includes(options.format)) {\n format = options.format as OutputFormat;\n } else {\n console.error(\n `Error: Invalid format \"${options.format}\". Must be one of: ${validFormats.join(\", \")}`,\n );\n process.exit(1);\n }\n }\n\n const output = await statusCommand({ cwd: process.cwd(), format });\n console.log(output);\n } catch (error) {\n console.error(\n \"Error:\",\n error instanceof Error ? error.message : String(error),\n );\n process.exit(1);\n }\n });\n\n // next command\n specCmd\n .command(\"next\")\n .description(\"Find next work item to work on\")\n .action(async () => {\n try {\n const output = await nextCommand({ cwd: process.cwd() });\n console.log(output);\n } catch (error) {\n console.error(\n \"Error:\",\n error instanceof Error ? error.message : String(error),\n );\n process.exit(1);\n }\n });\n}\n\n/**\n * Spec domain - Manage spec workflow\n */\nexport const specDomain: Domain = {\n name: \"spec\",\n description: \"Manage spec workflow\",\n register: (program: Command) => {\n const specCmd = program\n .command(\"spec\")\n .description(\"Manage spec workflow\");\n\n registerSpecCommands(specCmd);\n },\n};\n","/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\n'use strict';\n/**\n * Creates a JSON scanner on the given text.\n * If ignoreTrivia is set, whitespaces or comments are ignored.\n */\nexport function createScanner(text, ignoreTrivia = false) {\n const len = text.length;\n let pos = 0, value = '', tokenOffset = 0, token = 16 /* SyntaxKind.Unknown */, lineNumber = 0, lineStartOffset = 0, tokenLineStartOffset = 0, prevTokenLineStartOffset = 0, scanError = 0 /* ScanError.None */;\n function scanHexDigits(count, exact) {\n let digits = 0;\n let value = 0;\n while (digits < count || !exact) {\n let ch = text.charCodeAt(pos);\n if (ch >= 48 /* CharacterCodes._0 */ && ch <= 57 /* CharacterCodes._9 */) {\n value = value * 16 + ch - 48 /* CharacterCodes._0 */;\n }\n else if (ch >= 65 /* CharacterCodes.A */ && ch <= 70 /* CharacterCodes.F */) {\n value = value * 16 + ch - 65 /* CharacterCodes.A */ + 10;\n }\n else if (ch >= 97 /* CharacterCodes.a */ && ch <= 102 /* CharacterCodes.f */) {\n value = value * 16 + ch - 97 /* CharacterCodes.a */ + 10;\n }\n else {\n break;\n }\n pos++;\n digits++;\n }\n if (digits < count) {\n value = -1;\n }\n return value;\n }\n function setPosition(newPosition) {\n pos = newPosition;\n value = '';\n tokenOffset = 0;\n token = 16 /* SyntaxKind.Unknown */;\n scanError = 0 /* ScanError.None */;\n }\n function scanNumber() {\n let start = pos;\n if (text.charCodeAt(pos) === 48 /* CharacterCodes._0 */) {\n pos++;\n }\n else {\n pos++;\n while (pos < text.length && isDigit(text.charCodeAt(pos))) {\n pos++;\n }\n }\n if (pos < text.length && text.charCodeAt(pos) === 46 /* CharacterCodes.dot */) {\n pos++;\n if (pos < text.length && isDigit(text.charCodeAt(pos))) {\n pos++;\n while (pos < text.length && isDigit(text.charCodeAt(pos))) {\n pos++;\n }\n }\n else {\n scanError = 3 /* ScanError.UnexpectedEndOfNumber */;\n return text.substring(start, pos);\n }\n }\n let end = pos;\n if (pos < text.length && (text.charCodeAt(pos) === 69 /* CharacterCodes.E */ || text.charCodeAt(pos) === 101 /* CharacterCodes.e */)) {\n pos++;\n if (pos < text.length && text.charCodeAt(pos) === 43 /* CharacterCodes.plus */ || text.charCodeAt(pos) === 45 /* CharacterCodes.minus */) {\n pos++;\n }\n if (pos < text.length && isDigit(text.charCodeAt(pos))) {\n pos++;\n while (pos < text.length && isDigit(text.charCodeAt(pos))) {\n pos++;\n }\n end = pos;\n }\n else {\n scanError = 3 /* ScanError.UnexpectedEndOfNumber */;\n }\n }\n return text.substring(start, end);\n }\n function scanString() {\n let result = '', start = pos;\n while (true) {\n if (pos >= len) {\n result += text.substring(start, pos);\n scanError = 2 /* ScanError.UnexpectedEndOfString */;\n break;\n }\n const ch = text.charCodeAt(pos);\n if (ch === 34 /* CharacterCodes.doubleQuote */) {\n result += text.substring(start, pos);\n pos++;\n break;\n }\n if (ch === 92 /* CharacterCodes.backslash */) {\n result += text.substring(start, pos);\n pos++;\n if (pos >= len) {\n scanError = 2 /* ScanError.UnexpectedEndOfString */;\n break;\n }\n const ch2 = text.charCodeAt(pos++);\n switch (ch2) {\n case 34 /* CharacterCodes.doubleQuote */:\n result += '\\\"';\n break;\n case 92 /* CharacterCodes.backslash */:\n result += '\\\\';\n break;\n case 47 /* CharacterCodes.slash */:\n result += '/';\n break;\n case 98 /* CharacterCodes.b */:\n result += '\\b';\n break;\n case 102 /* CharacterCodes.f */:\n result += '\\f';\n break;\n case 110 /* CharacterCodes.n */:\n result += '\\n';\n break;\n case 114 /* CharacterCodes.r */:\n result += '\\r';\n break;\n case 116 /* CharacterCodes.t */:\n result += '\\t';\n break;\n case 117 /* CharacterCodes.u */:\n const ch3 = scanHexDigits(4, true);\n if (ch3 >= 0) {\n result += String.fromCharCode(ch3);\n }\n else {\n scanError = 4 /* ScanError.InvalidUnicode */;\n }\n break;\n default:\n scanError = 5 /* ScanError.InvalidEscapeCharacter */;\n }\n start = pos;\n continue;\n }\n if (ch >= 0 && ch <= 0x1f) {\n if (isLineBreak(ch)) {\n result += text.substring(start, pos);\n scanError = 2 /* ScanError.UnexpectedEndOfString */;\n break;\n }\n else {\n scanError = 6 /* ScanError.InvalidCharacter */;\n // mark as error but continue with string\n }\n }\n pos++;\n }\n return result;\n }\n function scanNext() {\n value = '';\n scanError = 0 /* ScanError.None */;\n tokenOffset = pos;\n lineStartOffset = lineNumber;\n prevTokenLineStartOffset = tokenLineStartOffset;\n if (pos >= len) {\n // at the end\n tokenOffset = len;\n return token = 17 /* SyntaxKind.EOF */;\n }\n let code = text.charCodeAt(pos);\n // trivia: whitespace\n if (isWhiteSpace(code)) {\n do {\n pos++;\n value += String.fromCharCode(code);\n code = text.charCodeAt(pos);\n } while (isWhiteSpace(code));\n return token = 15 /* SyntaxKind.Trivia */;\n }\n // trivia: newlines\n if (isLineBreak(code)) {\n pos++;\n value += String.fromCharCode(code);\n if (code === 13 /* CharacterCodes.carriageReturn */ && text.charCodeAt(pos) === 10 /* CharacterCodes.lineFeed */) {\n pos++;\n value += '\\n';\n }\n lineNumber++;\n tokenLineStartOffset = pos;\n return token = 14 /* SyntaxKind.LineBreakTrivia */;\n }\n switch (code) {\n // tokens: []{}:,\n case 123 /* CharacterCodes.openBrace */:\n pos++;\n return token = 1 /* SyntaxKind.OpenBraceToken */;\n case 125 /* CharacterCodes.closeBrace */:\n pos++;\n return token = 2 /* SyntaxKind.CloseBraceToken */;\n case 91 /* CharacterCodes.openBracket */:\n pos++;\n return token = 3 /* SyntaxKind.OpenBracketToken */;\n case 93 /* CharacterCodes.closeBracket */:\n pos++;\n return token = 4 /* SyntaxKind.CloseBracketToken */;\n case 58 /* CharacterCodes.colon */:\n pos++;\n return token = 6 /* SyntaxKind.ColonToken */;\n case 44 /* CharacterCodes.comma */:\n pos++;\n return token = 5 /* SyntaxKind.CommaToken */;\n // strings\n case 34 /* CharacterCodes.doubleQuote */:\n pos++;\n value = scanString();\n return token = 10 /* SyntaxKind.StringLiteral */;\n // comments\n case 47 /* CharacterCodes.slash */:\n const start = pos - 1;\n // Single-line comment\n if (text.charCodeAt(pos + 1) === 47 /* CharacterCodes.slash */) {\n pos += 2;\n while (pos < len) {\n if (isLineBreak(text.charCodeAt(pos))) {\n break;\n }\n pos++;\n }\n value = text.substring(start, pos);\n return token = 12 /* SyntaxKind.LineCommentTrivia */;\n }\n // Multi-line comment\n if (text.charCodeAt(pos + 1) === 42 /* CharacterCodes.asterisk */) {\n pos += 2;\n const safeLength = len - 1; // For lookahead.\n let commentClosed = false;\n while (pos < safeLength) {\n const ch = text.charCodeAt(pos);\n if (ch === 42 /* CharacterCodes.asterisk */ && text.charCodeAt(pos + 1) === 47 /* CharacterCodes.slash */) {\n pos += 2;\n commentClosed = true;\n break;\n }\n pos++;\n if (isLineBreak(ch)) {\n if (ch === 13 /* CharacterCodes.carriageReturn */ && text.charCodeAt(pos) === 10 /* CharacterCodes.lineFeed */) {\n pos++;\n }\n lineNumber++;\n tokenLineStartOffset = pos;\n }\n }\n if (!commentClosed) {\n pos++;\n scanError = 1 /* ScanError.UnexpectedEndOfComment */;\n }\n value = text.substring(start, pos);\n return token = 13 /* SyntaxKind.BlockCommentTrivia */;\n }\n // just a single slash\n value += String.fromCharCode(code);\n pos++;\n return token = 16 /* SyntaxKind.Unknown */;\n // numbers\n case 45 /* CharacterCodes.minus */:\n value += String.fromCharCode(code);\n pos++;\n if (pos === len || !isDigit(text.charCodeAt(pos))) {\n return token = 16 /* SyntaxKind.Unknown */;\n }\n // found a minus, followed by a number so\n // we fall through to proceed with scanning\n // numbers\n case 48 /* CharacterCodes._0 */:\n case 49 /* CharacterCodes._1 */:\n case 50 /* CharacterCodes._2 */:\n case 51 /* CharacterCodes._3 */:\n case 52 /* CharacterCodes._4 */:\n case 53 /* CharacterCodes._5 */:\n case 54 /* CharacterCodes._6 */:\n case 55 /* CharacterCodes._7 */:\n case 56 /* CharacterCodes._8 */:\n case 57 /* CharacterCodes._9 */:\n value += scanNumber();\n return token = 11 /* SyntaxKind.NumericLiteral */;\n // literals and unknown symbols\n default:\n // is a literal? Read the full word.\n while (pos < len && isUnknownContentCharacter(code)) {\n pos++;\n code = text.charCodeAt(pos);\n }\n if (tokenOffset !== pos) {\n value = text.substring(tokenOffset, pos);\n // keywords: true, false, null\n switch (value) {\n case 'true': return token = 8 /* SyntaxKind.TrueKeyword */;\n case 'false': return token = 9 /* SyntaxKind.FalseKeyword */;\n case 'null': return token = 7 /* SyntaxKind.NullKeyword */;\n }\n return token = 16 /* SyntaxKind.Unknown */;\n }\n // some\n value += String.fromCharCode(code);\n pos++;\n return token = 16 /* SyntaxKind.Unknown */;\n }\n }\n function isUnknownContentCharacter(code) {\n if (isWhiteSpace(code) || isLineBreak(code)) {\n return false;\n }\n switch (code) {\n case 125 /* CharacterCodes.closeBrace */:\n case 93 /* CharacterCodes.closeBracket */:\n case 123 /* CharacterCodes.openBrace */:\n case 91 /* CharacterCodes.openBracket */:\n case 34 /* CharacterCodes.doubleQuote */:\n case 58 /* CharacterCodes.colon */:\n case 44 /* CharacterCodes.comma */:\n case 47 /* CharacterCodes.slash */:\n return false;\n }\n return true;\n }\n function scanNextNonTrivia() {\n let result;\n do {\n result = scanNext();\n } while (result >= 12 /* SyntaxKind.LineCommentTrivia */ && result <= 15 /* SyntaxKind.Trivia */);\n return result;\n }\n return {\n setPosition: setPosition,\n getPosition: () => pos,\n scan: ignoreTrivia ? scanNextNonTrivia : scanNext,\n getToken: () => token,\n getTokenValue: () => value,\n getTokenOffset: () => tokenOffset,\n getTokenLength: () => pos - tokenOffset,\n getTokenStartLine: () => lineStartOffset,\n getTokenStartCharacter: () => tokenOffset - prevTokenLineStartOffset,\n getTokenError: () => scanError,\n };\n}\nfunction isWhiteSpace(ch) {\n return ch === 32 /* CharacterCodes.space */ || ch === 9 /* CharacterCodes.tab */;\n}\nfunction isLineBreak(ch) {\n return ch === 10 /* CharacterCodes.lineFeed */ || ch === 13 /* CharacterCodes.carriageReturn */;\n}\nfunction isDigit(ch) {\n return ch >= 48 /* CharacterCodes._0 */ && ch <= 57 /* CharacterCodes._9 */;\n}\nvar CharacterCodes;\n(function (CharacterCodes) {\n CharacterCodes[CharacterCodes[\"lineFeed\"] = 10] = \"lineFeed\";\n CharacterCodes[CharacterCodes[\"carriageReturn\"] = 13] = \"carriageReturn\";\n CharacterCodes[CharacterCodes[\"space\"] = 32] = \"space\";\n CharacterCodes[CharacterCodes[\"_0\"] = 48] = \"_0\";\n CharacterCodes[CharacterCodes[\"_1\"] = 49] = \"_1\";\n CharacterCodes[CharacterCodes[\"_2\"] = 50] = \"_2\";\n CharacterCodes[CharacterCodes[\"_3\"] = 51] = \"_3\";\n CharacterCodes[CharacterCodes[\"_4\"] = 52] = \"_4\";\n CharacterCodes[CharacterCodes[\"_5\"] = 53] = \"_5\";\n CharacterCodes[CharacterCodes[\"_6\"] = 54] = \"_6\";\n CharacterCodes[CharacterCodes[\"_7\"] = 55] = \"_7\";\n CharacterCodes[CharacterCodes[\"_8\"] = 56] = \"_8\";\n CharacterCodes[CharacterCodes[\"_9\"] = 57] = \"_9\";\n CharacterCodes[CharacterCodes[\"a\"] = 97] = \"a\";\n CharacterCodes[CharacterCodes[\"b\"] = 98] = \"b\";\n CharacterCodes[CharacterCodes[\"c\"] = 99] = \"c\";\n CharacterCodes[CharacterCodes[\"d\"] = 100] = \"d\";\n CharacterCodes[CharacterCodes[\"e\"] = 101] = \"e\";\n CharacterCodes[CharacterCodes[\"f\"] = 102] = \"f\";\n CharacterCodes[CharacterCodes[\"g\"] = 103] = \"g\";\n CharacterCodes[CharacterCodes[\"h\"] = 104] = \"h\";\n CharacterCodes[CharacterCodes[\"i\"] = 105] = \"i\";\n CharacterCodes[CharacterCodes[\"j\"] = 106] = \"j\";\n CharacterCodes[CharacterCodes[\"k\"] = 107] = \"k\";\n CharacterCodes[CharacterCodes[\"l\"] = 108] = \"l\";\n CharacterCodes[CharacterCodes[\"m\"] = 109] = \"m\";\n CharacterCodes[CharacterCodes[\"n\"] = 110] = \"n\";\n CharacterCodes[CharacterCodes[\"o\"] = 111] = \"o\";\n CharacterCodes[CharacterCodes[\"p\"] = 112] = \"p\";\n CharacterCodes[CharacterCodes[\"q\"] = 113] = \"q\";\n CharacterCodes[CharacterCodes[\"r\"] = 114] = \"r\";\n CharacterCodes[CharacterCodes[\"s\"] = 115] = \"s\";\n CharacterCodes[CharacterCodes[\"t\"] = 116] = \"t\";\n CharacterCodes[CharacterCodes[\"u\"] = 117] = \"u\";\n CharacterCodes[CharacterCodes[\"v\"] = 118] = \"v\";\n CharacterCodes[CharacterCodes[\"w\"] = 119] = \"w\";\n CharacterCodes[CharacterCodes[\"x\"] = 120] = \"x\";\n CharacterCodes[CharacterCodes[\"y\"] = 121] = \"y\";\n CharacterCodes[CharacterCodes[\"z\"] = 122] = \"z\";\n CharacterCodes[CharacterCodes[\"A\"] = 65] = \"A\";\n CharacterCodes[CharacterCodes[\"B\"] = 66] = \"B\";\n CharacterCodes[CharacterCodes[\"C\"] = 67] = \"C\";\n CharacterCodes[CharacterCodes[\"D\"] = 68] = \"D\";\n CharacterCodes[CharacterCodes[\"E\"] = 69] = \"E\";\n CharacterCodes[CharacterCodes[\"F\"] = 70] = \"F\";\n CharacterCodes[CharacterCodes[\"G\"] = 71] = \"G\";\n CharacterCodes[CharacterCodes[\"H\"] = 72] = \"H\";\n CharacterCodes[CharacterCodes[\"I\"] = 73] = \"I\";\n CharacterCodes[CharacterCodes[\"J\"] = 74] = \"J\";\n CharacterCodes[CharacterCodes[\"K\"] = 75] = \"K\";\n CharacterCodes[CharacterCodes[\"L\"] = 76] = \"L\";\n CharacterCodes[CharacterCodes[\"M\"] = 77] = \"M\";\n CharacterCodes[CharacterCodes[\"N\"] = 78] = \"N\";\n CharacterCodes[CharacterCodes[\"O\"] = 79] = \"O\";\n CharacterCodes[CharacterCodes[\"P\"] = 80] = \"P\";\n CharacterCodes[CharacterCodes[\"Q\"] = 81] = \"Q\";\n CharacterCodes[CharacterCodes[\"R\"] = 82] = \"R\";\n CharacterCodes[CharacterCodes[\"S\"] = 83] = \"S\";\n CharacterCodes[CharacterCodes[\"T\"] = 84] = \"T\";\n CharacterCodes[CharacterCodes[\"U\"] = 85] = \"U\";\n CharacterCodes[CharacterCodes[\"V\"] = 86] = \"V\";\n CharacterCodes[CharacterCodes[\"W\"] = 87] = \"W\";\n CharacterCodes[CharacterCodes[\"X\"] = 88] = \"X\";\n CharacterCodes[CharacterCodes[\"Y\"] = 89] = \"Y\";\n CharacterCodes[CharacterCodes[\"Z\"] = 90] = \"Z\";\n CharacterCodes[CharacterCodes[\"asterisk\"] = 42] = \"asterisk\";\n CharacterCodes[CharacterCodes[\"backslash\"] = 92] = \"backslash\";\n CharacterCodes[CharacterCodes[\"closeBrace\"] = 125] = \"closeBrace\";\n CharacterCodes[CharacterCodes[\"closeBracket\"] = 93] = \"closeBracket\";\n CharacterCodes[CharacterCodes[\"colon\"] = 58] = \"colon\";\n CharacterCodes[CharacterCodes[\"comma\"] = 44] = \"comma\";\n CharacterCodes[CharacterCodes[\"dot\"] = 46] = \"dot\";\n CharacterCodes[CharacterCodes[\"doubleQuote\"] = 34] = \"doubleQuote\";\n CharacterCodes[CharacterCodes[\"minus\"] = 45] = \"minus\";\n CharacterCodes[CharacterCodes[\"openBrace\"] = 123] = \"openBrace\";\n CharacterCodes[CharacterCodes[\"openBracket\"] = 91] = \"openBracket\";\n CharacterCodes[CharacterCodes[\"plus\"] = 43] = \"plus\";\n CharacterCodes[CharacterCodes[\"slash\"] = 47] = \"slash\";\n CharacterCodes[CharacterCodes[\"formFeed\"] = 12] = \"formFeed\";\n CharacterCodes[CharacterCodes[\"tab\"] = 9] = \"tab\";\n})(CharacterCodes || (CharacterCodes = {}));\n","export const cachedSpaces = new Array(20).fill(0).map((_, index) => {\n return ' '.repeat(index);\n});\nconst maxCachedValues = 200;\nexport const cachedBreakLinesWithSpaces = {\n ' ': {\n '\\n': new Array(maxCachedValues).fill(0).map((_, index) => {\n return '\\n' + ' '.repeat(index);\n }),\n '\\r': new Array(maxCachedValues).fill(0).map((_, index) => {\n return '\\r' + ' '.repeat(index);\n }),\n '\\r\\n': new Array(maxCachedValues).fill(0).map((_, index) => {\n return '\\r\\n' + ' '.repeat(index);\n }),\n },\n '\\t': {\n '\\n': new Array(maxCachedValues).fill(0).map((_, index) => {\n return '\\n' + '\\t'.repeat(index);\n }),\n '\\r': new Array(maxCachedValues).fill(0).map((_, index) => {\n return '\\r' + '\\t'.repeat(index);\n }),\n '\\r\\n': new Array(maxCachedValues).fill(0).map((_, index) => {\n return '\\r\\n' + '\\t'.repeat(index);\n }),\n }\n};\nexport const supportedEols = ['\\n', '\\r', '\\r\\n'];\n","/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\n'use strict';\nimport { createScanner } from './scanner';\nvar ParseOptions;\n(function (ParseOptions) {\n ParseOptions.DEFAULT = {\n allowTrailingComma: false\n };\n})(ParseOptions || (ParseOptions = {}));\n/**\n * For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index.\n */\nexport function getLocation(text, position) {\n const segments = []; // strings or numbers\n const earlyReturnException = new Object();\n let previousNode = undefined;\n const previousNodeInst = {\n value: {},\n offset: 0,\n length: 0,\n type: 'object',\n parent: undefined\n };\n let isAtPropertyKey = false;\n function setPreviousNode(value, offset, length, type) {\n previousNodeInst.value = value;\n previousNodeInst.offset = offset;\n previousNodeInst.length = length;\n previousNodeInst.type = type;\n previousNodeInst.colonOffset = undefined;\n previousNode = previousNodeInst;\n }\n try {\n visit(text, {\n onObjectBegin: (offset, length) => {\n if (position <= offset) {\n throw earlyReturnException;\n }\n previousNode = undefined;\n isAtPropertyKey = position > offset;\n segments.push(''); // push a placeholder (will be replaced)\n },\n onObjectProperty: (name, offset, length) => {\n if (position < offset) {\n throw earlyReturnException;\n }\n setPreviousNode(name, offset, length, 'property');\n segments[segments.length - 1] = name;\n if (position <= offset + length) {\n throw earlyReturnException;\n }\n },\n onObjectEnd: (offset, length) => {\n if (position <= offset) {\n throw earlyReturnException;\n }\n previousNode = undefined;\n segments.pop();\n },\n onArrayBegin: (offset, length) => {\n if (position <= offset) {\n throw earlyReturnException;\n }\n previousNode = undefined;\n segments.push(0);\n },\n onArrayEnd: (offset, length) => {\n if (position <= offset) {\n throw earlyReturnException;\n }\n previousNode = undefined;\n segments.pop();\n },\n onLiteralValue: (value, offset, length) => {\n if (position < offset) {\n throw earlyReturnException;\n }\n setPreviousNode(value, offset, length, getNodeType(value));\n if (position <= offset + length) {\n throw earlyReturnException;\n }\n },\n onSeparator: (sep, offset, length) => {\n if (position <= offset) {\n throw earlyReturnException;\n }\n if (sep === ':' && previousNode && previousNode.type === 'property') {\n previousNode.colonOffset = offset;\n isAtPropertyKey = false;\n previousNode = undefined;\n }\n else if (sep === ',') {\n const last = segments[segments.length - 1];\n if (typeof last === 'number') {\n segments[segments.length - 1] = last + 1;\n }\n else {\n isAtPropertyKey = true;\n segments[segments.length - 1] = '';\n }\n previousNode = undefined;\n }\n }\n });\n }\n catch (e) {\n if (e !== earlyReturnException) {\n throw e;\n }\n }\n return {\n path: segments,\n previousNode,\n isAtPropertyKey,\n matches: (pattern) => {\n let k = 0;\n for (let i = 0; k < pattern.length && i < segments.length; i++) {\n if (pattern[k] === segments[i] || pattern[k] === '*') {\n k++;\n }\n else if (pattern[k] !== '**') {\n return false;\n }\n }\n return k === pattern.length;\n }\n };\n}\n/**\n * Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.\n * Therefore always check the errors list to find out if the input was valid.\n */\nexport function parse(text, errors = [], options = ParseOptions.DEFAULT) {\n let currentProperty = null;\n let currentParent = [];\n const previousParents = [];\n function onValue(value) {\n if (Array.isArray(currentParent)) {\n currentParent.push(value);\n }\n else if (currentProperty !== null) {\n currentParent[currentProperty] = value;\n }\n }\n const visitor = {\n onObjectBegin: () => {\n const object = {};\n onValue(object);\n previousParents.push(currentParent);\n currentParent = object;\n currentProperty = null;\n },\n onObjectProperty: (name) => {\n currentProperty = name;\n },\n onObjectEnd: () => {\n currentParent = previousParents.pop();\n },\n onArrayBegin: () => {\n const array = [];\n onValue(array);\n previousParents.push(currentParent);\n currentParent = array;\n currentProperty = null;\n },\n onArrayEnd: () => {\n currentParent = previousParents.pop();\n },\n onLiteralValue: onValue,\n onError: (error, offset, length) => {\n errors.push({ error, offset, length });\n }\n };\n visit(text, visitor, options);\n return currentParent[0];\n}\n/**\n * Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.\n */\nexport function parseTree(text, errors = [], options = ParseOptions.DEFAULT) {\n let currentParent = { type: 'array', offset: -1, length: -1, children: [], parent: undefined }; // artificial root\n function ensurePropertyComplete(endOffset) {\n if (currentParent.type === 'property') {\n currentParent.length = endOffset - currentParent.offset;\n currentParent = currentParent.parent;\n }\n }\n function onValue(valueNode) {\n currentParent.children.push(valueNode);\n return valueNode;\n }\n const visitor = {\n onObjectBegin: (offset) => {\n currentParent = onValue({ type: 'object', offset, length: -1, parent: currentParent, children: [] });\n },\n onObjectProperty: (name, offset, length) => {\n currentParent = onValue({ type: 'property', offset, length: -1, parent: currentParent, children: [] });\n currentParent.children.push({ type: 'string', value: name, offset, length, parent: currentParent });\n },\n onObjectEnd: (offset, length) => {\n ensurePropertyComplete(offset + length); // in case of a missing value for a property: make sure property is complete\n currentParent.length = offset + length - currentParent.offset;\n currentParent = currentParent.parent;\n ensurePropertyComplete(offset + length);\n },\n onArrayBegin: (offset, length) => {\n currentParent = onValue({ type: 'array', offset, length: -1, parent: currentParent, children: [] });\n },\n onArrayEnd: (offset, length) => {\n currentParent.length = offset + length - currentParent.offset;\n currentParent = currentParent.parent;\n ensurePropertyComplete(offset + length);\n },\n onLiteralValue: (value, offset, length) => {\n onValue({ type: getNodeType(value), offset, length, parent: currentParent, value });\n ensurePropertyComplete(offset + length);\n },\n onSeparator: (sep, offset, length) => {\n if (currentParent.type === 'property') {\n if (sep === ':') {\n currentParent.colonOffset = offset;\n }\n else if (sep === ',') {\n ensurePropertyComplete(offset);\n }\n }\n },\n onError: (error, offset, length) => {\n errors.push({ error, offset, length });\n }\n };\n visit(text, visitor, options);\n const result = currentParent.children[0];\n if (result) {\n delete result.parent;\n }\n return result;\n}\n/**\n * Finds the node at the given path in a JSON DOM.\n */\nexport function findNodeAtLocation(root, path) {\n if (!root) {\n return undefined;\n }\n let node = root;\n for (let segment of path) {\n if (typeof segment === 'string') {\n if (node.type !== 'object' || !Array.isArray(node.children)) {\n return undefined;\n }\n let found = false;\n for (const propertyNode of node.children) {\n if (Array.isArray(propertyNode.children) && propertyNode.children[0].value === segment && propertyNode.children.length === 2) {\n node = propertyNode.children[1];\n found = true;\n break;\n }\n }\n if (!found) {\n return undefined;\n }\n }\n else {\n const index = segment;\n if (node.type !== 'array' || index < 0 || !Array.isArray(node.children) || index >= node.children.length) {\n return undefined;\n }\n node = node.children[index];\n }\n }\n return node;\n}\n/**\n * Gets the JSON path of the given JSON DOM node\n */\nexport function getNodePath(node) {\n if (!node.parent || !node.parent.children) {\n return [];\n }\n const path = getNodePath(node.parent);\n if (node.parent.type === 'property') {\n const key = node.parent.children[0].value;\n path.push(key);\n }\n else if (node.parent.type === 'array') {\n const index = node.parent.children.indexOf(node);\n if (index !== -1) {\n path.push(index);\n }\n }\n return path;\n}\n/**\n * Evaluates the JavaScript object of the given JSON DOM node\n */\nexport function getNodeValue(node) {\n switch (node.type) {\n case 'array':\n return node.children.map(getNodeValue);\n case 'object':\n const obj = Object.create(null);\n for (let prop of node.children) {\n const valueNode = prop.children[1];\n if (valueNode) {\n obj[prop.children[0].value] = getNodeValue(valueNode);\n }\n }\n return obj;\n case 'null':\n case 'string':\n case 'number':\n case 'boolean':\n return node.value;\n default:\n return undefined;\n }\n}\nexport function contains(node, offset, includeRightBound = false) {\n return (offset >= node.offset && offset < (node.offset + node.length)) || includeRightBound && (offset === (node.offset + node.length));\n}\n/**\n * Finds the most inner node at the given offset. If includeRightBound is set, also finds nodes that end at the given offset.\n */\nexport function findNodeAtOffset(node, offset, includeRightBound = false) {\n if (contains(node, offset, includeRightBound)) {\n const children = node.children;\n if (Array.isArray(children)) {\n for (let i = 0; i < children.length && children[i].offset <= offset; i++) {\n const item = findNodeAtOffset(children[i], offset, includeRightBound);\n if (item) {\n return item;\n }\n }\n }\n return node;\n }\n return undefined;\n}\n/**\n * Parses the given text and invokes the visitor functions for each object, array and literal reached.\n */\nexport function visit(text, visitor, options = ParseOptions.DEFAULT) {\n const _scanner = createScanner(text, false);\n // Important: Only pass copies of this to visitor functions to prevent accidental modification, and\n // to not affect visitor functions which stored a reference to a previous JSONPath\n const _jsonPath = [];\n // Depth of onXXXBegin() callbacks suppressed. onXXXEnd() decrements this if it isn't 0 already.\n // Callbacks are only called when this value is 0.\n let suppressedCallbacks = 0;\n function toNoArgVisit(visitFunction) {\n return visitFunction ? () => suppressedCallbacks === 0 && visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter()) : () => true;\n }\n function toOneArgVisit(visitFunction) {\n return visitFunction ? (arg) => suppressedCallbacks === 0 && visitFunction(arg, _scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter()) : () => true;\n }\n function toOneArgVisitWithPath(visitFunction) {\n return visitFunction ? (arg) => suppressedCallbacks === 0 && visitFunction(arg, _scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter(), () => _jsonPath.slice()) : () => true;\n }\n function toBeginVisit(visitFunction) {\n return visitFunction ?\n () => {\n if (suppressedCallbacks > 0) {\n suppressedCallbacks++;\n }\n else {\n let cbReturn = visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter(), () => _jsonPath.slice());\n if (cbReturn === false) {\n suppressedCallbacks = 1;\n }\n }\n }\n : () => true;\n }\n function toEndVisit(visitFunction) {\n return visitFunction ?\n () => {\n if (suppressedCallbacks > 0) {\n suppressedCallbacks--;\n }\n if (suppressedCallbacks === 0) {\n visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter());\n }\n }\n : () => true;\n }\n const onObjectBegin = toBeginVisit(visitor.onObjectBegin), onObjectProperty = toOneArgVisitWithPath(visitor.onObjectProperty), onObjectEnd = toEndVisit(visitor.onObjectEnd), onArrayBegin = toBeginVisit(visitor.onArrayBegin), onArrayEnd = toEndVisit(visitor.onArrayEnd), onLiteralValue = toOneArgVisitWithPath(visitor.onLiteralValue), onSeparator = toOneArgVisit(visitor.onSeparator), onComment = toNoArgVisit(visitor.onComment), onError = toOneArgVisit(visitor.onError);\n const disallowComments = options && options.disallowComments;\n const allowTrailingComma = options && options.allowTrailingComma;\n function scanNext() {\n while (true) {\n const token = _scanner.scan();\n switch (_scanner.getTokenError()) {\n case 4 /* ScanError.InvalidUnicode */:\n handleError(14 /* ParseErrorCode.InvalidUnicode */);\n break;\n case 5 /* ScanError.InvalidEscapeCharacter */:\n handleError(15 /* ParseErrorCode.InvalidEscapeCharacter */);\n break;\n case 3 /* ScanError.UnexpectedEndOfNumber */:\n handleError(13 /* ParseErrorCode.UnexpectedEndOfNumber */);\n break;\n case 1 /* ScanError.UnexpectedEndOfComment */:\n if (!disallowComments) {\n handleError(11 /* ParseErrorCode.UnexpectedEndOfComment */);\n }\n break;\n case 2 /* ScanError.UnexpectedEndOfString */:\n handleError(12 /* ParseErrorCode.UnexpectedEndOfString */);\n break;\n case 6 /* ScanError.InvalidCharacter */:\n handleError(16 /* ParseErrorCode.InvalidCharacter */);\n break;\n }\n switch (token) {\n case 12 /* SyntaxKind.LineCommentTrivia */:\n case 13 /* SyntaxKind.BlockCommentTrivia */:\n if (disallowComments) {\n handleError(10 /* ParseErrorCode.InvalidCommentToken */);\n }\n else {\n onComment();\n }\n break;\n case 16 /* SyntaxKind.Unknown */:\n handleError(1 /* ParseErrorCode.InvalidSymbol */);\n break;\n case 15 /* SyntaxKind.Trivia */:\n case 14 /* SyntaxKind.LineBreakTrivia */:\n break;\n default:\n return token;\n }\n }\n }\n function handleError(error, skipUntilAfter = [], skipUntil = []) {\n onError(error);\n if (skipUntilAfter.length + skipUntil.length > 0) {\n let token = _scanner.getToken();\n while (token !== 17 /* SyntaxKind.EOF */) {\n if (skipUntilAfter.indexOf(token) !== -1) {\n scanNext();\n break;\n }\n else if (skipUntil.indexOf(token) !== -1) {\n break;\n }\n token = scanNext();\n }\n }\n }\n function parseString(isValue) {\n const value = _scanner.getTokenValue();\n if (isValue) {\n onLiteralValue(value);\n }\n else {\n onObjectProperty(value);\n // add property name afterwards\n _jsonPath.push(value);\n }\n scanNext();\n return true;\n }\n function parseLiteral() {\n switch (_scanner.getToken()) {\n case 11 /* SyntaxKind.NumericLiteral */:\n const tokenValue = _scanner.getTokenValue();\n let value = Number(tokenValue);\n if (isNaN(value)) {\n handleError(2 /* ParseErrorCode.InvalidNumberFormat */);\n value = 0;\n }\n onLiteralValue(value);\n break;\n case 7 /* SyntaxKind.NullKeyword */:\n onLiteralValue(null);\n break;\n case 8 /* SyntaxKind.TrueKeyword */:\n onLiteralValue(true);\n break;\n case 9 /* SyntaxKind.FalseKeyword */:\n onLiteralValue(false);\n break;\n default:\n return false;\n }\n scanNext();\n return true;\n }\n function parseProperty() {\n if (_scanner.getToken() !== 10 /* SyntaxKind.StringLiteral */) {\n handleError(3 /* ParseErrorCode.PropertyNameExpected */, [], [2 /* SyntaxKind.CloseBraceToken */, 5 /* SyntaxKind.CommaToken */]);\n return false;\n }\n parseString(false);\n if (_scanner.getToken() === 6 /* SyntaxKind.ColonToken */) {\n onSeparator(':');\n scanNext(); // consume colon\n if (!parseValue()) {\n handleError(4 /* ParseErrorCode.ValueExpected */, [], [2 /* SyntaxKind.CloseBraceToken */, 5 /* SyntaxKind.CommaToken */]);\n }\n }\n else {\n handleError(5 /* ParseErrorCode.ColonExpected */, [], [2 /* SyntaxKind.CloseBraceToken */, 5 /* SyntaxKind.CommaToken */]);\n }\n _jsonPath.pop(); // remove processed property name\n return true;\n }\n function parseObject() {\n onObjectBegin();\n scanNext(); // consume open brace\n let needsComma = false;\n while (_scanner.getToken() !== 2 /* SyntaxKind.CloseBraceToken */ && _scanner.getToken() !== 17 /* SyntaxKind.EOF */) {\n if (_scanner.getToken() === 5 /* SyntaxKind.CommaToken */) {\n if (!needsComma) {\n handleError(4 /* ParseErrorCode.ValueExpected */, [], []);\n }\n onSeparator(',');\n scanNext(); // consume comma\n if (_scanner.getToken() === 2 /* SyntaxKind.CloseBraceToken */ && allowTrailingComma) {\n break;\n }\n }\n else if (needsComma) {\n handleError(6 /* ParseErrorCode.CommaExpected */, [], []);\n }\n if (!parseProperty()) {\n handleError(4 /* ParseErrorCode.ValueExpected */, [], [2 /* SyntaxKind.CloseBraceToken */, 5 /* SyntaxKind.CommaToken */]);\n }\n needsComma = true;\n }\n onObjectEnd();\n if (_scanner.getToken() !== 2 /* SyntaxKind.CloseBraceToken */) {\n handleError(7 /* ParseErrorCode.CloseBraceExpected */, [2 /* SyntaxKind.CloseBraceToken */], []);\n }\n else {\n scanNext(); // consume close brace\n }\n return true;\n }\n function parseArray() {\n onArrayBegin();\n scanNext(); // consume open bracket\n let isFirstElement = true;\n let needsComma = false;\n while (_scanner.getToken() !== 4 /* SyntaxKind.CloseBracketToken */ && _scanner.getToken() !== 17 /* SyntaxKind.EOF */) {\n if (_scanner.getToken() === 5 /* SyntaxKind.CommaToken */) {\n if (!needsComma) {\n handleError(4 /* ParseErrorCode.ValueExpected */, [], []);\n }\n onSeparator(',');\n scanNext(); // consume comma\n if (_scanner.getToken() === 4 /* SyntaxKind.CloseBracketToken */ && allowTrailingComma) {\n break;\n }\n }\n else if (needsComma) {\n handleError(6 /* ParseErrorCode.CommaExpected */, [], []);\n }\n if (isFirstElement) {\n _jsonPath.push(0);\n isFirstElement = false;\n }\n else {\n _jsonPath[_jsonPath.length - 1]++;\n }\n if (!parseValue()) {\n handleError(4 /* ParseErrorCode.ValueExpected */, [], [4 /* SyntaxKind.CloseBracketToken */, 5 /* SyntaxKind.CommaToken */]);\n }\n needsComma = true;\n }\n onArrayEnd();\n if (!isFirstElement) {\n _jsonPath.pop(); // remove array index\n }\n if (_scanner.getToken() !== 4 /* SyntaxKind.CloseBracketToken */) {\n handleError(8 /* ParseErrorCode.CloseBracketExpected */, [4 /* SyntaxKind.CloseBracketToken */], []);\n }\n else {\n scanNext(); // consume close bracket\n }\n return true;\n }\n function parseValue() {\n switch (_scanner.getToken()) {\n case 3 /* SyntaxKind.OpenBracketToken */:\n return parseArray();\n case 1 /* SyntaxKind.OpenBraceToken */:\n return parseObject();\n case 10 /* SyntaxKind.StringLiteral */:\n return parseString(true);\n default:\n return parseLiteral();\n }\n }\n scanNext();\n if (_scanner.getToken() === 17 /* SyntaxKind.EOF */) {\n if (options.allowEmptyContent) {\n return true;\n }\n handleError(4 /* ParseErrorCode.ValueExpected */, [], []);\n return false;\n }\n if (!parseValue()) {\n handleError(4 /* ParseErrorCode.ValueExpected */, [], []);\n return false;\n }\n if (_scanner.getToken() !== 17 /* SyntaxKind.EOF */) {\n handleError(9 /* ParseErrorCode.EndOfFileExpected */, [], []);\n }\n return true;\n}\n/**\n * Takes JSON with JavaScript-style comments and remove\n * them. Optionally replaces every none-newline character\n * of comments with a replaceCharacter\n */\nexport function stripComments(text, replaceCh) {\n let _scanner = createScanner(text), parts = [], kind, offset = 0, pos;\n do {\n pos = _scanner.getPosition();\n kind = _scanner.scan();\n switch (kind) {\n case 12 /* SyntaxKind.LineCommentTrivia */:\n case 13 /* SyntaxKind.BlockCommentTrivia */:\n case 17 /* SyntaxKind.EOF */:\n if (offset !== pos) {\n parts.push(text.substring(offset, pos));\n }\n if (replaceCh !== undefined) {\n parts.push(_scanner.getTokenValue().replace(/[^\\r\\n]/g, replaceCh));\n }\n offset = _scanner.getPosition();\n break;\n }\n } while (kind !== 17 /* SyntaxKind.EOF */);\n return parts.join('');\n}\nexport function getNodeType(value) {\n switch (typeof value) {\n case 'boolean': return 'boolean';\n case 'number': return 'number';\n case 'string': return 'string';\n case 'object': {\n if (!value) {\n return 'null';\n }\n else if (Array.isArray(value)) {\n return 'array';\n }\n return 'object';\n }\n default: return 'null';\n }\n}\n","/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\n'use strict';\nimport * as formatter from './impl/format';\nimport * as edit from './impl/edit';\nimport * as scanner from './impl/scanner';\nimport * as parser from './impl/parser';\n/**\n * Creates a JSON scanner on the given text.\n * If ignoreTrivia is set, whitespaces or comments are ignored.\n */\nexport const createScanner = scanner.createScanner;\nexport var ScanError;\n(function (ScanError) {\n ScanError[ScanError[\"None\"] = 0] = \"None\";\n ScanError[ScanError[\"UnexpectedEndOfComment\"] = 1] = \"UnexpectedEndOfComment\";\n ScanError[ScanError[\"UnexpectedEndOfString\"] = 2] = \"UnexpectedEndOfString\";\n ScanError[ScanError[\"UnexpectedEndOfNumber\"] = 3] = \"UnexpectedEndOfNumber\";\n ScanError[ScanError[\"InvalidUnicode\"] = 4] = \"InvalidUnicode\";\n ScanError[ScanError[\"InvalidEscapeCharacter\"] = 5] = \"InvalidEscapeCharacter\";\n ScanError[ScanError[\"InvalidCharacter\"] = 6] = \"InvalidCharacter\";\n})(ScanError || (ScanError = {}));\nexport var SyntaxKind;\n(function (SyntaxKind) {\n SyntaxKind[SyntaxKind[\"OpenBraceToken\"] = 1] = \"OpenBraceToken\";\n SyntaxKind[SyntaxKind[\"CloseBraceToken\"] = 2] = \"CloseBraceToken\";\n SyntaxKind[SyntaxKind[\"OpenBracketToken\"] = 3] = \"OpenBracketToken\";\n SyntaxKind[SyntaxKind[\"CloseBracketToken\"] = 4] = \"CloseBracketToken\";\n SyntaxKind[SyntaxKind[\"CommaToken\"] = 5] = \"CommaToken\";\n SyntaxKind[SyntaxKind[\"ColonToken\"] = 6] = \"ColonToken\";\n SyntaxKind[SyntaxKind[\"NullKeyword\"] = 7] = \"NullKeyword\";\n SyntaxKind[SyntaxKind[\"TrueKeyword\"] = 8] = \"TrueKeyword\";\n SyntaxKind[SyntaxKind[\"FalseKeyword\"] = 9] = \"FalseKeyword\";\n SyntaxKind[SyntaxKind[\"StringLiteral\"] = 10] = \"StringLiteral\";\n SyntaxKind[SyntaxKind[\"NumericLiteral\"] = 11] = \"NumericLiteral\";\n SyntaxKind[SyntaxKind[\"LineCommentTrivia\"] = 12] = \"LineCommentTrivia\";\n SyntaxKind[SyntaxKind[\"BlockCommentTrivia\"] = 13] = \"BlockCommentTrivia\";\n SyntaxKind[SyntaxKind[\"LineBreakTrivia\"] = 14] = \"LineBreakTrivia\";\n SyntaxKind[SyntaxKind[\"Trivia\"] = 15] = \"Trivia\";\n SyntaxKind[SyntaxKind[\"Unknown\"] = 16] = \"Unknown\";\n SyntaxKind[SyntaxKind[\"EOF\"] = 17] = \"EOF\";\n})(SyntaxKind || (SyntaxKind = {}));\n/**\n * For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index.\n */\nexport const getLocation = parser.getLocation;\n/**\n * Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.\n * Therefore, always check the errors list to find out if the input was valid.\n */\nexport const parse = parser.parse;\n/**\n * Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.\n */\nexport const parseTree = parser.parseTree;\n/**\n * Finds the node at the given path in a JSON DOM.\n */\nexport const findNodeAtLocation = parser.findNodeAtLocation;\n/**\n * Finds the innermost node at the given offset. If includeRightBound is set, also finds nodes that end at the given offset.\n */\nexport const findNodeAtOffset = parser.findNodeAtOffset;\n/**\n * Gets the JSON path of the given JSON DOM node\n */\nexport const getNodePath = parser.getNodePath;\n/**\n * Evaluates the JavaScript object of the given JSON DOM node\n */\nexport const getNodeValue = parser.getNodeValue;\n/**\n * Parses the given text and invokes the visitor functions for each object, array and literal reached.\n */\nexport const visit = parser.visit;\n/**\n * Takes JSON with JavaScript-style comments and remove\n * them. Optionally replaces every none-newline character\n * of comments with a replaceCharacter\n */\nexport const stripComments = parser.stripComments;\nexport var ParseErrorCode;\n(function (ParseErrorCode) {\n ParseErrorCode[ParseErrorCode[\"InvalidSymbol\"] = 1] = \"InvalidSymbol\";\n ParseErrorCode[ParseErrorCode[\"InvalidNumberFormat\"] = 2] = \"InvalidNumberFormat\";\n ParseErrorCode[ParseErrorCode[\"PropertyNameExpected\"] = 3] = \"PropertyNameExpected\";\n ParseErrorCode[ParseErrorCode[\"ValueExpected\"] = 4] = \"ValueExpected\";\n ParseErrorCode[ParseErrorCode[\"ColonExpected\"] = 5] = \"ColonExpected\";\n ParseErrorCode[ParseErrorCode[\"CommaExpected\"] = 6] = \"CommaExpected\";\n ParseErrorCode[ParseErrorCode[\"CloseBraceExpected\"] = 7] = \"CloseBraceExpected\";\n ParseErrorCode[ParseErrorCode[\"CloseBracketExpected\"] = 8] = \"CloseBracketExpected\";\n ParseErrorCode[ParseErrorCode[\"EndOfFileExpected\"] = 9] = \"EndOfFileExpected\";\n ParseErrorCode[ParseErrorCode[\"InvalidCommentToken\"] = 10] = \"InvalidCommentToken\";\n ParseErrorCode[ParseErrorCode[\"UnexpectedEndOfComment\"] = 11] = \"UnexpectedEndOfComment\";\n ParseErrorCode[ParseErrorCode[\"UnexpectedEndOfString\"] = 12] = \"UnexpectedEndOfString\";\n ParseErrorCode[ParseErrorCode[\"UnexpectedEndOfNumber\"] = 13] = \"UnexpectedEndOfNumber\";\n ParseErrorCode[ParseErrorCode[\"InvalidUnicode\"] = 14] = \"InvalidUnicode\";\n ParseErrorCode[ParseErrorCode[\"InvalidEscapeCharacter\"] = 15] = \"InvalidEscapeCharacter\";\n ParseErrorCode[ParseErrorCode[\"InvalidCharacter\"] = 16] = \"InvalidCharacter\";\n})(ParseErrorCode || (ParseErrorCode = {}));\nexport function printParseErrorCode(code) {\n switch (code) {\n case 1 /* ParseErrorCode.InvalidSymbol */: return 'InvalidSymbol';\n case 2 /* ParseErrorCode.InvalidNumberFormat */: return 'InvalidNumberFormat';\n case 3 /* ParseErrorCode.PropertyNameExpected */: return 'PropertyNameExpected';\n case 4 /* ParseErrorCode.ValueExpected */: return 'ValueExpected';\n case 5 /* ParseErrorCode.ColonExpected */: return 'ColonExpected';\n case 6 /* ParseErrorCode.CommaExpected */: return 'CommaExpected';\n case 7 /* ParseErrorCode.CloseBraceExpected */: return 'CloseBraceExpected';\n case 8 /* ParseErrorCode.CloseBracketExpected */: return 'CloseBracketExpected';\n case 9 /* ParseErrorCode.EndOfFileExpected */: return 'EndOfFileExpected';\n case 10 /* ParseErrorCode.InvalidCommentToken */: return 'InvalidCommentToken';\n case 11 /* ParseErrorCode.UnexpectedEndOfComment */: return 'UnexpectedEndOfComment';\n case 12 /* ParseErrorCode.UnexpectedEndOfString */: return 'UnexpectedEndOfString';\n case 13 /* ParseErrorCode.UnexpectedEndOfNumber */: return 'UnexpectedEndOfNumber';\n case 14 /* ParseErrorCode.InvalidUnicode */: return 'InvalidUnicode';\n case 15 /* ParseErrorCode.InvalidEscapeCharacter */: return 'InvalidEscapeCharacter';\n case 16 /* ParseErrorCode.InvalidCharacter */: return 'InvalidCharacter';\n }\n return '<unknown ParseErrorCode>';\n}\n/**\n * Computes the edit operations needed to format a JSON document.\n *\n * @param documentText The input text\n * @param range The range to format or `undefined` to format the full content\n * @param options The formatting options\n * @returns The edit operations describing the formatting changes to the original document following the format described in {@linkcode EditResult}.\n * To apply the edit operations to the input, use {@linkcode applyEdits}.\n */\nexport function format(documentText, range, options) {\n return formatter.format(documentText, range, options);\n}\n/**\n * Computes the edit operations needed to modify a value in the JSON document.\n *\n * @param documentText The input text\n * @param path The path of the value to change. The path represents either to the document root, a property or an array item.\n * If the path points to an non-existing property or item, it will be created.\n * @param value The new value for the specified property or item. If the value is undefined,\n * the property or item will be removed.\n * @param options Options\n * @returns The edit operations describing the changes to the original document, following the format described in {@linkcode EditResult}.\n * To apply the edit operations to the input, use {@linkcode applyEdits}.\n */\nexport function modify(text, path, value, options) {\n return edit.setProperty(text, path, value, options);\n}\n/**\n * Applies edits to an input string.\n * @param text The input text\n * @param edits Edit operations following the format described in {@linkcode EditResult}.\n * @returns The text with the applied edits.\n * @throws An error if the edit operations are not well-formed as described in {@linkcode EditResult}.\n */\nexport function applyEdits(text, edits) {\n let sortedEdits = edits.slice(0).sort((a, b) => {\n const diff = a.offset - b.offset;\n if (diff === 0) {\n return a.length - b.length;\n }\n return diff;\n });\n let lastModifiedOffset = text.length;\n for (let i = sortedEdits.length - 1; i >= 0; i--) {\n let e = sortedEdits[i];\n if (e.offset + e.length <= lastModifiedOffset) {\n text = edit.applyEdit(text, e);\n }\n else {\n throw new Error('Overlapping edit');\n }\n lastModifiedOffset = e.offset;\n }\n return text;\n}\n","/**\n * TypeScript scope resolution for validation.\n *\n * Provides functions to determine which directories should be validated\n * based on tsconfig.json settings, ensuring alignment between TypeScript\n * and ESLint validation.\n *\n * @module validation/config/scope\n */\n\nimport * as JSONC from \"jsonc-parser\";\nimport { existsSync, readdirSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nimport type { ScopeConfig, ValidationScope } from \"../types.js\";\n\n// =============================================================================\n// CONSTANTS\n// =============================================================================\n\n/**\n * TSConfig file paths for each validation scope.\n */\nexport const TSCONFIG_FILES = {\n full: \"tsconfig.json\",\n production: \"tsconfig.production.json\",\n} as const;\n\n// =============================================================================\n// DEPENDENCY INJECTION INTERFACES\n// =============================================================================\n\n/**\n * Dependencies for scope resolution.\n *\n * Enables dependency injection for testing without mocking.\n */\nexport interface ScopeDeps {\n readFileSync: typeof readFileSync;\n existsSync: typeof existsSync;\n readdirSync: typeof readdirSync;\n}\n\n/**\n * Default production dependencies.\n */\nexport const defaultScopeDeps: ScopeDeps = {\n readFileSync,\n existsSync,\n readdirSync,\n};\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\ninterface TypeScriptConfig {\n include?: string[];\n exclude?: string[];\n extends?: string;\n}\n\n// =============================================================================\n// INTERNAL FUNCTIONS\n// =============================================================================\n\n/**\n * Parse TypeScript configuration using proper JSONC parser.\n *\n * @param configPath - Path to tsconfig file\n * @param deps - Injectable dependencies\n * @returns Parsed TypeScript configuration\n */\nexport function parseTypeScriptConfig(\n configPath: string,\n deps: ScopeDeps = defaultScopeDeps,\n): TypeScriptConfig {\n try {\n const configContent = deps.readFileSync(configPath, \"utf-8\");\n const parsed = JSONC.parse(configContent) as TypeScriptConfig;\n return parsed;\n } catch {\n // Fallback: return minimal config and let directory detection work\n return {\n include: [\"**/*.ts\", \"**/*.tsx\"],\n exclude: [\"node_modules/**\", \".pnpm-store/**\", \"dist/**\"],\n };\n }\n}\n\n/**\n * Resolve complete TypeScript configuration including extends.\n *\n * @param scope - Validation scope\n * @param deps - Injectable dependencies\n * @returns Resolved TypeScript configuration\n */\nexport function resolveTypeScriptConfig(\n scope: ValidationScope,\n deps: ScopeDeps = defaultScopeDeps,\n): TypeScriptConfig {\n const configFile = TSCONFIG_FILES[scope];\n const config = parseTypeScriptConfig(configFile, deps);\n\n if (config.extends) {\n const baseConfig = parseTypeScriptConfig(config.extends, deps);\n return {\n include: config.include ?? baseConfig.include ?? [],\n exclude: [...(baseConfig.exclude ?? []), ...(config.exclude ?? [])],\n };\n }\n\n return {\n include: config.include ?? [],\n exclude: config.exclude ?? [],\n };\n}\n\n/**\n * Check if a directory contains TypeScript files recursively.\n *\n * @param dirPath - Directory to check\n * @param maxDepth - Maximum recursion depth\n * @param deps - Injectable dependencies\n * @returns True if directory contains TypeScript files\n */\nexport function hasTypeScriptFilesRecursive(\n dirPath: string,\n maxDepth: number = 2,\n deps: ScopeDeps = defaultScopeDeps,\n): boolean {\n if (maxDepth <= 0) return false;\n\n try {\n const items = deps.readdirSync(dirPath, { withFileTypes: true });\n\n // Check for TypeScript files in current directory\n const hasDirectTsFiles = items.some(\n (item) => item.isFile() && (item.name.endsWith(\".ts\") || item.name.endsWith(\".tsx\")),\n );\n\n if (hasDirectTsFiles) return true;\n\n // Check subdirectories (limited depth to avoid performance issues)\n const subdirs = items.filter((item) => item.isDirectory() && !item.name.startsWith(\".\"));\n for (const subdir of subdirs.slice(0, 5)) {\n // Limit to first 5 subdirs\n if (hasTypeScriptFilesRecursive(join(dirPath, subdir.name), maxDepth - 1, deps)) {\n return true;\n }\n }\n\n return false;\n } catch {\n return false;\n }\n}\n\n/**\n * Get top-level directories containing TypeScript files.\n *\n * @param config - TypeScript configuration\n * @param deps - Injectable dependencies\n * @returns Array of directory names\n */\nexport function getTopLevelDirectoriesWithTypeScript(\n config: TypeScriptConfig,\n deps: ScopeDeps = defaultScopeDeps,\n): string[] {\n const allTopLevelItems = deps.readdirSync(\".\", { withFileTypes: true });\n const directories = new Set<string>();\n\n // Find all top-level directories\n const topLevelDirs = allTopLevelItems\n .filter((item) => item.isDirectory())\n .map((item) => item.name)\n .filter((name) => !name.startsWith(\".\"));\n\n // Check if each directory should be included based on tsconfig include/exclude patterns\n for (const dir of topLevelDirs) {\n // Check if directory is explicitly excluded\n const isExcluded = config.exclude?.some((pattern) => {\n // Handle patterns like \"specs/**/*\", \"docs/**/*\"\n if (pattern.includes(\"/**\")) {\n const dirPattern = pattern.split(\"/**\")[0];\n return dirPattern === dir;\n }\n // Handle exact matches and directory patterns\n return pattern === dir || pattern.startsWith(dir + \"/\") || pattern === dir + \"/**\";\n });\n\n if (!isExcluded) {\n // Check if directory has TypeScript files\n try {\n const hasTypeScriptFiles = hasTypeScriptFilesRecursive(dir, 2, deps);\n if (hasTypeScriptFiles) {\n directories.add(dir);\n }\n } catch {\n // Directory access error, skip\n continue;\n }\n }\n }\n\n // Also add explicitly mentioned directories from include patterns\n if (config.include) {\n for (const pattern of config.include) {\n // Extract directory from patterns like \"scripts/**/*.ts\", \"tests/**/*.tsx\"\n if (pattern.includes(\"/\")) {\n const topLevelDir = pattern.split(\"/\")[0];\n if (topLevelDir && !topLevelDir.includes(\"*\") && !topLevelDir.startsWith(\".\")) {\n directories.add(topLevelDir);\n }\n }\n }\n }\n\n return Array.from(directories).sort();\n}\n\n/**\n * Get validation directories based on tsconfig files.\n *\n * @param scope - Validation scope\n * @param deps - Injectable dependencies\n * @returns Array of directory names to validate\n */\nexport function getValidationDirectories(\n scope: ValidationScope,\n deps: ScopeDeps = defaultScopeDeps,\n): string[] {\n // Get TypeScript configuration for the specified mode\n const config = resolveTypeScriptConfig(scope, deps);\n\n // Get directories that contain TypeScript files and respect tsconfig exclude patterns\n const configDirectories = getTopLevelDirectoriesWithTypeScript(config, deps);\n\n // Only include directories that actually exist\n const existingDirectories = configDirectories.filter((dir) => deps.existsSync(dir));\n\n return existingDirectories;\n}\n\n/**\n * Get authoritative validation scope configuration.\n *\n * This is the main entry point for scope resolution. Returns a ScopeConfig\n * object that can be used to configure validation tools.\n *\n * @param scope - Validation scope\n * @param deps - Injectable dependencies\n * @returns Scope configuration\n *\n * @example\n * ```typescript\n * const scopeConfig = getTypeScriptScope(\"full\");\n * console.log(scopeConfig.directories); // [\"src\", \"tests\", \"scripts\"]\n * ```\n */\nexport function getTypeScriptScope(\n scope: ValidationScope,\n deps: ScopeDeps = defaultScopeDeps,\n): ScopeConfig {\n // Use validation-focused directory selection\n const directories = getValidationDirectories(scope, deps);\n\n // Read TypeScript config for patterns\n const config = resolveTypeScriptConfig(scope, deps);\n\n return {\n directories,\n filePatterns: config.include ?? [],\n excludePatterns: config.exclude ?? [],\n };\n}\n","/**\n * Tool discovery for validation infrastructure.\n *\n * Discovers validation tools (eslint, tsc, madge, etc.) using a three-tier\n * priority system: bundled → project → global.\n *\n * @module validation/discovery/tool-finder\n */\n\nimport { execSync } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport path from \"node:path\";\n\nimport { TOOL_DISCOVERY, type ToolSource } from \"./constants.js\";\n\n/**\n * Information about a found tool.\n */\nexport interface ToolLocation {\n /** The tool name */\n tool: string;\n /** Absolute path to the tool executable or package */\n path: string;\n /** Where the tool was found */\n source: ToolSource;\n}\n\n/**\n * Information about a tool that was not found.\n */\nexport interface ToolNotFound {\n /** The tool name that was searched for */\n tool: string;\n /** Human-readable reason why the tool was not found */\n reason: string;\n}\n\n/**\n * Result of tool discovery - either found with location or not found with reason.\n */\nexport type ToolDiscoveryResult =\n | { found: true; location: ToolLocation }\n | { found: false; notFound: ToolNotFound };\n\n/**\n * Dependencies for tool discovery.\n * Enables testing without mocking by accepting controlled implementations.\n */\nexport interface ToolDiscoveryDeps {\n /**\n * Resolve a module path, returns the resolved path or null if not found.\n * @param modulePath - The module path to resolve (e.g., \"eslint/package.json\")\n */\n resolveModule: (modulePath: string) => string | null;\n\n /**\n * Check if a file exists at the given path.\n * @param filePath - The path to check\n */\n existsSync: (filePath: string) => boolean;\n\n /**\n * Find an executable in the system PATH.\n * @param tool - The tool name to find\n * @returns The absolute path to the tool, or null if not found\n */\n whichSync: (tool: string) => string | null;\n}\n\n/**\n * Create a require function for resolving modules.\n * Uses import.meta.url to create a require that resolves from this package.\n */\nconst require = createRequire(import.meta.url);\n\n/**\n * Default production dependencies for tool discovery.\n */\nexport const defaultToolDiscoveryDeps: ToolDiscoveryDeps = {\n resolveModule: (modulePath: string): string | null => {\n try {\n return require.resolve(modulePath);\n } catch {\n return null;\n }\n },\n\n existsSync: fs.existsSync,\n\n whichSync: (tool: string): string | null => {\n try {\n const result = execSync(`which ${tool}`, {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n return result.trim() || null;\n } catch {\n return null;\n }\n },\n};\n\n/**\n * Options for tool discovery.\n */\nexport interface DiscoverToolOptions {\n /**\n * Project root directory for checking project-local node_modules.\n * Defaults to current working directory.\n */\n projectRoot?: string;\n\n /**\n * Dependencies for tool discovery.\n * Defaults to production dependencies.\n */\n deps?: ToolDiscoveryDeps;\n}\n\n/**\n * Discover a validation tool using three-tier priority.\n *\n * Discovery order:\n * 1. **Bundled**: Check if the tool is bundled with spx-cli via require.resolve\n * 2. **Project**: Check project's node_modules/.bin directory\n * 3. **Global**: Check system PATH via `which` command\n *\n * @param tool - The tool name to discover (e.g., \"eslint\", \"typescript\", \"madge\")\n * @param options - Discovery options including projectRoot and dependencies\n * @returns Discovery result with found location or not found reason\n *\n * @example\n * ```typescript\n * const result = await discoverTool(\"eslint\");\n * if (result.found) {\n * console.log(`Found ${result.location.tool} at ${result.location.path}`);\n * console.log(`Source: ${result.location.source}`);\n * } else {\n * console.log(`Not found: ${result.notFound.reason}`);\n * }\n * ```\n */\nexport async function discoverTool(\n tool: string,\n options: DiscoverToolOptions = {},\n): Promise<ToolDiscoveryResult> {\n const { projectRoot = process.cwd(), deps = defaultToolDiscoveryDeps } = options;\n\n // Tier 1: Check if bundled with spx-cli\n const bundledPath = deps.resolveModule(`${tool}/package.json`);\n if (bundledPath) {\n return {\n found: true,\n location: {\n tool,\n path: path.dirname(bundledPath),\n source: TOOL_DISCOVERY.SOURCES.BUNDLED,\n },\n };\n }\n\n // Tier 2: Check project's node_modules/.bin\n const projectBinPath = path.join(projectRoot, \"node_modules\", \".bin\", tool);\n if (deps.existsSync(projectBinPath)) {\n return {\n found: true,\n location: {\n tool,\n path: projectBinPath,\n source: TOOL_DISCOVERY.SOURCES.PROJECT,\n },\n };\n }\n\n // Tier 3: Check system PATH\n const globalPath = deps.whichSync(tool);\n if (globalPath) {\n return {\n found: true,\n location: {\n tool,\n path: globalPath,\n source: TOOL_DISCOVERY.SOURCES.GLOBAL,\n },\n };\n }\n\n // Not found anywhere\n return {\n found: false,\n notFound: {\n tool,\n reason: TOOL_DISCOVERY.MESSAGES.NOT_FOUND_REASON(tool),\n },\n };\n}\n\n/**\n * Format a graceful skip message for when a tool is not found.\n *\n * @param stepName - The name of the validation step being skipped\n * @param result - The tool discovery result\n * @returns Formatted skip message, or empty string if tool was found\n *\n * @example\n * ```typescript\n * const result = await discoverTool(\"madge\");\n * const message = formatSkipMessage(\"Circular dependency check\", result);\n * // \"⏭ Skipping Circular dependency check (madge not available)\"\n * ```\n */\nexport function formatSkipMessage(\n stepName: string,\n result: ToolDiscoveryResult,\n): string {\n if (result.found) {\n return \"\";\n }\n return TOOL_DISCOVERY.MESSAGES.SKIP_FORMAT(stepName, result.notFound.tool);\n}\n","/**\n * Constants for tool discovery.\n * @module validation/discovery/constants\n */\n\n/**\n * Tool discovery constants.\n * Using constants ensures DRY principle and makes tests verify behavior via constants.\n */\nexport const TOOL_DISCOVERY = {\n /** Tool source identifiers */\n SOURCES: {\n /** Tool bundled with spx-cli */\n BUNDLED: \"bundled\",\n /** Tool in project's node_modules */\n PROJECT: \"project\",\n /** Tool in system PATH */\n GLOBAL: \"global\",\n } as const,\n\n /** Message templates */\n MESSAGES: {\n /** Prefix for skip messages */\n SKIP_PREFIX: \"\\u23ED\", // ⏭ emoji\n /**\n * Format not found reason message.\n * @param tool - The tool name that was not found\n */\n NOT_FOUND_REASON: (tool: string): string =>\n `${tool} not found in bundled deps, project node_modules, or system PATH`,\n /**\n * Format skip message for graceful degradation.\n * @param step - The validation step name\n * @param tool - The tool that was not found\n */\n SKIP_FORMAT: (step: string, tool: string): string =>\n `${TOOL_DISCOVERY.MESSAGES.SKIP_PREFIX} Skipping ${step} (${tool} not available)`,\n },\n} as const;\n\n/** Type for tool source */\nexport type ToolSource = (typeof TOOL_DISCOVERY.SOURCES)[keyof typeof TOOL_DISCOVERY.SOURCES];\n","/**\n * Circular dependency validation step.\n *\n * Uses madge to detect circular imports in the codebase.\n *\n * @module validation/steps/circular\n */\n\nimport madge from \"madge\";\n\nimport { TSCONFIG_FILES } from \"../config/scope.js\";\nimport type {\n CircularDependencyResult,\n ScopeConfig,\n ValidationContext,\n ValidationScope,\n ValidationStep,\n ValidationStepResult,\n} from \"../types.js\";\n\nimport { STEP_DESCRIPTIONS, STEP_IDS, STEP_NAMES, VALIDATION_KEYS } from \"./constants.js\";\n\n// =============================================================================\n// DEPENDENCY INJECTION INTERFACES\n// =============================================================================\n\n/**\n * Dependencies for circular dependency validation.\n *\n * Enables dependency injection for testing.\n */\nexport interface CircularDeps {\n madge: typeof madge;\n}\n\n/**\n * Default production dependencies.\n */\nexport const defaultCircularDeps: CircularDeps = {\n madge,\n};\n\n// =============================================================================\n// VALIDATION FUNCTION\n// =============================================================================\n\n/**\n * Validate circular dependencies using TypeScript-derived scope.\n *\n * @param scope - Validation scope\n * @param typescriptScope - Scope configuration from tsconfig\n * @param deps - Injectable dependencies\n * @returns Result with success status and any circular dependencies found\n *\n * @example\n * ```typescript\n * const result = await validateCircularDependencies(\"full\", scopeConfig);\n * if (!result.success) {\n * console.error(\"Found circular dependencies:\", result.circularDependencies);\n * }\n * ```\n */\nexport async function validateCircularDependencies(\n scope: ValidationScope,\n typescriptScope: ScopeConfig,\n deps: CircularDeps = defaultCircularDeps,\n): Promise<CircularDependencyResult> {\n try {\n // Use TypeScript-derived directories for perfect scope alignment\n const analyzeDirectories = typescriptScope.directories;\n\n if (analyzeDirectories.length === 0) {\n return { success: true };\n }\n\n // Use the appropriate TypeScript config based on scope\n const tsConfigFile = TSCONFIG_FILES[scope];\n\n // Convert tsconfig exclude patterns to madge excludeRegExp\n const excludeRegExps = typescriptScope.excludePatterns.map((pattern) => {\n // Remove trailing /**/* or /* for cleaner matching\n const cleanPattern = pattern.replace(/\\/\\*\\*?\\/\\*$/, \"\");\n // Escape regex special chars and create regex\n const escaped = cleanPattern.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n return new RegExp(escaped);\n });\n\n const result = await deps.madge(analyzeDirectories, {\n fileExtensions: [\"ts\", \"tsx\"],\n tsConfig: tsConfigFile,\n excludeRegExp: excludeRegExps,\n });\n\n const circular = result.circular();\n\n if (circular.length === 0) {\n return { success: true };\n } else {\n return {\n success: false,\n error: `Found ${circular.length} circular dependency cycle(s)`,\n circularDependencies: circular,\n };\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n return { success: false, error: errorMessage };\n }\n}\n\n// =============================================================================\n// VALIDATION STEP DEFINITION\n// =============================================================================\n\n/**\n * Circular dependency validation step.\n *\n * Enabled when not in file-specific mode and TypeScript validation is enabled.\n */\nexport const circularDependencyStep: ValidationStep = {\n id: STEP_IDS.CIRCULAR,\n name: STEP_NAMES.CIRCULAR,\n description: STEP_DESCRIPTIONS.CIRCULAR,\n enabled: (context: ValidationContext) =>\n !context.isFileSpecificMode && context.enabledValidations[VALIDATION_KEYS.TYPESCRIPT] === true,\n execute: async (context: ValidationContext): Promise<ValidationStepResult> => {\n const startTime = performance.now();\n try {\n const result = await validateCircularDependencies(context.scope, context.scopeConfig);\n return {\n success: result.success,\n error: result.error,\n duration: performance.now() - startTime,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n duration: performance.now() - startTime,\n };\n }\n },\n};\n","/**\n * Constants for validation steps.\n *\n * DRY constants that are verified in tests rather than using literal strings.\n *\n * @module validation/steps/constants\n */\n\n/**\n * Step identifiers for validation steps.\n */\nexport const STEP_IDS = {\n CIRCULAR: \"circular-deps\",\n ESLINT: \"eslint\",\n TYPESCRIPT: \"typescript\",\n KNIP: \"knip\",\n} as const;\n\n/**\n * Human-readable step names.\n */\nexport const STEP_NAMES = {\n CIRCULAR: \"Circular Dependencies\",\n ESLINT: \"ESLint\",\n TYPESCRIPT: \"TypeScript\",\n KNIP: \"Unused Code\",\n} as const;\n\n/**\n * Step descriptions shown during execution.\n */\nexport const STEP_DESCRIPTIONS = {\n CIRCULAR: \"Checking for circular dependencies\",\n ESLINT: \"Validating ESLint compliance\",\n TYPESCRIPT: \"Validating TypeScript\",\n KNIP: \"Detecting unused exports, dependencies, and files\",\n} as const;\n\n/**\n * Cache file locations.\n */\nexport const CACHE_PATHS = {\n ESLINT: \"dist/.eslintcache\",\n TIMINGS: \"dist/.validation-timings.json\",\n} as const;\n\n/**\n * Validation type keys.\n */\nexport const VALIDATION_KEYS = {\n TYPESCRIPT: \"TYPESCRIPT\",\n ESLINT: \"ESLINT\",\n KNIP: \"KNIP\",\n} as const;\n\n/**\n * Default enabled state for each validation type.\n */\nexport const VALIDATION_DEFAULTS = {\n [VALIDATION_KEYS.TYPESCRIPT]: true,\n [VALIDATION_KEYS.ESLINT]: true,\n [VALIDATION_KEYS.KNIP]: false,\n} as const;\n","/**\n * Circular dependency check command.\n *\n * Runs madge to detect circular dependencies.\n */\nimport { getTypeScriptScope } from \"../../validation/config/scope.js\";\nimport { discoverTool, formatSkipMessage } from \"../../validation/discovery/index.js\";\nimport { validateCircularDependencies } from \"../../validation/steps/circular.js\";\nimport type { CircularCommandOptions, ValidationCommandResult } from \"./types\";\n\n/**\n * Check for circular dependencies.\n *\n * @param options - Command options\n * @returns Command result with exit code and output\n */\nexport async function circularCommand(options: CircularCommandOptions): Promise<ValidationCommandResult> {\n const { cwd, quiet } = options;\n const startTime = Date.now();\n\n // Discover madge\n const toolResult = await discoverTool(\"madge\", { projectRoot: cwd });\n if (!toolResult.found) {\n const skipMessage = formatSkipMessage(\"circular dependency check\", toolResult);\n return { exitCode: 0, output: skipMessage, durationMs: Date.now() - startTime };\n }\n\n // Get scope configuration from tsconfig (circular always uses full scope)\n const scopeConfig = getTypeScriptScope(\"full\");\n\n // Run circular dependency validation\n const result = await validateCircularDependencies(\"full\", scopeConfig);\n const durationMs = Date.now() - startTime;\n\n // Map result to command output\n if (result.success) {\n const output = quiet ? \"\" : `Circular dependencies: ✓ None found`;\n return { exitCode: 0, output, durationMs };\n } else {\n // Format circular dependency output\n let output = result.error ?? \"Circular dependencies found\";\n if (result.circularDependencies && result.circularDependencies.length > 0) {\n const cycles = result.circularDependencies\n .map((cycle) => ` ${cycle.join(\" → \")}`)\n .join(\"\\n\");\n output = `Circular dependencies found:\\n${cycles}`;\n }\n return { exitCode: 1, output, durationMs };\n }\n}\n","/**\n * Output formatting functions for validation commands.\n *\n * Pure functions that format validation results for CLI display.\n * These functions have no side effects and are fully testable.\n */\n\n/** Threshold in milliseconds for switching from ms to seconds display */\nexport const DURATION_THRESHOLD_MS = 1000;\n\n/** Symbols used in validation output */\nexport const VALIDATION_SYMBOLS = {\n SUCCESS: \"✓\",\n FAILURE: \"✗\",\n} as const;\n\n/**\n * Format a duration in milliseconds for display.\n *\n * @param ms - Duration in milliseconds\n * @returns Formatted string (e.g., \"500ms\" or \"1.5s\")\n */\nexport function formatDuration(ms: number): string {\n if (ms < DURATION_THRESHOLD_MS) {\n return `${ms}ms`;\n }\n const seconds = ms / 1000;\n return `${seconds.toFixed(1)}s`;\n}\n\n/** Options for formatting a validation step output */\nexport interface FormatStepOptions {\n /** Current step number (1-indexed) */\n stepNumber: number;\n /** Total number of steps */\n totalSteps: number;\n /** Name of the validation step */\n name: string;\n /** Result message (e.g., \"✓ No issues found\") */\n result: string;\n /** Duration in milliseconds */\n durationMs: number;\n}\n\n/**\n * Format a validation step result for display.\n *\n * @param options - Step formatting options\n * @returns Formatted string (e.g., \"[1/4] ESLint: ✓ No issues found (0.8s)\")\n */\nexport function formatStepOutput(options: FormatStepOptions): string {\n const { stepNumber, totalSteps, name, result, durationMs } = options;\n const duration = formatDuration(durationMs);\n return `[${stepNumber}/${totalSteps}] ${name}: ${result} (${duration})`;\n}\n\n/** Options for formatting the validation summary */\nexport interface FormatSummaryOptions {\n /** Whether all required validations passed */\n success: boolean;\n /** Total duration in milliseconds */\n totalDurationMs: number;\n}\n\n/**\n * Format the final validation summary.\n *\n * @param options - Summary formatting options\n * @returns Formatted string (e.g., \"✓ Validation passed (2.7s total)\")\n */\nexport function formatSummary(options: FormatSummaryOptions): string {\n const { success, totalDurationMs } = options;\n const symbol = success ? VALIDATION_SYMBOLS.SUCCESS : VALIDATION_SYMBOLS.FAILURE;\n const status = success ? \"passed\" : \"failed\";\n const duration = formatDuration(totalDurationMs);\n return `${symbol} Validation ${status} (${duration} total)`;\n}\n","/**\n * ESLint validation step.\n *\n * Validates code against ESLint rules with automatic TypeScript scope alignment.\n *\n * @module validation/steps/eslint\n */\n\nimport { spawn } from \"node:child_process\";\n\nimport type {\n ExecutionMode,\n ProcessRunner,\n ValidationContext,\n ValidationStep,\n ValidationStepResult,\n} from \"../types.js\";\nimport { EXECUTION_MODES, VALIDATION_SCOPES } from \"../types.js\";\n\nimport { CACHE_PATHS, STEP_DESCRIPTIONS, STEP_IDS, STEP_NAMES, VALIDATION_KEYS } from \"./constants.js\";\n\n// =============================================================================\n// DEFAULT DEPENDENCIES\n// =============================================================================\n\n/**\n * Default production process runner for ESLint.\n */\nexport const defaultEslintProcessRunner: ProcessRunner = { spawn };\n\n// =============================================================================\n// PURE ARGUMENT BUILDER\n// =============================================================================\n\n/**\n * Build ESLint CLI arguments based on validation context.\n *\n * Pure function for testability - can be verified at Level 1.\n *\n * @param context - Context for building arguments\n * @returns Array of ESLint CLI arguments\n *\n * @example\n * ```typescript\n * const args = buildEslintArgs({\n * validatedFiles: [\"src/index.ts\"],\n * mode: \"write\",\n * cacheFile: \"dist/.eslintcache\",\n * });\n * // Returns: [\"eslint\", \"--config\", \"eslint.config.ts\", \"--cache\", ...]\n * ```\n */\nexport function buildEslintArgs(context: {\n validatedFiles?: string[];\n mode?: ExecutionMode;\n cacheFile: string;\n}): string[] {\n const { validatedFiles, mode, cacheFile } = context;\n const fixArg = mode === EXECUTION_MODES.WRITE ? [\"--fix\"] : [];\n const cacheArgs = [\"--cache\", \"--cache-location\", cacheFile];\n\n if (validatedFiles && validatedFiles.length > 0) {\n return [\"eslint\", \"--config\", \"eslint.config.ts\", ...cacheArgs, ...fixArg, \"--\", ...validatedFiles];\n }\n return [\"eslint\", \".\", \"--config\", \"eslint.config.ts\", ...cacheArgs, ...fixArg];\n}\n\n// =============================================================================\n// VALIDATION FUNCTION\n// =============================================================================\n\n/**\n * Validate ESLint compliance using automatic TypeScript scope alignment.\n *\n * @param context - Validation context\n * @param runner - Injectable process runner\n * @returns Promise resolving to validation result\n *\n * @example\n * ```typescript\n * const result = await validateESLint(context);\n * if (!result.success) {\n * console.error(\"ESLint failed:\", result.error);\n * }\n * ```\n */\nexport async function validateESLint(\n context: ValidationContext,\n runner: ProcessRunner = defaultEslintProcessRunner,\n): Promise<{\n success: boolean;\n error?: string;\n}> {\n const { scope, validatedFiles, mode } = context;\n\n return new Promise((resolve) => {\n // Set environment variable to control ESLint scope\n if (!validatedFiles || validatedFiles.length === 0) {\n if (scope === VALIDATION_SCOPES.PRODUCTION) {\n process.env.ESLINT_PRODUCTION_ONLY = \"1\";\n } else {\n delete process.env.ESLINT_PRODUCTION_ONLY;\n }\n }\n\n // Build ESLint arguments\n const eslintArgs = buildEslintArgs({\n validatedFiles,\n mode,\n cacheFile: CACHE_PATHS.ESLINT,\n });\n\n const eslintProcess = runner.spawn(\"npx\", eslintArgs, {\n cwd: process.cwd(),\n stdio: \"inherit\",\n });\n\n eslintProcess.on(\"close\", (code) => {\n if (code === 0) {\n resolve({ success: true });\n } else {\n resolve({ success: false, error: `ESLint exited with code ${code}` });\n }\n });\n\n eslintProcess.on(\"error\", (error) => {\n resolve({ success: false, error: error.message });\n });\n });\n}\n\n// =============================================================================\n// ENVIRONMENT CHECK\n// =============================================================================\n\n/**\n * Check if a validation type is enabled via environment variable.\n *\n * @param envVarKey - Validation key (TYPESCRIPT, ESLINT, KNIP)\n * @param defaults - Default enabled states\n * @returns True if the validation is enabled\n */\nexport function validationEnabled(\n envVarKey: string,\n defaults: Record<string, boolean> = {},\n): boolean {\n const envVar = `${envVarKey}_VALIDATION_ENABLED`;\n const explicitlyDisabled = process.env[envVar] === \"0\";\n const explicitlyEnabled = process.env[envVar] === \"1\";\n\n const defaultValue = defaults[envVarKey] ?? true;\n if (defaultValue) {\n return !explicitlyDisabled;\n }\n return explicitlyEnabled;\n}\n\n// =============================================================================\n// VALIDATION STEP DEFINITION\n// =============================================================================\n\n/**\n * ESLint validation step.\n *\n * Enabled when ESLint validation is enabled in the context.\n */\nexport const eslintStep: ValidationStep = {\n id: STEP_IDS.ESLINT,\n name: STEP_NAMES.ESLINT,\n description: STEP_DESCRIPTIONS.ESLINT,\n enabled: (context: ValidationContext) =>\n context.enabledValidations[VALIDATION_KEYS.ESLINT] === true\n && validationEnabled(VALIDATION_KEYS.ESLINT),\n execute: async (context: ValidationContext): Promise<ValidationStepResult> => {\n const startTime = performance.now();\n try {\n const result = await validateESLint(context);\n return {\n success: result.success,\n error: result.error,\n duration: performance.now() - startTime,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n duration: performance.now() - startTime,\n };\n }\n },\n};\n","/**\n * Shared types for the validation module.\n *\n * These types define the contracts for validation steps and their dependencies,\n * enabling dependency injection for testability (per ADR-001).\n *\n * @module validation/types\n */\n\nimport type { ChildProcess, SpawnOptions } from \"node:child_process\";\n\n// =============================================================================\n// DEPENDENCY INJECTION INTERFACES\n// =============================================================================\n\n/**\n * Interface for subprocess execution.\n *\n * Enables dependency injection for testing - production code uses real spawn,\n * tests can provide controlled implementations.\n *\n * @example\n * ```typescript\n * // Production usage\n * const runner: ProcessRunner = { spawn };\n *\n * // Test usage with controlled implementation\n * const testRunner: ProcessRunner = {\n * spawn: (cmd, args, opts) => createMockProcess({ exitCode: 0 }),\n * };\n * ```\n */\nexport interface ProcessRunner {\n spawn(command: string, args: readonly string[], options?: SpawnOptions): ChildProcess;\n}\n\n// =============================================================================\n// VALIDATION SCOPE\n// =============================================================================\n\n/**\n * Validation scope constants.\n *\n * @example\n * ```typescript\n * import { VALIDATION_SCOPES } from \"./types.js\";\n * const scope = VALIDATION_SCOPES.FULL; // \"full\"\n * ```\n */\nexport const VALIDATION_SCOPES = {\n /** Validate entire codebase including tests and scripts */\n FULL: \"full\",\n /** Validate production files only */\n PRODUCTION: \"production\",\n} as const;\n\n/** Type for validation scope values */\nexport type ValidationScope = (typeof VALIDATION_SCOPES)[keyof typeof VALIDATION_SCOPES];\n\n/**\n * Configuration for validation scope.\n *\n * Derived from tsconfig.json settings to ensure alignment between\n * TypeScript and ESLint validation.\n */\nexport interface ScopeConfig {\n /** Directories to include in validation */\n directories: string[];\n /** File patterns to match (from tsconfig include) */\n filePatterns: string[];\n /** Patterns to exclude from validation */\n excludePatterns: string[];\n}\n\n// =============================================================================\n// EXECUTION MODE\n// =============================================================================\n\n/**\n * Execution mode constants.\n */\nexport const EXECUTION_MODES = {\n /** Read-only mode - report errors without fixing */\n READ: \"read\",\n /** Write mode - fix errors when possible (e.g., eslint --fix) */\n WRITE: \"write\",\n} as const;\n\n/** Type for execution mode values */\nexport type ExecutionMode = (typeof EXECUTION_MODES)[keyof typeof EXECUTION_MODES];\n\n// =============================================================================\n// VALIDATION CONTEXT\n// =============================================================================\n\n/**\n * Context object passed to validation steps.\n *\n * Contains all information needed to execute a validation step,\n * enabling pure functions that don't rely on global state.\n */\nexport interface ValidationContext {\n /** Root directory of the project being validated */\n projectRoot: string;\n /** Execution mode (read-only or write/fix) */\n mode?: ExecutionMode;\n /** Validation scope (full or production) */\n scope: ValidationScope;\n /** Scope configuration derived from tsconfig */\n scopeConfig: ScopeConfig;\n /** Which validations are enabled */\n enabledValidations: Partial<Record<string, boolean>>;\n /** Specific files to validate (if file-specific mode) */\n validatedFiles?: string[];\n /** Whether running in file-specific mode */\n isFileSpecificMode: boolean;\n}\n\n// =============================================================================\n// VALIDATION RESULTS\n// =============================================================================\n\n/**\n * Result from a validation step.\n *\n * All validation steps return this structure to enable consistent\n * result handling and progress reporting.\n */\nexport interface ValidationStepResult {\n /** Whether the validation passed */\n success: boolean;\n /** Error message if validation failed */\n error?: string;\n /** Duration of the validation step in milliseconds */\n duration: number;\n /** Whether the step was skipped (e.g., tool not available) */\n skipped?: boolean;\n}\n\n/**\n * Result from circular dependency validation.\n *\n * Extends ValidationStepResult with circular dependency details.\n */\nexport interface CircularDependencyResult {\n /** Whether no circular dependencies were found */\n success: boolean;\n /** Error message if circular dependencies found */\n error?: string;\n /** The circular dependency cycles found */\n circularDependencies?: string[][];\n}\n\n// =============================================================================\n// VALIDATION STEP INTERFACE\n// =============================================================================\n\n/**\n * Definition of a validation step.\n *\n * Validation steps are pluggable units that can be enabled/disabled\n * and executed in sequence.\n */\nexport interface ValidationStep {\n /** Unique identifier for the step */\n id: string;\n /** Human-readable name for progress reporting */\n name: string;\n /** Description shown during execution */\n description: string;\n /** Function to determine if step should run */\n enabled: (context: ValidationContext) => boolean;\n /** Function to execute the validation */\n execute: (context: ValidationContext) => Promise<ValidationStepResult>;\n}\n","/**\n * Knip validation step.\n *\n * Detects unused exports, dependencies, and files using knip.\n *\n * @module validation/steps/knip\n */\n\nimport { spawn } from \"node:child_process\";\n\nimport type { ProcessRunner, ScopeConfig, ValidationContext, ValidationStep, ValidationStepResult } from \"../types.js\";\n\nimport { STEP_DESCRIPTIONS, STEP_IDS, STEP_NAMES, VALIDATION_DEFAULTS, VALIDATION_KEYS } from \"./constants.js\";\nimport { validationEnabled } from \"./eslint.js\";\n\n// =============================================================================\n// DEFAULT DEPENDENCIES\n// =============================================================================\n\n/**\n * Default production process runner for Knip.\n */\nexport const defaultKnipProcessRunner: ProcessRunner = { spawn };\n\n// =============================================================================\n// VALIDATION FUNCTION\n// =============================================================================\n\n/**\n * Validate unused code using knip with TypeScript-derived scope.\n *\n * @param scope - Validation scope\n * @param typescriptScope - Scope configuration from tsconfig\n * @param runner - Injectable process runner\n * @returns Promise resolving to validation result\n *\n * @example\n * ```typescript\n * const result = await validateKnip(\"full\", scopeConfig);\n * if (!result.success) {\n * console.error(\"Knip found issues:\", result.error);\n * }\n * ```\n */\nexport async function validateKnip(\n typescriptScope: ScopeConfig,\n runner: ProcessRunner = defaultKnipProcessRunner,\n): Promise<{\n success: boolean;\n error?: string;\n}> {\n try {\n // Use TypeScript-derived directories for perfect scope alignment\n const analyzeDirectories = typescriptScope.directories;\n\n if (analyzeDirectories.length === 0) {\n return { success: true };\n }\n\n return new Promise((resolve) => {\n const knipProcess = runner.spawn(\"npx\", [\"knip\"], {\n cwd: process.cwd(),\n stdio: \"pipe\",\n });\n\n let knipOutput = \"\";\n let knipError = \"\";\n\n knipProcess.stdout?.on(\"data\", (data: Buffer) => {\n knipOutput += data.toString();\n });\n\n knipProcess.stderr?.on(\"data\", (data: Buffer) => {\n knipError += data.toString();\n });\n\n knipProcess.on(\"close\", (code) => {\n if (code === 0) {\n resolve({ success: true });\n } else {\n const errorOutput = knipOutput || knipError || \"Unused code detected\";\n resolve({\n success: false,\n error: errorOutput,\n });\n }\n });\n\n knipProcess.on(\"error\", (error) => {\n resolve({ success: false, error: error.message });\n });\n });\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n return { success: false, error: errorMessage };\n }\n}\n\n// =============================================================================\n// VALIDATION STEP DEFINITION\n// =============================================================================\n\n/**\n * Knip unused code validation step.\n *\n * Enabled when knip validation is enabled and not in file-specific mode.\n * Knip is disabled by default.\n */\nexport const knipStep: ValidationStep = {\n id: STEP_IDS.KNIP,\n name: STEP_NAMES.KNIP,\n description: STEP_DESCRIPTIONS.KNIP,\n enabled: (context: ValidationContext) =>\n context.enabledValidations[VALIDATION_KEYS.KNIP] === true\n && validationEnabled(VALIDATION_KEYS.KNIP, VALIDATION_DEFAULTS)\n && !context.isFileSpecificMode,\n execute: async (context: ValidationContext): Promise<ValidationStepResult> => {\n const startTime = performance.now();\n try {\n const result = await validateKnip(context.scopeConfig);\n return {\n success: result.success,\n error: result.error,\n duration: performance.now() - startTime,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n duration: performance.now() - startTime,\n };\n }\n },\n};\n","/**\n * Knip command for detecting unused code.\n *\n * Runs knip to find unused exports, dependencies, and files.\n * Disabled by default - enable with KNIP_VALIDATION_ENABLED=1.\n */\nimport { getTypeScriptScope } from \"../../validation/config/scope.js\";\nimport { discoverTool, formatSkipMessage } from \"../../validation/discovery/index.js\";\nimport { validationEnabled } from \"../../validation/steps/eslint.js\";\nimport { validateKnip } from \"../../validation/steps/knip.js\";\nimport type { KnipCommandOptions, ValidationCommandResult } from \"./types\";\n\n/**\n * Detect unused code with knip.\n *\n * @param options - Command options\n * @returns Command result with exit code and output\n */\nexport async function knipCommand(options: KnipCommandOptions): Promise<ValidationCommandResult> {\n const { cwd, quiet } = options;\n const startTime = Date.now();\n\n // Knip is disabled by default - check if explicitly enabled\n if (!validationEnabled(\"KNIP\", { KNIP: false })) {\n const output = quiet ? \"\" : \"Knip: skipped (disabled by default, set KNIP_VALIDATION_ENABLED=1 to enable)\";\n return { exitCode: 0, output, durationMs: Date.now() - startTime };\n }\n\n // Discover knip\n const toolResult = await discoverTool(\"knip\", { projectRoot: cwd });\n if (!toolResult.found) {\n const skipMessage = formatSkipMessage(\"unused code detection\", toolResult);\n return { exitCode: 0, output: skipMessage, durationMs: Date.now() - startTime };\n }\n\n // Get scope configuration from tsconfig (knip uses full scope)\n const scopeConfig = getTypeScriptScope(\"full\");\n\n // Run knip validation\n const result = await validateKnip(scopeConfig);\n const durationMs = Date.now() - startTime;\n\n // Map result to command output\n if (result.success) {\n const output = quiet ? \"\" : `Knip: ✓ No unused code found`;\n return { exitCode: 0, output, durationMs };\n } else {\n const output = result.error ?? \"Unused code found\";\n return { exitCode: 1, output, durationMs };\n }\n}\n","/**\n * ESLint validation command.\n *\n * Runs ESLint for code quality checks.\n */\nimport { getTypeScriptScope } from \"../../validation/config/scope.js\";\nimport { discoverTool, formatSkipMessage } from \"../../validation/discovery/index.js\";\nimport { validateESLint } from \"../../validation/steps/eslint.js\";\nimport type { ValidationContext } from \"../../validation/types.js\";\nimport type { LintCommandOptions, ValidationCommandResult } from \"./types\";\n\n/**\n * Run ESLint validation.\n *\n * @param options - Command options\n * @returns Command result with exit code and output\n */\nexport async function lintCommand(options: LintCommandOptions): Promise<ValidationCommandResult> {\n const { cwd, scope = \"full\", files, fix, quiet } = options;\n const startTime = Date.now();\n\n // Discover eslint\n const toolResult = await discoverTool(\"eslint\", { projectRoot: cwd });\n if (!toolResult.found) {\n const skipMessage = formatSkipMessage(\"ESLint\", toolResult);\n return { exitCode: 0, output: skipMessage, durationMs: Date.now() - startTime };\n }\n\n // Get scope configuration from tsconfig\n const scopeConfig = getTypeScriptScope(scope);\n\n // Build validation context\n const context: ValidationContext = {\n projectRoot: cwd,\n scope,\n scopeConfig,\n mode: fix ? \"write\" : \"read\",\n enabledValidations: { ESLINT: true },\n validatedFiles: files,\n isFileSpecificMode: Boolean(files && files.length > 0),\n };\n\n // Run ESLint validation\n const result = await validateESLint(context);\n const durationMs = Date.now() - startTime;\n\n // Map result to command output\n if (result.success) {\n const output = quiet ? \"\" : `ESLint: ✓ No issues found`;\n return { exitCode: 0, output, durationMs };\n } else {\n const output = result.error ?? \"ESLint validation failed\";\n return { exitCode: 1, output, durationMs };\n }\n}\n","/**\n * TypeScript validation step.\n *\n * Validates TypeScript code using the tsc compiler.\n *\n * @module validation/steps/typescript\n */\n\nimport { spawn } from \"node:child_process\";\nimport { existsSync, mkdirSync, rmSync, writeFileSync } from \"node:fs\";\nimport { mkdtemp } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { isAbsolute, join } from \"node:path\";\n\nimport { TSCONFIG_FILES } from \"../config/scope.js\";\nimport type {\n ProcessRunner,\n ScopeConfig,\n ValidationContext,\n ValidationScope,\n ValidationStep,\n ValidationStepResult,\n} from \"../types.js\";\nimport { VALIDATION_SCOPES } from \"../types.js\";\n\nimport { STEP_DESCRIPTIONS, STEP_IDS, STEP_NAMES, VALIDATION_KEYS } from \"./constants.js\";\nimport { validationEnabled } from \"./eslint.js\";\n\n// =============================================================================\n// DEFAULT DEPENDENCIES\n// =============================================================================\n\n/**\n * Default production process runner for TypeScript.\n */\nexport const defaultTypeScriptProcessRunner: ProcessRunner = { spawn };\n\n/**\n * Dependencies for file-specific TypeScript validation.\n */\nexport interface TypeScriptDeps {\n mkdtemp: typeof mkdtemp;\n writeFileSync: typeof writeFileSync;\n rmSync: typeof rmSync;\n existsSync: typeof existsSync;\n mkdirSync: typeof mkdirSync;\n}\n\n/**\n * Default production dependencies.\n */\nexport const defaultTypeScriptDeps: TypeScriptDeps = {\n mkdtemp,\n writeFileSync,\n rmSync,\n existsSync,\n mkdirSync,\n};\n\n// =============================================================================\n// PURE ARGUMENT BUILDER\n// =============================================================================\n\n/**\n * Build TypeScript CLI arguments based on validation scope.\n *\n * Pure function for testability - can be verified at Level 1.\n *\n * @param context - Context for building arguments\n * @returns Array of tsc CLI arguments\n *\n * @example\n * ```typescript\n * const args = buildTypeScriptArgs({ scope: \"full\", configFile: \"tsconfig.json\" });\n * // Returns: [\"tsc\", \"--noEmit\"]\n * ```\n */\nexport function buildTypeScriptArgs(context: { scope: ValidationScope; configFile: string }): string[] {\n const { scope, configFile } = context;\n return scope === VALIDATION_SCOPES.FULL ? [\"tsc\", \"--noEmit\"] : [\"tsc\", \"--project\", configFile];\n}\n\n// =============================================================================\n// FILE-SPECIFIC VALIDATION SUPPORT\n// =============================================================================\n\n/**\n * Create a temporary TypeScript configuration file for file-specific validation.\n *\n * @param scope - Validation scope\n * @param files - Files to validate\n * @param deps - Injectable dependencies\n * @returns Config path and cleanup function\n */\nexport async function createFileSpecificTsconfig(\n scope: ValidationScope,\n files: string[],\n deps: TypeScriptDeps = defaultTypeScriptDeps,\n): Promise<{ configPath: string; tempDir: string; cleanup: () => void }> {\n // Create temporary directory\n const tempDir = await deps.mkdtemp(join(tmpdir(), \"validate-ts-\"));\n const configPath = join(tempDir, \"tsconfig.json\");\n\n // Get base config file\n const baseConfigFile = TSCONFIG_FILES[scope];\n\n // Ensure all file paths are absolute\n const projectRoot = process.cwd();\n const absoluteFiles = files.map((file) => (isAbsolute(file) ? file : join(projectRoot, file)));\n\n // Create temporary tsconfig that extends the base config\n const tempConfig = {\n extends: join(projectRoot, baseConfigFile),\n files: absoluteFiles,\n include: [],\n exclude: [],\n compilerOptions: {\n noEmit: true,\n typeRoots: [join(projectRoot, \"node_modules\", \"@types\")],\n types: [\"node\"],\n },\n };\n\n // Write temporary config\n deps.writeFileSync(configPath, JSON.stringify(tempConfig, null, 2));\n\n // Return config path and cleanup function\n const cleanup = () => {\n try {\n deps.rmSync(tempDir, { recursive: true, force: true });\n } catch {\n // Cleanup error - don't fail validation\n }\n };\n\n return { configPath, tempDir, cleanup };\n}\n\n// =============================================================================\n// VALIDATION FUNCTION\n// =============================================================================\n\n/**\n * Validate TypeScript using authoritative configuration.\n *\n * @param scope - Validation scope\n * @param typescriptScope - Scope configuration from tsconfig\n * @param files - Optional specific files to validate\n * @param runner - Injectable process runner\n * @param deps - Injectable TypeScript dependencies\n * @returns Promise resolving to validation result\n *\n * @example\n * ```typescript\n * const result = await validateTypeScript(\"full\", scopeConfig);\n * if (!result.success) {\n * console.error(\"TypeScript failed:\", result.error);\n * }\n * ```\n */\nexport async function validateTypeScript(\n scope: ValidationScope,\n typescriptScope: ScopeConfig,\n files?: string[],\n runner: ProcessRunner = defaultTypeScriptProcessRunner,\n deps: TypeScriptDeps = defaultTypeScriptDeps,\n): Promise<{\n success: boolean;\n error?: string;\n skipped?: boolean;\n}> {\n const configFile = TSCONFIG_FILES[scope];\n\n // Determine tool and arguments based on whether specific files are provided\n let tool: string;\n let tscArgs: string[];\n\n if (files && files.length > 0) {\n // File-specific validation using custom temporary tsconfig\n const { configPath, cleanup } = await createFileSpecificTsconfig(scope, files, deps);\n\n try {\n return await new Promise((resolve) => {\n const tscProcess = runner.spawn(\"npx\", [\"tsc\", \"--project\", configPath], {\n cwd: process.cwd(),\n stdio: \"inherit\",\n });\n\n tscProcess.on(\"close\", (code) => {\n cleanup();\n if (code === 0) {\n resolve({ success: true, skipped: false });\n } else {\n resolve({ success: false, error: `TypeScript exited with code ${code}` });\n }\n });\n\n tscProcess.on(\"error\", (error) => {\n cleanup();\n resolve({ success: false, error: error.message });\n });\n });\n } catch (error) {\n cleanup();\n const errorMessage = error instanceof Error ? error.message : String(error);\n return { success: false, error: `Failed to create temporary config: ${errorMessage}` };\n }\n } else {\n // Full validation using tsc\n tool = \"npx\";\n tscArgs = buildTypeScriptArgs({ scope, configFile });\n }\n\n return new Promise((resolve) => {\n const tscProcess = runner.spawn(tool, tscArgs, {\n cwd: process.cwd(),\n stdio: \"inherit\",\n });\n\n tscProcess.on(\"close\", (code) => {\n if (code === 0) {\n resolve({ success: true, skipped: false });\n } else {\n resolve({ success: false, error: `TypeScript exited with code ${code}` });\n }\n });\n\n tscProcess.on(\"error\", (error) => {\n resolve({ success: false, error: error.message });\n });\n });\n}\n\n// =============================================================================\n// VALIDATION STEP DEFINITION\n// =============================================================================\n\n/**\n * TypeScript validation step.\n *\n * Enabled when TypeScript validation is enabled in the context.\n */\nexport const typescriptStep: ValidationStep = {\n id: STEP_IDS.TYPESCRIPT,\n name: STEP_NAMES.TYPESCRIPT,\n description: STEP_DESCRIPTIONS.TYPESCRIPT,\n enabled: (context: ValidationContext) =>\n context.enabledValidations[VALIDATION_KEYS.TYPESCRIPT] === true\n && validationEnabled(VALIDATION_KEYS.TYPESCRIPT),\n execute: async (context: ValidationContext): Promise<ValidationStepResult> => {\n const startTime = performance.now();\n try {\n const result = await validateTypeScript(\n context.scope,\n context.scopeConfig,\n context.validatedFiles,\n );\n return {\n success: result.success,\n error: result.error,\n duration: performance.now() - startTime,\n skipped: result.skipped,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n duration: performance.now() - startTime,\n };\n }\n },\n};\n","/**\n * TypeScript validation command.\n *\n * Runs TypeScript type checking using tsc.\n */\nimport { getTypeScriptScope } from \"../../validation/config/scope.js\";\nimport { discoverTool, formatSkipMessage } from \"../../validation/discovery/index.js\";\nimport { validateTypeScript } from \"../../validation/steps/typescript.js\";\nimport type { TypeScriptCommandOptions, ValidationCommandResult } from \"./types\";\n\n/**\n * Run TypeScript type checking.\n *\n * @param options - Command options\n * @returns Command result with exit code and output\n */\nexport async function typescriptCommand(options: TypeScriptCommandOptions): Promise<ValidationCommandResult> {\n const { cwd, scope = \"full\", files, quiet } = options;\n const startTime = Date.now();\n\n // Discover tsc (provided by typescript package)\n const toolResult = await discoverTool(\"typescript\", { projectRoot: cwd });\n if (!toolResult.found) {\n const skipMessage = formatSkipMessage(\"TypeScript\", toolResult);\n return { exitCode: 0, output: skipMessage, durationMs: Date.now() - startTime };\n }\n\n // Get scope configuration from tsconfig\n const scopeConfig = getTypeScriptScope(scope);\n\n // Run TypeScript validation\n const result = await validateTypeScript(scope, scopeConfig, files);\n const durationMs = Date.now() - startTime;\n\n // Map result to command output\n if (result.success) {\n const output = quiet ? \"\" : `TypeScript: ✓ No type errors`;\n return { exitCode: 0, output, durationMs };\n } else {\n const output = result.error ?? \"TypeScript validation failed\";\n return { exitCode: 1, output, durationMs };\n }\n}\n","/**\n * Run all validations command.\n *\n * Executes all validation steps in sequence:\n * 1. Circular dependencies (fastest)\n * 2. Knip (optional)\n * 3. ESLint\n * 4. TypeScript (slowest)\n */\nimport { circularCommand } from \"./circular\";\nimport { formatDuration, formatSummary } from \"./format\";\nimport { knipCommand } from \"./knip\";\nimport { lintCommand } from \"./lint\";\nimport type { AllCommandOptions, ValidationCommandResult } from \"./types\";\nimport { typescriptCommand } from \"./typescript\";\n\n/** Total number of validation steps */\nconst TOTAL_STEPS = 4;\n\n/**\n * Format step output with step number and timing.\n *\n * @param stepNumber - Current step number (1-indexed)\n * @param result - Validation result\n * @param quiet - Whether to suppress output\n * @returns Formatted output string\n */\nfunction formatStepWithTiming(\n stepNumber: number,\n result: ValidationCommandResult,\n quiet: boolean,\n): string {\n if (quiet || !result.output) return \"\";\n\n const timing = result.durationMs === undefined ? \"\" : ` (${formatDuration(result.durationMs)})`;\n return `[${stepNumber}/${TOTAL_STEPS}] ${result.output}${timing}`;\n}\n\n/**\n * Run all validation steps.\n *\n * @param options - Command options\n * @returns Command result with exit code and output\n */\nexport async function allCommand(options: AllCommandOptions): Promise<ValidationCommandResult> {\n const { cwd, scope, files, fix, quiet = false, json } = options;\n const startTime = Date.now();\n const outputs: string[] = [];\n let hasFailure = false;\n\n // 1. Circular dependencies\n const circularResult = await circularCommand({ cwd, quiet, json });\n const circularOutput = formatStepWithTiming(1, circularResult, quiet);\n if (circularOutput) outputs.push(circularOutput);\n if (circularResult.exitCode !== 0) hasFailure = true;\n\n // 2. Knip (optional - skip on failure, it's informational)\n const knipResult = await knipCommand({ cwd, quiet, json });\n const knipOutput = formatStepWithTiming(2, knipResult, quiet);\n if (knipOutput) outputs.push(knipOutput);\n // Don't fail on knip - it's optional\n\n // 3. ESLint\n const lintResult = await lintCommand({ cwd, scope, files, fix, quiet, json });\n const lintOutput = formatStepWithTiming(3, lintResult, quiet);\n if (lintOutput) outputs.push(lintOutput);\n if (lintResult.exitCode !== 0) hasFailure = true;\n\n // 4. TypeScript\n const tsResult = await typescriptCommand({ cwd, scope, files, quiet, json });\n const tsOutput = formatStepWithTiming(4, tsResult, quiet);\n if (tsOutput) outputs.push(tsOutput);\n if (tsResult.exitCode !== 0) hasFailure = true;\n\n // Calculate total duration\n const totalDurationMs = Date.now() - startTime;\n\n // Add summary line\n if (!quiet) {\n const summary = formatSummary({ success: !hasFailure, totalDurationMs });\n outputs.push(\"\", summary); // Empty line before summary\n }\n\n return {\n exitCode: hasFailure ? 1 : 0,\n output: outputs.join(\"\\n\"),\n durationMs: totalDurationMs,\n };\n}\n","/**\n * Validation domain - Run code validation tools\n *\n * Provides CLI commands for running validation tools (TypeScript, ESLint, etc.)\n * as a globally-installed tool across TypeScript projects.\n */\nimport type { Command } from \"commander\";\n\nimport { allCommand, circularCommand, knipCommand, lintCommand, typescriptCommand } from \"../../commands/validation\";\nimport type { Domain } from \"../types\";\n\n/** Validation scope options */\ntype ValidationScope = \"full\" | \"production\";\n\n/** Common options for all validation commands */\ninterface CommonOptions {\n scope?: ValidationScope;\n files?: string[];\n quiet?: boolean;\n json?: boolean;\n}\n\n/** Options for lint command */\ninterface LintOptions extends CommonOptions {\n fix?: boolean;\n}\n\n/**\n * Add common options to a command\n */\nfunction addCommonOptions(cmd: Command): Command {\n return cmd\n .option(\"--scope <scope>\", \"Validation scope (full|production)\", \"full\")\n .option(\"--files <paths...>\", \"Specific files/directories to validate\")\n .option(\"--quiet\", \"Suppress progress output\")\n .option(\"--json\", \"Output results as JSON\");\n}\n\n/**\n * Register validation domain commands\n */\nfunction registerValidationCommands(validationCmd: Command): void {\n // typescript command\n const tsCmd = validationCmd\n .command(\"typescript\")\n .alias(\"ts\")\n .description(\"Run TypeScript type checking\")\n .action(async (options: CommonOptions) => {\n const result = await typescriptCommand({\n cwd: process.cwd(),\n scope: options.scope,\n files: options.files,\n quiet: options.quiet,\n json: options.json,\n });\n if (result.output) console.log(result.output);\n process.exit(result.exitCode);\n });\n addCommonOptions(tsCmd);\n\n // lint command\n const lintCmd = validationCmd\n .command(\"lint\")\n .description(\"Run ESLint\")\n .option(\"--fix\", \"Auto-fix issues\")\n .action(async (options: LintOptions) => {\n const result = await lintCommand({\n cwd: process.cwd(),\n scope: options.scope,\n files: options.files,\n fix: options.fix,\n quiet: options.quiet,\n json: options.json,\n });\n if (result.output) console.log(result.output);\n process.exit(result.exitCode);\n });\n addCommonOptions(lintCmd);\n\n // circular command\n const circularCmd = validationCmd\n .command(\"circular\")\n .description(\"Check for circular dependencies\")\n .action(async (options: CommonOptions) => {\n const result = await circularCommand({\n cwd: process.cwd(),\n quiet: options.quiet,\n json: options.json,\n });\n if (result.output) console.log(result.output);\n process.exit(result.exitCode);\n });\n addCommonOptions(circularCmd);\n\n // knip command\n const knipCmd = validationCmd\n .command(\"knip\")\n .description(\"Detect unused code\")\n .action(async (options: CommonOptions) => {\n const result = await knipCommand({\n cwd: process.cwd(),\n quiet: options.quiet,\n json: options.json,\n });\n if (result.output) console.log(result.output);\n process.exit(result.exitCode);\n });\n addCommonOptions(knipCmd);\n\n // all command (default)\n const allCmd = validationCmd\n .command(\"all\", { isDefault: true })\n .description(\"Run all validations\")\n .option(\"--fix\", \"Auto-fix ESLint issues\")\n .action(async (options: LintOptions) => {\n const result = await allCommand({\n cwd: process.cwd(),\n scope: options.scope,\n files: options.files,\n fix: options.fix,\n quiet: options.quiet,\n json: options.json,\n });\n if (result.output) console.log(result.output);\n process.exit(result.exitCode);\n });\n addCommonOptions(allCmd);\n}\n\n/**\n * Validation domain - Run code validation tools\n */\nexport const validationDomain: Domain = {\n name: \"validation\",\n description: \"Run code validation tools\",\n register: (program: Command) => {\n const validationCmd = program\n .command(\"validation\")\n .alias(\"v\")\n .description(\"Run code validation tools\");\n\n registerValidationCommands(validationCmd);\n },\n};\n"],"mappings":"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AAA,gBAAeA,gBAAe,GAAG,IAAI,GAAG,IAAI;AAC5C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,GAAG,IAAI;AAC5C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,GAAG,IAAI;AAC5C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,GAAG,IAAI;AAC5C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,GAAG,IAAI;AAC5C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,GAAG,IAAI;AAC5C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,GAAG,IAAI;AAC5C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,GAAG,IAAI;AAC5C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,GAAG,IAAI;AAC5C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,GAAG,IAAI;AAC5C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,GAAG,IAAI;AAC5C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,GAAG,IAAI;AAC5C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,EAAE,IAAI;AAC3C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,EAAE,IAAI;AAC3C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,EAAE,IAAI;AAC3C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,EAAE,IAAI;AAC3C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,EAAE,IAAI;AAC3C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,EAAE,IAAI;AAC3C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,EAAE,IAAI;AAC3C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,EAAE,IAAI;AAC3C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,EAAE,IAAI;AAC3C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,EAAE,IAAI;AAC3C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,EAAE,IAAI;AAC3C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,EAAE,IAAI;AAC3C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,EAAE,IAAI;AAC3C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,EAAE,IAAI;AAC3C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,EAA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","names":["os","path","fs","fs","path","path","fs","fs","fs","path","path","os","program","join","join","stat","join","path","path","stat","join","mkdir","join","execa","join","statusDirs","join","mkdir","join","join","mkdir","readdir","readFile","rename","join","readdir","join","readFile","mkdir","rename","readdir","readFile","unlink","join","readdir","join","readFile","lines","unlink","readdir","rename","join","readdir","join","rename","readFile","stat","join","stat","join","readFile","resolve","program","path","path","readdir","stat","path","path","readdir","stat","getDisplayNumber","getDisplayNumber","formatNode","formatWorkItemName","format","format","program","value","CharacterCodes","ParseOptions","handleError","ScanError","SyntaxKind","parse","ParseErrorCode","join","parse","fs","path","require","fs","path","resolve","spawn","spawn","resolve","spawn","existsSync","join","spawn","existsSync","join","resolve","program"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/commands/claude/init.ts","../src/commands/claude/settings/consolidate.ts","../src/lib/claude/permissions/discovery.ts","../src/lib/claude/permissions/parser.ts","../src/scanner/walk.ts","../src/lib/claude/permissions/subsumption.ts","../src/lib/claude/permissions/merger.ts","../src/lib/claude/settings/backup.ts","../src/lib/claude/settings/reporter.ts","../src/lib/claude/settings/writer.ts","../src/domains/claude/index.ts","../src/commands/session/archive.ts","../src/session/errors.ts","../src/session/show.ts","../src/config/defaults.ts","../src/session/list.ts","../src/session/timestamp.ts","../src/session/types.ts","../src/commands/session/delete.ts","../src/session/delete.ts","../src/commands/session/handoff.ts","../src/git/root.ts","../src/session/create.ts","../src/commands/session/list.ts","../src/commands/session/pickup.ts","../src/session/pickup.ts","../src/commands/session/prune.ts","../src/commands/session/release.ts","../src/session/release.ts","../src/commands/session/show.ts","../src/domains/session/help.ts","../src/domains/session/index.ts","../src/scanner/scanner.ts","../src/status/state.ts","../src/tree/build.ts","../src/commands/spec/next.ts","../src/reporter/json.ts","../src/reporter/markdown.ts","../src/reporter/table.ts","../src/reporter/text.ts","../src/commands/spec/status.ts","../src/domains/spec/index.ts","../node_modules/.pnpm/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/scanner.js","../node_modules/.pnpm/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/string-intern.js","../node_modules/.pnpm/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/parser.js","../node_modules/.pnpm/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/main.js","../src/validation/config/scope.ts","../src/validation/discovery/tool-finder.ts","../src/validation/discovery/constants.ts","../src/validation/steps/circular.ts","../src/validation/steps/constants.ts","../src/commands/validation/circular.ts","../src/commands/validation/format.ts","../src/validation/steps/eslint.ts","../src/validation/types.ts","../src/validation/steps/knip.ts","../src/commands/validation/knip.ts","../src/commands/validation/lint.ts","../src/validation/steps/typescript.ts","../src/commands/validation/typescript.ts","../src/commands/validation/all.ts","../src/domains/validation/index.ts"],"sourcesContent":["/**\n * CLI entry point for spx\n */\nimport { Command } from \"commander\";\nimport { createRequire } from \"node:module\";\nimport { claudeDomain } from \"./domains/claude\";\nimport { sessionDomain } from \"./domains/session\";\nimport { specDomain } from \"./domains/spec\";\nimport { validationDomain } from \"./domains/validation\";\n\nconst require = createRequire(import.meta.url);\nconst { version } = require(\"../package.json\") as { version: string };\n\nconst program = new Command();\n\nprogram\n .name(\"spx\")\n .description(\"Fast, deterministic CLI tool for spec workflow management\")\n .version(version);\n\n// Register domains\nclaudeDomain.register(program);\nsessionDomain.register(program);\nspecDomain.register(program);\nvalidationDomain.register(program);\n\nprogram.parse();\n","/**\n * Init command implementation\n *\n * Wrapper around `claude plugin marketplace` to install/update spx-claude marketplace\n */\nimport { execa } from \"execa\";\n\n/**\n * Options for init command\n */\nexport interface InitOptions {\n /** Working directory (for testing) */\n cwd?: string;\n}\n\n/**\n * Execute claude init command\n *\n * Wraps the Claude CLI's `plugin marketplace` commands to manage\n * the spx-claude marketplace installation.\n *\n * Behavior:\n * 1. Check if spx-claude marketplace exists via `claude plugin marketplace list`\n * 2. If missing: shell `claude plugin marketplace add simonheimlicher/spx-claude`\n * 3. If exists: shell `claude plugin marketplace update spx-claude`\n * 4. Parse output and return status message\n *\n * @param options - Command options\n * @returns Status message\n * @throws Error if claude CLI not available or commands fail\n *\n * @example\n * ```typescript\n * const output = await initCommand();\n * console.log(output);\n * // Output: \"✓ spx-claude marketplace installed successfully\"\n * // or: \"✓ spx-claude marketplace updated successfully\"\n * ```\n */\nexport async function initCommand(\n options: InitOptions = {},\n): Promise<string> {\n const cwd = options.cwd || process.cwd();\n\n try {\n // Step 1: Check if spx-claude marketplace exists\n const { stdout: listOutput } = await execa(\n \"claude\",\n [\"plugin\", \"marketplace\", \"list\"],\n { cwd },\n );\n\n const exists = listOutput.includes(\"spx-claude\");\n\n // Step 2: Add or update based on existence\n if (!exists) {\n // Add marketplace\n await execa(\n \"claude\",\n [\"plugin\", \"marketplace\", \"add\", \"simonheimlicher/spx-claude\"],\n { cwd },\n );\n\n return \"✓ spx-claude marketplace installed successfully\\n\\nRun 'claude plugin marketplace list' to view all marketplaces.\";\n } else {\n // Update marketplace\n await execa(\"claude\", [\"plugin\", \"marketplace\", \"update\", \"spx-claude\"], {\n cwd,\n });\n\n return \"✓ spx-claude marketplace updated successfully\\n\\nThe marketplace is now up to date.\";\n }\n } catch (error) {\n if (error instanceof Error) {\n // Check for specific error conditions\n if (\n error.message.includes(\"ENOENT\")\n || error.message.includes(\"command not found\")\n ) {\n throw new Error(\n \"Claude CLI not found. Please install Claude Code first.\\n\\nVisit: https://docs.anthropic.com/claude-code\",\n );\n }\n\n throw new Error(`Failed to initialize marketplace: ${error.message}`);\n }\n throw error;\n }\n}\n","/**\n * Consolidate command implementation\n *\n * Orchestrates the full consolidation pipeline:\n * 1. Discovery - Find all settings.local.json files\n * 2. Parsing - Extract permissions from each file\n * 3. Merging - Combine with subsumption and conflict resolution\n * 4. Backup - Create timestamped backup (if not dry-run)\n * 5. Writing - Atomically write merged settings (if not dry-run)\n * 6. Reporting - Format and return result summary\n */\nimport os from \"os\";\nimport path from \"path\";\nimport { findSettingsFiles } from \"../../../lib/claude/permissions/discovery.js\";\nimport { mergePermissions } from \"../../../lib/claude/permissions/merger.js\";\nimport { parseAllSettings, parseSettingsFile } from \"../../../lib/claude/permissions/parser.js\";\nimport { createBackup } from \"../../../lib/claude/settings/backup.js\";\nimport { formatReport } from \"../../../lib/claude/settings/reporter.js\";\nimport { writeSettings } from \"../../../lib/claude/settings/writer.js\";\n\n/**\n * Options for consolidate command\n */\nexport interface ConsolidateOptions {\n /** Root directory to scan for settings files (default: ~/Code) */\n root?: string;\n /** Write changes to global settings file (default: false = preview only) */\n write?: boolean;\n /** Write merged settings to specified file instead of global settings */\n outputFile?: string;\n /** Path to global settings file (for testing; default: ~/.claude/settings.json) */\n globalSettings?: string;\n}\n\n/**\n * Execute settings consolidate command\n *\n * Consolidates permissions from project-local settings files into\n * the global Claude Code settings file.\n *\n * Features:\n * - Discovers all `.claude/settings.local.json` files recursively\n * - Applies subsumption to remove narrower permissions\n * - Resolves conflicts (deny wins over allow)\n * - Deduplicates and sorts permissions\n * - Creates backup before modifications\n * - Supports dry-run mode for preview\n *\n * @param options - Command options\n * @returns Formatted report string\n * @throws Error if discovery, parsing, or writing fails\n *\n * @example\n * ```typescript\n * // Normal consolidation\n * const output = await consolidateCommand({ root: \"~/Code\" });\n * console.log(output);\n *\n * // Dry-run preview\n * const preview = await consolidateCommand({ root: \"~/Code\", dryRun: true });\n * console.log(preview);\n * ```\n */\nexport async function consolidateCommand(\n options: ConsolidateOptions = {},\n): Promise<string> {\n // Resolve paths\n const root = options.root\n ? path.resolve(options.root.replace(/^~/, os.homedir()))\n : path.join(os.homedir(), \"Code\");\n\n const globalSettingsPath = options.globalSettings\n || path.join(os.homedir(), \".claude\", \"settings.json\");\n\n const shouldWrite = options.write || false;\n const outputFile = options.outputFile;\n const previewOnly = !shouldWrite && !outputFile;\n\n // Step 1: Discovery - find all settings.local.json files\n const settingsFiles = await findSettingsFiles(root);\n\n if (settingsFiles.length === 0) {\n return `No settings files found in ${root}\\n\\nSearched for: **/.claude/settings.local.json`;\n }\n\n // Step 2: Parsing - extract permissions from each file\n const localPermissions = await parseAllSettings(settingsFiles);\n\n // Step 3: Read global settings\n let globalSettings = await parseSettingsFile(globalSettingsPath);\n\n // If global settings doesn't exist, create empty structure\n if (!globalSettings) {\n globalSettings = {\n permissions: {\n allow: [],\n deny: [],\n ask: [],\n },\n };\n }\n\n // Ensure permissions object exists\n if (!globalSettings.permissions) {\n globalSettings.permissions = {\n allow: [],\n deny: [],\n ask: [],\n };\n }\n\n // Step 4: Merge with subsumption and conflict resolution\n const { merged, result } = mergePermissions(\n globalSettings.permissions,\n localPermissions,\n );\n\n // Step 5: Backup (only when writing to global settings)\n if (shouldWrite) {\n try {\n result.backupPath = await createBackup(globalSettingsPath);\n } catch (error) {\n // If backup fails because file doesn't exist, that's okay (first time)\n if (error instanceof Error && !error.message.includes(\"not found\")) {\n throw error;\n }\n }\n }\n\n // Step 6: Write (if --write or --output-file specified)\n if (shouldWrite) {\n const updatedSettings = {\n ...globalSettings,\n permissions: merged,\n };\n await writeSettings(globalSettingsPath, updatedSettings);\n } else if (outputFile) {\n const updatedSettings = {\n ...globalSettings,\n permissions: merged,\n };\n const resolvedOutputPath = path.resolve(outputFile.replace(/^~/, os.homedir()));\n await writeSettings(resolvedOutputPath, updatedSettings);\n result.outputPath = resolvedOutputPath;\n }\n\n // Step 7: Report\n return formatReport(result, previewOnly, globalSettingsPath, outputFile);\n}\n","/**\n * Discovery of Claude Code settings files across project directories\n */\nimport fs from \"fs/promises\";\nimport path from \"path\";\n\n/**\n * Recursively find all .claude/settings.local.json files under a root directory\n *\n * Walks the directory tree looking for files matching the pattern:\n * `**\\/.claude/settings.local.json`\n *\n * @param root - Root directory path to start searching from\n * @param visited - Set of visited paths to avoid symlink loops (internal use)\n * @returns Promise resolving to array of absolute paths to settings.local.json files\n * @throws Error if root directory doesn't exist or permission denied\n *\n * @example\n * ```typescript\n * const files = await findSettingsFiles(\"~/Code\");\n * // Returns: [\n * // \"/Users/shz/Code/project-a/.claude/settings.local.json\",\n * // \"/Users/shz/Code/project-b/.claude/settings.local.json\"\n * // ]\n * ```\n */\nexport async function findSettingsFiles(\n root: string,\n visited: Set<string> = new Set(),\n): Promise<string[]> {\n // Normalize and resolve path to handle symlinks and ~ expansion\n const normalizedRoot = path.resolve(root.replace(/^~/, process.env.HOME || \"~\"));\n\n // Check for symlink loops\n if (visited.has(normalizedRoot)) {\n return []; // Skip already visited directories\n }\n visited.add(normalizedRoot);\n\n try {\n // Stat the root to verify it exists and is a directory\n const stats = await fs.stat(normalizedRoot);\n if (!stats.isDirectory()) {\n throw new Error(`Path is not a directory: ${normalizedRoot}`);\n }\n\n // Read directory contents\n const entries = await fs.readdir(normalizedRoot, { withFileTypes: true });\n const results: string[] = [];\n\n for (const entry of entries) {\n const fullPath = path.join(normalizedRoot, entry.name);\n\n // If this is a .claude directory, check for settings.local.json\n if (entry.isDirectory() && entry.name === \".claude\") {\n const settingsPath = path.join(fullPath, \"settings.local.json\");\n if (await isValidSettingsFile(settingsPath)) {\n results.push(settingsPath);\n }\n }\n\n // Recursively search subdirectories (skip .claude to avoid double-checking)\n if (entry.isDirectory() && entry.name !== \".claude\") {\n const subFiles = await findSettingsFiles(fullPath, visited);\n results.push(...subFiles);\n }\n }\n\n return results;\n } catch (error) {\n // Re-throw with more context\n if (error instanceof Error) {\n if (error.message.includes(\"ENOENT\")) {\n throw new Error(`Directory not found: ${normalizedRoot}`);\n }\n if (error.message.includes(\"EACCES\")) {\n throw new Error(`Permission denied: ${normalizedRoot}`);\n }\n throw new Error(\n `Failed to search directory \"${normalizedRoot}\": ${error.message}`,\n );\n }\n throw error;\n }\n}\n\n/**\n * Check if a given path is a valid settings.local.json file\n *\n * Validates that:\n * - File exists\n * - File is readable\n * - File has .json extension\n *\n * @param filePath - Absolute path to check\n * @returns Promise resolving to true if valid settings file, false otherwise\n *\n * @example\n * ```typescript\n * const isValid = await isValidSettingsFile(\"/path/to/.claude/settings.local.json\");\n * // Returns: true or false\n * ```\n */\nexport async function isValidSettingsFile(filePath: string): Promise<boolean> {\n try {\n // Check if file exists and is readable\n await fs.access(filePath, fs.constants.R_OK);\n\n // Check if it's actually a file (not a directory)\n const stats = await fs.stat(filePath);\n if (!stats.isFile()) {\n return false;\n }\n\n // Validate it has .json extension\n return path.extname(filePath) === \".json\";\n } catch {\n // File doesn't exist or isn't readable\n return false;\n }\n}\n","/**\n * Parser for Claude Code settings files and permissions\n */\nimport fs from \"fs/promises\";\nimport type { ClaudeSettings, Permission, PermissionCategory, Permissions } from \"./types.js\";\n\n/**\n * Parse a settings.json file and extract permissions\n *\n * Handles:\n * - Malformed JSON (returns null)\n * - Missing permissions object (returns empty permissions)\n * - Validates basic structure\n *\n * @param filePath - Absolute path to settings.json file\n * @returns Promise resolving to ClaudeSettings object, or null if malformed\n *\n * @example\n * ```typescript\n * const settings = await parseSettingsFile(\"/path/to/.claude/settings.json\");\n * if (settings) {\n * console.log(settings.permissions?.allow);\n * }\n * ```\n */\nexport async function parseSettingsFile(\n filePath: string,\n): Promise<ClaudeSettings | null> {\n try {\n // Read file contents\n const content = await fs.readFile(filePath, \"utf-8\");\n\n // Parse JSON\n const parsed = JSON.parse(content);\n\n // Basic validation: should be an object\n if (typeof parsed !== \"object\" || parsed === null) {\n return null;\n }\n\n return parsed as ClaudeSettings;\n } catch {\n // JSON parse error or file read error\n return null;\n }\n}\n\n/**\n * Parse a permission string into structured components\n *\n * Permission format: \"Type(scope)\"\n * Examples:\n * - \"Bash(git:*)\" => { type: \"Bash\", scope: \"git:*\" }\n * - \"Read(file_path:/Users/shz/Code/**)\" => { type: \"Read\", scope: \"file_path:/Users/shz/Code/**\" }\n * - \"WebFetch(domain:github.com)\" => { type: \"WebFetch\", scope: \"domain:github.com\" }\n *\n * @param raw - Raw permission string\n * @param category - Permission category (allow/deny/ask)\n * @returns Parsed Permission object\n * @throws Error if permission string is malformed\n *\n * @example\n * ```typescript\n * const perm = parsePermission(\"Bash(git:*)\", \"allow\");\n * // Returns: { raw: \"Bash(git:*)\", type: \"Bash\", scope: \"git:*\", category: \"allow\" }\n * ```\n */\nexport function parsePermission(\n raw: string,\n category: PermissionCategory,\n): Permission {\n // Match pattern: Type(scope)\n const match = raw.match(/^([^(]+)\\((.+)\\)$/);\n\n if (!match) {\n throw new Error(`Malformed permission string: \"${raw}\"`);\n }\n\n const [, type, scope] = match;\n\n return {\n raw,\n type: type.trim(),\n scope: scope.trim(),\n category,\n };\n}\n\n/**\n * Parse all permissions from a Permissions object\n *\n * Converts permission strings to structured Permission objects,\n * grouped by category (allow/deny/ask).\n *\n * @param permissions - Permissions object from settings.json\n * @returns Array of parsed Permission objects\n *\n * @example\n * ```typescript\n * const permissions = {\n * allow: [\"Bash(git:*)\", \"Bash(npm:*)\"],\n * deny: [\"Bash(rm -rf:*)\"]\n * };\n * const parsed = parseAllPermissions(permissions);\n * // Returns array of Permission objects with category set\n * ```\n */\nexport function parseAllPermissions(permissions: Permissions): Permission[] {\n const result: Permission[] = [];\n\n // Parse allow permissions\n if (permissions.allow) {\n for (const perm of permissions.allow) {\n try {\n result.push(parsePermission(perm, \"allow\"));\n } catch {\n // Skip malformed permissions\n continue;\n }\n }\n }\n\n // Parse deny permissions\n if (permissions.deny) {\n for (const perm of permissions.deny) {\n try {\n result.push(parsePermission(perm, \"deny\"));\n } catch {\n // Skip malformed permissions\n continue;\n }\n }\n }\n\n // Parse ask permissions\n if (permissions.ask) {\n for (const perm of permissions.ask) {\n try {\n result.push(parsePermission(perm, \"ask\"));\n } catch {\n // Skip malformed permissions\n continue;\n }\n }\n }\n\n return result;\n}\n\n/**\n * Read and parse multiple settings files\n *\n * Processes an array of file paths, reading and parsing each one.\n * Skips files that can't be read or parsed.\n *\n * @param filePaths - Array of absolute paths to settings files\n * @returns Promise resolving to array of Permissions objects (one per valid file)\n *\n * @example\n * ```typescript\n * const files = [\n * \"/Users/shz/Code/project-a/.claude/settings.local.json\",\n * \"/Users/shz/Code/project-b/.claude/settings.local.json\"\n * ];\n * const allPermissions = await parseAllSettings(files);\n * // Returns: [{ allow: [...], deny: [...] }, { allow: [...] }]\n * ```\n */\nexport async function parseAllSettings(\n filePaths: string[],\n): Promise<Permissions[]> {\n const results: Permissions[] = [];\n\n for (const filePath of filePaths) {\n const settings = await parseSettingsFile(filePath);\n if (settings?.permissions) {\n results.push(settings.permissions);\n }\n }\n\n return results;\n}\n","/**\n * Directory walking and filesystem traversal\n */\nimport fs from \"fs/promises\";\nimport path from \"path\";\nimport type { DirectoryEntry, WorkItem } from \"../types.js\";\nimport { parseWorkItemName } from \"./patterns.js\";\n\n/**\n * Recursively walk a directory tree and return all subdirectories\n *\n * @param root - Root directory path to start walking from\n * @param visited - Set of visited paths to avoid symlink loops (internal use)\n * @returns Promise resolving to array of directory entries\n * @throws Error if root directory doesn't exist or permission denied\n *\n * @example\n * ```typescript\n * const entries = await walkDirectory(\"/path/to/specs\");\n * // Returns: [{ name: \"capability-21_test\", path: \"/path/to/specs/capability-21_test\", isDirectory: true }, ...]\n * ```\n */\nexport async function walkDirectory(\n root: string,\n visited: Set<string> = new Set()\n): Promise<DirectoryEntry[]> {\n // Normalize and resolve path to handle symlinks\n const normalizedRoot = path.resolve(root);\n\n // Check for symlink loops\n if (visited.has(normalizedRoot)) {\n return []; // Skip already visited directories\n }\n visited.add(normalizedRoot);\n\n try {\n // Read directory contents\n const entries = await fs.readdir(normalizedRoot, { withFileTypes: true });\n const results: DirectoryEntry[] = [];\n\n for (const entry of entries) {\n const fullPath = path.join(normalizedRoot, entry.name);\n\n // Only process directories\n if (entry.isDirectory()) {\n // Add current directory\n results.push({\n name: entry.name,\n path: fullPath,\n isDirectory: true,\n });\n\n // Recursively walk subdirectories\n const subEntries = await walkDirectory(fullPath, visited);\n results.push(...subEntries);\n }\n }\n\n return results;\n } catch (error) {\n // Re-throw with more context\n if (error instanceof Error) {\n throw new Error(\n `Failed to walk directory \"${normalizedRoot}\": ${error.message}`\n );\n }\n throw error;\n }\n}\n\n/**\n * Filter directory entries to include only work item directories\n *\n * Uses parseWorkItemName() to validate directory names match work item patterns.\n * Excludes directories that don't match capability/feature/story patterns.\n *\n * @param entries - Array of directory entries to filter\n * @returns Filtered array containing only valid work item directories\n *\n * @example\n * ```typescript\n * const entries = [\n * { name: \"capability-21_test\", path: \"/specs/capability-21_test\", isDirectory: true },\n * { name: \"node_modules\", path: \"/specs/node_modules\", isDirectory: true },\n * ];\n * const filtered = filterWorkItemDirectories(entries);\n * // Returns: [{ name: \"capability-21_test\", ... }]\n * ```\n */\nexport function filterWorkItemDirectories(\n entries: DirectoryEntry[]\n): DirectoryEntry[] {\n return entries.filter((entry) => {\n try {\n // Try to parse the directory name as a work item\n parseWorkItemName(entry.name);\n return true; // Valid work item pattern\n } catch {\n return false; // Not a work item pattern\n }\n });\n}\n\n/**\n * Convert directory entries to WorkItem objects\n *\n * Parses each directory entry name to extract work item metadata (kind, number, slug)\n * and combines it with the full filesystem path.\n *\n * @param entries - Filtered directory entries (work items only)\n * @returns Array of WorkItem objects with full metadata\n * @throws Error if any entry has invalid work item pattern\n *\n * @example\n * ```typescript\n * const entries = [\n * { name: \"capability-21_core-cli\", path: \"/specs/capability-21_core-cli\", isDirectory: true },\n * ];\n * const workItems = buildWorkItemList(entries);\n * // Returns: [{ kind: \"capability\", number: 20, slug: \"core-cli\", path: \"/specs/capability-21_core-cli\" }]\n * ```\n */\nexport function buildWorkItemList(entries: DirectoryEntry[]): WorkItem[] {\n return entries.map((entry) => ({\n ...parseWorkItemName(entry.name),\n path: entry.path,\n }));\n}\n\n/**\n * Normalize path separators for cross-platform consistency\n *\n * Converts Windows backslashes to forward slashes for consistent path handling\n * across different operating systems.\n *\n * @param filepath - Path to normalize\n * @returns Normalized path with forward slashes\n *\n * @example\n * ```typescript\n * normalizePath(\"C:\\\\Users\\\\test\\\\specs\"); // Returns: \"C:/Users/test/specs\"\n * normalizePath(\"/home/user/specs\"); // Returns: \"/home/user/specs\"\n * ```\n */\nexport function normalizePath(filepath: string): string {\n // Replace all backslashes with forward slashes for cross-platform consistency\n return filepath.replace(/\\\\/g, \"/\");\n}\n","/**\n * Permission subsumption detection\n *\n * Detects when broader permissions subsume narrower ones:\n * - Bash(git:*) subsumes Bash(git log:*), Bash(git worktree:*), etc.\n * - Read(file_path:/Users/shz/Code/**) subsumes Read(file_path:/Users/shz/Code/project-a/**)\n */\nimport { normalizePath } from \"../../../scanner/walk.js\";\nimport { parsePermission } from \"./parser.js\";\nimport type { Permission, PermissionCategory, ScopePattern, SubsumptionResult } from \"./types.js\";\n\n/**\n * Parse a scope string to extract pattern type and value\n *\n * Determines whether the scope is a command pattern (e.g., \"git:*\")\n * or a path pattern (e.g., \"file_path:/Users/shz/Code/**\").\n *\n * @param scope - Scope string from permission\n * @returns ScopePattern with type and pattern\n *\n * @example\n * ```typescript\n * parseScopePattern(\"git:*\")\n * // Returns: { type: \"command\", pattern: \"git:*\" }\n *\n * parseScopePattern(\"file_path:/Users/shz/Code/**\")\n * // Returns: { type: \"path\", pattern: \"/Users/shz/Code/**\" }\n * ```\n */\nexport function parseScopePattern(scope: string): ScopePattern {\n // Check if scope contains path indicators\n if (\n scope.includes(\"file_path:\")\n || scope.includes(\"directory_path:\")\n || scope.includes(\"path:\")\n ) {\n // Extract path after the colon\n const colonIndex = scope.indexOf(\":\");\n const pattern = colonIndex >= 0 ? scope.substring(colonIndex + 1) : scope;\n return { type: \"path\", pattern };\n }\n\n // Default to command pattern (e.g., \"git:*\", \"npm:*\")\n return { type: \"command\", pattern: scope };\n}\n\n/**\n * Check if permission A subsumes permission B\n *\n * Subsumption rules:\n * 1. Same type required (Bash subsumes Bash, not Read)\n * 2. Identical scopes = not subsumption (exact match)\n * 3. Broader scope subsumes narrower scope:\n * - Command: \"git:*\" subsumes \"git log:*\", \"git worktree:*\"\n * - Path: \"/Users/shz/Code/**\" subsumes \"/Users/shz/Code/project-a/**\"\n *\n * @param broader - Permission that might subsume the other\n * @param narrower - Permission that might be subsumed\n * @returns true if broader subsumes narrower, false otherwise\n *\n * @example\n * ```typescript\n * subsumes(\n * parsePermission(\"Bash(git:*)\", \"allow\"),\n * parsePermission(\"Bash(git log:*)\", \"allow\")\n * )\n * // Returns: true\n *\n * subsumes(\n * parsePermission(\"Read(file_path:/Users/shz/Code/**)\", \"allow\"),\n * parsePermission(\"Read(file_path:/Users/shz/Code/project-a/**)\", \"allow\")\n * )\n * // Returns: true\n *\n * subsumes(\n * parsePermission(\"Bash(git:*)\", \"allow\"),\n * parsePermission(\"Read(file_path:/Users/shz/Code/**)\", \"allow\")\n * )\n * // Returns: false (different types)\n * ```\n */\nexport function subsumes(broader: Permission, narrower: Permission): boolean {\n // 1. Type must match\n if (broader.type !== narrower.type) {\n return false;\n }\n\n // 2. Identical = not subsumption (this is just a duplicate)\n if (broader.scope === narrower.scope) {\n return false;\n }\n\n // 3. Parse scope patterns\n const broaderScope = parseScopePattern(broader.scope);\n const narrowerScope = parseScopePattern(narrower.scope);\n\n // 4. Handle command patterns (e.g., \"git:*\")\n if (broaderScope.type === \"command\" && narrowerScope.type === \"command\") {\n // Extract base command by removing :* suffix\n const broaderBase = broaderScope.pattern.replace(/:?\\*+$/, \"\");\n const narrowerFull = narrowerScope.pattern.replace(/:?\\*+$/, \"\");\n\n // Check if narrower starts with broader prefix\n // \"git:*\" subsumes \"git log:*\" if:\n // - narrowerFull starts with broaderBase\n // - narrowerFull is longer (more specific)\n if (narrowerFull.startsWith(broaderBase)) {\n // Additional specificity check: narrower must add more detail\n // e.g., \"git\" subsumes \"git log\", but not \"git\" subsumes \"git\"\n return narrowerFull.length > broaderBase.length;\n }\n }\n\n // 5. Handle path patterns (e.g., \"/Users/shz/Code/**\")\n if (broaderScope.type === \"path\" && narrowerScope.type === \"path\") {\n // Normalize paths for comparison\n const broaderPath = normalizePath(broaderScope.pattern.replace(/\\/?\\*+$/, \"\"));\n const narrowerPath = normalizePath(narrowerScope.pattern.replace(/\\/?\\*+$/, \"\"));\n\n // Check if narrower is a sub-path of broader\n // \"/Users/shz/Code\" subsumes \"/Users/shz/Code/project-a\" if:\n // - narrowerPath starts with broaderPath + \"/\"\n return narrowerPath.startsWith(broaderPath + \"/\");\n }\n\n // 6. Mixed pattern types don't subsume each other\n return false;\n}\n\n/**\n * Find all subsumption relationships in a permission list\n *\n * Returns a map of broader permissions to the narrower permissions\n * they subsume. This allows identifying which permissions can be\n * removed from the list.\n *\n * @param permissions - Array of permissions to analyze\n * @returns Array of subsumption results\n *\n * @example\n * ```typescript\n * const permissions = [\n * parsePermission(\"Bash(git:*)\", \"allow\"),\n * parsePermission(\"Bash(git log:*)\", \"allow\"),\n * parsePermission(\"Bash(git worktree:*)\", \"allow\"),\n * parsePermission(\"Bash(npm:*)\", \"allow\"),\n * ];\n *\n * const results = detectSubsumptions(permissions);\n * // Returns: [\n * // {\n * // broader: { raw: \"Bash(git:*)\", ... },\n * // narrower: [\n * // { raw: \"Bash(git log:*)\", ... },\n * // { raw: \"Bash(git worktree:*)\", ... }\n * // ]\n * // }\n * // ]\n * ```\n */\nexport function detectSubsumptions(\n permissions: Permission[],\n): SubsumptionResult[] {\n const results: SubsumptionResult[] = [];\n const processedBroader = new Set<string>();\n\n for (let i = 0; i < permissions.length; i++) {\n const broader = permissions[i];\n\n // Skip if already processed as a broader permission\n if (processedBroader.has(broader.raw)) {\n continue;\n }\n\n const narrowerPerms: Permission[] = [];\n\n // Check all other permissions to see if this one subsumes them\n for (let j = 0; j < permissions.length; j++) {\n if (i === j) continue; // Skip comparing with self\n\n const narrower = permissions[j];\n\n if (subsumes(broader, narrower)) {\n narrowerPerms.push(narrower);\n }\n }\n\n // If this permission subsumes others, record it\n if (narrowerPerms.length > 0) {\n results.push({\n broader,\n narrower: narrowerPerms,\n });\n processedBroader.add(broader.raw);\n }\n }\n\n return results;\n}\n\n/**\n * Remove subsumed permissions from a list, keeping only the broadest ones\n *\n * This is the main function for consolidating permissions.\n * It finds all subsumption relationships and removes narrower\n * permissions, leaving only the broadest ones.\n *\n * @param permissionStrings - Array of raw permission strings\n * @param category - Permission category (for parsing)\n * @returns Array of permission strings with subsumed ones removed\n *\n * @example\n * ```typescript\n * const permissions = [\n * \"Bash(git:*)\",\n * \"Bash(git log:*)\",\n * \"Bash(git worktree:*)\",\n * \"Bash(npm:*)\",\n * ];\n *\n * const result = removeSubsumed(permissions, \"allow\");\n * // Returns: [\"Bash(git:*)\", \"Bash(npm:*)\"]\n * ```\n */\nexport function removeSubsumed(\n permissionStrings: string[],\n category: PermissionCategory,\n): string[] {\n // Parse all permission strings\n const permissions = permissionStrings\n .map((raw) => {\n try {\n return parsePermission(raw, category);\n } catch {\n // Keep malformed permissions as-is (don't filter them out)\n return null;\n }\n })\n .filter((p): p is Permission => p !== null);\n\n // Detect subsumptions\n const subsumptions = detectSubsumptions(permissions);\n\n // Build set of narrower permissions to remove\n const toRemove = new Set<string>();\n for (const result of subsumptions) {\n for (const narrower of result.narrower) {\n toRemove.add(narrower.raw);\n }\n }\n\n // Filter out subsumed permissions\n return permissionStrings.filter((perm) => !toRemove.has(perm));\n}\n","/**\n * Permission merging with subsumption and conflict resolution\n */\nimport { parsePermission } from \"./parser.js\";\nimport { removeSubsumed, subsumes } from \"./subsumption.js\";\nimport type { ConsolidationResult, Permissions, PermissionsAdded } from \"./types.js\";\n\n/**\n * Merge permissions from global settings and multiple local settings files\n *\n * Process:\n * 1. Combine all permissions by category (allow/deny/ask)\n * 2. Apply subsumption to remove narrower permissions\n * 3. Resolve conflicts (deny wins over allow)\n * 4. Deduplicate using Sets\n * 5. Sort alphabetically\n *\n * @param global - Global settings permissions (baseline)\n * @param local - Array of local settings permissions to merge in\n * @returns Merged permissions and consolidation statistics\n *\n * @example\n * ```typescript\n * const global = { allow: [\"Bash(ls:*)\"] };\n * const local = [\n * { allow: [\"Bash(git:*)\", \"Bash(git log:*)\"] },\n * { deny: [\"Bash(rm:*)\"] }\n * ];\n *\n * const { merged, result } = mergePermissions(global, local);\n * // merged.allow: [\"Bash(git:*)\", \"Bash(ls:*)\"] // git log:* removed by subsumption\n * // merged.deny: [\"Bash(rm:*)\"]\n * // result.subsumed: [\"Bash(git log:*)\"]\n * ```\n */\nexport function mergePermissions(\n global: Permissions,\n local: Permissions[],\n): { merged: Permissions; result: ConsolidationResult } {\n // Track original global permissions for computing added\n const originalGlobal = {\n allow: new Set(global.allow || []),\n deny: new Set(global.deny || []),\n ask: new Set(global.ask || []),\n };\n\n // Step 1: Combine all permissions by category\n const combined: Permissions = {\n allow: [...(global.allow || [])],\n deny: [...(global.deny || [])],\n ask: [...(global.ask || [])],\n };\n\n let filesProcessed = 0;\n let filesSkipped = 0;\n\n for (const localPerms of local) {\n let hasPerms = false;\n\n if (localPerms.allow && localPerms.allow.length > 0) {\n combined.allow?.push(...localPerms.allow);\n hasPerms = true;\n }\n if (localPerms.deny && localPerms.deny.length > 0) {\n combined.deny?.push(...localPerms.deny);\n hasPerms = true;\n }\n if (localPerms.ask && localPerms.ask.length > 0) {\n combined.ask?.push(...localPerms.ask);\n hasPerms = true;\n }\n\n if (hasPerms) {\n filesProcessed++;\n } else {\n filesSkipped++;\n }\n }\n\n // Step 2: Apply subsumption to each category\n const allSubsumed: string[] = [];\n\n const afterSubsumption: Permissions = {};\n\n if (combined.allow && combined.allow.length > 0) {\n const before = new Set(combined.allow);\n combined.allow = removeSubsumed(combined.allow, \"allow\");\n const after = new Set(combined.allow);\n\n // Track what was removed\n for (const perm of before) {\n if (!after.has(perm)) {\n allSubsumed.push(perm);\n }\n }\n }\n\n if (combined.deny && combined.deny.length > 0) {\n const before = new Set(combined.deny);\n combined.deny = removeSubsumed(combined.deny, \"deny\");\n const after = new Set(combined.deny);\n\n // Track what was removed\n for (const perm of before) {\n if (!after.has(perm)) {\n allSubsumed.push(perm);\n }\n }\n }\n\n if (combined.ask && combined.ask.length > 0) {\n const before = new Set(combined.ask);\n combined.ask = removeSubsumed(combined.ask, \"ask\");\n const after = new Set(combined.ask);\n\n // Track what was removed\n for (const perm of before) {\n if (!after.has(perm)) {\n allSubsumed.push(perm);\n }\n }\n }\n\n afterSubsumption.allow = combined.allow;\n afterSubsumption.deny = combined.deny;\n afterSubsumption.ask = combined.ask;\n\n // Step 3: Resolve conflicts (deny wins over allow)\n const {\n resolved,\n conflictCount,\n subsumed: conflictSubsumed,\n } = resolveConflicts(afterSubsumption);\n\n // Combine subsumptions from step 2 and step 3\n const subsumed = [...allSubsumed, ...conflictSubsumed];\n\n // Step 4: Deduplicate using Sets and sort\n const merged: Permissions = {};\n\n if (resolved.allow && resolved.allow.length > 0) {\n merged.allow = Array.from(new Set(resolved.allow)).sort();\n }\n\n if (resolved.deny && resolved.deny.length > 0) {\n merged.deny = Array.from(new Set(resolved.deny)).sort();\n }\n\n if (resolved.ask && resolved.ask.length > 0) {\n merged.ask = Array.from(new Set(resolved.ask)).sort();\n }\n\n // Step 5: Compute what was added\n const added: PermissionsAdded = {\n allow: [],\n deny: [],\n ask: [],\n };\n\n for (const perm of merged.allow || []) {\n if (!originalGlobal.allow.has(perm)) {\n added.allow.push(perm);\n }\n }\n\n for (const perm of merged.deny || []) {\n if (!originalGlobal.deny.has(perm)) {\n added.deny.push(perm);\n }\n }\n\n for (const perm of merged.ask || []) {\n if (!originalGlobal.ask.has(perm)) {\n added.ask.push(perm);\n }\n }\n\n // Build result\n const result: ConsolidationResult = {\n filesScanned: local.length,\n filesProcessed,\n filesSkipped,\n added,\n subsumed,\n conflictsResolved: conflictCount,\n };\n\n return { merged, result };\n}\n\n/**\n * Resolve conflicts between allow and deny permissions\n *\n * Rules:\n * - If exact match in both allow and deny: keep in deny, remove from allow\n * - If deny has broader permission that subsumes allow: remove from allow\n *\n * This implements a security-first approach: deny always wins.\n *\n * @param permissions - Permissions with potential conflicts\n * @returns Resolved permissions with conflicts removed, count of conflicts, and list of subsumed permissions\n *\n * @example\n * ```typescript\n * const permissions = {\n * allow: [\"Bash(git log:*)\", \"Bash(npm:*)\"],\n * deny: [\"Bash(git:*)\"]\n * };\n *\n * const { resolved, conflictCount } = resolveConflicts(permissions);\n * // resolved.allow: [\"Bash(npm:*)\"] // git log:* removed (subsumed by deny git:*)\n * // resolved.deny: [\"Bash(git:*)\"]\n * // conflictCount: 1\n * ```\n */\nexport function resolveConflicts(permissions: Permissions): {\n resolved: Permissions;\n conflictCount: number;\n subsumed: string[];\n} {\n const allow = permissions.allow || [];\n const deny = permissions.deny || [];\n const ask = permissions.ask || [];\n\n const denySet = new Set(deny);\n const subsumed: string[] = [];\n let conflictCount = 0;\n\n // Check each allow permission against deny permissions\n const allowToRemove = new Set<string>();\n\n for (const allowPerm of allow) {\n // Exact match: move to deny\n if (denySet.has(allowPerm)) {\n allowToRemove.add(allowPerm);\n conflictCount++;\n continue;\n }\n\n // Check if any deny permission subsumes this allow permission\n for (const denyPerm of deny) {\n try {\n const allowParsed = parsePermission(allowPerm, \"allow\");\n const denyParsed = parsePermission(denyPerm, \"deny\");\n\n if (subsumes(denyParsed, allowParsed)) {\n // Deny subsumes allow - remove from allow\n allowToRemove.add(allowPerm);\n subsumed.push(allowPerm);\n conflictCount++;\n break;\n }\n } catch {\n // Skip malformed permissions\n continue;\n }\n }\n }\n\n // Build resolved permissions\n const resolved: Permissions = {\n allow: allow.filter((p) => !allowToRemove.has(p)),\n deny,\n ask,\n };\n\n return { resolved, conflictCount, subsumed };\n}\n","/**\n * Backup management for Claude Code settings files\n */\nimport fs from \"fs/promises\";\n\n/**\n * Create a timestamped backup of a settings file\n *\n * Backup format: `<original-path>.backup.YYYY-MM-DD-HHmmss`\n *\n * Example: `settings.json.backup.2026-01-08-143022`\n *\n * @param settingsPath - Absolute path to settings file to back up\n * @returns Promise resolving to backup file path\n * @throws Error if source file doesn't exist or backup fails\n *\n * @example\n * ```typescript\n * const backupPath = await createBackup(\"/Users/shz/.claude/settings.json\");\n * // Returns: \"/Users/shz/.claude/settings.json.backup.2026-01-08-143022\"\n * ```\n */\nexport async function createBackup(settingsPath: string): Promise<string> {\n try {\n // Verify source file exists\n await fs.access(settingsPath, fs.constants.R_OK);\n\n // Generate timestamp: YYYY-MM-DD-HHmmss\n const now = new Date();\n const timestamp = [\n now.getFullYear(),\n String(now.getMonth() + 1).padStart(2, \"0\"),\n String(now.getDate()).padStart(2, \"0\"),\n ].join(\"-\") + \"-\" + [\n String(now.getHours()).padStart(2, \"0\"),\n String(now.getMinutes()).padStart(2, \"0\"),\n String(now.getSeconds()).padStart(2, \"0\"),\n ].join(\"\");\n\n // Build backup path\n const backupPath = `${settingsPath}.backup.${timestamp}`;\n\n // Copy file to backup location\n await fs.copyFile(settingsPath, backupPath);\n\n return backupPath;\n } catch (error) {\n if (error instanceof Error) {\n if (error.message.includes(\"ENOENT\")) {\n throw new Error(`Settings file not found: ${settingsPath}`);\n }\n if (error.message.includes(\"EACCES\")) {\n throw new Error(`Permission denied: ${settingsPath}`);\n }\n throw new Error(`Failed to create backup: ${error.message}`);\n }\n throw error;\n }\n}\n","/**\n * Formatting and reporting of consolidation results\n */\nimport type { ConsolidationResult } from \"../permissions/types.js\";\n\n/**\n * Format consolidation result as user-friendly text report\n *\n * Shows:\n * - Files scanned/processed/skipped\n * - Permissions added by category (allow/deny/ask)\n * - Conflicts resolved\n * - Subsumed permissions removed\n * - Backup path (if created)\n * - Instructions (if preview mode) or confirmation (if written)\n *\n * @param result - Consolidation result data\n * @param previewOnly - Whether this is preview-only mode (default behavior)\n * @param globalSettingsPath - Path to global settings file\n * @param outputFile - Optional output file path\n * @returns Formatted report string\n *\n * @example\n * ```typescript\n * const result = {\n * filesScanned: 12,\n * filesProcessed: 10,\n * filesSkipped: 2,\n * added: {\n * allow: [\"Bash(git:*)\", \"Bash(npm:*)\"],\n * deny: [\"Bash(rm:*)\"],\n * ask: []\n * },\n * subsumed: [\"Bash(git log:*)\", \"Bash(git worktree:*)\"],\n * conflictsResolved: 1,\n * backupPath: \"/Users/shz/.claude/settings.json.backup.2026-01-08-143022\"\n * };\n *\n * console.log(formatReport(result, true, \"/Users/shz/.claude/settings.json\"));\n * // Outputs formatted report with instructions\n * ```\n */\nexport function formatReport(\n result: ConsolidationResult,\n previewOnly: boolean,\n globalSettingsPath?: string,\n outputFile?: string,\n): string {\n const lines: string[] = [];\n\n // Header\n lines.push(\"Scanning for Claude Code settings files...\");\n lines.push(\"\");\n\n // Files summary\n lines.push(`Found ${result.filesScanned} settings files`);\n lines.push(` Processed: ${result.filesProcessed}`);\n if (result.filesSkipped > 0) {\n lines.push(` Skipped: ${result.filesSkipped} (no permissions)`);\n }\n lines.push(\"\");\n\n // Permissions added\n const totalAdded = result.added.allow.length\n + result.added.deny.length\n + result.added.ask.length;\n\n if (totalAdded > 0) {\n lines.push(`Permissions to add: ${totalAdded}`);\n\n if (result.added.allow.length > 0) {\n lines.push(\"\");\n lines.push(\" allow:\");\n for (const perm of result.added.allow) {\n lines.push(` + ${perm}`);\n }\n }\n\n if (result.added.deny.length > 0) {\n lines.push(\"\");\n lines.push(\" deny:\");\n for (const perm of result.added.deny) {\n lines.push(` + ${perm}`);\n }\n }\n\n if (result.added.ask.length > 0) {\n lines.push(\"\");\n lines.push(\" ask:\");\n for (const perm of result.added.ask) {\n lines.push(` + ${perm}`);\n }\n }\n } else {\n lines.push(\"No new permissions to add (all permissions already in global settings)\");\n }\n\n lines.push(\"\");\n\n // Subsumption results\n if (result.subsumed.length > 0) {\n lines.push(`Subsumed permissions removed: ${result.subsumed.length}`);\n lines.push(\" (narrower permissions replaced by broader ones)\");\n for (const perm of result.subsumed) {\n lines.push(` - ${perm}`);\n }\n lines.push(\"\");\n }\n\n // Conflicts\n if (result.conflictsResolved > 0) {\n lines.push(`Conflicts resolved: ${result.conflictsResolved}`);\n lines.push(\" (permissions moved from allow to deny)\");\n lines.push(\"\");\n }\n\n // Backup\n if (result.backupPath) {\n lines.push(`Backup created: ${result.backupPath}`);\n lines.push(\"\");\n }\n\n // Summary\n lines.push(\"Summary:\");\n lines.push(` Files scanned: ${result.filesScanned}`);\n lines.push(\n ` Permissions added: ${result.added.allow.length} allow, ${result.added.deny.length} deny, ${result.added.ask.length} ask`,\n );\n if (result.subsumed.length > 0) {\n lines.push(` Subsumed removed: ${result.subsumed.length}`);\n }\n if (result.conflictsResolved > 0) {\n lines.push(` Conflicts resolved: ${result.conflictsResolved}`);\n }\n\n // Final status message\n lines.push(\"\");\n if (previewOnly) {\n lines.push(\"ℹ️ Preview mode: No changes written\");\n lines.push(\"\");\n lines.push(\"To apply changes:\");\n lines.push(` • Modify global settings: spx claude settings consolidate --write`);\n lines.push(` • Write to file: spx claude settings consolidate --output-file /path/to/file`);\n } else if (outputFile) {\n lines.push(`✓ Settings written to: ${result.outputPath || outputFile}`);\n lines.push(\"\");\n lines.push(\"To apply to your global settings:\");\n lines.push(` • Review the file, then copy to: ${globalSettingsPath || \"~/.claude/settings.json\"}`);\n lines.push(` • Or run: spx claude settings consolidate --write`);\n } else {\n lines.push(`✓ Global settings updated: ${globalSettingsPath || \"~/.claude/settings.json\"}`);\n }\n\n return lines.join(\"\\n\");\n}\n","/**\n * Atomic file writing for Claude Code settings\n */\nimport fs from \"fs/promises\";\nimport os from \"os\";\nimport path from \"path\";\nimport type { ClaudeSettings } from \"../permissions/types.js\";\n\n/**\n * Filesystem abstraction for dependency injection\n *\n * Enables testing error paths without mocking.\n */\nexport interface FileSystem {\n writeFile(path: string, content: string): Promise<void>;\n rename(oldPath: string, newPath: string): Promise<void>;\n unlink(path: string): Promise<void>;\n mkdir(path: string, options?: { recursive?: boolean }): Promise<void>;\n}\n\n/**\n * Production filesystem implementation\n */\nconst realFs: FileSystem = {\n writeFile: (path, content) => fs.writeFile(path, content, \"utf-8\"),\n rename: fs.rename,\n unlink: fs.unlink,\n mkdir: async (path, options) => {\n await fs.mkdir(path, options);\n },\n};\n\n/**\n * Atomically write settings to a file\n *\n * Uses temp file + rename pattern for atomicity:\n * 1. Write to temporary file\n * 2. Rename to target (atomic operation on most filesystems)\n *\n * Preserves JSON formatting with 2-space indentation.\n *\n * @param filePath - Absolute path to settings file\n * @param settings - Settings object to write\n * @param deps - Dependencies (for testing)\n * @throws Error if write fails\n *\n * @example\n * ```typescript\n * const settings = {\n * permissions: {\n * allow: [\"Bash(git:*)\", \"Bash(npm:*)\"]\n * }\n * };\n * await writeSettings(\"/Users/shz/.claude/settings.json\", settings);\n * ```\n */\nexport async function writeSettings(\n filePath: string,\n settings: ClaudeSettings,\n deps: { fs: FileSystem } = { fs: realFs },\n): Promise<void> {\n // Ensure directory exists\n const dir = path.dirname(filePath);\n await deps.fs.mkdir(dir, { recursive: true });\n\n // Generate temporary file path\n const tempPath = path.join(\n os.tmpdir(),\n `settings-${Date.now()}-${Math.random().toString(36).substring(7)}.json`,\n );\n\n try {\n // Format JSON with 2-space indentation and trailing newline\n const content = JSON.stringify(settings, null, 2) + \"\\n\";\n\n // Write to temp file\n await deps.fs.writeFile(tempPath, content);\n\n // Atomic rename\n await deps.fs.rename(tempPath, filePath);\n } catch (error) {\n // Cleanup temp file on failure\n try {\n await deps.fs.unlink(tempPath);\n } catch {\n // Ignore cleanup errors\n }\n\n // Re-throw original error\n if (error instanceof Error) {\n throw new Error(`Failed to write settings: ${error.message}`);\n }\n throw error;\n }\n}\n","/**\n * Claude domain - Manage Claude Code settings and plugins\n */\nimport type { Command } from \"commander\";\nimport { initCommand } from \"../../commands/claude/init.js\";\nimport { consolidateCommand } from \"../../commands/claude/settings/consolidate.js\";\nimport type { Domain } from \"../types.js\";\n\n/**\n * Register claude domain commands\n *\n * @param claudeCmd - Commander.js claude domain command\n */\nfunction registerClaudeCommands(claudeCmd: Command): void {\n // init command\n claudeCmd\n .command(\"init\")\n .description(\"Initialize or update spx-claude marketplace plugin\")\n .action(async () => {\n try {\n const output = await initCommand({ cwd: process.cwd() });\n console.log(output);\n } catch (error) {\n console.error(\n \"Error:\",\n error instanceof Error ? error.message : String(error),\n );\n process.exit(1);\n }\n });\n\n // settings subcommand group\n const settingsCmd = claudeCmd\n .command(\"settings\")\n .description(\"Manage Claude Code settings\");\n\n // settings consolidate command\n settingsCmd\n .command(\"consolidate\")\n .description(\n \"Consolidate permissions from project-specific settings into global settings\",\n )\n .option(\"--write\", \"Write changes to global settings file (default: preview only)\")\n .option(\n \"--output-file <path>\",\n \"Write merged settings to specified file instead of global settings\",\n )\n .option(\n \"--root <path>\",\n \"Root directory to scan for settings files (default: ~/Code)\",\n )\n .option(\n \"--global-settings <path>\",\n \"Path to global settings file (default: ~/.claude/settings.json)\",\n )\n .action(\n async (options: {\n write?: boolean;\n outputFile?: string;\n root?: string;\n globalSettings?: string;\n }) => {\n try {\n // Validate mutually exclusive options\n if (options.write && options.outputFile) {\n console.error(\n \"Error: --write and --output-file are mutually exclusive\\n\"\n + \"Use --write to modify global settings, or --output-file to write to a different location\",\n );\n process.exit(1);\n }\n\n const output = await consolidateCommand({\n write: options.write,\n outputFile: options.outputFile,\n root: options.root,\n globalSettings: options.globalSettings,\n });\n console.log(output);\n } catch (error) {\n console.error(\n \"Error:\",\n error instanceof Error ? error.message : String(error),\n );\n process.exit(1);\n }\n },\n );\n}\n\n/**\n * Claude domain - Manage Claude Code settings and plugins\n */\nexport const claudeDomain: Domain = {\n name: \"claude\",\n description: \"Manage Claude Code settings and plugins\",\n register: (program: Command) => {\n const claudeCmd = program\n .command(\"claude\")\n .description(\"Manage Claude Code settings and plugins\");\n\n registerClaudeCommands(claudeCmd);\n },\n};\n","/**\n * Session archive CLI command handler.\n *\n * @module commands/session/archive\n */\n\nimport { mkdir, rename, stat } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\n\nimport { SessionNotFoundError } from \"../../session/errors.js\";\nimport { DEFAULT_SESSION_CONFIG, type SessionDirectoryConfig } from \"../../session/show.js\";\n\n/**\n * Options for the archive command.\n */\nexport interface ArchiveOptions {\n /** Session ID to archive */\n sessionId: string;\n /** Custom sessions directory */\n sessionsDir?: string;\n}\n\n/**\n * Error thrown when a session is already archived.\n */\nexport class SessionAlreadyArchivedError extends Error {\n /** The session ID that is already archived */\n readonly sessionId: string;\n\n constructor(sessionId: string) {\n super(`Session already archived: ${sessionId}.`);\n this.name = \"SessionAlreadyArchivedError\";\n this.sessionId = sessionId;\n }\n}\n\n/**\n * Finds the source path for a session to archive.\n *\n * @param sessionId - Session ID to find\n * @param config - Directory configuration\n * @returns Source path and target path for archiving\n * @throws {SessionNotFoundError} When session is not found in todo or doing\n * @throws {SessionAlreadyArchivedError} When session is already in archive\n */\nexport async function resolveArchivePaths(\n sessionId: string,\n config: SessionDirectoryConfig,\n): Promise<{ source: string; target: string }> {\n const filename = `${sessionId}.md`;\n const todoPath = join(config.todoDir, filename);\n const doingPath = join(config.doingDir, filename);\n const archivePath = join(config.archiveDir, filename);\n\n // Check if already archived\n try {\n const archiveStats = await stat(archivePath);\n if (archiveStats.isFile()) {\n throw new SessionAlreadyArchivedError(sessionId);\n }\n } catch (error) {\n // ENOENT is expected - session not in archive\n if (error instanceof Error && \"code\" in error && error.code !== \"ENOENT\") {\n throw error;\n }\n // Rethrow SessionAlreadyArchivedError\n if (error instanceof SessionAlreadyArchivedError) {\n throw error;\n }\n }\n\n // Check todo directory first\n try {\n const todoStats = await stat(todoPath);\n if (todoStats.isFile()) {\n return { source: todoPath, target: archivePath };\n }\n } catch {\n // File not in todo, continue to check doing\n }\n\n // Check doing directory\n try {\n const doingStats = await stat(doingPath);\n if (doingStats.isFile()) {\n return { source: doingPath, target: archivePath };\n }\n } catch {\n // File not in doing either\n }\n\n // Session not found in either directory\n throw new SessionNotFoundError(sessionId);\n}\n\n/**\n * Executes the archive command.\n *\n * @param options - Command options\n * @returns Formatted output for display\n * @throws {SessionNotFoundError} When session not found\n * @throws {SessionAlreadyArchivedError} When session is already archived\n */\nexport async function archiveCommand(options: ArchiveOptions): Promise<string> {\n // Build config from options\n const config: SessionDirectoryConfig = options.sessionsDir\n ? {\n todoDir: join(options.sessionsDir, \"todo\"),\n doingDir: join(options.sessionsDir, \"doing\"),\n archiveDir: join(options.sessionsDir, \"archive\"),\n }\n : DEFAULT_SESSION_CONFIG;\n\n // Resolve source and target paths\n const { source, target } = await resolveArchivePaths(options.sessionId, config);\n\n // Ensure archive directory exists (FR2: create if missing)\n await mkdir(dirname(target), { recursive: true });\n\n // Move to archive\n await rename(source, target);\n\n return `Archived session: ${options.sessionId}\\nArchive location: ${target}`;\n}\n","/**\n * Session-specific error types.\n *\n * @module session/errors\n */\n\n/**\n * Base class for session errors.\n */\nexport class SessionError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"SessionError\";\n }\n}\n\n/**\n * Error thrown when a session cannot be found.\n */\nexport class SessionNotFoundError extends SessionError {\n /** The session ID that was not found */\n readonly sessionId: string;\n\n constructor(sessionId: string) {\n super(`Session not found: ${sessionId}. Check the session ID and try again.`);\n this.name = \"SessionNotFoundError\";\n this.sessionId = sessionId;\n }\n}\n\n/**\n * Error thrown when a session is not available for claiming (already claimed).\n */\nexport class SessionNotAvailableError extends SessionError {\n /** The session ID that is not available */\n readonly sessionId: string;\n\n constructor(sessionId: string) {\n super(`Session not available: ${sessionId}. It may have been claimed by another agent.`);\n this.name = \"SessionNotAvailableError\";\n this.sessionId = sessionId;\n }\n}\n\n/**\n * Error thrown when session content is invalid.\n */\nexport class SessionInvalidContentError extends SessionError {\n constructor(reason: string) {\n super(`Invalid session content: ${reason}`);\n this.name = \"SessionInvalidContentError\";\n }\n}\n\n/**\n * Error thrown when trying to release a session that is not currently claimed.\n */\nexport class SessionNotClaimedError extends SessionError {\n /** The session ID that is not claimed */\n readonly sessionId: string;\n\n constructor(sessionId: string) {\n super(`Session not claimed: ${sessionId}. The session is not in the doing directory.`);\n this.name = \"SessionNotClaimedError\";\n this.sessionId = sessionId;\n }\n}\n\n/**\n * Error thrown when no sessions are available for auto-pickup.\n */\nexport class NoSessionsAvailableError extends SessionError {\n constructor() {\n super(\"No sessions available. The todo directory is empty.\");\n this.name = \"NoSessionsAvailableError\";\n }\n}\n","/**\n * Session display utilities for showing session content without claiming.\n *\n * @module session/show\n */\n\nimport { join } from \"node:path\";\n\nimport { DEFAULT_CONFIG } from \"../config/defaults.js\";\nimport { parseSessionMetadata } from \"./list.js\";\nimport type { SessionStatus } from \"./types.js\";\n\n/**\n * Configuration for session directory paths.\n */\nexport interface SessionDirectoryConfig {\n /** Path to todo directory */\n todoDir: string;\n /** Path to doing directory */\n doingDir: string;\n /** Path to archive directory */\n archiveDir: string;\n}\n\n/**\n * Default session directory configuration.\n *\n * Derived from DEFAULT_CONFIG to ensure single source of truth for all path components.\n * NEVER hardcode path strings like \".spx\", \"sessions\", \"todo\" - always derive from config.\n */\nconst { dir: sessionsBaseDir, statusDirs } = DEFAULT_CONFIG.sessions;\n\nexport const DEFAULT_SESSION_CONFIG: SessionDirectoryConfig = {\n todoDir: join(sessionsBaseDir, statusDirs.todo),\n doingDir: join(sessionsBaseDir, statusDirs.doing),\n archiveDir: join(sessionsBaseDir, statusDirs.archive),\n};\n\n/**\n * Order to search directories (matches priority: todo first, then doing, then archive).\n */\nexport const SEARCH_ORDER: SessionStatus[] = [\"todo\", \"doing\", \"archive\"];\n\n/**\n * Options for formatting show output.\n */\nexport interface ShowOutputOptions {\n /** Current status of the session */\n status: SessionStatus;\n}\n\n/**\n * Resolves possible file paths for a session ID across all status directories.\n *\n * @param id - Session ID (timestamp format)\n * @param config - Directory configuration\n * @returns Array of possible file paths in search order\n *\n * @example\n * ```typescript\n * const paths = resolveSessionPaths('2026-01-13_08-01-05', {\n * todoDir: '.spx/sessions/todo',\n * doingDir: '.spx/sessions/doing',\n * archiveDir: '.spx/sessions/archive',\n * });\n * // => [\n * // '.spx/sessions/todo/2026-01-13_08-01-05.md',\n * // '.spx/sessions/doing/2026-01-13_08-01-05.md',\n * // '.spx/sessions/archive/2026-01-13_08-01-05.md',\n * // ]\n * ```\n */\nexport function resolveSessionPaths(\n id: string,\n config: SessionDirectoryConfig = DEFAULT_SESSION_CONFIG,\n): string[] {\n const filename = `${id}.md`;\n\n return [\n `${config.todoDir}/${filename}`,\n `${config.doingDir}/${filename}`,\n `${config.archiveDir}/${filename}`,\n ];\n}\n\n/**\n * Formats session content for display with metadata header.\n *\n * @param content - Raw session file content\n * @param options - Display options including status\n * @returns Formatted output string with metadata header\n *\n * @example\n * ```typescript\n * const output = formatShowOutput(sessionContent, { status: 'todo' });\n * // => \"Status: todo\\nPriority: high\\n---\\n# Session Content...\"\n * ```\n */\nexport function formatShowOutput(\n content: string,\n options: ShowOutputOptions,\n): string {\n const metadata = parseSessionMetadata(content);\n\n // Build header with extracted metadata\n const headerLines: string[] = [\n `Status: ${options.status}`,\n `Priority: ${metadata.priority}`,\n ];\n\n // Add optional metadata if present\n if (metadata.id) {\n headerLines.unshift(`ID: ${metadata.id}`);\n }\n if (metadata.branch) {\n headerLines.push(`Branch: ${metadata.branch}`);\n }\n if (metadata.tags.length > 0) {\n headerLines.push(`Tags: ${metadata.tags.join(\", \")}`);\n }\n if (metadata.createdAt) {\n headerLines.push(`Created: ${metadata.createdAt}`);\n }\n\n // Combine header with separator and original content\n const header = headerLines.join(\"\\n\");\n const separator = \"\\n\" + \"─\".repeat(40) + \"\\n\\n\";\n\n return header + separator + content;\n}\n","/**\n * Default configuration for spx CLI\n *\n * This module defines the default directory structure and configuration\n * constants used throughout the spx CLI. All directory paths should reference\n * this configuration instead of using hardcoded strings.\n *\n * @module config/defaults\n */\n\n/**\n * Configuration schema for spx CLI directory structure\n */\nexport interface SpxConfig {\n /**\n * Specifications directory configuration\n */\n specs: {\n /**\n * Base directory for all specification files\n * @default \"specs\"\n */\n root: string;\n\n /**\n * Work items organization\n */\n work: {\n /**\n * Container directory for all work items\n * @default \"work\"\n */\n dir: string;\n\n /**\n * Status-based subdirectories for work items\n */\n statusDirs: {\n /**\n * Active work directory\n * @default \"doing\"\n */\n doing: string;\n\n /**\n * Future work directory\n * @default \"backlog\"\n */\n backlog: string;\n\n /**\n * Completed work directory\n * @default \"archive\"\n */\n done: string;\n };\n };\n\n /**\n * Product-level architecture decision records directory\n * @default \"decisions\"\n */\n decisions: string;\n\n /**\n * Templates directory (optional)\n * @default \"templates\"\n */\n templates?: string;\n };\n\n /**\n * Session handoff files configuration\n */\n sessions: {\n /**\n * Directory for session handoff files\n * @default \".spx/sessions\"\n */\n dir: string;\n\n /**\n * Status-based subdirectories for sessions\n */\n statusDirs: {\n /**\n * Available sessions directory\n * @default \"todo\"\n */\n todo: string;\n\n /**\n * Claimed sessions directory\n * @default \"doing\"\n */\n doing: string;\n\n /**\n * Archived sessions directory\n * @default \"archive\"\n */\n archive: string;\n };\n };\n}\n\n/**\n * Default configuration constant\n *\n * This is the embedded default configuration that spx uses when no\n * .spx/config.json file exists in the product.\n *\n * DO NOT modify this constant at runtime - it should remain immutable.\n */\nexport const DEFAULT_CONFIG = {\n specs: {\n root: \"specs\",\n work: {\n dir: \"work\",\n statusDirs: {\n doing: \"doing\",\n backlog: \"backlog\",\n done: \"archive\",\n },\n },\n decisions: \"decisions\",\n templates: \"templates\",\n },\n sessions: {\n dir: \".spx/sessions\",\n statusDirs: {\n todo: \"todo\",\n doing: \"doing\",\n archive: \"archive\",\n },\n },\n} as const satisfies SpxConfig;\n","/**\n * Session listing and sorting utilities.\n *\n * @module session/list\n */\n\nimport { parse as parseYaml } from \"yaml\";\n\nimport { parseSessionId } from \"./timestamp\";\nimport { DEFAULT_PRIORITY, PRIORITY_ORDER, type Session, type SessionMetadata, type SessionPriority } from \"./types\";\n\n/**\n * Regular expression to match YAML front matter.\n * Matches content between opening `---` and closing `---` or `...`\n */\nconst FRONT_MATTER_PATTERN = /^---\\r?\\n([\\s\\S]*?)\\r?\\n(?:---|\\.\\.\\.)\\r?\\n?/;\n\n/**\n * Validates if a value is a valid priority.\n */\nfunction isValidPriority(value: unknown): value is SessionPriority {\n return value === \"high\" || value === \"medium\" || value === \"low\";\n}\n\n/**\n * Parses YAML front matter from session content to extract metadata.\n *\n * @param content - Full session file content\n * @returns Extracted metadata with defaults for missing fields\n *\n * @example\n * ```typescript\n * const metadata = parseSessionMetadata(`---\n * priority: high\n * tags: [bug, urgent]\n * ---\n * # Session content`);\n * // => { priority: 'high', tags: ['bug', 'urgent'] }\n * ```\n */\nexport function parseSessionMetadata(content: string): SessionMetadata {\n const match = FRONT_MATTER_PATTERN.exec(content);\n\n if (!match) {\n return {\n priority: DEFAULT_PRIORITY,\n tags: [],\n };\n }\n\n try {\n const parsed = parseYaml(match[1]) as Record<string, unknown>;\n\n if (!parsed || typeof parsed !== \"object\") {\n return {\n priority: DEFAULT_PRIORITY,\n tags: [],\n };\n }\n\n // Extract priority with validation\n const priority = isValidPriority(parsed.priority)\n ? parsed.priority\n : DEFAULT_PRIORITY;\n\n // Extract tags, ensuring it's an array of strings\n let tags: string[] = [];\n if (Array.isArray(parsed.tags)) {\n tags = parsed.tags.filter((t): t is string => typeof t === \"string\");\n }\n\n // Build metadata object\n const metadata: SessionMetadata = {\n priority,\n tags,\n };\n\n // Add optional fields if present\n if (typeof parsed.id === \"string\") {\n metadata.id = parsed.id;\n }\n if (typeof parsed.branch === \"string\") {\n metadata.branch = parsed.branch;\n }\n if (typeof parsed.created_at === \"string\") {\n metadata.createdAt = parsed.created_at;\n }\n if (typeof parsed.working_directory === \"string\") {\n metadata.workingDirectory = parsed.working_directory;\n }\n if (Array.isArray(parsed.specs)) {\n metadata.specs = parsed.specs.filter(\n (s): s is string => typeof s === \"string\",\n );\n }\n if (Array.isArray(parsed.files)) {\n metadata.files = parsed.files.filter(\n (f): f is string => typeof f === \"string\",\n );\n }\n\n return metadata;\n } catch {\n // Malformed YAML, return defaults\n return {\n priority: DEFAULT_PRIORITY,\n tags: [],\n };\n }\n}\n\n/**\n * Sorts sessions by priority (high first) then by timestamp (newest first).\n *\n * @param sessions - Array of sessions to sort\n * @returns New sorted array (does not mutate input)\n *\n * @example\n * ```typescript\n * const sorted = sortSessions([\n * { id: 'a', metadata: { priority: 'low' } },\n * { id: 'b', metadata: { priority: 'high' } },\n * ]);\n * // => [{ id: 'b', ... }, { id: 'a', ... }]\n * ```\n */\nexport function sortSessions(sessions: Session[]): Session[] {\n return [...sessions].sort((a, b) => {\n // First: sort by priority (high = 0, medium = 1, low = 2)\n const priorityA = PRIORITY_ORDER[a.metadata.priority];\n const priorityB = PRIORITY_ORDER[b.metadata.priority];\n\n if (priorityA !== priorityB) {\n return priorityA - priorityB;\n }\n\n // Second: sort by timestamp (newest first = descending)\n const dateA = parseSessionId(a.id);\n const dateB = parseSessionId(b.id);\n\n // Handle invalid session IDs by treating them as oldest\n if (!dateA && !dateB) return 0;\n if (!dateA) return 1; // a goes after b\n if (!dateB) return -1; // b goes after a\n\n return dateB.getTime() - dateA.getTime();\n });\n}\n","/**\n * Session timestamp utilities for generating and parsing session IDs.\n *\n * Session IDs use the format YYYY-MM-DD_HH-mm-ss as specified in\n * ADR-32 (Timestamp Format).\n *\n * @module session/timestamp\n */\n\n/**\n * Regular expression pattern for validating session IDs.\n * Format: YYYY-MM-DD_HH-mm-ss (all components zero-padded)\n *\n * Exported for use in validation and testing.\n */\nexport const SESSION_ID_PATTERN = /^(\\d{4})-(\\d{2})-(\\d{2})_(\\d{2})-(\\d{2})-(\\d{2})$/;\n\n/**\n * Separator between date and time components in session IDs.\n */\nexport const SESSION_ID_SEPARATOR = \"_\";\n\n/**\n * Options for generating session IDs.\n */\nexport interface GenerateSessionIdOptions {\n /**\n * Function that returns the current time.\n * Defaults to `() => new Date()` for production use.\n * Injectable for deterministic testing.\n */\n now?: () => Date;\n}\n\n/**\n * Generates a session ID from the current (or injected) time.\n *\n * @param options - Optional configuration including time source\n * @returns Session ID in format YYYY-MM-DD_HH-mm-ss\n *\n * @example\n * ```typescript\n * // Production usage\n * const id = generateSessionId();\n * // => \"2026-01-13_08-01-05\"\n *\n * // Testing with injected time\n * const id = generateSessionId({ now: () => new Date('2026-01-13T08:01:05') });\n * // => \"2026-01-13_08-01-05\"\n * ```\n */\nexport function generateSessionId(options: GenerateSessionIdOptions = {}): string {\n const now = options.now ?? (() => new Date());\n const date = now();\n\n const year = date.getFullYear();\n const month = String(date.getMonth() + 1).padStart(2, \"0\");\n const day = String(date.getDate()).padStart(2, \"0\");\n const hours = String(date.getHours()).padStart(2, \"0\");\n const minutes = String(date.getMinutes()).padStart(2, \"0\");\n const seconds = String(date.getSeconds()).padStart(2, \"0\");\n\n return `${year}-${month}-${day}${SESSION_ID_SEPARATOR}${hours}-${minutes}-${seconds}`;\n}\n\n/**\n * Parses a session ID string back into a Date object.\n *\n * @param id - Session ID string to parse\n * @returns Date object if valid, null if invalid format\n *\n * @example\n * ```typescript\n * const date = parseSessionId('2026-01-13_08-01-05');\n * // => Date representing 2026-01-13T08:01:05 (local time)\n *\n * const invalid = parseSessionId('invalid-format');\n * // => null\n * ```\n */\nexport function parseSessionId(id: string): Date | null {\n const match = SESSION_ID_PATTERN.exec(id);\n\n if (!match) {\n return null;\n }\n\n const [, yearStr, monthStr, dayStr, hoursStr, minutesStr, secondsStr] = match;\n\n const year = parseInt(yearStr, 10);\n const month = parseInt(monthStr, 10) - 1; // Date months are 0-indexed\n const day = parseInt(dayStr, 10);\n const hours = parseInt(hoursStr, 10);\n const minutes = parseInt(minutesStr, 10);\n const seconds = parseInt(secondsStr, 10);\n\n // Validate component ranges\n if (month < 0 || month > 11) return null;\n if (day < 1 || day > 31) return null;\n if (hours < 0 || hours > 23) return null;\n if (minutes < 0 || minutes > 59) return null;\n if (seconds < 0 || seconds > 59) return null;\n\n return new Date(year, month, day, hours, minutes, seconds);\n}\n","/**\n * Session type definitions for the session management domain.\n *\n * @module session/types\n */\n\n/**\n * Priority levels for session ordering.\n * Sessions are sorted: high → medium → low\n */\nexport type SessionPriority = \"high\" | \"medium\" | \"low\";\n\n/**\n * Status derived from directory location per ADR-21.\n */\nexport type SessionStatus = \"todo\" | \"doing\" | \"archive\";\n\n/**\n * Priority sort order (lower number = higher priority).\n */\nexport const PRIORITY_ORDER: Record<SessionPriority, number> = {\n high: 0,\n medium: 1,\n low: 2,\n} as const;\n\n/**\n * Default priority when not specified in YAML front matter.\n */\nexport const DEFAULT_PRIORITY: SessionPriority = \"medium\";\n\n/**\n * Metadata extracted from session YAML front matter.\n */\nexport interface SessionMetadata {\n /** Session ID (from filename or YAML) */\n id?: string;\n /** Priority level for sorting */\n priority: SessionPriority;\n /** Free-form tags for filtering */\n tags: string[];\n /** Git branch associated with session */\n branch?: string;\n /** Spec files to auto-inject on pickup */\n specs?: string[];\n /** Code files to auto-inject on pickup */\n files?: string[];\n /** ISO 8601 timestamp when session was created */\n createdAt?: string;\n /** Working directory path */\n workingDirectory?: string;\n}\n\n/**\n * Complete session information including status and metadata.\n */\nexport interface Session {\n /** Session ID (timestamp format: YYYY-MM-DD_HH-mm-ss) */\n id: string;\n /** Status derived from directory location */\n status: SessionStatus;\n /** Metadata from YAML front matter */\n metadata: SessionMetadata;\n /** Full path to session file */\n path: string;\n}\n","/**\n * Session delete CLI command handler.\n *\n * @module commands/session/delete\n */\n\nimport { stat, unlink } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { resolveDeletePath } from \"../../session/delete.js\";\nimport { DEFAULT_SESSION_CONFIG, resolveSessionPaths, type SessionDirectoryConfig } from \"../../session/show.js\";\n\n/**\n * Options for the delete command.\n */\nexport interface DeleteOptions {\n /** Session ID to delete */\n sessionId: string;\n /** Custom sessions directory */\n sessionsDir?: string;\n}\n\n/**\n * Checks which paths exist.\n */\nasync function findExistingPaths(paths: string[]): Promise<string[]> {\n const existing: string[] = [];\n\n for (const path of paths) {\n try {\n const stats = await stat(path);\n if (stats.isFile()) {\n existing.push(path);\n }\n } catch {\n // File doesn't exist, skip\n }\n }\n\n return existing;\n}\n\n/**\n * Executes the delete command.\n *\n * @param options - Command options\n * @returns Formatted output for display\n * @throws {SessionNotFoundError} When session not found\n */\nexport async function deleteCommand(options: DeleteOptions): Promise<string> {\n // Build config from options\n const config: SessionDirectoryConfig = options.sessionsDir\n ? {\n todoDir: join(options.sessionsDir, \"todo\"),\n doingDir: join(options.sessionsDir, \"doing\"),\n archiveDir: join(options.sessionsDir, \"archive\"),\n }\n : DEFAULT_SESSION_CONFIG;\n\n // Resolve possible paths\n const paths = resolveSessionPaths(options.sessionId, config);\n\n // Find existing paths\n const existingPaths = await findExistingPaths(paths);\n\n // Resolve the path to delete\n const pathToDelete = resolveDeletePath(options.sessionId, existingPaths);\n\n // Delete the file\n await unlink(pathToDelete);\n\n return `Deleted session: ${options.sessionId}`;\n}\n","/**\n * Session deletion utilities.\n *\n * @module session/delete\n */\n\nimport { SessionNotFoundError } from \"./errors\";\n\n/**\n * Resolves the path to delete for a session ID.\n *\n * Given a session ID and a list of existing paths (checked by caller),\n * returns the first path that contains the session ID.\n *\n * @param sessionId - Session ID to find\n * @param existingPaths - Paths that were found to exist\n * @returns The path to the session file\n * @throws {SessionNotFoundError} When session is not found in any directory\n *\n * @example\n * ```typescript\n * // Caller checks which paths exist, passes only existing ones\n * const existingPaths = ['.spx/sessions/doing/2026-01-13_08-01-05.md'];\n * const path = resolveDeletePath('2026-01-13_08-01-05', existingPaths);\n * // => '.spx/sessions/doing/2026-01-13_08-01-05.md'\n * ```\n */\nexport function resolveDeletePath(\n sessionId: string,\n existingPaths: string[],\n): string {\n // Find the first existing path that matches the session ID\n const matchingPath = existingPaths.find((path) => path.includes(sessionId));\n\n if (!matchingPath) {\n throw new SessionNotFoundError(sessionId);\n }\n\n return matchingPath;\n}\n","/**\n * Session handoff CLI command handler.\n *\n * Creates a new session for handoff to another agent context.\n * Metadata (priority, tags) should be included in the content as YAML frontmatter.\n *\n * @module commands/session/handoff\n */\n\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport { join, resolve } from \"node:path\";\n\nimport { DEFAULT_CONFIG } from \"../../config/defaults.js\";\nimport { buildSessionPathFromRoot, detectGitRoot } from \"../../git/root.js\";\nimport { validateSessionContent } from \"../../session/create.js\";\nimport { SessionInvalidContentError } from \"../../session/errors.js\";\nimport { DEFAULT_SESSION_CONFIG, type SessionDirectoryConfig } from \"../../session/show.js\";\nimport { generateSessionId } from \"../../session/timestamp.js\";\n\nconst { statusDirs } = DEFAULT_CONFIG.sessions;\n\n/**\n * Regex to detect YAML frontmatter presence.\n * Matches opening `---` at start of content.\n */\nconst FRONT_MATTER_START = /^---\\r?\\n/;\n\n/**\n * Options for the handoff command.\n */\nexport interface HandoffOptions {\n /** Session content (from stdin). Should include YAML frontmatter with priority/tags. */\n content?: string;\n /** Custom sessions directory */\n sessionsDir?: string;\n}\n\n/**\n * Checks if content has YAML frontmatter.\n *\n * @param content - Raw session content\n * @returns True if content starts with frontmatter delimiter\n */\nexport function hasFrontmatter(content: string): boolean {\n return FRONT_MATTER_START.test(content);\n}\n\n/**\n * Builds session content, adding default frontmatter only if not present.\n *\n * If content already has frontmatter, returns as-is (preserves agent-provided metadata).\n * If content lacks frontmatter, adds default frontmatter with medium priority.\n *\n * @param content - Raw content from stdin\n * @returns Content ready to be written to session file\n */\nexport function buildSessionContent(content: string | undefined): string {\n // Default content if none provided\n if (!content || content.trim().length === 0) {\n return `---\npriority: medium\n---\n\n# New Session\n\nDescribe your task here.`;\n }\n\n // If content already has frontmatter, preserve it as-is\n if (hasFrontmatter(content)) {\n return content;\n }\n\n // Add default frontmatter to content without it\n return `---\npriority: medium\n---\n\n${content}`;\n}\n\n/**\n * Executes the handoff command.\n *\n * Creates a new session in the todo directory for pickup by another context.\n * Output includes `<HANDOFF_ID>` tag for easy parsing by automation tools.\n *\n * When no --sessions-dir is provided, sessions are created at the git repository root\n * (if in a git repo) to ensure consistent location across subdirectories.\n *\n * Metadata (priority, tags) should be included in the content as YAML frontmatter:\n * ```\n * ---\n * priority: high\n * tags: [feature, api]\n * ---\n * # Session content...\n * ```\n *\n * @param options - Command options\n * @returns Formatted output for display with parseable session ID\n * @throws {SessionInvalidContentError} When content validation fails\n */\nexport async function handoffCommand(options: HandoffOptions): Promise<string> {\n // Build config from options\n const config: SessionDirectoryConfig = options.sessionsDir\n ? {\n todoDir: join(options.sessionsDir, statusDirs.todo),\n doingDir: join(options.sessionsDir, statusDirs.doing),\n archiveDir: join(options.sessionsDir, statusDirs.archive),\n }\n : DEFAULT_SESSION_CONFIG;\n\n // Detect git root when no explicit sessions directory provided\n let baseDir: string;\n let warningMessage: string | undefined;\n\n if (options.sessionsDir) {\n // Explicit directory provided - use as-is\n baseDir = options.sessionsDir;\n } else {\n // No explicit directory - detect git root\n const gitResult = await detectGitRoot();\n baseDir = gitResult.root;\n warningMessage = gitResult.warning;\n }\n\n // Generate session ID\n const sessionId = generateSessionId();\n\n // Build content - preserves existing frontmatter or adds defaults\n const fullContent = buildSessionContent(options.content);\n\n // Validate content\n const validation = validateSessionContent(fullContent);\n if (!validation.valid) {\n throw new SessionInvalidContentError(validation.error ?? \"Unknown validation error\");\n }\n\n // Build path to session file\n const filename = `${sessionId}.md`;\n const sessionPath = options.sessionsDir\n ? join(config.todoDir, filename)\n : buildSessionPathFromRoot(baseDir, sessionId, config);\n const absolutePath = resolve(sessionPath);\n\n // Ensure directory exists\n const todoDir = options.sessionsDir\n ? config.todoDir\n : join(baseDir, config.todoDir);\n await mkdir(todoDir, { recursive: true });\n\n // Write file\n await writeFile(sessionPath, fullContent, \"utf-8\");\n\n // Build output message\n let output =\n `Created handoff session <HANDOFF_ID>${sessionId}</HANDOFF_ID>\\n<SESSION_FILE>${absolutePath}</SESSION_FILE>`;\n\n // Emit warning to stderr if not in git repo\n if (warningMessage) {\n // Write warning to stderr (not included in returned string)\n process.stderr.write(`${warningMessage}\\n`);\n }\n\n return output;\n}\n","/**\n * Git repository root detection utilities.\n *\n * Provides git root detection with dependency injection for testability.\n * Sessions should be created at the git repository root, not relative to cwd.\n *\n * @module git/root\n */\n\nimport { execa, type ResultPromise } from \"execa\";\nimport { join } from \"node:path\";\n\nimport type { SessionDirectoryConfig } from \"../session/show.js\";\n\n/**\n * Result from git root detection.\n */\nexport interface GitRootResult {\n /** Absolute path to git root (or cwd if not in git repo) */\n root: string;\n /** Whether the directory is inside a git repository */\n isGitRepo: boolean;\n /** Warning message when not in a git repo (undefined if in repo) */\n warning?: string;\n}\n\n/**\n * Dependencies for git operations (injectable for testing).\n */\nexport interface GitDependencies {\n /**\n * Execute a command (typically execa).\n *\n * @param command - Command to execute\n * @param args - Command arguments\n * @param options - Execution options\n * @returns Result promise with stdout/stderr\n */\n execa: (\n command: string,\n args: string[],\n options?: { cwd?: string; reject?: boolean },\n ) => ResultPromise;\n}\n\n/**\n * Default dependencies using real execa.\n */\nconst defaultDeps: GitDependencies = {\n execa: (command, args, options) => execa(command, args, options),\n};\n\n/**\n * Warning message emitted when not in a git repository.\n */\nconst NOT_GIT_REPO_WARNING =\n \"Warning: Not in a git repository. Sessions will be created relative to current directory.\";\n\n/**\n * Detects the git repository root directory.\n *\n * Uses `git rev-parse --show-toplevel` to find the repository root.\n * If not in a git repository, returns the current working directory with a warning.\n *\n * @param cwd - Current working directory (defaults to process.cwd())\n * @param deps - Injectable dependencies for testing\n * @returns GitRootResult with root path, git status, and optional warning\n *\n * @example\n * ```typescript\n * // In a git repo subdirectory\n * const result = await detectGitRoot('/repo/src/components');\n * // => { root: '/repo', isGitRepo: true }\n *\n * // Not in a git repo\n * const result = await detectGitRoot('/tmp/random');\n * // => { root: '/tmp/random', isGitRepo: false, warning: '...' }\n * ```\n */\nexport async function detectGitRoot(\n cwd: string = process.cwd(),\n deps: GitDependencies = defaultDeps,\n): Promise<GitRootResult> {\n try {\n const result = await deps.execa(\n \"git\",\n [\"rev-parse\", \"--show-toplevel\"],\n { cwd, reject: false },\n );\n\n // Git command succeeded - we're in a repo\n if (result.exitCode === 0 && result.stdout) {\n // Trim whitespace and normalize path (remove trailing slashes)\n const stdout = typeof result.stdout === \"string\" ? result.stdout : result.stdout.toString();\n const gitRoot = stdout.trim().replace(/\\/+$/, \"\");\n\n return {\n root: gitRoot,\n isGitRepo: true,\n };\n }\n\n // Git command failed - not in a repo\n return {\n root: cwd,\n isGitRepo: false,\n warning: NOT_GIT_REPO_WARNING,\n };\n } catch {\n // Command execution failed (git not installed, permission error, etc.)\n return {\n root: cwd,\n isGitRepo: false,\n warning: NOT_GIT_REPO_WARNING,\n };\n }\n}\n\n/**\n * Builds an absolute session file path from git root and session ID.\n *\n * Pure function that constructs the path without I/O.\n * All path components come from the config parameter (single source of truth).\n *\n * @param gitRoot - Absolute path to git repository root\n * @param sessionId - Session timestamp ID (e.g., \"2026-01-13_08-01-05\")\n * @param config - Session directory configuration\n * @returns Absolute path to session file in todo directory\n *\n * @example\n * ```typescript\n * const path = buildSessionPathFromRoot(\n * '/Users/dev/myproject',\n * '2026-01-13_08-01-05',\n * DEFAULT_SESSION_CONFIG,\n * );\n * // => '/Users/dev/myproject/.spx/sessions/todo/2026-01-13_08-01-05.md'\n * ```\n */\nexport function buildSessionPathFromRoot(\n gitRoot: string,\n sessionId: string,\n config: SessionDirectoryConfig,\n): string {\n const filename = `${sessionId}.md`;\n\n // Build absolute path: git root + todo dir + filename\n // All components come from config (no hardcoded strings)\n return join(gitRoot, config.todoDir, filename);\n}\n","/**\n * Session creation utilities.\n *\n * @module session/create\n */\n\nimport type { SessionDirectoryConfig } from \"./show\";\n\n/**\n * Minimum content length for a valid session.\n */\nexport const MIN_CONTENT_LENGTH = 1;\n\n/**\n * Configuration subset needed for session creation.\n */\nexport type CreateSessionConfig = Pick<SessionDirectoryConfig, \"todoDir\">;\n\n/**\n * Result of session content validation.\n */\nexport interface ValidationResult {\n /** Whether the content is valid */\n valid: boolean;\n /** Error message if invalid */\n error?: string;\n}\n\n/**\n * Builds the full file path for a new session in the todo directory.\n *\n * @param sessionId - Session ID (timestamp format)\n * @param config - Directory configuration with todoDir\n * @returns Full path to session file\n *\n * @example\n * ```typescript\n * const path = buildSessionPath('2026-01-13_08-01-05', { todoDir: '.spx/sessions/todo' });\n * // => '.spx/sessions/todo/2026-01-13_08-01-05.md'\n * ```\n */\nexport function buildSessionPath(\n sessionId: string,\n config: CreateSessionConfig,\n): string {\n return `${config.todoDir}/${sessionId}.md`;\n}\n\n/**\n * Validates session content before creation.\n *\n * @param content - Raw session content\n * @returns Validation result with valid flag and optional error\n *\n * @example\n * ```typescript\n * const result = validateSessionContent('# My Session');\n * // => { valid: true }\n *\n * const result = validateSessionContent('');\n * // => { valid: false, error: 'Session content cannot be empty' }\n * ```\n */\nexport function validateSessionContent(content: string): ValidationResult {\n // Check for empty content\n if (!content || content.trim().length < MIN_CONTENT_LENGTH) {\n return {\n valid: false,\n error: \"Session content cannot be empty\",\n };\n }\n\n // Check for only whitespace\n if (content.trim().length === 0) {\n return {\n valid: false,\n error: \"Session content cannot be only whitespace\",\n };\n }\n\n return { valid: true };\n}\n","/**\n * Session list CLI command handler.\n *\n * @module commands/session/list\n */\n\nimport { readdir, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { parseSessionMetadata, sortSessions } from \"../../session/list.js\";\nimport { DEFAULT_SESSION_CONFIG, type SessionDirectoryConfig } from \"../../session/show.js\";\nimport type { Session, SessionStatus } from \"../../session/types.js\";\n\n/**\n * Options for the list command.\n */\nexport interface ListOptions {\n /** Filter by status */\n status?: SessionStatus;\n /** Custom sessions directory */\n sessionsDir?: string;\n /** Output format */\n format?: \"text\" | \"json\";\n}\n\n/**\n * Loads sessions from a specific directory.\n */\nasync function loadSessionsFromDir(\n dir: string,\n status: SessionStatus,\n): Promise<Session[]> {\n try {\n const files = await readdir(dir);\n const sessions: Session[] = [];\n\n for (const file of files) {\n if (!file.endsWith(\".md\")) continue;\n\n const id = file.replace(\".md\", \"\");\n const filePath = join(dir, file);\n const content = await readFile(filePath, \"utf-8\");\n const metadata = parseSessionMetadata(content);\n\n sessions.push({\n id,\n status,\n path: filePath,\n metadata,\n });\n }\n\n return sessions;\n } catch (error) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") {\n return [];\n }\n throw error;\n }\n}\n\n/**\n * Formats sessions for text output.\n */\nfunction formatTextOutput(sessions: Session[], _status: SessionStatus): string {\n if (sessions.length === 0) {\n return ` (no sessions)`;\n }\n\n return sessions\n .map((s) => {\n const priority = s.metadata.priority !== \"medium\" ? ` [${s.metadata.priority}]` : \"\";\n const tags = s.metadata.tags.length > 0 ? ` (${s.metadata.tags.join(\", \")})` : \"\";\n return ` ${s.id}${priority}${tags}`;\n })\n .join(\"\\n\");\n}\n\n/**\n * Executes the list command.\n *\n * @param options - Command options\n * @returns Formatted output for display\n */\nexport async function listCommand(options: ListOptions): Promise<string> {\n // Build config from options\n const config: SessionDirectoryConfig = options.sessionsDir\n ? {\n todoDir: join(options.sessionsDir, \"todo\"),\n doingDir: join(options.sessionsDir, \"doing\"),\n archiveDir: join(options.sessionsDir, \"archive\"),\n }\n : DEFAULT_SESSION_CONFIG;\n\n // Load sessions based on filter\n const statuses: SessionStatus[] = options.status\n ? [options.status]\n : [\"todo\", \"doing\", \"archive\"];\n\n const allSessions: Record<SessionStatus, Session[]> = {\n todo: [],\n doing: [],\n archive: [],\n };\n\n for (const status of statuses) {\n const dir = status === \"todo\"\n ? config.todoDir\n : status === \"doing\"\n ? config.doingDir\n : config.archiveDir;\n\n const sessions = await loadSessionsFromDir(dir, status);\n allSessions[status] = sortSessions(sessions);\n }\n\n // Format output\n if (options.format === \"json\") {\n return JSON.stringify(allSessions, null, 2);\n }\n\n // Text format\n const lines: string[] = [];\n\n if (statuses.includes(\"doing\")) {\n lines.push(\"DOING:\");\n lines.push(formatTextOutput(allSessions.doing, \"doing\"));\n lines.push(\"\");\n }\n\n if (statuses.includes(\"todo\")) {\n lines.push(\"TODO:\");\n lines.push(formatTextOutput(allSessions.todo, \"todo\"));\n lines.push(\"\");\n }\n\n if (statuses.includes(\"archive\")) {\n lines.push(\"ARCHIVE:\");\n lines.push(formatTextOutput(allSessions.archive, \"archive\"));\n }\n\n return lines.join(\"\\n\").trim();\n}\n","/**\n * Session pickup CLI command handler.\n *\n * @module commands/session/pickup\n */\n\nimport { mkdir, readdir, readFile, rename } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { NoSessionsAvailableError } from \"../../session/errors.js\";\nimport { parseSessionMetadata } from \"../../session/list.js\";\nimport { buildClaimPaths, classifyClaimError, selectBestSession } from \"../../session/pickup.js\";\nimport { DEFAULT_SESSION_CONFIG, formatShowOutput, type SessionDirectoryConfig } from \"../../session/show.js\";\nimport type { Session, SessionStatus } from \"../../session/types.js\";\n\n/**\n * Options for the pickup command.\n */\nexport interface PickupOptions {\n /** Session ID to pickup (mutually exclusive with auto) */\n sessionId?: string;\n /** Auto-select highest priority session */\n auto?: boolean;\n /** Custom sessions directory */\n sessionsDir?: string;\n}\n\n/**\n * Loads sessions from the todo directory.\n */\nasync function loadTodoSessions(config: SessionDirectoryConfig): Promise<Session[]> {\n try {\n const files = await readdir(config.todoDir);\n const sessions: Session[] = [];\n\n for (const file of files) {\n if (!file.endsWith(\".md\")) continue;\n\n const id = file.replace(\".md\", \"\");\n const filePath = join(config.todoDir, file);\n const content = await readFile(filePath, \"utf-8\");\n const metadata = parseSessionMetadata(content);\n\n sessions.push({\n id,\n status: \"todo\" as SessionStatus,\n path: filePath,\n metadata,\n });\n }\n\n return sessions;\n } catch (error) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") {\n return [];\n }\n throw error;\n }\n}\n\n/**\n * Executes the pickup command.\n *\n * Claims a session from the todo queue and moves it to doing.\n * Output includes `<PICKUP_ID>` tag for easy parsing by automation tools.\n *\n * @param options - Command options\n * @returns Formatted output for display with parseable session ID\n * @throws {NoSessionsAvailableError} When no sessions in todo for auto mode\n * @throws {SessionNotAvailableError} When session already claimed\n */\nexport async function pickupCommand(options: PickupOptions): Promise<string> {\n // Build config from options\n const config: SessionDirectoryConfig = options.sessionsDir\n ? {\n todoDir: join(options.sessionsDir, \"todo\"),\n doingDir: join(options.sessionsDir, \"doing\"),\n archiveDir: join(options.sessionsDir, \"archive\"),\n }\n : DEFAULT_SESSION_CONFIG;\n\n let sessionId: string;\n\n if (options.auto) {\n // Auto mode: select best session\n const sessions = await loadTodoSessions(config);\n const selected = selectBestSession(sessions);\n\n if (!selected) {\n throw new NoSessionsAvailableError();\n }\n\n sessionId = selected.id;\n } else if (options.sessionId) {\n sessionId = options.sessionId;\n } else {\n throw new Error(\"Either session ID or --auto flag is required\");\n }\n\n // Build paths and ensure doing directory exists\n const paths = buildClaimPaths(sessionId, config);\n await mkdir(config.doingDir, { recursive: true });\n\n // Perform atomic claim\n try {\n await rename(paths.source, paths.target);\n } catch (error) {\n throw classifyClaimError(error, sessionId);\n }\n\n // Read and format content\n const content = await readFile(paths.target, \"utf-8\");\n const output = formatShowOutput(content, { status: \"doing\" });\n\n // Output with parseable PICKUP_ID tag\n return `Claimed session <PICKUP_ID>${sessionId}</PICKUP_ID>\\n\\n${output}`;\n}\n","/**\n * Session pickup/claiming utilities.\n *\n * This module provides pure functions for atomic session claiming:\n * - Path construction for claim operations\n * - Error classification for claim failures\n * - Session selection for auto-pickup\n *\n * @module session/pickup\n */\n\nimport { SessionNotAvailableError } from \"./errors\";\nimport { parseSessionId } from \"./timestamp\";\nimport { PRIORITY_ORDER, type Session } from \"./types\";\n\n/**\n * Configuration for session claim paths.\n */\nexport interface ClaimPathConfig {\n /** Directory containing sessions to be claimed */\n todoDir: string;\n /** Directory for claimed sessions */\n doingDir: string;\n}\n\n/**\n * Result of building claim paths.\n */\nexport interface ClaimPaths {\n /** Source path (session in todo) */\n source: string;\n /** Target path (session in doing) */\n target: string;\n}\n\n/**\n * Builds source and target paths for claiming a session.\n *\n * @param sessionId - The session ID (timestamp format)\n * @param config - Directory configuration\n * @returns Source and target paths for the claim operation\n *\n * @example\n * ```typescript\n * const paths = buildClaimPaths(\"2026-01-13_08-01-05\", {\n * todoDir: \".spx/sessions/todo\",\n * doingDir: \".spx/sessions/doing\",\n * });\n * // => { source: \".spx/sessions/todo/2026-01-13_08-01-05.md\", target: \".spx/sessions/doing/2026-01-13_08-01-05.md\" }\n * ```\n */\nexport function buildClaimPaths(sessionId: string, config: ClaimPathConfig): ClaimPaths {\n return {\n source: `${config.todoDir}/${sessionId}.md`,\n target: `${config.doingDir}/${sessionId}.md`,\n };\n}\n\n/**\n * Classifies a filesystem error that occurred during claiming.\n *\n * When a claim operation fails, this function maps the raw filesystem\n * error to an appropriate domain error:\n * - ENOENT: Session was claimed by another agent (SessionNotAvailable)\n * - Other errors: Rethrown as-is\n *\n * @param error - The error from the claim operation\n * @param sessionId - The session ID being claimed\n * @returns A SessionNotAvailableError if ENOENT\n * @throws The original error if not ENOENT\n *\n * @example\n * ```typescript\n * const err = Object.assign(new Error(\"ENOENT\"), { code: \"ENOENT\" });\n * const classified = classifyClaimError(err, \"test-id\");\n * // => SessionNotAvailableError\n * ```\n */\nexport function classifyClaimError(error: unknown, sessionId: string): SessionNotAvailableError {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") {\n return new SessionNotAvailableError(sessionId);\n }\n throw error;\n}\n\n/**\n * Selects the best session for auto-pickup based on priority and timestamp.\n *\n * Selection criteria:\n * 1. Highest priority (high > medium > low)\n * 2. Oldest timestamp (FIFO) for sessions with equal priority\n *\n * @param sessions - Available sessions to choose from\n * @returns The best session to claim, or null if no sessions available\n *\n * @example\n * ```typescript\n * const sessions = [\n * { id: \"low-1\", metadata: { priority: \"low\" } },\n * { id: \"high-1\", metadata: { priority: \"high\" } },\n * ];\n * const best = selectBestSession(sessions);\n * // => { id: \"high-1\", ... }\n * ```\n */\nexport function selectBestSession(sessions: Session[]): Session | null {\n if (sessions.length === 0) {\n return null;\n }\n\n // Sort by priority (high first) then by timestamp (oldest first for FIFO)\n const sorted = [...sessions].sort((a, b) => {\n // First: sort by priority (high = 0, medium = 1, low = 2)\n const priorityA = PRIORITY_ORDER[a.metadata.priority];\n const priorityB = PRIORITY_ORDER[b.metadata.priority];\n\n if (priorityA !== priorityB) {\n return priorityA - priorityB;\n }\n\n // Second: sort by timestamp (oldest first = FIFO = ascending)\n const dateA = parseSessionId(a.id);\n const dateB = parseSessionId(b.id);\n\n // Handle invalid session IDs by treating them as newest (go last)\n if (!dateA && !dateB) return 0;\n if (!dateA) return 1; // a goes after b\n if (!dateB) return -1; // b goes after a\n\n return dateA.getTime() - dateB.getTime();\n });\n\n return sorted[0];\n}\n","/**\n * Session prune CLI command handler.\n *\n * @module commands/session/prune\n */\n\nimport { readdir, readFile, unlink } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { parseSessionMetadata, sortSessions } from \"../../session/list.js\";\nimport { DEFAULT_SESSION_CONFIG, type SessionDirectoryConfig } from \"../../session/show.js\";\nimport type { Session } from \"../../session/types.js\";\n\n/**\n * Default number of sessions to keep when pruning.\n */\nexport const DEFAULT_KEEP_COUNT = 5;\n\n/**\n * Options for the prune command.\n */\nexport interface PruneOptions {\n /** Number of sessions to keep (default: 5) */\n keep?: number;\n /** Show what would be deleted without actually deleting */\n dryRun?: boolean;\n /** Custom sessions directory */\n sessionsDir?: string;\n}\n\n/**\n * Error thrown when prune options are invalid.\n */\nexport class PruneValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"PruneValidationError\";\n }\n}\n\n/**\n * Validates prune options.\n *\n * @param options - Options to validate\n * @throws {PruneValidationError} When options are invalid\n */\nexport function validatePruneOptions(options: PruneOptions): void {\n if (options.keep !== undefined) {\n if (!Number.isInteger(options.keep) || options.keep < 1) {\n throw new PruneValidationError(\n `Invalid --keep value: ${options.keep}. Must be a positive integer.`,\n );\n }\n }\n}\n\n/**\n * Loads sessions from the archive directory.\n */\nasync function loadArchiveSessions(config: SessionDirectoryConfig): Promise<Session[]> {\n try {\n const files = await readdir(config.archiveDir);\n const sessions: Session[] = [];\n\n for (const file of files) {\n if (!file.endsWith(\".md\")) continue;\n\n const id = file.replace(\".md\", \"\");\n const filePath = join(config.archiveDir, file);\n const content = await readFile(filePath, \"utf-8\");\n const metadata = parseSessionMetadata(content);\n\n sessions.push({\n id,\n status: \"archive\",\n path: filePath,\n metadata,\n });\n }\n\n return sessions;\n } catch (error) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") {\n return [];\n }\n throw error;\n }\n}\n\n/**\n * Determines which sessions to prune.\n *\n * Sessions are sorted by priority (high first) then by timestamp (newest first).\n * The first `keep` sessions are retained, the rest are marked for deletion.\n *\n * @param sessions - All todo sessions\n * @param keep - Number of sessions to keep\n * @returns Sessions to delete (oldest/lowest priority first)\n */\nexport function selectSessionsToPrune(sessions: Session[], keep: number): Session[] {\n // Sort sessions by priority and timestamp\n const sorted = sortSessions(sessions);\n\n // Keep the top N sessions, prune the rest\n if (sorted.length <= keep) {\n return [];\n }\n\n return sorted.slice(keep);\n}\n\n/**\n * Executes the prune command.\n *\n * @param options - Command options\n * @returns Formatted output for display\n * @throws {PruneValidationError} When options are invalid\n */\nexport async function pruneCommand(options: PruneOptions): Promise<string> {\n // Validate options\n validatePruneOptions(options);\n\n const keep = options.keep ?? DEFAULT_KEEP_COUNT;\n const dryRun = options.dryRun ?? false;\n\n // Build config from options\n const config: SessionDirectoryConfig = options.sessionsDir\n ? {\n todoDir: join(options.sessionsDir, \"todo\"),\n doingDir: join(options.sessionsDir, \"doing\"),\n archiveDir: join(options.sessionsDir, \"archive\"),\n }\n : DEFAULT_SESSION_CONFIG;\n\n // Load and sort sessions\n const sessions = await loadArchiveSessions(config);\n const toPrune = selectSessionsToPrune(sessions, keep);\n\n if (toPrune.length === 0) {\n return `No sessions to prune. ${sessions.length} sessions kept.`;\n }\n\n // Dry run mode\n if (dryRun) {\n const lines = [\n `Would delete ${toPrune.length} sessions:`,\n ...toPrune.map((s) => ` - ${s.id}`),\n \"\",\n `${sessions.length - toPrune.length} sessions would be kept.`,\n ];\n return lines.join(\"\\n\");\n }\n\n // Delete sessions\n for (const session of toPrune) {\n await unlink(session.path);\n }\n\n const lines = [\n `Deleted ${toPrune.length} sessions:`,\n ...toPrune.map((s) => ` - ${s.id}`),\n \"\",\n `${sessions.length - toPrune.length} sessions kept.`,\n ];\n return lines.join(\"\\n\");\n}\n","/**\n * Session release CLI command handler.\n *\n * @module commands/session/release\n */\n\nimport { readdir, rename } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { SessionNotClaimedError } from \"../../session/errors.js\";\nimport { buildReleasePaths, findCurrentSession } from \"../../session/release.js\";\nimport { DEFAULT_SESSION_CONFIG, type SessionDirectoryConfig } from \"../../session/show.js\";\n\n/**\n * Options for the release command.\n */\nexport interface ReleaseOptions {\n /** Session ID to release (optional, defaults to most recent in doing) */\n sessionId?: string;\n /** Custom sessions directory */\n sessionsDir?: string;\n}\n\n/**\n * Loads session refs from the doing directory.\n */\nasync function loadDoingSessions(config: SessionDirectoryConfig): Promise<Array<{ id: string }>> {\n try {\n const files = await readdir(config.doingDir);\n return files\n .filter((file) => file.endsWith(\".md\"))\n .map((file) => ({ id: file.replace(\".md\", \"\") }));\n } catch (error) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") {\n return [];\n }\n throw error;\n }\n}\n\n/**\n * Executes the release command.\n *\n * @param options - Command options\n * @returns Formatted output for display\n * @throws {SessionNotClaimedError} When session not in doing directory\n */\nexport async function releaseCommand(options: ReleaseOptions): Promise<string> {\n // Build config from options\n const config: SessionDirectoryConfig = options.sessionsDir\n ? {\n todoDir: join(options.sessionsDir, \"todo\"),\n doingDir: join(options.sessionsDir, \"doing\"),\n archiveDir: join(options.sessionsDir, \"archive\"),\n }\n : DEFAULT_SESSION_CONFIG;\n\n let sessionId: string;\n\n if (options.sessionId) {\n sessionId = options.sessionId;\n } else {\n // Find most recent session in doing\n const sessions = await loadDoingSessions(config);\n const current = findCurrentSession(sessions);\n\n if (!current) {\n throw new SessionNotClaimedError(\"(none)\");\n }\n\n sessionId = current.id;\n }\n\n // Build paths and perform release\n const paths = buildReleasePaths(sessionId, config);\n\n try {\n await rename(paths.source, paths.target);\n } catch (error) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") {\n throw new SessionNotClaimedError(sessionId);\n }\n throw error;\n }\n\n return `Released session: ${sessionId}\\nSession returned to todo directory.`;\n}\n","/**\n * Session release utilities.\n *\n * This module provides pure functions for releasing claimed sessions:\n * - Path construction for release operations\n * - Finding the current session in doing directory\n *\n * @module session/release\n */\n\nimport { parseSessionId } from \"./timestamp\";\n\n/**\n * Configuration for session release paths.\n */\nexport interface ReleasePathConfig {\n /** Directory containing claimed sessions */\n doingDir: string;\n /** Directory for released sessions */\n todoDir: string;\n}\n\n/**\n * Result of building release paths.\n */\nexport interface ReleasePaths {\n /** Source path (session in doing) */\n source: string;\n /** Target path (session in todo) */\n target: string;\n}\n\n/**\n * Session reference with minimal required fields.\n */\nexport interface SessionRef {\n id: string;\n}\n\n/**\n * Builds source and target paths for releasing a session.\n *\n * @param sessionId - The session ID (timestamp format)\n * @param config - Directory configuration\n * @returns Source and target paths for the release operation\n *\n * @example\n * ```typescript\n * const paths = buildReleasePaths(\"2026-01-13_08-01-05\", {\n * doingDir: \".spx/sessions/doing\",\n * todoDir: \".spx/sessions/todo\",\n * });\n * // => { source: \".spx/sessions/doing/2026-01-13_08-01-05.md\", target: \".spx/sessions/todo/2026-01-13_08-01-05.md\" }\n * ```\n */\nexport function buildReleasePaths(sessionId: string, config: ReleasePathConfig): ReleasePaths {\n return {\n source: `${config.doingDir}/${sessionId}.md`,\n target: `${config.todoDir}/${sessionId}.md`,\n };\n}\n\n/**\n * Finds the most recent session in the doing directory.\n *\n * When no session ID is provided to release, this function\n * selects the most recently claimed session (newest timestamp).\n *\n * @param doingSessions - Sessions currently in doing directory\n * @returns The most recent session, or null if empty\n *\n * @example\n * ```typescript\n * const sessions = [\n * { id: \"2026-01-10_08-00-00\" },\n * { id: \"2026-01-13_08-00-00\" },\n * ];\n * const current = findCurrentSession(sessions);\n * // => { id: \"2026-01-13_08-00-00\" }\n * ```\n */\nexport function findCurrentSession(doingSessions: SessionRef[]): SessionRef | null {\n if (doingSessions.length === 0) {\n return null;\n }\n\n // Sort by timestamp descending (newest first)\n const sorted = [...doingSessions].sort((a, b) => {\n const dateA = parseSessionId(a.id);\n const dateB = parseSessionId(b.id);\n\n // Handle invalid session IDs by treating them as oldest\n if (!dateA && !dateB) return 0;\n if (!dateA) return 1; // a goes after b\n if (!dateB) return -1; // b goes after a\n\n return dateB.getTime() - dateA.getTime();\n });\n\n return sorted[0];\n}\n","/**\n * Session show CLI command handler.\n *\n * @module commands/session/show\n */\n\nimport { readFile, stat } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { SessionNotFoundError } from \"../../session/errors.js\";\nimport {\n DEFAULT_SESSION_CONFIG,\n formatShowOutput,\n resolveSessionPaths,\n SEARCH_ORDER,\n type SessionDirectoryConfig,\n} from \"../../session/show.js\";\nimport type { SessionStatus } from \"../../session/types.js\";\n\n/**\n * Options for the show command.\n */\nexport interface ShowOptions {\n /** Session ID to show */\n sessionId: string;\n /** Custom sessions directory */\n sessionsDir?: string;\n}\n\n/**\n * Finds the first existing path and its status.\n */\nasync function findExistingPath(\n paths: string[],\n _config: SessionDirectoryConfig,\n): Promise<{ path: string; status: SessionStatus } | null> {\n for (let i = 0; i < paths.length; i++) {\n const filePath = paths[i];\n try {\n const stats = await stat(filePath);\n if (stats.isFile()) {\n return { path: filePath, status: SEARCH_ORDER[i] };\n }\n } catch {\n // File doesn't exist, continue\n }\n }\n return null;\n}\n\n/**\n * Executes the show command.\n *\n * @param options - Command options\n * @returns Formatted output for display\n * @throws {SessionNotFoundError} When session not found\n */\nexport async function showCommand(options: ShowOptions): Promise<string> {\n // Build config from options\n const config: SessionDirectoryConfig = options.sessionsDir\n ? {\n todoDir: join(options.sessionsDir, \"todo\"),\n doingDir: join(options.sessionsDir, \"doing\"),\n archiveDir: join(options.sessionsDir, \"archive\"),\n }\n : DEFAULT_SESSION_CONFIG;\n\n // Resolve possible paths\n const paths = resolveSessionPaths(options.sessionId, config);\n\n // Find the existing file\n const found = await findExistingPath(paths, config);\n\n if (!found) {\n throw new SessionNotFoundError(options.sessionId);\n }\n\n // Read and format content\n const content = await readFile(found.path, \"utf-8\");\n return formatShowOutput(content, { status: found.status });\n}\n","/**\n * Shared help text for session commands.\n *\n * Centralizes help text to ensure consistency across subcommands.\n *\n * @module domains/session/help\n */\n\n/**\n * Session file format description for the main session help.\n */\nexport const SESSION_FORMAT_HELP = `\nSession File Format:\n Sessions are markdown files with YAML frontmatter for metadata.\n\n ---\n priority: high | medium | low\n tags: [tag1, tag2]\n ---\n # Session Title\n\n Session content...\n\nWorkflow:\n 1. handoff - Create session (todo)\n 2. pickup - Claim session (todo -> doing)\n 3. release - Return session (doing -> todo)\n 4. delete - Remove session\n`;\n\n/**\n * Frontmatter details for handoff command.\n */\nexport const HANDOFF_FRONTMATTER_HELP = `\nUsage:\n Option 1: Pipe content with frontmatter via stdin\n Option 2: Run without stdin, then edit the created file directly\n\nFrontmatter Format:\n ---\n priority: high # high | medium | low (default: medium)\n tags: [feat, api] # optional labels for categorization\n ---\n # Your session content here...\n\nOutput Tags (for automation):\n <HANDOFF_ID>session-id</HANDOFF_ID> - Session identifier\n <SESSION_FILE>/path/to/file</SESSION_FILE> - Absolute path to edit\n\nExamples:\n # With stdin content:\n echo '---\n priority: high\n ---\n # Fix login' | spx session handoff\n\n # Without stdin (creates empty session, edit file directly):\n spx session handoff\n`;\n\n/**\n * Selection logic for pickup command.\n */\nexport const PICKUP_SELECTION_HELP = `\nSelection Logic (--auto):\n Sessions are selected by priority, then age (FIFO):\n 1. high priority first\n 2. medium priority second\n 3. low priority last\n 4. Within same priority: oldest session first\n\nOutput:\n <PICKUP_ID>session-id</PICKUP_ID> tag for automation parsing\n`;\n","/**\n * Session domain - Manage session workflow\n */\nimport type { Command } from \"commander\";\n\nimport {\n archiveCommand,\n deleteCommand,\n handoffCommand,\n listCommand,\n pickupCommand,\n pruneCommand,\n PruneValidationError,\n releaseCommand,\n SessionAlreadyArchivedError,\n showCommand,\n} from \"../../commands/session/index.js\";\nimport type { Domain } from \"../types.js\";\nimport { HANDOFF_FRONTMATTER_HELP, PICKUP_SELECTION_HELP, SESSION_FORMAT_HELP } from \"./help.js\";\n\n/**\n * Reads content from stdin if available (piped input).\n * Returns undefined if stdin is a TTY (interactive terminal).\n */\nasync function readStdin(): Promise<string | undefined> {\n // Check if stdin is a TTY (interactive) - if so, don't wait for input\n if (process.stdin.isTTY) {\n return undefined;\n }\n\n return new Promise((resolve) => {\n let data = \"\";\n process.stdin.setEncoding(\"utf-8\");\n process.stdin.on(\"data\", (chunk) => {\n data += chunk;\n });\n process.stdin.on(\"end\", () => {\n resolve(data.trim() || undefined);\n });\n // Handle case where stdin closes without data\n process.stdin.on(\"error\", () => {\n resolve(undefined);\n });\n });\n}\n\n/**\n * Handles command errors with consistent formatting.\n */\nfunction handleError(error: unknown): never {\n console.error(\"Error:\", error instanceof Error ? error.message : String(error));\n process.exit(1);\n}\n\n/**\n * Register session domain commands\n *\n * @param sessionCmd - Commander.js session domain command\n */\nfunction registerSessionCommands(sessionCmd: Command): void {\n // list command\n sessionCmd\n .command(\"list\")\n .description(\"List all sessions\")\n .option(\"--status <status>\", \"Filter by status (todo|doing|archive)\")\n .option(\"--json\", \"Output as JSON\")\n .option(\"--sessions-dir <path>\", \"Custom sessions directory\")\n .action(async (options: { status?: string; json?: boolean; sessionsDir?: string }) => {\n try {\n const output = await listCommand({\n status: options.status as \"todo\" | \"doing\" | \"archive\" | undefined,\n format: options.json ? \"json\" : \"text\",\n sessionsDir: options.sessionsDir,\n });\n console.log(output);\n } catch (error) {\n handleError(error);\n }\n });\n\n // show command\n sessionCmd\n .command(\"show <id>\")\n .description(\"Show session content\")\n .option(\"--sessions-dir <path>\", \"Custom sessions directory\")\n .action(async (id: string, options: { sessionsDir?: string }) => {\n try {\n const output = await showCommand({\n sessionId: id,\n sessionsDir: options.sessionsDir,\n });\n console.log(output);\n } catch (error) {\n handleError(error);\n }\n });\n\n // pickup command\n sessionCmd\n .command(\"pickup [id]\")\n .description(\"Claim a session (move from todo to doing)\")\n .option(\"--auto\", \"Auto-select highest priority session\")\n .option(\"--sessions-dir <path>\", \"Custom sessions directory\")\n .addHelpText(\"after\", PICKUP_SELECTION_HELP)\n .action(async (id: string | undefined, options: { auto?: boolean; sessionsDir?: string }) => {\n try {\n if (!id && !options.auto) {\n console.error(\"Error: Either session ID or --auto flag is required\");\n process.exit(1);\n }\n const output = await pickupCommand({\n sessionId: id,\n auto: options.auto,\n sessionsDir: options.sessionsDir,\n });\n console.log(output);\n } catch (error) {\n handleError(error);\n }\n });\n\n // release command\n sessionCmd\n .command(\"release [id]\")\n .description(\"Release a session (move from doing to todo)\")\n .option(\"--sessions-dir <path>\", \"Custom sessions directory\")\n .action(async (id: string | undefined, options: { sessionsDir?: string }) => {\n try {\n const output = await releaseCommand({\n sessionId: id,\n sessionsDir: options.sessionsDir,\n });\n console.log(output);\n } catch (error) {\n handleError(error);\n }\n });\n\n // handoff command\n // Note: --priority and --tags removed. Metadata should be in content frontmatter.\n // This makes the command deterministic for Claude Code permission pre-approval.\n sessionCmd\n .command(\"handoff\")\n .description(\"Create a handoff session (reads content with frontmatter from stdin)\")\n .option(\"--sessions-dir <path>\", \"Custom sessions directory\")\n .addHelpText(\"after\", HANDOFF_FRONTMATTER_HELP)\n .action(async (options: { sessionsDir?: string }) => {\n try {\n // Read content from stdin if available\n const content = await readStdin();\n\n const output = await handoffCommand({\n content,\n sessionsDir: options.sessionsDir,\n });\n console.log(output);\n } catch (error) {\n handleError(error);\n }\n });\n\n // delete command\n sessionCmd\n .command(\"delete <id>\")\n .description(\"Delete a session\")\n .option(\"--sessions-dir <path>\", \"Custom sessions directory\")\n .action(async (id: string, options: { sessionsDir?: string }) => {\n try {\n const output = await deleteCommand({\n sessionId: id,\n sessionsDir: options.sessionsDir,\n });\n console.log(output);\n } catch (error) {\n handleError(error);\n }\n });\n\n // prune command\n sessionCmd\n .command(\"prune\")\n .description(\"Remove old todo sessions, keeping the most recent N\")\n .option(\"--keep <count>\", \"Number of sessions to keep (default: 5)\", \"5\")\n .option(\"--dry-run\", \"Show what would be deleted without deleting\")\n .option(\"--sessions-dir <path>\", \"Custom sessions directory\")\n .action(async (options: { keep?: string; dryRun?: boolean; sessionsDir?: string }) => {\n try {\n const keep = options.keep ? Number.parseInt(options.keep, 10) : undefined;\n const output = await pruneCommand({\n keep,\n dryRun: options.dryRun,\n sessionsDir: options.sessionsDir,\n });\n console.log(output);\n } catch (error) {\n if (error instanceof PruneValidationError) {\n console.error(\"Error:\", error.message);\n process.exit(1);\n }\n handleError(error);\n }\n });\n\n // archive command\n sessionCmd\n .command(\"archive <id>\")\n .description(\"Move a session to the archive directory\")\n .option(\"--sessions-dir <path>\", \"Custom sessions directory\")\n .action(async (id: string, options: { sessionsDir?: string }) => {\n try {\n const output = await archiveCommand({\n sessionId: id,\n sessionsDir: options.sessionsDir,\n });\n console.log(output);\n } catch (error) {\n if (error instanceof SessionAlreadyArchivedError) {\n console.error(\"Error:\", error.message);\n process.exit(1);\n }\n handleError(error);\n }\n });\n}\n\n/**\n * Session domain - Manage session workflow\n */\nexport const sessionDomain: Domain = {\n name: \"session\",\n description: \"Manage session workflow\",\n register: (program: Command) => {\n const sessionCmd = program\n .command(\"session\")\n .description(\"Manage session workflow\")\n .addHelpText(\"after\", SESSION_FORMAT_HELP);\n\n registerSessionCommands(sessionCmd);\n },\n};\n","/**\n * Scanner class for discovering work items in a project\n *\n * Encapsulates directory walking, filtering, and work item building with\n * configurable paths via dependency injection.\n *\n * @module scanner/scanner\n */\nimport path from \"node:path\";\nimport type { SpxConfig } from \"../config/defaults.js\";\nimport type { WorkItem } from \"../types.js\";\nimport { buildWorkItemList, filterWorkItemDirectories, walkDirectory } from \"./walk.js\";\n\n/**\n * Scanner for discovering work items in a project\n *\n * Uses dependency injection for configuration, eliminating hardcoded paths.\n * All directory paths are derived from the injected SpxConfig.\n *\n * @example\n * ```typescript\n * import { DEFAULT_CONFIG } from \"@/config/defaults\";\n *\n * const scanner = new Scanner(\"/path/to/project\", DEFAULT_CONFIG);\n * const workItems = await scanner.scan();\n * ```\n */\nexport class Scanner {\n /**\n * Create a new Scanner instance\n *\n * @param projectRoot - Absolute path to the project root directory\n * @param config - Configuration object defining directory structure\n */\n constructor(\n private readonly projectRoot: string,\n private readonly config: SpxConfig,\n ) {}\n\n /**\n * Scan the project for work items in the \"doing\" status directory\n *\n * Walks the configured specs/work/doing directory, filters for valid\n * work item directories, and returns structured work item data.\n *\n * @returns Array of work items found in the doing directory\n * @throws Error if the directory doesn't exist or is inaccessible\n */\n async scan(): Promise<WorkItem[]> {\n const doingPath = this.getDoingPath();\n\n // Walk directory to find all subdirectories\n const allEntries = await walkDirectory(doingPath);\n\n // Filter to only valid work item directories\n const workItemEntries = filterWorkItemDirectories(allEntries);\n\n // Build work item list with metadata\n return buildWorkItemList(workItemEntries);\n }\n\n /**\n * Get the full path to the \"doing\" status directory\n *\n * Constructs path from config: {projectRoot}/{specs.root}/{work.dir}/{statusDirs.doing}\n *\n * @returns Absolute path to the doing directory\n */\n getDoingPath(): string {\n return path.join(\n this.projectRoot,\n this.config.specs.root,\n this.config.specs.work.dir,\n this.config.specs.work.statusDirs.doing,\n );\n }\n\n /**\n * Get the full path to the \"backlog\" status directory\n *\n * @returns Absolute path to the backlog directory\n */\n getBacklogPath(): string {\n return path.join(\n this.projectRoot,\n this.config.specs.root,\n this.config.specs.work.dir,\n this.config.specs.work.statusDirs.backlog,\n );\n }\n\n /**\n * Get the full path to the \"done\" status directory\n *\n * @returns Absolute path to the done/archive directory\n */\n getDonePath(): string {\n return path.join(\n this.projectRoot,\n this.config.specs.root,\n this.config.specs.work.dir,\n this.config.specs.work.statusDirs.done,\n );\n }\n\n /**\n * Get the full path to the specs root directory\n *\n * @returns Absolute path to the specs root\n */\n getSpecsRootPath(): string {\n return path.join(this.projectRoot, this.config.specs.root);\n }\n\n /**\n * Get the full path to the work directory\n *\n * @returns Absolute path to the work directory\n */\n getWorkPath(): string {\n return path.join(\n this.projectRoot,\n this.config.specs.root,\n this.config.specs.work.dir,\n );\n }\n}\n","/**\n * Status determination state machine for work items\n */\nimport type { WorkItemStatus } from \"../types\";\nimport { access, readdir, stat } from \"fs/promises\";\nimport path from \"path\";\n\n/**\n * Input flags for status determination\n */\nexport interface StatusFlags {\n /** Whether tests/ directory exists */\n hasTestsDir: boolean;\n /** Whether tests/DONE.md exists */\n hasDoneMd: boolean;\n /** Whether tests/ directory is empty (excluding DONE.md) */\n testsIsEmpty: boolean;\n}\n\n/**\n * Determines work item status based on tests/ directory state\n *\n * Truth Table:\n * | hasTestsDir | testsIsEmpty | hasDoneMd | Status |\n * |-------------|--------------|-----------|-------------|\n * | false | N/A | N/A | OPEN |\n * | true | true | false | OPEN |\n * | true | true | true | DONE |\n * | true | false | false | IN_PROGRESS |\n * | true | false | true | DONE |\n *\n * @param flags - Status determination flags\n * @returns Work item status as OPEN, IN_PROGRESS, or DONE\n *\n * @example\n * ```typescript\n * // No tests directory\n * determineStatus({ hasTestsDir: false, hasDoneMd: false, testsIsEmpty: true })\n * // => \"OPEN\"\n *\n * // Tests directory with files, no DONE.md\n * determineStatus({ hasTestsDir: true, hasDoneMd: false, testsIsEmpty: false })\n * // => \"IN_PROGRESS\"\n *\n * // Tests directory with DONE.md\n * determineStatus({ hasTestsDir: true, hasDoneMd: true, testsIsEmpty: false })\n * // => \"DONE\"\n * ```\n */\nexport function determineStatus(flags: StatusFlags): WorkItemStatus {\n // No tests directory → OPEN\n if (!flags.hasTestsDir) {\n return \"OPEN\";\n }\n\n // Has DONE.md → DONE (regardless of other files)\n if (flags.hasDoneMd) {\n return \"DONE\";\n }\n\n // Has tests directory but empty → OPEN\n if (flags.testsIsEmpty) {\n return \"OPEN\";\n }\n\n // Has tests directory with files, no DONE.md → IN_PROGRESS\n return \"IN_PROGRESS\";\n}\n\n/**\n * Checks if a work item has a tests/ directory\n *\n * @param workItemPath - Absolute path to the work item directory\n * @returns Promise resolving to true if tests/ exists, false otherwise\n *\n * @example\n * ```typescript\n * const hasTests = await hasTestsDirectory('/path/to/story-21');\n * // => true if /path/to/story-21/tests exists\n * ```\n */\nexport async function hasTestsDirectory(\n workItemPath: string\n): Promise<boolean> {\n try {\n const testsPath = path.join(workItemPath, \"tests\");\n await access(testsPath);\n return true;\n } catch (error) {\n // ENOENT means directory doesn't exist → return false\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n return false;\n }\n // Re-throw permission errors and other failures\n throw error;\n }\n}\n\n/**\n * Checks if a tests/ directory is empty (has no test files)\n *\n * A directory is considered empty if it contains no files, or only contains:\n * - DONE.md (completion marker, not a test)\n * - Dotfiles like .gitkeep (version control artifacts, not tests)\n *\n * @param testsPath - Absolute path to the tests/ directory\n * @returns Promise resolving to true if empty, false if has test files\n *\n * @example\n * ```typescript\n * // Directory with only DONE.md\n * await isTestsDirectoryEmpty('/path/to/tests');\n * // => true (DONE.md doesn't count as a test)\n *\n * // Directory with test files\n * await isTestsDirectoryEmpty('/path/to/tests');\n * // => false\n * ```\n */\nexport async function isTestsDirectoryEmpty(\n testsPath: string\n): Promise<boolean> {\n try {\n const entries = await readdir(testsPath);\n\n // Filter out DONE.md and dotfiles\n const testFiles = entries.filter((entry) => {\n // Exclude DONE.md\n if (entry === \"DONE.md\") {\n return false;\n }\n // Exclude dotfiles (.gitkeep, .DS_Store, etc.)\n if (entry.startsWith(\".\")) {\n return false;\n }\n return true;\n });\n\n // Empty if no test files remain after filtering\n return testFiles.length === 0;\n } catch (error) {\n // ENOENT means directory doesn't exist → treat as empty\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n return true;\n }\n // Re-throw permission errors and other failures\n throw error;\n }\n}\n\n/**\n * Checks if a tests/ directory contains a DONE.md file\n *\n * Verifies that DONE.md exists and is a regular file (not a directory).\n * The check is case-sensitive: only \"DONE.md\" is accepted.\n *\n * @param testsPath - Absolute path to the tests/ directory\n * @returns Promise resolving to true if DONE.md exists, false otherwise\n *\n * @example\n * ```typescript\n * // Tests directory with DONE.md\n * await hasDoneMd('/path/to/tests');\n * // => true\n *\n * // Tests directory without DONE.md\n * await hasDoneMd('/path/to/tests');\n * // => false\n * ```\n */\nexport async function hasDoneMd(testsPath: string): Promise<boolean> {\n try {\n // Case-sensitive check: read directory and verify exact filename\n const entries = await readdir(testsPath);\n\n // Check if \"DONE.md\" exists in the directory listing (case-sensitive)\n if (!entries.includes(\"DONE.md\")) {\n return false;\n }\n\n // Verify it's a regular file, not a directory\n const donePath = path.join(testsPath, \"DONE.md\");\n const stats = await stat(donePath);\n return stats.isFile();\n } catch (error) {\n // ENOENT means directory doesn't exist → return false\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n return false;\n }\n // Re-throw permission errors and other failures\n throw error;\n }\n}\n\n/**\n * Custom error for status determination failures\n */\nexport class StatusDeterminationError extends Error {\n constructor(\n public readonly workItemPath: string,\n public readonly cause: unknown\n ) {\n const errorMessage =\n cause instanceof Error ? cause.message : String(cause);\n super(`Failed to determine status for ${workItemPath}: ${errorMessage}`);\n this.name = \"StatusDeterminationError\";\n }\n}\n\n/**\n * Determines the status of a work item by checking its tests/ directory\n *\n * This is the main orchestration function that combines all status checks:\n * 1. Checks if tests/ directory exists\n * 2. Checks if tests/ has DONE.md\n * 3. Checks if tests/ is empty (excluding DONE.md)\n * 4. Determines final status based on these flags\n *\n * Performance: Uses caching strategy to minimize filesystem calls.\n * All checks for a single work item are performed in one pass.\n *\n * @param workItemPath - Absolute path to the work item directory\n * @returns Promise resolving to OPEN, IN_PROGRESS, or DONE\n * @throws {StatusDeterminationError} If work item doesn't exist or has permission errors\n *\n * @example\n * ```typescript\n * // Work item with no tests directory\n * await getWorkItemStatus('/path/to/story-21');\n * // => \"OPEN\"\n *\n * // Work item with tests but no DONE.md\n * await getWorkItemStatus('/path/to/story-32');\n * // => \"IN_PROGRESS\"\n *\n * // Work item with DONE.md\n * await getWorkItemStatus('/path/to/story-43');\n * // => \"DONE\"\n * ```\n */\nexport async function getWorkItemStatus(\n workItemPath: string\n): Promise<WorkItemStatus> {\n try {\n // Step 0: Verify work item path exists\n try {\n await access(workItemPath);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n throw new Error(`Work item not found: ${workItemPath}`);\n }\n // Permission error or other failure\n throw error;\n }\n\n // Step 1: Check if tests/ directory exists\n const testsPath = path.join(workItemPath, \"tests\");\n let hasTests: boolean;\n try {\n await access(testsPath);\n hasTests = true;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n hasTests = false;\n } else {\n // Permission error or other failure\n throw error;\n }\n }\n\n // Early return if no tests directory\n if (!hasTests) {\n return determineStatus({\n hasTestsDir: false,\n hasDoneMd: false,\n testsIsEmpty: true,\n });\n }\n\n // Step 2: Read tests/ directory once (caching strategy)\n // This single readdir call gives us all the data we need\n const entries = await readdir(testsPath);\n\n // Step 3: Check for DONE.md (from cached entries)\n const hasDone = entries.includes(\"DONE.md\");\n if (hasDone) {\n // Verify it's a file, not a directory\n const donePath = path.join(testsPath, \"DONE.md\");\n const stats = await stat(donePath);\n if (!stats.isFile()) {\n // DONE.md is a directory, treat as no DONE.md\n return determineStatus({\n hasTestsDir: true,\n hasDoneMd: false,\n testsIsEmpty: isEmptyFromEntries(entries),\n });\n }\n }\n\n // Step 4: Check if empty (from cached entries)\n const isEmpty = isEmptyFromEntries(entries);\n\n // Step 5: Determine final status\n return determineStatus({\n hasTestsDir: true,\n hasDoneMd: hasDone,\n testsIsEmpty: isEmpty,\n });\n } catch (error) {\n // Wrap all errors with work item context\n throw new StatusDeterminationError(workItemPath, error);\n }\n}\n\n/**\n * Helper: Check if directory is empty from readdir entries\n * @param entries - Directory entries from readdir\n * @returns true if no test files (excluding DONE.md and dotfiles)\n */\nfunction isEmptyFromEntries(entries: string[]): boolean {\n const testFiles = entries.filter((entry) => {\n // Exclude DONE.md\n if (entry === \"DONE.md\") {\n return false;\n }\n // Exclude dotfiles (.gitkeep, .DS_Store, etc.)\n if (entry.startsWith(\".\")) {\n return false;\n }\n return true;\n });\n return testFiles.length === 0;\n}\n","/**\n * Tree building functions for converting flat work item lists to hierarchical trees\n *\n * Part of Feature 54 (Tree Building)\n */\nimport { getWorkItemStatus } from \"../status/state.js\";\nimport type { WorkItem } from \"../types.js\";\nimport type { TreeNode, WorkItemTree } from \"./types.js\";\n\n/**\n * Dependencies for tree building (for testing)\n */\nexport interface TreeBuildDeps {\n getStatus?: (path: string) => Promise<string>;\n}\n\n/**\n * Build hierarchical tree from flat list of work items\n *\n * Creates parent-child relationships based on directory paths.\n * Per ADR-002, children are BSP-sorted and status is rolled up from children.\n *\n * @param workItems - Flat list of work items from scanner\n * @param deps - Optional dependencies (for testing)\n * @returns Hierarchical tree structure\n * @throws Error if orphan work items detected (items without valid parents)\n *\n * @example\n * ```typescript\n * const workItems = await walkSpecs(\"/specs\");\n * const tree = await buildTree(workItems);\n * // tree.nodes contains top-level capabilities with nested children\n * ```\n */\nexport async function buildTree(\n workItems: WorkItem[],\n deps: TreeBuildDeps = {},\n): Promise<WorkItemTree> {\n const getStatus = deps.getStatus || getWorkItemStatus;\n\n // Step 1: Determine status for each work item\n const itemsWithStatus = await Promise.all(\n workItems.map(async (item) => ({\n ...item,\n status: await getStatus(item.path),\n })),\n );\n\n // Step 2: Separate work items by kind\n const capabilities = itemsWithStatus.filter(\n (item) => item.kind === \"capability\",\n );\n const features = itemsWithStatus.filter((item) => item.kind === \"feature\");\n const stories = itemsWithStatus.filter((item) => item.kind === \"story\");\n\n // Step 3: Build tree nodes for each level (with BSP sorting)\n const storyNodes = stories.map((item) => createTreeNode(item, []));\n const featureNodes = features.map((item) => {\n const children = storyNodes\n .filter((story) => isChildOf(story.path, item.path))\n .sort((a, b) => a.number - b.number); // Sort by BSP number\n return createTreeNode(item, children);\n });\n const capabilityNodes = capabilities.map((item) => {\n const children = featureNodes\n .filter((feature) => isChildOf(feature.path, item.path))\n .sort((a, b) => a.number - b.number); // Sort by BSP number\n return createTreeNode(item, children);\n });\n\n // Step 4: Detect orphans\n detectOrphans(stories, featureNodes);\n detectOrphans(features, capabilityNodes);\n\n // Step 5: Sort top-level capabilities by BSP number\n const sortedCapabilities = capabilityNodes.sort((a, b) => a.number - b.number);\n\n // Step 6: Roll up status from children to parents\n rollupStatus(sortedCapabilities);\n\n return {\n nodes: sortedCapabilities,\n };\n}\n\n/**\n * Create a TreeNode from a work item with status and children\n */\nfunction createTreeNode(\n item: WorkItem & { status: string },\n children: TreeNode[],\n): TreeNode {\n return {\n kind: item.kind,\n number: item.number,\n slug: item.slug,\n path: item.path,\n status: item.status as \"OPEN\" | \"IN_PROGRESS\" | \"DONE\",\n children,\n };\n}\n\n/**\n * Check if childPath is a direct child of parentPath\n *\n * A path is a child if it starts with the parent path followed by a separator.\n *\n * @param childPath - Potential child path\n * @param parentPath - Potential parent path\n * @returns true if childPath is a direct child of parentPath\n */\nfunction isChildOf(childPath: string, parentPath: string): boolean {\n // Normalize paths to ensure consistent comparison\n const normalizedChild = childPath.replace(/\\/$/, \"\");\n const normalizedParent = parentPath.replace(/\\/$/, \"\");\n\n // Check if child starts with parent followed by path separator\n if (!normalizedChild.startsWith(normalizedParent + \"/\")) {\n return false;\n }\n\n // Ensure it's a direct child (not a grandchild)\n const relativePath = normalizedChild.slice(normalizedParent.length + 1);\n return !relativePath.includes(\"/\");\n}\n\n/**\n * Detect orphan work items (items without valid parents)\n *\n * @param items - Work items to check\n * @param potentialParents - Potential parent nodes\n * @throws Error if orphans detected\n */\nfunction detectOrphans(\n items: (WorkItem & { status: string })[],\n potentialParents: TreeNode[],\n): void {\n for (const item of items) {\n const hasParent = potentialParents.some((parent) => isChildOf(item.path, parent.path));\n\n if (!hasParent) {\n throw new Error(\n `Orphan work item detected: ${item.kind} \"${item.slug}\" at ${item.path} has no valid parent`,\n );\n }\n }\n}\n\n/**\n * Roll up status from children to parents\n *\n * Recursively aggregates status from child nodes to parent nodes.\n * Parent status requires BOTH own tests/DONE.md AND all children complete.\n *\n * Rollup rules (ownStatus = status from parent's own tests/DONE.md):\n * - DONE: ownStatus is DONE AND all children are DONE\n * - OPEN: ownStatus is OPEN AND all children are OPEN\n * - IN_PROGRESS: everything else (any mismatch between own and child status)\n *\n * @param nodes - Tree nodes to process (modified in place)\n */\nfunction rollupStatus(nodes: TreeNode[]): void {\n for (const node of nodes) {\n if (node.children.length > 0) {\n // First, recursively roll up status for children\n rollupStatus(node.children);\n\n // Capture node's own status (from its tests/DONE.md)\n const ownStatus = node.status;\n\n // Then, aggregate status from children\n const childStatuses = node.children.map((child) => child.status);\n const allChildrenDone = childStatuses.every(\n (status) => status === \"DONE\",\n );\n const allChildrenOpen = childStatuses.every(\n (status) => status === \"OPEN\",\n );\n\n // DONE: own status is DONE AND all children are DONE\n if (ownStatus === \"DONE\" && allChildrenDone) {\n node.status = \"DONE\";\n } // OPEN: own status is OPEN AND all children are OPEN\n else if (ownStatus === \"OPEN\" && allChildrenOpen) {\n node.status = \"OPEN\";\n } // IN_PROGRESS: everything else\n else {\n node.status = \"IN_PROGRESS\";\n }\n }\n // Leaf nodes (stories) keep their original status\n }\n}\n","/**\n * Next command implementation\n *\n * Finds the next work item to work on based on BSP order:\n * - BSP order is absolute - lower number must complete first\n * - Status (IN_PROGRESS vs OPEN) is irrelevant to priority\n * - Returns first non-DONE item in BSP order\n */\nimport { DEFAULT_CONFIG } from \"../../config/defaults.js\";\nimport { Scanner } from \"../../scanner/scanner.js\";\nimport { buildTree } from \"../../tree/build.js\";\nimport type { TreeNode, WorkItemTree } from \"../../tree/types.js\";\nimport { LEAF_KIND } from \"../../types.js\";\n\n/**\n * Options for next command\n */\nexport interface NextOptions {\n /** Working directory (defaults to current directory) */\n cwd?: string;\n}\n\n/**\n * Find the next work item to work on\n *\n * Priority order:\n * - BSP order is absolute - lower number must complete first\n * - Status (IN_PROGRESS vs OPEN) is irrelevant to priority\n * - Returns first non-DONE leaf in BSP order\n *\n * Tree is already BSP-sorted at each level (per ADR-002).\n * Traverses depth-first in BSP order to find first non-DONE leaf.\n *\n * @param tree - Work item tree\n * @returns Next leaf node to work on, or null if all done\n *\n * @example\n * ```typescript\n * const tree = buildTreeWithMixedStatus();\n * const next = findNextWorkItem(tree);\n * // => { kind: \"story\", number: 21, slug: \"lowest-bsp-story\", status: \"OPEN\", ... }\n * ```\n */\nexport function findNextWorkItem(tree: WorkItemTree): TreeNode | null {\n // Tree is already BSP-sorted at each level (per ADR-002)\n // Traverse depth-first in BSP order to find first non-DONE leaf\n return findFirstNonDoneLeaf(tree.nodes);\n}\n\n/**\n * Recursively find first non-DONE story node in BSP order\n *\n * Tree children are assumed to be pre-sorted by BSP number (per ADR-002).\n * Only story nodes are considered actionable work items.\n *\n * @param nodes - Tree nodes to traverse (assumed BSP-sorted)\n * @returns First non-DONE story node, or null if all done\n */\nfunction findFirstNonDoneLeaf(nodes: TreeNode[]): TreeNode | null {\n for (const node of nodes) {\n if (node.kind === LEAF_KIND) {\n // Leaf kind is an actionable work item - check status\n if (node.status !== \"DONE\") {\n return node;\n }\n } else {\n // Non-leaf - recurse into children (already BSP-sorted)\n const found = findFirstNonDoneLeaf(node.children);\n if (found) {\n return found;\n }\n }\n }\n return null;\n}\n\n/**\n * Format work item name for display\n *\n * @param node - Tree node to format\n * @returns Formatted name (e.g., \"story-32_next-command\")\n */\nfunction formatWorkItemName(node: TreeNode): string {\n // Display number (capability needs +1, others as-is)\n const displayNum = node.kind === \"capability\" ? node.number + 1 : node.number;\n return `${node.kind}-${displayNum}_${node.slug}`;\n}\n\n/**\n * Execute next command\n *\n * Finds and displays the next work item to work on.\n * Shows full path information for context.\n *\n * @param options - Command options\n * @returns Formatted output\n * @throws Error if specs directory doesn't exist or is inaccessible\n *\n * @example\n * ```typescript\n * const output = await nextCommand({ cwd: \"/path/to/project\" });\n * console.log(output);\n * ```\n */\nexport async function nextCommand(options: NextOptions = {}): Promise<string> {\n const cwd = options.cwd || process.cwd();\n\n // Step 1-3: Use Scanner with config-driven paths\n const scanner = new Scanner(cwd, DEFAULT_CONFIG);\n const workItems = await scanner.scan();\n\n // Handle empty project\n if (workItems.length === 0) {\n return `No work items found in ${DEFAULT_CONFIG.specs.root}/${DEFAULT_CONFIG.specs.work.dir}/${DEFAULT_CONFIG.specs.work.statusDirs.doing}`;\n }\n\n // Step 4: Build hierarchical tree with status\n const tree = await buildTree(workItems);\n\n // Step 5: Find next work item\n const next = findNextWorkItem(tree);\n\n if (!next) {\n return \"All work items are complete! 🎉\";\n }\n\n // Step 6: Find parent feature and capability for context\n const parents = findParents(tree.nodes, next);\n\n // Step 7: Format output with context\n const lines: string[] = [];\n lines.push(\"Next work item:\");\n lines.push(\"\");\n\n if (parents.capability && parents.feature) {\n lines.push(\n ` ${formatWorkItemName(parents.capability)} > ${formatWorkItemName(parents.feature)} > ${\n formatWorkItemName(next)\n }`,\n );\n } else {\n lines.push(` ${formatWorkItemName(next)}`);\n }\n\n lines.push(\"\");\n lines.push(` Status: ${next.status}`);\n lines.push(` Path: ${next.path}`);\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Find parent capability and feature for a story\n *\n * @param nodes - Tree nodes to search\n * @param target - Story node to find parents for\n * @returns Parent capability and feature, or empty object if not found\n */\nfunction findParents(\n nodes: TreeNode[],\n target: TreeNode,\n): { capability?: TreeNode; feature?: TreeNode } {\n for (const capability of nodes) {\n for (const feature of capability.children) {\n for (const story of feature.children) {\n if (story.path === target.path) {\n return { capability, feature };\n }\n }\n }\n }\n return {};\n}\n","/**\n * JSON formatter for work item trees\n *\n * Produces structured JSON with summary statistics and full tree data.\n * Part of Feature 65 (Output Formatting), Story 32.\n */\nimport type { SpxConfig } from \"@/config/defaults\";\nimport type { TreeNode, WorkItemTree } from \"@/tree/types\";\n\n/** JSON indentation (2 spaces per ADR-002 and user requirements) */\nconst JSON_INDENT = 2;\n\n/**\n * Summary counts for capabilities and features only (NOT stories)\n */\ninterface Summary {\n done: number;\n inProgress: number;\n open: number;\n}\n\n/**\n * JSON output structure\n */\ninterface JSONOutput {\n config: {\n specs: SpxConfig[\"specs\"];\n sessions: SpxConfig[\"sessions\"];\n };\n summary: Summary;\n capabilities: unknown[];\n}\n\n/**\n * Format tree as JSON with summary statistics\n *\n * Summary counts capabilities + features only (per user requirement).\n * Display numbers (per ADR-002):\n * - Capabilities: internal + 1\n * - Features/Stories: as-is\n *\n * @param tree - Work item tree to format\n * @param config - SpxConfig used for path resolution\n * @returns JSON string with 2-space indentation\n */\nexport function formatJSON(tree: WorkItemTree, config: SpxConfig): string {\n const capabilities = tree.nodes.map((node) => nodeToJSON(node));\n const summary = calculateSummary(tree);\n\n const output: JSONOutput = {\n config: {\n specs: config.specs,\n sessions: config.sessions,\n },\n summary,\n capabilities,\n };\n\n return JSON.stringify(output, null, JSON_INDENT);\n}\n\n/**\n * Convert tree node to JSON structure with display numbers\n *\n * @param node - Tree node to convert\n * @returns JSON-serializable object\n */\nfunction nodeToJSON(node: TreeNode): unknown {\n const displayNumber = getDisplayNumber(node);\n\n const base = {\n kind: node.kind,\n number: displayNumber,\n slug: node.slug,\n status: node.status,\n };\n\n // Add children based on kind\n if (node.kind === \"capability\") {\n return {\n ...base,\n features: node.children.map((child) => nodeToJSON(child)),\n };\n } else if (node.kind === \"feature\") {\n return {\n ...base,\n stories: node.children.map((child) => nodeToJSON(child)),\n };\n } else {\n // Stories have no children\n return base;\n }\n}\n\n/**\n * Calculate summary statistics\n *\n * Counts capabilities + features only (NOT stories per user requirement)\n *\n * @param tree - Work item tree\n * @returns Summary counts\n */\nfunction calculateSummary(tree: WorkItemTree): Summary {\n const summary: Summary = {\n done: 0,\n inProgress: 0,\n open: 0,\n };\n\n for (const capability of tree.nodes) {\n // Count capability\n countNode(capability, summary);\n\n // Count features (but NOT stories)\n for (const feature of capability.children) {\n countNode(feature, summary);\n }\n }\n\n return summary;\n}\n\n/**\n * Count a single node in summary\n *\n * @param node - Node to count\n * @param summary - Summary object to update\n */\nfunction countNode(node: TreeNode, summary: Summary): void {\n switch (node.status) {\n case \"DONE\":\n summary.done++;\n break;\n case \"IN_PROGRESS\":\n summary.inProgress++;\n break;\n case \"OPEN\":\n summary.open++;\n break;\n }\n}\n\n/**\n * Get display number for a work item\n *\n * Per ADR-002:\n * - Capabilities: internal + 1 (dir capability-21 has internal 20)\n * - Features/Stories: as-is\n */\nfunction getDisplayNumber(node: TreeNode): number {\n return node.kind === \"capability\" ? node.number + 1 : node.number;\n}\n","/**\n * Markdown formatter for work item trees\n *\n * Renders trees with heading hierarchy and status lines.\n * Part of Feature 65 (Output Formatting), Story 43.\n */\nimport type { TreeNode, WorkItemTree } from \"../tree/types.js\";\n\n/**\n * Format tree as markdown with heading hierarchy\n *\n * Heading levels:\n * - Capabilities: #\n * - Features: ##\n * - Stories: ###\n *\n * Display numbers (per ADR-002):\n * - Capabilities: internal + 1\n * - Features/Stories: as-is\n *\n * @param tree - Work item tree to format\n * @returns Formatted markdown\n *\n * @example\n * ```typescript\n * const tree = buildTreeWithStories();\n * const output = formatMarkdown(tree);\n * // => \"# capability-21_test\\n\\nStatus: DONE\\n\\n## feature-21_test\\n...\"\n * ```\n */\nexport function formatMarkdown(tree: WorkItemTree): string {\n const sections: string[] = [];\n\n for (const node of tree.nodes) {\n formatNode(node, 1, sections);\n }\n\n return sections.join(\"\\n\\n\");\n}\n\n/**\n * Recursively format a tree node as markdown\n *\n * @param node - Current node to format\n * @param level - Heading level (1 = #, 2 = ##, 3 = ###)\n * @param sections - Output sections array\n */\nfunction formatNode(\n node: TreeNode,\n level: number,\n sections: string[]\n): void {\n const displayNumber = getDisplayNumber(node);\n const name = `${node.kind}-${displayNumber}_${node.slug}`;\n const heading = \"#\".repeat(level);\n\n sections.push(`${heading} ${name}`);\n sections.push(`Status: ${node.status}`);\n\n // Recurse for children\n for (const child of node.children) {\n formatNode(child, level + 1, sections);\n }\n}\n\n/**\n * Get display number for a work item\n *\n * Per ADR-002:\n * - Capabilities: internal + 1 (dir capability-21 has internal 20)\n * - Features/Stories: as-is\n */\nfunction getDisplayNumber(node: TreeNode): number {\n return node.kind === \"capability\" ? node.number + 1 : node.number;\n}\n","/**\n * Table formatter for work item trees\n *\n * Renders trees as aligned tables with dynamic column widths.\n * Part of Feature 65 (Output Formatting), Story 54.\n */\nimport type { TreeNode, WorkItemTree } from \"../tree/types.js\";\n\n/**\n * Table row data\n */\ninterface TableRow {\n level: string;\n number: string;\n name: string;\n status: string;\n}\n\n/**\n * Format tree as aligned table with dynamic column widths\n *\n * Columns: Level | Number | Name | Status\n * Level shows indented hierarchy:\n * - \"Capability\" (no indent)\n * - \" Feature\" (2-space indent)\n * - \" Story\" (4-space indent)\n *\n * Display numbers (per ADR-002):\n * - Capabilities: internal + 1\n * - Features/Stories: as-is\n *\n * @param tree - Work item tree to format\n * @returns Formatted table with aligned columns\n */\nexport function formatTable(tree: WorkItemTree): string {\n const rows: TableRow[] = [];\n\n // Collect all rows\n for (const node of tree.nodes) {\n collectRows(node, 0, rows);\n }\n\n // Calculate column widths\n const widths = calculateColumnWidths(rows);\n\n // Format table\n const lines: string[] = [];\n\n // Header row\n lines.push(\n formatRow(\n {\n level: \"Level\",\n number: \"Number\",\n name: \"Name\",\n status: \"Status\",\n },\n widths\n )\n );\n\n // Separator row\n lines.push(\n `|${\"-\".repeat(widths.level + 2)}|${\"-\".repeat(widths.number + 2)}|${\"-\".repeat(widths.name + 2)}|${\"-\".repeat(widths.status + 2)}|`\n );\n\n // Data rows\n for (const row of rows) {\n lines.push(formatRow(row, widths));\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Recursively collect table rows from tree\n *\n * @param node - Current node\n * @param depth - Current depth (0 = capability, 1 = feature, 2 = story)\n * @param rows - Output rows array\n */\nfunction collectRows(node: TreeNode, depth: number, rows: TableRow[]): void {\n const indent = \" \".repeat(depth);\n const levelName = getLevelName(node.kind);\n const displayNumber = getDisplayNumber(node);\n\n rows.push({\n level: `${indent}${levelName}`,\n number: String(displayNumber),\n name: node.slug,\n status: node.status,\n });\n\n // Recurse for children\n for (const child of node.children) {\n collectRows(child, depth + 1, rows);\n }\n}\n\n/**\n * Get level name with proper capitalization\n *\n * @param kind - Work item kind\n * @returns Capitalized level name\n */\nfunction getLevelName(kind: string): string {\n return kind.charAt(0).toUpperCase() + kind.slice(1);\n}\n\n/**\n * Get display number for a work item\n *\n * Per ADR-002:\n * - Capabilities: internal + 1 (dir capability-21 has internal 20)\n * - Features/Stories: as-is\n */\nfunction getDisplayNumber(node: TreeNode): number {\n return node.kind === \"capability\" ? node.number + 1 : node.number;\n}\n\n/**\n * Calculate maximum width for each column\n *\n * @param rows - All table rows including header\n * @returns Column widths\n */\nfunction calculateColumnWidths(rows: TableRow[]): {\n level: number;\n number: number;\n name: number;\n status: number;\n} {\n const widths = {\n level: \"Level\".length,\n number: \"Number\".length,\n name: \"Name\".length,\n status: \"Status\".length,\n };\n\n for (const row of rows) {\n widths.level = Math.max(widths.level, row.level.length);\n widths.number = Math.max(widths.number, row.number.length);\n widths.name = Math.max(widths.name, row.name.length);\n widths.status = Math.max(widths.status, row.status.length);\n }\n\n return widths;\n}\n\n/**\n * Format a single table row with proper padding\n *\n * @param row - Row data\n * @param widths - Column widths\n * @returns Formatted row with | delimiters\n */\nfunction formatRow(\n row: TableRow,\n widths: { level: number; number: number; name: number; status: number }\n): string {\n return `| ${row.level.padEnd(widths.level)} | ${row.number.padEnd(widths.number)} | ${row.name.padEnd(widths.name)} | ${row.status.padEnd(widths.status)} |`;\n}\n","/**\n * Text formatter for work item trees\n *\n * Renders hierarchical tree with indentation and colored status indicators.\n * Output format:\n * capability-21_name [STATUS] (colored)\n * feature-32_name [STATUS] (colored)\n * story-21_name [STATUS] (colored)\n */\nimport chalk from \"chalk\";\nimport type { TreeNode, WorkItemTree } from \"../tree/types.js\";\nimport type { WorkItemKind } from \"../types.js\";\n\n/**\n * Format work item tree as text with hierarchical indentation\n *\n * @param tree - Work item tree to format\n * @returns Formatted text output with indentation and status\n *\n * @example\n * ```typescript\n * const tree = buildTreeWithStories();\n * const output = formatText(tree);\n * // => \"capability-21_test [DONE]\\n feature-21_test [DONE]\\n story-21_test [DONE]\"\n * ```\n */\nexport function formatText(tree: WorkItemTree): string {\n const lines: string[] = [];\n\n for (const node of tree.nodes) {\n lines.push(formatNode(node, 0));\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Format a single tree node with indentation and colored status\n *\n * @param node - Tree node to format\n * @param indent - Indentation level (0 = no indent, 2 = feature, 4 = story)\n * @returns Formatted node and all children\n */\nfunction formatNode(node: TreeNode, indent: number): string {\n const lines: string[] = [];\n\n // Format current node\n const name = formatWorkItemName(node.kind, node.number, node.slug);\n const prefix = \" \".repeat(indent);\n const status = formatStatus(node.status);\n const line = `${prefix}${name} ${status}`;\n lines.push(line);\n\n // Recursively format children with increased indentation\n for (const child of node.children) {\n lines.push(formatNode(child, indent + 2));\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Format work item name for display\n *\n * Converts internal number to display number for capabilities.\n *\n * @param kind - Work item type\n * @param number - Internal BSP number\n * @param slug - URL-safe identifier\n * @returns Formatted name (e.g., \"capability-21_core-cli\")\n */\nfunction formatWorkItemName(\n kind: WorkItemKind,\n number: number,\n slug: string\n): string {\n // Capabilities: display = internal + 1\n // Features/Stories: display = internal\n const displayNumber = kind === \"capability\" ? number + 1 : number;\n return `${kind}-${displayNumber}_${slug}`;\n}\n\n/**\n * Format status with color\n *\n * Colors:\n * - DONE: green\n * - IN_PROGRESS: yellow\n * - OPEN: gray\n *\n * @param status - Work item status\n * @returns Colored status string with brackets\n */\nfunction formatStatus(status: string): string {\n switch (status) {\n case \"DONE\":\n return chalk.green(`[${status}]`);\n case \"IN_PROGRESS\":\n return chalk.yellow(`[${status}]`);\n case \"OPEN\":\n return chalk.gray(`[${status}]`);\n default:\n return `[${status}]`;\n }\n}\n","/**\n * Status command implementation\n *\n * Orchestrates all features to display project status:\n * 1. Scanner (Feature 32) - Walk specs directory\n * 2. Status (Feature 43) - Determine status for each work item\n * 3. Tree Building (Feature 54) - Build hierarchical tree\n * 4. Output Formatting (Feature 65) - Format in specified format\n */\nimport { DEFAULT_CONFIG } from \"../../config/defaults.js\";\nimport { formatJSON } from \"../../reporter/json.js\";\nimport { formatMarkdown } from \"../../reporter/markdown.js\";\nimport { formatTable } from \"../../reporter/table.js\";\nimport { formatText } from \"../../reporter/text.js\";\nimport { Scanner } from \"../../scanner/scanner.js\";\nimport { buildTree } from \"../../tree/build.js\";\n\n/**\n * Supported output formats\n */\nexport type OutputFormat = \"text\" | \"json\" | \"markdown\" | \"table\";\n\n/**\n * Options for status command\n */\nexport interface StatusOptions {\n /** Working directory (defaults to current directory) */\n cwd?: string;\n /** Output format (defaults to text) */\n format?: OutputFormat;\n}\n\n/**\n * Execute status command\n *\n * Displays current project status by:\n * - Walking specs/doing directory to find work items\n * - Determining status for each work item\n * - Building hierarchical tree structure\n * - Formatting and outputting in specified format\n *\n * @param options - Command options\n * @returns Formatted status output\n *\n * @example\n * ```typescript\n * const output = await statusCommand({ cwd: \"/path/to/project\", format: \"json\" });\n * console.log(output);\n * ```\n */\nexport async function statusCommand(\n options: StatusOptions = {},\n): Promise<string> {\n const cwd = options.cwd || process.cwd();\n const format = options.format || \"text\";\n\n // Step 1-3: Use Scanner with config-driven paths\n const scanner = new Scanner(cwd, DEFAULT_CONFIG);\n let workItems;\n try {\n workItems = await scanner.scan();\n } catch (error) {\n // Handle missing directory gracefully\n if (error instanceof Error && error.message.includes(\"ENOENT\")) {\n const doingPath =\n `${DEFAULT_CONFIG.specs.root}/${DEFAULT_CONFIG.specs.work.dir}/${DEFAULT_CONFIG.specs.work.statusDirs.doing}`;\n throw new Error(\n `Directory ${doingPath} not found.\\n\\nThis command is for legacy specs/ projects. For CODE framework projects, check the spx/ directory for specifications.`,\n );\n }\n throw error;\n }\n\n // Handle empty project\n if (workItems.length === 0) {\n return `No work items found in ${DEFAULT_CONFIG.specs.root}/${DEFAULT_CONFIG.specs.work.dir}/${DEFAULT_CONFIG.specs.work.statusDirs.doing}`;\n }\n\n // Step 4: Build hierarchical tree with status\n const tree = await buildTree(workItems);\n\n // Step 5: Format based on requested format\n switch (format) {\n case \"json\":\n return formatJSON(tree, DEFAULT_CONFIG);\n case \"markdown\":\n return formatMarkdown(tree);\n case \"table\":\n return formatTable(tree);\n case \"text\":\n default:\n return formatText(tree);\n }\n}\n","/**\n * Spec domain - Manage spec workflow\n */\nimport type { Command } from \"commander\";\nimport { nextCommand } from \"../../commands/spec/next.js\";\nimport { type OutputFormat, statusCommand } from \"../../commands/spec/status.js\";\nimport type { Domain } from \"../types.js\";\n\n/**\n * Register spec domain commands\n *\n * @param specCmd - Commander.js spec domain command\n */\nfunction registerSpecCommands(specCmd: Command): void {\n // status command\n specCmd\n .command(\"status\")\n .description(\"Get project status\")\n .option(\"--json\", \"Output as JSON\")\n .option(\"--format <format>\", \"Output format (text|json|markdown|table)\")\n .action(async (options: { json?: boolean; format?: string }) => {\n try {\n // Determine format: --json flag overrides --format option\n let format: OutputFormat = \"text\";\n if (options.json) {\n format = \"json\";\n } else if (options.format) {\n const validFormats = [\"text\", \"json\", \"markdown\", \"table\"];\n if (validFormats.includes(options.format)) {\n format = options.format as OutputFormat;\n } else {\n console.error(\n `Error: Invalid format \"${options.format}\". Must be one of: ${validFormats.join(\", \")}`,\n );\n process.exit(1);\n }\n }\n\n const output = await statusCommand({ cwd: process.cwd(), format });\n console.log(output);\n } catch (error) {\n console.error(\n \"Error:\",\n error instanceof Error ? error.message : String(error),\n );\n process.exit(1);\n }\n });\n\n // next command\n specCmd\n .command(\"next\")\n .description(\"Find next work item to work on\")\n .action(async () => {\n try {\n const output = await nextCommand({ cwd: process.cwd() });\n console.log(output);\n } catch (error) {\n console.error(\n \"Error:\",\n error instanceof Error ? error.message : String(error),\n );\n process.exit(1);\n }\n });\n}\n\n/**\n * Spec domain - Manage spec workflow\n */\nexport const specDomain: Domain = {\n name: \"spec\",\n description: \"Manage spec workflow\",\n register: (program: Command) => {\n const specCmd = program\n .command(\"spec\")\n .description(\"Manage spec workflow\");\n\n registerSpecCommands(specCmd);\n },\n};\n","/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\n'use strict';\n/**\n * Creates a JSON scanner on the given text.\n * If ignoreTrivia is set, whitespaces or comments are ignored.\n */\nexport function createScanner(text, ignoreTrivia = false) {\n const len = text.length;\n let pos = 0, value = '', tokenOffset = 0, token = 16 /* SyntaxKind.Unknown */, lineNumber = 0, lineStartOffset = 0, tokenLineStartOffset = 0, prevTokenLineStartOffset = 0, scanError = 0 /* ScanError.None */;\n function scanHexDigits(count, exact) {\n let digits = 0;\n let value = 0;\n while (digits < count || !exact) {\n let ch = text.charCodeAt(pos);\n if (ch >= 48 /* CharacterCodes._0 */ && ch <= 57 /* CharacterCodes._9 */) {\n value = value * 16 + ch - 48 /* CharacterCodes._0 */;\n }\n else if (ch >= 65 /* CharacterCodes.A */ && ch <= 70 /* CharacterCodes.F */) {\n value = value * 16 + ch - 65 /* CharacterCodes.A */ + 10;\n }\n else if (ch >= 97 /* CharacterCodes.a */ && ch <= 102 /* CharacterCodes.f */) {\n value = value * 16 + ch - 97 /* CharacterCodes.a */ + 10;\n }\n else {\n break;\n }\n pos++;\n digits++;\n }\n if (digits < count) {\n value = -1;\n }\n return value;\n }\n function setPosition(newPosition) {\n pos = newPosition;\n value = '';\n tokenOffset = 0;\n token = 16 /* SyntaxKind.Unknown */;\n scanError = 0 /* ScanError.None */;\n }\n function scanNumber() {\n let start = pos;\n if (text.charCodeAt(pos) === 48 /* CharacterCodes._0 */) {\n pos++;\n }\n else {\n pos++;\n while (pos < text.length && isDigit(text.charCodeAt(pos))) {\n pos++;\n }\n }\n if (pos < text.length && text.charCodeAt(pos) === 46 /* CharacterCodes.dot */) {\n pos++;\n if (pos < text.length && isDigit(text.charCodeAt(pos))) {\n pos++;\n while (pos < text.length && isDigit(text.charCodeAt(pos))) {\n pos++;\n }\n }\n else {\n scanError = 3 /* ScanError.UnexpectedEndOfNumber */;\n return text.substring(start, pos);\n }\n }\n let end = pos;\n if (pos < text.length && (text.charCodeAt(pos) === 69 /* CharacterCodes.E */ || text.charCodeAt(pos) === 101 /* CharacterCodes.e */)) {\n pos++;\n if (pos < text.length && text.charCodeAt(pos) === 43 /* CharacterCodes.plus */ || text.charCodeAt(pos) === 45 /* CharacterCodes.minus */) {\n pos++;\n }\n if (pos < text.length && isDigit(text.charCodeAt(pos))) {\n pos++;\n while (pos < text.length && isDigit(text.charCodeAt(pos))) {\n pos++;\n }\n end = pos;\n }\n else {\n scanError = 3 /* ScanError.UnexpectedEndOfNumber */;\n }\n }\n return text.substring(start, end);\n }\n function scanString() {\n let result = '', start = pos;\n while (true) {\n if (pos >= len) {\n result += text.substring(start, pos);\n scanError = 2 /* ScanError.UnexpectedEndOfString */;\n break;\n }\n const ch = text.charCodeAt(pos);\n if (ch === 34 /* CharacterCodes.doubleQuote */) {\n result += text.substring(start, pos);\n pos++;\n break;\n }\n if (ch === 92 /* CharacterCodes.backslash */) {\n result += text.substring(start, pos);\n pos++;\n if (pos >= len) {\n scanError = 2 /* ScanError.UnexpectedEndOfString */;\n break;\n }\n const ch2 = text.charCodeAt(pos++);\n switch (ch2) {\n case 34 /* CharacterCodes.doubleQuote */:\n result += '\\\"';\n break;\n case 92 /* CharacterCodes.backslash */:\n result += '\\\\';\n break;\n case 47 /* CharacterCodes.slash */:\n result += '/';\n break;\n case 98 /* CharacterCodes.b */:\n result += '\\b';\n break;\n case 102 /* CharacterCodes.f */:\n result += '\\f';\n break;\n case 110 /* CharacterCodes.n */:\n result += '\\n';\n break;\n case 114 /* CharacterCodes.r */:\n result += '\\r';\n break;\n case 116 /* CharacterCodes.t */:\n result += '\\t';\n break;\n case 117 /* CharacterCodes.u */:\n const ch3 = scanHexDigits(4, true);\n if (ch3 >= 0) {\n result += String.fromCharCode(ch3);\n }\n else {\n scanError = 4 /* ScanError.InvalidUnicode */;\n }\n break;\n default:\n scanError = 5 /* ScanError.InvalidEscapeCharacter */;\n }\n start = pos;\n continue;\n }\n if (ch >= 0 && ch <= 0x1f) {\n if (isLineBreak(ch)) {\n result += text.substring(start, pos);\n scanError = 2 /* ScanError.UnexpectedEndOfString */;\n break;\n }\n else {\n scanError = 6 /* ScanError.InvalidCharacter */;\n // mark as error but continue with string\n }\n }\n pos++;\n }\n return result;\n }\n function scanNext() {\n value = '';\n scanError = 0 /* ScanError.None */;\n tokenOffset = pos;\n lineStartOffset = lineNumber;\n prevTokenLineStartOffset = tokenLineStartOffset;\n if (pos >= len) {\n // at the end\n tokenOffset = len;\n return token = 17 /* SyntaxKind.EOF */;\n }\n let code = text.charCodeAt(pos);\n // trivia: whitespace\n if (isWhiteSpace(code)) {\n do {\n pos++;\n value += String.fromCharCode(code);\n code = text.charCodeAt(pos);\n } while (isWhiteSpace(code));\n return token = 15 /* SyntaxKind.Trivia */;\n }\n // trivia: newlines\n if (isLineBreak(code)) {\n pos++;\n value += String.fromCharCode(code);\n if (code === 13 /* CharacterCodes.carriageReturn */ && text.charCodeAt(pos) === 10 /* CharacterCodes.lineFeed */) {\n pos++;\n value += '\\n';\n }\n lineNumber++;\n tokenLineStartOffset = pos;\n return token = 14 /* SyntaxKind.LineBreakTrivia */;\n }\n switch (code) {\n // tokens: []{}:,\n case 123 /* CharacterCodes.openBrace */:\n pos++;\n return token = 1 /* SyntaxKind.OpenBraceToken */;\n case 125 /* CharacterCodes.closeBrace */:\n pos++;\n return token = 2 /* SyntaxKind.CloseBraceToken */;\n case 91 /* CharacterCodes.openBracket */:\n pos++;\n return token = 3 /* SyntaxKind.OpenBracketToken */;\n case 93 /* CharacterCodes.closeBracket */:\n pos++;\n return token = 4 /* SyntaxKind.CloseBracketToken */;\n case 58 /* CharacterCodes.colon */:\n pos++;\n return token = 6 /* SyntaxKind.ColonToken */;\n case 44 /* CharacterCodes.comma */:\n pos++;\n return token = 5 /* SyntaxKind.CommaToken */;\n // strings\n case 34 /* CharacterCodes.doubleQuote */:\n pos++;\n value = scanString();\n return token = 10 /* SyntaxKind.StringLiteral */;\n // comments\n case 47 /* CharacterCodes.slash */:\n const start = pos - 1;\n // Single-line comment\n if (text.charCodeAt(pos + 1) === 47 /* CharacterCodes.slash */) {\n pos += 2;\n while (pos < len) {\n if (isLineBreak(text.charCodeAt(pos))) {\n break;\n }\n pos++;\n }\n value = text.substring(start, pos);\n return token = 12 /* SyntaxKind.LineCommentTrivia */;\n }\n // Multi-line comment\n if (text.charCodeAt(pos + 1) === 42 /* CharacterCodes.asterisk */) {\n pos += 2;\n const safeLength = len - 1; // For lookahead.\n let commentClosed = false;\n while (pos < safeLength) {\n const ch = text.charCodeAt(pos);\n if (ch === 42 /* CharacterCodes.asterisk */ && text.charCodeAt(pos + 1) === 47 /* CharacterCodes.slash */) {\n pos += 2;\n commentClosed = true;\n break;\n }\n pos++;\n if (isLineBreak(ch)) {\n if (ch === 13 /* CharacterCodes.carriageReturn */ && text.charCodeAt(pos) === 10 /* CharacterCodes.lineFeed */) {\n pos++;\n }\n lineNumber++;\n tokenLineStartOffset = pos;\n }\n }\n if (!commentClosed) {\n pos++;\n scanError = 1 /* ScanError.UnexpectedEndOfComment */;\n }\n value = text.substring(start, pos);\n return token = 13 /* SyntaxKind.BlockCommentTrivia */;\n }\n // just a single slash\n value += String.fromCharCode(code);\n pos++;\n return token = 16 /* SyntaxKind.Unknown */;\n // numbers\n case 45 /* CharacterCodes.minus */:\n value += String.fromCharCode(code);\n pos++;\n if (pos === len || !isDigit(text.charCodeAt(pos))) {\n return token = 16 /* SyntaxKind.Unknown */;\n }\n // found a minus, followed by a number so\n // we fall through to proceed with scanning\n // numbers\n case 48 /* CharacterCodes._0 */:\n case 49 /* CharacterCodes._1 */:\n case 50 /* CharacterCodes._2 */:\n case 51 /* CharacterCodes._3 */:\n case 52 /* CharacterCodes._4 */:\n case 53 /* CharacterCodes._5 */:\n case 54 /* CharacterCodes._6 */:\n case 55 /* CharacterCodes._7 */:\n case 56 /* CharacterCodes._8 */:\n case 57 /* CharacterCodes._9 */:\n value += scanNumber();\n return token = 11 /* SyntaxKind.NumericLiteral */;\n // literals and unknown symbols\n default:\n // is a literal? Read the full word.\n while (pos < len && isUnknownContentCharacter(code)) {\n pos++;\n code = text.charCodeAt(pos);\n }\n if (tokenOffset !== pos) {\n value = text.substring(tokenOffset, pos);\n // keywords: true, false, null\n switch (value) {\n case 'true': return token = 8 /* SyntaxKind.TrueKeyword */;\n case 'false': return token = 9 /* SyntaxKind.FalseKeyword */;\n case 'null': return token = 7 /* SyntaxKind.NullKeyword */;\n }\n return token = 16 /* SyntaxKind.Unknown */;\n }\n // some\n value += String.fromCharCode(code);\n pos++;\n return token = 16 /* SyntaxKind.Unknown */;\n }\n }\n function isUnknownContentCharacter(code) {\n if (isWhiteSpace(code) || isLineBreak(code)) {\n return false;\n }\n switch (code) {\n case 125 /* CharacterCodes.closeBrace */:\n case 93 /* CharacterCodes.closeBracket */:\n case 123 /* CharacterCodes.openBrace */:\n case 91 /* CharacterCodes.openBracket */:\n case 34 /* CharacterCodes.doubleQuote */:\n case 58 /* CharacterCodes.colon */:\n case 44 /* CharacterCodes.comma */:\n case 47 /* CharacterCodes.slash */:\n return false;\n }\n return true;\n }\n function scanNextNonTrivia() {\n let result;\n do {\n result = scanNext();\n } while (result >= 12 /* SyntaxKind.LineCommentTrivia */ && result <= 15 /* SyntaxKind.Trivia */);\n return result;\n }\n return {\n setPosition: setPosition,\n getPosition: () => pos,\n scan: ignoreTrivia ? scanNextNonTrivia : scanNext,\n getToken: () => token,\n getTokenValue: () => value,\n getTokenOffset: () => tokenOffset,\n getTokenLength: () => pos - tokenOffset,\n getTokenStartLine: () => lineStartOffset,\n getTokenStartCharacter: () => tokenOffset - prevTokenLineStartOffset,\n getTokenError: () => scanError,\n };\n}\nfunction isWhiteSpace(ch) {\n return ch === 32 /* CharacterCodes.space */ || ch === 9 /* CharacterCodes.tab */;\n}\nfunction isLineBreak(ch) {\n return ch === 10 /* CharacterCodes.lineFeed */ || ch === 13 /* CharacterCodes.carriageReturn */;\n}\nfunction isDigit(ch) {\n return ch >= 48 /* CharacterCodes._0 */ && ch <= 57 /* CharacterCodes._9 */;\n}\nvar CharacterCodes;\n(function (CharacterCodes) {\n CharacterCodes[CharacterCodes[\"lineFeed\"] = 10] = \"lineFeed\";\n CharacterCodes[CharacterCodes[\"carriageReturn\"] = 13] = \"carriageReturn\";\n CharacterCodes[CharacterCodes[\"space\"] = 32] = \"space\";\n CharacterCodes[CharacterCodes[\"_0\"] = 48] = \"_0\";\n CharacterCodes[CharacterCodes[\"_1\"] = 49] = \"_1\";\n CharacterCodes[CharacterCodes[\"_2\"] = 50] = \"_2\";\n CharacterCodes[CharacterCodes[\"_3\"] = 51] = \"_3\";\n CharacterCodes[CharacterCodes[\"_4\"] = 52] = \"_4\";\n CharacterCodes[CharacterCodes[\"_5\"] = 53] = \"_5\";\n CharacterCodes[CharacterCodes[\"_6\"] = 54] = \"_6\";\n CharacterCodes[CharacterCodes[\"_7\"] = 55] = \"_7\";\n CharacterCodes[CharacterCodes[\"_8\"] = 56] = \"_8\";\n CharacterCodes[CharacterCodes[\"_9\"] = 57] = \"_9\";\n CharacterCodes[CharacterCodes[\"a\"] = 97] = \"a\";\n CharacterCodes[CharacterCodes[\"b\"] = 98] = \"b\";\n CharacterCodes[CharacterCodes[\"c\"] = 99] = \"c\";\n CharacterCodes[CharacterCodes[\"d\"] = 100] = \"d\";\n CharacterCodes[CharacterCodes[\"e\"] = 101] = \"e\";\n CharacterCodes[CharacterCodes[\"f\"] = 102] = \"f\";\n CharacterCodes[CharacterCodes[\"g\"] = 103] = \"g\";\n CharacterCodes[CharacterCodes[\"h\"] = 104] = \"h\";\n CharacterCodes[CharacterCodes[\"i\"] = 105] = \"i\";\n CharacterCodes[CharacterCodes[\"j\"] = 106] = \"j\";\n CharacterCodes[CharacterCodes[\"k\"] = 107] = \"k\";\n CharacterCodes[CharacterCodes[\"l\"] = 108] = \"l\";\n CharacterCodes[CharacterCodes[\"m\"] = 109] = \"m\";\n CharacterCodes[CharacterCodes[\"n\"] = 110] = \"n\";\n CharacterCodes[CharacterCodes[\"o\"] = 111] = \"o\";\n CharacterCodes[CharacterCodes[\"p\"] = 112] = \"p\";\n CharacterCodes[CharacterCodes[\"q\"] = 113] = \"q\";\n CharacterCodes[CharacterCodes[\"r\"] = 114] = \"r\";\n CharacterCodes[CharacterCodes[\"s\"] = 115] = \"s\";\n CharacterCodes[CharacterCodes[\"t\"] = 116] = \"t\";\n CharacterCodes[CharacterCodes[\"u\"] = 117] = \"u\";\n CharacterCodes[CharacterCodes[\"v\"] = 118] = \"v\";\n CharacterCodes[CharacterCodes[\"w\"] = 119] = \"w\";\n CharacterCodes[CharacterCodes[\"x\"] = 120] = \"x\";\n CharacterCodes[CharacterCodes[\"y\"] = 121] = \"y\";\n CharacterCodes[CharacterCodes[\"z\"] = 122] = \"z\";\n CharacterCodes[CharacterCodes[\"A\"] = 65] = \"A\";\n CharacterCodes[CharacterCodes[\"B\"] = 66] = \"B\";\n CharacterCodes[CharacterCodes[\"C\"] = 67] = \"C\";\n CharacterCodes[CharacterCodes[\"D\"] = 68] = \"D\";\n CharacterCodes[CharacterCodes[\"E\"] = 69] = \"E\";\n CharacterCodes[CharacterCodes[\"F\"] = 70] = \"F\";\n CharacterCodes[CharacterCodes[\"G\"] = 71] = \"G\";\n CharacterCodes[CharacterCodes[\"H\"] = 72] = \"H\";\n CharacterCodes[CharacterCodes[\"I\"] = 73] = \"I\";\n CharacterCodes[CharacterCodes[\"J\"] = 74] = \"J\";\n CharacterCodes[CharacterCodes[\"K\"] = 75] = \"K\";\n CharacterCodes[CharacterCodes[\"L\"] = 76] = \"L\";\n CharacterCodes[CharacterCodes[\"M\"] = 77] = \"M\";\n CharacterCodes[CharacterCodes[\"N\"] = 78] = \"N\";\n CharacterCodes[CharacterCodes[\"O\"] = 79] = \"O\";\n CharacterCodes[CharacterCodes[\"P\"] = 80] = \"P\";\n CharacterCodes[CharacterCodes[\"Q\"] = 81] = \"Q\";\n CharacterCodes[CharacterCodes[\"R\"] = 82] = \"R\";\n CharacterCodes[CharacterCodes[\"S\"] = 83] = \"S\";\n CharacterCodes[CharacterCodes[\"T\"] = 84] = \"T\";\n CharacterCodes[CharacterCodes[\"U\"] = 85] = \"U\";\n CharacterCodes[CharacterCodes[\"V\"] = 86] = \"V\";\n CharacterCodes[CharacterCodes[\"W\"] = 87] = \"W\";\n CharacterCodes[CharacterCodes[\"X\"] = 88] = \"X\";\n CharacterCodes[CharacterCodes[\"Y\"] = 89] = \"Y\";\n CharacterCodes[CharacterCodes[\"Z\"] = 90] = \"Z\";\n CharacterCodes[CharacterCodes[\"asterisk\"] = 42] = \"asterisk\";\n CharacterCodes[CharacterCodes[\"backslash\"] = 92] = \"backslash\";\n CharacterCodes[CharacterCodes[\"closeBrace\"] = 125] = \"closeBrace\";\n CharacterCodes[CharacterCodes[\"closeBracket\"] = 93] = \"closeBracket\";\n CharacterCodes[CharacterCodes[\"colon\"] = 58] = \"colon\";\n CharacterCodes[CharacterCodes[\"comma\"] = 44] = \"comma\";\n CharacterCodes[CharacterCodes[\"dot\"] = 46] = \"dot\";\n CharacterCodes[CharacterCodes[\"doubleQuote\"] = 34] = \"doubleQuote\";\n CharacterCodes[CharacterCodes[\"minus\"] = 45] = \"minus\";\n CharacterCodes[CharacterCodes[\"openBrace\"] = 123] = \"openBrace\";\n CharacterCodes[CharacterCodes[\"openBracket\"] = 91] = \"openBracket\";\n CharacterCodes[CharacterCodes[\"plus\"] = 43] = \"plus\";\n CharacterCodes[CharacterCodes[\"slash\"] = 47] = \"slash\";\n CharacterCodes[CharacterCodes[\"formFeed\"] = 12] = \"formFeed\";\n CharacterCodes[CharacterCodes[\"tab\"] = 9] = \"tab\";\n})(CharacterCodes || (CharacterCodes = {}));\n","export const cachedSpaces = new Array(20).fill(0).map((_, index) => {\n return ' '.repeat(index);\n});\nconst maxCachedValues = 200;\nexport const cachedBreakLinesWithSpaces = {\n ' ': {\n '\\n': new Array(maxCachedValues).fill(0).map((_, index) => {\n return '\\n' + ' '.repeat(index);\n }),\n '\\r': new Array(maxCachedValues).fill(0).map((_, index) => {\n return '\\r' + ' '.repeat(index);\n }),\n '\\r\\n': new Array(maxCachedValues).fill(0).map((_, index) => {\n return '\\r\\n' + ' '.repeat(index);\n }),\n },\n '\\t': {\n '\\n': new Array(maxCachedValues).fill(0).map((_, index) => {\n return '\\n' + '\\t'.repeat(index);\n }),\n '\\r': new Array(maxCachedValues).fill(0).map((_, index) => {\n return '\\r' + '\\t'.repeat(index);\n }),\n '\\r\\n': new Array(maxCachedValues).fill(0).map((_, index) => {\n return '\\r\\n' + '\\t'.repeat(index);\n }),\n }\n};\nexport const supportedEols = ['\\n', '\\r', '\\r\\n'];\n","/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\n'use strict';\nimport { createScanner } from './scanner';\nvar ParseOptions;\n(function (ParseOptions) {\n ParseOptions.DEFAULT = {\n allowTrailingComma: false\n };\n})(ParseOptions || (ParseOptions = {}));\n/**\n * For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index.\n */\nexport function getLocation(text, position) {\n const segments = []; // strings or numbers\n const earlyReturnException = new Object();\n let previousNode = undefined;\n const previousNodeInst = {\n value: {},\n offset: 0,\n length: 0,\n type: 'object',\n parent: undefined\n };\n let isAtPropertyKey = false;\n function setPreviousNode(value, offset, length, type) {\n previousNodeInst.value = value;\n previousNodeInst.offset = offset;\n previousNodeInst.length = length;\n previousNodeInst.type = type;\n previousNodeInst.colonOffset = undefined;\n previousNode = previousNodeInst;\n }\n try {\n visit(text, {\n onObjectBegin: (offset, length) => {\n if (position <= offset) {\n throw earlyReturnException;\n }\n previousNode = undefined;\n isAtPropertyKey = position > offset;\n segments.push(''); // push a placeholder (will be replaced)\n },\n onObjectProperty: (name, offset, length) => {\n if (position < offset) {\n throw earlyReturnException;\n }\n setPreviousNode(name, offset, length, 'property');\n segments[segments.length - 1] = name;\n if (position <= offset + length) {\n throw earlyReturnException;\n }\n },\n onObjectEnd: (offset, length) => {\n if (position <= offset) {\n throw earlyReturnException;\n }\n previousNode = undefined;\n segments.pop();\n },\n onArrayBegin: (offset, length) => {\n if (position <= offset) {\n throw earlyReturnException;\n }\n previousNode = undefined;\n segments.push(0);\n },\n onArrayEnd: (offset, length) => {\n if (position <= offset) {\n throw earlyReturnException;\n }\n previousNode = undefined;\n segments.pop();\n },\n onLiteralValue: (value, offset, length) => {\n if (position < offset) {\n throw earlyReturnException;\n }\n setPreviousNode(value, offset, length, getNodeType(value));\n if (position <= offset + length) {\n throw earlyReturnException;\n }\n },\n onSeparator: (sep, offset, length) => {\n if (position <= offset) {\n throw earlyReturnException;\n }\n if (sep === ':' && previousNode && previousNode.type === 'property') {\n previousNode.colonOffset = offset;\n isAtPropertyKey = false;\n previousNode = undefined;\n }\n else if (sep === ',') {\n const last = segments[segments.length - 1];\n if (typeof last === 'number') {\n segments[segments.length - 1] = last + 1;\n }\n else {\n isAtPropertyKey = true;\n segments[segments.length - 1] = '';\n }\n previousNode = undefined;\n }\n }\n });\n }\n catch (e) {\n if (e !== earlyReturnException) {\n throw e;\n }\n }\n return {\n path: segments,\n previousNode,\n isAtPropertyKey,\n matches: (pattern) => {\n let k = 0;\n for (let i = 0; k < pattern.length && i < segments.length; i++) {\n if (pattern[k] === segments[i] || pattern[k] === '*') {\n k++;\n }\n else if (pattern[k] !== '**') {\n return false;\n }\n }\n return k === pattern.length;\n }\n };\n}\n/**\n * Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.\n * Therefore always check the errors list to find out if the input was valid.\n */\nexport function parse(text, errors = [], options = ParseOptions.DEFAULT) {\n let currentProperty = null;\n let currentParent = [];\n const previousParents = [];\n function onValue(value) {\n if (Array.isArray(currentParent)) {\n currentParent.push(value);\n }\n else if (currentProperty !== null) {\n currentParent[currentProperty] = value;\n }\n }\n const visitor = {\n onObjectBegin: () => {\n const object = {};\n onValue(object);\n previousParents.push(currentParent);\n currentParent = object;\n currentProperty = null;\n },\n onObjectProperty: (name) => {\n currentProperty = name;\n },\n onObjectEnd: () => {\n currentParent = previousParents.pop();\n },\n onArrayBegin: () => {\n const array = [];\n onValue(array);\n previousParents.push(currentParent);\n currentParent = array;\n currentProperty = null;\n },\n onArrayEnd: () => {\n currentParent = previousParents.pop();\n },\n onLiteralValue: onValue,\n onError: (error, offset, length) => {\n errors.push({ error, offset, length });\n }\n };\n visit(text, visitor, options);\n return currentParent[0];\n}\n/**\n * Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.\n */\nexport function parseTree(text, errors = [], options = ParseOptions.DEFAULT) {\n let currentParent = { type: 'array', offset: -1, length: -1, children: [], parent: undefined }; // artificial root\n function ensurePropertyComplete(endOffset) {\n if (currentParent.type === 'property') {\n currentParent.length = endOffset - currentParent.offset;\n currentParent = currentParent.parent;\n }\n }\n function onValue(valueNode) {\n currentParent.children.push(valueNode);\n return valueNode;\n }\n const visitor = {\n onObjectBegin: (offset) => {\n currentParent = onValue({ type: 'object', offset, length: -1, parent: currentParent, children: [] });\n },\n onObjectProperty: (name, offset, length) => {\n currentParent = onValue({ type: 'property', offset, length: -1, parent: currentParent, children: [] });\n currentParent.children.push({ type: 'string', value: name, offset, length, parent: currentParent });\n },\n onObjectEnd: (offset, length) => {\n ensurePropertyComplete(offset + length); // in case of a missing value for a property: make sure property is complete\n currentParent.length = offset + length - currentParent.offset;\n currentParent = currentParent.parent;\n ensurePropertyComplete(offset + length);\n },\n onArrayBegin: (offset, length) => {\n currentParent = onValue({ type: 'array', offset, length: -1, parent: currentParent, children: [] });\n },\n onArrayEnd: (offset, length) => {\n currentParent.length = offset + length - currentParent.offset;\n currentParent = currentParent.parent;\n ensurePropertyComplete(offset + length);\n },\n onLiteralValue: (value, offset, length) => {\n onValue({ type: getNodeType(value), offset, length, parent: currentParent, value });\n ensurePropertyComplete(offset + length);\n },\n onSeparator: (sep, offset, length) => {\n if (currentParent.type === 'property') {\n if (sep === ':') {\n currentParent.colonOffset = offset;\n }\n else if (sep === ',') {\n ensurePropertyComplete(offset);\n }\n }\n },\n onError: (error, offset, length) => {\n errors.push({ error, offset, length });\n }\n };\n visit(text, visitor, options);\n const result = currentParent.children[0];\n if (result) {\n delete result.parent;\n }\n return result;\n}\n/**\n * Finds the node at the given path in a JSON DOM.\n */\nexport function findNodeAtLocation(root, path) {\n if (!root) {\n return undefined;\n }\n let node = root;\n for (let segment of path) {\n if (typeof segment === 'string') {\n if (node.type !== 'object' || !Array.isArray(node.children)) {\n return undefined;\n }\n let found = false;\n for (const propertyNode of node.children) {\n if (Array.isArray(propertyNode.children) && propertyNode.children[0].value === segment && propertyNode.children.length === 2) {\n node = propertyNode.children[1];\n found = true;\n break;\n }\n }\n if (!found) {\n return undefined;\n }\n }\n else {\n const index = segment;\n if (node.type !== 'array' || index < 0 || !Array.isArray(node.children) || index >= node.children.length) {\n return undefined;\n }\n node = node.children[index];\n }\n }\n return node;\n}\n/**\n * Gets the JSON path of the given JSON DOM node\n */\nexport function getNodePath(node) {\n if (!node.parent || !node.parent.children) {\n return [];\n }\n const path = getNodePath(node.parent);\n if (node.parent.type === 'property') {\n const key = node.parent.children[0].value;\n path.push(key);\n }\n else if (node.parent.type === 'array') {\n const index = node.parent.children.indexOf(node);\n if (index !== -1) {\n path.push(index);\n }\n }\n return path;\n}\n/**\n * Evaluates the JavaScript object of the given JSON DOM node\n */\nexport function getNodeValue(node) {\n switch (node.type) {\n case 'array':\n return node.children.map(getNodeValue);\n case 'object':\n const obj = Object.create(null);\n for (let prop of node.children) {\n const valueNode = prop.children[1];\n if (valueNode) {\n obj[prop.children[0].value] = getNodeValue(valueNode);\n }\n }\n return obj;\n case 'null':\n case 'string':\n case 'number':\n case 'boolean':\n return node.value;\n default:\n return undefined;\n }\n}\nexport function contains(node, offset, includeRightBound = false) {\n return (offset >= node.offset && offset < (node.offset + node.length)) || includeRightBound && (offset === (node.offset + node.length));\n}\n/**\n * Finds the most inner node at the given offset. If includeRightBound is set, also finds nodes that end at the given offset.\n */\nexport function findNodeAtOffset(node, offset, includeRightBound = false) {\n if (contains(node, offset, includeRightBound)) {\n const children = node.children;\n if (Array.isArray(children)) {\n for (let i = 0; i < children.length && children[i].offset <= offset; i++) {\n const item = findNodeAtOffset(children[i], offset, includeRightBound);\n if (item) {\n return item;\n }\n }\n }\n return node;\n }\n return undefined;\n}\n/**\n * Parses the given text and invokes the visitor functions for each object, array and literal reached.\n */\nexport function visit(text, visitor, options = ParseOptions.DEFAULT) {\n const _scanner = createScanner(text, false);\n // Important: Only pass copies of this to visitor functions to prevent accidental modification, and\n // to not affect visitor functions which stored a reference to a previous JSONPath\n const _jsonPath = [];\n // Depth of onXXXBegin() callbacks suppressed. onXXXEnd() decrements this if it isn't 0 already.\n // Callbacks are only called when this value is 0.\n let suppressedCallbacks = 0;\n function toNoArgVisit(visitFunction) {\n return visitFunction ? () => suppressedCallbacks === 0 && visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter()) : () => true;\n }\n function toOneArgVisit(visitFunction) {\n return visitFunction ? (arg) => suppressedCallbacks === 0 && visitFunction(arg, _scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter()) : () => true;\n }\n function toOneArgVisitWithPath(visitFunction) {\n return visitFunction ? (arg) => suppressedCallbacks === 0 && visitFunction(arg, _scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter(), () => _jsonPath.slice()) : () => true;\n }\n function toBeginVisit(visitFunction) {\n return visitFunction ?\n () => {\n if (suppressedCallbacks > 0) {\n suppressedCallbacks++;\n }\n else {\n let cbReturn = visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter(), () => _jsonPath.slice());\n if (cbReturn === false) {\n suppressedCallbacks = 1;\n }\n }\n }\n : () => true;\n }\n function toEndVisit(visitFunction) {\n return visitFunction ?\n () => {\n if (suppressedCallbacks > 0) {\n suppressedCallbacks--;\n }\n if (suppressedCallbacks === 0) {\n visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter());\n }\n }\n : () => true;\n }\n const onObjectBegin = toBeginVisit(visitor.onObjectBegin), onObjectProperty = toOneArgVisitWithPath(visitor.onObjectProperty), onObjectEnd = toEndVisit(visitor.onObjectEnd), onArrayBegin = toBeginVisit(visitor.onArrayBegin), onArrayEnd = toEndVisit(visitor.onArrayEnd), onLiteralValue = toOneArgVisitWithPath(visitor.onLiteralValue), onSeparator = toOneArgVisit(visitor.onSeparator), onComment = toNoArgVisit(visitor.onComment), onError = toOneArgVisit(visitor.onError);\n const disallowComments = options && options.disallowComments;\n const allowTrailingComma = options && options.allowTrailingComma;\n function scanNext() {\n while (true) {\n const token = _scanner.scan();\n switch (_scanner.getTokenError()) {\n case 4 /* ScanError.InvalidUnicode */:\n handleError(14 /* ParseErrorCode.InvalidUnicode */);\n break;\n case 5 /* ScanError.InvalidEscapeCharacter */:\n handleError(15 /* ParseErrorCode.InvalidEscapeCharacter */);\n break;\n case 3 /* ScanError.UnexpectedEndOfNumber */:\n handleError(13 /* ParseErrorCode.UnexpectedEndOfNumber */);\n break;\n case 1 /* ScanError.UnexpectedEndOfComment */:\n if (!disallowComments) {\n handleError(11 /* ParseErrorCode.UnexpectedEndOfComment */);\n }\n break;\n case 2 /* ScanError.UnexpectedEndOfString */:\n handleError(12 /* ParseErrorCode.UnexpectedEndOfString */);\n break;\n case 6 /* ScanError.InvalidCharacter */:\n handleError(16 /* ParseErrorCode.InvalidCharacter */);\n break;\n }\n switch (token) {\n case 12 /* SyntaxKind.LineCommentTrivia */:\n case 13 /* SyntaxKind.BlockCommentTrivia */:\n if (disallowComments) {\n handleError(10 /* ParseErrorCode.InvalidCommentToken */);\n }\n else {\n onComment();\n }\n break;\n case 16 /* SyntaxKind.Unknown */:\n handleError(1 /* ParseErrorCode.InvalidSymbol */);\n break;\n case 15 /* SyntaxKind.Trivia */:\n case 14 /* SyntaxKind.LineBreakTrivia */:\n break;\n default:\n return token;\n }\n }\n }\n function handleError(error, skipUntilAfter = [], skipUntil = []) {\n onError(error);\n if (skipUntilAfter.length + skipUntil.length > 0) {\n let token = _scanner.getToken();\n while (token !== 17 /* SyntaxKind.EOF */) {\n if (skipUntilAfter.indexOf(token) !== -1) {\n scanNext();\n break;\n }\n else if (skipUntil.indexOf(token) !== -1) {\n break;\n }\n token = scanNext();\n }\n }\n }\n function parseString(isValue) {\n const value = _scanner.getTokenValue();\n if (isValue) {\n onLiteralValue(value);\n }\n else {\n onObjectProperty(value);\n // add property name afterwards\n _jsonPath.push(value);\n }\n scanNext();\n return true;\n }\n function parseLiteral() {\n switch (_scanner.getToken()) {\n case 11 /* SyntaxKind.NumericLiteral */:\n const tokenValue = _scanner.getTokenValue();\n let value = Number(tokenValue);\n if (isNaN(value)) {\n handleError(2 /* ParseErrorCode.InvalidNumberFormat */);\n value = 0;\n }\n onLiteralValue(value);\n break;\n case 7 /* SyntaxKind.NullKeyword */:\n onLiteralValue(null);\n break;\n case 8 /* SyntaxKind.TrueKeyword */:\n onLiteralValue(true);\n break;\n case 9 /* SyntaxKind.FalseKeyword */:\n onLiteralValue(false);\n break;\n default:\n return false;\n }\n scanNext();\n return true;\n }\n function parseProperty() {\n if (_scanner.getToken() !== 10 /* SyntaxKind.StringLiteral */) {\n handleError(3 /* ParseErrorCode.PropertyNameExpected */, [], [2 /* SyntaxKind.CloseBraceToken */, 5 /* SyntaxKind.CommaToken */]);\n return false;\n }\n parseString(false);\n if (_scanner.getToken() === 6 /* SyntaxKind.ColonToken */) {\n onSeparator(':');\n scanNext(); // consume colon\n if (!parseValue()) {\n handleError(4 /* ParseErrorCode.ValueExpected */, [], [2 /* SyntaxKind.CloseBraceToken */, 5 /* SyntaxKind.CommaToken */]);\n }\n }\n else {\n handleError(5 /* ParseErrorCode.ColonExpected */, [], [2 /* SyntaxKind.CloseBraceToken */, 5 /* SyntaxKind.CommaToken */]);\n }\n _jsonPath.pop(); // remove processed property name\n return true;\n }\n function parseObject() {\n onObjectBegin();\n scanNext(); // consume open brace\n let needsComma = false;\n while (_scanner.getToken() !== 2 /* SyntaxKind.CloseBraceToken */ && _scanner.getToken() !== 17 /* SyntaxKind.EOF */) {\n if (_scanner.getToken() === 5 /* SyntaxKind.CommaToken */) {\n if (!needsComma) {\n handleError(4 /* ParseErrorCode.ValueExpected */, [], []);\n }\n onSeparator(',');\n scanNext(); // consume comma\n if (_scanner.getToken() === 2 /* SyntaxKind.CloseBraceToken */ && allowTrailingComma) {\n break;\n }\n }\n else if (needsComma) {\n handleError(6 /* ParseErrorCode.CommaExpected */, [], []);\n }\n if (!parseProperty()) {\n handleError(4 /* ParseErrorCode.ValueExpected */, [], [2 /* SyntaxKind.CloseBraceToken */, 5 /* SyntaxKind.CommaToken */]);\n }\n needsComma = true;\n }\n onObjectEnd();\n if (_scanner.getToken() !== 2 /* SyntaxKind.CloseBraceToken */) {\n handleError(7 /* ParseErrorCode.CloseBraceExpected */, [2 /* SyntaxKind.CloseBraceToken */], []);\n }\n else {\n scanNext(); // consume close brace\n }\n return true;\n }\n function parseArray() {\n onArrayBegin();\n scanNext(); // consume open bracket\n let isFirstElement = true;\n let needsComma = false;\n while (_scanner.getToken() !== 4 /* SyntaxKind.CloseBracketToken */ && _scanner.getToken() !== 17 /* SyntaxKind.EOF */) {\n if (_scanner.getToken() === 5 /* SyntaxKind.CommaToken */) {\n if (!needsComma) {\n handleError(4 /* ParseErrorCode.ValueExpected */, [], []);\n }\n onSeparator(',');\n scanNext(); // consume comma\n if (_scanner.getToken() === 4 /* SyntaxKind.CloseBracketToken */ && allowTrailingComma) {\n break;\n }\n }\n else if (needsComma) {\n handleError(6 /* ParseErrorCode.CommaExpected */, [], []);\n }\n if (isFirstElement) {\n _jsonPath.push(0);\n isFirstElement = false;\n }\n else {\n _jsonPath[_jsonPath.length - 1]++;\n }\n if (!parseValue()) {\n handleError(4 /* ParseErrorCode.ValueExpected */, [], [4 /* SyntaxKind.CloseBracketToken */, 5 /* SyntaxKind.CommaToken */]);\n }\n needsComma = true;\n }\n onArrayEnd();\n if (!isFirstElement) {\n _jsonPath.pop(); // remove array index\n }\n if (_scanner.getToken() !== 4 /* SyntaxKind.CloseBracketToken */) {\n handleError(8 /* ParseErrorCode.CloseBracketExpected */, [4 /* SyntaxKind.CloseBracketToken */], []);\n }\n else {\n scanNext(); // consume close bracket\n }\n return true;\n }\n function parseValue() {\n switch (_scanner.getToken()) {\n case 3 /* SyntaxKind.OpenBracketToken */:\n return parseArray();\n case 1 /* SyntaxKind.OpenBraceToken */:\n return parseObject();\n case 10 /* SyntaxKind.StringLiteral */:\n return parseString(true);\n default:\n return parseLiteral();\n }\n }\n scanNext();\n if (_scanner.getToken() === 17 /* SyntaxKind.EOF */) {\n if (options.allowEmptyContent) {\n return true;\n }\n handleError(4 /* ParseErrorCode.ValueExpected */, [], []);\n return false;\n }\n if (!parseValue()) {\n handleError(4 /* ParseErrorCode.ValueExpected */, [], []);\n return false;\n }\n if (_scanner.getToken() !== 17 /* SyntaxKind.EOF */) {\n handleError(9 /* ParseErrorCode.EndOfFileExpected */, [], []);\n }\n return true;\n}\n/**\n * Takes JSON with JavaScript-style comments and remove\n * them. Optionally replaces every none-newline character\n * of comments with a replaceCharacter\n */\nexport function stripComments(text, replaceCh) {\n let _scanner = createScanner(text), parts = [], kind, offset = 0, pos;\n do {\n pos = _scanner.getPosition();\n kind = _scanner.scan();\n switch (kind) {\n case 12 /* SyntaxKind.LineCommentTrivia */:\n case 13 /* SyntaxKind.BlockCommentTrivia */:\n case 17 /* SyntaxKind.EOF */:\n if (offset !== pos) {\n parts.push(text.substring(offset, pos));\n }\n if (replaceCh !== undefined) {\n parts.push(_scanner.getTokenValue().replace(/[^\\r\\n]/g, replaceCh));\n }\n offset = _scanner.getPosition();\n break;\n }\n } while (kind !== 17 /* SyntaxKind.EOF */);\n return parts.join('');\n}\nexport function getNodeType(value) {\n switch (typeof value) {\n case 'boolean': return 'boolean';\n case 'number': return 'number';\n case 'string': return 'string';\n case 'object': {\n if (!value) {\n return 'null';\n }\n else if (Array.isArray(value)) {\n return 'array';\n }\n return 'object';\n }\n default: return 'null';\n }\n}\n","/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\n'use strict';\nimport * as formatter from './impl/format';\nimport * as edit from './impl/edit';\nimport * as scanner from './impl/scanner';\nimport * as parser from './impl/parser';\n/**\n * Creates a JSON scanner on the given text.\n * If ignoreTrivia is set, whitespaces or comments are ignored.\n */\nexport const createScanner = scanner.createScanner;\nexport var ScanError;\n(function (ScanError) {\n ScanError[ScanError[\"None\"] = 0] = \"None\";\n ScanError[ScanError[\"UnexpectedEndOfComment\"] = 1] = \"UnexpectedEndOfComment\";\n ScanError[ScanError[\"UnexpectedEndOfString\"] = 2] = \"UnexpectedEndOfString\";\n ScanError[ScanError[\"UnexpectedEndOfNumber\"] = 3] = \"UnexpectedEndOfNumber\";\n ScanError[ScanError[\"InvalidUnicode\"] = 4] = \"InvalidUnicode\";\n ScanError[ScanError[\"InvalidEscapeCharacter\"] = 5] = \"InvalidEscapeCharacter\";\n ScanError[ScanError[\"InvalidCharacter\"] = 6] = \"InvalidCharacter\";\n})(ScanError || (ScanError = {}));\nexport var SyntaxKind;\n(function (SyntaxKind) {\n SyntaxKind[SyntaxKind[\"OpenBraceToken\"] = 1] = \"OpenBraceToken\";\n SyntaxKind[SyntaxKind[\"CloseBraceToken\"] = 2] = \"CloseBraceToken\";\n SyntaxKind[SyntaxKind[\"OpenBracketToken\"] = 3] = \"OpenBracketToken\";\n SyntaxKind[SyntaxKind[\"CloseBracketToken\"] = 4] = \"CloseBracketToken\";\n SyntaxKind[SyntaxKind[\"CommaToken\"] = 5] = \"CommaToken\";\n SyntaxKind[SyntaxKind[\"ColonToken\"] = 6] = \"ColonToken\";\n SyntaxKind[SyntaxKind[\"NullKeyword\"] = 7] = \"NullKeyword\";\n SyntaxKind[SyntaxKind[\"TrueKeyword\"] = 8] = \"TrueKeyword\";\n SyntaxKind[SyntaxKind[\"FalseKeyword\"] = 9] = \"FalseKeyword\";\n SyntaxKind[SyntaxKind[\"StringLiteral\"] = 10] = \"StringLiteral\";\n SyntaxKind[SyntaxKind[\"NumericLiteral\"] = 11] = \"NumericLiteral\";\n SyntaxKind[SyntaxKind[\"LineCommentTrivia\"] = 12] = \"LineCommentTrivia\";\n SyntaxKind[SyntaxKind[\"BlockCommentTrivia\"] = 13] = \"BlockCommentTrivia\";\n SyntaxKind[SyntaxKind[\"LineBreakTrivia\"] = 14] = \"LineBreakTrivia\";\n SyntaxKind[SyntaxKind[\"Trivia\"] = 15] = \"Trivia\";\n SyntaxKind[SyntaxKind[\"Unknown\"] = 16] = \"Unknown\";\n SyntaxKind[SyntaxKind[\"EOF\"] = 17] = \"EOF\";\n})(SyntaxKind || (SyntaxKind = {}));\n/**\n * For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index.\n */\nexport const getLocation = parser.getLocation;\n/**\n * Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.\n * Therefore, always check the errors list to find out if the input was valid.\n */\nexport const parse = parser.parse;\n/**\n * Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.\n */\nexport const parseTree = parser.parseTree;\n/**\n * Finds the node at the given path in a JSON DOM.\n */\nexport const findNodeAtLocation = parser.findNodeAtLocation;\n/**\n * Finds the innermost node at the given offset. If includeRightBound is set, also finds nodes that end at the given offset.\n */\nexport const findNodeAtOffset = parser.findNodeAtOffset;\n/**\n * Gets the JSON path of the given JSON DOM node\n */\nexport const getNodePath = parser.getNodePath;\n/**\n * Evaluates the JavaScript object of the given JSON DOM node\n */\nexport const getNodeValue = parser.getNodeValue;\n/**\n * Parses the given text and invokes the visitor functions for each object, array and literal reached.\n */\nexport const visit = parser.visit;\n/**\n * Takes JSON with JavaScript-style comments and remove\n * them. Optionally replaces every none-newline character\n * of comments with a replaceCharacter\n */\nexport const stripComments = parser.stripComments;\nexport var ParseErrorCode;\n(function (ParseErrorCode) {\n ParseErrorCode[ParseErrorCode[\"InvalidSymbol\"] = 1] = \"InvalidSymbol\";\n ParseErrorCode[ParseErrorCode[\"InvalidNumberFormat\"] = 2] = \"InvalidNumberFormat\";\n ParseErrorCode[ParseErrorCode[\"PropertyNameExpected\"] = 3] = \"PropertyNameExpected\";\n ParseErrorCode[ParseErrorCode[\"ValueExpected\"] = 4] = \"ValueExpected\";\n ParseErrorCode[ParseErrorCode[\"ColonExpected\"] = 5] = \"ColonExpected\";\n ParseErrorCode[ParseErrorCode[\"CommaExpected\"] = 6] = \"CommaExpected\";\n ParseErrorCode[ParseErrorCode[\"CloseBraceExpected\"] = 7] = \"CloseBraceExpected\";\n ParseErrorCode[ParseErrorCode[\"CloseBracketExpected\"] = 8] = \"CloseBracketExpected\";\n ParseErrorCode[ParseErrorCode[\"EndOfFileExpected\"] = 9] = \"EndOfFileExpected\";\n ParseErrorCode[ParseErrorCode[\"InvalidCommentToken\"] = 10] = \"InvalidCommentToken\";\n ParseErrorCode[ParseErrorCode[\"UnexpectedEndOfComment\"] = 11] = \"UnexpectedEndOfComment\";\n ParseErrorCode[ParseErrorCode[\"UnexpectedEndOfString\"] = 12] = \"UnexpectedEndOfString\";\n ParseErrorCode[ParseErrorCode[\"UnexpectedEndOfNumber\"] = 13] = \"UnexpectedEndOfNumber\";\n ParseErrorCode[ParseErrorCode[\"InvalidUnicode\"] = 14] = \"InvalidUnicode\";\n ParseErrorCode[ParseErrorCode[\"InvalidEscapeCharacter\"] = 15] = \"InvalidEscapeCharacter\";\n ParseErrorCode[ParseErrorCode[\"InvalidCharacter\"] = 16] = \"InvalidCharacter\";\n})(ParseErrorCode || (ParseErrorCode = {}));\nexport function printParseErrorCode(code) {\n switch (code) {\n case 1 /* ParseErrorCode.InvalidSymbol */: return 'InvalidSymbol';\n case 2 /* ParseErrorCode.InvalidNumberFormat */: return 'InvalidNumberFormat';\n case 3 /* ParseErrorCode.PropertyNameExpected */: return 'PropertyNameExpected';\n case 4 /* ParseErrorCode.ValueExpected */: return 'ValueExpected';\n case 5 /* ParseErrorCode.ColonExpected */: return 'ColonExpected';\n case 6 /* ParseErrorCode.CommaExpected */: return 'CommaExpected';\n case 7 /* ParseErrorCode.CloseBraceExpected */: return 'CloseBraceExpected';\n case 8 /* ParseErrorCode.CloseBracketExpected */: return 'CloseBracketExpected';\n case 9 /* ParseErrorCode.EndOfFileExpected */: return 'EndOfFileExpected';\n case 10 /* ParseErrorCode.InvalidCommentToken */: return 'InvalidCommentToken';\n case 11 /* ParseErrorCode.UnexpectedEndOfComment */: return 'UnexpectedEndOfComment';\n case 12 /* ParseErrorCode.UnexpectedEndOfString */: return 'UnexpectedEndOfString';\n case 13 /* ParseErrorCode.UnexpectedEndOfNumber */: return 'UnexpectedEndOfNumber';\n case 14 /* ParseErrorCode.InvalidUnicode */: return 'InvalidUnicode';\n case 15 /* ParseErrorCode.InvalidEscapeCharacter */: return 'InvalidEscapeCharacter';\n case 16 /* ParseErrorCode.InvalidCharacter */: return 'InvalidCharacter';\n }\n return '<unknown ParseErrorCode>';\n}\n/**\n * Computes the edit operations needed to format a JSON document.\n *\n * @param documentText The input text\n * @param range The range to format or `undefined` to format the full content\n * @param options The formatting options\n * @returns The edit operations describing the formatting changes to the original document following the format described in {@linkcode EditResult}.\n * To apply the edit operations to the input, use {@linkcode applyEdits}.\n */\nexport function format(documentText, range, options) {\n return formatter.format(documentText, range, options);\n}\n/**\n * Computes the edit operations needed to modify a value in the JSON document.\n *\n * @param documentText The input text\n * @param path The path of the value to change. The path represents either to the document root, a property or an array item.\n * If the path points to an non-existing property or item, it will be created.\n * @param value The new value for the specified property or item. If the value is undefined,\n * the property or item will be removed.\n * @param options Options\n * @returns The edit operations describing the changes to the original document, following the format described in {@linkcode EditResult}.\n * To apply the edit operations to the input, use {@linkcode applyEdits}.\n */\nexport function modify(text, path, value, options) {\n return edit.setProperty(text, path, value, options);\n}\n/**\n * Applies edits to an input string.\n * @param text The input text\n * @param edits Edit operations following the format described in {@linkcode EditResult}.\n * @returns The text with the applied edits.\n * @throws An error if the edit operations are not well-formed as described in {@linkcode EditResult}.\n */\nexport function applyEdits(text, edits) {\n let sortedEdits = edits.slice(0).sort((a, b) => {\n const diff = a.offset - b.offset;\n if (diff === 0) {\n return a.length - b.length;\n }\n return diff;\n });\n let lastModifiedOffset = text.length;\n for (let i = sortedEdits.length - 1; i >= 0; i--) {\n let e = sortedEdits[i];\n if (e.offset + e.length <= lastModifiedOffset) {\n text = edit.applyEdit(text, e);\n }\n else {\n throw new Error('Overlapping edit');\n }\n lastModifiedOffset = e.offset;\n }\n return text;\n}\n","/**\n * TypeScript scope resolution for validation.\n *\n * Provides functions to determine which directories should be validated\n * based on tsconfig.json settings, ensuring alignment between TypeScript\n * and ESLint validation.\n *\n * @module validation/config/scope\n */\n\nimport * as JSONC from \"jsonc-parser\";\nimport { existsSync, readdirSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nimport type { ScopeConfig, ValidationScope } from \"../types.js\";\n\n// =============================================================================\n// CONSTANTS\n// =============================================================================\n\n/**\n * TSConfig file paths for each validation scope.\n */\nexport const TSCONFIG_FILES = {\n full: \"tsconfig.json\",\n production: \"tsconfig.production.json\",\n} as const;\n\n// =============================================================================\n// DEPENDENCY INJECTION INTERFACES\n// =============================================================================\n\n/**\n * Dependencies for scope resolution.\n *\n * Enables dependency injection for testing without mocking.\n */\nexport interface ScopeDeps {\n readFileSync: typeof readFileSync;\n existsSync: typeof existsSync;\n readdirSync: typeof readdirSync;\n}\n\n/**\n * Default production dependencies.\n */\nexport const defaultScopeDeps: ScopeDeps = {\n readFileSync,\n existsSync,\n readdirSync,\n};\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\ninterface TypeScriptConfig {\n include?: string[];\n exclude?: string[];\n extends?: string;\n}\n\n// =============================================================================\n// INTERNAL FUNCTIONS\n// =============================================================================\n\n/**\n * Parse TypeScript configuration using proper JSONC parser.\n *\n * @param configPath - Path to tsconfig file\n * @param deps - Injectable dependencies\n * @returns Parsed TypeScript configuration\n */\nexport function parseTypeScriptConfig(\n configPath: string,\n deps: ScopeDeps = defaultScopeDeps,\n): TypeScriptConfig {\n try {\n const configContent = deps.readFileSync(configPath, \"utf-8\");\n const parsed = JSONC.parse(configContent) as TypeScriptConfig;\n return parsed;\n } catch {\n // Fallback: return minimal config and let directory detection work\n return {\n include: [\"**/*.ts\", \"**/*.tsx\"],\n exclude: [\"node_modules/**\", \".pnpm-store/**\", \"dist/**\"],\n };\n }\n}\n\n/**\n * Resolve complete TypeScript configuration including extends.\n *\n * @param scope - Validation scope\n * @param deps - Injectable dependencies\n * @returns Resolved TypeScript configuration\n */\nexport function resolveTypeScriptConfig(\n scope: ValidationScope,\n deps: ScopeDeps = defaultScopeDeps,\n): TypeScriptConfig {\n const configFile = TSCONFIG_FILES[scope];\n const config = parseTypeScriptConfig(configFile, deps);\n\n if (config.extends) {\n const baseConfig = parseTypeScriptConfig(config.extends, deps);\n return {\n include: config.include ?? baseConfig.include ?? [],\n exclude: [...(baseConfig.exclude ?? []), ...(config.exclude ?? [])],\n };\n }\n\n return {\n include: config.include ?? [],\n exclude: config.exclude ?? [],\n };\n}\n\n/**\n * Check if a directory contains TypeScript files recursively.\n *\n * @param dirPath - Directory to check\n * @param maxDepth - Maximum recursion depth\n * @param deps - Injectable dependencies\n * @returns True if directory contains TypeScript files\n */\nexport function hasTypeScriptFilesRecursive(\n dirPath: string,\n maxDepth: number = 2,\n deps: ScopeDeps = defaultScopeDeps,\n): boolean {\n if (maxDepth <= 0) return false;\n\n try {\n const items = deps.readdirSync(dirPath, { withFileTypes: true });\n\n // Check for TypeScript files in current directory\n const hasDirectTsFiles = items.some(\n (item) => item.isFile() && (item.name.endsWith(\".ts\") || item.name.endsWith(\".tsx\")),\n );\n\n if (hasDirectTsFiles) return true;\n\n // Check subdirectories (limited depth to avoid performance issues)\n const subdirs = items.filter((item) => item.isDirectory() && !item.name.startsWith(\".\"));\n for (const subdir of subdirs.slice(0, 5)) {\n // Limit to first 5 subdirs\n if (hasTypeScriptFilesRecursive(join(dirPath, subdir.name), maxDepth - 1, deps)) {\n return true;\n }\n }\n\n return false;\n } catch {\n return false;\n }\n}\n\n/**\n * Get top-level directories containing TypeScript files.\n *\n * @param config - TypeScript configuration\n * @param deps - Injectable dependencies\n * @returns Array of directory names\n */\nexport function getTopLevelDirectoriesWithTypeScript(\n config: TypeScriptConfig,\n deps: ScopeDeps = defaultScopeDeps,\n): string[] {\n const allTopLevelItems = deps.readdirSync(\".\", { withFileTypes: true });\n const directories = new Set<string>();\n\n // Find all top-level directories\n const topLevelDirs = allTopLevelItems\n .filter((item) => item.isDirectory())\n .map((item) => item.name)\n .filter((name) => !name.startsWith(\".\"));\n\n // Check if each directory should be included based on tsconfig include/exclude patterns\n for (const dir of topLevelDirs) {\n // Check if directory is explicitly excluded\n const isExcluded = config.exclude?.some((pattern) => {\n // Handle patterns like \"specs/**/*\", \"docs/**/*\"\n if (pattern.includes(\"/**\")) {\n const dirPattern = pattern.split(\"/**\")[0];\n return dirPattern === dir;\n }\n // Handle exact matches and directory patterns\n return pattern === dir || pattern.startsWith(dir + \"/\") || pattern === dir + \"/**\";\n });\n\n if (!isExcluded) {\n // Check if directory has TypeScript files\n try {\n const hasTypeScriptFiles = hasTypeScriptFilesRecursive(dir, 2, deps);\n if (hasTypeScriptFiles) {\n directories.add(dir);\n }\n } catch {\n // Directory access error, skip\n continue;\n }\n }\n }\n\n // Also add explicitly mentioned directories from include patterns\n if (config.include) {\n for (const pattern of config.include) {\n // Extract directory from patterns like \"scripts/**/*.ts\", \"tests/**/*.tsx\"\n if (pattern.includes(\"/\")) {\n const topLevelDir = pattern.split(\"/\")[0];\n if (topLevelDir && !topLevelDir.includes(\"*\") && !topLevelDir.startsWith(\".\")) {\n directories.add(topLevelDir);\n }\n }\n }\n }\n\n return Array.from(directories).sort();\n}\n\n/**\n * Get validation directories based on tsconfig files.\n *\n * @param scope - Validation scope\n * @param deps - Injectable dependencies\n * @returns Array of directory names to validate\n */\nexport function getValidationDirectories(\n scope: ValidationScope,\n deps: ScopeDeps = defaultScopeDeps,\n): string[] {\n // Get TypeScript configuration for the specified mode\n const config = resolveTypeScriptConfig(scope, deps);\n\n // Get directories that contain TypeScript files and respect tsconfig exclude patterns\n const configDirectories = getTopLevelDirectoriesWithTypeScript(config, deps);\n\n // Only include directories that actually exist\n const existingDirectories = configDirectories.filter((dir) => deps.existsSync(dir));\n\n return existingDirectories;\n}\n\n/**\n * Get authoritative validation scope configuration.\n *\n * This is the main entry point for scope resolution. Returns a ScopeConfig\n * object that can be used to configure validation tools.\n *\n * @param scope - Validation scope\n * @param deps - Injectable dependencies\n * @returns Scope configuration\n *\n * @example\n * ```typescript\n * const scopeConfig = getTypeScriptScope(\"full\");\n * console.log(scopeConfig.directories); // [\"src\", \"tests\", \"scripts\"]\n * ```\n */\nexport function getTypeScriptScope(\n scope: ValidationScope,\n deps: ScopeDeps = defaultScopeDeps,\n): ScopeConfig {\n // Use validation-focused directory selection\n const directories = getValidationDirectories(scope, deps);\n\n // Read TypeScript config for patterns\n const config = resolveTypeScriptConfig(scope, deps);\n\n return {\n directories,\n filePatterns: config.include ?? [],\n excludePatterns: config.exclude ?? [],\n };\n}\n","/**\n * Tool discovery for validation infrastructure.\n *\n * Discovers validation tools (eslint, tsc, madge, etc.) using a three-tier\n * priority system: bundled → project → global.\n *\n * @module validation/discovery/tool-finder\n */\n\nimport { execSync } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport path from \"node:path\";\n\nimport { TOOL_DISCOVERY, type ToolSource } from \"./constants.js\";\n\n/**\n * Information about a found tool.\n */\nexport interface ToolLocation {\n /** The tool name */\n tool: string;\n /** Absolute path to the tool executable or package */\n path: string;\n /** Where the tool was found */\n source: ToolSource;\n}\n\n/**\n * Information about a tool that was not found.\n */\nexport interface ToolNotFound {\n /** The tool name that was searched for */\n tool: string;\n /** Human-readable reason why the tool was not found */\n reason: string;\n}\n\n/**\n * Result of tool discovery - either found with location or not found with reason.\n */\nexport type ToolDiscoveryResult =\n | { found: true; location: ToolLocation }\n | { found: false; notFound: ToolNotFound };\n\n/**\n * Dependencies for tool discovery.\n * Enables testing without mocking by accepting controlled implementations.\n */\nexport interface ToolDiscoveryDeps {\n /**\n * Resolve a module path, returns the resolved path or null if not found.\n * @param modulePath - The module path to resolve (e.g., \"eslint/package.json\")\n */\n resolveModule: (modulePath: string) => string | null;\n\n /**\n * Check if a file exists at the given path.\n * @param filePath - The path to check\n */\n existsSync: (filePath: string) => boolean;\n\n /**\n * Find an executable in the system PATH.\n * @param tool - The tool name to find\n * @returns The absolute path to the tool, or null if not found\n */\n whichSync: (tool: string) => string | null;\n}\n\n/**\n * Create a require function for resolving modules.\n * Uses import.meta.url to create a require that resolves from this package.\n */\nconst require = createRequire(import.meta.url);\n\n/**\n * Default production dependencies for tool discovery.\n */\nexport const defaultToolDiscoveryDeps: ToolDiscoveryDeps = {\n resolveModule: (modulePath: string): string | null => {\n try {\n return require.resolve(modulePath);\n } catch {\n return null;\n }\n },\n\n existsSync: fs.existsSync,\n\n whichSync: (tool: string): string | null => {\n try {\n const result = execSync(`which ${tool}`, {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n return result.trim() || null;\n } catch {\n return null;\n }\n },\n};\n\n/**\n * Options for tool discovery.\n */\nexport interface DiscoverToolOptions {\n /**\n * Project root directory for checking project-local node_modules.\n * Defaults to current working directory.\n */\n projectRoot?: string;\n\n /**\n * Dependencies for tool discovery.\n * Defaults to production dependencies.\n */\n deps?: ToolDiscoveryDeps;\n}\n\n/**\n * Discover a validation tool using three-tier priority.\n *\n * Discovery order:\n * 1. **Bundled**: Check if the tool is bundled with spx-cli via require.resolve\n * 2. **Project**: Check project's node_modules/.bin directory\n * 3. **Global**: Check system PATH via `which` command\n *\n * @param tool - The tool name to discover (e.g., \"eslint\", \"typescript\", \"madge\")\n * @param options - Discovery options including projectRoot and dependencies\n * @returns Discovery result with found location or not found reason\n *\n * @example\n * ```typescript\n * const result = await discoverTool(\"eslint\");\n * if (result.found) {\n * console.log(`Found ${result.location.tool} at ${result.location.path}`);\n * console.log(`Source: ${result.location.source}`);\n * } else {\n * console.log(`Not found: ${result.notFound.reason}`);\n * }\n * ```\n */\nexport async function discoverTool(\n tool: string,\n options: DiscoverToolOptions = {},\n): Promise<ToolDiscoveryResult> {\n const { projectRoot = process.cwd(), deps = defaultToolDiscoveryDeps } = options;\n\n // Tier 1: Check if bundled with spx-cli\n const bundledPath = deps.resolveModule(`${tool}/package.json`);\n if (bundledPath) {\n return {\n found: true,\n location: {\n tool,\n path: path.dirname(bundledPath),\n source: TOOL_DISCOVERY.SOURCES.BUNDLED,\n },\n };\n }\n\n // Tier 2: Check project's node_modules/.bin\n const projectBinPath = path.join(projectRoot, \"node_modules\", \".bin\", tool);\n if (deps.existsSync(projectBinPath)) {\n return {\n found: true,\n location: {\n tool,\n path: projectBinPath,\n source: TOOL_DISCOVERY.SOURCES.PROJECT,\n },\n };\n }\n\n // Tier 3: Check system PATH\n const globalPath = deps.whichSync(tool);\n if (globalPath) {\n return {\n found: true,\n location: {\n tool,\n path: globalPath,\n source: TOOL_DISCOVERY.SOURCES.GLOBAL,\n },\n };\n }\n\n // Not found anywhere\n return {\n found: false,\n notFound: {\n tool,\n reason: TOOL_DISCOVERY.MESSAGES.NOT_FOUND_REASON(tool),\n },\n };\n}\n\n/**\n * Format a graceful skip message for when a tool is not found.\n *\n * @param stepName - The name of the validation step being skipped\n * @param result - The tool discovery result\n * @returns Formatted skip message, or empty string if tool was found\n *\n * @example\n * ```typescript\n * const result = await discoverTool(\"madge\");\n * const message = formatSkipMessage(\"Circular dependency check\", result);\n * // \"⏭ Skipping Circular dependency check (madge not available)\"\n * ```\n */\nexport function formatSkipMessage(\n stepName: string,\n result: ToolDiscoveryResult,\n): string {\n if (result.found) {\n return \"\";\n }\n return TOOL_DISCOVERY.MESSAGES.SKIP_FORMAT(stepName, result.notFound.tool);\n}\n","/**\n * Constants for tool discovery.\n * @module validation/discovery/constants\n */\n\n/**\n * Tool discovery constants.\n * Using constants ensures DRY principle and makes tests verify behavior via constants.\n */\nexport const TOOL_DISCOVERY = {\n /** Tool source identifiers */\n SOURCES: {\n /** Tool bundled with spx-cli */\n BUNDLED: \"bundled\",\n /** Tool in project's node_modules */\n PROJECT: \"project\",\n /** Tool in system PATH */\n GLOBAL: \"global\",\n } as const,\n\n /** Message templates */\n MESSAGES: {\n /** Prefix for skip messages */\n SKIP_PREFIX: \"\\u23ED\", // ⏭ emoji\n /**\n * Format not found reason message.\n * @param tool - The tool name that was not found\n */\n NOT_FOUND_REASON: (tool: string): string =>\n `${tool} not found in bundled deps, project node_modules, or system PATH`,\n /**\n * Format skip message for graceful degradation.\n * @param step - The validation step name\n * @param tool - The tool that was not found\n */\n SKIP_FORMAT: (step: string, tool: string): string =>\n `${TOOL_DISCOVERY.MESSAGES.SKIP_PREFIX} Skipping ${step} (${tool} not available)`,\n },\n} as const;\n\n/** Type for tool source */\nexport type ToolSource = (typeof TOOL_DISCOVERY.SOURCES)[keyof typeof TOOL_DISCOVERY.SOURCES];\n","/**\n * Circular dependency validation step.\n *\n * Uses madge to detect circular imports in the codebase.\n *\n * @module validation/steps/circular\n */\n\nimport madge from \"madge\";\n\nimport { TSCONFIG_FILES } from \"../config/scope.js\";\nimport type {\n CircularDependencyResult,\n ScopeConfig,\n ValidationContext,\n ValidationScope,\n ValidationStep,\n ValidationStepResult,\n} from \"../types.js\";\n\nimport { STEP_DESCRIPTIONS, STEP_IDS, STEP_NAMES, VALIDATION_KEYS } from \"./constants.js\";\n\n// =============================================================================\n// DEPENDENCY INJECTION INTERFACES\n// =============================================================================\n\n/**\n * Dependencies for circular dependency validation.\n *\n * Enables dependency injection for testing.\n */\nexport interface CircularDeps {\n madge: typeof madge;\n}\n\n/**\n * Default production dependencies.\n */\nexport const defaultCircularDeps: CircularDeps = {\n madge,\n};\n\n// =============================================================================\n// VALIDATION FUNCTION\n// =============================================================================\n\n/**\n * Validate circular dependencies using TypeScript-derived scope.\n *\n * @param scope - Validation scope\n * @param typescriptScope - Scope configuration from tsconfig\n * @param deps - Injectable dependencies\n * @returns Result with success status and any circular dependencies found\n *\n * @example\n * ```typescript\n * const result = await validateCircularDependencies(\"full\", scopeConfig);\n * if (!result.success) {\n * console.error(\"Found circular dependencies:\", result.circularDependencies);\n * }\n * ```\n */\nexport async function validateCircularDependencies(\n scope: ValidationScope,\n typescriptScope: ScopeConfig,\n deps: CircularDeps = defaultCircularDeps,\n): Promise<CircularDependencyResult> {\n try {\n // Use TypeScript-derived directories for perfect scope alignment\n const analyzeDirectories = typescriptScope.directories;\n\n if (analyzeDirectories.length === 0) {\n return { success: true };\n }\n\n // Use the appropriate TypeScript config based on scope\n const tsConfigFile = TSCONFIG_FILES[scope];\n\n // Convert tsconfig exclude patterns to madge excludeRegExp\n const excludeRegExps = typescriptScope.excludePatterns.map((pattern) => {\n // Remove trailing /**/* or /* for cleaner matching\n const cleanPattern = pattern.replace(/\\/\\*\\*?\\/\\*$/, \"\");\n // Escape regex special chars and create regex\n const escaped = cleanPattern.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n return new RegExp(escaped);\n });\n\n const result = await deps.madge(analyzeDirectories, {\n fileExtensions: [\"ts\", \"tsx\"],\n tsConfig: tsConfigFile,\n excludeRegExp: excludeRegExps,\n });\n\n const circular = result.circular();\n\n if (circular.length === 0) {\n return { success: true };\n } else {\n return {\n success: false,\n error: `Found ${circular.length} circular dependency cycle(s)`,\n circularDependencies: circular,\n };\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n return { success: false, error: errorMessage };\n }\n}\n\n// =============================================================================\n// VALIDATION STEP DEFINITION\n// =============================================================================\n\n/**\n * Circular dependency validation step.\n *\n * Enabled when not in file-specific mode and TypeScript validation is enabled.\n */\nexport const circularDependencyStep: ValidationStep = {\n id: STEP_IDS.CIRCULAR,\n name: STEP_NAMES.CIRCULAR,\n description: STEP_DESCRIPTIONS.CIRCULAR,\n enabled: (context: ValidationContext) =>\n !context.isFileSpecificMode && context.enabledValidations[VALIDATION_KEYS.TYPESCRIPT] === true,\n execute: async (context: ValidationContext): Promise<ValidationStepResult> => {\n const startTime = performance.now();\n try {\n const result = await validateCircularDependencies(context.scope, context.scopeConfig);\n return {\n success: result.success,\n error: result.error,\n duration: performance.now() - startTime,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n duration: performance.now() - startTime,\n };\n }\n },\n};\n","/**\n * Constants for validation steps.\n *\n * DRY constants that are verified in tests rather than using literal strings.\n *\n * @module validation/steps/constants\n */\n\n/**\n * Step identifiers for validation steps.\n */\nexport const STEP_IDS = {\n CIRCULAR: \"circular-deps\",\n ESLINT: \"eslint\",\n TYPESCRIPT: \"typescript\",\n KNIP: \"knip\",\n} as const;\n\n/**\n * Human-readable step names.\n */\nexport const STEP_NAMES = {\n CIRCULAR: \"Circular Dependencies\",\n ESLINT: \"ESLint\",\n TYPESCRIPT: \"TypeScript\",\n KNIP: \"Unused Code\",\n} as const;\n\n/**\n * Step descriptions shown during execution.\n */\nexport const STEP_DESCRIPTIONS = {\n CIRCULAR: \"Checking for circular dependencies\",\n ESLINT: \"Validating ESLint compliance\",\n TYPESCRIPT: \"Validating TypeScript\",\n KNIP: \"Detecting unused exports, dependencies, and files\",\n} as const;\n\n/**\n * Cache file locations.\n */\nexport const CACHE_PATHS = {\n ESLINT: \"dist/.eslintcache\",\n TIMINGS: \"dist/.validation-timings.json\",\n} as const;\n\n/**\n * Validation type keys.\n */\nexport const VALIDATION_KEYS = {\n TYPESCRIPT: \"TYPESCRIPT\",\n ESLINT: \"ESLINT\",\n KNIP: \"KNIP\",\n} as const;\n\n/**\n * Default enabled state for each validation type.\n */\nexport const VALIDATION_DEFAULTS = {\n [VALIDATION_KEYS.TYPESCRIPT]: true,\n [VALIDATION_KEYS.ESLINT]: true,\n [VALIDATION_KEYS.KNIP]: false,\n} as const;\n","/**\n * Circular dependency check command.\n *\n * Runs madge to detect circular dependencies.\n */\nimport { getTypeScriptScope } from \"../../validation/config/scope.js\";\nimport { discoverTool, formatSkipMessage } from \"../../validation/discovery/index.js\";\nimport { validateCircularDependencies } from \"../../validation/steps/circular.js\";\nimport type { CircularCommandOptions, ValidationCommandResult } from \"./types\";\n\n/**\n * Check for circular dependencies.\n *\n * @param options - Command options\n * @returns Command result with exit code and output\n */\nexport async function circularCommand(options: CircularCommandOptions): Promise<ValidationCommandResult> {\n const { cwd, quiet } = options;\n const startTime = Date.now();\n\n // Discover madge\n const toolResult = await discoverTool(\"madge\", { projectRoot: cwd });\n if (!toolResult.found) {\n const skipMessage = formatSkipMessage(\"circular dependency check\", toolResult);\n return { exitCode: 0, output: skipMessage, durationMs: Date.now() - startTime };\n }\n\n // Get scope configuration from tsconfig (circular always uses full scope)\n const scopeConfig = getTypeScriptScope(\"full\");\n\n // Run circular dependency validation\n const result = await validateCircularDependencies(\"full\", scopeConfig);\n const durationMs = Date.now() - startTime;\n\n // Map result to command output\n if (result.success) {\n const output = quiet ? \"\" : `Circular dependencies: ✓ None found`;\n return { exitCode: 0, output, durationMs };\n } else {\n // Format circular dependency output\n let output = result.error ?? \"Circular dependencies found\";\n if (result.circularDependencies && result.circularDependencies.length > 0) {\n const cycles = result.circularDependencies\n .map((cycle) => ` ${cycle.join(\" → \")}`)\n .join(\"\\n\");\n output = `Circular dependencies found:\\n${cycles}`;\n }\n return { exitCode: 1, output, durationMs };\n }\n}\n","/**\n * Output formatting functions for validation commands.\n *\n * Pure functions that format validation results for CLI display.\n * These functions have no side effects and are fully testable.\n */\n\n/** Threshold in milliseconds for switching from ms to seconds display */\nexport const DURATION_THRESHOLD_MS = 1000;\n\n/** Symbols used in validation output */\nexport const VALIDATION_SYMBOLS = {\n SUCCESS: \"✓\",\n FAILURE: \"✗\",\n} as const;\n\n/**\n * Format a duration in milliseconds for display.\n *\n * @param ms - Duration in milliseconds\n * @returns Formatted string (e.g., \"500ms\" or \"1.5s\")\n */\nexport function formatDuration(ms: number): string {\n if (ms < DURATION_THRESHOLD_MS) {\n return `${ms}ms`;\n }\n const seconds = ms / 1000;\n return `${seconds.toFixed(1)}s`;\n}\n\n/** Options for formatting a validation step output */\nexport interface FormatStepOptions {\n /** Current step number (1-indexed) */\n stepNumber: number;\n /** Total number of steps */\n totalSteps: number;\n /** Name of the validation step */\n name: string;\n /** Result message (e.g., \"✓ No issues found\") */\n result: string;\n /** Duration in milliseconds */\n durationMs: number;\n}\n\n/**\n * Format a validation step result for display.\n *\n * @param options - Step formatting options\n * @returns Formatted string (e.g., \"[1/4] ESLint: ✓ No issues found (0.8s)\")\n */\nexport function formatStepOutput(options: FormatStepOptions): string {\n const { stepNumber, totalSteps, name, result, durationMs } = options;\n const duration = formatDuration(durationMs);\n return `[${stepNumber}/${totalSteps}] ${name}: ${result} (${duration})`;\n}\n\n/** Options for formatting the validation summary */\nexport interface FormatSummaryOptions {\n /** Whether all required validations passed */\n success: boolean;\n /** Total duration in milliseconds */\n totalDurationMs: number;\n}\n\n/**\n * Format the final validation summary.\n *\n * @param options - Summary formatting options\n * @returns Formatted string (e.g., \"✓ Validation passed (2.7s total)\")\n */\nexport function formatSummary(options: FormatSummaryOptions): string {\n const { success, totalDurationMs } = options;\n const symbol = success ? VALIDATION_SYMBOLS.SUCCESS : VALIDATION_SYMBOLS.FAILURE;\n const status = success ? \"passed\" : \"failed\";\n const duration = formatDuration(totalDurationMs);\n return `${symbol} Validation ${status} (${duration} total)`;\n}\n","/**\n * ESLint validation step.\n *\n * Validates code against ESLint rules with automatic TypeScript scope alignment.\n *\n * @module validation/steps/eslint\n */\n\nimport { spawn } from \"node:child_process\";\n\nimport type {\n ExecutionMode,\n ProcessRunner,\n ValidationContext,\n ValidationStep,\n ValidationStepResult,\n} from \"../types.js\";\nimport { EXECUTION_MODES, VALIDATION_SCOPES } from \"../types.js\";\n\nimport { CACHE_PATHS, STEP_DESCRIPTIONS, STEP_IDS, STEP_NAMES, VALIDATION_KEYS } from \"./constants.js\";\n\n// =============================================================================\n// DEFAULT DEPENDENCIES\n// =============================================================================\n\n/**\n * Default production process runner for ESLint.\n */\nexport const defaultEslintProcessRunner: ProcessRunner = { spawn };\n\n// =============================================================================\n// PURE ARGUMENT BUILDER\n// =============================================================================\n\n/**\n * Build ESLint CLI arguments based on validation context.\n *\n * Pure function for testability - can be verified at Level 1.\n *\n * @param context - Context for building arguments\n * @returns Array of ESLint CLI arguments\n *\n * @example\n * ```typescript\n * const args = buildEslintArgs({\n * validatedFiles: [\"src/index.ts\"],\n * mode: \"write\",\n * cacheFile: \"dist/.eslintcache\",\n * });\n * // Returns: [\"eslint\", \"--config\", \"eslint.config.ts\", \"--cache\", ...]\n * ```\n */\nexport function buildEslintArgs(context: {\n validatedFiles?: string[];\n mode?: ExecutionMode;\n cacheFile: string;\n}): string[] {\n const { validatedFiles, mode, cacheFile } = context;\n const fixArg = mode === EXECUTION_MODES.WRITE ? [\"--fix\"] : [];\n const cacheArgs = [\"--cache\", \"--cache-location\", cacheFile];\n\n if (validatedFiles && validatedFiles.length > 0) {\n return [\"eslint\", \"--config\", \"eslint.config.ts\", ...cacheArgs, ...fixArg, \"--\", ...validatedFiles];\n }\n return [\"eslint\", \".\", \"--config\", \"eslint.config.ts\", ...cacheArgs, ...fixArg];\n}\n\n// =============================================================================\n// VALIDATION FUNCTION\n// =============================================================================\n\n/**\n * Validate ESLint compliance using automatic TypeScript scope alignment.\n *\n * @param context - Validation context\n * @param runner - Injectable process runner\n * @returns Promise resolving to validation result\n *\n * @example\n * ```typescript\n * const result = await validateESLint(context);\n * if (!result.success) {\n * console.error(\"ESLint failed:\", result.error);\n * }\n * ```\n */\nexport async function validateESLint(\n context: ValidationContext,\n runner: ProcessRunner = defaultEslintProcessRunner,\n): Promise<{\n success: boolean;\n error?: string;\n}> {\n const { scope, validatedFiles, mode } = context;\n\n return new Promise((resolve) => {\n // Set environment variable to control ESLint scope\n if (!validatedFiles || validatedFiles.length === 0) {\n if (scope === VALIDATION_SCOPES.PRODUCTION) {\n process.env.ESLINT_PRODUCTION_ONLY = \"1\";\n } else {\n delete process.env.ESLINT_PRODUCTION_ONLY;\n }\n }\n\n // Build ESLint arguments\n const eslintArgs = buildEslintArgs({\n validatedFiles,\n mode,\n cacheFile: CACHE_PATHS.ESLINT,\n });\n\n const eslintProcess = runner.spawn(\"npx\", eslintArgs, {\n cwd: process.cwd(),\n stdio: \"inherit\",\n });\n\n eslintProcess.on(\"close\", (code) => {\n if (code === 0) {\n resolve({ success: true });\n } else {\n resolve({ success: false, error: `ESLint exited with code ${code}` });\n }\n });\n\n eslintProcess.on(\"error\", (error) => {\n resolve({ success: false, error: error.message });\n });\n });\n}\n\n// =============================================================================\n// ENVIRONMENT CHECK\n// =============================================================================\n\n/**\n * Check if a validation type is enabled via environment variable.\n *\n * @param envVarKey - Validation key (TYPESCRIPT, ESLINT, KNIP)\n * @param defaults - Default enabled states\n * @returns True if the validation is enabled\n */\nexport function validationEnabled(\n envVarKey: string,\n defaults: Record<string, boolean> = {},\n): boolean {\n const envVar = `${envVarKey}_VALIDATION_ENABLED`;\n const explicitlyDisabled = process.env[envVar] === \"0\";\n const explicitlyEnabled = process.env[envVar] === \"1\";\n\n const defaultValue = defaults[envVarKey] ?? true;\n if (defaultValue) {\n return !explicitlyDisabled;\n }\n return explicitlyEnabled;\n}\n\n// =============================================================================\n// VALIDATION STEP DEFINITION\n// =============================================================================\n\n/**\n * ESLint validation step.\n *\n * Enabled when ESLint validation is enabled in the context.\n */\nexport const eslintStep: ValidationStep = {\n id: STEP_IDS.ESLINT,\n name: STEP_NAMES.ESLINT,\n description: STEP_DESCRIPTIONS.ESLINT,\n enabled: (context: ValidationContext) =>\n context.enabledValidations[VALIDATION_KEYS.ESLINT] === true\n && validationEnabled(VALIDATION_KEYS.ESLINT),\n execute: async (context: ValidationContext): Promise<ValidationStepResult> => {\n const startTime = performance.now();\n try {\n const result = await validateESLint(context);\n return {\n success: result.success,\n error: result.error,\n duration: performance.now() - startTime,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n duration: performance.now() - startTime,\n };\n }\n },\n};\n","/**\n * Shared types for the validation module.\n *\n * These types define the contracts for validation steps and their dependencies,\n * enabling dependency injection for testability (per ADR-001).\n *\n * @module validation/types\n */\n\nimport type { ChildProcess, SpawnOptions } from \"node:child_process\";\n\n// =============================================================================\n// DEPENDENCY INJECTION INTERFACES\n// =============================================================================\n\n/**\n * Interface for subprocess execution.\n *\n * Enables dependency injection for testing - production code uses real spawn,\n * tests can provide controlled implementations.\n *\n * @example\n * ```typescript\n * // Production usage\n * const runner: ProcessRunner = { spawn };\n *\n * // Test usage with controlled implementation\n * const testRunner: ProcessRunner = {\n * spawn: (cmd, args, opts) => createMockProcess({ exitCode: 0 }),\n * };\n * ```\n */\nexport interface ProcessRunner {\n spawn(command: string, args: readonly string[], options?: SpawnOptions): ChildProcess;\n}\n\n// =============================================================================\n// VALIDATION SCOPE\n// =============================================================================\n\n/**\n * Validation scope constants.\n *\n * @example\n * ```typescript\n * import { VALIDATION_SCOPES } from \"./types.js\";\n * const scope = VALIDATION_SCOPES.FULL; // \"full\"\n * ```\n */\nexport const VALIDATION_SCOPES = {\n /** Validate entire codebase including tests and scripts */\n FULL: \"full\",\n /** Validate production files only */\n PRODUCTION: \"production\",\n} as const;\n\n/** Type for validation scope values */\nexport type ValidationScope = (typeof VALIDATION_SCOPES)[keyof typeof VALIDATION_SCOPES];\n\n/**\n * Configuration for validation scope.\n *\n * Derived from tsconfig.json settings to ensure alignment between\n * TypeScript and ESLint validation.\n */\nexport interface ScopeConfig {\n /** Directories to include in validation */\n directories: string[];\n /** File patterns to match (from tsconfig include) */\n filePatterns: string[];\n /** Patterns to exclude from validation */\n excludePatterns: string[];\n}\n\n// =============================================================================\n// EXECUTION MODE\n// =============================================================================\n\n/**\n * Execution mode constants.\n */\nexport const EXECUTION_MODES = {\n /** Read-only mode - report errors without fixing */\n READ: \"read\",\n /** Write mode - fix errors when possible (e.g., eslint --fix) */\n WRITE: \"write\",\n} as const;\n\n/** Type for execution mode values */\nexport type ExecutionMode = (typeof EXECUTION_MODES)[keyof typeof EXECUTION_MODES];\n\n// =============================================================================\n// VALIDATION CONTEXT\n// =============================================================================\n\n/**\n * Context object passed to validation steps.\n *\n * Contains all information needed to execute a validation step,\n * enabling pure functions that don't rely on global state.\n */\nexport interface ValidationContext {\n /** Root directory of the project being validated */\n projectRoot: string;\n /** Execution mode (read-only or write/fix) */\n mode?: ExecutionMode;\n /** Validation scope (full or production) */\n scope: ValidationScope;\n /** Scope configuration derived from tsconfig */\n scopeConfig: ScopeConfig;\n /** Which validations are enabled */\n enabledValidations: Partial<Record<string, boolean>>;\n /** Specific files to validate (if file-specific mode) */\n validatedFiles?: string[];\n /** Whether running in file-specific mode */\n isFileSpecificMode: boolean;\n}\n\n// =============================================================================\n// VALIDATION RESULTS\n// =============================================================================\n\n/**\n * Result from a validation step.\n *\n * All validation steps return this structure to enable consistent\n * result handling and progress reporting.\n */\nexport interface ValidationStepResult {\n /** Whether the validation passed */\n success: boolean;\n /** Error message if validation failed */\n error?: string;\n /** Duration of the validation step in milliseconds */\n duration: number;\n /** Whether the step was skipped (e.g., tool not available) */\n skipped?: boolean;\n}\n\n/**\n * Result from circular dependency validation.\n *\n * Extends ValidationStepResult with circular dependency details.\n */\nexport interface CircularDependencyResult {\n /** Whether no circular dependencies were found */\n success: boolean;\n /** Error message if circular dependencies found */\n error?: string;\n /** The circular dependency cycles found */\n circularDependencies?: string[][];\n}\n\n// =============================================================================\n// VALIDATION STEP INTERFACE\n// =============================================================================\n\n/**\n * Definition of a validation step.\n *\n * Validation steps are pluggable units that can be enabled/disabled\n * and executed in sequence.\n */\nexport interface ValidationStep {\n /** Unique identifier for the step */\n id: string;\n /** Human-readable name for progress reporting */\n name: string;\n /** Description shown during execution */\n description: string;\n /** Function to determine if step should run */\n enabled: (context: ValidationContext) => boolean;\n /** Function to execute the validation */\n execute: (context: ValidationContext) => Promise<ValidationStepResult>;\n}\n","/**\n * Knip validation step.\n *\n * Detects unused exports, dependencies, and files using knip.\n *\n * @module validation/steps/knip\n */\n\nimport { spawn } from \"node:child_process\";\n\nimport type { ProcessRunner, ScopeConfig, ValidationContext, ValidationStep, ValidationStepResult } from \"../types.js\";\n\nimport { STEP_DESCRIPTIONS, STEP_IDS, STEP_NAMES, VALIDATION_DEFAULTS, VALIDATION_KEYS } from \"./constants.js\";\nimport { validationEnabled } from \"./eslint.js\";\n\n// =============================================================================\n// DEFAULT DEPENDENCIES\n// =============================================================================\n\n/**\n * Default production process runner for Knip.\n */\nexport const defaultKnipProcessRunner: ProcessRunner = { spawn };\n\n// =============================================================================\n// VALIDATION FUNCTION\n// =============================================================================\n\n/**\n * Validate unused code using knip with TypeScript-derived scope.\n *\n * @param scope - Validation scope\n * @param typescriptScope - Scope configuration from tsconfig\n * @param runner - Injectable process runner\n * @returns Promise resolving to validation result\n *\n * @example\n * ```typescript\n * const result = await validateKnip(\"full\", scopeConfig);\n * if (!result.success) {\n * console.error(\"Knip found issues:\", result.error);\n * }\n * ```\n */\nexport async function validateKnip(\n typescriptScope: ScopeConfig,\n runner: ProcessRunner = defaultKnipProcessRunner,\n): Promise<{\n success: boolean;\n error?: string;\n}> {\n try {\n // Use TypeScript-derived directories for perfect scope alignment\n const analyzeDirectories = typescriptScope.directories;\n\n if (analyzeDirectories.length === 0) {\n return { success: true };\n }\n\n return new Promise((resolve) => {\n const knipProcess = runner.spawn(\"npx\", [\"knip\"], {\n cwd: process.cwd(),\n stdio: \"pipe\",\n });\n\n let knipOutput = \"\";\n let knipError = \"\";\n\n knipProcess.stdout?.on(\"data\", (data: Buffer) => {\n knipOutput += data.toString();\n });\n\n knipProcess.stderr?.on(\"data\", (data: Buffer) => {\n knipError += data.toString();\n });\n\n knipProcess.on(\"close\", (code) => {\n if (code === 0) {\n resolve({ success: true });\n } else {\n const errorOutput = knipOutput || knipError || \"Unused code detected\";\n resolve({\n success: false,\n error: errorOutput,\n });\n }\n });\n\n knipProcess.on(\"error\", (error) => {\n resolve({ success: false, error: error.message });\n });\n });\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n return { success: false, error: errorMessage };\n }\n}\n\n// =============================================================================\n// VALIDATION STEP DEFINITION\n// =============================================================================\n\n/**\n * Knip unused code validation step.\n *\n * Enabled when knip validation is enabled and not in file-specific mode.\n * Knip is disabled by default.\n */\nexport const knipStep: ValidationStep = {\n id: STEP_IDS.KNIP,\n name: STEP_NAMES.KNIP,\n description: STEP_DESCRIPTIONS.KNIP,\n enabled: (context: ValidationContext) =>\n context.enabledValidations[VALIDATION_KEYS.KNIP] === true\n && validationEnabled(VALIDATION_KEYS.KNIP, VALIDATION_DEFAULTS)\n && !context.isFileSpecificMode,\n execute: async (context: ValidationContext): Promise<ValidationStepResult> => {\n const startTime = performance.now();\n try {\n const result = await validateKnip(context.scopeConfig);\n return {\n success: result.success,\n error: result.error,\n duration: performance.now() - startTime,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n duration: performance.now() - startTime,\n };\n }\n },\n};\n","/**\n * Knip command for detecting unused code.\n *\n * Runs knip to find unused exports, dependencies, and files.\n * Disabled by default - enable with KNIP_VALIDATION_ENABLED=1.\n */\nimport { getTypeScriptScope } from \"../../validation/config/scope.js\";\nimport { discoverTool, formatSkipMessage } from \"../../validation/discovery/index.js\";\nimport { validationEnabled } from \"../../validation/steps/eslint.js\";\nimport { validateKnip } from \"../../validation/steps/knip.js\";\nimport type { KnipCommandOptions, ValidationCommandResult } from \"./types\";\n\n/**\n * Detect unused code with knip.\n *\n * @param options - Command options\n * @returns Command result with exit code and output\n */\nexport async function knipCommand(options: KnipCommandOptions): Promise<ValidationCommandResult> {\n const { cwd, quiet } = options;\n const startTime = Date.now();\n\n // Knip is disabled by default - check if explicitly enabled\n if (!validationEnabled(\"KNIP\", { KNIP: false })) {\n const output = quiet ? \"\" : \"Knip: skipped (disabled by default, set KNIP_VALIDATION_ENABLED=1 to enable)\";\n return { exitCode: 0, output, durationMs: Date.now() - startTime };\n }\n\n // Discover knip\n const toolResult = await discoverTool(\"knip\", { projectRoot: cwd });\n if (!toolResult.found) {\n const skipMessage = formatSkipMessage(\"unused code detection\", toolResult);\n return { exitCode: 0, output: skipMessage, durationMs: Date.now() - startTime };\n }\n\n // Get scope configuration from tsconfig (knip uses full scope)\n const scopeConfig = getTypeScriptScope(\"full\");\n\n // Run knip validation\n const result = await validateKnip(scopeConfig);\n const durationMs = Date.now() - startTime;\n\n // Map result to command output\n if (result.success) {\n const output = quiet ? \"\" : `Knip: ✓ No unused code found`;\n return { exitCode: 0, output, durationMs };\n } else {\n const output = result.error ?? \"Unused code found\";\n return { exitCode: 1, output, durationMs };\n }\n}\n","/**\n * ESLint validation command.\n *\n * Runs ESLint for code quality checks.\n */\nimport { getTypeScriptScope } from \"../../validation/config/scope.js\";\nimport { discoverTool, formatSkipMessage } from \"../../validation/discovery/index.js\";\nimport { validateESLint } from \"../../validation/steps/eslint.js\";\nimport type { ValidationContext } from \"../../validation/types.js\";\nimport type { LintCommandOptions, ValidationCommandResult } from \"./types\";\n\n/**\n * Run ESLint validation.\n *\n * @param options - Command options\n * @returns Command result with exit code and output\n */\nexport async function lintCommand(options: LintCommandOptions): Promise<ValidationCommandResult> {\n const { cwd, scope = \"full\", files, fix, quiet } = options;\n const startTime = Date.now();\n\n // Discover eslint\n const toolResult = await discoverTool(\"eslint\", { projectRoot: cwd });\n if (!toolResult.found) {\n const skipMessage = formatSkipMessage(\"ESLint\", toolResult);\n return { exitCode: 0, output: skipMessage, durationMs: Date.now() - startTime };\n }\n\n // Get scope configuration from tsconfig\n const scopeConfig = getTypeScriptScope(scope);\n\n // Build validation context\n const context: ValidationContext = {\n projectRoot: cwd,\n scope,\n scopeConfig,\n mode: fix ? \"write\" : \"read\",\n enabledValidations: { ESLINT: true },\n validatedFiles: files,\n isFileSpecificMode: Boolean(files && files.length > 0),\n };\n\n // Run ESLint validation\n const result = await validateESLint(context);\n const durationMs = Date.now() - startTime;\n\n // Map result to command output\n if (result.success) {\n const output = quiet ? \"\" : `ESLint: ✓ No issues found`;\n return { exitCode: 0, output, durationMs };\n } else {\n const output = result.error ?? \"ESLint validation failed\";\n return { exitCode: 1, output, durationMs };\n }\n}\n","/**\n * TypeScript validation step.\n *\n * Validates TypeScript code using the tsc compiler.\n *\n * @module validation/steps/typescript\n */\n\nimport { spawn } from \"node:child_process\";\nimport { existsSync, mkdirSync, rmSync, writeFileSync } from \"node:fs\";\nimport { mkdtemp } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { isAbsolute, join } from \"node:path\";\n\nimport { TSCONFIG_FILES } from \"../config/scope.js\";\nimport type {\n ProcessRunner,\n ScopeConfig,\n ValidationContext,\n ValidationScope,\n ValidationStep,\n ValidationStepResult,\n} from \"../types.js\";\nimport { VALIDATION_SCOPES } from \"../types.js\";\n\nimport { STEP_DESCRIPTIONS, STEP_IDS, STEP_NAMES, VALIDATION_KEYS } from \"./constants.js\";\nimport { validationEnabled } from \"./eslint.js\";\n\n// =============================================================================\n// DEFAULT DEPENDENCIES\n// =============================================================================\n\n/**\n * Default production process runner for TypeScript.\n */\nexport const defaultTypeScriptProcessRunner: ProcessRunner = { spawn };\n\n/**\n * Dependencies for file-specific TypeScript validation.\n */\nexport interface TypeScriptDeps {\n mkdtemp: typeof mkdtemp;\n writeFileSync: typeof writeFileSync;\n rmSync: typeof rmSync;\n existsSync: typeof existsSync;\n mkdirSync: typeof mkdirSync;\n}\n\n/**\n * Default production dependencies.\n */\nexport const defaultTypeScriptDeps: TypeScriptDeps = {\n mkdtemp,\n writeFileSync,\n rmSync,\n existsSync,\n mkdirSync,\n};\n\n// =============================================================================\n// PURE ARGUMENT BUILDER\n// =============================================================================\n\n/**\n * Build TypeScript CLI arguments based on validation scope.\n *\n * Pure function for testability - can be verified at Level 1.\n *\n * @param context - Context for building arguments\n * @returns Array of tsc CLI arguments\n *\n * @example\n * ```typescript\n * const args = buildTypeScriptArgs({ scope: \"full\", configFile: \"tsconfig.json\" });\n * // Returns: [\"tsc\", \"--noEmit\"]\n * ```\n */\nexport function buildTypeScriptArgs(context: { scope: ValidationScope; configFile: string }): string[] {\n const { scope, configFile } = context;\n return scope === VALIDATION_SCOPES.FULL ? [\"tsc\", \"--noEmit\"] : [\"tsc\", \"--project\", configFile];\n}\n\n// =============================================================================\n// FILE-SPECIFIC VALIDATION SUPPORT\n// =============================================================================\n\n/**\n * Create a temporary TypeScript configuration file for file-specific validation.\n *\n * @param scope - Validation scope\n * @param files - Files to validate\n * @param deps - Injectable dependencies\n * @returns Config path and cleanup function\n */\nexport async function createFileSpecificTsconfig(\n scope: ValidationScope,\n files: string[],\n deps: TypeScriptDeps = defaultTypeScriptDeps,\n): Promise<{ configPath: string; tempDir: string; cleanup: () => void }> {\n // Create temporary directory\n const tempDir = await deps.mkdtemp(join(tmpdir(), \"validate-ts-\"));\n const configPath = join(tempDir, \"tsconfig.json\");\n\n // Get base config file\n const baseConfigFile = TSCONFIG_FILES[scope];\n\n // Ensure all file paths are absolute\n const projectRoot = process.cwd();\n const absoluteFiles = files.map((file) => (isAbsolute(file) ? file : join(projectRoot, file)));\n\n // Create temporary tsconfig that extends the base config\n const tempConfig = {\n extends: join(projectRoot, baseConfigFile),\n files: absoluteFiles,\n include: [],\n exclude: [],\n compilerOptions: {\n noEmit: true,\n typeRoots: [join(projectRoot, \"node_modules\", \"@types\")],\n types: [\"node\"],\n },\n };\n\n // Write temporary config\n deps.writeFileSync(configPath, JSON.stringify(tempConfig, null, 2));\n\n // Return config path and cleanup function\n const cleanup = () => {\n try {\n deps.rmSync(tempDir, { recursive: true, force: true });\n } catch {\n // Cleanup error - don't fail validation\n }\n };\n\n return { configPath, tempDir, cleanup };\n}\n\n// =============================================================================\n// VALIDATION FUNCTION\n// =============================================================================\n\n/**\n * Validate TypeScript using authoritative configuration.\n *\n * @param scope - Validation scope\n * @param typescriptScope - Scope configuration from tsconfig\n * @param files - Optional specific files to validate\n * @param runner - Injectable process runner\n * @param deps - Injectable TypeScript dependencies\n * @returns Promise resolving to validation result\n *\n * @example\n * ```typescript\n * const result = await validateTypeScript(\"full\", scopeConfig);\n * if (!result.success) {\n * console.error(\"TypeScript failed:\", result.error);\n * }\n * ```\n */\nexport async function validateTypeScript(\n scope: ValidationScope,\n typescriptScope: ScopeConfig,\n files?: string[],\n runner: ProcessRunner = defaultTypeScriptProcessRunner,\n deps: TypeScriptDeps = defaultTypeScriptDeps,\n): Promise<{\n success: boolean;\n error?: string;\n skipped?: boolean;\n}> {\n const configFile = TSCONFIG_FILES[scope];\n\n // Determine tool and arguments based on whether specific files are provided\n let tool: string;\n let tscArgs: string[];\n\n if (files && files.length > 0) {\n // File-specific validation using custom temporary tsconfig\n const { configPath, cleanup } = await createFileSpecificTsconfig(scope, files, deps);\n\n try {\n return await new Promise((resolve) => {\n const tscProcess = runner.spawn(\"npx\", [\"tsc\", \"--project\", configPath], {\n cwd: process.cwd(),\n stdio: \"inherit\",\n });\n\n tscProcess.on(\"close\", (code) => {\n cleanup();\n if (code === 0) {\n resolve({ success: true, skipped: false });\n } else {\n resolve({ success: false, error: `TypeScript exited with code ${code}` });\n }\n });\n\n tscProcess.on(\"error\", (error) => {\n cleanup();\n resolve({ success: false, error: error.message });\n });\n });\n } catch (error) {\n cleanup();\n const errorMessage = error instanceof Error ? error.message : String(error);\n return { success: false, error: `Failed to create temporary config: ${errorMessage}` };\n }\n } else {\n // Full validation using tsc\n tool = \"npx\";\n tscArgs = buildTypeScriptArgs({ scope, configFile });\n }\n\n return new Promise((resolve) => {\n const tscProcess = runner.spawn(tool, tscArgs, {\n cwd: process.cwd(),\n stdio: \"inherit\",\n });\n\n tscProcess.on(\"close\", (code) => {\n if (code === 0) {\n resolve({ success: true, skipped: false });\n } else {\n resolve({ success: false, error: `TypeScript exited with code ${code}` });\n }\n });\n\n tscProcess.on(\"error\", (error) => {\n resolve({ success: false, error: error.message });\n });\n });\n}\n\n// =============================================================================\n// VALIDATION STEP DEFINITION\n// =============================================================================\n\n/**\n * TypeScript validation step.\n *\n * Enabled when TypeScript validation is enabled in the context.\n */\nexport const typescriptStep: ValidationStep = {\n id: STEP_IDS.TYPESCRIPT,\n name: STEP_NAMES.TYPESCRIPT,\n description: STEP_DESCRIPTIONS.TYPESCRIPT,\n enabled: (context: ValidationContext) =>\n context.enabledValidations[VALIDATION_KEYS.TYPESCRIPT] === true\n && validationEnabled(VALIDATION_KEYS.TYPESCRIPT),\n execute: async (context: ValidationContext): Promise<ValidationStepResult> => {\n const startTime = performance.now();\n try {\n const result = await validateTypeScript(\n context.scope,\n context.scopeConfig,\n context.validatedFiles,\n );\n return {\n success: result.success,\n error: result.error,\n duration: performance.now() - startTime,\n skipped: result.skipped,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n duration: performance.now() - startTime,\n };\n }\n },\n};\n","/**\n * TypeScript validation command.\n *\n * Runs TypeScript type checking using tsc.\n */\nimport { getTypeScriptScope } from \"../../validation/config/scope.js\";\nimport { discoverTool, formatSkipMessage } from \"../../validation/discovery/index.js\";\nimport { validateTypeScript } from \"../../validation/steps/typescript.js\";\nimport type { TypeScriptCommandOptions, ValidationCommandResult } from \"./types\";\n\n/**\n * Run TypeScript type checking.\n *\n * @param options - Command options\n * @returns Command result with exit code and output\n */\nexport async function typescriptCommand(options: TypeScriptCommandOptions): Promise<ValidationCommandResult> {\n const { cwd, scope = \"full\", files, quiet } = options;\n const startTime = Date.now();\n\n // Discover tsc (provided by typescript package)\n const toolResult = await discoverTool(\"typescript\", { projectRoot: cwd });\n if (!toolResult.found) {\n const skipMessage = formatSkipMessage(\"TypeScript\", toolResult);\n return { exitCode: 0, output: skipMessage, durationMs: Date.now() - startTime };\n }\n\n // Get scope configuration from tsconfig\n const scopeConfig = getTypeScriptScope(scope);\n\n // Run TypeScript validation\n const result = await validateTypeScript(scope, scopeConfig, files);\n const durationMs = Date.now() - startTime;\n\n // Map result to command output\n if (result.success) {\n const output = quiet ? \"\" : `TypeScript: ✓ No type errors`;\n return { exitCode: 0, output, durationMs };\n } else {\n const output = result.error ?? \"TypeScript validation failed\";\n return { exitCode: 1, output, durationMs };\n }\n}\n","/**\n * Run all validations command.\n *\n * Executes all validation steps in sequence:\n * 1. Circular dependencies (fastest)\n * 2. Knip (optional)\n * 3. ESLint\n * 4. TypeScript (slowest)\n */\nimport { circularCommand } from \"./circular\";\nimport { formatDuration, formatSummary } from \"./format\";\nimport { knipCommand } from \"./knip\";\nimport { lintCommand } from \"./lint\";\nimport type { AllCommandOptions, ValidationCommandResult } from \"./types\";\nimport { typescriptCommand } from \"./typescript\";\n\n/** Total number of validation steps */\nconst TOTAL_STEPS = 4;\n\n/**\n * Format step output with step number and timing.\n *\n * @param stepNumber - Current step number (1-indexed)\n * @param result - Validation result\n * @param quiet - Whether to suppress output\n * @returns Formatted output string\n */\nfunction formatStepWithTiming(\n stepNumber: number,\n result: ValidationCommandResult,\n quiet: boolean,\n): string {\n if (quiet || !result.output) return \"\";\n\n const timing = result.durationMs === undefined ? \"\" : ` (${formatDuration(result.durationMs)})`;\n return `[${stepNumber}/${TOTAL_STEPS}] ${result.output}${timing}`;\n}\n\n/**\n * Run all validation steps.\n *\n * @param options - Command options\n * @returns Command result with exit code and output\n */\nexport async function allCommand(options: AllCommandOptions): Promise<ValidationCommandResult> {\n const { cwd, scope, files, fix, quiet = false, json } = options;\n const startTime = Date.now();\n const outputs: string[] = [];\n let hasFailure = false;\n\n // 1. Circular dependencies\n const circularResult = await circularCommand({ cwd, quiet, json });\n const circularOutput = formatStepWithTiming(1, circularResult, quiet);\n if (circularOutput) outputs.push(circularOutput);\n if (circularResult.exitCode !== 0) hasFailure = true;\n\n // 2. Knip (optional - skip on failure, it's informational)\n const knipResult = await knipCommand({ cwd, quiet, json });\n const knipOutput = formatStepWithTiming(2, knipResult, quiet);\n if (knipOutput) outputs.push(knipOutput);\n // Don't fail on knip - it's optional\n\n // 3. ESLint\n const lintResult = await lintCommand({ cwd, scope, files, fix, quiet, json });\n const lintOutput = formatStepWithTiming(3, lintResult, quiet);\n if (lintOutput) outputs.push(lintOutput);\n if (lintResult.exitCode !== 0) hasFailure = true;\n\n // 4. TypeScript\n const tsResult = await typescriptCommand({ cwd, scope, files, quiet, json });\n const tsOutput = formatStepWithTiming(4, tsResult, quiet);\n if (tsOutput) outputs.push(tsOutput);\n if (tsResult.exitCode !== 0) hasFailure = true;\n\n // Calculate total duration\n const totalDurationMs = Date.now() - startTime;\n\n // Add summary line\n if (!quiet) {\n const summary = formatSummary({ success: !hasFailure, totalDurationMs });\n outputs.push(\"\", summary); // Empty line before summary\n }\n\n return {\n exitCode: hasFailure ? 1 : 0,\n output: outputs.join(\"\\n\"),\n durationMs: totalDurationMs,\n };\n}\n","/**\n * Validation domain - Run code validation tools\n *\n * Provides CLI commands for running validation tools (TypeScript, ESLint, etc.)\n * as a globally-installed tool across TypeScript projects.\n */\nimport type { Command } from \"commander\";\n\nimport { allCommand, circularCommand, knipCommand, lintCommand, typescriptCommand } from \"../../commands/validation\";\nimport type { Domain } from \"../types\";\n\n/** Validation scope options */\ntype ValidationScope = \"full\" | \"production\";\n\n/** Common options for all validation commands */\ninterface CommonOptions {\n scope?: ValidationScope;\n files?: string[];\n quiet?: boolean;\n json?: boolean;\n}\n\n/** Options for lint command */\ninterface LintOptions extends CommonOptions {\n fix?: boolean;\n}\n\n/**\n * Add common options to a command\n */\nfunction addCommonOptions(cmd: Command): Command {\n return cmd\n .option(\"--scope <scope>\", \"Validation scope (full|production)\", \"full\")\n .option(\"--files <paths...>\", \"Specific files/directories to validate\")\n .option(\"--quiet\", \"Suppress progress output\")\n .option(\"--json\", \"Output results as JSON\");\n}\n\n/**\n * Register validation domain commands\n */\nfunction registerValidationCommands(validationCmd: Command): void {\n // typescript command\n const tsCmd = validationCmd\n .command(\"typescript\")\n .alias(\"ts\")\n .description(\"Run TypeScript type checking\")\n .action(async (options: CommonOptions) => {\n const result = await typescriptCommand({\n cwd: process.cwd(),\n scope: options.scope,\n files: options.files,\n quiet: options.quiet,\n json: options.json,\n });\n if (result.output) console.log(result.output);\n process.exit(result.exitCode);\n });\n addCommonOptions(tsCmd);\n\n // lint command\n const lintCmd = validationCmd\n .command(\"lint\")\n .description(\"Run ESLint\")\n .option(\"--fix\", \"Auto-fix issues\")\n .action(async (options: LintOptions) => {\n const result = await lintCommand({\n cwd: process.cwd(),\n scope: options.scope,\n files: options.files,\n fix: options.fix,\n quiet: options.quiet,\n json: options.json,\n });\n if (result.output) console.log(result.output);\n process.exit(result.exitCode);\n });\n addCommonOptions(lintCmd);\n\n // circular command\n const circularCmd = validationCmd\n .command(\"circular\")\n .description(\"Check for circular dependencies\")\n .action(async (options: CommonOptions) => {\n const result = await circularCommand({\n cwd: process.cwd(),\n quiet: options.quiet,\n json: options.json,\n });\n if (result.output) console.log(result.output);\n process.exit(result.exitCode);\n });\n addCommonOptions(circularCmd);\n\n // knip command\n const knipCmd = validationCmd\n .command(\"knip\")\n .description(\"Detect unused code\")\n .action(async (options: CommonOptions) => {\n const result = await knipCommand({\n cwd: process.cwd(),\n quiet: options.quiet,\n json: options.json,\n });\n if (result.output) console.log(result.output);\n process.exit(result.exitCode);\n });\n addCommonOptions(knipCmd);\n\n // all command (default)\n const allCmd = validationCmd\n .command(\"all\", { isDefault: true })\n .description(\"Run all validations\")\n .option(\"--fix\", \"Auto-fix ESLint issues\")\n .action(async (options: LintOptions) => {\n const result = await allCommand({\n cwd: process.cwd(),\n scope: options.scope,\n files: options.files,\n fix: options.fix,\n quiet: options.quiet,\n json: options.json,\n });\n if (result.output) console.log(result.output);\n process.exit(result.exitCode);\n });\n addCommonOptions(allCmd);\n}\n\n/**\n * Validation domain - Run code validation tools\n */\nexport const validationDomain: Domain = {\n name: \"validation\",\n description: \"Run code validation tools\",\n register: (program: Command) => {\n const validationCmd = program\n .command(\"validation\")\n .alias(\"v\")\n .description(\"Run code validation tools\");\n\n registerValidationCommands(validationCmd);\n },\n};\n"],"mappings":"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G,IAAI,GAAG,IAAI;AAC5C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,GAAG,IAAI;AAC5C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,GAAG,IAAI;AAC5C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,GAAG,IAAI;AAC5C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,GAAG,IAAI;AAC5C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,GAAG,IAAI;AAC5C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,GAAG,IAAI;AAC5C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,GAAG,IAAI;AAC5C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,GAAG,IAAI;AAC5C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,GAAG,IAAI;AAC5C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,GAAG,IAAI;AAC5C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,GAAG,IAAI;AAC5C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,GAAG,IAAI;AAC5C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,EAAE,IAAI;AAC3C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,EAAE,IAAI;AAC3C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,EAAE,IAAI;AAC3C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,EAAE,IAAI;AAC3C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,EAAE,IAAI;AAC3C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,EAAE,IAAI;AAC3C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,EAAE,IAAI;AAC3C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,EAAE,IAAI;AAC3C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,EAAE,IAAI;AAC3C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,EAAE,IAAI;AAC3C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,EAAE,IAAI;AAC3C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,EAAE,IAAI;AAC3C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,EAAE,IAAI;AAC3C,EAAAA,gBAAeA,gBAAe,GAAG,IAAI,EAAE,IAAI;AAC3C,EAAAA,g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","names":["createRequire","os","path","fs","fs","path","path","fs","fs","fs","path","path","os","program","join","join","stat","join","path","path","stat","join","mkdir","join","execa","join","statusDirs","join","mkdir","join","join","mkdir","readdir","readFile","rename","join","readdir","join","readFile","mkdir","rename","readdir","readFile","unlink","join","readdir","join","readFile","lines","unlink","readdir","rename","join","readdir","join","rename","readFile","stat","join","stat","join","readFile","resolve","program","path","path","readdir","stat","path","path","readdir","stat","getDisplayNumber","getDisplayNumber","formatNode","formatWorkItemName","format","format","program","value","CharacterCodes","ParseOptions","handleError","ScanError","SyntaxKind","parse","ParseErrorCode","join","parse","fs","path","require","fs","path","resolve","spawn","spawn","resolve","spawn","existsSync","join","spawn","existsSync","join","resolve","program","require","createRequire"]}
|