mason-context 0.3.0 → 0.3.2

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.
@@ -1269,7 +1269,7 @@ function createMcpServer() {
1269
1269
  const server = new McpServer(
1270
1270
  {
1271
1271
  name: "mason",
1272
- version: "0.3.0"
1272
+ version: "0.3.2"
1273
1273
  },
1274
1274
  {
1275
1275
  instructions: "Mason is a context engineering tool. Always call get_snapshot before using Explore agents, Glob, or Grep to understand the codebase. The snapshot is a concept map that maps features and flows to their implementing files \u2014 it eliminates the need to search. This applies to ANY question about architecture, features, flows, how things work, cross-feature interactions, or bug investigation. Workflow: 1) Call get_snapshot first. 2) If no snapshot, call full_analysis and then save_snapshot to create one. 3) If the snapshot is stale, tell the user and offer to update it. 4) Use your native file reading tool to read files the snapshot points to. 5) Before modifying a file, call get_impact to check what else might be affected. 6) After making significant changes (new features, refactors, architecture changes), call save_snapshot to update the concept map."
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/test-map.ts","../../src/impact/impact.ts","../../src/mcp/server.ts","../../src/mcp/tools.ts","../../src/analyzers/git-history.ts","../../src/analyzers/base.ts","../../src/analyzers/index.ts","../../src/utils/git.ts","../../src/mcp/sampler.ts","../../src/snapshot/snapshot.ts","../../src/llm/providers.ts","../../src/llm/config.ts","../../bin/mason-mcp.ts"],"sourcesContent":["import path from \"node:path\";\nimport fg from \"fast-glob\";\n\nconst IGNORE = [\n \"**/node_modules/**\",\n \"**/dist/**\",\n \"**/build/**\",\n \"**/.gradle/**\",\n \"**/target/**\",\n \"**/.git/**\",\n \"**/vendor/**\",\n \"**/__pycache__/**\",\n \"**/venv/**\",\n \"**/.venv/**\",\n \"**/*.min.*\",\n \"**/*.map\",\n];\n\nexport interface TestPair {\n test: string;\n source: string;\n confidence: string;\n}\n\nexport interface TestMapResult {\n totalTestFiles: number;\n paired: TestPair[];\n unmatched: string[];\n}\n\nexport async function buildTestMap(dir: string): Promise<TestMapResult> {\n const rootDir = path.resolve(dir);\n\n // Find all test files\n const testPatterns = [\n \"**/*.test.*\", \"**/*.spec.*\",\n \"**/*Test.kt\", \"**/*Test.java\", \"**/*Tests.kt\", \"**/*Tests.java\",\n \"**/test_*.py\", \"**/*_test.py\",\n \"**/*_test.go\",\n \"**/*Tests.swift\", \"**/*Test.swift\",\n \"**/*_test.rs\",\n ];\n const testFiles = await fg(testPatterns, { cwd: rootDir, ignore: IGNORE });\n\n // Find all source files\n const sourceFiles = await fg(\n \"**/*.{ts,tsx,js,jsx,kt,kts,java,py,go,rs,swift,rb,cs,cpp,dart}\",\n { cwd: rootDir, ignore: IGNORE }\n );\n\n // Build source file index by base name (without extension)\n const sourceByBaseName = new Map<string, string[]>();\n for (const file of sourceFiles) {\n if (testFiles.includes(file)) continue; // Skip test files\n const baseName = path.basename(file).replace(/\\.[^.]+$/, \"\");\n const existing = sourceByBaseName.get(baseName) ?? [];\n existing.push(file);\n sourceByBaseName.set(baseName, existing);\n }\n\n // Match test files to source files by name\n const paired: TestPair[] = [];\n const unmatched: string[] = [];\n\n for (const testFile of testFiles) {\n const testBaseName = path.basename(testFile).replace(/\\.[^.]+$/, \"\");\n\n // Strip test suffixes/prefixes to get the source name\n const sourceName = testBaseName\n .replace(/Test$|Tests$|Spec$|\\.test$|\\.spec$/, \"\")\n .replace(/^test_|_test$/, \"\");\n\n if (!sourceName) {\n unmatched.push(testFile);\n continue;\n }\n\n const candidates = sourceByBaseName.get(sourceName);\n if (candidates && candidates.length > 0) {\n // If multiple candidates, prefer one in a similar directory path\n const testDir = path.dirname(testFile);\n const bestMatch = candidates.reduce((best, candidate) => {\n const candidateDir = path.dirname(candidate);\n const bestDir = path.dirname(best);\n const candidateOverlap = commonSegments(testDir, candidateDir);\n const bestOverlap = commonSegments(testDir, bestDir);\n return candidateOverlap > bestOverlap ? candidate : best;\n });\n\n paired.push({\n test: testFile,\n source: bestMatch,\n confidence: candidates.length === 1 ? \"exact\" : \"best-guess\",\n });\n } else {\n unmatched.push(testFile);\n }\n }\n\n return { totalTestFiles: testFiles.length, paired, unmatched };\n}\n\nfunction commonSegments(pathA: string, pathB: string): number {\n const segsA = pathA.split(\"/\");\n const segsB = pathB.split(\"/\");\n let count = 0;\n for (let i = 0; i < Math.min(segsA.length, segsB.length); i++) {\n if (segsA[i] === segsB[i]) count++;\n else break;\n }\n return count;\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport fg from \"fast-glob\";\n\nconst exec = promisify(execFile);\n\nconst IGNORE = [\n \"**/node_modules/**\",\n \"**/dist/**\",\n \"**/build/**\",\n \"**/.gradle/**\",\n \"**/target/**\",\n \"**/.git/**\",\n \"**/vendor/**\",\n \"**/__pycache__/**\",\n \"**/venv/**\",\n \"**/.venv/**\",\n \"**/generated/**\",\n];\n\nconst SOURCE_EXTENSIONS =\n \"*.{ts,tsx,js,jsx,kt,kts,java,py,go,rs,swift,rb,cs,cpp,c,h,dart,gradle.kts,gradle}\";\n\nexport interface CochangeEntry {\n file: string;\n cochangeRate: number;\n sharedCommits: number;\n}\n\nexport interface ReferenceEntry {\n file: string;\n matches: string[];\n}\n\nexport interface TestEntry {\n file: string;\n confidence: \"exact\" | \"best-guess\";\n}\n\nexport interface ImpactResult {\n targetFiles: string[];\n cochange: CochangeEntry[];\n references: ReferenceEntry[];\n tests: TestEntry[];\n}\n\nexport async function analyzeImpact(\n rootDir: string,\n targetFiles: string[]\n): Promise<ImpactResult> {\n const resolvedRoot = path.resolve(rootDir);\n\n // Resolve target files to full relative paths if only basename given\n const resolvedTargets = await resolveTargetFiles(resolvedRoot, targetFiles);\n\n const [cochange, references, tests] = await Promise.all([\n getCochangeFiles(resolvedRoot, resolvedTargets),\n getReferences(resolvedRoot, resolvedTargets),\n getRelatedTests(resolvedRoot, resolvedTargets),\n ]);\n\n return {\n targetFiles: resolvedTargets,\n cochange,\n references,\n tests,\n };\n}\n\nasync function resolveTargetFiles(\n rootDir: string,\n targets: string[]\n): Promise<string[]> {\n const resolved: string[] = [];\n\n for (const target of targets) {\n // If it contains a path separator, use as-is\n if (target.includes(\"/\")) {\n resolved.push(target);\n continue;\n }\n\n // Otherwise, search for the filename\n const matches = await fg(`**/${target}`, {\n cwd: rootDir,\n ignore: IGNORE,\n });\n\n if (matches.length > 0) {\n resolved.push(matches[0]);\n } else {\n // Try without extension\n const noExt = target.replace(/\\.[^.]+$/, \"\");\n const extMatches = await fg(`**/${noExt}.*`, {\n cwd: rootDir,\n ignore: IGNORE,\n });\n if (extMatches.length > 0) {\n resolved.push(extMatches[0]);\n } else {\n resolved.push(target); // Keep as-is, might still work for grep\n }\n }\n }\n\n return resolved;\n}\n\nasync function getCochangeFiles(\n rootDir: string,\n targetFiles: string[]\n): Promise<CochangeEntry[]> {\n const cochangeCounts = new Map<string, number>();\n let totalTargetCommits = 0;\n\n for (const targetFile of targetFiles) {\n try {\n // Get commits that touched this file (cap at 500)\n const { stdout: commitLog } = await exec(\n \"git\",\n [\"log\", \"--format=%H\", \"-n\", \"500\", \"--\", targetFile],\n { cwd: rootDir, maxBuffer: 5_000_000 }\n );\n\n const commits = commitLog.trim().split(\"\\n\").filter(Boolean);\n totalTargetCommits += commits.length;\n\n if (commits.length === 0) continue;\n\n // For each commit, get the other files that changed\n for (const commit of commits) {\n try {\n const { stdout: filesInCommit } = await exec(\n \"git\",\n [\"diff-tree\", \"--no-commit-id\", \"--name-only\", \"-r\", commit],\n { cwd: rootDir }\n );\n\n const files = filesInCommit.trim().split(\"\\n\").filter(Boolean);\n for (const file of files) {\n if (targetFiles.includes(file)) continue; // Skip the target itself\n cochangeCounts.set(file, (cochangeCounts.get(file) ?? 0) + 1);\n }\n } catch {\n // Skip this commit\n }\n }\n } catch {\n // No git or file not tracked\n }\n }\n\n if (totalTargetCommits === 0) return [];\n\n // Filter to files that co-change >30% of the time, sort by rate\n return [...cochangeCounts.entries()]\n .map(([file, count]) => ({\n file,\n cochangeRate: Math.round((count / totalTargetCommits) * 100) / 100,\n sharedCommits: count,\n }))\n .filter((e) => e.cochangeRate >= 0.3 || e.sharedCommits >= 3)\n .sort((a, b) => b.cochangeRate - a.cochangeRate)\n .slice(0, 20);\n}\n\nasync function getReferences(\n rootDir: string,\n targetFiles: string[]\n): Promise<ReferenceEntry[]> {\n // Extract searchable names from target files\n const searchNames = new Set<string>();\n for (const target of targetFiles) {\n const basename = path.basename(target).replace(/\\.[^.]+$/, \"\");\n searchNames.add(basename);\n }\n\n const allSourceFiles = await fg(`**/${SOURCE_EXTENSIONS}`, {\n cwd: rootDir,\n ignore: IGNORE,\n });\n\n // Exclude target files from search\n const targetSet = new Set(targetFiles);\n const filesToSearch = allSourceFiles.filter((f) => !targetSet.has(f));\n\n const results = new Map<string, Set<string>>();\n\n // Read files in batches to avoid too many open handles\n const batchSize = 50;\n for (let i = 0; i < filesToSearch.length; i += batchSize) {\n const batch = filesToSearch.slice(i, i + batchSize);\n\n await Promise.all(\n batch.map(async (file) => {\n try {\n const content = await fs.readFile(\n path.join(rootDir, file),\n \"utf-8\"\n );\n\n for (const name of searchNames) {\n // Match the name as a word boundary (not part of another word)\n const regex = new RegExp(`\\\\b${escapeRegex(name)}\\\\b`);\n if (regex.test(content)) {\n if (!results.has(file)) results.set(file, new Set());\n results.get(file)!.add(name);\n }\n }\n } catch {\n // Skip unreadable files\n }\n })\n );\n }\n\n return [...results.entries()]\n .map(([file, matches]) => ({\n file,\n matches: [...matches],\n }))\n .sort((a, b) => b.matches.length - a.matches.length);\n}\n\nasync function getRelatedTests(\n rootDir: string,\n targetFiles: string[]\n): Promise<TestEntry[]> {\n const testPatterns = [\n \"**/*.test.*\",\n \"**/*.spec.*\",\n \"**/*Test.kt\",\n \"**/*Test.java\",\n \"**/*Tests.kt\",\n \"**/*Tests.java\",\n \"**/test_*.py\",\n \"**/*_test.py\",\n \"**/*_test.go\",\n \"**/*Tests.swift\",\n \"**/*Test.swift\",\n \"**/*_test.rs\",\n ];\n\n const testFiles = await fg(testPatterns, { cwd: rootDir, ignore: IGNORE });\n const results: TestEntry[] = [];\n\n for (const target of targetFiles) {\n const targetBaseName = path\n .basename(target)\n .replace(/\\.[^.]+$/, \"\");\n\n for (const testFile of testFiles) {\n const testBaseName = path\n .basename(testFile)\n .replace(/\\.[^.]+$/, \"\");\n\n // Strip test suffixes to get the source name\n const sourceName = testBaseName\n .replace(/Test$|Tests$|Spec$|\\.test$|\\.spec$/, \"\")\n .replace(/^test_|_test$/, \"\");\n\n if (sourceName === targetBaseName) {\n results.push({\n file: testFile,\n confidence: \"exact\",\n });\n }\n }\n }\n\n return results;\n}\n\nfunction escapeRegex(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { z } from \"zod\";\nimport {\n analyzeProject,\n fullAnalysis,\n getCodeSamples,\n getImpact,\n getSnapshot,\n saveSnapshotData,\n} from \"./tools.js\";\n\ndeclare const PKG_VERSION: string;\n\nexport function createMcpServer(): McpServer {\n const server = new McpServer(\n {\n name: \"mason\",\n version: PKG_VERSION,\n },\n {\n instructions:\n \"Mason is a context engineering tool. Always call get_snapshot before using Explore agents, Glob, or Grep to understand the codebase. The snapshot is a concept map that maps features and flows to their implementing files — it eliminates the need to search. This applies to ANY question about architecture, features, flows, how things work, cross-feature interactions, or bug investigation. Workflow: 1) Call get_snapshot first. 2) If no snapshot, call full_analysis and then save_snapshot to create one. 3) If the snapshot is stale, tell the user and offer to update it. 4) Use your native file reading tool to read files the snapshot points to. 5) Before modifying a file, call get_impact to check what else might be affected. 6) After making significant changes (new features, refactors, architecture changes), call save_snapshot to update the concept map.\",\n }\n );\n\n server.tool(\n \"full_analysis\",\n \"Run a complete project analysis in one call. Returns git history stats, project structure with file counts, curated code sample previews (~60 lines each), and test-to-source file mapping. This is the recommended starting point — call this first, then read specific files natively for full content.\",\n {\n dir: z\n .string()\n .describe(\"Absolute path to the project root directory\"),\n },\n async ({ dir }) => {\n const result = await fullAnalysis(dir);\n return {\n content: [{ type: \"text\", text: result }],\n };\n }\n );\n\n server.tool(\n \"analyze_project\",\n \"Run git history analysis on a codebase. Returns commit convention patterns, stale directories, and frequently changed files. These are aggregate stats across hundreds of commits that would be expensive to compute manually.\",\n {\n dir: z\n .string()\n .describe(\"Absolute path to the project root directory\"),\n },\n async ({ dir }) => {\n const result = await analyzeProject(dir);\n return {\n content: [{ type: \"text\", text: result }],\n };\n }\n );\n\n server.tool(\n \"get_code_samples\",\n \"Get previews (first ~60 lines) of representative source files from the codebase. Includes entry points, config files, hot files (frequently changed), test examples, and one file per directory for breadth. Read files natively for full content.\",\n {\n dir: z\n .string()\n .describe(\"Absolute path to the project root directory\"),\n count: z\n .number()\n .optional()\n .default(15)\n .describe(\"Maximum number of files to sample (default: 15)\"),\n },\n async ({ dir, count }) => {\n const result = await getCodeSamples(dir, count);\n return {\n content: [{ type: \"text\", text: result }],\n };\n }\n );\n\n server.tool(\n \"get_snapshot\",\n \"Get the project's concept map — a lookup table from features and flows to the files that implement them. Use this to jump straight to relevant files instead of exploring. Example: 'home screen' → [HomeScreen.kt, HomeViewModel.kt, HomeModule.kt]. If stale, run 'mason snapshot-update' to refresh.\",\n {\n dir: z\n .string()\n .describe(\"Absolute path to the project root directory\"),\n },\n async ({ dir }) => {\n const result = await getSnapshot(dir);\n return {\n content: [{ type: \"text\", text: result }],\n };\n }\n );\n\n server.tool(\n \"save_snapshot\",\n \"Save a concept-to-files map as a persistent project snapshot. Maps feature names and data flows to the files that implement them. Persists across conversations — future sessions can call get_snapshot to instantly find relevant files. No API key needed — you are the LLM generating the map.\",\n {\n dir: z\n .string()\n .describe(\"Absolute path to the project root directory\"),\n features: z\n .record(\n z.object({\n description: z.string().describe(\"One-line description of the feature\"),\n files: z.array(z.string()).describe(\"File paths that implement this feature\"),\n tests: z.array(z.string()).optional().describe(\"Test file paths for this feature\"),\n })\n )\n .describe(\"Map of feature names to their implementing files\"),\n flows: z\n .record(\n z.object({\n description: z.string().describe(\"One-line description of the flow\"),\n chain: z.array(z.string()).describe(\"Ordered list of file paths showing data/call flow\"),\n })\n )\n .describe(\"Map of flow names to ordered file chains\"),\n },\n async ({ dir, features, flows }) => {\n const result = await saveSnapshotData(dir, features, flows);\n return {\n content: [{ type: \"text\", text: result }],\n };\n }\n );\n\n server.tool(\n \"get_impact\",\n \"Analyze the impact of changing specific files. Returns three signals: git co-change (files that historically change together), references (files that mention the target by name), and related tests. Use this before editing a file to understand what else might need updating.\",\n {\n dir: z\n .string()\n .describe(\"Absolute path to the project root directory\"),\n files: z\n .array(z.string())\n .describe(\"File paths or names to analyze (e.g., ['WeatherRepository.kt'] or ['src/services/auth.ts'])\"),\n },\n async ({ dir, files }) => {\n const result = await getImpact(dir, files);\n return {\n content: [{ type: \"text\", text: result }],\n };\n }\n );\n\n return server;\n}\n\nexport async function startMcpServer(): Promise<void> {\n const server = createMcpServer();\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport fg from \"fast-glob\";\n\nconst exec = promisify(execFile);\nimport { runAll } from \"../analyzers/index.js\";\nimport { isGitRepo } from \"../utils/git.js\";\nimport { sampleFiles } from \"./sampler.js\";\nimport {\n loadSnapshot,\n saveSnapshot,\n getCurrentGitHash,\n} from \"../snapshot/snapshot.js\";\nimport type { Snapshot } from \"../snapshot/snapshot.js\";\nimport type { AnalyzerContext } from \"../types.js\";\n\nconst IGNORE = [\n \"**/node_modules/**\",\n \"**/dist/**\",\n \"**/build/**\",\n \"**/.gradle/**\",\n \"**/target/**\",\n \"**/.git/**\",\n \"**/vendor/**\",\n \"**/__pycache__/**\",\n \"**/venv/**\",\n \"**/.venv/**\",\n \"**/*.min.*\",\n \"**/*.map\",\n];\n\nasync function buildContext(dir: string): Promise<AnalyzerContext> {\n return {\n rootDir: dir,\n gitAvailable: await isGitRepo(dir),\n };\n}\n\nexport async function analyzeProject(dir: string): Promise<string> {\n const rootDir = path.resolve(dir);\n const context = await buildContext(rootDir);\n const results = await runAll(context);\n\n // Lightweight project snapshot — pure file existence checks, no parsing\n const projectSnapshot = await detectProjectSnapshot(rootDir);\n\n const output = {\n project: projectSnapshot,\n analyzers: results.map((r) => ({\n name: r.analyzer,\n durationMs: r.durationMs,\n findings: r.findings.map((f) => ({\n category: f.category,\n confidence: f.confidence,\n summary: f.summary,\n evidence: f.evidence,\n suggestedRule: f.ruleCandidate,\n })),\n gaps: r.gaps.map((g) => ({\n question: g.question,\n context: g.context,\n })),\n })),\n };\n\n return JSON.stringify(output, null, 2);\n}\n\nasync function detectProjectSnapshot(rootDir: string): Promise<Record<string, unknown>> {\n // Build config files present (what exists, not what's in them)\n const buildFiles = [\n \"package.json\", \"tsconfig.json\",\n \"build.gradle.kts\", \"build.gradle\", \"settings.gradle.kts\", \"settings.gradle\",\n \"gradle/libs.versions.toml\",\n \"Cargo.toml\", \"go.mod\", \"go.sum\",\n \"pyproject.toml\", \"setup.py\", \"requirements.txt\", \"Pipfile\",\n \"Gemfile\", \"Package.swift\",\n \"Makefile\", \"CMakeLists.txt\",\n \"Dockerfile\", \"docker-compose.yml\", \"docker-compose.yaml\",\n \".github/workflows\", \".gitlab-ci.yml\", \"Jenkinsfile\",\n ];\n\n const present: string[] = [];\n for (const file of buildFiles) {\n try {\n await fs.access(path.join(rootDir, file));\n present.push(file);\n } catch {\n // Not found\n }\n }\n\n // Test directories and file counts\n const testDirs = [\n \"test\", \"tests\", \"__tests__\", \"spec\",\n \"src/test\", \"src/tests\",\n \"**/src/test\", \"**/src/androidTest\", \"**/src/iosTest\",\n ];\n const testInfo: Record<string, number> = {};\n for (const pattern of testDirs) {\n const files = await fg(`${pattern}/**/*`, {\n cwd: rootDir,\n ignore: IGNORE,\n onlyFiles: true,\n });\n if (files.length > 0) {\n testInfo[pattern] = files.length;\n }\n }\n\n // Also count test files by naming convention\n const testFilePatterns = [\n { pattern: \"**/*.test.*\", label: \"*.test.*\" },\n { pattern: \"**/*.spec.*\", label: \"*.spec.*\" },\n { pattern: \"**/*Test.kt\", label: \"*Test.kt\" },\n { pattern: \"**/*Test.java\", label: \"*Test.java\" },\n { pattern: \"**/test_*.py\", label: \"test_*.py\" },\n { pattern: \"**/*_test.go\", label: \"*_test.go\" },\n { pattern: \"**/*Tests.swift\", label: \"*Tests.swift\" },\n { pattern: \"**/*_test.rs\", label: \"*_test.rs\" },\n ];\n for (const { pattern, label } of testFilePatterns) {\n const files = await fg(pattern, { cwd: rootDir, ignore: IGNORE });\n if (files.length > 0) {\n testInfo[label] = files.length;\n }\n }\n\n // Source file counts by extension\n const sourceFiles = await fg(\"**/*.{ts,tsx,js,jsx,kt,kts,java,py,go,rs,swift,rb,cs,cpp,c,dart}\", {\n cwd: rootDir,\n ignore: IGNORE,\n });\n const fileCounts: Record<string, number> = {};\n for (const file of sourceFiles) {\n const ext = path.extname(file).slice(1);\n fileCounts[ext] = (fileCounts[ext] ?? 0) + 1;\n }\n\n return {\n configFilesPresent: present,\n sourceFileCounts: fileCounts,\n totalSourceFiles: sourceFiles.length,\n testInfo: Object.keys(testInfo).length > 0 ? testInfo : undefined,\n };\n}\n\nexport async function getCodeSamples(\n dir: string,\n count: number = 15\n): Promise<string> {\n const rootDir = path.resolve(dir);\n const samples = await sampleFiles(rootDir, count);\n\n const output = {\n note: \"These are previews (first ~60 lines). Use get_file_content to read the full file if needed.\",\n files: samples.map((s) => ({\n path: s.path,\n reason: s.reason,\n totalLines: s.totalLines,\n sizeBytes: s.sizeBytes,\n preview: s.preview,\n })),\n };\n\n return JSON.stringify(output, null, 2);\n}\n\nexport async function getProjectStructure(dir: string): Promise<string> {\n const rootDir = path.resolve(dir);\n\n // Get all files\n const allFiles = await fg(\"**/*\", {\n cwd: rootDir,\n ignore: IGNORE,\n onlyFiles: true,\n });\n\n // Build directory summary with file counts and extension breakdown\n const dirInfo = new Map<\n string,\n { fileCount: number; extensions: Map<string, number> }\n >();\n\n for (const file of allFiles) {\n const parts = file.split(\"/\");\n // Track up to 2 levels deep\n for (let depth = 1; depth <= Math.min(parts.length, 2); depth++) {\n const dirPath = parts.slice(0, depth).join(\"/\");\n if (!dirInfo.has(dirPath)) {\n dirInfo.set(dirPath, { fileCount: 0, extensions: new Map() });\n }\n const info = dirInfo.get(dirPath)!;\n info.fileCount++;\n const ext = path.extname(file).slice(1);\n if (ext) {\n info.extensions.set(ext, (info.extensions.get(ext) ?? 0) + 1);\n }\n }\n }\n\n // Format directories sorted by path\n const directories = [...dirInfo.entries()]\n .sort((a, b) => a[0].localeCompare(b[0]))\n .map(([dirPath, info]) => {\n const extensions: Record<string, number> = {};\n for (const [ext, count] of info.extensions) {\n extensions[ext] = count;\n }\n return { path: dirPath, fileCount: info.fileCount, extensions };\n });\n\n // Top-level files\n const topLevelFiles = allFiles.filter((f) => !f.includes(\"/\"));\n\n const output = {\n totalFiles: allFiles.length,\n topLevelFiles,\n directories,\n };\n\n return JSON.stringify(output, null, 2);\n}\n\nexport async function getTestMap(dir: string): Promise<string> {\n const { buildTestMap } = await import(\"../test-map.js\");\n const result = await buildTestMap(dir);\n return JSON.stringify(result, null, 2);\n}\n\nexport async function getSnapshot(dir: string): Promise<string> {\n const rootDir = path.resolve(dir);\n const snapshot = await loadSnapshot(rootDir);\n\n if (!snapshot) {\n return JSON.stringify({\n exists: false,\n message:\n \"No concept map found. Run 'mason snapshot' to create one, or call save_snapshot with features and flows.\",\n });\n }\n\n // Check staleness\n const currentHash = await getCurrentGitHash(rootDir);\n const isStale = snapshot.gitHash !== currentHash && snapshot.gitHash !== \"unknown\";\n\n // Return compact format: feature/flow names -> file lists only.\n // Descriptions and metadata stay in the full snapshot on disk.\n // Deduplicate files that appear in multiple features.\n const seenFiles = new Set<string>();\n const compactFeatures: Record<string, { files: string[]; tests?: string[] }> = {};\n for (const [name, feat] of Object.entries(snapshot.features)) {\n const unique = feat.files.filter((f) => !seenFiles.has(f));\n if (unique.length === 0) continue; // Skip fully duplicate features\n for (const f of unique) seenFiles.add(f);\n const entry: { files: string[]; tests?: string[] } = { files: unique };\n if (feat.tests && feat.tests.length > 0) {\n entry.tests = feat.tests;\n }\n compactFeatures[name] = entry;\n }\n\n const compactFlows: Record<string, string[]> = {};\n for (const [name, flow] of Object.entries(snapshot.flows)) {\n compactFlows[name] = flow.chain; // Flows keep all files (order matters)\n }\n\n const output: Record<string, unknown> = {\n exists: true,\n updatedAt: snapshot.updatedAt,\n features: compactFeatures,\n flows: compactFlows,\n stale: isStale,\n };\n\n if (isStale) {\n output.message =\n \"Snapshot is behind HEAD. Run 'mason snapshot-update' or call save_snapshot to refresh.\";\n }\n\n return JSON.stringify(output);\n}\n\nexport async function fullAnalysis(dir: string): Promise<string> {\n const rootDir = path.resolve(dir);\n\n const [analysis, structure, samples, testMap, snapshot] = await Promise.all([\n analyzeProject(dir),\n getProjectStructure(dir),\n getCodeSamples(dir, 25),\n getTestMap(dir),\n loadSnapshot(rootDir),\n ]);\n\n const output: Record<string, unknown> = {\n note: \"Full project analysis. Code samples are previews (~60 lines). Use get_file_content to read any file in full.\",\n analysis: JSON.parse(analysis),\n structure: JSON.parse(structure),\n codeSamples: JSON.parse(samples),\n testMap: JSON.parse(testMap),\n };\n\n if (snapshot) {\n output.conceptMap = {\n updatedAt: snapshot.updatedAt,\n features: snapshot.features,\n flows: snapshot.flows,\n };\n output.note =\n \"Full project analysis with concept map. The concept map shows which files implement each feature and how data flows through them. Use it to jump straight to relevant files instead of exploring. Use get_file_content to read specific files.\";\n }\n\n return JSON.stringify(output, null, 2);\n}\n\nexport async function saveSnapshotData(\n dir: string,\n features: Record<string, { description: string; files: string[]; tests?: string[] }>,\n flows: Record<string, { description: string; chain: string[] }>\n): Promise<string> {\n const rootDir = path.resolve(dir);\n const gitHash = await getCurrentGitHash(rootDir);\n const now = new Date().toISOString();\n\n const existing = await loadSnapshot(rootDir);\n\n if (existing) {\n // Merge: overwrite matching features/flows, keep the rest\n existing.features = { ...existing.features, ...features };\n existing.flows = { ...existing.flows, ...flows };\n existing.updatedAt = now;\n existing.gitHash = gitHash;\n await saveSnapshot(rootDir, existing);\n return JSON.stringify({\n status: \"updated\",\n features: Object.keys(existing.features).length,\n flows: Object.keys(existing.flows).length,\n });\n }\n\n const snapshot: Snapshot = {\n version: 2,\n createdAt: now,\n updatedAt: now,\n gitHash,\n features,\n flows,\n };\n\n await saveSnapshot(rootDir, snapshot);\n return JSON.stringify({\n status: \"created\",\n features: Object.keys(features).length,\n flows: Object.keys(flows).length,\n });\n}\n\nexport async function configureProject(\n dir: string,\n config: {\n patterns?: string[];\n alwaysInclude?: string[];\n ignore?: string[];\n }\n): Promise<string> {\n const rootDir = path.resolve(dir);\n const configDir = path.join(rootDir, \".mason\");\n const configPath = path.join(configDir, \"config.json\");\n\n // Load existing config and merge\n let existing: Record<string, unknown> = {};\n try {\n const raw = await fs.readFile(configPath, \"utf-8\");\n existing = JSON.parse(raw);\n } catch {\n // No existing config\n }\n\n if (config.patterns) existing.patterns = config.patterns;\n if (config.alwaysInclude) existing.alwaysInclude = config.alwaysInclude;\n if (config.ignore) existing.ignore = config.ignore;\n\n await fs.mkdir(configDir, { recursive: true });\n await fs.writeFile(configPath, JSON.stringify(existing, null, 2), \"utf-8\");\n\n return JSON.stringify({\n status: \"saved\",\n path: configPath,\n config: existing,\n });\n}\n\nexport async function getImpact(\n dir: string,\n files: string[]\n): Promise<string> {\n const { analyzeImpact } = await import(\"../impact/impact.js\");\n const rootDir = path.resolve(dir);\n const result = await analyzeImpact(rootDir, files);\n return JSON.stringify(result, null, 2);\n}\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { BaseAnalyzer } from \"./base.js\";\nimport type { AnalyzerContext, AnalyzerResult, Finding, Gap } from \"../types.js\";\n\nconst exec = promisify(execFile);\n\nexport class GitHistoryAnalyzer extends BaseAnalyzer {\n name = \"git-history\";\n\n async analyze(context: AnalyzerContext): Promise<AnalyzerResult> {\n const startTime = Date.now();\n const findings: Finding[] = [];\n const gaps: Gap[] = [];\n\n if (!context.gitAvailable) {\n return this.createResult([], [], startTime);\n }\n\n const [staleFindings, staleGaps] = await this.findStaleDirectories(context);\n findings.push(...staleFindings);\n gaps.push(...staleGaps);\n\n const hotFindings = await this.findHotFiles(context);\n findings.push(...hotFindings);\n\n const commitFindings = await this.analyzeCommitPatterns(context);\n findings.push(...commitFindings);\n\n return this.createResult(findings, gaps, startTime);\n }\n\n private async git(\n args: string[],\n cwd: string\n ): Promise<string> {\n try {\n const { stdout } = await exec(\"git\", args, { cwd, maxBuffer: 10_000_000 });\n return stdout.trim();\n } catch {\n return \"\";\n }\n }\n\n private async findStaleDirectories(\n context: AnalyzerContext\n ): Promise<[Finding[], Gap[]]> {\n const findings: Finding[] = [];\n const gaps: Gap[] = [];\n\n // Get top-level directories with their last commit date\n const output = await this.git(\n [\"log\", \"--all\", \"--format=%ci\", \"--name-only\", \"--diff-filter=AMCR\", \"-n\", \"500\"],\n context.rootDir\n );\n\n if (!output) return [findings, gaps];\n\n const dirLastTouch = new Map<string, Date>();\n let currentDate: Date | null = null;\n\n for (const line of output.split(\"\\n\")) {\n if (!line) continue;\n if (/^\\d{4}-\\d{2}-\\d{2}/.test(line)) {\n currentDate = new Date(line);\n } else if (currentDate) {\n const topDir = line.split(\"/\")[0];\n if (\n topDir &&\n !topDir.startsWith(\".\") &&\n !topDir.includes(\"node_modules\")\n ) {\n const existing = dirLastTouch.get(topDir);\n if (!existing || currentDate > existing) {\n dirLastTouch.set(topDir, currentDate);\n }\n }\n }\n }\n\n const sixMonthsAgo = new Date();\n sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 6);\n\n for (const [dir, lastTouch] of dirLastTouch) {\n if (lastTouch < sixMonthsAgo) {\n const monthsStale = Math.floor(\n (Date.now() - lastTouch.getTime()) / (1000 * 60 * 60 * 24 * 30)\n );\n findings.push(\n this.createFinding({\n category: \"risk\",\n confidence: 0.7,\n summary: `Directory \"${dir}\" hasn't been modified in ${monthsStale} months`,\n evidence: [\n { filePath: dir, detail: `Last commit: ${lastTouch.toISOString().split(\"T\")[0]}` },\n ],\n ruleCandidate: `Do not refactor or modify files in \"${dir}/\" unless explicitly asked — this area has been stable for ${monthsStale} months and may be legacy code.`,\n })\n );\n gaps.push({\n analyzer: this.name,\n question: `Directory \"${dir}\" hasn't been touched in ${monthsStale} months. Is it deprecated, stable, or legacy?`,\n context: `Last modified: ${lastTouch.toISOString().split(\"T\")[0]}`,\n answerKey: `stale-dir-${dir}`,\n });\n }\n }\n\n return [findings, gaps];\n }\n\n private async findHotFiles(context: AnalyzerContext): Promise<Finding[]> {\n const findings: Finding[] = [];\n\n // Most frequently changed files in the last 3 months\n const output = await this.git(\n [\"log\", \"--since=3 months ago\", \"--format=\", \"--name-only\"],\n context.rootDir\n );\n\n if (!output) return findings;\n\n const fileCounts = new Map<string, number>();\n for (const line of output.split(\"\\n\")) {\n if (!line || line.startsWith(\".\") || line.includes(\"node_modules\")) continue;\n fileCounts.set(line, (fileCounts.get(line) ?? 0) + 1);\n }\n\n const sorted = [...fileCounts.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, 10);\n\n if (sorted.length > 0 && sorted[0][1] >= 5) {\n const hotFiles = sorted.filter(([, count]) => count >= 5);\n if (hotFiles.length > 0) {\n findings.push(\n this.createFinding({\n category: \"risk\",\n confidence: 0.8,\n summary: `${hotFiles.length} files changed frequently in the last 3 months`,\n evidence: hotFiles.map(([file, count]) => ({\n filePath: file,\n detail: `${count} commits`,\n })),\n ruleCandidate: `These files change frequently and are high-risk for conflicts: ${hotFiles.map(([f]) => f).join(\", \")}. Take extra care when modifying them.`,\n })\n );\n }\n }\n\n return findings;\n }\n\n private async analyzeCommitPatterns(\n context: AnalyzerContext\n ): Promise<Finding[]> {\n const findings: Finding[] = [];\n\n const output = await this.git(\n [\"log\", \"--format=%s\", \"-n\", \"100\"],\n context.rootDir\n );\n\n if (!output) return findings;\n\n const messages = output.split(\"\\n\").filter(Boolean);\n\n // Check for conventional commits\n const conventionalPattern = /^(feat|fix|chore|docs|style|refactor|test|perf|ci|build|revert)(\\(.+\\))?:/;\n const conventionalCount = messages.filter((m) =>\n conventionalPattern.test(m)\n ).length;\n const conventionalRatio = conventionalCount / messages.length;\n\n if (conventionalRatio > 0.5) {\n findings.push(\n this.createFinding({\n category: \"convention\",\n confidence: Math.min(conventionalRatio + 0.1, 1),\n summary: `${Math.round(conventionalRatio * 100)}% of recent commits use conventional commit format`,\n evidence: [\n {\n filePath: \".git\",\n detail: `${conventionalCount} of ${messages.length} commits match`,\n },\n ],\n ruleCandidate:\n \"Use conventional commit format: type(scope): description (e.g., feat(auth): add login endpoint)\",\n })\n );\n }\n\n // Check for ticket/issue references\n const ticketPattern = /[A-Z]+-\\d+|#\\d+/;\n const ticketCount = messages.filter((m) => ticketPattern.test(m)).length;\n const ticketRatio = ticketCount / messages.length;\n\n if (ticketRatio > 0.3) {\n findings.push(\n this.createFinding({\n category: \"convention\",\n confidence: ticketRatio,\n summary: `${Math.round(ticketRatio * 100)}% of commits reference issue/ticket IDs`,\n evidence: [\n {\n filePath: \".git\",\n detail: `${ticketCount} of ${messages.length} commits have ticket refs`,\n },\n ],\n ruleCandidate:\n \"Include issue/ticket references in commit messages when applicable.\",\n })\n );\n }\n\n return findings;\n }\n}\n","import fs from \"node:fs/promises\";\nimport fg from \"fast-glob\";\nimport type {\n AnalyzerContext,\n AnalyzerResult,\n Finding,\n FindingCategory,\n} from \"../types.js\";\n\nexport abstract class BaseAnalyzer {\n abstract name: string;\n abstract analyze(context: AnalyzerContext): Promise<AnalyzerResult>;\n\n protected async findFiles(\n patterns: string[],\n root: string\n ): Promise<string[]> {\n return fg(patterns, {\n cwd: root,\n ignore: [\"**/node_modules/**\", \"**/dist/**\", \"**/.git/**\"],\n absolute: true,\n });\n }\n\n protected async readFile(filePath: string): Promise<string> {\n return fs.readFile(filePath, \"utf-8\");\n }\n\n protected createFinding(partial: {\n category: FindingCategory;\n confidence: number;\n summary: string;\n evidence?: Finding[\"evidence\"];\n ruleCandidate?: string | null;\n }): Finding {\n return {\n analyzer: this.name,\n category: partial.category,\n confidence: partial.confidence,\n summary: partial.summary,\n evidence: partial.evidence ?? [],\n ruleCandidate: partial.ruleCandidate ?? null,\n };\n }\n\n protected createResult(\n findings: Finding[],\n gaps: AnalyzerResult[\"gaps\"],\n startTime: number\n ): AnalyzerResult {\n return {\n analyzer: this.name,\n findings,\n gaps,\n durationMs: Date.now() - startTime,\n };\n }\n}\n","import type { AnalyzerContext, AnalyzerResult } from \"../types.js\";\nimport type { BaseAnalyzer } from \"./base.js\";\nimport { GitHistoryAnalyzer } from \"./git-history.js\";\n\nconst analyzers: BaseAnalyzer[] = [new GitHistoryAnalyzer()];\n\nexport async function runAll(\n context: AnalyzerContext\n): Promise<AnalyzerResult[]> {\n return Promise.all(analyzers.map((a) => a.analyze(context)));\n}\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nconst exec = promisify(execFile);\n\nexport async function isGitRepo(dir: string): Promise<boolean> {\n try {\n await exec(\"git\", [\"rev-parse\", \"--git-dir\"], { cwd: dir });\n return true;\n } catch {\n return false;\n }\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport fg from \"fast-glob\";\n\nconst exec = promisify(execFile);\n\nconst SOURCE_EXTENSIONS = [\n \"ts\", \"tsx\", \"js\", \"jsx\", \"mts\", \"mjs\",\n \"kt\", \"kts\", \"java\",\n \"py\",\n \"go\",\n \"rs\",\n \"swift\",\n \"rb\",\n \"cs\", \"cpp\", \"c\", \"h\",\n \"dart\",\n];\n\nconst CONFIG_FILES = [\n // Build & project config\n \"package.json\",\n \"tsconfig.json\",\n \"build.gradle.kts\",\n \"build.gradle\",\n \"settings.gradle.kts\",\n \"settings.gradle\",\n \"Cargo.toml\",\n \"go.mod\",\n \"pyproject.toml\",\n \"Gemfile\",\n \"*.csproj\",\n // Version catalogs & dependency locks\n \"gradle/libs.versions.toml\",\n // Code quality & formatting\n \".editorconfig\",\n \".eslintrc.*\",\n \"eslint.config.*\",\n \".prettierrc\",\n \"rustfmt.toml\",\n \".swiftlint.yml\",\n // CI/CD\n \".github/workflows/*.yml\",\n \".gitlab-ci.yml\",\n \"Jenkinsfile\",\n // Containerization\n \"Dockerfile\",\n \"docker-compose.yml\",\n \"docker-compose.yaml\",\n];\n\nconst ENTRY_POINT_PATTERNS = [\n \"src/main.*\",\n \"src/index.*\",\n \"src/app.*\",\n \"main.*\",\n \"index.*\",\n \"app.*\",\n \"App.*\",\n \"**/Main.kt\",\n \"**/Application.kt\",\n \"**/main.py\",\n \"**/main.go\",\n \"**/main.rs\",\n \"**/lib.rs\",\n \"**/Program.cs\",\n];\n\n// Filename patterns that reveal architectural patterns and conventions.\n// These are language-agnostic — the suffixes appear across ecosystems.\n// Ordered by architectural importance — most distinctive patterns first.\nconst ARCHITECTURAL_PATTERNS = [\n // State/data flow\n { glob: \"**/*ViewModel.*\", category: \"state\", reason: \"viewmodel (state management)\" },\n { glob: \"**/*Store.*\", category: \"state\", reason: \"store (state management)\" },\n { glob: \"**/*Reducer.*\", category: \"state\", reason: \"reducer (state management)\" },\n // Data layer — interface\n { glob: \"**/*Repository.*\", category: \"data-interface\", reason: \"repository interface (data layer contract)\" },\n { glob: \"**/*Dao.*\", category: \"data-interface\", reason: \"DAO (data access)\" },\n { glob: \"**/*DataSource.*\", category: \"data-interface\", reason: \"data source\" },\n // Data layer — implementation (where actual patterns live: mappers, retry, IO dispatchers)\n { glob: \"**/*RepositoryImpl.*\", category: \"data-impl\", reason: \"repository implementation (data layer patterns)\" },\n { glob: \"**/*ServiceImpl.*\", category: \"data-impl\", reason: \"service implementation\" },\n { glob: \"**/*Impl.*\", category: \"data-impl\", reason: \"implementation (concrete patterns)\" },\n // Data transformation\n { glob: \"**/*Mapper.*\", category: \"transform\", reason: \"mapper (data transformation)\" },\n { glob: \"**/*Converter.*\", category: \"transform\", reason: \"converter (data transformation)\" },\n { glob: \"**/*Adapter.*\", category: \"transform\", reason: \"adapter (interface adaptation)\" },\n // Dependency injection / wiring\n { glob: \"**/*Module.*\", category: \"di\", reason: \"module (DI/wiring)\" },\n { glob: \"**/*Provider.*\", category: \"di\", reason: \"provider (DI/wiring)\" },\n { glob: \"**/*Container.*\", category: \"di\", reason: \"container (DI/wiring)\" },\n { glob: \"**/*Factory.*\", category: \"di\", reason: \"factory (object creation)\" },\n // API / network\n { glob: \"**/*Service.*\", category: \"api\", reason: \"service (business/API layer)\" },\n { glob: \"**/*Client.*\", category: \"api\", reason: \"client (API/network layer)\" },\n { glob: \"**/*Api.*\", category: \"api\", reason: \"API interface definition\" },\n // Interface contracts / protocols\n { glob: \"**/*Interface.*\", category: \"contract\", reason: \"interface definition\" },\n { glob: \"**/*Protocol.*\", category: \"contract\", reason: \"protocol definition\" },\n { glob: \"**/*Trait.*\", category: \"contract\", reason: \"trait definition\" },\n // Routing / navigation\n { glob: \"**/*Router.*\", category: \"routing\", reason: \"router (navigation/routing)\" },\n { glob: \"**/*Route.*\", category: \"routing\", reason: \"route definition\" },\n { glob: \"**/*NavHost.*\", category: \"routing\", reason: \"navigation host\" },\n { glob: \"**/*Controller.*\", category: \"routing\", reason: \"controller (request handling)\" },\n { glob: \"**/*Handler.*\", category: \"routing\", reason: \"handler (request handling)\" },\n // Middleware / interceptors\n { glob: \"**/*Middleware.*\", category: \"middleware\", reason: \"middleware (request pipeline)\" },\n { glob: \"**/*Interceptor.*\", category: \"middleware\", reason: \"interceptor (cross-cutting)\" },\n { glob: \"**/*Plugin.*\", category: \"middleware\", reason: \"plugin (extensibility)\" },\n // Models / types\n { glob: \"**/*Model.*\", category: \"model\", reason: \"model (domain types)\" },\n { glob: \"**/*Entity.*\", category: \"model\", reason: \"entity (persistence types)\" },\n { glob: \"**/*Dto.*\", category: \"model\", reason: \"DTO (data transfer types)\" },\n { glob: \"**/*Schema.*\", category: \"model\", reason: \"schema (data validation)\" },\n // Use cases / commands\n { glob: \"**/*UseCase.*\", category: \"usecase\", reason: \"use case (business logic)\" },\n { glob: \"**/*Interactor.*\", category: \"usecase\", reason: \"interactor (business logic)\" },\n { glob: \"**/*Command.*\", category: \"usecase\", reason: \"command (CQRS pattern)\" },\n];\n\nconst IGNORE_PATTERNS = [\n \"**/node_modules/**\",\n \"**/dist/**\",\n \"**/build/**\",\n \"**/.gradle/**\",\n \"**/target/**\",\n \"**/.git/**\",\n \"**/vendor/**\",\n \"**/__pycache__/**\",\n \"**/venv/**\",\n \"**/.venv/**\",\n \"**/*.min.*\",\n \"**/*.map\",\n \"**/package-lock.json\",\n \"**/yarn.lock\",\n \"**/pnpm-lock.yaml\",\n \"**/*.lock\",\n \"**/*.generated.*\",\n \"**/generated/**\",\n \"**/R.java\",\n \"**/BuildConfig.java\",\n];\n\nconst PREVIEW_LINES = 60;\n\nexport interface ProjectConfig {\n patterns?: string[];\n alwaysInclude?: string[];\n ignore?: string[];\n}\n\nexport interface SampledFile {\n path: string;\n preview: string;\n totalLines: number;\n sizeBytes: number;\n reason: string;\n}\n\nasync function loadProjectConfig(\n rootDir: string\n): Promise<ProjectConfig> {\n try {\n const raw = await fs.readFile(\n path.join(rootDir, \".mason\", \"config.json\"),\n \"utf-8\"\n );\n return JSON.parse(raw);\n } catch {\n return {};\n }\n}\n\nexport async function sampleFiles(\n rootDir: string,\n maxFiles: number = 25\n): Promise<SampledFile[]> {\n const selected = new Map<string, string>(); // path -> reason\n const projectConfig = await loadProjectConfig(rootDir);\n const ignorePatterns = [...IGNORE_PATTERNS, ...(projectConfig.ignore ?? [])];\n\n // 0. Always-include files from project config (highest priority)\n for (const filePath of projectConfig.alwaysInclude ?? []) {\n if (selected.size >= maxFiles) break;\n // Validate path stays within project root\n const resolvedPath = path.resolve(rootDir, filePath);\n if (!resolvedPath.startsWith(path.resolve(rootDir))) continue;\n selected.set(filePath, \"always-include (project config)\");\n }\n\n // 1. Config files (cap at 5)\n let configCount = 0;\n for (const pattern of CONFIG_FILES) {\n if (configCount >= 5) break;\n const matches = await fg(pattern, {\n cwd: rootDir,\n ignore: ignorePatterns,\n deep: 3,\n });\n for (const match of matches) {\n if (configCount >= 5 || selected.size >= maxFiles) break;\n selected.set(match, \"config file\");\n configCount++;\n }\n }\n\n // 2. Module build/config files — build files from subdirectories reveal dependency graph\n const moduleBuildPatterns = [\n // Gradle\n \"**/build.gradle.kts\",\n \"**/build.gradle\",\n // Cargo workspace members\n \"**/Cargo.toml\",\n // Node workspaces\n \"**/package.json\",\n // Go sub-modules\n \"**/go.mod\",\n ];\n let moduleBuildCount = 0;\n for (const pattern of moduleBuildPatterns) {\n const matches = await fg(pattern, {\n cwd: rootDir,\n ignore: ignorePatterns,\n deep: 4,\n });\n // Skip root-level files (already captured as config)\n const subMatches = matches.filter((m) => m.includes(\"/\"));\n for (const match of subMatches) {\n if (moduleBuildCount >= 4 || selected.size >= maxFiles) break;\n if (!selected.has(match)) {\n selected.set(match, \"module build file (reveals dependency graph)\");\n moduleBuildCount++;\n }\n }\n if (moduleBuildCount >= 4) break;\n }\n\n // 3. Entry points (cap at 2)\n let entryCount = 0;\n for (const pattern of ENTRY_POINT_PATTERNS) {\n if (entryCount >= 2) break;\n const matches = await fg(pattern, {\n cwd: rootDir,\n ignore: ignorePatterns,\n deep: 5,\n });\n for (const match of matches) {\n if (entryCount >= 2 || selected.size >= maxFiles) break;\n if (!selected.has(match)) {\n selected.set(match, \"entry point\");\n entryCount++;\n }\n }\n }\n\n // 4. Hot files from git (up to 5)\n try {\n const { stdout } = await exec(\n \"git\",\n [\"log\", \"--since=3 months ago\", \"--format=\", \"--name-only\"],\n { cwd: rootDir, maxBuffer: 5_000_000 }\n );\n\n const fileCounts = new Map<string, number>();\n for (const line of stdout.split(\"\\n\")) {\n if (!line) continue;\n if (\n line.includes(\"node_modules\") ||\n line.includes(\"/build/\") ||\n line.includes(\".gradle\") ||\n line.includes(\"/generated/\")\n )\n continue;\n const ext = path.extname(line).slice(1);\n if (!SOURCE_EXTENSIONS.includes(ext)) continue;\n fileCounts.set(line, (fileCounts.get(line) ?? 0) + 1);\n }\n\n const hotFiles = [...fileCounts.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, 5);\n\n for (const [file, count] of hotFiles) {\n if (selected.size >= maxFiles) break;\n if (!selected.has(file)) {\n selected.set(file, `frequently changed (${count} commits in 3 months)`);\n }\n }\n } catch {\n // No git\n }\n\n // 5. Architectural pattern files — one per category (cap at 8)\n const seenCategories = new Set<string>();\n let patternCount = 0;\n for (const pattern of ARCHITECTURAL_PATTERNS) {\n if (patternCount >= 8 || selected.size >= maxFiles) break;\n if (seenCategories.has(pattern.category)) continue;\n\n const matches = await fg(pattern.glob, {\n cwd: rootDir,\n ignore: ignorePatterns,\n });\n\n if (matches.length > 0) {\n for (const match of matches) {\n if (!selected.has(match)) {\n selected.set(match, pattern.reason);\n seenCategories.add(pattern.category);\n patternCount++;\n break;\n }\n }\n }\n }\n\n // 5b. Custom patterns from project config\n for (const customGlob of projectConfig.patterns ?? []) {\n if (selected.size >= maxFiles) break;\n const matches = await fg(customGlob, {\n cwd: rootDir,\n ignore: ignorePatterns,\n });\n for (const match of matches) {\n if (selected.size >= maxFiles) break;\n if (!selected.has(match)) {\n selected.set(match, \"custom pattern (project config)\");\n break; // one per pattern\n }\n }\n }\n\n // 6. Test examples — diverse across file types (cap at 3)\n const testPatternGroups = [\n // JS/TS tests\n { patterns: [\"**/*.test.*\", \"**/*.spec.*\"], label: \"JS/TS test\" },\n // JVM tests\n { patterns: [\"**/*Test.kt\", \"**/*Test.java\"], label: \"JVM test\" },\n // Python tests\n { patterns: [\"**/test_*.py\", \"**/*_test.py\"], label: \"Python test\" },\n // Go tests\n { patterns: [\"**/*_test.go\"], label: \"Go test\" },\n // Swift tests\n { patterns: [\"**/*Tests.swift\", \"**/*Test.swift\"], label: \"Swift test\" },\n // Rust tests\n { patterns: [\"**/*_test.rs\"], label: \"Rust test\" },\n ];\n let testCount = 0;\n for (const group of testPatternGroups) {\n if (testCount >= 3 || selected.size >= maxFiles) break;\n const testFiles = await fg(group.patterns, {\n cwd: rootDir,\n ignore: ignorePatterns,\n });\n if (testFiles.length > 0) {\n for (const file of testFiles) {\n if (!selected.has(file)) {\n selected.set(file, `test example (${group.label})`);\n testCount++;\n break;\n }\n }\n }\n }\n\n // 7. Directory breadth — fill remaining slots with one file per top-level dir\n const sourceGlobs = SOURCE_EXTENSIONS.map((ext) => `**/*.${ext}`);\n const allSourceFiles = await fg(sourceGlobs, {\n cwd: rootDir,\n ignore: ignorePatterns,\n });\n\n const dirRepresentatives = new Map<string, string>();\n const boringFiles = /\\.(gradle|gradle\\.kts|json|toml|yaml|yml|xml|properties)$/;\n for (const file of allSourceFiles) {\n const topDir = file.split(\"/\")[0];\n if (!dirRepresentatives.has(topDir) && !boringFiles.test(file)) {\n dirRepresentatives.set(topDir, file);\n }\n }\n\n for (const [, file] of dirRepresentatives) {\n if (selected.size >= maxFiles) break;\n if (!selected.has(file)) {\n selected.set(file, \"directory representative\");\n }\n }\n\n // Read file previews\n const results: SampledFile[] = [];\n for (const [filePath, reason] of selected) {\n try {\n const fullPath = path.resolve(rootDir, filePath);\n if (!fullPath.startsWith(path.resolve(rootDir))) continue;\n if (isSensitiveFile(filePath)) continue;\n const stat = await fs.stat(fullPath);\n if (stat.size > 100_000) continue;\n\n const content = await fs.readFile(fullPath, \"utf-8\");\n const lines = content.split(\"\\n\");\n const preview = lines.slice(0, PREVIEW_LINES).join(\"\\n\");\n\n results.push({\n path: filePath,\n preview,\n totalLines: lines.length,\n sizeBytes: stat.size,\n reason,\n });\n } catch {\n // Skip\n }\n }\n\n return results;\n}\n\nconst SENSITIVE_PATTERNS = [\n /^\\.env$/,\n /^\\.env\\./,\n /\\.pem$/,\n /\\.key$/,\n /\\.p12$/,\n /\\.pfx$/,\n /\\.jks$/,\n /id_rsa/,\n /id_ed25519/,\n /credentials\\./,\n /secret/i,\n /\\.keystore$/,\n /local\\.properties$/,\n];\n\nfunction isSensitiveFile(filePath: string): boolean {\n const basename = path.basename(filePath);\n return SENSITIVE_PATTERNS.some((p) => p.test(basename));\n}\n\nexport async function readFullFile(\n rootDir: string,\n filePath: string\n): Promise<{ path: string; content: string; totalLines: number } | null> {\n try {\n const fullPath = path.join(path.resolve(rootDir), filePath);\n if (!fullPath.startsWith(path.resolve(rootDir))) return null;\n if (isSensitiveFile(filePath)) return null;\n\n const content = await fs.readFile(fullPath, \"utf-8\");\n return {\n path: filePath,\n content,\n totalLines: content.split(\"\\n\").length,\n };\n } catch {\n return null;\n }\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { sampleFiles, readFullFile } from \"../mcp/sampler.js\";\nimport { buildTestMap } from \"../test-map.js\";\nimport { callLLM } from \"../llm/providers.js\";\nimport type { MasonConfig } from \"../llm/config.js\";\nimport {\n SNAPSHOT_SYSTEM_PROMPT,\n buildSnapshotPrompt,\n buildIncrementalPrompt,\n} from \"./prompt.js\";\n\nconst exec = promisify(execFile);\n\nexport interface FeatureEntry {\n description: string;\n files: string[];\n tests?: string[];\n}\n\nexport interface FlowEntry {\n description: string;\n chain: string[];\n}\n\nexport interface Snapshot {\n version: 2;\n createdAt: string;\n updatedAt: string;\n gitHash: string;\n features: Record<string, FeatureEntry>;\n flows: Record<string, FlowEntry>;\n}\n\nfunction snapshotDir(rootDir: string): string {\n return path.join(rootDir, \".mason\");\n}\n\nfunction snapshotPath(rootDir: string): string {\n return path.join(snapshotDir(rootDir), \"snapshot.json\");\n}\n\nexport async function loadSnapshot(rootDir: string): Promise<Snapshot | null> {\n try {\n const raw = await fs.readFile(snapshotPath(rootDir), \"utf-8\");\n const parsed = JSON.parse(raw);\n // Skip v1 snapshots — they're the old per-file format\n if (parsed.version !== 2) return null;\n return parsed;\n } catch {\n return null;\n }\n}\n\nexport async function saveSnapshot(\n rootDir: string,\n snapshot: Snapshot\n): Promise<void> {\n await fs.mkdir(snapshotDir(rootDir), { recursive: true });\n await fs.writeFile(\n snapshotPath(rootDir),\n JSON.stringify(snapshot, null, 2),\n \"utf-8\"\n );\n}\n\nexport async function getCurrentGitHash(rootDir: string): Promise<string> {\n try {\n const { stdout } = await exec(\"git\", [\"rev-parse\", \"HEAD\"], {\n cwd: rootDir,\n });\n return stdout.trim();\n } catch {\n return \"unknown\";\n }\n}\n\nfunction parseSnapshotResponse(raw: string): {\n features: Record<string, FeatureEntry>;\n flows: Record<string, FlowEntry>;\n} {\n let cleaned = raw.trim();\n if (cleaned.startsWith(\"```\")) {\n cleaned = cleaned.replace(/^```(?:json)?\\n?/, \"\").replace(/\\n?```$/, \"\");\n }\n\n try {\n const parsed = JSON.parse(cleaned);\n return {\n features: parsed.features ?? {},\n flows: parsed.flows ?? {},\n };\n } catch {\n // Try to find JSON object in the response\n const match = raw.match(/\\{[\\s\\S]*\\}/);\n if (match) {\n try {\n const parsed = JSON.parse(match[0]);\n return {\n features: parsed.features ?? {},\n flows: parsed.flows ?? {},\n };\n } catch {\n return { features: {}, flows: {} };\n }\n }\n return { features: {}, flows: {} };\n }\n}\n\nexport async function createSnapshot(\n rootDir: string,\n config: MasonConfig\n): Promise<Snapshot> {\n const resolvedRoot = path.resolve(rootDir);\n\n // Use sampler to pick key files\n const sampled = await sampleFiles(resolvedRoot, 25);\n\n // Read full content of each sampled file\n const filesWithContent: Array<{ path: string; content: string }> = [];\n for (const sample of sampled) {\n const full = await readFullFile(resolvedRoot, sample.path);\n if (full) {\n filesWithContent.push({ path: full.path, content: full.content });\n }\n }\n\n const gitHash = await getCurrentGitHash(resolvedRoot);\n const now = new Date().toISOString();\n\n if (filesWithContent.length === 0) {\n return {\n version: 2,\n createdAt: now,\n updatedAt: now,\n gitHash,\n features: {},\n flows: {},\n };\n }\n\n // Get test-to-source mappings so the LLM can include tests in features\n const testMap = await buildTestMap(resolvedRoot);\n\n // Call LLM to build concept map\n const userMessage = buildSnapshotPrompt(filesWithContent, testMap.paired);\n const result = await callLLM(config, userMessage, SNAPSHOT_SYSTEM_PROMPT);\n\n const resultText =\n typeof result === \"string\" ? result : result.type === \"response\" ? result.text : \"\";\n\n if (!resultText) {\n throw new Error(\n \"No CLI or API key available for this provider. Use claude or ollama (no key needed), or provide an API key.\"\n );\n }\n\n const { features, flows } = parseSnapshotResponse(resultText);\n\n const snapshot: Snapshot = {\n version: 2,\n createdAt: now,\n updatedAt: now,\n gitHash,\n features,\n flows,\n };\n\n await saveSnapshot(resolvedRoot, snapshot);\n return snapshot;\n}\n\nexport async function updateSnapshot(\n rootDir: string,\n config: MasonConfig\n): Promise<{ status: string; details: string }> {\n const resolvedRoot = path.resolve(rootDir);\n const existing = await loadSnapshot(resolvedRoot);\n\n if (!existing) {\n const snapshot = await createSnapshot(rootDir, config);\n const featureCount = Object.keys(snapshot.features).length;\n const flowCount = Object.keys(snapshot.flows).length;\n return {\n status: \"created\",\n details: `New snapshot: ${featureCount} features, ${flowCount} flows`,\n };\n }\n\n // Find files changed since last snapshot\n let changedFiles: string[] = [];\n try {\n const { stdout } = await exec(\n \"git\",\n [\"diff\", \"--name-only\", existing.gitHash, \"HEAD\"],\n { cwd: resolvedRoot }\n );\n changedFiles = stdout\n .trim()\n .split(\"\\n\")\n .filter((f) => f.length > 0);\n } catch {\n // Full rebuild if git diff fails\n const snapshot = await createSnapshot(rootDir, config);\n const featureCount = Object.keys(snapshot.features).length;\n return { status: \"rebuilt\", details: `${featureCount} features` };\n }\n\n if (changedFiles.length === 0) {\n return { status: \"up-to-date\", details: \"No changes since last snapshot\" };\n }\n\n // Check which changed files are architecturally relevant\n const sampled = await sampleFiles(resolvedRoot, 30);\n const sampledPaths = new Set(sampled.map((s) => s.path));\n\n // Also check which changed files are referenced in the existing snapshot\n const snapshotFiles = new Set<string>();\n for (const feature of Object.values(existing.features)) {\n for (const f of feature.files) snapshotFiles.add(f);\n for (const t of feature.tests ?? []) snapshotFiles.add(t);\n }\n for (const flow of Object.values(existing.flows)) {\n for (const f of flow.chain) snapshotFiles.add(f);\n }\n\n const relevantChanges = changedFiles.filter(\n (f) => sampledPaths.has(f) || snapshotFiles.has(f)\n );\n\n if (relevantChanges.length === 0) {\n // Changes don't affect snapshot files\n existing.gitHash = await getCurrentGitHash(resolvedRoot);\n existing.updatedAt = new Date().toISOString();\n await saveSnapshot(resolvedRoot, existing);\n return {\n status: \"unchanged\",\n details: `${changedFiles.length} files changed but none affect the concept map`,\n };\n }\n\n // Read changed files and ask LLM to update the map\n const filesWithContent: Array<{ path: string; content: string }> = [];\n for (const filePath of relevantChanges) {\n const full = await readFullFile(resolvedRoot, filePath);\n if (full) {\n filesWithContent.push({ path: full.path, content: full.content });\n }\n }\n\n if (filesWithContent.length === 0) {\n return { status: \"unchanged\", details: \"Changed files could not be read\" };\n }\n\n const userMessage = buildIncrementalPrompt(filesWithContent, {\n features: existing.features,\n flows: existing.flows,\n });\n\n const result = await callLLM(config, userMessage, SNAPSHOT_SYSTEM_PROMPT);\n const resultText =\n typeof result === \"string\" ? result : result.type === \"response\" ? result.text : \"\";\n\n if (!resultText) {\n throw new Error(\"No CLI or API key available for this provider.\");\n }\n\n const { features, flows } = parseSnapshotResponse(resultText);\n const gitHash = await getCurrentGitHash(resolvedRoot);\n\n existing.features = features;\n existing.flows = flows;\n existing.updatedAt = new Date().toISOString();\n existing.gitHash = gitHash;\n\n await saveSnapshot(resolvedRoot, existing);\n\n return {\n status: \"updated\",\n details: `${Object.keys(features).length} features, ${Object.keys(flows).length} flows (${relevantChanges.length} files changed)`,\n };\n}\n\nexport async function installHook(rootDir: string): Promise<void> {\n const resolvedRoot = path.resolve(rootDir);\n const hooksDir = path.join(resolvedRoot, \".git\", \"hooks\");\n\n try {\n await fs.access(hooksDir);\n } catch {\n throw new Error(\"Not a git repository (no .git/hooks directory)\");\n }\n\n const hookPath = path.join(hooksDir, \"post-commit\");\n const hookContent = `#!/bin/sh\n# Mason: auto-update project snapshot after commit\n# Runs in background so it doesn't block your workflow\nmason snapshot-update \"$(git rev-parse --show-toplevel)\" &\n`;\n\n try {\n const existing = await fs.readFile(hookPath, \"utf-8\");\n if (existing.includes(\"mason snapshot-update\")) {\n return; // Already installed\n }\n await fs.appendFile(hookPath, \"\\n\" + hookContent);\n } catch {\n await fs.writeFile(hookPath, hookContent, { mode: 0o755 });\n }\n}\n","import { execFile, spawn } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport type { MasonConfig } from \"./config.js\";\nimport { getDefaultModel } from \"./config.js\";\n\nconst exec = promisify(execFile);\n\nconst CLAUDE_MD_SYSTEM_PROMPT = `You are Mason, a context engineering tool. You've been given a comprehensive analysis of a codebase including:\n- Git history stats (commit patterns, frequently changed files, stale directories)\n- Project structure (directory layout, file counts by type)\n- Curated code samples (key architectural files with previews)\n- Test-to-source file mapping\n\nYour job: write a COMPLETE CLAUDE.md file from scratch based ONLY on the analysis data provided below. Do NOT read any existing files in the project. Do NOT reference or preserve any existing CLAUDE.md. Generate the entire document fresh.\n\nCRITICAL: Output ONLY the raw markdown content. No preamble, no summary, no \"Here's the CLAUDE.md:\", no explanation, no questions, no commentary. Start directly with \"# CLAUDE.md\" and end with the last line of content. Your entire response will be written directly to a file.\n\nThe CLAUDE.md should include:\n- Project overview (what it is, tech stack, architecture)\n- Module/package structure and boundaries\n- Code conventions and patterns you observe in the samples\n- Testing conventions and coverage\n- Build and development commands\n- Important files and hot spots\n- Any warnings or gotchas\n\nBe specific and actionable. Reference actual file paths. Don't be generic — every rule should be grounded in what you see in the data.`;\n\nexport type CallResult =\n | { type: \"response\"; text: string }\n | { type: \"prompt\"; text: string };\n\nexport async function callLLM(\n config: MasonConfig,\n userMessage: string,\n systemPrompt?: string\n): Promise<CallResult> {\n const model = config.model ?? getDefaultModel(config.provider);\n const system = systemPrompt ?? CLAUDE_MD_SYSTEM_PROMPT;\n\n switch (config.provider) {\n case \"claude\":\n if (config.apiKey) {\n return {\n type: \"response\",\n text: await callClaudeAPI(config.apiKey, model, system, userMessage),\n };\n }\n return {\n type: \"response\",\n text: await callClaudeCLI(system, userMessage),\n };\n\n case \"ollama\":\n return {\n type: \"response\",\n text: await callOllamaCLI(\n config.ollamaHost ?? \"http://localhost:11434\",\n model,\n system,\n userMessage,\n ),\n };\n\n case \"gemini\":\n if (config.apiKey) {\n return {\n type: \"response\",\n text: await callGeminiAPI(config.apiKey, model, system, userMessage),\n };\n }\n return {\n type: \"response\",\n text: await callGeminiCLI(system, userMessage),\n };\n\n case \"openai\":\n if (config.apiKey) {\n return {\n type: \"response\",\n text: await callOpenAIAPI(config.apiKey, model, system, userMessage),\n };\n }\n return {\n type: \"prompt\",\n text: formatPromptForCopy(system, userMessage),\n };\n }\n}\n\nfunction formatPromptForCopy(system: string, userMessage: string): string {\n return `${system}\\n\\n---\\n\\n${userMessage}`;\n}\n\n// === CLI-based providers (no API key) ===\n\nasync function callViaTempFile(\n command: string,\n args: (promptPath: string) => string[],\n system: string,\n userMessage: string\n): Promise<string> {\n const fs = await import(\"node:fs/promises\");\n const os = await import(\"node:os\");\n const path = await import(\"node:path\");\n\n const prompt = `${system}\\n\\n${userMessage}`;\n const tmpFile = path.join(os.tmpdir(), `mason-prompt-${Date.now()}.txt`);\n\n try {\n await fs.writeFile(tmpFile, prompt, \"utf-8\");\n const { stdout } = await exec(command, args(tmpFile), {\n maxBuffer: 10_000_000,\n timeout: 300_000,\n });\n return stdout.trim();\n } finally {\n await fs.unlink(tmpFile).catch(() => {});\n }\n}\n\nfunction spawnWithStdin(\n command: string,\n args: string[],\n input: string\n): Promise<string> {\n return new Promise((resolve, reject) => {\n const proc = spawn(command, args, {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n timeout: 300_000,\n });\n\n let stdout = \"\";\n let stderr = \"\";\n\n proc.stdout.on(\"data\", (data: Buffer) => {\n stdout += data.toString();\n });\n proc.stderr.on(\"data\", (data: Buffer) => {\n stderr += data.toString();\n });\n\n proc.on(\"close\", (code: number | null) => {\n if (code === 0) {\n resolve(stdout.trim());\n } else {\n reject(new Error(`${command} exited with code ${code}: ${stderr}`));\n }\n });\n\n proc.on(\"error\", reject);\n\n proc.stdin.write(input);\n proc.stdin.end();\n });\n}\n\nasync function callClaudeCLI(\n system: string,\n userMessage: string\n): Promise<string> {\n const prompt = `${system}\\n\\n${userMessage}`;\n return spawnWithStdin(\"claude\", [\"-p\"], prompt);\n}\n\nasync function callGeminiCLI(\n system: string,\n userMessage: string\n): Promise<string> {\n const prompt = `${system}\\n\\n${userMessage}`;\n return spawnWithStdin(\"gemini\", [\"-p\", \"\"], prompt);\n}\n\nasync function callOllamaCLI(\n host: string,\n model: string,\n system: string,\n userMessage: string\n): Promise<string> {\n const response = await fetch(`${host}/api/chat`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n model,\n stream: false,\n messages: [\n { role: \"system\", content: system },\n { role: \"user\", content: userMessage },\n ],\n }),\n });\n\n const result = (await response.json()) as {\n message?: { content?: string };\n };\n return result.message?.content ?? \"\";\n}\n\n// === API-based providers ===\n\nasync function callClaudeAPI(\n apiKey: string,\n model: string,\n system: string,\n userMessage: string\n): Promise<string> {\n const { default: Anthropic } = await import(\"@anthropic-ai/sdk\");\n const client = new Anthropic({ apiKey });\n\n const response = await client.messages.create({\n model,\n max_tokens: 8192,\n system,\n messages: [{ role: \"user\", content: userMessage }],\n });\n\n const textBlock = response.content.find((b) => b.type === \"text\");\n return textBlock?.text ?? \"\";\n}\n\nasync function callGeminiAPI(\n apiKey: string,\n model: string,\n system: string,\n userMessage: string\n): Promise<string> {\n const { default: OpenAI } = await import(\"openai\");\n const client = new OpenAI({\n apiKey,\n baseURL: \"https://generativelanguage.googleapis.com/v1beta/openai/\",\n });\n\n const response = await client.chat.completions.create({\n model,\n max_tokens: 8192,\n messages: [\n { role: \"system\", content: system },\n { role: \"user\", content: userMessage },\n ],\n });\n\n return response.choices[0]?.message?.content ?? \"\";\n}\n\nasync function callOpenAIAPI(\n apiKey: string,\n model: string,\n system: string,\n userMessage: string\n): Promise<string> {\n const { default: OpenAI } = await import(\"openai\");\n const client = new OpenAI({ apiKey });\n\n const response = await client.chat.completions.create({\n model,\n max_tokens: 8192,\n messages: [\n { role: \"system\", content: system },\n { role: \"user\", content: userMessage },\n ],\n });\n\n return response.choices[0]?.message?.content ?? \"\";\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nconst exec = promisify(execFile);\n\nexport type Provider = \"claude\" | \"gemini\" | \"openai\" | \"ollama\";\n\nexport interface MasonConfig {\n provider: Provider;\n apiKey?: string;\n model?: string;\n ollamaHost?: string;\n}\n\nconst CONFIG_DIR = path.join(os.homedir(), \".mason\");\nconst CONFIG_FILE = path.join(CONFIG_DIR, \"config.json\");\n\nconst DEFAULT_MODELS: Record<Provider, string> = {\n claude: \"claude-sonnet-4-20250514\",\n gemini: \"gemini-2.5-flash\",\n openai: \"gpt-4o\",\n ollama: \"llama3\",\n};\n\nexport async function loadConfig(): Promise<MasonConfig | null> {\n try {\n const raw = await fs.readFile(CONFIG_FILE, \"utf-8\");\n return JSON.parse(raw);\n } catch {\n return null;\n }\n}\n\nexport async function saveConfig(config: MasonConfig): Promise<void> {\n await fs.mkdir(CONFIG_DIR, { recursive: true });\n await fs.writeFile(CONFIG_FILE, JSON.stringify(config, null, 2), \"utf-8\");\n}\n\nexport function getDefaultModel(provider: Provider): string {\n return DEFAULT_MODELS[provider];\n}\n\nexport function validateProvider(value: string): Provider {\n const valid: Provider[] = [\"claude\", \"gemini\", \"openai\", \"ollama\"];\n if (!valid.includes(value as Provider)) {\n throw new Error(\n `Invalid provider \"${value}\". Must be one of: ${valid.join(\", \")}`\n );\n }\n return value as Provider;\n}\n\nexport async function detectCLI(\n provider: Provider\n): Promise<{ available: boolean; version?: string }> {\n const cliName = provider === \"claude\" ? \"claude\"\n : provider === \"gemini\" ? \"gemini\"\n : provider === \"ollama\" ? \"ollama\"\n : null;\n\n if (!cliName) return { available: false };\n\n try {\n const { stdout } = await exec(cliName, [\"--version\"]);\n return { available: true, version: stdout.trim() };\n } catch {\n return { available: false };\n }\n}\n\nexport function needsApiKey(provider: Provider): boolean {\n return provider === \"openai\";\n}\n","import { startMcpServer } from \"../src/mcp/server.js\";\n\nstartMcpServer().catch((err) => {\n process.stderr.write(`Mason MCP server error: ${err}\\n`);\n process.exit(1);\n});\n"],"mappings":"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","names":["path","fg","fs","path","execFile","promisify","fg","IGNORE","exec","SOURCE_EXTENSIONS","fs","path","execFile","promisify","fg","execFile","promisify","exec","fs","execFile","promisify","fg","exec","fs","path","execFile","promisify","execFile","promisify","fs","path","execFile","promisify","exec","exec","promisify","execFile","exec","promisify","execFile","path","fs","exec","promisify","execFile","IGNORE","path","fs","fg","buildTestMap","analyzeImpact","path"]}
1
+ {"version":3,"sources":["../../src/test-map.ts","../../src/impact/impact.ts","../../src/mcp/server.ts","../../src/mcp/tools.ts","../../src/analyzers/git-history.ts","../../src/analyzers/base.ts","../../src/analyzers/index.ts","../../src/utils/git.ts","../../src/mcp/sampler.ts","../../src/snapshot/snapshot.ts","../../src/llm/providers.ts","../../src/llm/config.ts","../../bin/mason-mcp.ts"],"sourcesContent":["import path from \"node:path\";\nimport fg from \"fast-glob\";\n\nconst IGNORE = [\n \"**/node_modules/**\",\n \"**/dist/**\",\n \"**/build/**\",\n \"**/.gradle/**\",\n \"**/target/**\",\n \"**/.git/**\",\n \"**/vendor/**\",\n \"**/__pycache__/**\",\n \"**/venv/**\",\n \"**/.venv/**\",\n \"**/*.min.*\",\n \"**/*.map\",\n];\n\nexport interface TestPair {\n test: string;\n source: string;\n confidence: string;\n}\n\nexport interface TestMapResult {\n totalTestFiles: number;\n paired: TestPair[];\n unmatched: string[];\n}\n\nexport async function buildTestMap(dir: string): Promise<TestMapResult> {\n const rootDir = path.resolve(dir);\n\n // Find all test files\n const testPatterns = [\n \"**/*.test.*\", \"**/*.spec.*\",\n \"**/*Test.kt\", \"**/*Test.java\", \"**/*Tests.kt\", \"**/*Tests.java\",\n \"**/test_*.py\", \"**/*_test.py\",\n \"**/*_test.go\",\n \"**/*Tests.swift\", \"**/*Test.swift\",\n \"**/*_test.rs\",\n ];\n const testFiles = await fg(testPatterns, { cwd: rootDir, ignore: IGNORE });\n\n // Find all source files\n const sourceFiles = await fg(\n \"**/*.{ts,tsx,js,jsx,kt,kts,java,py,go,rs,swift,rb,cs,cpp,dart}\",\n { cwd: rootDir, ignore: IGNORE }\n );\n\n // Build source file index by base name (without extension)\n const sourceByBaseName = new Map<string, string[]>();\n for (const file of sourceFiles) {\n if (testFiles.includes(file)) continue; // Skip test files\n const baseName = path.basename(file).replace(/\\.[^.]+$/, \"\");\n const existing = sourceByBaseName.get(baseName) ?? [];\n existing.push(file);\n sourceByBaseName.set(baseName, existing);\n }\n\n // Match test files to source files by name\n const paired: TestPair[] = [];\n const unmatched: string[] = [];\n\n for (const testFile of testFiles) {\n const testBaseName = path.basename(testFile).replace(/\\.[^.]+$/, \"\");\n\n // Strip test suffixes/prefixes to get the source name\n const sourceName = testBaseName\n .replace(/Test$|Tests$|Spec$|\\.test$|\\.spec$/, \"\")\n .replace(/^test_|_test$/, \"\");\n\n if (!sourceName) {\n unmatched.push(testFile);\n continue;\n }\n\n const candidates = sourceByBaseName.get(sourceName);\n if (candidates && candidates.length > 0) {\n // If multiple candidates, prefer one in a similar directory path\n const testDir = path.dirname(testFile);\n const bestMatch = candidates.reduce((best, candidate) => {\n const candidateDir = path.dirname(candidate);\n const bestDir = path.dirname(best);\n const candidateOverlap = commonSegments(testDir, candidateDir);\n const bestOverlap = commonSegments(testDir, bestDir);\n return candidateOverlap > bestOverlap ? candidate : best;\n });\n\n paired.push({\n test: testFile,\n source: bestMatch,\n confidence: candidates.length === 1 ? \"exact\" : \"best-guess\",\n });\n } else {\n unmatched.push(testFile);\n }\n }\n\n return { totalTestFiles: testFiles.length, paired, unmatched };\n}\n\nfunction commonSegments(pathA: string, pathB: string): number {\n const segsA = pathA.split(\"/\");\n const segsB = pathB.split(\"/\");\n let count = 0;\n for (let i = 0; i < Math.min(segsA.length, segsB.length); i++) {\n if (segsA[i] === segsB[i]) count++;\n else break;\n }\n return count;\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport fg from \"fast-glob\";\n\nconst exec = promisify(execFile);\n\nconst IGNORE = [\n \"**/node_modules/**\",\n \"**/dist/**\",\n \"**/build/**\",\n \"**/.gradle/**\",\n \"**/target/**\",\n \"**/.git/**\",\n \"**/vendor/**\",\n \"**/__pycache__/**\",\n \"**/venv/**\",\n \"**/.venv/**\",\n \"**/generated/**\",\n];\n\nconst SOURCE_EXTENSIONS =\n \"*.{ts,tsx,js,jsx,kt,kts,java,py,go,rs,swift,rb,cs,cpp,c,h,dart,gradle.kts,gradle}\";\n\nexport interface CochangeEntry {\n file: string;\n cochangeRate: number;\n sharedCommits: number;\n}\n\nexport interface ReferenceEntry {\n file: string;\n matches: string[];\n}\n\nexport interface TestEntry {\n file: string;\n confidence: \"exact\" | \"best-guess\";\n}\n\nexport interface ImpactResult {\n targetFiles: string[];\n cochange: CochangeEntry[];\n references: ReferenceEntry[];\n tests: TestEntry[];\n}\n\nexport async function analyzeImpact(\n rootDir: string,\n targetFiles: string[]\n): Promise<ImpactResult> {\n const resolvedRoot = path.resolve(rootDir);\n\n // Resolve target files to full relative paths if only basename given\n const resolvedTargets = await resolveTargetFiles(resolvedRoot, targetFiles);\n\n const [cochange, references, tests] = await Promise.all([\n getCochangeFiles(resolvedRoot, resolvedTargets),\n getReferences(resolvedRoot, resolvedTargets),\n getRelatedTests(resolvedRoot, resolvedTargets),\n ]);\n\n return {\n targetFiles: resolvedTargets,\n cochange,\n references,\n tests,\n };\n}\n\nasync function resolveTargetFiles(\n rootDir: string,\n targets: string[]\n): Promise<string[]> {\n const resolved: string[] = [];\n\n for (const target of targets) {\n // If it contains a path separator, use as-is\n if (target.includes(\"/\")) {\n resolved.push(target);\n continue;\n }\n\n // Otherwise, search for the filename\n const matches = await fg(`**/${target}`, {\n cwd: rootDir,\n ignore: IGNORE,\n });\n\n if (matches.length > 0) {\n resolved.push(matches[0]);\n } else {\n // Try without extension\n const noExt = target.replace(/\\.[^.]+$/, \"\");\n const extMatches = await fg(`**/${noExt}.*`, {\n cwd: rootDir,\n ignore: IGNORE,\n });\n if (extMatches.length > 0) {\n resolved.push(extMatches[0]);\n } else {\n resolved.push(target); // Keep as-is, might still work for grep\n }\n }\n }\n\n return resolved;\n}\n\nasync function getCochangeFiles(\n rootDir: string,\n targetFiles: string[]\n): Promise<CochangeEntry[]> {\n const cochangeCounts = new Map<string, number>();\n let totalTargetCommits = 0;\n\n for (const targetFile of targetFiles) {\n try {\n // Get commits that touched this file (cap at 500)\n const { stdout: commitLog } = await exec(\n \"git\",\n [\"log\", \"--format=%H\", \"-n\", \"500\", \"--\", targetFile],\n { cwd: rootDir, maxBuffer: 5_000_000 }\n );\n\n const commits = commitLog.trim().split(\"\\n\").filter(Boolean);\n totalTargetCommits += commits.length;\n\n if (commits.length === 0) continue;\n\n // For each commit, get the other files that changed\n for (const commit of commits) {\n try {\n const { stdout: filesInCommit } = await exec(\n \"git\",\n [\"diff-tree\", \"--no-commit-id\", \"--name-only\", \"-r\", commit],\n { cwd: rootDir }\n );\n\n const files = filesInCommit.trim().split(\"\\n\").filter(Boolean);\n for (const file of files) {\n if (targetFiles.includes(file)) continue; // Skip the target itself\n cochangeCounts.set(file, (cochangeCounts.get(file) ?? 0) + 1);\n }\n } catch {\n // Skip this commit\n }\n }\n } catch {\n // No git or file not tracked\n }\n }\n\n if (totalTargetCommits === 0) return [];\n\n // Filter to files that co-change >30% of the time, sort by rate\n return [...cochangeCounts.entries()]\n .map(([file, count]) => ({\n file,\n cochangeRate: Math.round((count / totalTargetCommits) * 100) / 100,\n sharedCommits: count,\n }))\n .filter((e) => e.cochangeRate >= 0.3 || e.sharedCommits >= 3)\n .sort((a, b) => b.cochangeRate - a.cochangeRate)\n .slice(0, 20);\n}\n\nasync function getReferences(\n rootDir: string,\n targetFiles: string[]\n): Promise<ReferenceEntry[]> {\n // Extract searchable names from target files\n const searchNames = new Set<string>();\n for (const target of targetFiles) {\n const basename = path.basename(target).replace(/\\.[^.]+$/, \"\");\n searchNames.add(basename);\n }\n\n const allSourceFiles = await fg(`**/${SOURCE_EXTENSIONS}`, {\n cwd: rootDir,\n ignore: IGNORE,\n });\n\n // Exclude target files from search\n const targetSet = new Set(targetFiles);\n const filesToSearch = allSourceFiles.filter((f) => !targetSet.has(f));\n\n const results = new Map<string, Set<string>>();\n\n // Read files in batches to avoid too many open handles\n const batchSize = 50;\n for (let i = 0; i < filesToSearch.length; i += batchSize) {\n const batch = filesToSearch.slice(i, i + batchSize);\n\n await Promise.all(\n batch.map(async (file) => {\n try {\n const content = await fs.readFile(\n path.join(rootDir, file),\n \"utf-8\"\n );\n\n for (const name of searchNames) {\n // Match the name as a word boundary (not part of another word)\n const regex = new RegExp(`\\\\b${escapeRegex(name)}\\\\b`);\n if (regex.test(content)) {\n if (!results.has(file)) results.set(file, new Set());\n results.get(file)!.add(name);\n }\n }\n } catch {\n // Skip unreadable files\n }\n })\n );\n }\n\n return [...results.entries()]\n .map(([file, matches]) => ({\n file,\n matches: [...matches],\n }))\n .sort((a, b) => b.matches.length - a.matches.length);\n}\n\nasync function getRelatedTests(\n rootDir: string,\n targetFiles: string[]\n): Promise<TestEntry[]> {\n const testPatterns = [\n \"**/*.test.*\",\n \"**/*.spec.*\",\n \"**/*Test.kt\",\n \"**/*Test.java\",\n \"**/*Tests.kt\",\n \"**/*Tests.java\",\n \"**/test_*.py\",\n \"**/*_test.py\",\n \"**/*_test.go\",\n \"**/*Tests.swift\",\n \"**/*Test.swift\",\n \"**/*_test.rs\",\n ];\n\n const testFiles = await fg(testPatterns, { cwd: rootDir, ignore: IGNORE });\n const results: TestEntry[] = [];\n\n for (const target of targetFiles) {\n const targetBaseName = path\n .basename(target)\n .replace(/\\.[^.]+$/, \"\");\n\n for (const testFile of testFiles) {\n const testBaseName = path\n .basename(testFile)\n .replace(/\\.[^.]+$/, \"\");\n\n // Strip test suffixes to get the source name\n const sourceName = testBaseName\n .replace(/Test$|Tests$|Spec$|\\.test$|\\.spec$/, \"\")\n .replace(/^test_|_test$/, \"\");\n\n if (sourceName === targetBaseName) {\n results.push({\n file: testFile,\n confidence: \"exact\",\n });\n }\n }\n }\n\n return results;\n}\n\nfunction escapeRegex(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { z } from \"zod\";\nimport {\n analyzeProject,\n fullAnalysis,\n getCodeSamples,\n getImpact,\n getSnapshot,\n saveSnapshotData,\n} from \"./tools.js\";\n\ndeclare const PKG_VERSION: string;\n\nexport function createMcpServer(): McpServer {\n const server = new McpServer(\n {\n name: \"mason\",\n version: PKG_VERSION,\n },\n {\n instructions:\n \"Mason is a context engineering tool. Always call get_snapshot before using Explore agents, Glob, or Grep to understand the codebase. The snapshot is a concept map that maps features and flows to their implementing files — it eliminates the need to search. This applies to ANY question about architecture, features, flows, how things work, cross-feature interactions, or bug investigation. Workflow: 1) Call get_snapshot first. 2) If no snapshot, call full_analysis and then save_snapshot to create one. 3) If the snapshot is stale, tell the user and offer to update it. 4) Use your native file reading tool to read files the snapshot points to. 5) Before modifying a file, call get_impact to check what else might be affected. 6) After making significant changes (new features, refactors, architecture changes), call save_snapshot to update the concept map.\",\n }\n );\n\n server.tool(\n \"full_analysis\",\n \"Run a complete project analysis in one call. Returns git history stats, project structure with file counts, curated code sample previews (~60 lines each), and test-to-source file mapping. This is the recommended starting point — call this first, then read specific files natively for full content.\",\n {\n dir: z\n .string()\n .describe(\"Absolute path to the project root directory\"),\n },\n async ({ dir }) => {\n const result = await fullAnalysis(dir);\n return {\n content: [{ type: \"text\", text: result }],\n };\n }\n );\n\n server.tool(\n \"analyze_project\",\n \"Run git history analysis on a codebase. Returns commit convention patterns, stale directories, and frequently changed files. These are aggregate stats across hundreds of commits that would be expensive to compute manually.\",\n {\n dir: z\n .string()\n .describe(\"Absolute path to the project root directory\"),\n },\n async ({ dir }) => {\n const result = await analyzeProject(dir);\n return {\n content: [{ type: \"text\", text: result }],\n };\n }\n );\n\n server.tool(\n \"get_code_samples\",\n \"Get previews (first ~60 lines) of representative source files from the codebase. Includes entry points, config files, hot files (frequently changed), test examples, and one file per directory for breadth. Read files natively for full content.\",\n {\n dir: z\n .string()\n .describe(\"Absolute path to the project root directory\"),\n count: z\n .number()\n .optional()\n .default(15)\n .describe(\"Maximum number of files to sample (default: 15)\"),\n },\n async ({ dir, count }) => {\n const result = await getCodeSamples(dir, count);\n return {\n content: [{ type: \"text\", text: result }],\n };\n }\n );\n\n server.tool(\n \"get_snapshot\",\n \"Get the project's concept map — a lookup table from features and flows to the files that implement them. Use this to jump straight to relevant files instead of exploring. Example: 'home screen' → [HomeScreen.kt, HomeViewModel.kt, HomeModule.kt]. If stale, run 'mason snapshot-update' to refresh.\",\n {\n dir: z\n .string()\n .describe(\"Absolute path to the project root directory\"),\n },\n async ({ dir }) => {\n const result = await getSnapshot(dir);\n return {\n content: [{ type: \"text\", text: result }],\n };\n }\n );\n\n server.tool(\n \"save_snapshot\",\n \"Save a concept-to-files map as a persistent project snapshot. Maps feature names and data flows to the files that implement them. Persists across conversations — future sessions can call get_snapshot to instantly find relevant files. No API key needed — you are the LLM generating the map.\",\n {\n dir: z\n .string()\n .describe(\"Absolute path to the project root directory\"),\n features: z\n .record(\n z.object({\n description: z.string().describe(\"One-line description of the feature\"),\n files: z.array(z.string()).describe(\"File paths that implement this feature\"),\n tests: z.array(z.string()).optional().describe(\"Test file paths for this feature\"),\n })\n )\n .describe(\"Map of feature names to their implementing files\"),\n flows: z\n .record(\n z.object({\n description: z.string().describe(\"One-line description of the flow\"),\n chain: z.array(z.string()).describe(\"Ordered list of file paths showing data/call flow\"),\n })\n )\n .describe(\"Map of flow names to ordered file chains\"),\n },\n async ({ dir, features, flows }) => {\n const result = await saveSnapshotData(dir, features, flows);\n return {\n content: [{ type: \"text\", text: result }],\n };\n }\n );\n\n server.tool(\n \"get_impact\",\n \"Analyze the impact of changing specific files. Returns three signals: git co-change (files that historically change together), references (files that mention the target by name), and related tests. Use this before editing a file to understand what else might need updating.\",\n {\n dir: z\n .string()\n .describe(\"Absolute path to the project root directory\"),\n files: z\n .array(z.string())\n .describe(\"File paths or names to analyze (e.g., ['WeatherRepository.kt'] or ['src/services/auth.ts'])\"),\n },\n async ({ dir, files }) => {\n const result = await getImpact(dir, files);\n return {\n content: [{ type: \"text\", text: result }],\n };\n }\n );\n\n return server;\n}\n\nexport async function startMcpServer(): Promise<void> {\n const server = createMcpServer();\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport fg from \"fast-glob\";\n\nconst exec = promisify(execFile);\nimport { runAll } from \"../analyzers/index.js\";\nimport { isGitRepo } from \"../utils/git.js\";\nimport { sampleFiles } from \"./sampler.js\";\nimport {\n loadSnapshot,\n saveSnapshot,\n getCurrentGitHash,\n} from \"../snapshot/snapshot.js\";\nimport type { Snapshot } from \"../snapshot/snapshot.js\";\nimport type { AnalyzerContext } from \"../types.js\";\n\nconst IGNORE = [\n \"**/node_modules/**\",\n \"**/dist/**\",\n \"**/build/**\",\n \"**/.gradle/**\",\n \"**/target/**\",\n \"**/.git/**\",\n \"**/vendor/**\",\n \"**/__pycache__/**\",\n \"**/venv/**\",\n \"**/.venv/**\",\n \"**/*.min.*\",\n \"**/*.map\",\n];\n\nasync function buildContext(dir: string): Promise<AnalyzerContext> {\n return {\n rootDir: dir,\n gitAvailable: await isGitRepo(dir),\n };\n}\n\nexport async function analyzeProject(dir: string): Promise<string> {\n const rootDir = path.resolve(dir);\n const context = await buildContext(rootDir);\n const results = await runAll(context);\n\n // Lightweight project snapshot — pure file existence checks, no parsing\n const projectSnapshot = await detectProjectSnapshot(rootDir);\n\n const output = {\n project: projectSnapshot,\n analyzers: results.map((r) => ({\n name: r.analyzer,\n durationMs: r.durationMs,\n findings: r.findings.map((f) => ({\n category: f.category,\n confidence: f.confidence,\n summary: f.summary,\n evidence: f.evidence,\n suggestedRule: f.ruleCandidate,\n })),\n gaps: r.gaps.map((g) => ({\n question: g.question,\n context: g.context,\n })),\n })),\n };\n\n return JSON.stringify(output, null, 2);\n}\n\nasync function detectProjectSnapshot(rootDir: string): Promise<Record<string, unknown>> {\n // Build config files present (what exists, not what's in them)\n const buildFiles = [\n \"package.json\", \"tsconfig.json\",\n \"build.gradle.kts\", \"build.gradle\", \"settings.gradle.kts\", \"settings.gradle\",\n \"gradle/libs.versions.toml\",\n \"Cargo.toml\", \"go.mod\", \"go.sum\",\n \"pyproject.toml\", \"setup.py\", \"requirements.txt\", \"Pipfile\",\n \"Gemfile\", \"Package.swift\",\n \"Makefile\", \"CMakeLists.txt\",\n \"Dockerfile\", \"docker-compose.yml\", \"docker-compose.yaml\",\n \".github/workflows\", \".gitlab-ci.yml\", \"Jenkinsfile\",\n ];\n\n const present: string[] = [];\n for (const file of buildFiles) {\n try {\n await fs.access(path.join(rootDir, file));\n present.push(file);\n } catch {\n // Not found\n }\n }\n\n // Test directories and file counts\n const testDirs = [\n \"test\", \"tests\", \"__tests__\", \"spec\",\n \"src/test\", \"src/tests\",\n \"**/src/test\", \"**/src/androidTest\", \"**/src/iosTest\",\n ];\n const testInfo: Record<string, number> = {};\n for (const pattern of testDirs) {\n const files = await fg(`${pattern}/**/*`, {\n cwd: rootDir,\n ignore: IGNORE,\n onlyFiles: true,\n });\n if (files.length > 0) {\n testInfo[pattern] = files.length;\n }\n }\n\n // Also count test files by naming convention\n const testFilePatterns = [\n { pattern: \"**/*.test.*\", label: \"*.test.*\" },\n { pattern: \"**/*.spec.*\", label: \"*.spec.*\" },\n { pattern: \"**/*Test.kt\", label: \"*Test.kt\" },\n { pattern: \"**/*Test.java\", label: \"*Test.java\" },\n { pattern: \"**/test_*.py\", label: \"test_*.py\" },\n { pattern: \"**/*_test.go\", label: \"*_test.go\" },\n { pattern: \"**/*Tests.swift\", label: \"*Tests.swift\" },\n { pattern: \"**/*_test.rs\", label: \"*_test.rs\" },\n ];\n for (const { pattern, label } of testFilePatterns) {\n const files = await fg(pattern, { cwd: rootDir, ignore: IGNORE });\n if (files.length > 0) {\n testInfo[label] = files.length;\n }\n }\n\n // Source file counts by extension\n const sourceFiles = await fg(\"**/*.{ts,tsx,js,jsx,kt,kts,java,py,go,rs,swift,rb,cs,cpp,c,dart}\", {\n cwd: rootDir,\n ignore: IGNORE,\n });\n const fileCounts: Record<string, number> = {};\n for (const file of sourceFiles) {\n const ext = path.extname(file).slice(1);\n fileCounts[ext] = (fileCounts[ext] ?? 0) + 1;\n }\n\n return {\n configFilesPresent: present,\n sourceFileCounts: fileCounts,\n totalSourceFiles: sourceFiles.length,\n testInfo: Object.keys(testInfo).length > 0 ? testInfo : undefined,\n };\n}\n\nexport async function getCodeSamples(\n dir: string,\n count: number = 15\n): Promise<string> {\n const rootDir = path.resolve(dir);\n const samples = await sampleFiles(rootDir, count);\n\n const output = {\n note: \"These are previews (first ~60 lines). Use get_file_content to read the full file if needed.\",\n files: samples.map((s) => ({\n path: s.path,\n reason: s.reason,\n totalLines: s.totalLines,\n sizeBytes: s.sizeBytes,\n preview: s.preview,\n })),\n };\n\n return JSON.stringify(output, null, 2);\n}\n\nexport async function getProjectStructure(dir: string): Promise<string> {\n const rootDir = path.resolve(dir);\n\n // Get all files\n const allFiles = await fg(\"**/*\", {\n cwd: rootDir,\n ignore: IGNORE,\n onlyFiles: true,\n });\n\n // Build directory summary with file counts and extension breakdown\n const dirInfo = new Map<\n string,\n { fileCount: number; extensions: Map<string, number> }\n >();\n\n for (const file of allFiles) {\n const parts = file.split(\"/\");\n // Track up to 2 levels deep\n for (let depth = 1; depth <= Math.min(parts.length, 2); depth++) {\n const dirPath = parts.slice(0, depth).join(\"/\");\n if (!dirInfo.has(dirPath)) {\n dirInfo.set(dirPath, { fileCount: 0, extensions: new Map() });\n }\n const info = dirInfo.get(dirPath)!;\n info.fileCount++;\n const ext = path.extname(file).slice(1);\n if (ext) {\n info.extensions.set(ext, (info.extensions.get(ext) ?? 0) + 1);\n }\n }\n }\n\n // Format directories sorted by path\n const directories = [...dirInfo.entries()]\n .sort((a, b) => a[0].localeCompare(b[0]))\n .map(([dirPath, info]) => {\n const extensions: Record<string, number> = {};\n for (const [ext, count] of info.extensions) {\n extensions[ext] = count;\n }\n return { path: dirPath, fileCount: info.fileCount, extensions };\n });\n\n // Top-level files\n const topLevelFiles = allFiles.filter((f) => !f.includes(\"/\"));\n\n const output = {\n totalFiles: allFiles.length,\n topLevelFiles,\n directories,\n };\n\n return JSON.stringify(output, null, 2);\n}\n\nexport async function getTestMap(dir: string): Promise<string> {\n const { buildTestMap } = await import(\"../test-map.js\");\n const result = await buildTestMap(dir);\n return JSON.stringify(result, null, 2);\n}\n\nexport async function getSnapshot(dir: string): Promise<string> {\n const rootDir = path.resolve(dir);\n const snapshot = await loadSnapshot(rootDir);\n\n if (!snapshot) {\n return JSON.stringify({\n exists: false,\n message:\n \"No concept map found. Run 'mason snapshot' to create one, or call save_snapshot with features and flows.\",\n });\n }\n\n // Check staleness\n const currentHash = await getCurrentGitHash(rootDir);\n const isStale = snapshot.gitHash !== currentHash && snapshot.gitHash !== \"unknown\";\n\n // Return compact format: feature/flow names -> file lists only.\n // Descriptions and metadata stay in the full snapshot on disk.\n // Deduplicate files that appear in multiple features.\n const seenFiles = new Set<string>();\n const compactFeatures: Record<string, { files: string[]; tests?: string[] }> = {};\n for (const [name, feat] of Object.entries(snapshot.features)) {\n const unique = feat.files.filter((f) => !seenFiles.has(f));\n if (unique.length === 0) continue; // Skip fully duplicate features\n for (const f of unique) seenFiles.add(f);\n const entry: { files: string[]; tests?: string[] } = { files: unique };\n if (feat.tests && feat.tests.length > 0) {\n entry.tests = feat.tests;\n }\n compactFeatures[name] = entry;\n }\n\n const compactFlows: Record<string, string[]> = {};\n for (const [name, flow] of Object.entries(snapshot.flows)) {\n compactFlows[name] = flow.chain; // Flows keep all files (order matters)\n }\n\n const output: Record<string, unknown> = {\n exists: true,\n updatedAt: snapshot.updatedAt,\n features: compactFeatures,\n flows: compactFlows,\n stale: isStale,\n };\n\n if (isStale) {\n output.message =\n \"Snapshot is behind HEAD. Run 'mason snapshot-update' or call save_snapshot to refresh.\";\n }\n\n return JSON.stringify(output);\n}\n\nexport async function fullAnalysis(dir: string): Promise<string> {\n const rootDir = path.resolve(dir);\n\n const [analysis, structure, samples, testMap, snapshot] = await Promise.all([\n analyzeProject(dir),\n getProjectStructure(dir),\n getCodeSamples(dir, 25),\n getTestMap(dir),\n loadSnapshot(rootDir),\n ]);\n\n const output: Record<string, unknown> = {\n note: \"Full project analysis. Code samples are previews (~60 lines). Use get_file_content to read any file in full.\",\n analysis: JSON.parse(analysis),\n structure: JSON.parse(structure),\n codeSamples: JSON.parse(samples),\n testMap: JSON.parse(testMap),\n };\n\n if (snapshot) {\n output.conceptMap = {\n updatedAt: snapshot.updatedAt,\n features: snapshot.features,\n flows: snapshot.flows,\n };\n output.note =\n \"Full project analysis with concept map. The concept map shows which files implement each feature and how data flows through them. Use it to jump straight to relevant files instead of exploring. Use get_file_content to read specific files.\";\n }\n\n return JSON.stringify(output, null, 2);\n}\n\nexport async function saveSnapshotData(\n dir: string,\n features: Record<string, { description: string; files: string[]; tests?: string[] }>,\n flows: Record<string, { description: string; chain: string[] }>\n): Promise<string> {\n const rootDir = path.resolve(dir);\n const gitHash = await getCurrentGitHash(rootDir);\n const now = new Date().toISOString();\n\n const existing = await loadSnapshot(rootDir);\n\n if (existing) {\n // Merge: overwrite matching features/flows, keep the rest\n existing.features = { ...existing.features, ...features };\n existing.flows = { ...existing.flows, ...flows };\n existing.updatedAt = now;\n existing.gitHash = gitHash;\n await saveSnapshot(rootDir, existing);\n return JSON.stringify({\n status: \"updated\",\n features: Object.keys(existing.features).length,\n flows: Object.keys(existing.flows).length,\n });\n }\n\n const snapshot: Snapshot = {\n version: 2,\n createdAt: now,\n updatedAt: now,\n gitHash,\n features,\n flows,\n };\n\n await saveSnapshot(rootDir, snapshot);\n return JSON.stringify({\n status: \"created\",\n features: Object.keys(features).length,\n flows: Object.keys(flows).length,\n });\n}\n\nexport async function configureProject(\n dir: string,\n config: {\n patterns?: string[];\n alwaysInclude?: string[];\n ignore?: string[];\n }\n): Promise<string> {\n const rootDir = path.resolve(dir);\n const configDir = path.join(rootDir, \".mason\");\n const configPath = path.join(configDir, \"config.json\");\n\n // Load existing config and merge\n let existing: Record<string, unknown> = {};\n try {\n const raw = await fs.readFile(configPath, \"utf-8\");\n existing = JSON.parse(raw);\n } catch {\n // No existing config\n }\n\n if (config.patterns) existing.patterns = config.patterns;\n if (config.alwaysInclude) existing.alwaysInclude = config.alwaysInclude;\n if (config.ignore) existing.ignore = config.ignore;\n\n await fs.mkdir(configDir, { recursive: true });\n await fs.writeFile(configPath, JSON.stringify(existing, null, 2), \"utf-8\");\n\n return JSON.stringify({\n status: \"saved\",\n path: configPath,\n config: existing,\n });\n}\n\nexport async function getImpact(\n dir: string,\n files: string[]\n): Promise<string> {\n const { analyzeImpact } = await import(\"../impact/impact.js\");\n const rootDir = path.resolve(dir);\n const result = await analyzeImpact(rootDir, files);\n return JSON.stringify(result, null, 2);\n}\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { BaseAnalyzer } from \"./base.js\";\nimport type { AnalyzerContext, AnalyzerResult, Finding, Gap } from \"../types.js\";\n\nconst exec = promisify(execFile);\n\nexport class GitHistoryAnalyzer extends BaseAnalyzer {\n name = \"git-history\";\n\n async analyze(context: AnalyzerContext): Promise<AnalyzerResult> {\n const startTime = Date.now();\n const findings: Finding[] = [];\n const gaps: Gap[] = [];\n\n if (!context.gitAvailable) {\n return this.createResult([], [], startTime);\n }\n\n const [staleFindings, staleGaps] = await this.findStaleDirectories(context);\n findings.push(...staleFindings);\n gaps.push(...staleGaps);\n\n const hotFindings = await this.findHotFiles(context);\n findings.push(...hotFindings);\n\n const commitFindings = await this.analyzeCommitPatterns(context);\n findings.push(...commitFindings);\n\n return this.createResult(findings, gaps, startTime);\n }\n\n private async git(\n args: string[],\n cwd: string\n ): Promise<string> {\n try {\n const { stdout } = await exec(\"git\", args, { cwd, maxBuffer: 10_000_000 });\n return stdout.trim();\n } catch {\n return \"\";\n }\n }\n\n private async findStaleDirectories(\n context: AnalyzerContext\n ): Promise<[Finding[], Gap[]]> {\n const findings: Finding[] = [];\n const gaps: Gap[] = [];\n\n // Get top-level directories with their last commit date\n const output = await this.git(\n [\"log\", \"--all\", \"--format=%ci\", \"--name-only\", \"--diff-filter=AMCR\", \"-n\", \"500\"],\n context.rootDir\n );\n\n if (!output) return [findings, gaps];\n\n const dirLastTouch = new Map<string, Date>();\n let currentDate: Date | null = null;\n\n for (const line of output.split(\"\\n\")) {\n if (!line) continue;\n if (/^\\d{4}-\\d{2}-\\d{2}/.test(line)) {\n currentDate = new Date(line);\n } else if (currentDate) {\n const topDir = line.split(\"/\")[0];\n if (\n topDir &&\n !topDir.startsWith(\".\") &&\n !topDir.includes(\"node_modules\")\n ) {\n const existing = dirLastTouch.get(topDir);\n if (!existing || currentDate > existing) {\n dirLastTouch.set(topDir, currentDate);\n }\n }\n }\n }\n\n const sixMonthsAgo = new Date();\n sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 6);\n\n for (const [dir, lastTouch] of dirLastTouch) {\n if (lastTouch < sixMonthsAgo) {\n const monthsStale = Math.floor(\n (Date.now() - lastTouch.getTime()) / (1000 * 60 * 60 * 24 * 30)\n );\n findings.push(\n this.createFinding({\n category: \"risk\",\n confidence: 0.7,\n summary: `Directory \"${dir}\" hasn't been modified in ${monthsStale} months`,\n evidence: [\n { filePath: dir, detail: `Last commit: ${lastTouch.toISOString().split(\"T\")[0]}` },\n ],\n ruleCandidate: `Do not refactor or modify files in \"${dir}/\" unless explicitly asked — this area has been stable for ${monthsStale} months and may be legacy code.`,\n })\n );\n gaps.push({\n analyzer: this.name,\n question: `Directory \"${dir}\" hasn't been touched in ${monthsStale} months. Is it deprecated, stable, or legacy?`,\n context: `Last modified: ${lastTouch.toISOString().split(\"T\")[0]}`,\n answerKey: `stale-dir-${dir}`,\n });\n }\n }\n\n return [findings, gaps];\n }\n\n private async findHotFiles(context: AnalyzerContext): Promise<Finding[]> {\n const findings: Finding[] = [];\n\n // Most frequently changed files in the last 3 months\n const output = await this.git(\n [\"log\", \"--since=3 months ago\", \"--format=\", \"--name-only\"],\n context.rootDir\n );\n\n if (!output) return findings;\n\n const fileCounts = new Map<string, number>();\n for (const line of output.split(\"\\n\")) {\n if (!line || line.startsWith(\".\") || line.includes(\"node_modules\")) continue;\n fileCounts.set(line, (fileCounts.get(line) ?? 0) + 1);\n }\n\n const sorted = [...fileCounts.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, 10);\n\n if (sorted.length > 0 && sorted[0][1] >= 5) {\n const hotFiles = sorted.filter(([, count]) => count >= 5);\n if (hotFiles.length > 0) {\n findings.push(\n this.createFinding({\n category: \"risk\",\n confidence: 0.8,\n summary: `${hotFiles.length} files changed frequently in the last 3 months`,\n evidence: hotFiles.map(([file, count]) => ({\n filePath: file,\n detail: `${count} commits`,\n })),\n ruleCandidate: `These files change frequently and are high-risk for conflicts: ${hotFiles.map(([f]) => f).join(\", \")}. Take extra care when modifying them.`,\n })\n );\n }\n }\n\n return findings;\n }\n\n private async analyzeCommitPatterns(\n context: AnalyzerContext\n ): Promise<Finding[]> {\n const findings: Finding[] = [];\n\n const output = await this.git(\n [\"log\", \"--format=%s\", \"-n\", \"100\"],\n context.rootDir\n );\n\n if (!output) return findings;\n\n const messages = output.split(\"\\n\").filter(Boolean);\n\n // Check for conventional commits\n const conventionalPattern = /^(feat|fix|chore|docs|style|refactor|test|perf|ci|build|revert)(\\(.+\\))?:/;\n const conventionalCount = messages.filter((m) =>\n conventionalPattern.test(m)\n ).length;\n const conventionalRatio = conventionalCount / messages.length;\n\n if (conventionalRatio > 0.5) {\n findings.push(\n this.createFinding({\n category: \"convention\",\n confidence: Math.min(conventionalRatio + 0.1, 1),\n summary: `${Math.round(conventionalRatio * 100)}% of recent commits use conventional commit format`,\n evidence: [\n {\n filePath: \".git\",\n detail: `${conventionalCount} of ${messages.length} commits match`,\n },\n ],\n ruleCandidate:\n \"Use conventional commit format: type(scope): description (e.g., feat(auth): add login endpoint)\",\n })\n );\n }\n\n // Check for ticket/issue references\n const ticketPattern = /[A-Z]+-\\d+|#\\d+/;\n const ticketCount = messages.filter((m) => ticketPattern.test(m)).length;\n const ticketRatio = ticketCount / messages.length;\n\n if (ticketRatio > 0.3) {\n findings.push(\n this.createFinding({\n category: \"convention\",\n confidence: ticketRatio,\n summary: `${Math.round(ticketRatio * 100)}% of commits reference issue/ticket IDs`,\n evidence: [\n {\n filePath: \".git\",\n detail: `${ticketCount} of ${messages.length} commits have ticket refs`,\n },\n ],\n ruleCandidate:\n \"Include issue/ticket references in commit messages when applicable.\",\n })\n );\n }\n\n return findings;\n }\n}\n","import fs from \"node:fs/promises\";\nimport fg from \"fast-glob\";\nimport type {\n AnalyzerContext,\n AnalyzerResult,\n Finding,\n FindingCategory,\n} from \"../types.js\";\n\nexport abstract class BaseAnalyzer {\n abstract name: string;\n abstract analyze(context: AnalyzerContext): Promise<AnalyzerResult>;\n\n protected async findFiles(\n patterns: string[],\n root: string\n ): Promise<string[]> {\n return fg(patterns, {\n cwd: root,\n ignore: [\"**/node_modules/**\", \"**/dist/**\", \"**/.git/**\"],\n absolute: true,\n });\n }\n\n protected async readFile(filePath: string): Promise<string> {\n return fs.readFile(filePath, \"utf-8\");\n }\n\n protected createFinding(partial: {\n category: FindingCategory;\n confidence: number;\n summary: string;\n evidence?: Finding[\"evidence\"];\n ruleCandidate?: string | null;\n }): Finding {\n return {\n analyzer: this.name,\n category: partial.category,\n confidence: partial.confidence,\n summary: partial.summary,\n evidence: partial.evidence ?? [],\n ruleCandidate: partial.ruleCandidate ?? null,\n };\n }\n\n protected createResult(\n findings: Finding[],\n gaps: AnalyzerResult[\"gaps\"],\n startTime: number\n ): AnalyzerResult {\n return {\n analyzer: this.name,\n findings,\n gaps,\n durationMs: Date.now() - startTime,\n };\n }\n}\n","import type { AnalyzerContext, AnalyzerResult } from \"../types.js\";\nimport type { BaseAnalyzer } from \"./base.js\";\nimport { GitHistoryAnalyzer } from \"./git-history.js\";\n\nconst analyzers: BaseAnalyzer[] = [new GitHistoryAnalyzer()];\n\nexport async function runAll(\n context: AnalyzerContext\n): Promise<AnalyzerResult[]> {\n return Promise.all(analyzers.map((a) => a.analyze(context)));\n}\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nconst exec = promisify(execFile);\n\nexport async function isGitRepo(dir: string): Promise<boolean> {\n try {\n await exec(\"git\", [\"rev-parse\", \"--git-dir\"], { cwd: dir });\n return true;\n } catch {\n return false;\n }\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport fg from \"fast-glob\";\n\nconst exec = promisify(execFile);\n\nconst SOURCE_EXTENSIONS = [\n \"ts\", \"tsx\", \"js\", \"jsx\", \"mts\", \"mjs\",\n \"kt\", \"kts\", \"java\",\n \"py\",\n \"go\",\n \"rs\",\n \"swift\",\n \"rb\",\n \"cs\", \"cpp\", \"c\", \"h\",\n \"dart\",\n];\n\nconst CONFIG_FILES = [\n // Build & project config\n \"package.json\",\n \"tsconfig.json\",\n \"build.gradle.kts\",\n \"build.gradle\",\n \"settings.gradle.kts\",\n \"settings.gradle\",\n \"Cargo.toml\",\n \"go.mod\",\n \"pyproject.toml\",\n \"Gemfile\",\n \"*.csproj\",\n // Version catalogs & dependency locks\n \"gradle/libs.versions.toml\",\n // Code quality & formatting\n \".editorconfig\",\n \".eslintrc.*\",\n \"eslint.config.*\",\n \".prettierrc\",\n \"rustfmt.toml\",\n \".swiftlint.yml\",\n // CI/CD\n \".github/workflows/*.yml\",\n \".gitlab-ci.yml\",\n \"Jenkinsfile\",\n // Containerization\n \"Dockerfile\",\n \"docker-compose.yml\",\n \"docker-compose.yaml\",\n];\n\nconst ENTRY_POINT_PATTERNS = [\n \"src/main.*\",\n \"src/index.*\",\n \"src/app.*\",\n \"main.*\",\n \"index.*\",\n \"app.*\",\n \"App.*\",\n \"**/Main.kt\",\n \"**/Application.kt\",\n \"**/main.py\",\n \"**/main.go\",\n \"**/main.rs\",\n \"**/lib.rs\",\n \"**/Program.cs\",\n];\n\n// Filename patterns that reveal architectural patterns and conventions.\n// These are language-agnostic — the suffixes appear across ecosystems.\n// Ordered by architectural importance — most distinctive patterns first.\nconst ARCHITECTURAL_PATTERNS = [\n // State/data flow\n { glob: \"**/*ViewModel.*\", category: \"state\", reason: \"viewmodel (state management)\" },\n { glob: \"**/*Store.*\", category: \"state\", reason: \"store (state management)\" },\n { glob: \"**/*Reducer.*\", category: \"state\", reason: \"reducer (state management)\" },\n // Data layer — interface\n { glob: \"**/*Repository.*\", category: \"data-interface\", reason: \"repository interface (data layer contract)\" },\n { glob: \"**/*Dao.*\", category: \"data-interface\", reason: \"DAO (data access)\" },\n { glob: \"**/*DataSource.*\", category: \"data-interface\", reason: \"data source\" },\n // Data layer — implementation (where actual patterns live: mappers, retry, IO dispatchers)\n { glob: \"**/*RepositoryImpl.*\", category: \"data-impl\", reason: \"repository implementation (data layer patterns)\" },\n { glob: \"**/*ServiceImpl.*\", category: \"data-impl\", reason: \"service implementation\" },\n { glob: \"**/*Impl.*\", category: \"data-impl\", reason: \"implementation (concrete patterns)\" },\n // Data transformation\n { glob: \"**/*Mapper.*\", category: \"transform\", reason: \"mapper (data transformation)\" },\n { glob: \"**/*Converter.*\", category: \"transform\", reason: \"converter (data transformation)\" },\n { glob: \"**/*Adapter.*\", category: \"transform\", reason: \"adapter (interface adaptation)\" },\n // Dependency injection / wiring\n { glob: \"**/*Module.*\", category: \"di\", reason: \"module (DI/wiring)\" },\n { glob: \"**/*Provider.*\", category: \"di\", reason: \"provider (DI/wiring)\" },\n { glob: \"**/*Container.*\", category: \"di\", reason: \"container (DI/wiring)\" },\n { glob: \"**/*Factory.*\", category: \"di\", reason: \"factory (object creation)\" },\n // API / network\n { glob: \"**/*Service.*\", category: \"api\", reason: \"service (business/API layer)\" },\n { glob: \"**/*Client.*\", category: \"api\", reason: \"client (API/network layer)\" },\n { glob: \"**/*Api.*\", category: \"api\", reason: \"API interface definition\" },\n // Interface contracts / protocols\n { glob: \"**/*Interface.*\", category: \"contract\", reason: \"interface definition\" },\n { glob: \"**/*Protocol.*\", category: \"contract\", reason: \"protocol definition\" },\n { glob: \"**/*Trait.*\", category: \"contract\", reason: \"trait definition\" },\n // Routing / navigation\n { glob: \"**/*Router.*\", category: \"routing\", reason: \"router (navigation/routing)\" },\n { glob: \"**/*Route.*\", category: \"routing\", reason: \"route definition\" },\n { glob: \"**/*NavHost.*\", category: \"routing\", reason: \"navigation host\" },\n { glob: \"**/*Controller.*\", category: \"routing\", reason: \"controller (request handling)\" },\n { glob: \"**/*Handler.*\", category: \"routing\", reason: \"handler (request handling)\" },\n // Middleware / interceptors\n { glob: \"**/*Middleware.*\", category: \"middleware\", reason: \"middleware (request pipeline)\" },\n { glob: \"**/*Interceptor.*\", category: \"middleware\", reason: \"interceptor (cross-cutting)\" },\n { glob: \"**/*Plugin.*\", category: \"middleware\", reason: \"plugin (extensibility)\" },\n // Models / types\n { glob: \"**/*Model.*\", category: \"model\", reason: \"model (domain types)\" },\n { glob: \"**/*Entity.*\", category: \"model\", reason: \"entity (persistence types)\" },\n { glob: \"**/*Dto.*\", category: \"model\", reason: \"DTO (data transfer types)\" },\n { glob: \"**/*Schema.*\", category: \"model\", reason: \"schema (data validation)\" },\n // Use cases / commands\n { glob: \"**/*UseCase.*\", category: \"usecase\", reason: \"use case (business logic)\" },\n { glob: \"**/*Interactor.*\", category: \"usecase\", reason: \"interactor (business logic)\" },\n { glob: \"**/*Command.*\", category: \"usecase\", reason: \"command (CQRS pattern)\" },\n];\n\nconst IGNORE_PATTERNS = [\n \"**/node_modules/**\",\n \"**/dist/**\",\n \"**/build/**\",\n \"**/.gradle/**\",\n \"**/target/**\",\n \"**/.git/**\",\n \"**/vendor/**\",\n \"**/__pycache__/**\",\n \"**/venv/**\",\n \"**/.venv/**\",\n \"**/*.min.*\",\n \"**/*.map\",\n \"**/package-lock.json\",\n \"**/yarn.lock\",\n \"**/pnpm-lock.yaml\",\n \"**/*.lock\",\n \"**/*.generated.*\",\n \"**/generated/**\",\n \"**/R.java\",\n \"**/BuildConfig.java\",\n];\n\nconst PREVIEW_LINES = 60;\n\nexport interface ProjectConfig {\n patterns?: string[];\n alwaysInclude?: string[];\n ignore?: string[];\n}\n\nexport interface SampledFile {\n path: string;\n preview: string;\n totalLines: number;\n sizeBytes: number;\n reason: string;\n}\n\nasync function loadProjectConfig(\n rootDir: string\n): Promise<ProjectConfig> {\n try {\n const raw = await fs.readFile(\n path.join(rootDir, \".mason\", \"config.json\"),\n \"utf-8\"\n );\n return JSON.parse(raw);\n } catch {\n return {};\n }\n}\n\nexport async function sampleFiles(\n rootDir: string,\n maxFiles: number = 25\n): Promise<SampledFile[]> {\n const selected = new Map<string, string>(); // path -> reason\n const projectConfig = await loadProjectConfig(rootDir);\n const ignorePatterns = [...IGNORE_PATTERNS, ...(projectConfig.ignore ?? [])];\n\n // 0. Always-include files from project config (highest priority)\n for (const filePath of projectConfig.alwaysInclude ?? []) {\n if (selected.size >= maxFiles) break;\n // Validate path stays within project root\n const resolvedPath = path.resolve(rootDir, filePath);\n if (!resolvedPath.startsWith(path.resolve(rootDir))) continue;\n selected.set(filePath, \"always-include (project config)\");\n }\n\n // 1. Config files (cap at 5)\n let configCount = 0;\n for (const pattern of CONFIG_FILES) {\n if (configCount >= 5) break;\n const matches = await fg(pattern, {\n cwd: rootDir,\n ignore: ignorePatterns,\n deep: 3,\n });\n for (const match of matches) {\n if (configCount >= 5 || selected.size >= maxFiles) break;\n selected.set(match, \"config file\");\n configCount++;\n }\n }\n\n // 2. Module build/config files — build files from subdirectories reveal dependency graph\n const moduleBuildPatterns = [\n // Gradle\n \"**/build.gradle.kts\",\n \"**/build.gradle\",\n // Cargo workspace members\n \"**/Cargo.toml\",\n // Node workspaces\n \"**/package.json\",\n // Go sub-modules\n \"**/go.mod\",\n ];\n let moduleBuildCount = 0;\n for (const pattern of moduleBuildPatterns) {\n const matches = await fg(pattern, {\n cwd: rootDir,\n ignore: ignorePatterns,\n deep: 4,\n });\n // Skip root-level files (already captured as config)\n const subMatches = matches.filter((m) => m.includes(\"/\"));\n for (const match of subMatches) {\n if (moduleBuildCount >= 4 || selected.size >= maxFiles) break;\n if (!selected.has(match)) {\n selected.set(match, \"module build file (reveals dependency graph)\");\n moduleBuildCount++;\n }\n }\n if (moduleBuildCount >= 4) break;\n }\n\n // 3. Entry points (cap at 2)\n let entryCount = 0;\n for (const pattern of ENTRY_POINT_PATTERNS) {\n if (entryCount >= 2) break;\n const matches = await fg(pattern, {\n cwd: rootDir,\n ignore: ignorePatterns,\n deep: 5,\n });\n for (const match of matches) {\n if (entryCount >= 2 || selected.size >= maxFiles) break;\n if (!selected.has(match)) {\n selected.set(match, \"entry point\");\n entryCount++;\n }\n }\n }\n\n // 4. Hot files from git (up to 5)\n try {\n const { stdout } = await exec(\n \"git\",\n [\"log\", \"--since=3 months ago\", \"--format=\", \"--name-only\"],\n { cwd: rootDir, maxBuffer: 5_000_000 }\n );\n\n const fileCounts = new Map<string, number>();\n for (const line of stdout.split(\"\\n\")) {\n if (!line) continue;\n if (\n line.includes(\"node_modules\") ||\n line.includes(\"/build/\") ||\n line.includes(\".gradle\") ||\n line.includes(\"/generated/\")\n )\n continue;\n const ext = path.extname(line).slice(1);\n if (!SOURCE_EXTENSIONS.includes(ext)) continue;\n fileCounts.set(line, (fileCounts.get(line) ?? 0) + 1);\n }\n\n const hotFiles = [...fileCounts.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, 5);\n\n for (const [file, count] of hotFiles) {\n if (selected.size >= maxFiles) break;\n if (!selected.has(file)) {\n selected.set(file, `frequently changed (${count} commits in 3 months)`);\n }\n }\n } catch {\n // No git\n }\n\n // 5. Architectural pattern files — one per category (cap at 8)\n const seenCategories = new Set<string>();\n let patternCount = 0;\n for (const pattern of ARCHITECTURAL_PATTERNS) {\n if (patternCount >= 8 || selected.size >= maxFiles) break;\n if (seenCategories.has(pattern.category)) continue;\n\n const matches = await fg(pattern.glob, {\n cwd: rootDir,\n ignore: ignorePatterns,\n });\n\n if (matches.length > 0) {\n for (const match of matches) {\n if (!selected.has(match)) {\n selected.set(match, pattern.reason);\n seenCategories.add(pattern.category);\n patternCount++;\n break;\n }\n }\n }\n }\n\n // 5b. Custom patterns from project config\n for (const customGlob of projectConfig.patterns ?? []) {\n if (selected.size >= maxFiles) break;\n const matches = await fg(customGlob, {\n cwd: rootDir,\n ignore: ignorePatterns,\n });\n for (const match of matches) {\n if (selected.size >= maxFiles) break;\n if (!selected.has(match)) {\n selected.set(match, \"custom pattern (project config)\");\n break; // one per pattern\n }\n }\n }\n\n // 6. Test examples — diverse across file types (cap at 3)\n const testPatternGroups = [\n // JS/TS tests\n { patterns: [\"**/*.test.*\", \"**/*.spec.*\"], label: \"JS/TS test\" },\n // JVM tests\n { patterns: [\"**/*Test.kt\", \"**/*Test.java\"], label: \"JVM test\" },\n // Python tests\n { patterns: [\"**/test_*.py\", \"**/*_test.py\"], label: \"Python test\" },\n // Go tests\n { patterns: [\"**/*_test.go\"], label: \"Go test\" },\n // Swift tests\n { patterns: [\"**/*Tests.swift\", \"**/*Test.swift\"], label: \"Swift test\" },\n // Rust tests\n { patterns: [\"**/*_test.rs\"], label: \"Rust test\" },\n ];\n let testCount = 0;\n for (const group of testPatternGroups) {\n if (testCount >= 3 || selected.size >= maxFiles) break;\n const testFiles = await fg(group.patterns, {\n cwd: rootDir,\n ignore: ignorePatterns,\n });\n if (testFiles.length > 0) {\n for (const file of testFiles) {\n if (!selected.has(file)) {\n selected.set(file, `test example (${group.label})`);\n testCount++;\n break;\n }\n }\n }\n }\n\n // 7. Directory breadth — fill remaining slots with one file per top-level dir\n const sourceGlobs = SOURCE_EXTENSIONS.map((ext) => `**/*.${ext}`);\n const allSourceFiles = await fg(sourceGlobs, {\n cwd: rootDir,\n ignore: ignorePatterns,\n });\n\n const dirRepresentatives = new Map<string, string>();\n const boringFiles = /\\.(gradle|gradle\\.kts|json|toml|yaml|yml|xml|properties)$/;\n for (const file of allSourceFiles) {\n const topDir = file.split(\"/\")[0];\n if (!dirRepresentatives.has(topDir) && !boringFiles.test(file)) {\n dirRepresentatives.set(topDir, file);\n }\n }\n\n for (const [, file] of dirRepresentatives) {\n if (selected.size >= maxFiles) break;\n if (!selected.has(file)) {\n selected.set(file, \"directory representative\");\n }\n }\n\n // Read file previews\n const results: SampledFile[] = [];\n for (const [filePath, reason] of selected) {\n try {\n const fullPath = path.resolve(rootDir, filePath);\n if (!fullPath.startsWith(path.resolve(rootDir))) continue;\n if (isSensitiveFile(filePath)) continue;\n const stat = await fs.stat(fullPath);\n if (stat.size > 100_000) continue;\n\n const content = await fs.readFile(fullPath, \"utf-8\");\n const lines = content.split(\"\\n\");\n const preview = lines.slice(0, PREVIEW_LINES).join(\"\\n\");\n\n results.push({\n path: filePath,\n preview,\n totalLines: lines.length,\n sizeBytes: stat.size,\n reason,\n });\n } catch {\n // Skip\n }\n }\n\n return results;\n}\n\nconst SENSITIVE_PATTERNS = [\n /^\\.env$/,\n /^\\.env\\./,\n /\\.pem$/,\n /\\.key$/,\n /\\.p12$/,\n /\\.pfx$/,\n /\\.jks$/,\n /id_rsa/,\n /id_ed25519/,\n /credentials\\./,\n /secret/i,\n /\\.keystore$/,\n /local\\.properties$/,\n];\n\nfunction isSensitiveFile(filePath: string): boolean {\n const basename = path.basename(filePath);\n return SENSITIVE_PATTERNS.some((p) => p.test(basename));\n}\n\nexport async function readFullFile(\n rootDir: string,\n filePath: string\n): Promise<{ path: string; content: string; totalLines: number } | null> {\n try {\n const fullPath = path.join(path.resolve(rootDir), filePath);\n if (!fullPath.startsWith(path.resolve(rootDir))) return null;\n if (isSensitiveFile(filePath)) return null;\n\n const content = await fs.readFile(fullPath, \"utf-8\");\n return {\n path: filePath,\n content,\n totalLines: content.split(\"\\n\").length,\n };\n } catch {\n return null;\n }\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { sampleFiles, readFullFile } from \"../mcp/sampler.js\";\nimport { buildTestMap } from \"../test-map.js\";\nimport { callLLM } from \"../llm/providers.js\";\nimport type { MasonConfig } from \"../llm/config.js\";\nimport {\n SNAPSHOT_SYSTEM_PROMPT,\n buildSnapshotPrompt,\n buildIncrementalPrompt,\n} from \"./prompt.js\";\n\nconst exec = promisify(execFile);\n\nexport interface FeatureEntry {\n description: string;\n files: string[];\n tests?: string[];\n}\n\nexport interface FlowEntry {\n description: string;\n chain: string[];\n}\n\nexport interface Snapshot {\n version: 2;\n createdAt: string;\n updatedAt: string;\n gitHash: string;\n features: Record<string, FeatureEntry>;\n flows: Record<string, FlowEntry>;\n}\n\nfunction snapshotDir(rootDir: string): string {\n return path.join(rootDir, \".mason\");\n}\n\nfunction snapshotPath(rootDir: string): string {\n return path.join(snapshotDir(rootDir), \"snapshot.json\");\n}\n\nexport async function loadSnapshot(rootDir: string): Promise<Snapshot | null> {\n try {\n const raw = await fs.readFile(snapshotPath(rootDir), \"utf-8\");\n const parsed = JSON.parse(raw);\n // Skip v1 snapshots — they're the old per-file format\n if (parsed.version !== 2) return null;\n return parsed;\n } catch {\n return null;\n }\n}\n\nexport async function saveSnapshot(\n rootDir: string,\n snapshot: Snapshot\n): Promise<void> {\n await fs.mkdir(snapshotDir(rootDir), { recursive: true });\n await fs.writeFile(\n snapshotPath(rootDir),\n JSON.stringify(snapshot, null, 2),\n \"utf-8\"\n );\n}\n\nexport async function getCurrentGitHash(rootDir: string): Promise<string> {\n try {\n const { stdout } = await exec(\"git\", [\"rev-parse\", \"HEAD\"], {\n cwd: rootDir,\n });\n return stdout.trim();\n } catch {\n return \"unknown\";\n }\n}\n\nfunction parseSnapshotResponse(raw: string): {\n features: Record<string, FeatureEntry>;\n flows: Record<string, FlowEntry>;\n} {\n let cleaned = raw.trim();\n if (cleaned.startsWith(\"```\")) {\n cleaned = cleaned.replace(/^```(?:json)?\\n?/, \"\").replace(/\\n?```$/, \"\");\n }\n\n try {\n const parsed = JSON.parse(cleaned);\n return {\n features: parsed.features ?? {},\n flows: parsed.flows ?? {},\n };\n } catch {\n // Try to find JSON object in the response\n const match = raw.match(/\\{[\\s\\S]*\\}/);\n if (match) {\n try {\n const parsed = JSON.parse(match[0]);\n return {\n features: parsed.features ?? {},\n flows: parsed.flows ?? {},\n };\n } catch {\n return { features: {}, flows: {} };\n }\n }\n return { features: {}, flows: {} };\n }\n}\n\nexport async function createSnapshot(\n rootDir: string,\n config: MasonConfig\n): Promise<Snapshot> {\n const resolvedRoot = path.resolve(rootDir);\n\n // Use sampler to pick key files\n const sampled = await sampleFiles(resolvedRoot, 25);\n\n // Read full content of each sampled file\n const filesWithContent: Array<{ path: string; content: string }> = [];\n for (const sample of sampled) {\n const full = await readFullFile(resolvedRoot, sample.path);\n if (full) {\n filesWithContent.push({ path: full.path, content: full.content });\n }\n }\n\n const gitHash = await getCurrentGitHash(resolvedRoot);\n const now = new Date().toISOString();\n\n if (filesWithContent.length === 0) {\n return {\n version: 2,\n createdAt: now,\n updatedAt: now,\n gitHash,\n features: {},\n flows: {},\n };\n }\n\n // Get test-to-source mappings so the LLM can include tests in features\n const testMap = await buildTestMap(resolvedRoot);\n\n // Call LLM to build concept map\n const userMessage = buildSnapshotPrompt(filesWithContent, testMap.paired);\n const result = await callLLM(config, userMessage, SNAPSHOT_SYSTEM_PROMPT);\n\n const resultText =\n typeof result === \"string\" ? result : result.type === \"response\" ? result.text : \"\";\n\n if (!resultText) {\n throw new Error(\n \"No CLI or API key available for this provider. Use claude or ollama (no key needed), or provide an API key.\"\n );\n }\n\n const { features, flows } = parseSnapshotResponse(resultText);\n\n const snapshot: Snapshot = {\n version: 2,\n createdAt: now,\n updatedAt: now,\n gitHash,\n features,\n flows,\n };\n\n await saveSnapshot(resolvedRoot, snapshot);\n return snapshot;\n}\n\nexport async function updateSnapshot(\n rootDir: string,\n config: MasonConfig\n): Promise<{ status: string; details: string }> {\n const resolvedRoot = path.resolve(rootDir);\n const existing = await loadSnapshot(resolvedRoot);\n\n if (!existing) {\n const snapshot = await createSnapshot(rootDir, config);\n const featureCount = Object.keys(snapshot.features).length;\n const flowCount = Object.keys(snapshot.flows).length;\n return {\n status: \"created\",\n details: `New snapshot: ${featureCount} features, ${flowCount} flows`,\n };\n }\n\n // Find files changed since last snapshot\n let changedFiles: string[] = [];\n try {\n const { stdout } = await exec(\n \"git\",\n [\"diff\", \"--name-only\", existing.gitHash, \"HEAD\"],\n { cwd: resolvedRoot }\n );\n changedFiles = stdout\n .trim()\n .split(\"\\n\")\n .filter((f) => f.length > 0);\n } catch {\n // Full rebuild if git diff fails\n const snapshot = await createSnapshot(rootDir, config);\n const featureCount = Object.keys(snapshot.features).length;\n return { status: \"rebuilt\", details: `${featureCount} features` };\n }\n\n if (changedFiles.length === 0) {\n return { status: \"up-to-date\", details: \"No changes since last snapshot\" };\n }\n\n // Check which changed files are architecturally relevant\n const sampled = await sampleFiles(resolvedRoot, 30);\n const sampledPaths = new Set(sampled.map((s) => s.path));\n\n // Also check which changed files are referenced in the existing snapshot\n const snapshotFiles = new Set<string>();\n for (const feature of Object.values(existing.features)) {\n for (const f of feature.files) snapshotFiles.add(f);\n for (const t of feature.tests ?? []) snapshotFiles.add(t);\n }\n for (const flow of Object.values(existing.flows)) {\n for (const f of flow.chain) snapshotFiles.add(f);\n }\n\n const relevantChanges = changedFiles.filter(\n (f) => sampledPaths.has(f) || snapshotFiles.has(f)\n );\n\n if (relevantChanges.length === 0) {\n // Changes don't affect snapshot files\n existing.gitHash = await getCurrentGitHash(resolvedRoot);\n existing.updatedAt = new Date().toISOString();\n await saveSnapshot(resolvedRoot, existing);\n return {\n status: \"unchanged\",\n details: `${changedFiles.length} files changed but none affect the concept map`,\n };\n }\n\n // Read changed files and ask LLM to update the map\n const filesWithContent: Array<{ path: string; content: string }> = [];\n for (const filePath of relevantChanges) {\n const full = await readFullFile(resolvedRoot, filePath);\n if (full) {\n filesWithContent.push({ path: full.path, content: full.content });\n }\n }\n\n if (filesWithContent.length === 0) {\n return { status: \"unchanged\", details: \"Changed files could not be read\" };\n }\n\n const userMessage = buildIncrementalPrompt(filesWithContent, {\n features: existing.features,\n flows: existing.flows,\n });\n\n const result = await callLLM(config, userMessage, SNAPSHOT_SYSTEM_PROMPT);\n const resultText =\n typeof result === \"string\" ? result : result.type === \"response\" ? result.text : \"\";\n\n if (!resultText) {\n throw new Error(\"No CLI or API key available for this provider.\");\n }\n\n const { features, flows } = parseSnapshotResponse(resultText);\n const gitHash = await getCurrentGitHash(resolvedRoot);\n\n existing.features = features;\n existing.flows = flows;\n existing.updatedAt = new Date().toISOString();\n existing.gitHash = gitHash;\n\n await saveSnapshot(resolvedRoot, existing);\n\n return {\n status: \"updated\",\n details: `${Object.keys(features).length} features, ${Object.keys(flows).length} flows (${relevantChanges.length} files changed)`,\n };\n}\n\nexport async function installHook(rootDir: string): Promise<void> {\n const resolvedRoot = path.resolve(rootDir);\n const hooksDir = path.join(resolvedRoot, \".git\", \"hooks\");\n\n try {\n await fs.access(hooksDir);\n } catch {\n throw new Error(\"Not a git repository (no .git/hooks directory)\");\n }\n\n const hookPath = path.join(hooksDir, \"post-commit\");\n const hookContent = `#!/bin/sh\n# Mason: auto-update project snapshot after commit\n# Runs in background so it doesn't block your workflow\nmason snapshot-update \"$(git rev-parse --show-toplevel)\" &\n`;\n\n try {\n const existing = await fs.readFile(hookPath, \"utf-8\");\n if (existing.includes(\"mason snapshot-update\")) {\n return; // Already installed\n }\n await fs.appendFile(hookPath, \"\\n\" + hookContent);\n } catch {\n await fs.writeFile(hookPath, hookContent, { mode: 0o755 });\n }\n}\n","import { execFile, spawn } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport type { MasonConfig } from \"./config.js\";\nimport { getDefaultModel } from \"./config.js\";\n\nconst exec = promisify(execFile);\n\nconst CLAUDE_MD_SYSTEM_PROMPT = `You are Mason, a context engineering tool. You've been given a comprehensive analysis of a codebase including:\n- Git history stats (commit patterns, frequently changed files, stale directories)\n- Project structure (directory layout, file counts by type)\n- Curated code samples (key architectural files with previews)\n- Test-to-source file mapping\n\nYour job: write a COMPLETE CLAUDE.md file from scratch based ONLY on the analysis data provided below. Do NOT read any existing files in the project. Do NOT reference or preserve any existing CLAUDE.md. Generate the entire document fresh.\n\nCRITICAL: Output ONLY the raw markdown content. No preamble, no summary, no \"Here's the CLAUDE.md:\", no explanation, no questions, no commentary. Start directly with \"# CLAUDE.md\" and end with the last line of content. Your entire response will be written directly to a file.\n\nThe CLAUDE.md should include:\n- Project overview (what it is, tech stack, architecture)\n- Module/package structure and boundaries\n- Code conventions and patterns you observe in the samples\n- Testing conventions and coverage\n- Build and development commands\n- Important files and hot spots\n- Any warnings or gotchas\n\nBe specific and actionable. Reference actual file paths. Don't be generic — every rule should be grounded in what you see in the data.`;\n\nexport type CallResult =\n | { type: \"response\"; text: string }\n | { type: \"prompt\"; text: string };\n\nexport async function callLLM(\n config: MasonConfig,\n userMessage: string,\n systemPrompt?: string\n): Promise<CallResult> {\n const model = config.model ?? getDefaultModel(config.provider);\n const system = systemPrompt ?? CLAUDE_MD_SYSTEM_PROMPT;\n\n switch (config.provider) {\n case \"claude\":\n if (config.apiKey) {\n return {\n type: \"response\",\n text: await callClaudeAPI(config.apiKey, model, system, userMessage),\n };\n }\n return {\n type: \"response\",\n text: await callClaudeCLI(system, userMessage),\n };\n\n case \"ollama\":\n return {\n type: \"response\",\n text: await callOllamaCLI(\n config.ollamaHost ?? \"http://localhost:11434\",\n model,\n system,\n userMessage,\n ),\n };\n\n case \"gemini\":\n if (config.apiKey) {\n return {\n type: \"response\",\n text: await callGeminiAPI(config.apiKey, model, system, userMessage),\n };\n }\n return {\n type: \"response\",\n text: await callGeminiCLI(system, userMessage),\n };\n\n case \"openai\":\n if (config.apiKey) {\n return {\n type: \"response\",\n text: await callOpenAIAPI(config.apiKey, model, system, userMessage),\n };\n }\n return {\n type: \"prompt\",\n text: formatPromptForCopy(system, userMessage),\n };\n }\n}\n\nfunction formatPromptForCopy(system: string, userMessage: string): string {\n return `${system}\\n\\n---\\n\\n${userMessage}`;\n}\n\n// === CLI-based providers (no API key) ===\n\nasync function callViaTempFile(\n command: string,\n args: (promptPath: string) => string[],\n system: string,\n userMessage: string\n): Promise<string> {\n const fs = await import(\"node:fs/promises\");\n const os = await import(\"node:os\");\n const path = await import(\"node:path\");\n\n const prompt = `${system}\\n\\n${userMessage}`;\n const tmpFile = path.join(os.tmpdir(), `mason-prompt-${Date.now()}.txt`);\n\n try {\n await fs.writeFile(tmpFile, prompt, \"utf-8\");\n const { stdout } = await exec(command, args(tmpFile), {\n maxBuffer: 10_000_000,\n timeout: 300_000,\n });\n return stdout.trim();\n } finally {\n await fs.unlink(tmpFile).catch(() => {});\n }\n}\n\nfunction spawnWithStdin(\n command: string,\n args: string[],\n input: string\n): Promise<string> {\n return new Promise((resolve, reject) => {\n const proc = spawn(command, args, {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n timeout: 300_000,\n });\n\n // ora puts stdin into raw mode, which means Ctrl+C is emitted as\n // process.emit(\"SIGINT\") rather than a real signal to the process\n // group. Forward it to the child so it can terminate.\n const onSigint = () => proc.kill(\"SIGINT\");\n process.on(\"SIGINT\", onSigint);\n\n let stdout = \"\";\n let stderr = \"\";\n\n proc.stdout.on(\"data\", (data: Buffer) => {\n stdout += data.toString();\n });\n proc.stderr.on(\"data\", (data: Buffer) => {\n stderr += data.toString();\n });\n\n proc.on(\"close\", (code: number | null) => {\n process.off(\"SIGINT\", onSigint);\n if (code === 0) {\n resolve(stdout.trim());\n } else {\n reject(new Error(`${command} exited with code ${code}: ${stderr}`));\n }\n });\n\n proc.on(\"error\", (err) => {\n process.off(\"SIGINT\", onSigint);\n reject(err);\n });\n\n proc.stdin.write(input);\n proc.stdin.end();\n });\n}\n\nasync function callClaudeCLI(\n system: string,\n userMessage: string\n): Promise<string> {\n const prompt = `${system}\\n\\n${userMessage}`;\n return spawnWithStdin(\"claude\", [\"-p\"], prompt);\n}\n\nasync function callGeminiCLI(\n system: string,\n userMessage: string\n): Promise<string> {\n const prompt = `${system}\\n\\n${userMessage}`;\n return spawnWithStdin(\"gemini\", [\"-p\", \"\"], prompt);\n}\n\nasync function callOllamaCLI(\n host: string,\n model: string,\n system: string,\n userMessage: string\n): Promise<string> {\n const response = await fetch(`${host}/api/chat`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n model,\n stream: false,\n messages: [\n { role: \"system\", content: system },\n { role: \"user\", content: userMessage },\n ],\n }),\n });\n\n const result = (await response.json()) as {\n message?: { content?: string };\n };\n return result.message?.content ?? \"\";\n}\n\n// === API-based providers ===\n\nasync function callClaudeAPI(\n apiKey: string,\n model: string,\n system: string,\n userMessage: string\n): Promise<string> {\n const { default: Anthropic } = await import(\"@anthropic-ai/sdk\");\n const client = new Anthropic({ apiKey });\n\n const response = await client.messages.create({\n model,\n max_tokens: 8192,\n system,\n messages: [{ role: \"user\", content: userMessage }],\n });\n\n const textBlock = response.content.find((b) => b.type === \"text\");\n return textBlock?.text ?? \"\";\n}\n\nasync function callGeminiAPI(\n apiKey: string,\n model: string,\n system: string,\n userMessage: string\n): Promise<string> {\n const { default: OpenAI } = await import(\"openai\");\n const client = new OpenAI({\n apiKey,\n baseURL: \"https://generativelanguage.googleapis.com/v1beta/openai/\",\n });\n\n const response = await client.chat.completions.create({\n model,\n max_tokens: 8192,\n messages: [\n { role: \"system\", content: system },\n { role: \"user\", content: userMessage },\n ],\n });\n\n return response.choices[0]?.message?.content ?? \"\";\n}\n\nasync function callOpenAIAPI(\n apiKey: string,\n model: string,\n system: string,\n userMessage: string\n): Promise<string> {\n const { default: OpenAI } = await import(\"openai\");\n const client = new OpenAI({ apiKey });\n\n const response = await client.chat.completions.create({\n model,\n max_tokens: 8192,\n messages: [\n { role: \"system\", content: system },\n { role: \"user\", content: userMessage },\n ],\n });\n\n return response.choices[0]?.message?.content ?? \"\";\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nconst exec = promisify(execFile);\n\nexport type Provider = \"claude\" | \"gemini\" | \"openai\" | \"ollama\";\n\nexport interface MasonConfig {\n provider: Provider;\n apiKey?: string;\n model?: string;\n ollamaHost?: string;\n}\n\nconst CONFIG_DIR = path.join(os.homedir(), \".mason\");\nconst CONFIG_FILE = path.join(CONFIG_DIR, \"config.json\");\n\nconst DEFAULT_MODELS: Record<Provider, string> = {\n claude: \"claude-sonnet-4-20250514\",\n gemini: \"gemini-2.5-flash\",\n openai: \"gpt-4o\",\n ollama: \"llama3\",\n};\n\nexport async function loadConfig(): Promise<MasonConfig | null> {\n try {\n const raw = await fs.readFile(CONFIG_FILE, \"utf-8\");\n return JSON.parse(raw);\n } catch {\n return null;\n }\n}\n\nexport async function saveConfig(config: MasonConfig): Promise<void> {\n await fs.mkdir(CONFIG_DIR, { recursive: true });\n await fs.writeFile(CONFIG_FILE, JSON.stringify(config, null, 2), \"utf-8\");\n}\n\nexport function getDefaultModel(provider: Provider): string {\n return DEFAULT_MODELS[provider];\n}\n\nexport function validateProvider(value: string): Provider {\n const valid: Provider[] = [\"claude\", \"gemini\", \"openai\", \"ollama\"];\n if (!valid.includes(value as Provider)) {\n throw new Error(\n `Invalid provider \"${value}\". Must be one of: ${valid.join(\", \")}`\n );\n }\n return value as Provider;\n}\n\nexport async function detectCLI(\n provider: Provider\n): Promise<{ available: boolean; version?: string }> {\n const cliName = provider === \"claude\" ? \"claude\"\n : provider === \"gemini\" ? \"gemini\"\n : provider === \"ollama\" ? \"ollama\"\n : null;\n\n if (!cliName) return { available: false };\n\n try {\n const { stdout } = await exec(cliName, [\"--version\"]);\n return { available: true, version: stdout.trim() };\n } catch {\n return { available: false };\n }\n}\n\nexport function needsApiKey(provider: Provider): boolean {\n return provider === \"openai\";\n}\n","import { startMcpServer } from \"../src/mcp/server.js\";\n\nstartMcpServer().catch((err) => {\n process.stderr.write(`Mason MCP server error: ${err}\\n`);\n process.exit(1);\n});\n"],"mappings":"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","names":["path","fg","fs","path","execFile","promisify","fg","IGNORE","exec","SOURCE_EXTENSIONS","fs","path","execFile","promisify","fg","execFile","promisify","exec","fs","execFile","promisify","fg","exec","fs","path","execFile","promisify","execFile","promisify","fs","path","execFile","promisify","exec","exec","promisify","execFile","exec","promisify","execFile","path","fs","exec","promisify","execFile","IGNORE","path","fs","fg","buildTestMap","analyzeImpact","path"]}