{"version":3,"file":"workspace-skills-CjnfyQuP.cjs","names":["fs","FileNotFoundError","#basePath","#resolvePath","fs","#source","#skillsResolver","#searchEngine","#validateOnLoad","#checkSkillFileMtime","#ensureInitialized","#skills","#dedupeCanonicalCandidates","#resolveByName","#resolveByPath","#tieBreak","#getCanonicalSkillPath","#removeSkillFromIndex","#initialized","#initPromise","#discoverSkills","#resolvePaths","#arePathsEqual","#resolvedPaths","#isSkillsPathStale","#getParentPath","#joinPath","#inferSource","#parseSkillFile","#indexSkill","#lastDiscoveryTime","#simpleSearch","#assertRelativePath","#globDirCache","#globResolveTimes","#determineSource","#discoverDirectSkill","#discoverSkillsInPath","#addToSkillsMap","#validateSkillMetadata","#discoverFilesInSubdir","#buildIndexableContent","#walkDirectory"],"sources":["../src/workspace/filesystem/fs-utils.ts","../src/workspace/glob.ts","../src/workspace/skills/schemas.ts","../src/workspace/skills/local-skill-source.ts","../src/workspace/skills/workspace-skills.ts"],"sourcesContent":["/**\n * Shared filesystem utilities for LocalFilesystem and LocalSkillSource.\n *\n * These utilities provide consistent implementations for common fs operations.\n */\n\nimport * as fs from 'node:fs/promises';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\n\nimport { FileNotFoundError } from '../errors';\n\n// =============================================================================\n// Tilde Expansion\n// =============================================================================\n\n/**\n * Expand a leading `~` or `~/` to the user's home directory.\n * Shell commands handle this automatically, but Node.js path APIs do not.\n */\nexport function expandTilde(p: string): string {\n  if (p === '~') return os.homedir();\n  if (p.startsWith('~/') || p.startsWith('~\\\\')) {\n    return path.join(os.homedir(), p.slice(2));\n  }\n  return p;\n}\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Full file stat information.\n * Used by both WorkspaceFilesystem and SkillSource.\n */\nexport interface FsStatResult {\n  /** File or directory name */\n  name: string;\n  /** 'file' or 'directory' */\n  type: 'file' | 'directory';\n  /** Size in bytes (0 for directories) */\n  size: number;\n  /** Creation time */\n  createdAt: Date;\n  /** Last modification time */\n  modifiedAt: Date;\n  /** MIME type (for files) */\n  mimeType?: string;\n}\n\n// =============================================================================\n// Error Utilities\n// =============================================================================\n\n/**\n * Check if an error is an ENOENT (file not found) error.\n */\nexport function isEnoentError(error: unknown): error is NodeJS.ErrnoException & { code: 'ENOENT' } {\n  return (\n    error !== null && typeof error === 'object' && 'code' in error && (error as NodeJS.ErrnoException).code === 'ENOENT'\n  );\n}\n\n/**\n * Check if an error is an EEXIST (file exists) error.\n */\nexport function isEexistError(error: unknown): error is NodeJS.ErrnoException & { code: 'EEXIST' } {\n  return (\n    error !== null && typeof error === 'object' && 'code' in error && (error as NodeJS.ErrnoException).code === 'EEXIST'\n  );\n}\n\n// =============================================================================\n// MIME Type Detection\n// =============================================================================\n\nconst MIME_TYPES: Record<string, string> = {\n  // Text\n  txt: 'text/plain',\n  html: 'text/html',\n  htm: 'text/html',\n  css: 'text/css',\n  csv: 'text/csv',\n  md: 'text/markdown',\n  // Code\n  js: 'application/javascript',\n  mjs: 'application/javascript',\n  ts: 'application/typescript',\n  tsx: 'application/typescript',\n  jsx: 'application/javascript',\n  json: 'application/json',\n  xml: 'application/xml',\n  yaml: 'text/yaml',\n  yml: 'text/yaml',\n  // Programming languages\n  py: 'text/x-python',\n  rb: 'text/x-ruby',\n  go: 'text/x-go',\n  rs: 'text/x-rust',\n  java: 'text/x-java',\n  c: 'text/x-c',\n  cpp: 'text/x-c++',\n  h: 'text/x-c',\n  hpp: 'text/x-c++',\n  sh: 'text/x-sh',\n  bash: 'text/x-sh',\n  zsh: 'text/x-sh',\n  // Config\n  toml: 'text/toml',\n  ini: 'text/plain',\n  env: 'text/plain',\n  // Database/Query\n  sql: 'text/x-sql',\n  graphql: 'application/graphql',\n  gql: 'application/graphql',\n  // Frameworks\n  vue: 'text/x-vue',\n  svelte: 'text/x-svelte',\n  // Web styles\n  scss: 'text/x-scss',\n  sass: 'text/x-sass',\n  less: 'text/x-less',\n  // Additional languages\n  php: 'application/x-php',\n  swift: 'text/x-swift',\n  kt: 'text/x-kotlin',\n  kts: 'text/x-kotlin',\n  dart: 'application/dart',\n  lua: 'text/x-lua',\n  r: 'text/x-r',\n  tf: 'text/x-terraform',\n  tfvars: 'text/x-terraform',\n  mdx: 'text/markdown',\n  // Images\n  png: 'image/png',\n  jpg: 'image/jpeg',\n  jpeg: 'image/jpeg',\n  gif: 'image/gif',\n  svg: 'image/svg+xml',\n  webp: 'image/webp',\n  ico: 'image/x-icon',\n  bmp: 'image/bmp',\n  tiff: 'image/tiff',\n  tif: 'image/tiff',\n  heic: 'image/heic',\n  heif: 'image/heif',\n  avif: 'image/avif',\n  // Documents\n  pdf: 'application/pdf',\n  // Audio\n  mp3: 'audio/mpeg',\n  wav: 'audio/wav',\n  ogg: 'audio/ogg',\n  flac: 'audio/flac',\n  m4a: 'audio/mp4',\n  aac: 'audio/aac',\n  // Video\n  mp4: 'video/mp4',\n  webm: 'video/webm',\n  mov: 'video/quicktime',\n  avi: 'video/x-msvideo',\n  mkv: 'video/x-matroska',\n  // Archives\n  zip: 'application/zip',\n  tar: 'application/x-tar',\n  gz: 'application/gzip',\n  tgz: 'application/gzip',\n  bz2: 'application/x-bzip2',\n  '7z': 'application/x-7z-compressed',\n  rar: 'application/vnd.rar',\n  // Executables / binaries\n  exe: 'application/vnd.microsoft.portable-executable',\n  dll: 'application/vnd.microsoft.portable-executable',\n  so: 'application/x-sharedlib',\n  dylib: 'application/x-sharedlib',\n  bin: 'application/x-binary',\n  dat: 'application/x-binary',\n  // Disk images / packages\n  dmg: 'application/x-apple-diskimage',\n  iso: 'application/x-iso9660-image',\n  deb: 'application/vnd.debian.binary-package',\n  rpm: 'application/x-rpm',\n  // Office documents\n  doc: 'application/msword',\n  docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n  xls: 'application/vnd.ms-excel',\n  xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n  ppt: 'application/vnd.ms-powerpoint',\n  pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n  // Fonts\n  ttf: 'font/ttf',\n  otf: 'font/otf',\n  woff: 'font/woff',\n  woff2: 'font/woff2',\n  // Compiled code\n  wasm: 'application/wasm',\n  class: 'application/java-vm',\n  pyc: 'application/x-python-code',\n};\n\n/**\n * Get MIME type for a filename based on extension.\n */\nexport function getMimeType(filename: string): string {\n  const ext = path.extname(filename).slice(1).toLowerCase();\n  return MIME_TYPES[ext] ?? 'application/octet-stream';\n}\n\n/**\n * Extensions that should be treated as text files.\n */\nconst TEXT_EXTENSIONS = new Set([\n  '.md',\n  '.txt',\n  '.json',\n  '.yaml',\n  '.yml',\n  '.js',\n  '.mjs',\n  '.ts',\n  '.tsx',\n  '.jsx',\n  '.py',\n  '.rb',\n  '.go',\n  '.rs',\n  '.java',\n  '.c',\n  '.cpp',\n  '.h',\n  '.hpp',\n  '.sh',\n  '.bash',\n  '.zsh',\n  '.html',\n  '.htm',\n  '.css',\n  '.xml',\n  '.toml',\n  '.ini',\n  '.env',\n  '.csv',\n  '.sql',\n  '.graphql',\n  '.gql',\n  '.vue',\n  '.svg',\n  '.mdx',\n  '.scss',\n  '.sass',\n  '.less',\n  '.svelte',\n  '.php',\n  '.swift',\n  '.kt',\n  '.kts',\n  '.dart',\n  '.lua',\n  '.r',\n  '.tf',\n  '.tfvars',\n]);\n\n/**\n * Check if a file should be treated as text based on extension.\n */\nexport function isTextFile(filename: string): boolean {\n  const ext = path.extname(filename).toLowerCase();\n  return TEXT_EXTENSIONS.has(ext);\n}\n\n// =============================================================================\n// Path Resolution\n// =============================================================================\n\n/**\n * Resolve a path against a base directory.\n *\n * - Tilde (`~`) is expanded to the user's home directory.\n * - Absolute paths are normalized and returned as-is.\n * - Relative paths (including `../`) are resolved against `basePath`.\n *\n * @param basePath - The absolute base path to resolve against\n * @param filePath - The path to resolve\n * @returns The absolute resolved path\n */\nexport function resolveToBasePath(basePath: string, filePath: string): string {\n  const expanded = expandTilde(filePath);\n  if (path.isAbsolute(expanded)) {\n    return path.normalize(expanded);\n  }\n  return path.resolve(basePath, expanded);\n}\n\n// =============================================================================\n// Filesystem Operations\n// =============================================================================\n\n/**\n * Check if a path exists.\n * Never throws - returns false on any error.\n *\n * @param absolutePath - The absolute path to check\n * @returns true if path exists and is accessible\n */\nexport async function fsExists(absolutePath: string): Promise<boolean> {\n  try {\n    await fs.access(absolutePath);\n    return true;\n  } catch {\n    return false;\n  }\n}\n\n/**\n * Get file/directory stats.\n * Throws FileNotFoundError if path doesn't exist.\n *\n * @param absolutePath - The absolute path to stat\n * @param userPath - The user-facing path for error messages\n * @returns File stat information\n * @throws {FileNotFoundError} if path doesn't exist\n */\nexport async function fsStat(absolutePath: string, userPath: string): Promise<FsStatResult> {\n  try {\n    const stats = await fs.stat(absolutePath);\n    return {\n      name: path.basename(absolutePath),\n      type: stats.isDirectory() ? 'directory' : 'file',\n      size: stats.size,\n      createdAt: stats.birthtime,\n      modifiedAt: stats.mtime,\n      mimeType: stats.isFile() ? getMimeType(absolutePath) : undefined,\n    };\n  } catch (error: unknown) {\n    if (isEnoentError(error)) {\n      throw new FileNotFoundError(userPath);\n    }\n    throw error;\n  }\n}\n","/**\n * Glob Pattern Utilities\n *\n * Shared glob pattern matching for workspace operations.\n * Uses picomatch for battle-tested glob support including\n * brace expansion, character classes, negation, and `**`.\n */\n\nimport picomatch from 'picomatch';\n\n// =============================================================================\n// Glob Metacharacter Detection\n// =============================================================================\n\n/** Characters that indicate a glob pattern (not a plain path) */\nconst GLOB_CHARS = /[*?{}[\\]]/;\n\n/**\n * Check if a string contains glob metacharacters.\n *\n * @example\n * isGlobPattern('/docs')           // false\n * isGlobPattern('/docs/**\\/*.md')   // true\n * isGlobPattern('*.ts')            // true\n * isGlobPattern('/src/{a,b}')      // true\n */\nexport function isGlobPattern(input: string): boolean {\n  return GLOB_CHARS.test(input);\n}\n\n// =============================================================================\n// Glob Base Extraction\n// =============================================================================\n\n/**\n * Extract the static directory prefix before the first glob metacharacter.\n * Returns the deepest non-glob ancestor directory.\n *\n * @example\n * extractGlobBase('docs/**\\/*.md')   // 'docs'\n * extractGlobBase('**\\/*.md')        // '.'\n * extractGlobBase('src/*.ts')       // 'src'\n * extractGlobBase('exact/path')     // 'exact/path'\n */\nexport function extractGlobBase(pattern: string): string {\n  // Find position of first glob metacharacter\n  const firstMeta = pattern.search(GLOB_CHARS);\n\n  if (firstMeta === -1) {\n    // No glob chars — return the pattern as-is (it's a plain path)\n    return pattern;\n  }\n\n  // Get the portion before the first metacharacter\n  const prefix = pattern.slice(0, firstMeta);\n\n  // Walk back to the last directory separator\n  const lastSlash = prefix.lastIndexOf('/');\n\n  if (lastSlash <= 0) {\n    // No slash or only root slash — base is workspace root\n    return '.';\n  }\n\n  return prefix.slice(0, lastSlash);\n}\n\n// =============================================================================\n// Glob Matcher\n// =============================================================================\n\n/** A compiled matcher function: returns true if a path matches */\nexport type GlobMatcher = (path: string) => boolean;\n\nexport interface GlobMatcherOptions {\n  /** Match dotfiles (default: false) */\n  dot?: boolean;\n}\n\n/**\n * Strip leading './' or '/' from a path for picomatch matching.\n * picomatch does not match paths with these prefixes, so both\n * patterns and test paths must be normalized before matching.\n *\n * This only affects matching — filesystem paths should keep their\n * original form for correct resolution with contained/uncontained modes.\n */\nfunction normalizeForMatch(input: string): string {\n  if (input.startsWith('./')) return input.slice(2);\n  if (input.startsWith('/')) return input.slice(1);\n  return input;\n}\n\n/**\n * Compile glob pattern(s) into a reusable matcher function.\n * The matcher tests paths using workspace-style forward slashes.\n *\n * Automatically normalizes leading './' and '/' from both patterns\n * and test paths, since picomatch does not match these prefixes.\n *\n * @example\n * const match = createGlobMatcher('**\\/*.ts');\n * match('src/index.ts')     // true\n * match('src/style.css')    // false\n *\n * const multi = createGlobMatcher(['**\\/*.ts', '**\\/*.tsx']);\n * multi('App.tsx')           // true\n */\nexport function createGlobMatcher(patterns: string | string[], options?: GlobMatcherOptions): GlobMatcher {\n  const patternArray = (Array.isArray(patterns) ? patterns : [patterns]).map(normalizeForMatch);\n  const matcher = picomatch(patternArray, {\n    posix: true,\n    dot: options?.dot ?? false,\n  });\n  return (path: string) => matcher(normalizeForMatch(path));\n}\n\n/**\n * One-off convenience: test if a path matches a glob pattern.\n *\n * For repeated matching against the same pattern, prefer createGlobMatcher()\n * to compile once and reuse.\n *\n * @example\n * matchGlob('src/index.ts', '**\\/*.ts')  // true\n */\nexport function matchGlob(path: string, pattern: string | string[], options?: GlobMatcherOptions): boolean {\n  return createGlobMatcher(pattern, options)(path);\n}\n\n// =============================================================================\n// Path Pattern Resolution\n// =============================================================================\n\n/** A filesystem entry returned by resolvePathPattern */\nexport interface PathEntry {\n  path: string;\n  type: 'file' | 'directory';\n}\n\n/** Minimal readdir entry — compatible with both FileEntry and SkillSourceEntry */\nexport interface ReaddirEntry {\n  name: string;\n  type: 'file' | 'directory';\n  isSymlink?: boolean;\n}\n\nexport interface ResolvePathOptions {\n  /** Match dotfiles (default: false) */\n  dot?: boolean;\n  /** Maximum directory depth to walk (default: 10) */\n  maxDepth?: number;\n}\n\n/**\n * Walk a directory tree recursively, returning all entries (files and directories).\n * Skips symlinked directories to prevent infinite loops.\n */\nasync function walkAll(\n  readdir: (dir: string) => Promise<ReaddirEntry[]>,\n  dir: string,\n  depth: number,\n  maxDepth: number,\n): Promise<PathEntry[]> {\n  if (depth >= maxDepth) return [];\n  try {\n    const entries = await readdir(dir);\n    const results: PathEntry[] = [];\n    for (const entry of entries) {\n      if (entry.type === 'directory' && entry.isSymlink) continue;\n      const fullPath = dir === '.' || dir === '' ? entry.name : `${dir}/${entry.name}`;\n      results.push({ path: fullPath, type: entry.type });\n      if (entry.type === 'directory') {\n        results.push(...(await walkAll(readdir, fullPath, depth + 1, maxDepth)));\n      }\n    }\n    return results;\n  } catch {\n    return [];\n  }\n}\n\n/**\n * Resolve a path pattern to matching filesystem entries.\n *\n * Handles both plain paths and glob patterns consistently:\n * - Plain paths: determines file vs directory via readdir probe, returns single entry\n * - Glob patterns: walks from the glob base, matches both files and directories\n *\n * @example\n * // Plain paths\n * resolvePathPattern('/docs', readdir)            // [{ path: '/docs', type: 'directory' }]\n * resolvePathPattern('/docs/readme.md', readdir)  // [{ path: '/docs/readme.md', type: 'file' }]\n *\n * // Glob patterns — matches files and directories\n * resolvePathPattern('/docs/**\\/*.md', readdir)    // all .md files under /docs\n * resolvePathPattern('**\\/skills', readdir)         // all directories (and files) named 'skills'\n * resolvePathPattern('/skills/**', readdir)         // everything under /skills\n */\nexport async function resolvePathPattern(\n  pattern: string,\n  readdir: (dir: string) => Promise<ReaddirEntry[]>,\n  options?: ResolvePathOptions,\n): Promise<PathEntry[]> {\n  const maxDepth = options?.maxDepth ?? 10;\n\n  // Strip trailing slash for consistent path handling (e.g. '/skills/' → '/skills')\n  const normalized = pattern.length > 1 && pattern.endsWith('/') ? pattern.slice(0, -1) : pattern;\n\n  if (!isGlobPattern(normalized)) {\n    // Plain path — probe with readdir to determine if it's a directory or file\n    try {\n      await readdir(normalized);\n      return [{ path: normalized, type: 'directory' }];\n    } catch {\n      // readdir failed — treat as a file path (consumer handles non-existence)\n      return [{ path: normalized, type: 'file' }];\n    }\n  }\n\n  // Glob pattern — walk from base, match all entries (files and directories)\n  const walkRoot = extractGlobBase(normalized);\n  const matcher = createGlobMatcher(normalized, { dot: options?.dot ?? false });\n  const allEntries = await walkAll(readdir, walkRoot, 0, maxDepth);\n  return allEntries.filter(entry => matcher(entry.path));\n}\n","/**\n * Validation for Skills following the Agent Skills specification.\n * @see https://agentskills.io/specification\n *\n * This module uses plain validation functions instead of Zod to avoid\n * version compatibility issues between Zod 3 and Zod 4.\n */\n\n// =============================================================================\n// Constants\n// =============================================================================\n\n/**\n * Recommended limits from the Agent Skills spec\n */\nexport const SKILL_LIMITS = {\n  /** Recommended max tokens for instructions */\n  MAX_INSTRUCTION_TOKENS: 5000,\n  /** Recommended max lines for SKILL.md */\n  MAX_INSTRUCTION_LINES: 500,\n  /** Max characters for name field */\n  MAX_NAME_LENGTH: 64,\n  /** Max characters for description field */\n  MAX_DESCRIPTION_LENGTH: 1024,\n  /** Max characters for compatibility field */\n  MAX_COMPATIBILITY_LENGTH: 500,\n} as const;\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Skill metadata input type (what users provide)\n */\nexport interface SkillMetadataInput {\n  /** Skill name (1-64 chars, lowercase letters/numbers/hyphens only, must match directory name) */\n  name: string;\n  /** Description of what the skill does and when to use it (1-1024 characters) */\n  description: string;\n  /** License for the skill (e.g., \"Apache-2.0\", \"MIT\") */\n  license?: string;\n  /** Environment requirements or compatibility notes (string or object for flexibility) */\n  compatibility?: unknown;\n  /** Whether this skill should be directly invokable by users. Defaults to true. */\n  'user-invocable'?: boolean;\n  /** Arbitrary key-value metadata - values can be strings, arrays, objects, etc. */\n  metadata?: Record<string, unknown>;\n}\n\n/**\n * Skill metadata output type (after validation)\n */\nexport type SkillMetadataOutput = SkillMetadataInput;\n\n/**\n * Validation result with warnings\n */\nexport interface SkillValidationResult {\n  valid: boolean;\n  errors: string[];\n  warnings: string[];\n}\n\n// =============================================================================\n// Field Validators\n// =============================================================================\n\n/**\n * Validate skill name according to spec:\n * - 1-64 characters\n * - Lowercase letters, numbers, hyphens only\n * - Must not start or end with hyphen\n * - Must not contain consecutive hyphens\n *\n * @param name - The name to validate\n * @returns Array of error messages (empty if valid)\n */\nfunction validateSkillName(name: unknown): string[] {\n  const errors: string[] = [];\n  const fieldPath = 'name';\n\n  // Check type\n  if (typeof name !== 'string') {\n    errors.push(`${fieldPath}: Expected string, received ${typeof name}`);\n    return errors;\n  }\n\n  // Check not empty\n  if (name.length === 0) {\n    errors.push(`${fieldPath}: Skill name cannot be empty`);\n    return errors;\n  }\n\n  // Check max length\n  if (name.length > SKILL_LIMITS.MAX_NAME_LENGTH) {\n    errors.push(`${fieldPath}: Skill name must be ${SKILL_LIMITS.MAX_NAME_LENGTH} characters or less`);\n  }\n\n  // Check allowed characters (lowercase letters, numbers, hyphens only)\n  if (!/^[a-z0-9-]+$/.test(name)) {\n    errors.push(`${fieldPath}: Skill name must contain only lowercase letters, numbers, and hyphens`);\n  }\n\n  // Check not starting or ending with hyphen\n  if (name.startsWith('-') || name.endsWith('-')) {\n    errors.push(`${fieldPath}: Skill name must not start or end with a hyphen`);\n  }\n\n  // Check no consecutive hyphens\n  if (name.includes('--')) {\n    errors.push(`${fieldPath}: Skill name must not contain consecutive hyphens`);\n  }\n\n  return errors;\n}\n\n/**\n * Validate skill description according to spec:\n * - 1-1024 characters\n * - Cannot be empty or only whitespace\n *\n * @param description - The description to validate\n * @returns Array of error messages (empty if valid)\n */\nfunction validateSkillDescription(description: unknown): string[] {\n  const errors: string[] = [];\n  const fieldPath = 'description';\n\n  // Check type\n  if (typeof description !== 'string') {\n    errors.push(`${fieldPath}: Expected string, received ${typeof description}`);\n    return errors;\n  }\n\n  // Check not empty\n  if (description.length === 0) {\n    errors.push(`${fieldPath}: Skill description cannot be empty`);\n    return errors;\n  }\n\n  // Check max length\n  if (description.length > SKILL_LIMITS.MAX_DESCRIPTION_LENGTH) {\n    errors.push(`${fieldPath}: Skill description must be ${SKILL_LIMITS.MAX_DESCRIPTION_LENGTH} characters or less`);\n  }\n\n  // Check not only whitespace\n  if (description.trim().length === 0) {\n    errors.push(`${fieldPath}: Skill description cannot be only whitespace`);\n  }\n\n  return errors;\n}\n\n/**\n * Validate skill license (optional string).\n *\n * @param license - The license to validate\n * @returns Array of error messages (empty if valid)\n */\nfunction validateSkillLicense(license: unknown): string[] {\n  const errors: string[] = [];\n  const fieldPath = 'license';\n\n  // Optional field - undefined/null is valid\n  if (license === undefined || license === null) {\n    return errors;\n  }\n\n  // If provided, must be string\n  if (typeof license !== 'string') {\n    errors.push(`${fieldPath}: Expected string, received ${typeof license}`);\n  }\n\n  return errors;\n}\n\n/**\n * Validate skill compatibility notes (optional).\n * Accepts string or any JSON-serializable value for flexibility with external skills.\n *\n * @param compatibility - The compatibility value to validate\n * @returns Array of error messages (empty if valid)\n */\nfunction validateSkillCompatibility(_compatibility: unknown): string[] {\n  // Optional field - any value is allowed (string, object, array, etc.)\n  // External skills don't always follow the spec strictly\n  return [];\n}\n\n/**\n * Validate skill metadata field (optional Record<string, unknown>).\n * Accepts any values (not just strings) for flexibility with external skills.\n *\n * @param metadata - The metadata object to validate\n * @returns Array of error messages (empty if valid)\n */\nfunction validateSkillMetadataField(metadata: unknown): string[] {\n  const errors: string[] = [];\n  const fieldPath = 'metadata';\n\n  // Optional field - undefined/null is valid\n  if (metadata === undefined || metadata === null) {\n    return errors;\n  }\n\n  // If provided, must be object (but values can be anything)\n  if (typeof metadata !== 'object' || Array.isArray(metadata)) {\n    errors.push(`${fieldPath}: Expected object, received ${Array.isArray(metadata) ? 'array' : typeof metadata}`);\n    return errors;\n  }\n\n  // Allow any values - external skills use arrays, objects, etc.\n  return errors;\n}\n\nfunction validateUserInvocable(userInvocable: unknown): string[] {\n  if (userInvocable === undefined || typeof userInvocable === 'boolean') return [];\n  return [`user-invocable: Expected boolean, received ${typeof userInvocable}`];\n}\n\n// =============================================================================\n// Validation Helpers\n// =============================================================================\n\n/**\n * Rough token estimate (words * 1.3)\n * This is a simple heuristic; actual token counts vary by model\n */\nfunction estimateTokens(text: string): number {\n  const words = text.split(/\\s+/).filter(Boolean).length;\n  return Math.ceil(words * 1.3);\n}\n\n/**\n * Count lines in text\n */\nfunction countLines(text: string): number {\n  return text.split('\\n').length;\n}\n\n// =============================================================================\n// Main Validation Function\n// =============================================================================\n\n/**\n * Validate skill metadata with optional content warnings.\n *\n * @param metadata - The skill metadata to validate\n * @param dirName - The directory name (must match skill name)\n * @param instructions - Optional instructions content for token/line warnings\n * @returns Validation result with errors and warnings\n *\n * @example\n * ```typescript\n * const result = validateSkillMetadata(\n *   { name: 'my-skill', description: 'A helpful skill' },\n *   'my-skill',\n *   '# Instructions\\n...'\n * );\n *\n * if (!result.valid) {\n *   console.error('Validation errors:', result.errors);\n * }\n * if (result.warnings.length > 0) {\n *   console.warn('Warnings:', result.warnings);\n * }\n * ```\n */\nexport function validateSkillMetadata(\n  metadata: unknown,\n  dirName?: string,\n  instructions?: string,\n): SkillValidationResult {\n  const errors: string[] = [];\n  const warnings: string[] = [];\n\n  // Check that metadata is an object\n  if (typeof metadata !== 'object' || metadata === null || Array.isArray(metadata)) {\n    errors.push(\n      `Expected object, received ${metadata === null ? 'null' : Array.isArray(metadata) ? 'array' : typeof metadata}`,\n    );\n    return { valid: false, errors, warnings };\n  }\n\n  const data = metadata as Record<string, unknown>;\n\n  // Validate each field\n  errors.push(...validateSkillName(data.name));\n  errors.push(...validateSkillDescription(data.description));\n  errors.push(...validateSkillLicense(data.license));\n  errors.push(...validateSkillCompatibility(data.compatibility));\n  errors.push(...validateUserInvocable(data['user-invocable']));\n  errors.push(...validateSkillMetadataField(data.metadata));\n\n  // Check directory name match (only if no name errors and name is valid)\n  if (dirName && typeof data.name === 'string' && data.name !== dirName) {\n    errors.push(`Skill name \"${data.name}\" must match directory name \"${dirName}\"`);\n  }\n\n  // Check instruction limits (warnings only)\n  if (instructions) {\n    const lineCount = countLines(instructions);\n    const tokenEstimate = estimateTokens(instructions);\n\n    if (lineCount > SKILL_LIMITS.MAX_INSTRUCTION_LINES) {\n      warnings.push(\n        `Instructions have ${lineCount} lines (recommended: <${SKILL_LIMITS.MAX_INSTRUCTION_LINES}). Consider moving content to references/.`,\n      );\n    }\n\n    if (tokenEstimate > SKILL_LIMITS.MAX_INSTRUCTION_TOKENS) {\n      warnings.push(\n        `Instructions have ~${tokenEstimate} estimated tokens (recommended: <${SKILL_LIMITS.MAX_INSTRUCTION_TOKENS}). Consider moving content to references/.`,\n      );\n    }\n  }\n\n  return {\n    valid: errors.length === 0,\n    errors,\n    warnings,\n  };\n}\n","/**\n * LocalSkillSource - Read-only skill source backed by local filesystem.\n *\n * Uses Node.js fs/promises to read skills directly from disk.\n * This allows skills to be loaded without requiring a full WorkspaceFilesystem.\n *\n * @example\n * ```typescript\n * const source = new LocalSkillSource({\n *   basePath: process.cwd(),\n * });\n *\n * // skills paths are relative to basePath\n * const skillsImpl = new WorkspaceSkillsImpl({\n *   source,\n *   skills: ['./skills', './node_modules/@company/skills'],\n * });\n * ```\n */\n\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\n\nimport { fsExists, fsStat, isTextFile } from '../filesystem';\nimport type { SkillSource, SkillSourceEntry, SkillSourceStat } from './skill-source';\n\n/**\n * Configuration for LocalSkillSource.\n */\nexport interface LocalSkillSourceOptions {\n  /**\n   * Base path for resolving relative skill paths.\n   * Defaults to process.cwd().\n   */\n  basePath?: string;\n}\n\n/**\n * Read-only skill source that loads skills from the local filesystem.\n *\n * Unlike WorkspaceFilesystem, this doesn't provide write operations.\n * Skills loaded from this source are read-only.\n */\nexport class LocalSkillSource implements SkillSource {\n  readonly #basePath: string;\n\n  constructor(options: LocalSkillSourceOptions = {}) {\n    this.#basePath = options.basePath ?? process.cwd();\n  }\n\n  /**\n   * Resolve a path relative to the base path.\n   * Handles both absolute and relative paths.\n   */\n  #resolvePath(skillPath: string): string {\n    if (path.isAbsolute(skillPath)) {\n      return skillPath;\n    }\n    return path.resolve(this.#basePath, skillPath);\n  }\n\n  async exists(skillPath: string): Promise<boolean> {\n    return fsExists(this.#resolvePath(skillPath));\n  }\n\n  async stat(skillPath: string): Promise<SkillSourceStat> {\n    return fsStat(this.#resolvePath(skillPath), skillPath);\n  }\n\n  async readFile(skillPath: string): Promise<string | Buffer> {\n    const resolved = this.#resolvePath(skillPath);\n    const content = await fs.readFile(resolved);\n    // Convert to string for text files\n    if (isTextFile(skillPath)) {\n      return content.toString('utf-8');\n    }\n    return content;\n  }\n\n  async readdir(skillPath: string): Promise<SkillSourceEntry[]> {\n    const resolved = this.#resolvePath(skillPath);\n    const entries = await fs.readdir(resolved, { withFileTypes: true });\n    // Dirent.isDirectory() returns false for symlinks, even when they point to\n    // directories. Detect the target type so skill discovery can load symlinked\n    // skills while still letting higher layers decide whether to recurse.\n    return Promise.all(\n      entries.map(async entry => {\n        const entryPath = path.join(resolved, entry.name);\n        const isSymlink = entry.isSymbolicLink();\n        let type: SkillSourceEntry['type'] = entry.isDirectory() ? 'directory' : 'file';\n\n        if (isSymlink) {\n          try {\n            const targetStat = await fs.stat(entryPath);\n            type = targetStat.isDirectory() ? 'directory' : 'file';\n          } catch {\n            type = 'file';\n          }\n        }\n\n        return {\n          name: entry.name,\n          type,\n          isSymlink: isSymlink || undefined,\n        };\n      }),\n    );\n  }\n\n  async realpath(skillPath: string): Promise<string> {\n    return fs.realpath(this.#resolvePath(skillPath));\n  }\n}\n","/**\n * WorkspaceSkills - Skills implementation.\n *\n * Provides discovery and search operations for skills stored\n * in skills paths. All operations are async.\n */\n\nimport matter from 'gray-matter';\n\nimport { isGlobPattern, resolvePathPattern } from '../glob';\nimport type { ReaddirEntry } from '../glob';\nimport type { IndexDocument, SearchResult } from '../search';\nimport { validateSkillMetadata } from './schemas';\nimport type { SkillSource as SkillSourceInterface } from './skill-source';\nimport type {\n  ContentSource,\n  Skill,\n  SkillMetadata,\n  SkillSearchResult,\n  SkillSearchOptions,\n  WorkspaceSkills,\n  SkillsResolver,\n  SkillsContext,\n} from './types';\n\n// =============================================================================\n// Internal Types\n// =============================================================================\n\n/**\n * Minimal search engine interface - only the methods we actually use.\n * This allows both the real SearchEngine and test mocks to be used.\n */\ninterface SkillSearchEngine {\n  index(doc: IndexDocument): Promise<void>;\n  remove?(id: string): Promise<void>;\n  search(\n    query: string,\n    options?: { topK?: number; minScore?: number; mode?: 'bm25' | 'vector' | 'hybrid' },\n  ): Promise<SearchResult[]>;\n  clear(): void;\n}\n\ninterface InternalSkill extends Skill {\n  /** Content for BM25 indexing (instructions + all references) */\n  indexableContent: string;\n}\n\n// =============================================================================\n// WorkspaceSkillsImpl\n// =============================================================================\n\n/**\n * Configuration for WorkspaceSkillsImpl\n */\nexport interface WorkspaceSkillsImplConfig {\n  /**\n   * Source for loading skills.\n   */\n  source: SkillSourceInterface;\n  /**\n   * Paths to scan for skills.\n   * Can be a static array or a function that returns paths based on context.\n   */\n  skills: SkillsResolver;\n  /** Search engine for skill search (optional) */\n  searchEngine?: SkillSearchEngine;\n  /** Validate skills on load (default: true) */\n  validateOnLoad?: boolean;\n  /**\n   * Check SKILL.md file mtime in addition to directory mtime for staleness detection.\n   * Enables detection of in-place file edits (e.g., fixing validation errors).\n   * Increases stat calls - recommended for local development only.\n   * Default: false\n   */\n  checkSkillFileMtime?: boolean;\n}\n\n/**\n * Implementation of WorkspaceSkills interface.\n */\nexport class WorkspaceSkillsImpl implements WorkspaceSkills {\n  readonly #source: SkillSourceInterface;\n  readonly #skillsResolver: SkillsResolver;\n  readonly #searchEngine?: SkillSearchEngine;\n  readonly #validateOnLoad: boolean;\n  readonly #checkSkillFileMtime: boolean;\n\n  /** Map of skill name -> array of candidates (supports same-named skills from different sources) */\n  #skills: Map<string, InternalSkill[]> = new Map();\n\n  /** Whether skills have been discovered */\n  #initialized = false;\n\n  /** Promise for ongoing initialization (prevents concurrent discovery) */\n  #initPromise: Promise<void> | null = null;\n\n  /** Timestamp of last skills discovery (for staleness check) */\n  #lastDiscoveryTime = 0;\n\n  /** Currently resolved skills paths (used to detect changes) */\n  #resolvedPaths: string[] = [];\n\n  /** Cached glob-resolved directories and per-pattern resolve timestamps */\n  #globDirCache: Map<string, string[]> = new Map();\n  #globResolveTimes: Map<string, number> = new Map();\n  static readonly GLOB_RESOLVE_INTERVAL = 5_000; // Re-walk glob dirs every 5s\n  static readonly STALENESS_CHECK_COOLDOWN = 2_000; // Skip staleness check for 2s after discovery\n\n  constructor(config: WorkspaceSkillsImplConfig) {\n    this.#source = config.source;\n    this.#skillsResolver = config.skills;\n    this.#searchEngine = config.searchEngine;\n    this.#validateOnLoad = config.validateOnLoad ?? true;\n    this.#checkSkillFileMtime = config.checkSkillFileMtime ?? false;\n  }\n\n  // ===========================================================================\n  // Discovery\n  // ===========================================================================\n\n  async list(): Promise<SkillMetadata[]> {\n    await this.#ensureInitialized();\n\n    const results: SkillMetadata[] = [];\n    for (const candidates of this.#skills.values()) {\n      const canonicalCandidates = await this.#dedupeCanonicalCandidates(candidates);\n      for (const skill of canonicalCandidates) {\n        results.push({\n          name: skill.name,\n          path: skill.path,\n          description: skill.description,\n          license: skill.license,\n          compatibility: skill.compatibility,\n          'user-invocable': skill['user-invocable'],\n          metadata: skill.metadata,\n        });\n      }\n    }\n    return results;\n  }\n\n  async get(name: string): Promise<Skill | null> {\n    await this.#ensureInitialized();\n    // Try name-based lookup first, then fall back to path-based (escape hatch)\n    const skill = (await this.#resolveByName(name)) ?? this.#resolveByPath(name);\n    if (!skill) return null;\n\n    // Return without internal indexableContent field\n    const { indexableContent: _, ...skillData } = skill;\n    return skillData;\n  }\n\n  async has(name: string): Promise<boolean> {\n    await this.#ensureInitialized();\n    return ((await this.#resolveByName(name)) ?? this.#resolveByPath(name)) !== null;\n  }\n\n  // ===========================================================================\n  // Skill Resolution (Private)\n  // ===========================================================================\n\n  /**\n   * Resolve a skill by name with tie-breaking when multiple candidates exist.\n   * Priority: local > managed > external, then alphabetical path.\n   */\n  async #resolveByName(name: string): Promise<InternalSkill | null> {\n    const candidates = this.#skills.get(name);\n    if (!candidates || candidates.length === 0) return null;\n    return this.#tieBreak(candidates);\n  }\n\n  /**\n   * Resolve a skill by exact path (escape hatch for disambiguation).\n   * Searches across all candidate arrays.\n   * Accepts paths with or without a trailing `/SKILL.md` suffix, since\n   * SkillsProcessor.formatLocation() exposes `${path}/SKILL.md` to the LLM.\n   */\n  #resolveByPath(skillPath: string): InternalSkill | null {\n    const normalized = skillPath.replace(/\\/SKILL\\.md$/, '');\n    for (const candidates of this.#skills.values()) {\n      const match = candidates.find(s => s.path === normalized);\n      if (match) return match;\n    }\n    return null;\n  }\n\n  async #getCanonicalSkillPath(skillPath: string): Promise<string> {\n    if (!this.#source.realpath) return skillPath;\n\n    try {\n      return await this.#source.realpath(skillPath);\n    } catch {\n      return skillPath;\n    }\n  }\n\n  async #dedupeCanonicalCandidates(candidates: InternalSkill[]): Promise<InternalSkill[]> {\n    const canonicalGroups = new Map<string, InternalSkill[]>();\n    for (const candidate of candidates) {\n      const canonicalPath = await this.#getCanonicalSkillPath(candidate.path);\n      const group = canonicalGroups.get(canonicalPath) ?? [];\n      group.push(candidate);\n      canonicalGroups.set(canonicalPath, group);\n    }\n\n    const SOURCE_PRIORITY: Record<string, number> = { local: 0, managed: 1, external: 2 };\n    return [...canonicalGroups.values()].map(\n      group =>\n        [...group].sort((a, b) => {\n          const aPri = SOURCE_PRIORITY[a.source.type] ?? 99;\n          const bPri = SOURCE_PRIORITY[b.source.type] ?? 99;\n          if (aPri !== bPri) return aPri - bPri;\n          return a.path.localeCompare(b.path);\n        })[0]!,\n    );\n  }\n\n  /**\n   * Pick the winning skill from an array of same-named candidates.\n   * When there's only one candidate, returns it directly (no warning).\n   * When there are multiple, de-duplicates alias paths that point to the same\n   * canonical skill, then applies source-type priority and warns.\n   *\n   * Priority: local (0) > managed (1) > external (2).\n   * Throws if source-type priority can't resolve the tie (e.g., two distinct local skills with same name).\n   */\n  async #tieBreak(candidates: InternalSkill[]): Promise<InternalSkill | null> {\n    if (candidates.length === 0) return null;\n    if (candidates.length === 1) return candidates[0]!;\n\n    const deduped = await this.#dedupeCanonicalCandidates(candidates);\n\n    if (deduped.length === 1) return deduped[0]!;\n\n    const SOURCE_PRIORITY: Record<string, number> = { local: 0, managed: 1, external: 2 };\n    const sorted = [...deduped].sort((a, b) => {\n      const aPri = SOURCE_PRIORITY[a.source.type] ?? 99;\n      const bPri = SOURCE_PRIORITY[b.source.type] ?? 99;\n      if (aPri !== bPri) return aPri - bPri;\n      return a.path.localeCompare(b.path);\n    });\n\n    const winner = sorted[0]!;\n    const runnerUp = sorted[1]!;\n\n    // Error if source-type priority can't break the tie\n    if (winner.source.type === runnerUp.source.type) {\n      const paths = sorted\n        .filter(s => s.source.type === winner.source.type)\n        .map(s => `\"${s.path}\"`)\n        .join(', ');\n      throw new Error(\n        `[WorkspaceSkills] Cannot resolve skill \"${winner.name}\": multiple ${winner.source.type} skills found at ${paths}. ` +\n          `Rename one or move it to a different source type.`,\n      );\n    }\n\n    console.warn(\n      `[WorkspaceSkills] Multiple skills named \"${winner.name}\" found. ` +\n        `Using \"${winner.path}\" (source: ${winner.source.type}). ` +\n        `Other candidates: ${sorted\n          .slice(1)\n          .map(s => `\"${s.path}\" (${s.source.type})`)\n          .join(', ')}`,\n    );\n\n    return winner;\n  }\n\n  async refresh(): Promise<void> {\n    // Remove only skill entries from the shared search engine (not workspace content)\n    for (const candidates of this.#skills.values()) {\n      for (const skill of candidates) {\n        await this.#removeSkillFromIndex(skill);\n      }\n    }\n    this.#skills.clear();\n    this.#initialized = false;\n    this.#initPromise = null;\n    await this.#discoverSkills();\n    this.#initialized = true;\n  }\n\n  async maybeRefresh(context?: SkillsContext): Promise<void> {\n    // Ensure initial discovery is complete\n    await this.#ensureInitialized();\n\n    // Resolve current paths (may be dynamic based on context)\n    const currentPaths = await this.#resolvePaths(context);\n\n    // Check if paths have changed (for dynamic resolvers)\n    const pathsChanged = !this.#arePathsEqual(this.#resolvedPaths, currentPaths);\n    if (pathsChanged) {\n      // Paths changed - need full refresh with new paths\n      this.#resolvedPaths = currentPaths;\n      await this.refresh();\n      return;\n    }\n\n    // Check if any skills path has been modified since last discovery\n    const isStale = await this.#isSkillsPathStale();\n    if (isStale) {\n      await this.refresh();\n    }\n  }\n\n  async addSkill(skillPath: string): Promise<void> {\n    await this.#ensureInitialized();\n\n    // Determine SKILL.md path and dirName\n    let skillFilePath: string;\n    let dirName: string;\n    if (isSkillFilePath(skillPath)) {\n      skillFilePath = skillPath;\n      dirName = splitPathSegments(this.#getParentPath(skillPath)).pop() || 'unknown';\n    } else {\n      skillFilePath = this.#joinPath(skillPath, 'SKILL.md');\n      dirName = splitPathSegments(skillPath).pop() || 'unknown';\n    }\n\n    // Determine source from existing resolved paths\n    const source = this.#inferSource(skillPath);\n\n    // Parse and add to cache\n    const skill = await this.#parseSkillFile(skillFilePath, dirName, source);\n\n    // Remove old index entries if skill already exists at same path (for update case)\n    const candidates = this.#skills.get(skill.name) ?? [];\n    const existingIdx = candidates.findIndex(s => s.path === skill.path);\n    if (existingIdx >= 0) {\n      await this.#removeSkillFromIndex(candidates[existingIdx]!);\n      candidates[existingIdx] = skill;\n    } else {\n      candidates.push(skill);\n    }\n    this.#skills.set(skill.name, candidates);\n    await this.#indexSkill(skill);\n\n    // Update discovery time so maybeRefresh() doesn't trigger full scan\n    this.#lastDiscoveryTime = Date.now();\n  }\n\n  async removeSkill(skillName: string): Promise<void> {\n    await this.#ensureInitialized();\n\n    // Resolve by name (tie-break winner), then fall back to path-based lookup\n    const skill = (await this.#resolveByName(skillName)) ?? this.#resolveByPath(skillName);\n    if (!skill) return;\n\n    // Remove from search index\n    await this.#removeSkillFromIndex(skill);\n\n    // Remove from candidates array\n    const candidates = this.#skills.get(skill.name);\n    if (candidates) {\n      const idx = candidates.findIndex(s => s.path === skill.path);\n      if (idx >= 0) candidates.splice(idx, 1);\n      if (candidates.length === 0) {\n        this.#skills.delete(skill.name);\n      }\n    }\n\n    // Update discovery time so maybeRefresh() doesn't trigger full scan\n    this.#lastDiscoveryTime = Date.now();\n  }\n\n  /**\n   * Resolve skills paths from the resolver (static array or function).\n   */\n  async #resolvePaths(context?: SkillsContext): Promise<string[]> {\n    if (Array.isArray(this.#skillsResolver)) {\n      return this.#skillsResolver;\n    }\n    return this.#skillsResolver(context ?? {});\n  }\n\n  /**\n   * Compare two path arrays for equality (order-independent).\n   */\n  #arePathsEqual(a: string[], b: string[]): boolean {\n    if (a.length !== b.length) return false;\n    const sortedA = [...a].sort();\n    const sortedB = [...b].sort();\n    return sortedA.every((path, i) => path === sortedB[i]);\n  }\n\n  // ===========================================================================\n  // Search\n  // ===========================================================================\n\n  async search(query: string, options: SkillSearchOptions = {}): Promise<SkillSearchResult[]> {\n    await this.#ensureInitialized();\n\n    if (!this.#searchEngine) {\n      // Fall back to simple text matching if no search engine\n      return this.#simpleSearch(query, options);\n    }\n\n    const { topK = 5, minScore, skillNames, includeReferences = true, mode } = options;\n\n    // Ask the search engine for enough rows to survive post-search filtering and\n    // canonical alias de-duplication before applying the final topK.\n    const totalIndexedDocuments = [...this.#skills.values()].reduce(\n      (count, candidates) =>\n        count + candidates.reduce((skillCount, skill) => skillCount + 1 + skill.references.length, 0),\n      0,\n    );\n    const expandedTopK = Math.max(skillNames ? topK * 3 : topK, totalIndexedDocuments);\n\n    // Delegate to SearchEngine\n    const searchResults = await this.#searchEngine.search(query, {\n      topK: expandedTopK,\n      minScore,\n      mode,\n    });\n\n    const results: SkillSearchResult[] = [];\n    const seenCanonicalSources = new Set<string>();\n\n    for (const result of searchResults) {\n      const skillPath = result.metadata?.skillPath as string;\n      const source = result.metadata?.source as string;\n\n      if (!skillPath || !source) continue;\n\n      // Map path back to the canonical skill winner for filtering and results.\n      const matchedSkill = this.#resolveByPath(skillPath);\n      if (!matchedSkill) continue;\n\n      const skill = (await this.#resolveByName(matchedSkill.name)) ?? matchedSkill;\n\n      // Filter by skill names if specified\n      if (skillNames && !skillNames.includes(skill.name)) {\n        continue;\n      }\n\n      // Filter out references if not included\n      if (!includeReferences && source !== 'SKILL.md') {\n        continue;\n      }\n\n      const canonicalSourceKey = `${skill.path}:${source}`;\n      if (seenCanonicalSources.has(canonicalSourceKey)) {\n        continue;\n      }\n      seenCanonicalSources.add(canonicalSourceKey);\n\n      results.push({\n        skillName: skill.name,\n        skillPath: skill.path,\n        source,\n        content: result.content,\n        score: result.score,\n        lineRange: result.lineRange,\n        scoreDetails: result.scoreDetails,\n      });\n\n      if (results.length >= topK) break;\n    }\n\n    return results;\n  }\n\n  // ===========================================================================\n  // Single-item Accessors\n  // ===========================================================================\n\n  async getReference(skillName: string, referencePath: string): Promise<string | null> {\n    await this.#ensureInitialized();\n\n    const skill = (await this.#resolveByName(skillName)) ?? this.#resolveByPath(skillName);\n    if (!skill) return null;\n\n    const safeRefPath = this.#assertRelativePath(referencePath, 'reference');\n    const refFilePath = this.#joinPath(skill.path, safeRefPath);\n\n    if (!(await this.#source.exists(refFilePath))) {\n      return null;\n    }\n\n    try {\n      const content = await this.#source.readFile(refFilePath);\n      return typeof content === 'string' ? content : content.toString('utf-8');\n    } catch {\n      return null;\n    }\n  }\n\n  async getScript(skillName: string, scriptPath: string): Promise<string | null> {\n    await this.#ensureInitialized();\n\n    const skill = (await this.#resolveByName(skillName)) ?? this.#resolveByPath(skillName);\n    if (!skill) return null;\n\n    const safeScriptPath = this.#assertRelativePath(scriptPath, 'script');\n    const scriptFilePath = this.#joinPath(skill.path, safeScriptPath);\n\n    if (!(await this.#source.exists(scriptFilePath))) {\n      return null;\n    }\n\n    try {\n      const content = await this.#source.readFile(scriptFilePath);\n      return typeof content === 'string' ? content : content.toString('utf-8');\n    } catch {\n      return null;\n    }\n  }\n\n  async getAsset(skillName: string, assetPath: string): Promise<Buffer | null> {\n    await this.#ensureInitialized();\n\n    const skill = (await this.#resolveByName(skillName)) ?? this.#resolveByPath(skillName);\n    if (!skill) return null;\n\n    const safeAssetPath = this.#assertRelativePath(assetPath, 'asset');\n    const assetFilePath = this.#joinPath(skill.path, safeAssetPath);\n\n    if (!(await this.#source.exists(assetFilePath))) {\n      return null;\n    }\n\n    try {\n      const content = await this.#source.readFile(assetFilePath);\n      return typeof content === 'string' ? Buffer.from(content, 'utf-8') : content;\n    } catch {\n      return null;\n    }\n  }\n\n  // ===========================================================================\n  // Listing Accessors\n  // ===========================================================================\n\n  async listReferences(skillName: string): Promise<string[]> {\n    await this.#ensureInitialized();\n    const skill = (await this.#resolveByName(skillName)) ?? this.#resolveByPath(skillName);\n    return skill?.references ?? [];\n  }\n\n  async listScripts(skillName: string): Promise<string[]> {\n    await this.#ensureInitialized();\n    const skill = (await this.#resolveByName(skillName)) ?? this.#resolveByPath(skillName);\n    return skill?.scripts ?? [];\n  }\n\n  async listAssets(skillName: string): Promise<string[]> {\n    await this.#ensureInitialized();\n    const skill = (await this.#resolveByName(skillName)) ?? this.#resolveByPath(skillName);\n    return skill?.assets ?? [];\n  }\n\n  // ===========================================================================\n  // Private Methods\n  // ===========================================================================\n\n  /**\n   * Ensure skills have been discovered.\n   * Uses a promise to prevent concurrent discovery.\n   */\n  async #ensureInitialized(): Promise<void> {\n    if (this.#initialized) {\n      return;\n    }\n\n    // If initialization is already in progress, wait for it\n    if (this.#initPromise) {\n      await this.#initPromise;\n      return;\n    }\n\n    // Start initialization and store the promise\n    this.#initPromise = (async () => {\n      try {\n        // Resolve paths on first initialization (uses empty context)\n        if (this.#resolvedPaths.length === 0) {\n          this.#resolvedPaths = await this.#resolvePaths();\n        }\n        await this.#discoverSkills();\n        this.#initialized = true;\n      } finally {\n        this.#initPromise = null;\n      }\n    })();\n\n    await this.#initPromise;\n  }\n\n  /**\n   * Add a skill to the candidates map, keyed by name.\n   * Replaces an existing entry at the same path (update case), otherwise appends.\n   */\n  #addToSkillsMap(skill: InternalSkill): void {\n    const candidates = this.#skills.get(skill.name) ?? [];\n    const idx = candidates.findIndex(s => s.path === skill.path);\n    if (idx >= 0) {\n      candidates[idx] = skill;\n    } else {\n      candidates.push(skill);\n    }\n    this.#skills.set(skill.name, candidates);\n  }\n\n  /**\n   * Discover skills from all skills paths.\n   * Uses currently resolved paths (must be set before calling).\n   *\n   * Paths can be plain directories, glob patterns, or direct\n   * skill references (e.g., '/skills/my-skill/SKILL.md').\n   *\n   * Uses resolvePathPattern for unified glob resolution. File matches\n   * pointing to SKILL.md are loaded directly; directory matches are\n   * tried as direct skills first, then scanned for subdirectories.\n   */\n  async #discoverSkills(): Promise<void> {\n    // Clear glob cache so discovery gets fresh results\n    this.#globDirCache.clear();\n    this.#globResolveTimes.clear();\n\n    // Adapt SkillSource.readdir to the ReaddirEntry interface used by resolvePathPattern\n    const readdir = async (dir: string): Promise<ReaddirEntry[]> => {\n      const entries = await this.#source.readdir(dir);\n      return entries.map(e => ({ name: e.name, type: e.type, isSymlink: e.isSymlink }));\n    };\n\n    for (const rawSkillsPath of this.#resolvedPaths) {\n      // Strip trailing slash for consistent path handling (e.g. '/skills/' → '/skills')\n      const skillsPath =\n        rawSkillsPath.length > 1 && rawSkillsPath.endsWith('/') ? rawSkillsPath.slice(0, -1) : rawSkillsPath;\n      const source = this.#determineSource(skillsPath);\n\n      if (isGlobPattern(skillsPath)) {\n        // Glob pattern: resolve to matching entries, then discover skills from each\n        const resolved = await resolvePathPattern(skillsPath, readdir, { dot: true, maxDepth: 4 });\n\n        // Cache directories for staleness checks: matched dirs directly,\n        // and parent dirs for matched files (e.g. **/SKILL.md → parent skill dir)\n        const dirs = new Set<string>();\n        for (const entry of resolved) {\n          if (entry.type === 'directory') {\n            dirs.add(entry.path);\n          } else {\n            dirs.add(this.#getParentPath(entry.path));\n          }\n        }\n        this.#globDirCache.set(skillsPath, [...dirs]);\n        this.#globResolveTimes.set(skillsPath, Date.now());\n\n        // Process glob-resolved entries in parallel (independent discoveries)\n        const results = await Promise.allSettled(\n          resolved.map(async entry => {\n            if (entry.type === 'file') {\n              // File match (e.g., **/SKILL.md) — load as direct skill\n              await this.#discoverDirectSkill(entry.path, source);\n            } else {\n              // Directory match — try as direct skill first, then scan subdirectories\n              const isDirect = await this.#discoverDirectSkill(entry.path, source);\n              if (!isDirect) {\n                await this.#discoverSkillsInPath(entry.path, source);\n              }\n            }\n          }),\n        );\n\n        for (const [index, result] of results.entries()) {\n          const entry = resolved[index];\n          if (entry && result.status === 'rejected') {\n            const error = result.reason;\n            if (error instanceof Error) {\n              console.error(`[WorkspaceSkills] Failed to load skill from ${entry.path}:`, error.message);\n            }\n          }\n        }\n      } else {\n        // Check if the path is a direct skill reference (directory with SKILL.md or SKILL.md file)\n        const isDirect = await this.#discoverDirectSkill(skillsPath, source);\n        if (!isDirect) {\n          // Plain path: scan subdirectories for skills\n          await this.#discoverSkillsInPath(skillsPath, source);\n        }\n      }\n    }\n    // Track when discovery completed for staleness check\n    this.#lastDiscoveryTime = Date.now();\n  }\n\n  /**\n   * Discover skills in a single path\n   */\n  async #discoverSkillsInPath(skillsPath: string, source: ContentSource): Promise<void> {\n    try {\n      if (!(await this.#source.exists(skillsPath))) {\n        return;\n      }\n    } catch (error) {\n      const msg = error instanceof Error ? error.message : String(error);\n      let hint = '';\n\n      // If an absolute path like \"/skills\" fails, check if the relative equivalent exists\n      if (skillsPath.startsWith('/') && msg.includes('Permission denied')) {\n        const relativePath = skillsPath.slice(1);\n        try {\n          if (await this.#source.exists(relativePath)) {\n            hint = ` (did you mean to use the relative path \"${relativePath}\"?)`;\n          }\n        } catch {\n          // ignore — just skip the hint\n        }\n      }\n\n      console.warn(`[WorkspaceSkills] Cannot access skills path \"${skillsPath}\": ${msg}${hint}`);\n      return;\n    }\n\n    try {\n      const entries = await this.#source.readdir(skillsPath);\n\n      // Process all skill directories in parallel (each is independent)\n      const results = await Promise.allSettled(\n        entries\n          .filter(entry => entry.type === 'directory')\n          .map(async entry => {\n            const entryPath = this.#joinPath(skillsPath, entry.name);\n            const skillFilePath = this.#joinPath(entryPath, 'SKILL.md');\n\n            if (await this.#source.exists(skillFilePath)) {\n              const skill = await this.#parseSkillFile(skillFilePath, entry.name, source);\n              return skill;\n            }\n            return null;\n          }),\n      );\n\n      // Apply results sequentially to preserve overwrite semantics\n      for (const result of results) {\n        if (result.status === 'fulfilled' && result.value) {\n          this.#addToSkillsMap(result.value);\n          await this.#indexSkill(result.value);\n        } else if (result.status === 'rejected') {\n          const error = result.reason;\n          if (error instanceof Error) {\n            console.error(`[WorkspaceSkills] Failed to load skill from ${skillsPath}:`, error.message);\n          }\n        }\n      }\n    } catch (error) {\n      if (error instanceof Error) {\n        console.error(`[WorkspaceSkills] Failed to scan skills directory ${skillsPath}:`, error.message);\n      }\n    }\n  }\n\n  /**\n   * Attempt to discover a skill from a direct path reference.\n   *\n   * Handles two cases:\n   * - Path ends with `/SKILL.md` → parse directly, extract dirName from parent\n   * - Path is a directory containing `SKILL.md` → parse it as a single skill\n   *\n   * Returns `true` if the path was a direct skill reference (skip subdirectory scan),\n   * `false` to fall through to the normal subdirectory scan.\n   */\n  async #discoverDirectSkill(skillsPath: string, source: ContentSource): Promise<boolean> {\n    try {\n      // Case 1: Path points directly to a SKILL.md file\n      if (isSkillFilePath(skillsPath)) {\n        if (!(await this.#source.exists(skillsPath))) {\n          return true; // It was a direct reference, just doesn't exist — skip subdirectory scan\n        }\n\n        const skillDir = this.#getParentPath(skillsPath);\n        const dirName = splitPathSegments(skillDir).pop() || skillDir;\n\n        try {\n          const skill = await this.#parseSkillFile(skillsPath, dirName, source);\n          this.#addToSkillsMap(skill);\n          await this.#indexSkill(skill);\n        } catch (error) {\n          if (error instanceof Error) {\n            console.error(`[WorkspaceSkills] Failed to load skill from ${skillsPath}:`, error.message);\n          }\n        }\n        return true;\n      }\n\n      // Case 2: Path is a directory that directly contains SKILL.md\n      if (await this.#source.exists(skillsPath)) {\n        const skillFilePath = this.#joinPath(skillsPath, 'SKILL.md');\n        if (await this.#source.exists(skillFilePath)) {\n          const dirName = splitPathSegments(skillsPath).pop() || skillsPath;\n\n          try {\n            const skill = await this.#parseSkillFile(skillFilePath, dirName, source);\n            this.#addToSkillsMap(skill);\n            await this.#indexSkill(skill);\n          } catch (error) {\n            if (error instanceof Error) {\n              console.error(`[WorkspaceSkills] Failed to load skill from ${skillFilePath}:`, error.message);\n            }\n          }\n          return true;\n        }\n      }\n\n      return false;\n    } catch {\n      return false;\n    }\n  }\n\n  /**\n   * Check if any skills path directory has been modified since last discovery.\n   * Compares directory mtime to lastDiscoveryTime.\n   * For glob patterns, checks the walk root and expanded directories.\n   */\n  async #isSkillsPathStale(): Promise<boolean> {\n    if (this.#lastDiscoveryTime === 0) {\n      // Never discovered, consider stale\n      return true;\n    }\n\n    // Skip the expensive stat calls if discovery happened very recently\n    // (e.g., right after a surgical addSkill/removeSkill). This avoids\n    // a timing race where the filesystem write updates directory mtime\n    // to the same second as #lastDiscoveryTime, and also avoids slow\n    // stat calls to external mounts immediately after a known-good update.\n    if (Date.now() - this.#lastDiscoveryTime < WorkspaceSkillsImpl.STALENESS_CHECK_COOLDOWN) {\n      return false;\n    }\n\n    for (const skillsPath of this.#resolvedPaths) {\n      let pathsToCheck: string[];\n\n      if (isGlobPattern(skillsPath)) {\n        // Use cached glob dirs, re-resolve periodically to discover new entries\n        const now = Date.now();\n        const lastResolved = this.#globResolveTimes.get(skillsPath) ?? 0;\n        if (now - lastResolved > WorkspaceSkillsImpl.GLOB_RESOLVE_INTERVAL || !this.#globDirCache.has(skillsPath)) {\n          const readdir = async (dir: string): Promise<ReaddirEntry[]> => {\n            const entries = await this.#source.readdir(dir);\n            return entries.map(e => ({ name: e.name, type: e.type, isSymlink: e.isSymlink }));\n          };\n          const resolved = await resolvePathPattern(skillsPath, readdir, { dot: true, maxDepth: 4 });\n          // For staleness checks we need directories: matched dirs directly,\n          // and parent dirs for matched files (e.g. **/SKILL.md → parent skill dir)\n          const dirs = new Set<string>();\n          for (const entry of resolved) {\n            if (entry.type === 'directory') {\n              dirs.add(entry.path);\n            } else {\n              dirs.add(this.#getParentPath(entry.path));\n            }\n          }\n          const dirList = [...dirs];\n          this.#globDirCache.set(skillsPath, dirList);\n          this.#globResolveTimes.set(skillsPath, now);\n        }\n        pathsToCheck = this.#globDirCache.get(skillsPath) ?? [];\n      } else {\n        pathsToCheck = [skillsPath];\n      }\n\n      for (const pathToCheck of pathsToCheck) {\n        try {\n          const stat = await this.#source.stat(pathToCheck);\n          const mtime = stat.modifiedAt.getTime();\n\n          if (mtime > this.#lastDiscoveryTime) {\n            return true;\n          }\n\n          // Skip subdirectory scan for non-directory paths (direct skill references)\n          if (stat.type !== 'directory') {\n            continue;\n          }\n\n          // If this directory is itself a skill root, check its SKILL.md mtime.\n          // This covers direct skill paths and file-level glob expansions (e.g., **/SKILL.md).\n          if (this.#checkSkillFileMtime) {\n            const directSkillFilePath = this.#joinPath(pathToCheck, 'SKILL.md');\n            try {\n              const directSkillFileStat = await this.#source.stat(directSkillFilePath);\n              if (\n                directSkillFileStat.type === 'file' &&\n                directSkillFileStat.modifiedAt.getTime() > this.#lastDiscoveryTime\n              ) {\n                return true;\n              }\n            } catch {\n              // Not a direct skill dir (or SKILL.md unavailable), continue to subdirectory scan.\n            }\n          }\n\n          // Also check subdirectories (skill directories) for changes — in parallel\n          const entries = await this.#source.readdir(pathToCheck);\n          const dirEntries = entries.filter(entry => entry.type === 'directory');\n\n          if (dirEntries.length > 0) {\n            const statResults = await Promise.all(\n              dirEntries.map(async entry => {\n                const entryPath = this.#joinPath(pathToCheck, entry.name);\n                try {\n                  const entryStat = await this.#source.stat(entryPath);\n                  if (entryStat.modifiedAt.getTime() > this.#lastDiscoveryTime) {\n                    return true;\n                  }\n\n                  // Optionally check SKILL.md file mtime - editing file content may not update directory mtime.\n                  // This doubles stat calls per skill, so it's opt-in for local development scenarios.\n                  if (this.#checkSkillFileMtime) {\n                    const skillFilePath = this.#joinPath(entryPath, 'SKILL.md');\n                    try {\n                      const skillFileStat = await this.#source.stat(skillFilePath);\n                      return (\n                        skillFileStat.type === 'file' && skillFileStat.modifiedAt.getTime() > this.#lastDiscoveryTime\n                      );\n                    } catch {\n                      // SKILL.md doesn't exist or can't be stat'd, skip\n                    }\n                  }\n                } catch {\n                  // Couldn't stat entry, skip it\n                }\n                return false;\n              }),\n            );\n            if (statResults.some(stale => stale)) {\n              return true;\n            }\n          }\n        } catch {\n          // Couldn't stat path (doesn't exist or error), skip to next\n          continue;\n        }\n      }\n    }\n\n    return false;\n  }\n\n  /**\n   * Parse a SKILL.md file\n   */\n  async #parseSkillFile(filePath: string, dirName: string, source: ContentSource): Promise<InternalSkill> {\n    const rawContent = await this.#source.readFile(filePath);\n    const content = typeof rawContent === 'string' ? rawContent : rawContent.toString('utf-8');\n\n    const parsed = matter(content);\n    const frontmatter = parsed.data;\n    const body = parsed.content.trim();\n\n    // Extract required fields\n    // Get skill directory path (parent of SKILL.md) - needed for SkillMetadata\n    const skillPath = this.#getParentPath(filePath);\n\n    const metadata: SkillMetadata = {\n      name: frontmatter.name,\n      path: skillPath,\n      description: frontmatter.description,\n      license: frontmatter.license,\n      compatibility: frontmatter.compatibility,\n      'user-invocable': frontmatter['user-invocable'],\n      metadata: frontmatter.metadata,\n    };\n\n    // Validate if enabled (includes token/line count warnings)\n    if (this.#validateOnLoad) {\n      const validation = this.#validateSkillMetadata(metadata, dirName, body);\n      if (!validation.valid) {\n        throw new Error(`Invalid skill metadata in ${filePath}:\\n${validation.errors.join('\\n')}`);\n      }\n    }\n\n    // Discover reference, script, and asset files (parallel — independent subdirs)\n    const [references, scripts, assets] = await Promise.all([\n      this.#discoverFilesInSubdir(skillPath, 'references'),\n      this.#discoverFilesInSubdir(skillPath, 'scripts'),\n      this.#discoverFilesInSubdir(skillPath, 'assets'),\n    ]);\n\n    // Build indexable content (instructions + references)\n    const indexableContent = await this.#buildIndexableContent(body, skillPath, references);\n\n    return {\n      ...metadata,\n      instructions: body,\n      source,\n      references,\n      scripts,\n      assets,\n      indexableContent,\n    };\n  }\n\n  /**\n   * Validate skill metadata (delegates to shared validation function)\n   */\n  #validateSkillMetadata(\n    metadata: SkillMetadata,\n    dirName: string,\n    instructions?: string,\n  ): { valid: boolean; errors: string[]; warnings: string[] } {\n    const result = validateSkillMetadata(metadata, dirName, instructions);\n\n    // Log warnings if any\n    if (result.warnings.length > 0) {\n      for (const warning of result.warnings) {\n        console.warn(`[WorkspaceSkills] ${metadata.name}: ${warning}`);\n      }\n    }\n\n    return result;\n  }\n\n  /**\n   * Discover files in a subdirectory of a skill (references/, scripts/, assets/)\n   */\n  async #discoverFilesInSubdir(skillPath: string, subdir: 'references' | 'scripts' | 'assets'): Promise<string[]> {\n    const subdirPath = this.#joinPath(skillPath, subdir);\n    const files: string[] = [];\n\n    if (!(await this.#source.exists(subdirPath))) {\n      return files;\n    }\n\n    try {\n      await this.#walkDirectory(subdirPath, subdirPath, (relativePath: string) => {\n        files.push(relativePath);\n      });\n    } catch {\n      // Failed to read subdirectory\n    }\n\n    return files;\n  }\n\n  /**\n   * Walk a directory recursively and call callback for each file.\n   * Limited to maxDepth (default 20) to prevent stack overflow on deep hierarchies.\n   */\n  async #walkDirectory(\n    basePath: string,\n    dirPath: string,\n    callback: (relativePath: string) => void,\n    depth: number = 0,\n    maxDepth: number = 20,\n  ): Promise<void> {\n    if (depth >= maxDepth) {\n      return;\n    }\n\n    const entries = await this.#source.readdir(dirPath);\n\n    for (const entry of entries) {\n      const entryPath = this.#joinPath(dirPath, entry.name);\n\n      if (entry.type === 'directory' && !entry.isSymlink) {\n        await this.#walkDirectory(basePath, entryPath, callback, depth + 1, maxDepth);\n      } else {\n        // Get relative path from base\n        const relativePath = entryPath.substring(basePath.length + 1);\n        callback(relativePath);\n      }\n    }\n  }\n\n  /**\n   * Build indexable content from instructions and references\n   */\n  async #buildIndexableContent(instructions: string, skillPath: string, references: string[]): Promise<string> {\n    const parts = [instructions];\n\n    // Read all reference files in parallel (independent reads, order preserved by map)\n    const refContents = await Promise.all(\n      references.map(async refPath => {\n        const fullPath = this.#joinPath(skillPath, 'references', refPath);\n        try {\n          const rawContent = await this.#source.readFile(fullPath);\n          return typeof rawContent === 'string' ? rawContent : rawContent.toString('utf-8');\n        } catch {\n          return null; // Skip files that can't be read\n        }\n      }),\n    );\n\n    for (const content of refContents) {\n      if (content !== null) parts.push(content);\n    }\n\n    return parts.join('\\n\\n');\n  }\n\n  /**\n   * Remove a skill's entries from the search index.\n   */\n  async #removeSkillFromIndex(skill: InternalSkill): Promise<void> {\n    if (!this.#searchEngine?.remove) return;\n\n    const ids = [`skill:${skill.path}:SKILL.md`, ...skill.references.map(r => `skill:${skill.path}:${r}`)];\n    for (const id of ids) {\n      try {\n        await this.#searchEngine.remove(id);\n      } catch {\n        // Best-effort removal; entry may already be gone\n      }\n    }\n  }\n\n  /**\n   * Infer the ContentSource for a skill path by matching against resolved paths.\n   */\n  #inferSource(skillPath: string): ContentSource {\n    for (const rp of this.#resolvedPaths) {\n      if (skillPath === rp || skillPath.startsWith(rp + '/')) {\n        return this.#determineSource(rp);\n      }\n    }\n    return this.#determineSource(skillPath);\n  }\n\n  /**\n   * Index a skill for search\n   */\n  async #indexSkill(skill: InternalSkill): Promise<void> {\n    if (!this.#searchEngine) return;\n\n    // Index the main skill instructions\n    await this.#searchEngine.index({\n      id: `skill:${skill.path}:SKILL.md`,\n      content: skill.instructions,\n      metadata: {\n        skillPath: skill.path,\n        source: 'SKILL.md',\n      },\n    });\n\n    // Index each reference file in parallel (independent reads + index calls)\n    await Promise.all(\n      skill.references.map(async refPath => {\n        const fullPath = this.#joinPath(skill.path, 'references', refPath);\n        try {\n          const rawContent = await this.#source.readFile(fullPath);\n          const content = typeof rawContent === 'string' ? rawContent : rawContent.toString('utf-8');\n          await this.#searchEngine!.index({\n            id: `skill:${skill.path}:${refPath}`,\n            content,\n            metadata: {\n              skillPath: skill.path,\n              source: `references/${refPath}`,\n            },\n          });\n        } catch {\n          // Skip files that can't be read\n        }\n      }),\n    );\n  }\n\n  /**\n   * Simple text search fallback when no search engine is configured\n   */\n  async #simpleSearch(query: string, options: SkillSearchOptions): Promise<SkillSearchResult[]> {\n    const { topK = 5, skillNames, includeReferences = true } = options;\n    const queryLower = query.toLowerCase();\n    const results: SkillSearchResult[] = [];\n\n    for (const candidates of this.#skills.values()) {\n      // Use tie-break winner for each name\n      const skill = await this.#tieBreak(candidates);\n      if (!skill) continue;\n\n      // Filter by skill names if specified\n      if (skillNames && !skillNames.includes(skill.name)) {\n        continue;\n      }\n\n      // Search in instructions\n      if (skill.instructions.toLowerCase().includes(queryLower)) {\n        results.push({\n          skillName: skill.name,\n          skillPath: skill.path,\n          source: 'SKILL.md',\n          content: skill.instructions.substring(0, 200),\n          score: 1,\n        });\n      }\n\n      // Search in references if included\n      if (includeReferences) {\n        for (const refPath of skill.references) {\n          if (results.length >= topK) break;\n          const content = await this.getReference(skill.name, `references/${refPath}`);\n          if (content && content.toLowerCase().includes(queryLower)) {\n            results.push({\n              skillName: skill.name,\n              skillPath: skill.path,\n              source: `references/${refPath}`,\n              content: content.substring(0, 200),\n              score: 0.8,\n            });\n          }\n        }\n      }\n\n      if (results.length >= topK) break;\n    }\n\n    return results.slice(0, topK);\n  }\n\n  /**\n   * Determine the source type based on the path\n   */\n  #determineSource(skillsPath: string): ContentSource {\n    // Use path segment matching to avoid false positives (e.g., my-node_modules).\n    // Consumer-supplied absolute paths may use either separator ('\\' on Windows).\n    const segments = splitPathSegments(skillsPath);\n    if (segments.includes('node_modules')) {\n      return { type: 'external', packagePath: skillsPath };\n    }\n    const normalized = skillsPath.replace(/\\\\/g, '/');\n    if (normalized.includes('/.mastra/skills') || normalized.startsWith('.mastra/skills')) {\n      return { type: 'managed', mastraPath: skillsPath };\n    }\n    return { type: 'local', projectPath: skillsPath };\n  }\n\n  /**\n   * Join path segments (workspace paths use forward slashes)\n   */\n  #joinPath(...segments: string[]): string {\n    return segments\n      .map((seg, i) => (i === 0 ? stripTrailingSlashes(seg) : stripLeadingAndTrailingSlashes(seg)))\n      .filter(Boolean)\n      .join('/');\n  }\n\n  /**\n   * Validate and normalize a relative path to prevent directory traversal.\n   * Throws if the path contains traversal segments (..) or is absolute.\n   */\n  #assertRelativePath(input: string, label: string): string {\n    const normalized = input.replace(/\\\\/g, '/');\n    const segments = normalized.split('/').filter(seg => Boolean(seg) && seg !== '.');\n    if (normalized.startsWith('/') || segments.some(seg => seg === '..')) {\n      throw new Error(`Invalid ${label} path: ${input}`);\n    }\n    return segments.join('/');\n  }\n\n  /**\n   * Get parent path\n   */\n  #getParentPath(path: string): string {\n    const lastSlash = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\\\'));\n    return lastSlash > 0 ? path.substring(0, lastSlash) : '/';\n  }\n}\n\n/**\n * Split a path into segments, tolerating both POSIX (`/`) and Windows (`\\`)\n * separators. Workspace-internal paths use forward slashes, but consumer-supplied\n * absolute paths (e.g. via `new Workspace({ skills: [...] })`) may use backslashes\n * on Windows.\n */\nfunction splitPathSegments(path: string): string[] {\n  return path.split(/[\\\\/]+/);\n}\n\n/**\n * Whether a path points directly at a `SKILL.md` file, tolerating both separators.\n */\nfunction isSkillFilePath(path: string): boolean {\n  return path === 'SKILL.md' || /[\\\\/]SKILL\\.md$/.test(path);\n}\n\nfunction stripTrailingSlashes(s: string): string {\n  let end = s.length;\n  while (end > 0 && s.charCodeAt(end - 1) === 47 /* \"/\" */) {\n    end--;\n  }\n  return end === s.length ? s : s.slice(0, end);\n}\n\nfunction stripLeadingAndTrailingSlashes(s: string): string {\n  let start = 0;\n  while (start < s.length && s.charCodeAt(start) === 47 /* \"/\" */) {\n    start++;\n  }\n  let end = s.length;\n  while (end > start && s.charCodeAt(end - 1) === 47 /* \"/\" */) {\n    end--;\n  }\n  return start === 0 && end === s.length ? s : s.slice(start, end);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,YAAY,GAAmB;CAC7C,IAAI,MAAM,KAAK,OAAO,GAAG,QAAQ;CACjC,IAAI,EAAE,WAAW,IAAI,KAAK,EAAE,WAAW,KAAK,GAC1C,OAAO,KAAK,KAAK,GAAG,QAAQ,GAAG,EAAE,MAAM,CAAC,CAAC;CAE3C,OAAO;AACT;;;;AAgCA,SAAgB,cAAc,OAAqE;CACjG,OACE,UAAU,QAAQ,OAAO,UAAU,YAAY,UAAU,SAAU,MAAgC,SAAS;AAEhH;;;;AAKA,SAAgB,cAAc,OAAqE;CACjG,OACE,UAAU,QAAQ,OAAO,UAAU,YAAY,UAAU,SAAU,MAAgC,SAAS;AAEhH;AAMA,MAAM,aAAqC;CAEzC,KAAK;CACL,MAAM;CACN,KAAK;CACL,KAAK;CACL,KAAK;CACL,IAAI;CAEJ,IAAI;CACJ,KAAK;CACL,IAAI;CACJ,KAAK;CACL,KAAK;CACL,MAAM;CACN,KAAK;CACL,MAAM;CACN,KAAK;CAEL,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,MAAM;CACN,GAAG;CACH,KAAK;CACL,GAAG;CACH,KAAK;CACL,IAAI;CACJ,MAAM;CACN,KAAK;CAEL,MAAM;CACN,KAAK;CACL,KAAK;CAEL,KAAK;CACL,SAAS;CACT,KAAK;CAEL,KAAK;CACL,QAAQ;CAER,MAAM;CACN,MAAM;CACN,MAAM;CAEN,KAAK;CACL,OAAO;CACP,IAAI;CACJ,KAAK;CACL,MAAM;CACN,KAAK;CACL,GAAG;CACH,IAAI;CACJ,QAAQ;CACR,KAAK;CAEL,KAAK;CACL,KAAK;CACL,MAAM;CACN,KAAK;CACL,KAAK;CACL,MAAM;CACN,KAAK;CACL,KAAK;CACL,MAAM;CACN,KAAK;CACL,MAAM;CACN,MAAM;CACN,MAAM;CAEN,KAAK;CAEL,KAAK;CACL,KAAK;CACL,KAAK;CACL,MAAM;CACN,KAAK;CACL,KAAK;CAEL,KAAK;CACL,MAAM;CACN,KAAK;CACL,KAAK;CACL,KAAK;CAEL,KAAK;CACL,KAAK;CACL,IAAI;CACJ,KAAK;CACL,KAAK;CACL,MAAM;CACN,KAAK;CAEL,KAAK;CACL,KAAK;CACL,IAAI;CACJ,OAAO;CACP,KAAK;CACL,KAAK;CAEL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CAEL,KAAK;CACL,MAAM;CACN,KAAK;CACL,MAAM;CACN,KAAK;CACL,MAAM;CAEN,KAAK;CACL,KAAK;CACL,MAAM;CACN,OAAO;CAEP,MAAM;CACN,OAAO;CACP,KAAK;AACP;;;;AAKA,SAAgB,YAAY,UAA0B;CACpD,MAAM,MAAM,KAAK,QAAQ,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY;CACxD,OAAO,WAAW,QAAQ;AAC5B;;;;AAKA,MAAM,kCAAkB,IAAI,IAAI;CAC9B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;;;;AAKD,SAAgB,WAAW,UAA2B;CACpD,MAAM,MAAM,KAAK,QAAQ,QAAQ,CAAC,CAAC,YAAY;CAC/C,OAAO,gBAAgB,IAAI,GAAG;AAChC;;;;;;;;;;;;AAiBA,SAAgB,kBAAkB,UAAkB,UAA0B;CAC5E,MAAM,WAAW,YAAY,QAAQ;CACrC,IAAI,KAAK,WAAW,QAAQ,GAC1B,OAAO,KAAK,UAAU,QAAQ;CAEhC,OAAO,KAAK,QAAQ,UAAU,QAAQ;AACxC;;;;;;;;AAaA,eAAsB,SAAS,cAAwC;CACrE,IAAI;EACF,MAAMA,YAAG,OAAO,YAAY;EAC5B,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;;;;;;;;;;AAWA,eAAsB,OAAO,cAAsB,UAAyC;CAC1F,IAAI;EACF,MAAM,QAAQ,MAAMA,YAAG,KAAK,YAAY;EACxC,OAAO;GACL,MAAM,KAAK,SAAS,YAAY;GAChC,MAAM,MAAM,YAAY,IAAI,cAAc;GAC1C,MAAM,MAAM;GACZ,WAAW,MAAM;GACjB,YAAY,MAAM;GAClB,UAAU,MAAM,OAAO,IAAI,YAAY,YAAY,IAAI,KAAA;EACzD;CACF,SAAS,OAAgB;EACvB,IAAI,cAAc,KAAK,GACrB,MAAM,IAAIC,eAAAA,kBAAkB,QAAQ;EAEtC,MAAM;CACR;AACF;;;;;;;;;;;ACtUA,MAAM,aAAa;;;;;;;;;;AAWnB,SAAgB,cAAc,OAAwB;CACpD,OAAO,WAAW,KAAK,KAAK;AAC9B;;;;;;;;;;;AAgBA,SAAgB,gBAAgB,SAAyB;CAEvD,MAAM,YAAY,QAAQ,OAAO,UAAU;CAE3C,IAAI,cAAc,IAEhB,OAAO;CAIT,MAAM,SAAS,QAAQ,MAAM,GAAG,SAAS;CAGzC,MAAM,YAAY,OAAO,YAAY,GAAG;CAExC,IAAI,aAAa,GAEf,OAAO;CAGT,OAAO,OAAO,MAAM,GAAG,SAAS;AAClC;;;;;;;;;AAsBA,SAAS,kBAAkB,OAAuB;CAChD,IAAI,MAAM,WAAW,IAAI,GAAG,OAAO,MAAM,MAAM,CAAC;CAChD,IAAI,MAAM,WAAW,GAAG,GAAG,OAAO,MAAM,MAAM,CAAC;CAC/C,OAAO;AACT;;;;;;;;;;;;;;;;AAiBA,SAAgB,kBAAkB,UAA6B,SAA2C;CAExG,MAAM,WAAA,GAAA,UAAA,QAAA,EADgB,MAAM,QAAQ,QAAQ,IAAI,WAAW,CAAC,QAAQ,EAAA,CAAG,IAAI,iBACtC,GAAG;EACtC,OAAO;EACP,KAAK,SAAS,OAAO;CACvB,CAAC;CACD,QAAQ,SAAiB,QAAQ,kBAAkB,IAAI,CAAC;AAC1D;;;;;;;;;;AAWA,SAAgB,UAAU,MAAc,SAA4B,SAAuC;CACzG,OAAO,kBAAkB,SAAS,OAAO,CAAC,CAAC,IAAI;AACjD;;;;;AA8BA,eAAe,QACb,SACA,KACA,OACA,UACsB;CACtB,IAAI,SAAS,UAAU,OAAO,CAAC;CAC/B,IAAI;EACF,MAAM,UAAU,MAAM,QAAQ,GAAG;EACjC,MAAM,UAAuB,CAAC;EAC9B,KAAK,MAAM,SAAS,SAAS;GAC3B,IAAI,MAAM,SAAS,eAAe,MAAM,WAAW;GACnD,MAAM,WAAW,QAAQ,OAAO,QAAQ,KAAK,MAAM,OAAO,GAAG,IAAI,GAAG,MAAM;GAC1E,QAAQ,KAAK;IAAE,MAAM;IAAU,MAAM,MAAM;GAAK,CAAC;GACjD,IAAI,MAAM,SAAS,aACjB,QAAQ,KAAK,GAAI,MAAM,QAAQ,SAAS,UAAU,QAAQ,GAAG,QAAQ,CAAE;EAE3E;EACA,OAAO;CACT,QAAQ;EACN,OAAO,CAAC;CACV;AACF;;;;;;;;;;;;;;;;;;AAmBA,eAAsB,mBACpB,SACA,SACA,SACsB;CACtB,MAAM,WAAW,SAAS,YAAY;CAGtC,MAAM,aAAa,QAAQ,SAAS,KAAK,QAAQ,SAAS,GAAG,IAAI,QAAQ,MAAM,GAAG,EAAE,IAAI;CAExF,IAAI,CAAC,cAAc,UAAU,GAE3B,IAAI;EACF,MAAM,QAAQ,UAAU;EACxB,OAAO,CAAC;GAAE,MAAM;GAAY,MAAM;EAAY,CAAC;CACjD,QAAQ;EAEN,OAAO,CAAC;GAAE,MAAM;GAAY,MAAM;EAAO,CAAC;CAC5C;CAIF,MAAM,WAAW,gBAAgB,UAAU;CAC3C,MAAM,UAAU,kBAAkB,YAAY,EAAE,KAAK,SAAS,OAAO,MAAM,CAAC;CAE5E,QAAO,MADkB,QAAQ,SAAS,UAAU,GAAG,QAAQ,EAAA,CAC7C,QAAO,UAAS,QAAQ,MAAM,IAAI,CAAC;AACvD;;;;;;;;;;;;;AClNA,MAAa,eAAe;;CAE1B,wBAAwB;;CAExB,uBAAuB;;CAEvB,iBAAiB;;CAEjB,wBAAwB;;CAExB,0BAA0B;AAC5B;;;;;;;;;;;AAoDA,SAAS,kBAAkB,MAAyB;CAClD,MAAM,SAAmB,CAAC;CAC1B,MAAM,YAAY;CAGlB,IAAI,OAAO,SAAS,UAAU;EAC5B,OAAO,KAAK,GAAG,UAAU,8BAA8B,OAAO,MAAM;EACpE,OAAO;CACT;CAGA,IAAI,KAAK,WAAW,GAAG;EACrB,OAAO,KAAK,GAAG,UAAU,6BAA6B;EACtD,OAAO;CACT;CAGA,IAAI,KAAK,SAAS,aAAa,iBAC7B,OAAO,KAAK,GAAG,UAAU,uBAAuB,aAAa,gBAAgB,oBAAoB;CAInG,IAAI,CAAC,eAAe,KAAK,IAAI,GAC3B,OAAO,KAAK,GAAG,UAAU,uEAAuE;CAIlG,IAAI,KAAK,WAAW,GAAG,KAAK,KAAK,SAAS,GAAG,GAC3C,OAAO,KAAK,GAAG,UAAU,iDAAiD;CAI5E,IAAI,KAAK,SAAS,IAAI,GACpB,OAAO,KAAK,GAAG,UAAU,kDAAkD;CAG7E,OAAO;AACT;;;;;;;;;AAUA,SAAS,yBAAyB,aAAgC;CAChE,MAAM,SAAmB,CAAC;CAC1B,MAAM,YAAY;CAGlB,IAAI,OAAO,gBAAgB,UAAU;EACnC,OAAO,KAAK,GAAG,UAAU,8BAA8B,OAAO,aAAa;EAC3E,OAAO;CACT;CAGA,IAAI,YAAY,WAAW,GAAG;EAC5B,OAAO,KAAK,GAAG,UAAU,oCAAoC;EAC7D,OAAO;CACT;CAGA,IAAI,YAAY,SAAS,aAAa,wBACpC,OAAO,KAAK,GAAG,UAAU,8BAA8B,aAAa,uBAAuB,oBAAoB;CAIjH,IAAI,YAAY,KAAK,CAAC,CAAC,WAAW,GAChC,OAAO,KAAK,GAAG,UAAU,8CAA8C;CAGzE,OAAO;AACT;;;;;;;AAQA,SAAS,qBAAqB,SAA4B;CACxD,MAAM,SAAmB,CAAC;CAC1B,MAAM,YAAY;CAGlB,IAAI,YAAY,KAAA,KAAa,YAAY,MACvC,OAAO;CAIT,IAAI,OAAO,YAAY,UACrB,OAAO,KAAK,GAAG,UAAU,8BAA8B,OAAO,SAAS;CAGzE,OAAO;AACT;;;;;;;;AASA,SAAS,2BAA2B,gBAAmC;CAGrE,OAAO,CAAC;AACV;;;;;;;;AASA,SAAS,2BAA2B,UAA6B;CAC/D,MAAM,SAAmB,CAAC;CAC1B,MAAM,YAAY;CAGlB,IAAI,aAAa,KAAA,KAAa,aAAa,MACzC,OAAO;CAIT,IAAI,OAAO,aAAa,YAAY,MAAM,QAAQ,QAAQ,GAAG;EAC3D,OAAO,KAAK,GAAG,UAAU,8BAA8B,MAAM,QAAQ,QAAQ,IAAI,UAAU,OAAO,UAAU;EAC5G,OAAO;CACT;CAGA,OAAO;AACT;AAEA,SAAS,sBAAsB,eAAkC;CAC/D,IAAI,kBAAkB,KAAA,KAAa,OAAO,kBAAkB,WAAW,OAAO,CAAC;CAC/E,OAAO,CAAC,8CAA8C,OAAO,eAAe;AAC9E;;;;;AAUA,SAAS,eAAe,MAAsB;CAC5C,MAAM,QAAQ,KAAK,MAAM,KAAK,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC;CAChD,OAAO,KAAK,KAAK,QAAQ,GAAG;AAC9B;;;;AAKA,SAAS,WAAW,MAAsB;CACxC,OAAO,KAAK,MAAM,IAAI,CAAC,CAAC;AAC1B;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,SAAgB,sBACd,UACA,SACA,cACuB;CACvB,MAAM,SAAmB,CAAC;CAC1B,MAAM,WAAqB,CAAC;CAG5B,IAAI,OAAO,aAAa,YAAY,aAAa,QAAQ,MAAM,QAAQ,QAAQ,GAAG;EAChF,OAAO,KACL,6BAA6B,aAAa,OAAO,SAAS,MAAM,QAAQ,QAAQ,IAAI,UAAU,OAAO,UACvG;EACA,OAAO;GAAE,OAAO;GAAO;GAAQ;EAAS;CAC1C;CAEA,MAAM,OAAO;CAGb,OAAO,KAAK,GAAG,kBAAkB,KAAK,IAAI,CAAC;CAC3C,OAAO,KAAK,GAAG,yBAAyB,KAAK,WAAW,CAAC;CACzD,OAAO,KAAK,GAAG,qBAAqB,KAAK,OAAO,CAAC;CACjD,OAAO,KAAK,GAAG,2BAA2B,KAAK,aAAa,CAAC;CAC7D,OAAO,KAAK,GAAG,sBAAsB,KAAK,iBAAiB,CAAC;CAC5D,OAAO,KAAK,GAAG,2BAA2B,KAAK,QAAQ,CAAC;CAGxD,IAAI,WAAW,OAAO,KAAK,SAAS,YAAY,KAAK,SAAS,SAC5D,OAAO,KAAK,eAAe,KAAK,KAAK,+BAA+B,QAAQ,EAAE;CAIhF,IAAI,cAAc;EAChB,MAAM,YAAY,WAAW,YAAY;EACzC,MAAM,gBAAgB,eAAe,YAAY;EAEjD,IAAI,YAAY,aAAa,uBAC3B,SAAS,KACP,qBAAqB,UAAU,wBAAwB,aAAa,sBAAsB,2CAC5F;EAGF,IAAI,gBAAgB,aAAa,wBAC/B,SAAS,KACP,sBAAsB,cAAc,mCAAmC,aAAa,uBAAuB,2CAC7G;CAEJ;CAEA,OAAO;EACL,OAAO,OAAO,WAAW;EACzB;EACA;CACF;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACxRA,IAAa,mBAAb,MAAqD;CACnD;CAEA,YAAY,UAAmC,CAAC,GAAG;EACjD,KAAKC,YAAY,QAAQ,YAAY,QAAQ,IAAI;CACnD;;;;;CAMA,aAAa,WAA2B;EACtC,IAAI,KAAK,WAAW,SAAS,GAC3B,OAAO;EAET,OAAO,KAAK,QAAQ,KAAKA,WAAW,SAAS;CAC/C;CAEA,MAAM,OAAO,WAAqC;EAChD,OAAO,SAAS,KAAKC,aAAa,SAAS,CAAC;CAC9C;CAEA,MAAM,KAAK,WAA6C;EACtD,OAAO,OAAO,KAAKA,aAAa,SAAS,GAAG,SAAS;CACvD;CAEA,MAAM,SAAS,WAA6C;EAC1D,MAAM,WAAW,KAAKA,aAAa,SAAS;EAC5C,MAAM,UAAU,MAAMC,YAAG,SAAS,QAAQ;EAE1C,IAAI,WAAW,SAAS,GACtB,OAAO,QAAQ,SAAS,OAAO;EAEjC,OAAO;CACT;CAEA,MAAM,QAAQ,WAAgD;EAC5D,MAAM,WAAW,KAAKD,aAAa,SAAS;EAC5C,MAAM,UAAU,MAAMC,YAAG,QAAQ,UAAU,EAAE,eAAe,KAAK,CAAC;EAIlE,OAAO,QAAQ,IACb,QAAQ,IAAI,OAAM,UAAS;GACzB,MAAM,YAAY,KAAK,KAAK,UAAU,MAAM,IAAI;GAChD,MAAM,YAAY,MAAM,eAAe;GACvC,IAAI,OAAiC,MAAM,YAAY,IAAI,cAAc;GAEzE,IAAI,WACF,IAAI;IAEF,QAAO,MADkBA,YAAG,KAAK,SAAS,EAAA,CACxB,YAAY,IAAI,cAAc;GAClD,QAAQ;IACN,OAAO;GACT;GAGF,OAAO;IACL,MAAM,MAAM;IACZ;IACA,WAAW,aAAa,KAAA;GAC1B;EACF,CAAC,CACH;CACF;CAEA,MAAM,SAAS,WAAoC;EACjD,OAAOA,YAAG,SAAS,KAAKD,aAAa,SAAS,CAAC;CACjD;AACF;;;;;;;;;;;;AC/BA,IAAa,sBAAb,MAAa,oBAA+C;CAC1D;CACA;CACA;CACA;CACA;;CAGA,0BAAwC,IAAI,IAAI;;CAGhD,eAAe;;CAGf,eAAqC;;CAGrC,qBAAqB;;CAGrB,iBAA2B,CAAC;;CAG5B,gCAAuC,IAAI,IAAI;CAC/C,oCAAyC,IAAI,IAAI;CACjD,OAAgB,wBAAwB;CACxC,OAAgB,2BAA2B;CAE3C,YAAY,QAAmC;EAC7C,KAAKE,UAAU,OAAO;EACtB,KAAKC,kBAAkB,OAAO;EAC9B,KAAKC,gBAAgB,OAAO;EAC5B,KAAKC,kBAAkB,OAAO,kBAAkB;EAChD,KAAKC,uBAAuB,OAAO,uBAAuB;CAC5D;CAMA,MAAM,OAAiC;EACrC,MAAM,KAAKC,mBAAmB;EAE9B,MAAM,UAA2B,CAAC;EAClC,KAAK,MAAM,cAAc,KAAKC,QAAQ,OAAO,GAAG;GAC9C,MAAM,sBAAsB,MAAM,KAAKC,2BAA2B,UAAU;GAC5E,KAAK,MAAM,SAAS,qBAClB,QAAQ,KAAK;IACX,MAAM,MAAM;IACZ,MAAM,MAAM;IACZ,aAAa,MAAM;IACnB,SAAS,MAAM;IACf,eAAe,MAAM;IACrB,kBAAkB,MAAM;IACxB,UAAU,MAAM;GAClB,CAAC;EAEL;EACA,OAAO;CACT;CAEA,MAAM,IAAI,MAAqC;EAC7C,MAAM,KAAKF,mBAAmB;EAE9B,MAAM,QAAS,MAAM,KAAKG,eAAe,IAAI,KAAM,KAAKC,eAAe,IAAI;EAC3E,IAAI,CAAC,OAAO,OAAO;EAGnB,MAAM,EAAE,kBAAkB,GAAG,GAAG,cAAc;EAC9C,OAAO;CACT;CAEA,MAAM,IAAI,MAAgC;EACxC,MAAM,KAAKJ,mBAAmB;EAC9B,QAAS,MAAM,KAAKG,eAAe,IAAI,KAAM,KAAKC,eAAe,IAAI,OAAO;CAC9E;;;;;CAUA,MAAMD,eAAe,MAA6C;EAChE,MAAM,aAAa,KAAKF,QAAQ,IAAI,IAAI;EACxC,IAAI,CAAC,cAAc,WAAW,WAAW,GAAG,OAAO;EACnD,OAAO,KAAKI,UAAU,UAAU;CAClC;;;;;;;CAQA,eAAe,WAAyC;EACtD,MAAM,aAAa,UAAU,QAAQ,gBAAgB,EAAE;EACvD,KAAK,MAAM,cAAc,KAAKJ,QAAQ,OAAO,GAAG;GAC9C,MAAM,QAAQ,WAAW,MAAK,MAAK,EAAE,SAAS,UAAU;GACxD,IAAI,OAAO,OAAO;EACpB;EACA,OAAO;CACT;CAEA,MAAMK,uBAAuB,WAAoC;EAC/D,IAAI,CAAC,KAAKX,QAAQ,UAAU,OAAO;EAEnC,IAAI;GACF,OAAO,MAAM,KAAKA,QAAQ,SAAS,SAAS;EAC9C,QAAQ;GACN,OAAO;EACT;CACF;CAEA,MAAMO,2BAA2B,YAAuD;EACtF,MAAM,kCAAkB,IAAI,IAA6B;EACzD,KAAK,MAAM,aAAa,YAAY;GAClC,MAAM,gBAAgB,MAAM,KAAKI,uBAAuB,UAAU,IAAI;GACtE,MAAM,QAAQ,gBAAgB,IAAI,aAAa,KAAK,CAAC;GACrD,MAAM,KAAK,SAAS;GACpB,gBAAgB,IAAI,eAAe,KAAK;EAC1C;EAEA,MAAM,kBAA0C;GAAE,OAAO;GAAG,SAAS;GAAG,UAAU;EAAE;EACpF,OAAO,CAAC,GAAG,gBAAgB,OAAO,CAAC,CAAC,CAAC,KACnC,UACE,CAAC,GAAG,KAAK,CAAC,CAAC,MAAM,GAAG,MAAM;GACxB,MAAM,OAAO,gBAAgB,EAAE,OAAO,SAAS;GAC/C,MAAM,OAAO,gBAAgB,EAAE,OAAO,SAAS;GAC/C,IAAI,SAAS,MAAM,OAAO,OAAO;GACjC,OAAO,EAAE,KAAK,cAAc,EAAE,IAAI;EACpC,CAAC,CAAC,CAAC,EACP;CACF;;;;;;;;;;CAWA,MAAMD,UAAU,YAA4D;EAC1E,IAAI,WAAW,WAAW,GAAG,OAAO;EACpC,IAAI,WAAW,WAAW,GAAG,OAAO,WAAW;EAE/C,MAAM,UAAU,MAAM,KAAKH,2BAA2B,UAAU;EAEhE,IAAI,QAAQ,WAAW,GAAG,OAAO,QAAQ;EAEzC,MAAM,kBAA0C;GAAE,OAAO;GAAG,SAAS;GAAG,UAAU;EAAE;EACpF,MAAM,SAAS,CAAC,GAAG,OAAO,CAAC,CAAC,MAAM,GAAG,MAAM;GACzC,MAAM,OAAO,gBAAgB,EAAE,OAAO,SAAS;GAC/C,MAAM,OAAO,gBAAgB,EAAE,OAAO,SAAS;GAC/C,IAAI,SAAS,MAAM,OAAO,OAAO;GACjC,OAAO,EAAE,KAAK,cAAc,EAAE,IAAI;EACpC,CAAC;EAED,MAAM,SAAS,OAAO;EACtB,MAAM,WAAW,OAAO;EAGxB,IAAI,OAAO,OAAO,SAAS,SAAS,OAAO,MAAM;GAC/C,MAAM,QAAQ,OACX,QAAO,MAAK,EAAE,OAAO,SAAS,OAAO,OAAO,IAAI,CAAC,CACjD,KAAI,MAAK,IAAI,EAAE,KAAK,EAAE,CAAC,CACvB,KAAK,IAAI;GACZ,MAAM,IAAI,MACR,2CAA2C,OAAO,KAAK,cAAc,OAAO,OAAO,KAAK,mBAAmB,MAAM,oDAEnH;EACF;EAEA,QAAQ,KACN,4CAA4C,OAAO,KAAK,kBAC5C,OAAO,KAAK,aAAa,OAAO,OAAO,KAAK,uBACjC,OAClB,MAAM,CAAC,CAAC,CACR,KAAI,MAAK,IAAI,EAAE,KAAK,KAAK,EAAE,OAAO,KAAK,EAAE,CAAC,CAC1C,KAAK,IAAI,GAChB;EAEA,OAAO;CACT;CAEA,MAAM,UAAyB;EAE7B,KAAK,MAAM,cAAc,KAAKD,QAAQ,OAAO,GAC3C,KAAK,MAAM,SAAS,YAClB,MAAM,KAAKM,sBAAsB,KAAK;EAG1C,KAAKN,QAAQ,MAAM;EACnB,KAAKO,eAAe;EACpB,KAAKC,eAAe;EACpB,MAAM,KAAKC,gBAAgB;EAC3B,KAAKF,eAAe;CACtB;CAEA,MAAM,aAAa,SAAwC;EAEzD,MAAM,KAAKR,mBAAmB;EAG9B,MAAM,eAAe,MAAM,KAAKW,cAAc,OAAO;EAIrD,IAAI,CADkB,KAAKC,eAAe,KAAKC,gBAAgB,YAAY,GACzD;GAEhB,KAAKA,iBAAiB;GACtB,MAAM,KAAK,QAAQ;GACnB;EACF;EAIA,IAAI,MADkB,KAAKC,mBAAmB,GAE5C,MAAM,KAAK,QAAQ;CAEvB;CAEA,MAAM,SAAS,WAAkC;EAC/C,MAAM,KAAKd,mBAAmB;EAG9B,IAAI;EACJ,IAAI;EACJ,IAAI,gBAAgB,SAAS,GAAG;GAC9B,gBAAgB;GAChB,UAAU,kBAAkB,KAAKe,eAAe,SAAS,CAAC,CAAC,CAAC,IAAI,KAAK;EACvE,OAAO;GACL,gBAAgB,KAAKC,UAAU,WAAW,UAAU;GACpD,UAAU,kBAAkB,SAAS,CAAC,CAAC,IAAI,KAAK;EAClD;EAGA,MAAM,SAAS,KAAKC,aAAa,SAAS;EAG1C,MAAM,QAAQ,MAAM,KAAKC,gBAAgB,eAAe,SAAS,MAAM;EAGvE,MAAM,aAAa,KAAKjB,QAAQ,IAAI,MAAM,IAAI,KAAK,CAAC;EACpD,MAAM,cAAc,WAAW,WAAU,MAAK,EAAE,SAAS,MAAM,IAAI;EACnE,IAAI,eAAe,GAAG;GACpB,MAAM,KAAKM,sBAAsB,WAAW,YAAa;GACzD,WAAW,eAAe;EAC5B,OACE,WAAW,KAAK,KAAK;EAEvB,KAAKN,QAAQ,IAAI,MAAM,MAAM,UAAU;EACvC,MAAM,KAAKkB,YAAY,KAAK;EAG5B,KAAKC,qBAAqB,KAAK,IAAI;CACrC;CAEA,MAAM,YAAY,WAAkC;EAClD,MAAM,KAAKpB,mBAAmB;EAG9B,MAAM,QAAS,MAAM,KAAKG,eAAe,SAAS,KAAM,KAAKC,eAAe,SAAS;EACrF,IAAI,CAAC,OAAO;EAGZ,MAAM,KAAKG,sBAAsB,KAAK;EAGtC,MAAM,aAAa,KAAKN,QAAQ,IAAI,MAAM,IAAI;EAC9C,IAAI,YAAY;GACd,MAAM,MAAM,WAAW,WAAU,MAAK,EAAE,SAAS,MAAM,IAAI;GAC3D,IAAI,OAAO,GAAG,WAAW,OAAO,KAAK,CAAC;GACtC,IAAI,WAAW,WAAW,GACxB,KAAKA,QAAQ,OAAO,MAAM,IAAI;EAElC;EAGA,KAAKmB,qBAAqB,KAAK,IAAI;CACrC;;;;CAKA,MAAMT,cAAc,SAA4C;EAC9D,IAAI,MAAM,QAAQ,KAAKf,eAAe,GACpC,OAAO,KAAKA;EAEd,OAAO,KAAKA,gBAAgB,WAAW,CAAC,CAAC;CAC3C;;;;CAKA,eAAe,GAAa,GAAsB;EAChD,IAAI,EAAE,WAAW,EAAE,QAAQ,OAAO;EAClC,MAAM,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK;EAC5B,MAAM,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK;EAC5B,OAAO,QAAQ,OAAO,MAAM,MAAM,SAAS,QAAQ,EAAE;CACvD;CAMA,MAAM,OAAO,OAAe,UAA8B,CAAC,GAAiC;EAC1F,MAAM,KAAKI,mBAAmB;EAE9B,IAAI,CAAC,KAAKH,eAER,OAAO,KAAKwB,cAAc,OAAO,OAAO;EAG1C,MAAM,EAAE,OAAO,GAAG,UAAU,YAAY,oBAAoB,MAAM,SAAS;EAI3E,MAAM,wBAAwB,CAAC,GAAG,KAAKpB,QAAQ,OAAO,CAAC,CAAC,CAAC,QACtD,OAAO,eACN,QAAQ,WAAW,QAAQ,YAAY,UAAU,aAAa,IAAI,MAAM,WAAW,QAAQ,CAAC,GAC9F,CACF;EACA,MAAM,eAAe,KAAK,IAAI,aAAa,OAAO,IAAI,MAAM,qBAAqB;EAGjF,MAAM,gBAAgB,MAAM,KAAKJ,cAAc,OAAO,OAAO;GAC3D,MAAM;GACN;GACA;EACF,CAAC;EAED,MAAM,UAA+B,CAAC;EACtC,MAAM,uCAAuB,IAAI,IAAY;EAE7C,KAAK,MAAM,UAAU,eAAe;GAClC,MAAM,YAAY,OAAO,UAAU;GACnC,MAAM,SAAS,OAAO,UAAU;GAEhC,IAAI,CAAC,aAAa,CAAC,QAAQ;GAG3B,MAAM,eAAe,KAAKO,eAAe,SAAS;GAClD,IAAI,CAAC,cAAc;GAEnB,MAAM,QAAS,MAAM,KAAKD,eAAe,aAAa,IAAI,KAAM;GAGhE,IAAI,cAAc,CAAC,WAAW,SAAS,MAAM,IAAI,GAC/C;GAIF,IAAI,CAAC,qBAAqB,WAAW,YACnC;GAGF,MAAM,qBAAqB,GAAG,MAAM,KAAK,GAAG;GAC5C,IAAI,qBAAqB,IAAI,kBAAkB,GAC7C;GAEF,qBAAqB,IAAI,kBAAkB;GAE3C,QAAQ,KAAK;IACX,WAAW,MAAM;IACjB,WAAW,MAAM;IACjB;IACA,SAAS,OAAO;IAChB,OAAO,OAAO;IACd,WAAW,OAAO;IAClB,cAAc,OAAO;GACvB,CAAC;GAED,IAAI,QAAQ,UAAU,MAAM;EAC9B;EAEA,OAAO;CACT;CAMA,MAAM,aAAa,WAAmB,eAA+C;EACnF,MAAM,KAAKH,mBAAmB;EAE9B,MAAM,QAAS,MAAM,KAAKG,eAAe,SAAS,KAAM,KAAKC,eAAe,SAAS;EACrF,IAAI,CAAC,OAAO,OAAO;EAEnB,MAAM,cAAc,KAAKkB,oBAAoB,eAAe,WAAW;EACvE,MAAM,cAAc,KAAKN,UAAU,MAAM,MAAM,WAAW;EAE1D,IAAI,CAAE,MAAM,KAAKrB,QAAQ,OAAO,WAAW,GACzC,OAAO;EAGT,IAAI;GACF,MAAM,UAAU,MAAM,KAAKA,QAAQ,SAAS,WAAW;GACvD,OAAO,OAAO,YAAY,WAAW,UAAU,QAAQ,SAAS,OAAO;EACzE,QAAQ;GACN,OAAO;EACT;CACF;CAEA,MAAM,UAAU,WAAmB,YAA4C;EAC7E,MAAM,KAAKK,mBAAmB;EAE9B,MAAM,QAAS,MAAM,KAAKG,eAAe,SAAS,KAAM,KAAKC,eAAe,SAAS;EACrF,IAAI,CAAC,OAAO,OAAO;EAEnB,MAAM,iBAAiB,KAAKkB,oBAAoB,YAAY,QAAQ;EACpE,MAAM,iBAAiB,KAAKN,UAAU,MAAM,MAAM,cAAc;EAEhE,IAAI,CAAE,MAAM,KAAKrB,QAAQ,OAAO,cAAc,GAC5C,OAAO;EAGT,IAAI;GACF,MAAM,UAAU,MAAM,KAAKA,QAAQ,SAAS,cAAc;GAC1D,OAAO,OAAO,YAAY,WAAW,UAAU,QAAQ,SAAS,OAAO;EACzE,QAAQ;GACN,OAAO;EACT;CACF;CAEA,MAAM,SAAS,WAAmB,WAA2C;EAC3E,MAAM,KAAKK,mBAAmB;EAE9B,MAAM,QAAS,MAAM,KAAKG,eAAe,SAAS,KAAM,KAAKC,eAAe,SAAS;EACrF,IAAI,CAAC,OAAO,OAAO;EAEnB,MAAM,gBAAgB,KAAKkB,oBAAoB,WAAW,OAAO;EACjE,MAAM,gBAAgB,KAAKN,UAAU,MAAM,MAAM,aAAa;EAE9D,IAAI,CAAE,MAAM,KAAKrB,QAAQ,OAAO,aAAa,GAC3C,OAAO;EAGT,IAAI;GACF,MAAM,UAAU,MAAM,KAAKA,QAAQ,SAAS,aAAa;GACzD,OAAO,OAAO,YAAY,WAAW,OAAO,KAAK,SAAS,OAAO,IAAI;EACvE,QAAQ;GACN,OAAO;EACT;CACF;CAMA,MAAM,eAAe,WAAsC;EACzD,MAAM,KAAKK,mBAAmB;EAE9B,QADe,MAAM,KAAKG,eAAe,SAAS,KAAM,KAAKC,eAAe,SAAS,EAAA,EACvE,cAAc,CAAC;CAC/B;CAEA,MAAM,YAAY,WAAsC;EACtD,MAAM,KAAKJ,mBAAmB;EAE9B,QADe,MAAM,KAAKG,eAAe,SAAS,KAAM,KAAKC,eAAe,SAAS,EAAA,EACvE,WAAW,CAAC;CAC5B;CAEA,MAAM,WAAW,WAAsC;EACrD,MAAM,KAAKJ,mBAAmB;EAE9B,QADe,MAAM,KAAKG,eAAe,SAAS,KAAM,KAAKC,eAAe,SAAS,EAAA,EACvE,UAAU,CAAC;CAC3B;;;;;CAUA,MAAMJ,qBAAoC;EACxC,IAAI,KAAKQ,cACP;EAIF,IAAI,KAAKC,cAAc;GACrB,MAAM,KAAKA;GACX;EACF;EAGA,KAAKA,gBAAgB,YAAY;GAC/B,IAAI;IAEF,IAAI,KAAKI,eAAe,WAAW,GACjC,KAAKA,iBAAiB,MAAM,KAAKF,cAAc;IAEjD,MAAM,KAAKD,gBAAgB;IAC3B,KAAKF,eAAe;GACtB,UAAU;IACR,KAAKC,eAAe;GACtB;EACF,EAAA,CAAG;EAEH,MAAM,KAAKA;CACb;;;;;CAMA,gBAAgB,OAA4B;EAC1C,MAAM,aAAa,KAAKR,QAAQ,IAAI,MAAM,IAAI,KAAK,CAAC;EACpD,MAAM,MAAM,WAAW,WAAU,MAAK,EAAE,SAAS,MAAM,IAAI;EAC3D,IAAI,OAAO,GACT,WAAW,OAAO;OAElB,WAAW,KAAK,KAAK;EAEvB,KAAKA,QAAQ,IAAI,MAAM,MAAM,UAAU;CACzC;;;;;;;;;;;;CAaA,MAAMS,kBAAiC;EAErC,KAAKa,cAAc,MAAM;EACzB,KAAKC,kBAAkB,MAAM;EAG7B,MAAM,UAAU,OAAO,QAAyC;GAE9D,QAAO,MADe,KAAK7B,QAAQ,QAAQ,GAAG,EAAA,CAC/B,KAAI,OAAM;IAAE,MAAM,EAAE;IAAM,MAAM,EAAE;IAAM,WAAW,EAAE;GAAU,EAAE;EAClF;EAEA,KAAK,MAAM,iBAAiB,KAAKkB,gBAAgB;GAE/C,MAAM,aACJ,cAAc,SAAS,KAAK,cAAc,SAAS,GAAG,IAAI,cAAc,MAAM,GAAG,EAAE,IAAI;GACzF,MAAM,SAAS,KAAKY,iBAAiB,UAAU;GAE/C,IAAI,cAAc,UAAU,GAAG;IAE7B,MAAM,WAAW,MAAM,mBAAmB,YAAY,SAAS;KAAE,KAAK;KAAM,UAAU;IAAE,CAAC;IAIzF,MAAM,uBAAO,IAAI,IAAY;IAC7B,KAAK,MAAM,SAAS,UAClB,IAAI,MAAM,SAAS,aACjB,KAAK,IAAI,MAAM,IAAI;SAEnB,KAAK,IAAI,KAAKV,eAAe,MAAM,IAAI,CAAC;IAG5C,KAAKQ,cAAc,IAAI,YAAY,CAAC,GAAG,IAAI,CAAC;IAC5C,KAAKC,kBAAkB,IAAI,YAAY,KAAK,IAAI,CAAC;IAGjD,MAAM,UAAU,MAAM,QAAQ,WAC5B,SAAS,IAAI,OAAM,UAAS;KAC1B,IAAI,MAAM,SAAS,QAEjB,MAAM,KAAKE,qBAAqB,MAAM,MAAM,MAAM;UAIlD,IAAI,CAAC,MADkB,KAAKA,qBAAqB,MAAM,MAAM,MAAM,GAEjE,MAAM,KAAKC,sBAAsB,MAAM,MAAM,MAAM;IAGzD,CAAC,CACH;IAEA,KAAK,MAAM,CAAC,OAAO,WAAW,QAAQ,QAAQ,GAAG;KAC/C,MAAM,QAAQ,SAAS;KACvB,IAAI,SAAS,OAAO,WAAW,YAAY;MACzC,MAAM,QAAQ,OAAO;MACrB,IAAI,iBAAiB,OACnB,QAAQ,MAAM,+CAA+C,MAAM,KAAK,IAAI,MAAM,OAAO;KAE7F;IACF;GACF,OAGE,IAAI,CAAC,MADkB,KAAKD,qBAAqB,YAAY,MAAM,GAGjE,MAAM,KAAKC,sBAAsB,YAAY,MAAM;EAGzD;EAEA,KAAKP,qBAAqB,KAAK,IAAI;CACrC;;;;CAKA,MAAMO,sBAAsB,YAAoB,QAAsC;EACpF,IAAI;GACF,IAAI,CAAE,MAAM,KAAKhC,QAAQ,OAAO,UAAU,GACxC;EAEJ,SAAS,OAAO;GACd,MAAM,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GACjE,IAAI,OAAO;GAGX,IAAI,WAAW,WAAW,GAAG,KAAK,IAAI,SAAS,mBAAmB,GAAG;IACnE,MAAM,eAAe,WAAW,MAAM,CAAC;IACvC,IAAI;KACF,IAAI,MAAM,KAAKA,QAAQ,OAAO,YAAY,GACxC,OAAO,4CAA4C,aAAa;IAEpE,QAAQ,CAER;GACF;GAEA,QAAQ,KAAK,gDAAgD,WAAW,KAAK,MAAM,MAAM;GACzF;EACF;EAEA,IAAI;GACF,MAAM,UAAU,MAAM,KAAKA,QAAQ,QAAQ,UAAU;GAGrD,MAAM,UAAU,MAAM,QAAQ,WAC5B,QACG,QAAO,UAAS,MAAM,SAAS,WAAW,CAAC,CAC3C,IAAI,OAAM,UAAS;IAClB,MAAM,YAAY,KAAKqB,UAAU,YAAY,MAAM,IAAI;IACvD,MAAM,gBAAgB,KAAKA,UAAU,WAAW,UAAU;IAE1D,IAAI,MAAM,KAAKrB,QAAQ,OAAO,aAAa,GAEzC,OAAO,MADa,KAAKuB,gBAAgB,eAAe,MAAM,MAAM,MAAM;IAG5E,OAAO;GACT,CAAC,CACL;GAGA,KAAK,MAAM,UAAU,SACnB,IAAI,OAAO,WAAW,eAAe,OAAO,OAAO;IACjD,KAAKU,gBAAgB,OAAO,KAAK;IACjC,MAAM,KAAKT,YAAY,OAAO,KAAK;GACrC,OAAO,IAAI,OAAO,WAAW,YAAY;IACvC,MAAM,QAAQ,OAAO;IACrB,IAAI,iBAAiB,OACnB,QAAQ,MAAM,+CAA+C,WAAW,IAAI,MAAM,OAAO;GAE7F;EAEJ,SAAS,OAAO;GACd,IAAI,iBAAiB,OACnB,QAAQ,MAAM,qDAAqD,WAAW,IAAI,MAAM,OAAO;EAEnG;CACF;;;;;;;;;;;CAYA,MAAMO,qBAAqB,YAAoB,QAAyC;EACtF,IAAI;GAEF,IAAI,gBAAgB,UAAU,GAAG;IAC/B,IAAI,CAAE,MAAM,KAAK/B,QAAQ,OAAO,UAAU,GACxC,OAAO;IAGT,MAAM,WAAW,KAAKoB,eAAe,UAAU;IAC/C,MAAM,UAAU,kBAAkB,QAAQ,CAAC,CAAC,IAAI,KAAK;IAErD,IAAI;KACF,MAAM,QAAQ,MAAM,KAAKG,gBAAgB,YAAY,SAAS,MAAM;KACpE,KAAKU,gBAAgB,KAAK;KAC1B,MAAM,KAAKT,YAAY,KAAK;IAC9B,SAAS,OAAO;KACd,IAAI,iBAAiB,OACnB,QAAQ,MAAM,+CAA+C,WAAW,IAAI,MAAM,OAAO;IAE7F;IACA,OAAO;GACT;GAGA,IAAI,MAAM,KAAKxB,QAAQ,OAAO,UAAU,GAAG;IACzC,MAAM,gBAAgB,KAAKqB,UAAU,YAAY,UAAU;IAC3D,IAAI,MAAM,KAAKrB,QAAQ,OAAO,aAAa,GAAG;KAC5C,MAAM,UAAU,kBAAkB,UAAU,CAAC,CAAC,IAAI,KAAK;KAEvD,IAAI;MACF,MAAM,QAAQ,MAAM,KAAKuB,gBAAgB,eAAe,SAAS,MAAM;MACvE,KAAKU,gBAAgB,KAAK;MAC1B,MAAM,KAAKT,YAAY,KAAK;KAC9B,SAAS,OAAO;MACd,IAAI,iBAAiB,OACnB,QAAQ,MAAM,+CAA+C,cAAc,IAAI,MAAM,OAAO;KAEhG;KACA,OAAO;IACT;GACF;GAEA,OAAO;EACT,QAAQ;GACN,OAAO;EACT;CACF;;;;;;CAOA,MAAML,qBAAuC;EAC3C,IAAI,KAAKM,uBAAuB,GAE9B,OAAO;EAQT,IAAI,KAAK,IAAI,IAAI,KAAKA,qBAAqB,oBAAoB,0BAC7D,OAAO;EAGT,KAAK,MAAM,cAAc,KAAKP,gBAAgB;GAC5C,IAAI;GAEJ,IAAI,cAAc,UAAU,GAAG;IAE7B,MAAM,MAAM,KAAK,IAAI;IAErB,IAAI,OADiB,KAAKW,kBAAkB,IAAI,UAAU,KAAK,KACtC,oBAAoB,yBAAyB,CAAC,KAAKD,cAAc,IAAI,UAAU,GAAG;KACzG,MAAM,UAAU,OAAO,QAAyC;MAE9D,QAAO,MADe,KAAK5B,QAAQ,QAAQ,GAAG,EAAA,CAC/B,KAAI,OAAM;OAAE,MAAM,EAAE;OAAM,MAAM,EAAE;OAAM,WAAW,EAAE;MAAU,EAAE;KAClF;KACA,MAAM,WAAW,MAAM,mBAAmB,YAAY,SAAS;MAAE,KAAK;MAAM,UAAU;KAAE,CAAC;KAGzF,MAAM,uBAAO,IAAI,IAAY;KAC7B,KAAK,MAAM,SAAS,UAClB,IAAI,MAAM,SAAS,aACjB,KAAK,IAAI,MAAM,IAAI;UAEnB,KAAK,IAAI,KAAKoB,eAAe,MAAM,IAAI,CAAC;KAG5C,MAAM,UAAU,CAAC,GAAG,IAAI;KACxB,KAAKQ,cAAc,IAAI,YAAY,OAAO;KAC1C,KAAKC,kBAAkB,IAAI,YAAY,GAAG;IAC5C;IACA,eAAe,KAAKD,cAAc,IAAI,UAAU,KAAK,CAAC;GACxD,OACE,eAAe,CAAC,UAAU;GAG5B,KAAK,MAAM,eAAe,cACxB,IAAI;IACF,MAAM,OAAO,MAAM,KAAK5B,QAAQ,KAAK,WAAW;IAGhD,IAFc,KAAK,WAAW,QAEtB,IAAI,KAAKyB,oBACf,OAAO;IAIT,IAAI,KAAK,SAAS,aAChB;IAKF,IAAI,KAAKrB,sBAAsB;KAC7B,MAAM,sBAAsB,KAAKiB,UAAU,aAAa,UAAU;KAClE,IAAI;MACF,MAAM,sBAAsB,MAAM,KAAKrB,QAAQ,KAAK,mBAAmB;MACvE,IACE,oBAAoB,SAAS,UAC7B,oBAAoB,WAAW,QAAQ,IAAI,KAAKyB,oBAEhD,OAAO;KAEX,QAAQ,CAER;IACF;IAIA,MAAM,cAAa,MADG,KAAKzB,QAAQ,QAAQ,WAAW,EAAA,CAC3B,QAAO,UAAS,MAAM,SAAS,WAAW;IAErE,IAAI,WAAW,SAAS,GA6BlB;UAAA,MA5BsB,QAAQ,IAChC,WAAW,IAAI,OAAM,UAAS;MAC5B,MAAM,YAAY,KAAKqB,UAAU,aAAa,MAAM,IAAI;MACxD,IAAI;OAEF,KAAI,MADoB,KAAKrB,QAAQ,KAAK,SAAS,EAAA,CACrC,WAAW,QAAQ,IAAI,KAAKyB,oBACxC,OAAO;OAKT,IAAI,KAAKrB,sBAAsB;QAC7B,MAAM,gBAAgB,KAAKiB,UAAU,WAAW,UAAU;QAC1D,IAAI;SACF,MAAM,gBAAgB,MAAM,KAAKrB,QAAQ,KAAK,aAAa;SAC3D,OACE,cAAc,SAAS,UAAU,cAAc,WAAW,QAAQ,IAAI,KAAKyB;QAE/E,QAAQ,CAER;OACF;MACF,QAAQ,CAER;MACA,OAAO;KACT,CAAC,CACH,EAAA,CACgB,MAAK,UAAS,KAAK,GACjC,OAAO;IAAA;GAGb,QAAQ;IAEN;GACF;EAEJ;EAEA,OAAO;CACT;;;;CAKA,MAAMF,gBAAgB,UAAkB,SAAiB,QAA+C;EACtG,MAAM,aAAa,MAAM,KAAKvB,QAAQ,SAAS,QAAQ;EAGvD,MAAM,UAAA,GAAA,YAAA,QAAA,CAFU,OAAO,eAAe,WAAW,aAAa,WAAW,SAAS,OAAO,CAE5D;EAC7B,MAAM,cAAc,OAAO;EAC3B,MAAM,OAAO,OAAO,QAAQ,KAAK;EAIjC,MAAM,YAAY,KAAKoB,eAAe,QAAQ;EAE9C,MAAM,WAA0B;GAC9B,MAAM,YAAY;GAClB,MAAM;GACN,aAAa,YAAY;GACzB,SAAS,YAAY;GACrB,eAAe,YAAY;GAC3B,kBAAkB,YAAY;GAC9B,UAAU,YAAY;EACxB;EAGA,IAAI,KAAKjB,iBAAiB;GACxB,MAAM,aAAa,KAAK+B,uBAAuB,UAAU,SAAS,IAAI;GACtE,IAAI,CAAC,WAAW,OACd,MAAM,IAAI,MAAM,6BAA6B,SAAS,KAAK,WAAW,OAAO,KAAK,IAAI,GAAG;EAE7F;EAGA,MAAM,CAAC,YAAY,SAAS,UAAU,MAAM,QAAQ,IAAI;GACtD,KAAKC,uBAAuB,WAAW,YAAY;GACnD,KAAKA,uBAAuB,WAAW,SAAS;GAChD,KAAKA,uBAAuB,WAAW,QAAQ;EACjD,CAAC;EAGD,MAAM,mBAAmB,MAAM,KAAKC,uBAAuB,MAAM,WAAW,UAAU;EAEtF,OAAO;GACL,GAAG;GACH,cAAc;GACd;GACA;GACA;GACA;GACA;EACF;CACF;;;;CAKA,uBACE,UACA,SACA,cAC0D;EAC1D,MAAM,SAAS,sBAAsB,UAAU,SAAS,YAAY;EAGpE,IAAI,OAAO,SAAS,SAAS,GAC3B,KAAK,MAAM,WAAW,OAAO,UAC3B,QAAQ,KAAK,qBAAqB,SAAS,KAAK,IAAI,SAAS;EAIjE,OAAO;CACT;;;;CAKA,MAAMD,uBAAuB,WAAmB,QAAgE;EAC9G,MAAM,aAAa,KAAKd,UAAU,WAAW,MAAM;EACnD,MAAM,QAAkB,CAAC;EAEzB,IAAI,CAAE,MAAM,KAAKrB,QAAQ,OAAO,UAAU,GACxC,OAAO;EAGT,IAAI;GACF,MAAM,KAAKqC,eAAe,YAAY,aAAa,iBAAyB;IAC1E,MAAM,KAAK,YAAY;GACzB,CAAC;EACH,QAAQ,CAER;EAEA,OAAO;CACT;;;;;CAMA,MAAMA,eACJ,UACA,SACA,UACA,QAAgB,GAChB,WAAmB,IACJ;EACf,IAAI,SAAS,UACX;EAGF,MAAM,UAAU,MAAM,KAAKrC,QAAQ,QAAQ,OAAO;EAElD,KAAK,MAAM,SAAS,SAAS;GAC3B,MAAM,YAAY,KAAKqB,UAAU,SAAS,MAAM,IAAI;GAEpD,IAAI,MAAM,SAAS,eAAe,CAAC,MAAM,WACvC,MAAM,KAAKgB,eAAe,UAAU,WAAW,UAAU,QAAQ,GAAG,QAAQ;QAI5E,SADqB,UAAU,UAAU,SAAS,SAAS,CACvC,CAAC;EAEzB;CACF;;;;CAKA,MAAMD,uBAAuB,cAAsB,WAAmB,YAAuC;EAC3G,MAAM,QAAQ,CAAC,YAAY;EAG3B,MAAM,cAAc,MAAM,QAAQ,IAChC,WAAW,IAAI,OAAM,YAAW;GAC9B,MAAM,WAAW,KAAKf,UAAU,WAAW,cAAc,OAAO;GAChE,IAAI;IACF,MAAM,aAAa,MAAM,KAAKrB,QAAQ,SAAS,QAAQ;IACvD,OAAO,OAAO,eAAe,WAAW,aAAa,WAAW,SAAS,OAAO;GAClF,QAAQ;IACN,OAAO;GACT;EACF,CAAC,CACH;EAEA,KAAK,MAAM,WAAW,aACpB,IAAI,YAAY,MAAM,MAAM,KAAK,OAAO;EAG1C,OAAO,MAAM,KAAK,MAAM;CAC1B;;;;CAKA,MAAMY,sBAAsB,OAAqC;EAC/D,IAAI,CAAC,KAAKV,eAAe,QAAQ;EAEjC,MAAM,MAAM,CAAC,SAAS,MAAM,KAAK,YAAY,GAAG,MAAM,WAAW,KAAI,MAAK,SAAS,MAAM,KAAK,GAAG,GAAG,CAAC;EACrG,KAAK,MAAM,MAAM,KACf,IAAI;GACF,MAAM,KAAKA,cAAc,OAAO,EAAE;EACpC,QAAQ,CAER;CAEJ;;;;CAKA,aAAa,WAAkC;EAC7C,KAAK,MAAM,MAAM,KAAKgB,gBACpB,IAAI,cAAc,MAAM,UAAU,WAAW,KAAK,GAAG,GACnD,OAAO,KAAKY,iBAAiB,EAAE;EAGnC,OAAO,KAAKA,iBAAiB,SAAS;CACxC;;;;CAKA,MAAMN,YAAY,OAAqC;EACrD,IAAI,CAAC,KAAKtB,eAAe;EAGzB,MAAM,KAAKA,cAAc,MAAM;GAC7B,IAAI,SAAS,MAAM,KAAK;GACxB,SAAS,MAAM;GACf,UAAU;IACR,WAAW,MAAM;IACjB,QAAQ;GACV;EACF,CAAC;EAGD,MAAM,QAAQ,IACZ,MAAM,WAAW,IAAI,OAAM,YAAW;GACpC,MAAM,WAAW,KAAKmB,UAAU,MAAM,MAAM,cAAc,OAAO;GACjE,IAAI;IACF,MAAM,aAAa,MAAM,KAAKrB,QAAQ,SAAS,QAAQ;IACvD,MAAM,UAAU,OAAO,eAAe,WAAW,aAAa,WAAW,SAAS,OAAO;IACzF,MAAM,KAAKE,cAAe,MAAM;KAC9B,IAAI,SAAS,MAAM,KAAK,GAAG;KAC3B;KACA,UAAU;MACR,WAAW,MAAM;MACjB,QAAQ,cAAc;KACxB;IACF,CAAC;GACH,QAAQ,CAER;EACF,CAAC,CACH;CACF;;;;CAKA,MAAMwB,cAAc,OAAe,SAA2D;EAC5F,MAAM,EAAE,OAAO,GAAG,YAAY,oBAAoB,SAAS;EAC3D,MAAM,aAAa,MAAM,YAAY;EACrC,MAAM,UAA+B,CAAC;EAEtC,KAAK,MAAM,cAAc,KAAKpB,QAAQ,OAAO,GAAG;GAE9C,MAAM,QAAQ,MAAM,KAAKI,UAAU,UAAU;GAC7C,IAAI,CAAC,OAAO;GAGZ,IAAI,cAAc,CAAC,WAAW,SAAS,MAAM,IAAI,GAC/C;GAIF,IAAI,MAAM,aAAa,YAAY,CAAC,CAAC,SAAS,UAAU,GACtD,QAAQ,KAAK;IACX,WAAW,MAAM;IACjB,WAAW,MAAM;IACjB,QAAQ;IACR,SAAS,MAAM,aAAa,UAAU,GAAG,GAAG;IAC5C,OAAO;GACT,CAAC;GAIH,IAAI,mBACF,KAAK,MAAM,WAAW,MAAM,YAAY;IACtC,IAAI,QAAQ,UAAU,MAAM;IAC5B,MAAM,UAAU,MAAM,KAAK,aAAa,MAAM,MAAM,cAAc,SAAS;IAC3E,IAAI,WAAW,QAAQ,YAAY,CAAC,CAAC,SAAS,UAAU,GACtD,QAAQ,KAAK;KACX,WAAW,MAAM;KACjB,WAAW,MAAM;KACjB,QAAQ,cAAc;KACtB,SAAS,QAAQ,UAAU,GAAG,GAAG;KACjC,OAAO;IACT,CAAC;GAEL;GAGF,IAAI,QAAQ,UAAU,MAAM;EAC9B;EAEA,OAAO,QAAQ,MAAM,GAAG,IAAI;CAC9B;;;;CAKA,iBAAiB,YAAmC;EAIlD,IADiB,kBAAkB,UACxB,CAAC,CAAC,SAAS,cAAc,GAClC,OAAO;GAAE,MAAM;GAAY,aAAa;EAAW;EAErD,MAAM,aAAa,WAAW,QAAQ,OAAO,GAAG;EAChD,IAAI,WAAW,SAAS,iBAAiB,KAAK,WAAW,WAAW,gBAAgB,GAClF,OAAO;GAAE,MAAM;GAAW,YAAY;EAAW;EAEnD,OAAO;GAAE,MAAM;GAAS,aAAa;EAAW;CAClD;;;;CAKA,UAAU,GAAG,UAA4B;EACvC,OAAO,SACJ,KAAK,KAAK,MAAO,MAAM,IAAI,qBAAqB,GAAG,IAAI,+BAA+B,GAAG,CAAE,CAAC,CAC5F,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;CACb;;;;;CAMA,oBAAoB,OAAe,OAAuB;EACxD,MAAM,aAAa,MAAM,QAAQ,OAAO,GAAG;EAC3C,MAAM,WAAW,WAAW,MAAM,GAAG,CAAC,CAAC,QAAO,QAAO,QAAQ,GAAG,KAAK,QAAQ,GAAG;EAChF,IAAI,WAAW,WAAW,GAAG,KAAK,SAAS,MAAK,QAAO,QAAQ,IAAI,GACjE,MAAM,IAAI,MAAM,WAAW,MAAM,SAAS,OAAO;EAEnD,OAAO,SAAS,KAAK,GAAG;CAC1B;;;;CAKA,eAAe,MAAsB;EACnC,MAAM,YAAY,KAAK,IAAI,KAAK,YAAY,GAAG,GAAG,KAAK,YAAY,IAAI,CAAC;EACxE,OAAO,YAAY,IAAI,KAAK,UAAU,GAAG,SAAS,IAAI;CACxD;AACF;;;;;;;AAQA,SAAS,kBAAkB,MAAwB;CACjD,OAAO,KAAK,MAAM,QAAQ;AAC5B;;;;AAKA,SAAS,gBAAgB,MAAuB;CAC9C,OAAO,SAAS,cAAc,kBAAkB,KAAK,IAAI;AAC3D;AAEA,SAAS,qBAAqB,GAAmB;CAC/C,IAAI,MAAM,EAAE;CACZ,OAAO,MAAM,KAAK,EAAE,WAAW,MAAM,CAAC,MAAM,IAC1C;CAEF,OAAO,QAAQ,EAAE,SAAS,IAAI,EAAE,MAAM,GAAG,GAAG;AAC9C;AAEA,SAAS,+BAA+B,GAAmB;CACzD,IAAI,QAAQ;CACZ,OAAO,QAAQ,EAAE,UAAU,EAAE,WAAW,KAAK,MAAM,IACjD;CAEF,IAAI,MAAM,EAAE;CACZ,OAAO,MAAM,SAAS,EAAE,WAAW,MAAM,CAAC,MAAM,IAC9C;CAEF,OAAO,UAAU,KAAK,QAAQ,EAAE,SAAS,IAAI,EAAE,MAAM,OAAO,GAAG;AACjE"}