archtracker-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +468 -0
- package/dist/cli/index.js +1853 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +144 -0
- package/dist/index.js +587 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/index.d.ts +2 -0
- package/dist/mcp/index.js +920 -0
- package/dist/mcp/index.js.map +1 -0
- package/package.json +78 -0
- package/skills/arch-analyze/SKILL.md +28 -0
- package/skills/arch-check/SKILL.md +22 -0
- package/skills/arch-context/SKILL.md +21 -0
- package/skills/arch-search/SKILL.md +24 -0
- package/skills/arch-snapshot/SKILL.md +20 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/mcp/index.ts","../../src/analyzer/analyze.ts","../../src/i18n/index.ts","../../src/analyzer/search.ts","../../src/analyzer/report.ts","../../src/storage/snapshot.ts","../../src/types/schema.ts","../../src/storage/diff.ts","../../src/utils/path-guard.ts"],"sourcesContent":["import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { z } from \"zod\";\nimport {\n analyzeProject,\n AnalyzerError,\n searchByPath,\n findAffectedFiles,\n findCriticalFiles,\n findOrphanFiles,\n formatAnalysisReport,\n} from \"../analyzer/index.js\";\nimport {\n saveSnapshot,\n loadSnapshot,\n computeDiff,\n formatDiffReport,\n StorageError,\n} from \"../storage/index.js\";\nimport type { ArchContext } from \"../types/schema.js\";\nimport { validatePath, PathTraversalError } from \"../utils/path-guard.js\";\nimport { t } from \"../i18n/index.js\";\n\nconst server = new McpServer({\n name: \"archtracker\",\n version: \"0.1.0\",\n});\n\n// ─── Tool 1: generate_map ───────────────────────────────────────\n\nserver.tool(\n \"generate_map\",\n \"Analyze dependency graph of a directory and return file import/export structure as JSON\",\n {\n targetDir: z\n .string()\n .default(\"src\")\n .describe(\"Target directory path (default: src)\"),\n exclude: z\n .array(z.string())\n .optional()\n .describe(\"Array of regex patterns to exclude (e.g. ['test', 'mock'])\"),\n maxDepth: z\n .number()\n .int()\n .min(0)\n .optional()\n .describe(\"Max analysis depth (0 = unlimited)\"),\n },\n async ({ targetDir, exclude, maxDepth }) => {\n try {\n validatePath(targetDir);\n const graph = await analyzeProject(targetDir, { exclude, maxDepth });\n\n const summary = [\n t(\"mcp.analyzeComplete\", { files: graph.totalFiles, edges: graph.totalEdges }),\n graph.circularDependencies.length > 0\n ? t(\"mcp.circularFound\", { count: graph.circularDependencies.length })\n : t(\"mcp.circularNone\"),\n ].join(\"\\n\");\n\n return {\n content: [\n { type: \"text\" as const, text: summary },\n { type: \"text\" as const, text: JSON.stringify(graph, null, 2) },\n ],\n };\n } catch (error) {\n return errorResponse(error);\n }\n },\n);\n\n// ─── Tool 2: analyze_existing_architecture ──────────────────────\n\nserver.tool(\n \"analyze_existing_architecture\",\n \"Comprehensive architecture analysis for existing projects. Shows critical components, circular dependencies, orphan files, coupling hotspots, and directory breakdown.\",\n {\n targetDir: z\n .string()\n .default(\"src\")\n .describe(\"Target directory path (default: src)\"),\n exclude: z\n .array(z.string())\n .optional()\n .describe(\"Array of regex patterns to exclude\"),\n topN: z\n .number()\n .int()\n .min(1)\n .max(50)\n .optional()\n .describe(\"Number of top items to show in each section (default: 10)\"),\n saveSnapshot: z\n .boolean()\n .optional()\n .describe(\"Also save a snapshot after analysis (default: false)\"),\n projectRoot: z\n .string()\n .default(\".\")\n .describe(\"Project root (needed only when saveSnapshot is true)\"),\n },\n async ({ targetDir, exclude, topN, saveSnapshot: doSave, projectRoot }) => {\n try {\n validatePath(targetDir);\n const graph = await analyzeProject(targetDir, { exclude });\n const report = formatAnalysisReport(graph, { topN: topN ?? 10 });\n\n const content: { type: \"text\"; text: string }[] = [\n { type: \"text\" as const, text: report },\n ];\n\n if (doSave) {\n validatePath(projectRoot);\n await saveSnapshot(projectRoot, graph);\n content.push({ type: \"text\" as const, text: t(\"analyze.snapshotSaved\") });\n }\n\n return { content };\n } catch (error) {\n return errorResponse(error);\n }\n },\n);\n\n// ─── Tool 3: save_architecture_snapshot ─────────────────────────\n\nserver.tool(\n \"save_architecture_snapshot\",\n \"Save the current dependency graph as a snapshot to .archtracker/snapshot.json\",\n {\n targetDir: z\n .string()\n .default(\"src\")\n .describe(\"Target directory path\"),\n projectRoot: z\n .string()\n .default(\".\")\n .describe(\"Project root (where .archtracker is placed)\"),\n },\n async ({ targetDir, projectRoot }) => {\n try {\n validatePath(targetDir);\n validatePath(projectRoot);\n const graph = await analyzeProject(targetDir);\n const snapshot = await saveSnapshot(projectRoot, graph);\n\n // Top 5 most-depended-on files\n const keyComponents = Object.values(graph.files)\n .sort((a, b) => b.dependents.length - a.dependents.length)\n .slice(0, 5)\n .map((f) => ` ${t(\"cli.dependedBy\", { path: f.path, count: f.dependents.length })}`);\n\n const report = [\n t(\"mcp.snapshotSaved\"),\n t(\"cli.timestamp\", { ts: snapshot.timestamp }),\n t(\"cli.fileCount\", { count: graph.totalFiles }),\n t(\"cli.edgeCount\", { count: graph.totalEdges }),\n \"\",\n t(\"cli.keyComponents\"),\n ...keyComponents,\n ].join(\"\\n\");\n\n return { content: [{ type: \"text\" as const, text: report }] };\n } catch (error) {\n return errorResponse(error);\n }\n },\n);\n\n// ─── Tool 3: check_architecture_diff ────────────────────────────\n\nserver.tool(\n \"check_architecture_diff\",\n \"Compare saved snapshot with current code dependencies and warn about files that may need updates\",\n {\n targetDir: z\n .string()\n .default(\"src\")\n .describe(\"Target directory path\"),\n projectRoot: z\n .string()\n .default(\".\")\n .describe(\"Project root (where .archtracker is placed)\"),\n },\n async ({ targetDir, projectRoot }) => {\n try {\n validatePath(targetDir);\n validatePath(projectRoot);\n const existingSnapshot = await loadSnapshot(projectRoot);\n\n if (!existingSnapshot) {\n // Auto-generate initial snapshot\n const graph = await analyzeProject(targetDir);\n await saveSnapshot(projectRoot, graph);\n return {\n content: [\n {\n type: \"text\" as const,\n text: [\n t(\"mcp.autoInit\"),\n t(\"cli.fileCount\", { count: graph.totalFiles }) + \", \" + t(\"cli.edgeCount\", { count: graph.totalEdges }),\n t(\"mcp.nextCheckEnabled\"),\n ].join(\"\\n\"),\n },\n ],\n };\n }\n\n const currentGraph = await analyzeProject(targetDir);\n const diff = computeDiff(existingSnapshot.graph, currentGraph);\n const report = formatDiffReport(diff);\n\n return { content: [{ type: \"text\" as const, text: report }] };\n } catch (error) {\n return errorResponse(error);\n }\n },\n);\n\n// ─── Tool 4: get_current_context ────────────────────────────────\n\nserver.tool(\n \"get_current_context\",\n \"Get current valid file paths and architecture summary for AI session initialization\",\n {\n targetDir: z\n .string()\n .default(\"src\")\n .describe(\"Target directory path\"),\n projectRoot: z\n .string()\n .default(\".\")\n .describe(\"Project root\"),\n },\n async ({ targetDir, projectRoot }) => {\n try {\n let snapshot = await loadSnapshot(projectRoot);\n\n // Auto-generate if no snapshot exists\n if (!snapshot) {\n const graph = await analyzeProject(targetDir);\n snapshot = await saveSnapshot(projectRoot, graph);\n }\n\n const graph = snapshot.graph;\n\n // Build key components list (sorted by dependent count)\n const keyComponents = Object.values(graph.files)\n .filter((f) => f.dependents.length > 0 || f.dependencies.length > 0)\n .sort((a, b) => b.dependents.length - a.dependents.length)\n .slice(0, 20)\n .map((f) => ({\n path: f.path,\n dependentCount: f.dependents.length,\n dependencyCount: f.dependencies.length,\n }));\n\n const validPaths = Object.keys(graph.files).sort();\n\n const summary = [\n t(\"cli.project\", { path: graph.rootDir }),\n t(\"cli.fileCount\", { count: graph.totalFiles }),\n t(\"cli.edgeCount\", { count: graph.totalEdges }),\n t(\"cli.circularCount\", { count: graph.circularDependencies.length }),\n t(\"cli.snapshot\", { ts: snapshot.timestamp }),\n \"\",\n t(\"cli.keyComponents\"),\n ...keyComponents.map(\n (c) =>\n ` ${t(\"cli.dependedBy\", { path: c.path, count: c.dependentCount })}`,\n ),\n ].join(\"\\n\");\n\n const context: ArchContext = {\n validPaths,\n summary,\n snapshotExists: true,\n snapshotTimestamp: snapshot.timestamp,\n keyComponents,\n };\n\n return {\n content: [\n { type: \"text\" as const, text: summary },\n {\n type: \"text\" as const,\n text: JSON.stringify(context, null, 2),\n },\n ],\n };\n } catch (error) {\n return errorResponse(error);\n }\n },\n);\n\n// ─── Tool 5: search_architecture ────────────────────────────────\n\nserver.tool(\n \"search_architecture\",\n \"Search architecture: file path search, impact analysis, critical component detection, orphan file detection\",\n {\n query: z\n .string()\n .optional()\n .describe(\"Search query (required for path/affected modes, not needed for critical/orphans)\"),\n mode: z\n .enum([\"path\", \"affected\", \"critical\", \"orphans\"])\n .default(\"path\")\n .describe(\n \"Search mode: path=search by path, affected=change impact, critical=key files, orphans=isolated files\",\n ),\n targetDir: z\n .string()\n .default(\"src\")\n .describe(\"Target directory path\"),\n projectRoot: z\n .string()\n .default(\".\")\n .describe(\"Project root\"),\n limit: z\n .number()\n .int()\n .min(1)\n .max(50)\n .optional()\n .describe(\"Max results (default: 10)\"),\n },\n async ({ query, mode, targetDir, projectRoot, limit }) => {\n try {\n validatePath(targetDir);\n validatePath(projectRoot);\n // Use existing snapshot or generate fresh\n let snapshot = await loadSnapshot(projectRoot);\n if (!snapshot) {\n const graph = await analyzeProject(targetDir);\n snapshot = await saveSnapshot(projectRoot, graph);\n }\n\n const graph = snapshot.graph;\n const maxResults = limit ?? 10;\n let results;\n\n // Validate query is provided for modes that require it\n if ((mode === \"path\" || mode === \"affected\") && !query) {\n return {\n content: [{ type: \"text\" as const, text: t(\"mcp.queryRequired\", { mode }) }],\n isError: true,\n };\n }\n\n switch (mode) {\n case \"path\":\n results = searchByPath(graph, query!);\n break;\n case \"affected\":\n results = findAffectedFiles(graph, query!);\n break;\n case \"critical\":\n results = findCriticalFiles(graph, maxResults);\n break;\n case \"orphans\":\n results = findOrphanFiles(graph);\n break;\n }\n\n if (results.length === 0) {\n return {\n content: [\n { type: \"text\" as const, text: t(\"search.noResults\", { query: query ?? \"\", mode }) },\n ],\n };\n }\n\n const lines = [\n t(\"search.results\", { count: results.length, mode }),\n \"\",\n ...results.slice(0, maxResults).map((r) => {\n return [\n ` ${r.file}`,\n ` ${r.matchReason}`,\n ` deps: ${r.dependencyCount} -> [${r.dependencies.slice(0, 5).join(\", \")}${r.dependencies.length > 5 ? \"...\" : \"\"}]`,\n ` dependents: ${r.dependentCount} <- [${r.dependents.slice(0, 5).join(\", \")}${r.dependents.length > 5 ? \"...\" : \"\"}]`,\n ].join(\"\\n\");\n }),\n ];\n\n return { content: [{ type: \"text\" as const, text: lines.join(\"\\n\") }] };\n } catch (error) {\n return errorResponse(error);\n }\n },\n);\n\n// ─── Error handling helper ──────────────────────────────────────\n\nfunction errorResponse(error: unknown) {\n let message: string;\n\n if (error instanceof PathTraversalError) {\n message = t(\"error.pathTraversal\", { message: error.message });\n } else if (error instanceof AnalyzerError) {\n message = t(\"error.analyzer\", { message: error.message });\n } else if (error instanceof StorageError) {\n message = t(\"error.storage\", { message: error.message });\n } else if (error instanceof Error) {\n message = t(\"error.generic\", { message: error.message });\n } else {\n message = t(\"error.unexpected\", { message: String(error) });\n }\n\n return {\n content: [{ type: \"text\" as const, text: message }],\n isError: true,\n };\n}\n\n// ─── Server startup ─────────────────────────────────────────────\n\nasync function main() {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n console.error(\"[archtracker] MCP server running on stdio\");\n}\n\nmain().catch((error) => {\n console.error(\"[archtracker] Fatal error:\", error);\n process.exit(1);\n});\n","import { resolve, relative } from \"node:path\";\nimport { cruise } from \"dependency-cruiser\";\nimport type { ICruiseResult } from \"dependency-cruiser\";\nimport type {\n DependencyGraph,\n DependencyEdge,\n FileNode,\n CircularDependency,\n} from \"../types/schema.js\";\n\n/** Options for the dependency analyzer */\nexport interface AnalyzeOptions {\n /** Regex patterns to exclude from analysis (e.g. [\"node_modules\", \"\\\\.test\\\\.ts$\"]) */\n exclude?: string[];\n /** Maximum recursion depth (0 = unlimited) */\n maxDepth?: number;\n /** Path to tsconfig.json (auto-detected if omitted) */\n tsConfigPath?: string;\n /** Include type-only imports (import type {...}) */\n includeTypeOnly?: boolean;\n}\n\nconst DEFAULT_EXCLUDE = [\n \"node_modules\",\n \"\\\\.d\\\\.ts$\",\n \"dist\",\n \"build\",\n \"coverage\",\n \"\\\\.archtracker\",\n];\n\n/**\n * Analyze project dependencies using dependency-cruiser.\n *\n * dependency-cruiser records module paths relative to CWD.\n * We resolve rootDir to an absolute path, then use it as baseDir\n * so that output paths are relative to rootDir.\n *\n * @param rootDir - Directory to analyze (e.g. \"src\" or absolute path)\n * @param options - Analysis configuration\n * @returns DependencyGraph with files, edges, and circular dependency warnings\n */\nexport async function analyzeProject(\n rootDir: string,\n options: AnalyzeOptions = {},\n): Promise<DependencyGraph> {\n const {\n exclude = [],\n maxDepth = 0,\n tsConfigPath,\n includeTypeOnly = true,\n } = options;\n\n const absRootDir = resolve(rootDir);\n const allExclude = [...DEFAULT_EXCLUDE, ...exclude];\n const excludePattern = allExclude.join(\"|\");\n\n // Cruise \".\" relative to absRootDir as baseDir,\n // so all output paths are relative to the target directory\n const cruiseOptions: Record<string, unknown> = {\n baseDir: absRootDir,\n exclude: { path: excludePattern },\n doNotFollow: { path: \"node_modules\" },\n maxDepth,\n tsPreCompilationDeps: includeTypeOnly ? true : false,\n combinedDependencies: false,\n };\n\n if (tsConfigPath) {\n cruiseOptions.tsConfig = { fileName: tsConfigPath };\n }\n\n let result;\n try {\n result = await cruise([\".\"], cruiseOptions);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new AnalyzerError(\n `dependency-cruiser の実行に失敗しました: ${message}`,\n { cause: error },\n );\n }\n\n if (result.exitCode !== 0 && !result.output) {\n throw new AnalyzerError(\n `解析がエラーコード ${result.exitCode} で終了しました`,\n );\n }\n\n const cruiseResult = result.output as ICruiseResult;\n return buildGraph(absRootDir, cruiseResult);\n}\n\n/** Convert dependency-cruiser output to our DependencyGraph format */\nfunction buildGraph(\n rootDir: string,\n cruiseResult: ICruiseResult,\n): DependencyGraph {\n const files: Record<string, FileNode> = {};\n const edges: DependencyEdge[] = [];\n const circularSet = new Set<string>();\n const circularDependencies: CircularDependency[] = [];\n\n // First pass: register only local project files\n // Skip core modules (fs, path), external packages (npm), and unresolvable\n for (const mod of cruiseResult.modules) {\n if (isExternalModule(mod)) continue;\n\n files[mod.source] = {\n path: mod.source,\n exists: !mod.couldNotResolve,\n dependencies: [],\n dependents: [],\n };\n }\n\n // Second pass: build edges and dependency lists\n for (const mod of cruiseResult.modules) {\n for (const dep of mod.dependencies) {\n // Skip unresolvable and core modules\n if (dep.couldNotResolve || dep.coreModule) continue;\n // Skip edges to/from external packages\n if (!files[mod.source] || isExternalDep(dep)) continue;\n\n const edgeType = dep.typeOnly\n ? (\"type-only\" as const)\n : dep.dynamic\n ? (\"dynamic\" as const)\n : (\"static\" as const);\n\n edges.push({\n source: mod.source,\n target: dep.resolved,\n type: edgeType,\n });\n\n // Update dependency/dependent lists\n if (files[mod.source]) {\n files[mod.source].dependencies.push(dep.resolved);\n }\n if (files[dep.resolved]) {\n files[dep.resolved].dependents.push(mod.source);\n }\n\n // Detect circular dependencies\n if (dep.circular && dep.cycle) {\n const cyclePath = dep.cycle.map((c) => c.name);\n const cycleKey = [...cyclePath].sort().join(\"→\");\n if (!circularSet.has(cycleKey)) {\n circularSet.add(cycleKey);\n circularDependencies.push({ cycle: cyclePath });\n }\n }\n }\n }\n\n return {\n rootDir,\n files,\n edges,\n circularDependencies,\n totalFiles: Object.keys(files).length,\n totalEdges: edges.length,\n };\n}\n\n/**\n * Check if a module is external (not a local project file).\n * Uses both dependency-cruiser metadata and path heuristics.\n */\nfunction isExternalModule(mod: { source: string; coreModule?: boolean; dependencyTypes?: string[] }): boolean {\n if (mod.coreModule) return true;\n const depTypes = mod.dependencyTypes ?? [];\n if (depTypes.some((t) => t.startsWith(\"npm\") || t === \"core\")) return true;\n // Path-based fallback: scoped packages (@org/pkg) or bare specifiers without extension\n return isExternalPath(mod.source);\n}\n\n/**\n * Check if a dependency edge points to an external target.\n */\nfunction isExternalDep(dep: { resolved: string; coreModule: boolean; dependencyTypes: string[] }): boolean {\n if (dep.coreModule) return true;\n if (dep.dependencyTypes.some((t) => t.startsWith(\"npm\") || t === \"core\")) return true;\n return isExternalPath(dep.resolved);\n}\n\n/**\n * Path heuristic: detect external modules by path pattern.\n * Local project files start with ./ or are relative paths with file extensions.\n * External: @scope/pkg, bare-name/sub, single-word (fs, path, os).\n */\nfunction isExternalPath(source: string): boolean {\n // Scoped packages: @modelcontextprotocol/sdk/...\n if (source.startsWith(\"@\")) return true;\n // Core node modules: single word without slash (fs, path, os, etc.)\n if (!source.includes(\"/\") && !source.includes(\"\\\\\") && !source.includes(\".\")) return true;\n // node: protocol\n if (source.startsWith(\"node:\")) return true;\n return false;\n}\n\n/** Custom error class for analyzer failures */\nexport class AnalyzerError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"AnalyzerError\";\n }\n}\n","export type Locale = \"en\" | \"ja\";\n\nlet currentLocale: Locale = detectLocale();\n\n/** Get the current locale */\nexport function getLocale(): Locale {\n return currentLocale;\n}\n\n/** Set the locale explicitly */\nexport function setLocale(locale: Locale): void {\n currentLocale = locale;\n}\n\n/** Auto-detect locale from LANG/LC_ALL environment variable */\nfunction detectLocale(): Locale {\n const env = process.env.LC_ALL || process.env.LANG || \"\";\n if (env.startsWith(\"ja\")) return \"ja\";\n return \"en\";\n}\n\n/** Get a translated message by key */\nexport function t(key: string, vars?: Record<string, string | number>): string {\n const messages = currentLocale === \"ja\" ? ja : en;\n let msg = (messages as Record<string, string>)[key] ?? (en as Record<string, string>)[key] ?? key;\n\n if (vars) {\n for (const [k, v] of Object.entries(vars)) {\n msg = msg.replaceAll(`{${k}}`, String(v));\n }\n }\n return msg;\n}\n\n// ─── Message catalogs ───────────────────────────────────────────\n\nconst en = {\n // Analyzer\n \"analyzer.failed\": \"dependency-cruiser failed: {message}\",\n \"analyzer.exitCode\": \"Analysis finished with error code {code}\",\n\n // Storage\n \"storage.parseFailed\": \"Failed to parse snapshot.json. File may be corrupted: {path}\",\n \"storage.readFailed\": \"Failed to read snapshot.json: {path}\",\n \"storage.invalidSchema\": \"snapshot.json schema is invalid. Please regenerate with `archtracker init`:\\n{issues}\",\n \"storage.versionMismatch\": \"snapshot.json version ({version}) is incompatible with current schema ({expected}). Regenerate with `archtracker init`.\",\n\n // Path guard\n \"pathGuard.traversal\": \"Path points outside project root: \\\"{input}\\\" → \\\"{resolved}\\\" (allowed: \\\"{boundary}\\\")\",\n\n // Diff report\n \"diff.title\": \"# Architecture Change Report\\n\",\n \"diff.noChanges\": \"No changes — snapshot matches current code.\\n\",\n \"diff.added\": \"## Added Files ({count})\",\n \"diff.removed\": \"## Removed Files ({count})\",\n \"diff.modified\": \"## Modified Dependencies ({count})\",\n \"diff.affected\": \"## Files Requiring Review ({count})\",\n \"diff.reasonRemoved\": \"Dependency \\\"{file}\\\" was removed\",\n \"diff.reasonModified\": \"Dependency \\\"{file}\\\" had its dependencies changed\",\n \"diff.reasonAdded\": \"New dependency \\\"{file}\\\" was added\",\n\n // Search\n \"search.pathMatch\": \"Path matches \\\"{pattern}\\\"\",\n \"search.affected\": \"May be affected by changes to \\\"{file}\\\" (via: {via})\",\n \"search.critical\": \"{count} files depend on this component\",\n \"search.orphan\": \"Orphan file (no dependencies, no dependents)\",\n \"search.noResults\": \"No results: \\\"{query}\\\" (mode: {mode})\",\n \"search.results\": \"Results: {count} (mode: {mode})\",\n\n // CLI\n \"cli.analyzing\": \"Analyzing...\",\n \"cli.snapshotSaved\": \"Snapshot saved\",\n \"cli.timestamp\": \" Timestamp: {ts}\",\n \"cli.fileCount\": \" Files: {count}\",\n \"cli.edgeCount\": \" Edges: {count}\",\n \"cli.circularCount\": \" Circular deps: {count}\",\n \"cli.keyComponents\": \"\\nKey components:\",\n \"cli.dependedBy\": \"{path} ({count} dependents)\",\n \"cli.noSnapshot\": \"No snapshot found. Run `archtracker init` first.\",\n \"cli.ciFailed\": \"\\nCI check failed: {count} file(s) require review\",\n \"cli.autoGenerating\": \"No snapshot found, auto-generating...\",\n \"cli.project\": \"Project: {path}\",\n \"cli.validPaths\": \"\\nValid file paths:\",\n \"cli.snapshot\": \"Snapshot: {ts}\",\n\n // MCP\n \"mcp.analyzeComplete\": \"Analysis complete: {files} files, {edges} edges\",\n \"mcp.circularFound\": \"Circular deps: {count} found\",\n \"mcp.circularNone\": \"Circular deps: none\",\n \"mcp.snapshotSaved\": \"Snapshot saved\",\n \"mcp.autoInit\": \"No snapshot existed. Initial snapshot auto-generated.\",\n \"mcp.nextCheckEnabled\": \"Diff checking will be active from the next run.\",\n \"mcp.queryRequired\": \"\\\"{mode}\\\" mode requires the query parameter\",\n\n // Analyze report\n \"analyze.title\": \"# Architecture Analysis Report\\n\",\n \"analyze.overview\": \"## Overview\",\n \"analyze.totalFiles\": \" Total files: {count}\",\n \"analyze.totalEdges\": \" Total edges: {count}\",\n \"analyze.totalCircular\": \" Circular dependencies: {count}\",\n \"analyze.criticalTitle\": \"\\n## Critical Components (Top {count})\",\n \"analyze.criticalItem\": \" {path} ({count} dependents)\",\n \"analyze.circularTitle\": \"\\n## Circular Dependencies ({count})\",\n \"analyze.circularItem\": \" {files}\",\n \"analyze.orphanTitle\": \"\\n## Orphan Files ({count})\",\n \"analyze.couplingTitle\": \"\\n## High Coupling (Top {count} by import count)\",\n \"analyze.couplingItem\": \" {path} ({count} imports)\",\n \"analyze.layerTitle\": \"\\n## Directory Breakdown\",\n \"analyze.layerItem\": \" {dir}/ — {count} files\",\n \"analyze.noIssues\": \"\\nNo architectural issues detected.\",\n \"analyze.snapshotSaved\": \"\\nSnapshot saved alongside analysis.\",\n\n // CI\n \"ci.generated\": \"GitHub Actions workflow generated: {path}\",\n\n // Web viewer\n \"web.starting\": \"Starting architecture viewer...\",\n \"web.listening\": \"Architecture graph available at: http://localhost:{port}\",\n \"web.stop\": \"Press Ctrl+C to stop\",\n \"web.watching\": \"Watching {dir}/ for changes...\",\n \"web.reloading\": \"File change detected, reloading...\",\n \"web.reloaded\": \"Graph reloaded\",\n\n // Errors\n \"error.analyzer\": \"[Analysis Error] {message}\",\n \"error.storage\": \"[Storage Error] {message}\",\n \"error.pathTraversal\": \"[Security Error] {message}\",\n \"error.generic\": \"[Error] {message}\",\n \"error.unexpected\": \"[Error] Unexpected error: {message}\",\n \"error.cli.analyzer\": \"Analysis error: {message}\",\n \"error.cli.storage\": \"Storage error: {message}\",\n \"error.cli.generic\": \"Error: {message}\",\n \"error.cli.unexpected\": \"Unexpected error: {message}\",\n} as const;\n\nconst ja = {\n // Analyzer\n \"analyzer.failed\": \"dependency-cruiser の実行に失敗しました: {message}\",\n \"analyzer.exitCode\": \"解析がエラーコード {code} で終了しました\",\n\n // Storage\n \"storage.parseFailed\": \"snapshot.json のパースに失敗しました。ファイルが破損している可能性があります: {path}\",\n \"storage.readFailed\": \"snapshot.json の読み取りに失敗しました: {path}\",\n \"storage.invalidSchema\": \"snapshot.json のスキーマが不正です。archtracker init で再生成してください:\\n{issues}\",\n \"storage.versionMismatch\": \"snapshot.json のバージョン ({version}) が現在のスキーマ ({expected}) と互換性がありません。archtracker init で再生成してください。\",\n\n // Path guard\n \"pathGuard.traversal\": \"パスがプロジェクトルートの外部を指しています: \\\"{input}\\\" → \\\"{resolved}\\\" (許可範囲: \\\"{boundary}\\\")\",\n\n // Diff report\n \"diff.title\": \"# アーキテクチャ変更レポート\\n\",\n \"diff.noChanges\": \"変更なし — スナップショットと現在のコードは一致しています。\\n\",\n \"diff.added\": \"## 追加されたファイル ({count}件)\",\n \"diff.removed\": \"## 削除されたファイル ({count}件)\",\n \"diff.modified\": \"## 依存関係が変更されたファイル ({count}件)\",\n \"diff.affected\": \"## 確認が必要なファイル ({count}件)\",\n \"diff.reasonRemoved\": \"依存先 \\\"{file}\\\" が削除されました\",\n \"diff.reasonModified\": \"依存先 \\\"{file}\\\" の依存関係が変更されました\",\n \"diff.reasonAdded\": \"新しい依存先 \\\"{file}\\\" が追加されました\",\n\n // Search\n \"search.pathMatch\": \"パスが \\\"{pattern}\\\" にマッチ\",\n \"search.affected\": \"\\\"{file}\\\" の変更により影響を受ける可能性(経由: {via})\",\n \"search.critical\": \"{count}件のファイルが依存する重要コンポーネント\",\n \"search.orphan\": \"孤立ファイル(依存なし・被依存なし)\",\n \"search.noResults\": \"検索結果なし: \\\"{query}\\\" (モード: {mode})\",\n \"search.results\": \"検索結果: {count}件 (モード: {mode})\",\n\n // CLI\n \"cli.analyzing\": \"解析中...\",\n \"cli.snapshotSaved\": \"スナップショットを保存しました\",\n \"cli.timestamp\": \" タイムスタンプ: {ts}\",\n \"cli.fileCount\": \" ファイル数: {count}\",\n \"cli.edgeCount\": \" エッジ数: {count}\",\n \"cli.circularCount\": \" 循環参照: {count}件\",\n \"cli.keyComponents\": \"\\n主要コンポーネント:\",\n \"cli.dependedBy\": \"{path} ({count}件が依存)\",\n \"cli.noSnapshot\": \"スナップショットが見つかりません。`archtracker init` を先に実行してください。\",\n \"cli.ciFailed\": \"\\nCI チェック失敗: {count}件の要確認ファイルがあります\",\n \"cli.autoGenerating\": \"スナップショットが無いため自動生成します...\",\n \"cli.project\": \"プロジェクト: {path}\",\n \"cli.validPaths\": \"\\n有効なファイルパス:\",\n \"cli.snapshot\": \"スナップショット: {ts}\",\n\n // MCP\n \"mcp.analyzeComplete\": \"解析完了: {files}ファイル, {edges}エッジ\",\n \"mcp.circularFound\": \"循環参照: {count}件検出\",\n \"mcp.circularNone\": \"循環参照: なし\",\n \"mcp.snapshotSaved\": \"スナップショットを保存しました\",\n \"mcp.autoInit\": \"スナップショットが存在しなかったため、初期スナップショットを自動生成しました。\",\n \"mcp.nextCheckEnabled\": \"次回の実行時から差分チェックが有効になります。\",\n \"mcp.queryRequired\": \"\\\"{mode}\\\" モードでは query パラメータが必須です\",\n\n // Analyze report\n \"analyze.title\": \"# アーキテクチャ分析レポート\\n\",\n \"analyze.overview\": \"## 概要\",\n \"analyze.totalFiles\": \" 総ファイル数: {count}\",\n \"analyze.totalEdges\": \" 総エッジ数: {count}\",\n \"analyze.totalCircular\": \" 循環参照: {count}件\",\n \"analyze.criticalTitle\": \"\\n## 重要コンポーネント (上位{count}件)\",\n \"analyze.criticalItem\": \" {path} ({count}件が依存)\",\n \"analyze.circularTitle\": \"\\n## 循環参照 ({count}件)\",\n \"analyze.circularItem\": \" {files}\",\n \"analyze.orphanTitle\": \"\\n## 孤立ファイル ({count}件)\",\n \"analyze.couplingTitle\": \"\\n## 高結合ファイル (import数 上位{count}件)\",\n \"analyze.couplingItem\": \" {path} ({count}件をimport)\",\n \"analyze.layerTitle\": \"\\n## ディレクトリ構成\",\n \"analyze.layerItem\": \" {dir}/ — {count}ファイル\",\n \"analyze.noIssues\": \"\\nアーキテクチャ上の問題は検出されませんでした。\",\n \"analyze.snapshotSaved\": \"\\n分析と同時にスナップショットを保存しました。\",\n\n // CI\n \"ci.generated\": \"GitHub Actions ワークフローを生成しました: {path}\",\n\n // Web viewer\n \"web.starting\": \"アーキテクチャビューアーを起動中...\",\n \"web.listening\": \"アーキテクチャグラフ: http://localhost:{port}\",\n \"web.stop\": \"Ctrl+C で停止\",\n \"web.watching\": \"{dir}/ を監視中...\",\n \"web.reloading\": \"ファイル変更を検出、リロード中...\",\n \"web.reloaded\": \"グラフを更新しました\",\n\n // Errors\n \"error.analyzer\": \"[解析エラー] {message}\",\n \"error.storage\": \"[ストレージエラー] {message}\",\n \"error.pathTraversal\": \"[セキュリティエラー] {message}\",\n \"error.generic\": \"[エラー] {message}\",\n \"error.unexpected\": \"[エラー] 予期しないエラーが発生しました: {message}\",\n \"error.cli.analyzer\": \"解析エラー: {message}\",\n \"error.cli.storage\": \"ストレージエラー: {message}\",\n \"error.cli.generic\": \"エラー: {message}\",\n \"error.cli.unexpected\": \"予期しないエラー: {message}\",\n} as const;\n","import type { DependencyGraph, FileNode } from \"../types/schema.js\";\nimport { t } from \"../i18n/index.js\";\n\n/** Search result for architecture queries */\nexport interface SearchResult {\n file: string;\n dependents: string[];\n dependencies: string[];\n dependentCount: number;\n dependencyCount: number;\n matchReason: string;\n}\n\n/**\n * Search the dependency graph by file path pattern.\n * Pattern is treated as a literal substring (escaped to prevent ReDoS).\n */\nexport function searchByPath(\n graph: DependencyGraph,\n pattern: string,\n): SearchResult[] {\n const escaped = pattern.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const regex = new RegExp(escaped, \"i\");\n return Object.values(graph.files)\n .filter((f) => regex.test(f.path))\n .map((f) => toSearchResult(f, t(\"search.pathMatch\", { pattern })));\n}\n\n/**\n * Find all files that would be affected if a given file changes.\n * Traverses the dependent chain recursively (reverse dependency tree).\n */\nexport function findAffectedFiles(\n graph: DependencyGraph,\n filePath: string,\n maxDepth: number = 10,\n): SearchResult[] {\n const node = findNode(graph, filePath);\n if (!node) return [];\n\n const visited = new Set<string>();\n const results: SearchResult[] = [];\n\n function traverse(current: FileNode, depth: number, via: string) {\n if (depth > maxDepth || visited.has(current.path)) return;\n visited.add(current.path);\n\n if (current.path !== node!.path) {\n results.push(\n toSearchResult(\n current,\n t(\"search.affected\", { file: filePath, via }),\n ),\n );\n }\n\n for (const depPath of current.dependents) {\n const depNode = graph.files[depPath];\n if (depNode) {\n traverse(depNode, depth + 1, current.path);\n }\n }\n }\n\n traverse(node, 0, filePath);\n return results;\n}\n\n/**\n * Find the most critical files in the project.\n * Ranked by dependent count (files that many other files depend on).\n */\nexport function findCriticalFiles(\n graph: DependencyGraph,\n limit: number = 10,\n): SearchResult[] {\n return Object.values(graph.files)\n .filter((f) => f.dependents.length > 0)\n .sort((a, b) => b.dependents.length - a.dependents.length)\n .slice(0, limit)\n .map((f) =>\n toSearchResult(f, t(\"search.critical\", { count: f.dependents.length })),\n );\n}\n\n/**\n * Find orphan files (no dependencies and no dependents).\n */\nexport function findOrphanFiles(graph: DependencyGraph): SearchResult[] {\n return Object.values(graph.files)\n .filter((f) => f.dependents.length === 0 && f.dependencies.length === 0)\n .map((f) => toSearchResult(f, t(\"search.orphan\")));\n}\n\n/** Find a node by exact or partial path match */\nfunction findNode(\n graph: DependencyGraph,\n filePath: string,\n): FileNode | undefined {\n // Exact match\n if (graph.files[filePath]) return graph.files[filePath];\n\n // Partial match (find first file ending with the pattern)\n const key = Object.keys(graph.files).find(\n (k) => k.endsWith(filePath) || k.includes(filePath),\n );\n return key ? graph.files[key] : undefined;\n}\n\nfunction toSearchResult(node: FileNode, matchReason: string): SearchResult {\n return {\n file: node.path,\n dependents: node.dependents,\n dependencies: node.dependencies,\n dependentCount: node.dependents.length,\n dependencyCount: node.dependencies.length,\n matchReason,\n };\n}\n","import type { DependencyGraph } from \"../types/schema.js\";\nimport { t } from \"../i18n/index.js\";\n\n/**\n * Generate a comprehensive architecture analysis report.\n *\n * Designed for onboarding into existing projects — provides\n * a full overview of dependencies, critical components, circular\n * references, orphan files, coupling hotspots, and directory breakdown.\n */\nexport function formatAnalysisReport(\n graph: DependencyGraph,\n options: { topN?: number } = {},\n): string {\n const topN = options.topN ?? 10;\n const lines: string[] = [];\n const files = Object.values(graph.files);\n\n // ─── Overview ────────────────────────────────────────────\n lines.push(t(\"analyze.title\"));\n lines.push(t(\"analyze.overview\"));\n lines.push(t(\"analyze.totalFiles\", { count: graph.totalFiles }));\n lines.push(t(\"analyze.totalEdges\", { count: graph.totalEdges }));\n lines.push(t(\"analyze.totalCircular\", { count: graph.circularDependencies.length }));\n\n // ─── Critical Components ─────────────────────────────────\n const critical = files\n .filter((f) => f.dependents.length > 0)\n .sort((a, b) => b.dependents.length - a.dependents.length)\n .slice(0, topN);\n\n if (critical.length > 0) {\n lines.push(t(\"analyze.criticalTitle\", { count: critical.length }));\n for (const f of critical) {\n lines.push(t(\"analyze.criticalItem\", { path: f.path, count: f.dependents.length }));\n }\n }\n\n // ─── Circular Dependencies ──────────────────────────────\n if (graph.circularDependencies.length > 0) {\n lines.push(t(\"analyze.circularTitle\", { count: graph.circularDependencies.length }));\n for (const c of graph.circularDependencies) {\n lines.push(t(\"analyze.circularItem\", { files: c.cycle.join(\" → \") }));\n }\n }\n\n // ─── High Coupling (most imports) ───────────────────────\n const highCoupling = files\n .filter((f) => f.dependencies.length > 0)\n .sort((a, b) => b.dependencies.length - a.dependencies.length)\n .slice(0, topN);\n\n if (highCoupling.length > 0) {\n lines.push(t(\"analyze.couplingTitle\", { count: highCoupling.length }));\n for (const f of highCoupling) {\n lines.push(t(\"analyze.couplingItem\", { path: f.path, count: f.dependencies.length }));\n }\n }\n\n // ─── Orphan Files ───────────────────────────────────────\n const orphans = files.filter(\n (f) => f.dependents.length === 0 && f.dependencies.length === 0,\n );\n\n if (orphans.length > 0) {\n lines.push(t(\"analyze.orphanTitle\", { count: orphans.length }));\n for (const f of orphans) {\n lines.push(` ${f.path}`);\n }\n }\n\n // ─── Directory Breakdown ────────────────────────────────\n const dirCounts = new Map<string, number>();\n for (const f of files) {\n const dir = f.path.includes(\"/\") ? f.path.substring(0, f.path.lastIndexOf(\"/\")) : \".\";\n dirCounts.set(dir, (dirCounts.get(dir) ?? 0) + 1);\n }\n\n if (dirCounts.size > 1) {\n lines.push(t(\"analyze.layerTitle\"));\n const sorted = [...dirCounts.entries()].sort((a, b) => b[1] - a[1]);\n for (const [dir, count] of sorted) {\n lines.push(t(\"analyze.layerItem\", { dir, count }));\n }\n }\n\n // ─── Summary ────────────────────────────────────────────\n if (graph.circularDependencies.length === 0 && orphans.length === 0) {\n lines.push(t(\"analyze.noIssues\"));\n }\n\n return lines.join(\"\\n\");\n}\n","import { mkdir, writeFile, readFile, access } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { z } from \"zod\";\nimport type { ArchSnapshot } from \"../types/schema.js\";\nimport { SCHEMA_VERSION } from \"../types/schema.js\";\nimport type { DependencyGraph } from \"../types/schema.js\";\nimport { t } from \"../i18n/index.js\";\n\nconst ARCHTRACKER_DIR = \".archtracker\";\nconst SNAPSHOT_FILE = \"snapshot.json\";\n\n/** Zod schema for runtime validation of loaded snapshots */\nconst FileNodeSchema = z.object({\n path: z.string(),\n exists: z.boolean(),\n dependencies: z.array(z.string()),\n dependents: z.array(z.string()),\n});\n\nconst DependencyGraphSchema = z.object({\n rootDir: z.string(),\n files: z.record(z.string(), FileNodeSchema),\n edges: z.array(z.object({\n source: z.string(),\n target: z.string(),\n type: z.enum([\"static\", \"dynamic\", \"type-only\"]),\n })),\n circularDependencies: z.array(z.object({ cycle: z.array(z.string()) })),\n totalFiles: z.number(),\n totalEdges: z.number(),\n});\n\nconst SnapshotSchema = z.object({\n version: z.literal(SCHEMA_VERSION),\n timestamp: z.string(),\n rootDir: z.string(),\n graph: DependencyGraphSchema,\n});\n\n/**\n * Save a dependency graph as a versioned snapshot.\n *\n * Creates .archtracker/ directory if it doesn't exist.\n * Writes snapshot.json with schema version and timestamp.\n */\nexport async function saveSnapshot(\n projectRoot: string,\n graph: DependencyGraph,\n): Promise<ArchSnapshot> {\n const dirPath = join(projectRoot, ARCHTRACKER_DIR);\n const filePath = join(dirPath, SNAPSHOT_FILE);\n\n const snapshot: ArchSnapshot = {\n version: SCHEMA_VERSION,\n timestamp: new Date().toISOString(),\n rootDir: graph.rootDir,\n graph,\n };\n\n await mkdir(dirPath, { recursive: true });\n await writeFile(filePath, JSON.stringify(snapshot, null, 2), \"utf-8\");\n\n return snapshot;\n}\n\n/**\n * Load the most recent snapshot from .archtracker/snapshot.json.\n *\n * Returns null if no snapshot exists.\n * Validates schema structure with Zod for data integrity.\n */\nexport async function loadSnapshot(\n projectRoot: string,\n): Promise<ArchSnapshot | null> {\n const filePath = join(projectRoot, ARCHTRACKER_DIR, SNAPSHOT_FILE);\n\n // Read file directly — no TOCTOU race with access() check\n let raw: string;\n try {\n raw = await readFile(filePath, \"utf-8\");\n } catch (error: unknown) {\n if (isNodeError(error) && error.code === \"ENOENT\") {\n return null;\n }\n throw new StorageError(\n t(\"storage.readFailed\", { path: filePath }),\n { cause: error },\n );\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new StorageError(\n t(\"storage.parseFailed\", { path: filePath }),\n );\n }\n\n // Validate structure with Zod\n const result = SnapshotSchema.safeParse(parsed);\n if (!result.success) {\n const issues = result.error.issues\n .map((i) => ` ${i.path.join(\".\")}: ${i.message}`)\n .slice(0, 5)\n .join(\"\\n\");\n throw new StorageError(\n t(\"storage.invalidSchema\", { issues }),\n );\n }\n\n return result.data as ArchSnapshot;\n}\n\n/** Check if .archtracker directory exists */\nexport async function hasArchtrackerDir(\n projectRoot: string,\n): Promise<boolean> {\n try {\n await access(join(projectRoot, ARCHTRACKER_DIR));\n return true;\n } catch {\n return false;\n }\n}\n\n/** Custom error class for storage failures */\nexport class StorageError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"StorageError\";\n }\n}\n\nfunction isNodeError(error: unknown): error is NodeJS.ErrnoException {\n return error instanceof Error && \"code\" in error;\n}\n","/**\n * ArchTracker core schema definitions.\n * Snapshot format is versioned for backward compatibility.\n */\n\nexport const SCHEMA_VERSION = \"1.0\" as const;\n\n/** A single dependency edge: source imports target */\nexport interface DependencyEdge {\n source: string;\n target: string;\n type: \"static\" | \"dynamic\" | \"type-only\";\n}\n\n/** A file node in the architecture graph */\nexport interface FileNode {\n path: string;\n exists: boolean;\n dependencies: string[];\n dependents: string[];\n}\n\n/** A detected circular dependency */\nexport interface CircularDependency {\n cycle: string[];\n}\n\n/** The full dependency graph */\nexport interface DependencyGraph {\n rootDir: string;\n files: Record<string, FileNode>;\n edges: DependencyEdge[];\n circularDependencies: CircularDependency[];\n totalFiles: number;\n totalEdges: number;\n}\n\n/** Persisted snapshot with schema version */\nexport interface ArchSnapshot {\n version: typeof SCHEMA_VERSION;\n timestamp: string;\n rootDir: string;\n graph: DependencyGraph;\n}\n\n/** Diff result between two snapshots */\nexport interface ArchDiff {\n added: string[];\n removed: string[];\n modified: string[];\n affectedDependents: Array<{\n file: string;\n reason: string;\n dependsOn: string;\n }>;\n}\n\n/** Context summary for AI session initialization */\nexport interface ArchContext {\n validPaths: string[];\n summary: string;\n snapshotExists: boolean;\n snapshotTimestamp?: string;\n keyComponents: Array<{\n path: string;\n dependentCount: number;\n dependencyCount: number;\n }>;\n}\n","import type { ArchDiff, DependencyGraph } from \"../types/schema.js\";\nimport { t } from \"../i18n/index.js\";\n\n/**\n * Compute the diff between an old and new dependency graph.\n *\n * Identifies:\n * - Added files (new in the codebase)\n * - Removed files (deleted from the codebase)\n * - Modified files (dependencies changed)\n * - Affected dependents (files that depend on changed/removed files and may need updates)\n */\nexport function computeDiff(\n oldGraph: DependencyGraph,\n newGraph: DependencyGraph,\n): ArchDiff {\n const oldFiles = new Set(Object.keys(oldGraph.files));\n const newFiles = new Set(Object.keys(newGraph.files));\n\n // Files that exist in new but not in old\n const added = [...newFiles].filter((f) => !oldFiles.has(f));\n\n // Files that existed in old but not in new\n const removed = [...oldFiles].filter((f) => !newFiles.has(f));\n\n // Files that exist in both but have different dependency sets\n const modified: string[] = [];\n for (const file of newFiles) {\n if (!oldFiles.has(file)) continue;\n\n const oldDeps = oldGraph.files[file].dependencies.slice().sort();\n const newDeps = newGraph.files[file].dependencies.slice().sort();\n\n if (!arraysEqual(oldDeps, newDeps)) {\n modified.push(file);\n }\n }\n\n // Find all files that depend on changed/removed files\n const removedSet = new Set(removed);\n const changedFiles = new Set([...removed, ...modified]);\n const affectedDependents: ArchDiff[\"affectedDependents\"] = [];\n const seenAffected = new Set<string>();\n\n for (const changedFile of changedFiles) {\n // Look up dependents in the NEW graph (for modified files)\n // and in the OLD graph (for removed files)\n const graph = removedSet.has(changedFile) ? oldGraph : newGraph;\n const node = graph.files[changedFile];\n if (!node) continue;\n\n for (const dependent of node.dependents) {\n const key = `${dependent}→${changedFile}`;\n if (seenAffected.has(key)) continue;\n seenAffected.add(key);\n\n const reason = removedSet.has(changedFile)\n ? t(\"diff.reasonRemoved\", { file: changedFile })\n : t(\"diff.reasonModified\", { file: changedFile });\n\n affectedDependents.push({\n file: dependent,\n reason,\n dependsOn: changedFile,\n });\n }\n }\n\n // Also check for files that depend on added files\n // (new dependencies that might need verification)\n for (const addedFile of added) {\n const node = newGraph.files[addedFile];\n if (!node) continue;\n\n for (const dependent of node.dependents) {\n const key = `${dependent}→${addedFile}`;\n if (seenAffected.has(key)) continue;\n seenAffected.add(key);\n\n affectedDependents.push({\n file: dependent,\n reason: t(\"diff.reasonAdded\", { file: addedFile }),\n dependsOn: addedFile,\n });\n }\n }\n\n return { added, removed, modified, affectedDependents };\n}\n\n/** Generate a human-readable report from an ArchDiff */\nexport function formatDiffReport(diff: ArchDiff): string {\n const lines: string[] = [];\n\n lines.push(t(\"diff.title\"));\n\n if (diff.added.length === 0 && diff.removed.length === 0 && diff.modified.length === 0) {\n lines.push(t(\"diff.noChanges\"));\n return lines.join(\"\\n\");\n }\n\n if (diff.added.length > 0) {\n lines.push(t(\"diff.added\", { count: diff.added.length }));\n for (const f of diff.added) {\n lines.push(` + ${f}`);\n }\n lines.push(\"\");\n }\n\n if (diff.removed.length > 0) {\n lines.push(t(\"diff.removed\", { count: diff.removed.length }));\n for (const f of diff.removed) {\n lines.push(` - ${f}`);\n }\n lines.push(\"\");\n }\n\n if (diff.modified.length > 0) {\n lines.push(t(\"diff.modified\", { count: diff.modified.length }));\n for (const f of diff.modified) {\n lines.push(` ~ ${f}`);\n }\n lines.push(\"\");\n }\n\n if (diff.affectedDependents.length > 0) {\n lines.push(t(\"diff.affected\", { count: diff.affectedDependents.length }));\n for (const a of diff.affectedDependents) {\n lines.push(` ! ${a.file}`);\n lines.push(` ${a.reason}`);\n }\n lines.push(\"\");\n }\n\n return lines.join(\"\\n\");\n}\n\nfunction arraysEqual(a: string[], b: string[]): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n","import { resolve } from \"node:path\";\nimport { t } from \"../i18n/index.js\";\n\n/**\n * Validate that a path resolves within the allowed boundary (CWD by default).\n * Prevents path traversal attacks via ../../../etc/passwd style inputs.\n */\nexport function validatePath(\n inputPath: string,\n boundary?: string,\n): string {\n const resolved = resolve(inputPath);\n const root = boundary ? resolve(boundary) : process.cwd();\n\n if (!resolved.startsWith(root)) {\n throw new PathTraversalError(\n t(\"pathGuard.traversal\", { input: inputPath, resolved, boundary: root }),\n );\n }\n return resolved;\n}\n\nexport class PathTraversalError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"PathTraversalError\";\n }\n}\n"],"mappings":";AAAA,SAAS,iBAAiB;AAC1B,SAAS,4BAA4B;AACrC,SAAS,KAAAA,UAAS;;;ACFlB,SAAS,eAAyB;AAClC,SAAS,cAAc;AAqBvB,IAAM,kBAAkB;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAaA,eAAsB,eACpB,SACA,UAA0B,CAAC,GACD;AAC1B,QAAM;AAAA,IACJ,UAAU,CAAC;AAAA,IACX,WAAW;AAAA,IACX;AAAA,IACA,kBAAkB;AAAA,EACpB,IAAI;AAEJ,QAAM,aAAa,QAAQ,OAAO;AAClC,QAAM,aAAa,CAAC,GAAG,iBAAiB,GAAG,OAAO;AAClD,QAAM,iBAAiB,WAAW,KAAK,GAAG;AAI1C,QAAM,gBAAyC;AAAA,IAC7C,SAAS;AAAA,IACT,SAAS,EAAE,MAAM,eAAe;AAAA,IAChC,aAAa,EAAE,MAAM,eAAe;AAAA,IACpC;AAAA,IACA,sBAAsB,kBAAkB,OAAO;AAAA,IAC/C,sBAAsB;AAAA,EACxB;AAEA,MAAI,cAAc;AAChB,kBAAc,WAAW,EAAE,UAAU,aAAa;AAAA,EACpD;AAEA,MAAI;AACJ,MAAI;AACF,aAAS,MAAM,OAAO,CAAC,GAAG,GAAG,aAAa;AAAA,EAC5C,SAAS,OAAO;AACd,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,UAAM,IAAI;AAAA,MACR,oFAAkC,OAAO;AAAA,MACzC,EAAE,OAAO,MAAM;AAAA,IACjB;AAAA,EACF;AAEA,MAAI,OAAO,aAAa,KAAK,CAAC,OAAO,QAAQ;AAC3C,UAAM,IAAI;AAAA,MACR,0DAAa,OAAO,QAAQ;AAAA,IAC9B;AAAA,EACF;AAEA,QAAM,eAAe,OAAO;AAC5B,SAAO,WAAW,YAAY,YAAY;AAC5C;AAGA,SAAS,WACP,SACA,cACiB;AACjB,QAAM,QAAkC,CAAC;AACzC,QAAM,QAA0B,CAAC;AACjC,QAAM,cAAc,oBAAI,IAAY;AACpC,QAAM,uBAA6C,CAAC;AAIpD,aAAW,OAAO,aAAa,SAAS;AACtC,QAAI,iBAAiB,GAAG,EAAG;AAE3B,UAAM,IAAI,MAAM,IAAI;AAAA,MAClB,MAAM,IAAI;AAAA,MACV,QAAQ,CAAC,IAAI;AAAA,MACb,cAAc,CAAC;AAAA,MACf,YAAY,CAAC;AAAA,IACf;AAAA,EACF;AAGA,aAAW,OAAO,aAAa,SAAS;AACtC,eAAW,OAAO,IAAI,cAAc;AAElC,UAAI,IAAI,mBAAmB,IAAI,WAAY;AAE3C,UAAI,CAAC,MAAM,IAAI,MAAM,KAAK,cAAc,GAAG,EAAG;AAE9C,YAAM,WAAW,IAAI,WAChB,cACD,IAAI,UACD,YACA;AAEP,YAAM,KAAK;AAAA,QACT,QAAQ,IAAI;AAAA,QACZ,QAAQ,IAAI;AAAA,QACZ,MAAM;AAAA,MACR,CAAC;AAGD,UAAI,MAAM,IAAI,MAAM,GAAG;AACrB,cAAM,IAAI,MAAM,EAAE,aAAa,KAAK,IAAI,QAAQ;AAAA,MAClD;AACA,UAAI,MAAM,IAAI,QAAQ,GAAG;AACvB,cAAM,IAAI,QAAQ,EAAE,WAAW,KAAK,IAAI,MAAM;AAAA,MAChD;AAGA,UAAI,IAAI,YAAY,IAAI,OAAO;AAC7B,cAAM,YAAY,IAAI,MAAM,IAAI,CAAC,MAAM,EAAE,IAAI;AAC7C,cAAM,WAAW,CAAC,GAAG,SAAS,EAAE,KAAK,EAAE,KAAK,QAAG;AAC/C,YAAI,CAAC,YAAY,IAAI,QAAQ,GAAG;AAC9B,sBAAY,IAAI,QAAQ;AACxB,+BAAqB,KAAK,EAAE,OAAO,UAAU,CAAC;AAAA,QAChD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY,OAAO,KAAK,KAAK,EAAE;AAAA,IAC/B,YAAY,MAAM;AAAA,EACpB;AACF;AAMA,SAAS,iBAAiB,KAAoF;AAC5G,MAAI,IAAI,WAAY,QAAO;AAC3B,QAAM,WAAW,IAAI,mBAAmB,CAAC;AACzC,MAAI,SAAS,KAAK,CAACC,OAAMA,GAAE,WAAW,KAAK,KAAKA,OAAM,MAAM,EAAG,QAAO;AAEtE,SAAO,eAAe,IAAI,MAAM;AAClC;AAKA,SAAS,cAAc,KAAoF;AACzG,MAAI,IAAI,WAAY,QAAO;AAC3B,MAAI,IAAI,gBAAgB,KAAK,CAACA,OAAMA,GAAE,WAAW,KAAK,KAAKA,OAAM,MAAM,EAAG,QAAO;AACjF,SAAO,eAAe,IAAI,QAAQ;AACpC;AAOA,SAAS,eAAe,QAAyB;AAE/C,MAAI,OAAO,WAAW,GAAG,EAAG,QAAO;AAEnC,MAAI,CAAC,OAAO,SAAS,GAAG,KAAK,CAAC,OAAO,SAAS,IAAI,KAAK,CAAC,OAAO,SAAS,GAAG,EAAG,QAAO;AAErF,MAAI,OAAO,WAAW,OAAO,EAAG,QAAO;AACvC,SAAO;AACT;AAGO,IAAM,gBAAN,cAA4B,MAAM;AAAA,EACvC,YAAY,SAAiB,SAAwB;AACnD,UAAM,SAAS,OAAO;AACtB,SAAK,OAAO;AAAA,EACd;AACF;;;AC9MA,IAAI,gBAAwB,aAAa;AAazC,SAAS,eAAuB;AAC9B,QAAM,MAAM,QAAQ,IAAI,UAAU,QAAQ,IAAI,QAAQ;AACtD,MAAI,IAAI,WAAW,IAAI,EAAG,QAAO;AACjC,SAAO;AACT;AAGO,SAAS,EAAE,KAAa,MAAgD;AAC7E,QAAM,WAAW,kBAAkB,OAAO,KAAK;AAC/C,MAAI,MAAO,SAAoC,GAAG,KAAM,GAA8B,GAAG,KAAK;AAE9F,MAAI,MAAM;AACR,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,IAAI,GAAG;AACzC,YAAM,IAAI,WAAW,IAAI,CAAC,KAAK,OAAO,CAAC,CAAC;AAAA,IAC1C;AAAA,EACF;AACA,SAAO;AACT;AAIA,IAAM,KAAK;AAAA;AAAA,EAET,mBAAmB;AAAA,EACnB,qBAAqB;AAAA;AAAA,EAGrB,uBAAuB;AAAA,EACvB,sBAAsB;AAAA,EACtB,yBAAyB;AAAA,EACzB,2BAA2B;AAAA;AAAA,EAG3B,uBAAuB;AAAA;AAAA,EAGvB,cAAc;AAAA,EACd,kBAAkB;AAAA,EAClB,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,sBAAsB;AAAA,EACtB,uBAAuB;AAAA,EACvB,oBAAoB;AAAA;AAAA,EAGpB,oBAAoB;AAAA,EACpB,mBAAmB;AAAA,EACnB,mBAAmB;AAAA,EACnB,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,kBAAkB;AAAA;AAAA,EAGlB,iBAAiB;AAAA,EACjB,qBAAqB;AAAA,EACrB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,gBAAgB;AAAA,EAChB,sBAAsB;AAAA,EACtB,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,gBAAgB;AAAA;AAAA,EAGhB,uBAAuB;AAAA,EACvB,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,qBAAqB;AAAA,EACrB,gBAAgB;AAAA,EAChB,wBAAwB;AAAA,EACxB,qBAAqB;AAAA;AAAA,EAGrB,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,sBAAsB;AAAA,EACtB,sBAAsB;AAAA,EACtB,yBAAyB;AAAA,EACzB,yBAAyB;AAAA,EACzB,wBAAwB;AAAA,EACxB,yBAAyB;AAAA,EACzB,wBAAwB;AAAA,EACxB,uBAAuB;AAAA,EACvB,yBAAyB;AAAA,EACzB,wBAAwB;AAAA,EACxB,sBAAsB;AAAA,EACtB,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,yBAAyB;AAAA;AAAA,EAGzB,gBAAgB;AAAA;AAAA,EAGhB,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,gBAAgB;AAAA;AAAA,EAGhB,kBAAkB;AAAA,EAClB,iBAAiB;AAAA,EACjB,uBAAuB;AAAA,EACvB,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,sBAAsB;AAAA,EACtB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,wBAAwB;AAC1B;AAEA,IAAM,KAAK;AAAA;AAAA,EAET,mBAAmB;AAAA,EACnB,qBAAqB;AAAA;AAAA,EAGrB,uBAAuB;AAAA,EACvB,sBAAsB;AAAA,EACtB,yBAAyB;AAAA,EACzB,2BAA2B;AAAA;AAAA,EAG3B,uBAAuB;AAAA;AAAA,EAGvB,cAAc;AAAA,EACd,kBAAkB;AAAA,EAClB,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,sBAAsB;AAAA,EACtB,uBAAuB;AAAA,EACvB,oBAAoB;AAAA;AAAA,EAGpB,oBAAoB;AAAA,EACpB,mBAAmB;AAAA,EACnB,mBAAmB;AAAA,EACnB,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,kBAAkB;AAAA;AAAA,EAGlB,iBAAiB;AAAA,EACjB,qBAAqB;AAAA,EACrB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,gBAAgB;AAAA,EAChB,sBAAsB;AAAA,EACtB,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,gBAAgB;AAAA;AAAA,EAGhB,uBAAuB;AAAA,EACvB,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,qBAAqB;AAAA,EACrB,gBAAgB;AAAA,EAChB,wBAAwB;AAAA,EACxB,qBAAqB;AAAA;AAAA,EAGrB,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,sBAAsB;AAAA,EACtB,sBAAsB;AAAA,EACtB,yBAAyB;AAAA,EACzB,yBAAyB;AAAA,EACzB,wBAAwB;AAAA,EACxB,yBAAyB;AAAA,EACzB,wBAAwB;AAAA,EACxB,uBAAuB;AAAA,EACvB,yBAAyB;AAAA,EACzB,wBAAwB;AAAA,EACxB,sBAAsB;AAAA,EACtB,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,yBAAyB;AAAA;AAAA,EAGzB,gBAAgB;AAAA;AAAA,EAGhB,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,gBAAgB;AAAA;AAAA,EAGhB,kBAAkB;AAAA,EAClB,iBAAiB;AAAA,EACjB,uBAAuB;AAAA,EACvB,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,sBAAsB;AAAA,EACtB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,wBAAwB;AAC1B;;;ACvNO,SAAS,aACd,OACA,SACgB;AAChB,QAAM,UAAU,QAAQ,QAAQ,uBAAuB,MAAM;AAC7D,QAAM,QAAQ,IAAI,OAAO,SAAS,GAAG;AACrC,SAAO,OAAO,OAAO,MAAM,KAAK,EAC7B,OAAO,CAAC,MAAM,MAAM,KAAK,EAAE,IAAI,CAAC,EAChC,IAAI,CAAC,MAAM,eAAe,GAAG,EAAE,oBAAoB,EAAE,QAAQ,CAAC,CAAC,CAAC;AACrE;AAMO,SAAS,kBACd,OACA,UACA,WAAmB,IACH;AAChB,QAAM,OAAO,SAAS,OAAO,QAAQ;AACrC,MAAI,CAAC,KAAM,QAAO,CAAC;AAEnB,QAAM,UAAU,oBAAI,IAAY;AAChC,QAAM,UAA0B,CAAC;AAEjC,WAAS,SAAS,SAAmB,OAAe,KAAa;AAC/D,QAAI,QAAQ,YAAY,QAAQ,IAAI,QAAQ,IAAI,EAAG;AACnD,YAAQ,IAAI,QAAQ,IAAI;AAExB,QAAI,QAAQ,SAAS,KAAM,MAAM;AAC/B,cAAQ;AAAA,QACN;AAAA,UACE;AAAA,UACA,EAAE,mBAAmB,EAAE,MAAM,UAAU,IAAI,CAAC;AAAA,QAC9C;AAAA,MACF;AAAA,IACF;AAEA,eAAW,WAAW,QAAQ,YAAY;AACxC,YAAM,UAAU,MAAM,MAAM,OAAO;AACnC,UAAI,SAAS;AACX,iBAAS,SAAS,QAAQ,GAAG,QAAQ,IAAI;AAAA,MAC3C;AAAA,IACF;AAAA,EACF;AAEA,WAAS,MAAM,GAAG,QAAQ;AAC1B,SAAO;AACT;AAMO,SAAS,kBACd,OACA,QAAgB,IACA;AAChB,SAAO,OAAO,OAAO,MAAM,KAAK,EAC7B,OAAO,CAAC,MAAM,EAAE,WAAW,SAAS,CAAC,EACrC,KAAK,CAAC,GAAG,MAAM,EAAE,WAAW,SAAS,EAAE,WAAW,MAAM,EACxD,MAAM,GAAG,KAAK,EACd;AAAA,IAAI,CAAC,MACJ,eAAe,GAAG,EAAE,mBAAmB,EAAE,OAAO,EAAE,WAAW,OAAO,CAAC,CAAC;AAAA,EACxE;AACJ;AAKO,SAAS,gBAAgB,OAAwC;AACtE,SAAO,OAAO,OAAO,MAAM,KAAK,EAC7B,OAAO,CAAC,MAAM,EAAE,WAAW,WAAW,KAAK,EAAE,aAAa,WAAW,CAAC,EACtE,IAAI,CAAC,MAAM,eAAe,GAAG,EAAE,eAAe,CAAC,CAAC;AACrD;AAGA,SAAS,SACP,OACA,UACsB;AAEtB,MAAI,MAAM,MAAM,QAAQ,EAAG,QAAO,MAAM,MAAM,QAAQ;AAGtD,QAAM,MAAM,OAAO,KAAK,MAAM,KAAK,EAAE;AAAA,IACnC,CAAC,MAAM,EAAE,SAAS,QAAQ,KAAK,EAAE,SAAS,QAAQ;AAAA,EACpD;AACA,SAAO,MAAM,MAAM,MAAM,GAAG,IAAI;AAClC;AAEA,SAAS,eAAe,MAAgB,aAAmC;AACzE,SAAO;AAAA,IACL,MAAM,KAAK;AAAA,IACX,YAAY,KAAK;AAAA,IACjB,cAAc,KAAK;AAAA,IACnB,gBAAgB,KAAK,WAAW;AAAA,IAChC,iBAAiB,KAAK,aAAa;AAAA,IACnC;AAAA,EACF;AACF;;;AC5GO,SAAS,qBACd,OACA,UAA6B,CAAC,GACtB;AACR,QAAM,OAAO,QAAQ,QAAQ;AAC7B,QAAM,QAAkB,CAAC;AACzB,QAAM,QAAQ,OAAO,OAAO,MAAM,KAAK;AAGvC,QAAM,KAAK,EAAE,eAAe,CAAC;AAC7B,QAAM,KAAK,EAAE,kBAAkB,CAAC;AAChC,QAAM,KAAK,EAAE,sBAAsB,EAAE,OAAO,MAAM,WAAW,CAAC,CAAC;AAC/D,QAAM,KAAK,EAAE,sBAAsB,EAAE,OAAO,MAAM,WAAW,CAAC,CAAC;AAC/D,QAAM,KAAK,EAAE,yBAAyB,EAAE,OAAO,MAAM,qBAAqB,OAAO,CAAC,CAAC;AAGnF,QAAM,WAAW,MACd,OAAO,CAAC,MAAM,EAAE,WAAW,SAAS,CAAC,EACrC,KAAK,CAAC,GAAG,MAAM,EAAE,WAAW,SAAS,EAAE,WAAW,MAAM,EACxD,MAAM,GAAG,IAAI;AAEhB,MAAI,SAAS,SAAS,GAAG;AACvB,UAAM,KAAK,EAAE,yBAAyB,EAAE,OAAO,SAAS,OAAO,CAAC,CAAC;AACjE,eAAW,KAAK,UAAU;AACxB,YAAM,KAAK,EAAE,wBAAwB,EAAE,MAAM,EAAE,MAAM,OAAO,EAAE,WAAW,OAAO,CAAC,CAAC;AAAA,IACpF;AAAA,EACF;AAGA,MAAI,MAAM,qBAAqB,SAAS,GAAG;AACzC,UAAM,KAAK,EAAE,yBAAyB,EAAE,OAAO,MAAM,qBAAqB,OAAO,CAAC,CAAC;AACnF,eAAW,KAAK,MAAM,sBAAsB;AAC1C,YAAM,KAAK,EAAE,wBAAwB,EAAE,OAAO,EAAE,MAAM,KAAK,UAAK,EAAE,CAAC,CAAC;AAAA,IACtE;AAAA,EACF;AAGA,QAAM,eAAe,MAClB,OAAO,CAAC,MAAM,EAAE,aAAa,SAAS,CAAC,EACvC,KAAK,CAAC,GAAG,MAAM,EAAE,aAAa,SAAS,EAAE,aAAa,MAAM,EAC5D,MAAM,GAAG,IAAI;AAEhB,MAAI,aAAa,SAAS,GAAG;AAC3B,UAAM,KAAK,EAAE,yBAAyB,EAAE,OAAO,aAAa,OAAO,CAAC,CAAC;AACrE,eAAW,KAAK,cAAc;AAC5B,YAAM,KAAK,EAAE,wBAAwB,EAAE,MAAM,EAAE,MAAM,OAAO,EAAE,aAAa,OAAO,CAAC,CAAC;AAAA,IACtF;AAAA,EACF;AAGA,QAAM,UAAU,MAAM;AAAA,IACpB,CAAC,MAAM,EAAE,WAAW,WAAW,KAAK,EAAE,aAAa,WAAW;AAAA,EAChE;AAEA,MAAI,QAAQ,SAAS,GAAG;AACtB,UAAM,KAAK,EAAE,uBAAuB,EAAE,OAAO,QAAQ,OAAO,CAAC,CAAC;AAC9D,eAAW,KAAK,SAAS;AACvB,YAAM,KAAK,KAAK,EAAE,IAAI,EAAE;AAAA,IAC1B;AAAA,EACF;AAGA,QAAM,YAAY,oBAAI,IAAoB;AAC1C,aAAW,KAAK,OAAO;AACrB,UAAM,MAAM,EAAE,KAAK,SAAS,GAAG,IAAI,EAAE,KAAK,UAAU,GAAG,EAAE,KAAK,YAAY,GAAG,CAAC,IAAI;AAClF,cAAU,IAAI,MAAM,UAAU,IAAI,GAAG,KAAK,KAAK,CAAC;AAAA,EAClD;AAEA,MAAI,UAAU,OAAO,GAAG;AACtB,UAAM,KAAK,EAAE,oBAAoB,CAAC;AAClC,UAAM,SAAS,CAAC,GAAG,UAAU,QAAQ,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;AAClE,eAAW,CAAC,KAAK,KAAK,KAAK,QAAQ;AACjC,YAAM,KAAK,EAAE,qBAAqB,EAAE,KAAK,MAAM,CAAC,CAAC;AAAA,IACnD;AAAA,EACF;AAGA,MAAI,MAAM,qBAAqB,WAAW,KAAK,QAAQ,WAAW,GAAG;AACnE,UAAM,KAAK,EAAE,kBAAkB,CAAC;AAAA,EAClC;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;;;AC5FA,SAAS,OAAO,WAAW,UAAU,cAAc;AACnD,SAAS,YAAY;AACrB,SAAS,SAAS;;;ACGX,IAAM,iBAAiB;;;ADG9B,IAAM,kBAAkB;AACxB,IAAM,gBAAgB;AAGtB,IAAM,iBAAiB,EAAE,OAAO;AAAA,EAC9B,MAAM,EAAE,OAAO;AAAA,EACf,QAAQ,EAAE,QAAQ;AAAA,EAClB,cAAc,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA,EAChC,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC;AAChC,CAAC;AAED,IAAM,wBAAwB,EAAE,OAAO;AAAA,EACrC,SAAS,EAAE,OAAO;AAAA,EAClB,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,cAAc;AAAA,EAC1C,OAAO,EAAE,MAAM,EAAE,OAAO;AAAA,IACtB,QAAQ,EAAE,OAAO;AAAA,IACjB,QAAQ,EAAE,OAAO;AAAA,IACjB,MAAM,EAAE,KAAK,CAAC,UAAU,WAAW,WAAW,CAAC;AAAA,EACjD,CAAC,CAAC;AAAA,EACF,sBAAsB,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;AAAA,EACtE,YAAY,EAAE,OAAO;AAAA,EACrB,YAAY,EAAE,OAAO;AACvB,CAAC;AAED,IAAM,iBAAiB,EAAE,OAAO;AAAA,EAC9B,SAAS,EAAE,QAAQ,cAAc;AAAA,EACjC,WAAW,EAAE,OAAO;AAAA,EACpB,SAAS,EAAE,OAAO;AAAA,EAClB,OAAO;AACT,CAAC;AAQD,eAAsB,aACpB,aACA,OACuB;AACvB,QAAM,UAAU,KAAK,aAAa,eAAe;AACjD,QAAM,WAAW,KAAK,SAAS,aAAa;AAE5C,QAAM,WAAyB;AAAA,IAC7B,SAAS;AAAA,IACT,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IAClC,SAAS,MAAM;AAAA,IACf;AAAA,EACF;AAEA,QAAM,MAAM,SAAS,EAAE,WAAW,KAAK,CAAC;AACxC,QAAM,UAAU,UAAU,KAAK,UAAU,UAAU,MAAM,CAAC,GAAG,OAAO;AAEpE,SAAO;AACT;AAQA,eAAsB,aACpB,aAC8B;AAC9B,QAAM,WAAW,KAAK,aAAa,iBAAiB,aAAa;AAGjE,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,SAAS,UAAU,OAAO;AAAA,EACxC,SAAS,OAAgB;AACvB,QAAI,YAAY,KAAK,KAAK,MAAM,SAAS,UAAU;AACjD,aAAO;AAAA,IACT;AACA,UAAM,IAAI;AAAA,MACR,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAAA,MAC1C,EAAE,OAAO,MAAM;AAAA,IACjB;AAAA,EACF;AAEA,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,GAAG;AAAA,EACzB,QAAQ;AACN,UAAM,IAAI;AAAA,MACR,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAAA,IAC7C;AAAA,EACF;AAGA,QAAM,SAAS,eAAe,UAAU,MAAM;AAC9C,MAAI,CAAC,OAAO,SAAS;AACnB,UAAM,SAAS,OAAO,MAAM,OACzB,IAAI,CAAC,MAAM,KAAK,EAAE,KAAK,KAAK,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAChD,MAAM,GAAG,CAAC,EACV,KAAK,IAAI;AACZ,UAAM,IAAI;AAAA,MACR,EAAE,yBAAyB,EAAE,OAAO,CAAC;AAAA,IACvC;AAAA,EACF;AAEA,SAAO,OAAO;AAChB;AAeO,IAAM,eAAN,cAA2B,MAAM;AAAA,EACtC,YAAY,SAAiB,SAAwB;AACnD,UAAM,SAAS,OAAO;AACtB,SAAK,OAAO;AAAA,EACd;AACF;AAEA,SAAS,YAAY,OAAgD;AACnE,SAAO,iBAAiB,SAAS,UAAU;AAC7C;;;AE5HO,SAAS,YACd,UACA,UACU;AACV,QAAM,WAAW,IAAI,IAAI,OAAO,KAAK,SAAS,KAAK,CAAC;AACpD,QAAM,WAAW,IAAI,IAAI,OAAO,KAAK,SAAS,KAAK,CAAC;AAGpD,QAAM,QAAQ,CAAC,GAAG,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC;AAG1D,QAAM,UAAU,CAAC,GAAG,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC;AAG5D,QAAM,WAAqB,CAAC;AAC5B,aAAW,QAAQ,UAAU;AAC3B,QAAI,CAAC,SAAS,IAAI,IAAI,EAAG;AAEzB,UAAM,UAAU,SAAS,MAAM,IAAI,EAAE,aAAa,MAAM,EAAE,KAAK;AAC/D,UAAM,UAAU,SAAS,MAAM,IAAI,EAAE,aAAa,MAAM,EAAE,KAAK;AAE/D,QAAI,CAAC,YAAY,SAAS,OAAO,GAAG;AAClC,eAAS,KAAK,IAAI;AAAA,IACpB;AAAA,EACF;AAGA,QAAM,aAAa,IAAI,IAAI,OAAO;AAClC,QAAM,eAAe,oBAAI,IAAI,CAAC,GAAG,SAAS,GAAG,QAAQ,CAAC;AACtD,QAAM,qBAAqD,CAAC;AAC5D,QAAM,eAAe,oBAAI,IAAY;AAErC,aAAW,eAAe,cAAc;AAGtC,UAAM,QAAQ,WAAW,IAAI,WAAW,IAAI,WAAW;AACvD,UAAM,OAAO,MAAM,MAAM,WAAW;AACpC,QAAI,CAAC,KAAM;AAEX,eAAW,aAAa,KAAK,YAAY;AACvC,YAAM,MAAM,GAAG,SAAS,SAAI,WAAW;AACvC,UAAI,aAAa,IAAI,GAAG,EAAG;AAC3B,mBAAa,IAAI,GAAG;AAEpB,YAAM,SAAS,WAAW,IAAI,WAAW,IACrC,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC,IAC7C,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAElD,yBAAmB,KAAK;AAAA,QACtB,MAAM;AAAA,QACN;AAAA,QACA,WAAW;AAAA,MACb,CAAC;AAAA,IACH;AAAA,EACF;AAIA,aAAW,aAAa,OAAO;AAC7B,UAAM,OAAO,SAAS,MAAM,SAAS;AACrC,QAAI,CAAC,KAAM;AAEX,eAAW,aAAa,KAAK,YAAY;AACvC,YAAM,MAAM,GAAG,SAAS,SAAI,SAAS;AACrC,UAAI,aAAa,IAAI,GAAG,EAAG;AAC3B,mBAAa,IAAI,GAAG;AAEpB,yBAAmB,KAAK;AAAA,QACtB,MAAM;AAAA,QACN,QAAQ,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAAA,QACjD,WAAW;AAAA,MACb,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO,EAAE,OAAO,SAAS,UAAU,mBAAmB;AACxD;AAGO,SAAS,iBAAiB,MAAwB;AACvD,QAAM,QAAkB,CAAC;AAEzB,QAAM,KAAK,EAAE,YAAY,CAAC;AAE1B,MAAI,KAAK,MAAM,WAAW,KAAK,KAAK,QAAQ,WAAW,KAAK,KAAK,SAAS,WAAW,GAAG;AACtF,UAAM,KAAK,EAAE,gBAAgB,CAAC;AAC9B,WAAO,MAAM,KAAK,IAAI;AAAA,EACxB;AAEA,MAAI,KAAK,MAAM,SAAS,GAAG;AACzB,UAAM,KAAK,EAAE,cAAc,EAAE,OAAO,KAAK,MAAM,OAAO,CAAC,CAAC;AACxD,eAAW,KAAK,KAAK,OAAO;AAC1B,YAAM,KAAK,OAAO,CAAC,EAAE;AAAA,IACvB;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,MAAI,KAAK,QAAQ,SAAS,GAAG;AAC3B,UAAM,KAAK,EAAE,gBAAgB,EAAE,OAAO,KAAK,QAAQ,OAAO,CAAC,CAAC;AAC5D,eAAW,KAAK,KAAK,SAAS;AAC5B,YAAM,KAAK,OAAO,CAAC,EAAE;AAAA,IACvB;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,MAAI,KAAK,SAAS,SAAS,GAAG;AAC5B,UAAM,KAAK,EAAE,iBAAiB,EAAE,OAAO,KAAK,SAAS,OAAO,CAAC,CAAC;AAC9D,eAAW,KAAK,KAAK,UAAU;AAC7B,YAAM,KAAK,OAAO,CAAC,EAAE;AAAA,IACvB;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,MAAI,KAAK,mBAAmB,SAAS,GAAG;AACtC,UAAM,KAAK,EAAE,iBAAiB,EAAE,OAAO,KAAK,mBAAmB,OAAO,CAAC,CAAC;AACxE,eAAW,KAAK,KAAK,oBAAoB;AACvC,YAAM,KAAK,OAAO,EAAE,IAAI,EAAE;AAC1B,YAAM,KAAK,OAAO,EAAE,MAAM,EAAE;AAAA,IAC9B;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,YAAY,GAAa,GAAsB;AACtD,MAAI,EAAE,WAAW,EAAE,OAAQ,QAAO;AAClC,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;AACjC,QAAI,EAAE,CAAC,MAAM,EAAE,CAAC,EAAG,QAAO;AAAA,EAC5B;AACA,SAAO;AACT;;;AC/IA,SAAS,WAAAC,gBAAe;AAOjB,SAAS,aACd,WACA,UACQ;AACR,QAAM,WAAWC,SAAQ,SAAS;AAClC,QAAM,OAAO,WAAWA,SAAQ,QAAQ,IAAI,QAAQ,IAAI;AAExD,MAAI,CAAC,SAAS,WAAW,IAAI,GAAG;AAC9B,UAAM,IAAI;AAAA,MACR,EAAE,uBAAuB,EAAE,OAAO,WAAW,UAAU,UAAU,KAAK,CAAC;AAAA,IACzE;AAAA,EACF;AACA,SAAO;AACT;AAEO,IAAM,qBAAN,cAAiC,MAAM;AAAA,EAC5C,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;;;ARJA,IAAM,SAAS,IAAI,UAAU;AAAA,EAC3B,MAAM;AAAA,EACN,SAAS;AACX,CAAC;AAID,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA;AAAA,IACE,WAAWC,GACR,OAAO,EACP,QAAQ,KAAK,EACb,SAAS,sCAAsC;AAAA,IAClD,SAASA,GACN,MAAMA,GAAE,OAAO,CAAC,EAChB,SAAS,EACT,SAAS,4DAA4D;AAAA,IACxE,UAAUA,GACP,OAAO,EACP,IAAI,EACJ,IAAI,CAAC,EACL,SAAS,EACT,SAAS,oCAAoC;AAAA,EAClD;AAAA,EACA,OAAO,EAAE,WAAW,SAAS,SAAS,MAAM;AAC1C,QAAI;AACF,mBAAa,SAAS;AACtB,YAAM,QAAQ,MAAM,eAAe,WAAW,EAAE,SAAS,SAAS,CAAC;AAEnE,YAAM,UAAU;AAAA,QACd,EAAE,uBAAuB,EAAE,OAAO,MAAM,YAAY,OAAO,MAAM,WAAW,CAAC;AAAA,QAC7E,MAAM,qBAAqB,SAAS,IAChC,EAAE,qBAAqB,EAAE,OAAO,MAAM,qBAAqB,OAAO,CAAC,IACnE,EAAE,kBAAkB;AAAA,MAC1B,EAAE,KAAK,IAAI;AAEX,aAAO;AAAA,QACL,SAAS;AAAA,UACP,EAAE,MAAM,QAAiB,MAAM,QAAQ;AAAA,UACvC,EAAE,MAAM,QAAiB,MAAM,KAAK,UAAU,OAAO,MAAM,CAAC,EAAE;AAAA,QAChE;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,aAAO,cAAc,KAAK;AAAA,IAC5B;AAAA,EACF;AACF;AAIA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA;AAAA,IACE,WAAWA,GACR,OAAO,EACP,QAAQ,KAAK,EACb,SAAS,sCAAsC;AAAA,IAClD,SAASA,GACN,MAAMA,GAAE,OAAO,CAAC,EAChB,SAAS,EACT,SAAS,oCAAoC;AAAA,IAChD,MAAMA,GACH,OAAO,EACP,IAAI,EACJ,IAAI,CAAC,EACL,IAAI,EAAE,EACN,SAAS,EACT,SAAS,2DAA2D;AAAA,IACvE,cAAcA,GACX,QAAQ,EACR,SAAS,EACT,SAAS,sDAAsD;AAAA,IAClE,aAAaA,GACV,OAAO,EACP,QAAQ,GAAG,EACX,SAAS,sDAAsD;AAAA,EACpE;AAAA,EACA,OAAO,EAAE,WAAW,SAAS,MAAM,cAAc,QAAQ,YAAY,MAAM;AACzE,QAAI;AACF,mBAAa,SAAS;AACtB,YAAM,QAAQ,MAAM,eAAe,WAAW,EAAE,QAAQ,CAAC;AACzD,YAAM,SAAS,qBAAqB,OAAO,EAAE,MAAM,QAAQ,GAAG,CAAC;AAE/D,YAAM,UAA4C;AAAA,QAChD,EAAE,MAAM,QAAiB,MAAM,OAAO;AAAA,MACxC;AAEA,UAAI,QAAQ;AACV,qBAAa,WAAW;AACxB,cAAM,aAAa,aAAa,KAAK;AACrC,gBAAQ,KAAK,EAAE,MAAM,QAAiB,MAAM,EAAE,uBAAuB,EAAE,CAAC;AAAA,MAC1E;AAEA,aAAO,EAAE,QAAQ;AAAA,IACnB,SAAS,OAAO;AACd,aAAO,cAAc,KAAK;AAAA,IAC5B;AAAA,EACF;AACF;AAIA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA;AAAA,IACE,WAAWA,GACR,OAAO,EACP,QAAQ,KAAK,EACb,SAAS,uBAAuB;AAAA,IACnC,aAAaA,GACV,OAAO,EACP,QAAQ,GAAG,EACX,SAAS,6CAA6C;AAAA,EAC3D;AAAA,EACA,OAAO,EAAE,WAAW,YAAY,MAAM;AACpC,QAAI;AACF,mBAAa,SAAS;AACtB,mBAAa,WAAW;AACxB,YAAM,QAAQ,MAAM,eAAe,SAAS;AAC5C,YAAM,WAAW,MAAM,aAAa,aAAa,KAAK;AAGtD,YAAM,gBAAgB,OAAO,OAAO,MAAM,KAAK,EAC5C,KAAK,CAAC,GAAG,MAAM,EAAE,WAAW,SAAS,EAAE,WAAW,MAAM,EACxD,MAAM,GAAG,CAAC,EACV,IAAI,CAAC,MAAM,KAAK,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,OAAO,EAAE,WAAW,OAAO,CAAC,CAAC,EAAE;AAEtF,YAAM,SAAS;AAAA,QACb,EAAE,mBAAmB;AAAA,QACrB,EAAE,iBAAiB,EAAE,IAAI,SAAS,UAAU,CAAC;AAAA,QAC7C,EAAE,iBAAiB,EAAE,OAAO,MAAM,WAAW,CAAC;AAAA,QAC9C,EAAE,iBAAiB,EAAE,OAAO,MAAM,WAAW,CAAC;AAAA,QAC9C;AAAA,QACA,EAAE,mBAAmB;AAAA,QACrB,GAAG;AAAA,MACL,EAAE,KAAK,IAAI;AAEX,aAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,OAAO,CAAC,EAAE;AAAA,IAC9D,SAAS,OAAO;AACd,aAAO,cAAc,KAAK;AAAA,IAC5B;AAAA,EACF;AACF;AAIA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA;AAAA,IACE,WAAWA,GACR,OAAO,EACP,QAAQ,KAAK,EACb,SAAS,uBAAuB;AAAA,IACnC,aAAaA,GACV,OAAO,EACP,QAAQ,GAAG,EACX,SAAS,6CAA6C;AAAA,EAC3D;AAAA,EACA,OAAO,EAAE,WAAW,YAAY,MAAM;AACpC,QAAI;AACF,mBAAa,SAAS;AACtB,mBAAa,WAAW;AACxB,YAAM,mBAAmB,MAAM,aAAa,WAAW;AAEvD,UAAI,CAAC,kBAAkB;AAErB,cAAM,QAAQ,MAAM,eAAe,SAAS;AAC5C,cAAM,aAAa,aAAa,KAAK;AACrC,eAAO;AAAA,UACL,SAAS;AAAA,YACP;AAAA,cACE,MAAM;AAAA,cACN,MAAM;AAAA,gBACJ,EAAE,cAAc;AAAA,gBAChB,EAAE,iBAAiB,EAAE,OAAO,MAAM,WAAW,CAAC,IAAI,OAAO,EAAE,iBAAiB,EAAE,OAAO,MAAM,WAAW,CAAC;AAAA,gBACvG,EAAE,sBAAsB;AAAA,cAC1B,EAAE,KAAK,IAAI;AAAA,YACb;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,YAAM,eAAe,MAAM,eAAe,SAAS;AACnD,YAAM,OAAO,YAAY,iBAAiB,OAAO,YAAY;AAC7D,YAAM,SAAS,iBAAiB,IAAI;AAEpC,aAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,OAAO,CAAC,EAAE;AAAA,IAC9D,SAAS,OAAO;AACd,aAAO,cAAc,KAAK;AAAA,IAC5B;AAAA,EACF;AACF;AAIA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA;AAAA,IACE,WAAWA,GACR,OAAO,EACP,QAAQ,KAAK,EACb,SAAS,uBAAuB;AAAA,IACnC,aAAaA,GACV,OAAO,EACP,QAAQ,GAAG,EACX,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA,OAAO,EAAE,WAAW,YAAY,MAAM;AACpC,QAAI;AACF,UAAI,WAAW,MAAM,aAAa,WAAW;AAG7C,UAAI,CAAC,UAAU;AACb,cAAMC,SAAQ,MAAM,eAAe,SAAS;AAC5C,mBAAW,MAAM,aAAa,aAAaA,MAAK;AAAA,MAClD;AAEA,YAAM,QAAQ,SAAS;AAGvB,YAAM,gBAAgB,OAAO,OAAO,MAAM,KAAK,EAC5C,OAAO,CAAC,MAAM,EAAE,WAAW,SAAS,KAAK,EAAE,aAAa,SAAS,CAAC,EAClE,KAAK,CAAC,GAAG,MAAM,EAAE,WAAW,SAAS,EAAE,WAAW,MAAM,EACxD,MAAM,GAAG,EAAE,EACX,IAAI,CAAC,OAAO;AAAA,QACX,MAAM,EAAE;AAAA,QACR,gBAAgB,EAAE,WAAW;AAAA,QAC7B,iBAAiB,EAAE,aAAa;AAAA,MAClC,EAAE;AAEJ,YAAM,aAAa,OAAO,KAAK,MAAM,KAAK,EAAE,KAAK;AAEjD,YAAM,UAAU;AAAA,QACd,EAAE,eAAe,EAAE,MAAM,MAAM,QAAQ,CAAC;AAAA,QACxC,EAAE,iBAAiB,EAAE,OAAO,MAAM,WAAW,CAAC;AAAA,QAC9C,EAAE,iBAAiB,EAAE,OAAO,MAAM,WAAW,CAAC;AAAA,QAC9C,EAAE,qBAAqB,EAAE,OAAO,MAAM,qBAAqB,OAAO,CAAC;AAAA,QACnE,EAAE,gBAAgB,EAAE,IAAI,SAAS,UAAU,CAAC;AAAA,QAC5C;AAAA,QACA,EAAE,mBAAmB;AAAA,QACrB,GAAG,cAAc;AAAA,UACf,CAAC,MACC,KAAK,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,OAAO,EAAE,eAAe,CAAC,CAAC;AAAA,QACvE;AAAA,MACF,EAAE,KAAK,IAAI;AAEX,YAAM,UAAuB;AAAA,QAC3B;AAAA,QACA;AAAA,QACA,gBAAgB;AAAA,QAChB,mBAAmB,SAAS;AAAA,QAC5B;AAAA,MACF;AAEA,aAAO;AAAA,QACL,SAAS;AAAA,UACP,EAAE,MAAM,QAAiB,MAAM,QAAQ;AAAA,UACvC;AAAA,YACE,MAAM;AAAA,YACN,MAAM,KAAK,UAAU,SAAS,MAAM,CAAC;AAAA,UACvC;AAAA,QACF;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,aAAO,cAAc,KAAK;AAAA,IAC5B;AAAA,EACF;AACF;AAIA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA;AAAA,IACE,OAAOD,GACJ,OAAO,EACP,SAAS,EACT,SAAS,kFAAkF;AAAA,IAC9F,MAAMA,GACH,KAAK,CAAC,QAAQ,YAAY,YAAY,SAAS,CAAC,EAChD,QAAQ,MAAM,EACd;AAAA,MACC;AAAA,IACF;AAAA,IACF,WAAWA,GACR,OAAO,EACP,QAAQ,KAAK,EACb,SAAS,uBAAuB;AAAA,IACnC,aAAaA,GACV,OAAO,EACP,QAAQ,GAAG,EACX,SAAS,cAAc;AAAA,IAC1B,OAAOA,GACJ,OAAO,EACP,IAAI,EACJ,IAAI,CAAC,EACL,IAAI,EAAE,EACN,SAAS,EACT,SAAS,2BAA2B;AAAA,EACzC;AAAA,EACA,OAAO,EAAE,OAAO,MAAM,WAAW,aAAa,MAAM,MAAM;AACxD,QAAI;AACF,mBAAa,SAAS;AACtB,mBAAa,WAAW;AAExB,UAAI,WAAW,MAAM,aAAa,WAAW;AAC7C,UAAI,CAAC,UAAU;AACb,cAAMC,SAAQ,MAAM,eAAe,SAAS;AAC5C,mBAAW,MAAM,aAAa,aAAaA,MAAK;AAAA,MAClD;AAEA,YAAM,QAAQ,SAAS;AACvB,YAAM,aAAa,SAAS;AAC5B,UAAI;AAGJ,WAAK,SAAS,UAAU,SAAS,eAAe,CAAC,OAAO;AACtD,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,EAAE,qBAAqB,EAAE,KAAK,CAAC,EAAE,CAAC;AAAA,UAC3E,SAAS;AAAA,QACX;AAAA,MACF;AAEA,cAAQ,MAAM;AAAA,QACZ,KAAK;AACH,oBAAU,aAAa,OAAO,KAAM;AACpC;AAAA,QACF,KAAK;AACH,oBAAU,kBAAkB,OAAO,KAAM;AACzC;AAAA,QACF,KAAK;AACH,oBAAU,kBAAkB,OAAO,UAAU;AAC7C;AAAA,QACF,KAAK;AACH,oBAAU,gBAAgB,KAAK;AAC/B;AAAA,MACJ;AAEA,UAAI,QAAQ,WAAW,GAAG;AACxB,eAAO;AAAA,UACL,SAAS;AAAA,YACP,EAAE,MAAM,QAAiB,MAAM,EAAE,oBAAoB,EAAE,OAAO,SAAS,IAAI,KAAK,CAAC,EAAE;AAAA,UACrF;AAAA,QACF;AAAA,MACF;AAEA,YAAM,QAAQ;AAAA,QACZ,EAAE,kBAAkB,EAAE,OAAO,QAAQ,QAAQ,KAAK,CAAC;AAAA,QACnD;AAAA,QACA,GAAG,QAAQ,MAAM,GAAG,UAAU,EAAE,IAAI,CAAC,MAAM;AACzC,iBAAO;AAAA,YACL,KAAK,EAAE,IAAI;AAAA,YACX,MAAM,EAAE,WAAW;AAAA,YACnB,YAAY,EAAE,eAAe,QAAQ,EAAE,aAAa,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,GAAG,EAAE,aAAa,SAAS,IAAI,QAAQ,EAAE;AAAA,YACnH,kBAAkB,EAAE,cAAc,QAAQ,EAAE,WAAW,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,GAAG,EAAE,WAAW,SAAS,IAAI,QAAQ,EAAE;AAAA,UACtH,EAAE,KAAK,IAAI;AAAA,QACb,CAAC;AAAA,MACH;AAEA,aAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,MAAM,KAAK,IAAI,EAAE,CAAC,EAAE;AAAA,IACxE,SAAS,OAAO;AACd,aAAO,cAAc,KAAK;AAAA,IAC5B;AAAA,EACF;AACF;AAIA,SAAS,cAAc,OAAgB;AACrC,MAAI;AAEJ,MAAI,iBAAiB,oBAAoB;AACvC,cAAU,EAAE,uBAAuB,EAAE,SAAS,MAAM,QAAQ,CAAC;AAAA,EAC/D,WAAW,iBAAiB,eAAe;AACzC,cAAU,EAAE,kBAAkB,EAAE,SAAS,MAAM,QAAQ,CAAC;AAAA,EAC1D,WAAW,iBAAiB,cAAc;AACxC,cAAU,EAAE,iBAAiB,EAAE,SAAS,MAAM,QAAQ,CAAC;AAAA,EACzD,WAAW,iBAAiB,OAAO;AACjC,cAAU,EAAE,iBAAiB,EAAE,SAAS,MAAM,QAAQ,CAAC;AAAA,EACzD,OAAO;AACL,cAAU,EAAE,oBAAoB,EAAE,SAAS,OAAO,KAAK,EAAE,CAAC;AAAA,EAC5D;AAEA,SAAO;AAAA,IACL,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,QAAQ,CAAC;AAAA,IAClD,SAAS;AAAA,EACX;AACF;AAIA,eAAe,OAAO;AACpB,QAAM,YAAY,IAAI,qBAAqB;AAC3C,QAAM,OAAO,QAAQ,SAAS;AAC9B,UAAQ,MAAM,2CAA2C;AAC3D;AAEA,KAAK,EAAE,MAAM,CAAC,UAAU;AACtB,UAAQ,MAAM,8BAA8B,KAAK;AACjD,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["z","t","resolve","resolve","z","graph"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "archtracker-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Architecture & Dependency Tracker — MCP server + CLI + interactive web viewer for AI-driven development",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
},
|
|
13
|
+
"./mcp": {
|
|
14
|
+
"import": "./dist/mcp/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"bin": {
|
|
18
|
+
"archtracker": "dist/cli/index.js"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"skills"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsup",
|
|
26
|
+
"dev": "tsup --watch",
|
|
27
|
+
"test": "vitest run",
|
|
28
|
+
"test:watch": "vitest",
|
|
29
|
+
"typecheck": "tsc --noEmit",
|
|
30
|
+
"prepublishOnly": "npm run build"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"mcp",
|
|
34
|
+
"model-context-protocol",
|
|
35
|
+
"architecture",
|
|
36
|
+
"dependency-graph",
|
|
37
|
+
"dependency-tracker",
|
|
38
|
+
"claude-code",
|
|
39
|
+
"ai-development",
|
|
40
|
+
"static-analysis",
|
|
41
|
+
"visualization",
|
|
42
|
+
"typescript",
|
|
43
|
+
"cli",
|
|
44
|
+
"devtools",
|
|
45
|
+
"architecture-drift",
|
|
46
|
+
"impact-analysis",
|
|
47
|
+
"code-quality"
|
|
48
|
+
],
|
|
49
|
+
"author": "un907",
|
|
50
|
+
"license": "MIT",
|
|
51
|
+
"repository": {
|
|
52
|
+
"type": "git",
|
|
53
|
+
"url": "git+https://github.com/un907/archtracker-mcp.git"
|
|
54
|
+
},
|
|
55
|
+
"homepage": "https://github.com/un907/archtracker-mcp#readme",
|
|
56
|
+
"bugs": {
|
|
57
|
+
"url": "https://github.com/un907/archtracker-mcp/issues"
|
|
58
|
+
},
|
|
59
|
+
"funding": {
|
|
60
|
+
"type": "github",
|
|
61
|
+
"url": "https://github.com/sponsors/un907"
|
|
62
|
+
},
|
|
63
|
+
"engines": {
|
|
64
|
+
"node": ">=18.0.0"
|
|
65
|
+
},
|
|
66
|
+
"dependencies": {
|
|
67
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
68
|
+
"commander": "^14.0.3",
|
|
69
|
+
"dependency-cruiser": "^17.3.8",
|
|
70
|
+
"zod": "^4.3.6"
|
|
71
|
+
},
|
|
72
|
+
"devDependencies": {
|
|
73
|
+
"@types/node": "^25.3.3",
|
|
74
|
+
"tsup": "^8.5.1",
|
|
75
|
+
"typescript": "^5.9.3",
|
|
76
|
+
"vitest": "^4.0.18"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: arch-analyze
|
|
3
|
+
description: Analyze existing architecture — comprehensive report of an existing project's dependency structure, critical components, circular deps, orphan files, and coupling hotspots. Use when introducing archtracker to an existing project or when you need a full architectural overview.
|
|
4
|
+
argument-hint: "[target directory, e.g. src]"
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- mcp__archtracker__analyze_existing_architecture
|
|
7
|
+
- mcp__archtracker__save_architecture_snapshot
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Architecture Analysis
|
|
11
|
+
|
|
12
|
+
Perform a comprehensive architecture analysis of the project.
|
|
13
|
+
|
|
14
|
+
1. Run `analyze_existing_architecture` on the target directory
|
|
15
|
+
2. Present the full report covering:
|
|
16
|
+
- Overview (file count, edge count, circular deps)
|
|
17
|
+
- Critical components (most depended-on files)
|
|
18
|
+
- Circular dependency details
|
|
19
|
+
- High coupling files (most imports)
|
|
20
|
+
- Orphan files (isolated, no connections)
|
|
21
|
+
- Directory breakdown
|
|
22
|
+
3. Offer to save a snapshot if one doesn't exist yet
|
|
23
|
+
|
|
24
|
+
Present results clearly, highlighting:
|
|
25
|
+
- Architectural risks (circular deps, high coupling)
|
|
26
|
+
- Key files that many components depend on
|
|
27
|
+
- Orphan files that may be dead code
|
|
28
|
+
- Recommendations for improvement
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: arch-check
|
|
3
|
+
description: Check architecture diff — compare current code against the saved snapshot and report components that may need updates. Use when checking for dependency breakage after code changes.
|
|
4
|
+
allowed-tools:
|
|
5
|
+
- mcp__archtracker__check_architecture_diff
|
|
6
|
+
- mcp__archtracker__generate_map
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Architecture Diff Check
|
|
10
|
+
|
|
11
|
+
Run an architecture diff check for the current project.
|
|
12
|
+
|
|
13
|
+
1. Generate the current dependency map
|
|
14
|
+
2. Compare it against the saved snapshot
|
|
15
|
+
3. Report any files that have changed and their affected dependents
|
|
16
|
+
|
|
17
|
+
If no snapshot exists, generate one first and inform the user this is the initial baseline.
|
|
18
|
+
|
|
19
|
+
Present results in Japanese, clearly listing:
|
|
20
|
+
- 変更されたファイル
|
|
21
|
+
- 影響を受ける依存ファイル(要確認)
|
|
22
|
+
- 推奨アクション
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: arch-context
|
|
3
|
+
description: Load the current architecture context into the session. Use at the start of a new session or when you need to understand the project structure. Prevents hallucination of old file paths.
|
|
4
|
+
allowed-tools:
|
|
5
|
+
- mcp__archtracker__get_current_context
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Load Architecture Context
|
|
9
|
+
|
|
10
|
+
Retrieve and display the current project architecture context.
|
|
11
|
+
|
|
12
|
+
1. Call `get_current_context` to get valid file paths and architecture summary
|
|
13
|
+
2. Internalize the returned structure so you reference only existing files
|
|
14
|
+
3. Display a brief summary to the user
|
|
15
|
+
|
|
16
|
+
Present results in Japanese:
|
|
17
|
+
- 現在の有効なファイルパス一覧
|
|
18
|
+
- アーキテクチャの概要サマリー
|
|
19
|
+
- 前回のスナップショットからの経過時間(あれば)
|
|
20
|
+
|
|
21
|
+
**重要**: このコンテキストを元に、以降のセッションでは存在しないファイルパスを参照しないこと。
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: arch-search
|
|
3
|
+
description: Search the architecture for files, impact analysis, critical components, or orphans. Use when asking about dependencies, impact of changes, or finding important files.
|
|
4
|
+
argument-hint: [query]
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- mcp__archtracker__search_architecture
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Architecture Search
|
|
10
|
+
|
|
11
|
+
Search the project architecture using $ARGUMENTS.
|
|
12
|
+
|
|
13
|
+
Available search modes:
|
|
14
|
+
- **path**: Find files matching a pattern (default)
|
|
15
|
+
- **affected**: Find all files affected if a specific file changes
|
|
16
|
+
- **critical**: Find the most important files (most depended-on)
|
|
17
|
+
- **orphans**: Find isolated files with no connections
|
|
18
|
+
|
|
19
|
+
Choose the most appropriate mode based on the user's question and execute the search.
|
|
20
|
+
|
|
21
|
+
Present results in Japanese with clear formatting:
|
|
22
|
+
- ファイルパスと依存関係の数
|
|
23
|
+
- 検索にマッチした理由
|
|
24
|
+
- 推奨アクション(影響分析の場合)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: arch-snapshot
|
|
3
|
+
description: Save the current architecture state as a snapshot. Use when the current code state is confirmed good and should be the new baseline.
|
|
4
|
+
allowed-tools:
|
|
5
|
+
- mcp__archtracker__generate_map
|
|
6
|
+
- mcp__archtracker__save_architecture_snapshot
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Save Architecture Snapshot
|
|
10
|
+
|
|
11
|
+
Save the current project architecture as the baseline snapshot.
|
|
12
|
+
|
|
13
|
+
1. Generate the current dependency map
|
|
14
|
+
2. Save it as `.archtracker/snapshot.json`
|
|
15
|
+
3. Confirm the save with a summary
|
|
16
|
+
|
|
17
|
+
Present results in Japanese:
|
|
18
|
+
- 保存されたファイル数とエッジ数
|
|
19
|
+
- 主要コンポーネント(依存が多いファイルのトップ5)
|
|
20
|
+
- スナップショットのタイムスタンプ
|