@ez-corp/ez-context 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +6 -11
- package/dist/cli.js.map +1 -1
- package/dist/{emitters-D6bP4xWs.js → emitters-Doc-arnD.js} +4 -1
- package/dist/emitters-Doc-arnD.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/emitters-D6bP4xWs.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as writeWithMarkers, i as MARKER_START, n as emit, o as extractConventions, r as MARKER_END, s as createBridge, t as FORMAT_EMITTER_MAP } from "./emitters-
|
|
2
|
+
import { a as writeWithMarkers, i as MARKER_START, n as emit, o as extractConventions, r as MARKER_END, s as createBridge, t as FORMAT_EMITTER_MAP } from "./emitters-Doc-arnD.js";
|
|
3
3
|
import { copyFile, readFile, writeFile } from "node:fs/promises";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { existsSync } from "node:fs";
|
|
@@ -318,11 +318,9 @@ async function driftAction(pathArg, options) {
|
|
|
318
318
|
const spinner = ora("Loading context files...").start();
|
|
319
319
|
try {
|
|
320
320
|
const bridge = await createBridge(projectPath);
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
spinner.text = "Index created. Loading context files...";
|
|
325
|
-
}
|
|
321
|
+
spinner.text = "Refreshing search index...";
|
|
322
|
+
await bridge.refreshIndex(projectPath);
|
|
323
|
+
spinner.text = "Loading context files...";
|
|
326
324
|
let filePaths;
|
|
327
325
|
if (options.file) filePaths = [path.resolve(projectPath, options.file)];
|
|
328
326
|
else filePaths = CANDIDATE_FILES.map((name) => path.join(projectPath, name)).filter((p) => existsSync(p));
|
|
@@ -515,11 +513,8 @@ async function updateAction(pathArg, options) {
|
|
|
515
513
|
const spinner = ora("Checking for drift...").start();
|
|
516
514
|
try {
|
|
517
515
|
const bridge = await createBridge(projectPath);
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
console.error(chalk.red("Run 'ez-context generate' or 'ez-search index .' first to create an index."));
|
|
521
|
-
process.exit(1);
|
|
522
|
-
}
|
|
516
|
+
spinner.text = "Refreshing search index...";
|
|
517
|
+
await bridge.refreshIndex(projectPath);
|
|
523
518
|
let filePaths;
|
|
524
519
|
if (options.file) filePaths = [path.resolve(projectPath, options.file)];
|
|
525
520
|
else filePaths = Object.values(FORMAT_EMITTER_MAP).map((entry) => path.join(projectPath, entry.filename)).filter((p) => existsSync(p));
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","names":[],"sources":["../src/commands/generate.ts","../src/commands/inspect.ts","../src/core/drift/claim-extractor.ts","../src/core/drift/claim-scorer.ts","../src/core/drift/report.ts","../src/commands/drift.ts","../src/core/updater.ts","../src/commands/update.ts","../src/cli.ts"],"sourcesContent":["import path from \"node:path\";\nimport ora from \"ora\";\nimport chalk from \"chalk\";\nimport { extractConventions } from \"../core/pipeline.js\";\nimport { emit } from \"../emitters/index.js\";\nimport type { EmitOptions, OutputFormat } from \"../emitters/types.js\";\n\nconst DRY_RUN_PREVIEW_LINES = 20;\n\nconst VALID_FORMATS: OutputFormat[] = [\n \"claude\",\n \"agents\",\n \"cursor\",\n \"copilot\",\n \"skills\",\n \"rulesync\",\n \"ruler\",\n];\n\nexport function parseFormats(raw: string): OutputFormat[] {\n const formats = [...new Set(raw.split(\",\").map((s) => s.trim()).filter(Boolean))];\n const invalid = formats.filter((f) => !VALID_FORMATS.includes(f as OutputFormat));\n if (invalid.length > 0) {\n throw new Error(\n `Invalid format(s): ${invalid.join(\", \")}. Valid: ${VALID_FORMATS.join(\", \")}`\n );\n }\n return formats as OutputFormat[];\n}\n\nfunction truncatePreview(content: string): string {\n const lines = content.split(\"\\n\");\n if (lines.length <= DRY_RUN_PREVIEW_LINES) {\n return content;\n }\n const preview = lines.slice(0, DRY_RUN_PREVIEW_LINES).join(\"\\n\");\n return `${preview}\\n... (${lines.length} lines total)`;\n}\n\nexport async function generateAction(\n pathArg: string,\n options: { dryRun?: boolean; yes?: boolean; output?: string; threshold?: string; format?: string }\n): Promise<void> {\n const projectPath = path.resolve(pathArg);\n\n // --yes and non-TTY environments: ora handles non-TTY gracefully by\n // falling back to plain text. No interactive prompts are used anywhere.\n const spinner = ora(\"Analyzing project conventions...\").start();\n\n try {\n const registry = await extractConventions(projectPath);\n const conventionCount = registry.conventions.length;\n spinner.succeed(`Found ${conventionCount} convention${conventionCount === 1 ? \"\" : \"s\"}`);\n\n const confidenceThreshold = parseFloat(options.threshold ?? \"0.7\");\n const outputDir = path.resolve(options.output ?? \".\");\n\n // Parse and validate --format (default: \"claude,agents\")\n const formats = parseFormats(options.format ?? \"claude,agents\");\n\n const emitOptions: EmitOptions = {\n outputDir,\n confidenceThreshold,\n dryRun: options.dryRun ?? false,\n formats,\n };\n\n const isDefault = formats.length === 2 && formats.includes(\"claude\") && formats.includes(\"agents\");\n const genSpinnerText = isDefault\n ? \"Generating context files...\"\n : `Generating ${formats.length} context file${formats.length === 1 ? \"\" : \"s\"}...`;\n const genSpinner = ora(genSpinnerText).start();\n const result = await emit(registry, emitOptions);\n\n if (options.dryRun) {\n genSpinner.succeed(\"Dry run complete\");\n console.log();\n console.log(chalk.bold.yellow(\"╔══════════════════════════════════════╗\"));\n console.log(chalk.bold.yellow(\"║ DRY RUN -- no files will be written ║\"));\n console.log(chalk.bold.yellow(\"╚══════════════════════════════════════╝\"));\n console.log();\n for (const [format, content] of Object.entries(result.rendered)) {\n console.log(chalk.cyan(`--- ${format.toUpperCase()} ---`));\n console.log(truncatePreview(content));\n console.log();\n }\n } else {\n genSpinner.succeed(`Generated ${result.filesWritten.length} file${result.filesWritten.length === 1 ? \"\" : \"s\"}`);\n console.log();\n console.log(chalk.bold.green(\"Generated files:\"));\n for (const filePath of result.filesWritten) {\n const relPath = path.relative(outputDir, filePath);\n console.log(` ${chalk.cyan(relPath)}`);\n }\n }\n } catch (err) {\n spinner.fail(\"Analysis failed\");\n const message = err instanceof Error ? err.message : String(err);\n console.error(chalk.red(message));\n process.exit(1);\n }\n}\n","import path from \"node:path\";\nimport ora from \"ora\";\nimport chalk from \"chalk\";\nimport { extractConventions } from \"../core/pipeline.js\";\nimport type { ConventionEntry } from \"../core/schema.js\";\n\nfunction confidenceDot(confidence: number): string {\n if (confidence >= 0.8) return chalk.green(\"●\");\n if (confidence >= 0.6) return chalk.yellow(\"●\");\n return chalk.red(\"●\");\n}\n\nexport async function inspectAction(\n pathArg: string,\n options: { threshold?: string }\n): Promise<void> {\n const projectPath = path.resolve(pathArg);\n const spinner = ora(\"Analyzing project conventions...\").start();\n\n try {\n const registry = await extractConventions(projectPath);\n const totalCount = registry.conventions.length;\n spinner.succeed(`Extracted ${totalCount} convention${totalCount === 1 ? \"\" : \"s\"}`);\n\n const threshold = parseFloat(options.threshold ?? \"0.7\");\n\n const filtered = registry.conventions.filter(\n (c) => c.confidence >= threshold\n );\n\n if (filtered.length === 0) {\n console.log(\n chalk.yellow(\n `\\nNo conventions found above ${threshold} confidence threshold. Try lowering --threshold.`\n )\n );\n return;\n }\n\n // Group by category\n const byCategory = new Map<string, ConventionEntry[]>();\n for (const convention of filtered) {\n const group = byCategory.get(convention.category) ?? [];\n group.push(convention);\n byCategory.set(convention.category, group);\n }\n\n console.log();\n for (const [category, conventions] of byCategory) {\n console.log(chalk.bold(category.toUpperCase()));\n for (const convention of conventions) {\n const pct = Math.round(convention.confidence * 100);\n console.log(\n ` ${confidenceDot(convention.confidence)} ${convention.pattern} ${chalk.gray(`(${pct}%)`)}`\n );\n }\n console.log();\n }\n\n const categoryCount = byCategory.size;\n console.log(\n chalk.gray(\n `Found ${filtered.length} convention${filtered.length === 1 ? \"\" : \"s\"} across ${categoryCount} categor${categoryCount === 1 ? \"y\" : \"ies\"} (threshold: ${threshold})`\n )\n );\n } catch (err) {\n spinner.fail(\"Analysis failed\");\n const message = err instanceof Error ? err.message : String(err);\n console.error(chalk.red(message));\n process.exit(1);\n }\n}\n","/**\n * Claim extractor — parses markdown context files into individual testable claims.\n *\n * Input: raw markdown string (CLAUDE.md, AGENTS.md, .cursorrules, etc.)\n * Output: Claim[] where each claim is an atomic declarative statement\n *\n * Extraction rules:\n * - Bullet points: ^[-*+]\\s+\n * - Numbered list items: ^\\d+\\.\\s+\n * - Bold/code markers stripped from extracted text\n * - Boilerplate value lines skipped (Language: X, Framework: X, etc.)\n * - ez-context markers and HTML comments skipped\n * - Claims shorter than 10 chars or longer than 300 chars excluded\n * - Current section heading tracked for context\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface Claim {\n text: string; // The claim text (bold/code markers stripped)\n sourceFile: string; // Which file it came from\n sourceLine: number; // 1-based line number\n sourceSection: string; // Nearest parent heading\n}\n\n// ---------------------------------------------------------------------------\n// Filters\n// ---------------------------------------------------------------------------\n\n/**\n * Matches boilerplate key-value lines that are structural metadata, not\n * behavioral claims. Applied AFTER bold/code stripping.\n *\n * Examples skipped:\n * \"Language: TypeScript\"\n * \"Package Manager: bun\"\n */\nconst BOILERPLATE_VALUE =\n /^(Language|Framework|Build|Package Manager|Test Runner|Pattern|Layers):\\s/i;\n\n// ---------------------------------------------------------------------------\n// Core function\n// ---------------------------------------------------------------------------\n\n/**\n * Extract all testable claims from a markdown string.\n *\n * @param content Raw markdown content of the context file\n * @param sourceFile Path to the source file (stored on each claim)\n * @returns Array of extracted claims, filtered and deduplicated\n */\nexport function extractClaims(content: string, sourceFile: string): Claim[] {\n const claims: Claim[] = [];\n const lines = content.split(\"\\n\");\n let currentSection = \"\";\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!.trim();\n const lineNum = i + 1; // 1-based\n\n // Skip blank lines\n if (!line) continue;\n\n // Skip HTML comments (includes ez-context markers like <!-- ez-context:... -->)\n if (line.startsWith(\"<!--\")) continue;\n\n // Skip lines containing ez-context markers (belt-and-suspenders for inline markers)\n if (line.includes(\"ez-context:\")) continue;\n\n // Track section headings — H1, H2, H3\n const heading = line.match(/^#{1,3}\\s+(.+)/);\n if (heading) {\n currentSection = heading[1]!.trim();\n continue;\n }\n\n // Match bullet points or numbered list items\n const bullet = line.match(/^[-*+]\\s+(.+)/);\n const numbered = !bullet ? line.match(/^\\d+\\.\\s+(.+)/) : null;\n const rawText = bullet ? bullet[1]! : numbered ? numbered[1]! : null;\n\n if (!rawText) continue;\n\n // Strip bold markers (**text** -> text) and inline code markers (`text` -> text)\n const text = rawText\n .replace(/\\*\\*([^*]+)\\*\\*/g, \"$1\")\n .replace(/`([^`]+)`/g, \"$1\")\n .trim();\n\n // Apply length filters\n if (text.length < 10 || text.length > 300) continue;\n\n // Skip boilerplate key-value lines\n if (BOILERPLATE_VALUE.test(text)) continue;\n\n claims.push({\n text,\n sourceFile,\n sourceLine: lineNum,\n sourceSection: currentSection,\n });\n }\n\n return claims;\n}\n","/**\n * Claim scorer — compares extracted claims against the code index via semantic search.\n *\n * Each claim is searched against the indexed codebase. The top similarity score\n * determines whether the claim is GREEN (well-supported), YELLOW (possibly stale),\n * or RED (contradicted / not found).\n *\n * Claims are processed in batches to avoid ONNX pipeline contention.\n */\nimport type { Claim } from \"./claim-extractor.js\";\nimport type { SearchResult, EzSearchBridge } from \"../ez-search-bridge.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nexport const GREEN_THRESHOLD = 0.65;\nexport const YELLOW_THRESHOLD = 0.40;\nexport const BATCH_SIZE = 10;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type ClaimStatus = \"GREEN\" | \"YELLOW\" | \"RED\";\n\nexport interface ScoredClaim {\n claim: Claim;\n status: ClaimStatus;\n score: number; // Top bridge.search() score (0.0-1.0)\n evidence: SearchResult[]; // Top k results\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction chunk<T>(arr: T[], size: number): T[][] {\n const chunks: T[][] = [];\n for (let i = 0; i < arr.length; i += size) {\n chunks.push(arr.slice(i, i + size));\n }\n return chunks;\n}\n\nfunction classifyScore(score: number): ClaimStatus {\n if (score >= GREEN_THRESHOLD) return \"GREEN\";\n if (score >= YELLOW_THRESHOLD) return \"YELLOW\";\n return \"RED\";\n}\n\nasync function scoreSingleClaim(\n claim: Claim,\n bridge: EzSearchBridge\n): Promise<ScoredClaim> {\n const evidence = await bridge.search(claim.text, { k: 5 });\n const topScore = evidence.length > 0 ? evidence[0]!.score : 0;\n return {\n claim,\n status: classifyScore(topScore),\n score: topScore,\n evidence,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Score all claims by searching the code index in batches of BATCH_SIZE.\n *\n * @param claims Claims to score\n * @param bridge EzSearchBridge instance bound to the project\n * @param onProgress Optional callback fired after each batch: (done, total)\n * @returns ScoredClaim[] in the same order as input claims\n */\nexport async function scoreClaims(\n claims: Claim[],\n bridge: EzSearchBridge,\n onProgress?: (completed: number, total: number) => void\n): Promise<ScoredClaim[]> {\n const total = claims.length;\n const batches = chunk(claims, BATCH_SIZE);\n const results: ScoredClaim[] = [];\n let completed = 0;\n\n for (const batch of batches) {\n const batchResults = await Promise.all(\n batch.map((claim) => scoreSingleClaim(claim, bridge))\n );\n results.push(...batchResults);\n completed += batch.length;\n onProgress?.(completed, total);\n }\n\n return results;\n}\n","/**\n * Drift report — aggregates scored claims into a health score and markdown report.\n *\n * Health score: mean of per-claim scores scaled 0-100 (rounded).\n * Zero claims yields a health score of 100 (nothing to contradict = healthy).\n *\n * Rendered markdown groups claims by status with evidence for stale/contradicted claims.\n */\nimport type { ScoredClaim } from \"./claim-scorer.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface DriftReport {\n sourceFile: string;\n healthScore: number;\n scoredClaims: ScoredClaim[];\n}\n\n// ---------------------------------------------------------------------------\n// Health score\n// ---------------------------------------------------------------------------\n\n/**\n * Compute the aggregate health score for a set of scored claims.\n * Returns 100 for empty input (no claims = no drift).\n */\nexport function computeHealthScore(scoredClaims: ScoredClaim[]): number {\n if (scoredClaims.length === 0) return 100;\n const mean =\n scoredClaims.reduce((sum, sc) => sum + sc.score, 0) / scoredClaims.length;\n return Math.round(mean * 100);\n}\n\n// ---------------------------------------------------------------------------\n// Report assembly\n// ---------------------------------------------------------------------------\n\n/**\n * Build a DriftReport from a source file path and its scored claims.\n */\nexport function buildDriftReport(\n sourceFile: string,\n scoredClaims: ScoredClaim[]\n): DriftReport {\n return {\n sourceFile,\n healthScore: computeHealthScore(scoredClaims),\n scoredClaims,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Renderer\n// ---------------------------------------------------------------------------\n\nconst STATUS_LABEL: Record<string, string> = {\n GREEN: \"Confirmed\",\n YELLOW: \"Possibly Stale\",\n RED: \"Contradicted\",\n};\n\n/**\n * Render a drift report as a readable markdown string.\n *\n * Layout:\n * # Drift Report\n * Health score, source file, claim count\n *\n * ## Confirmed (GREEN)\n * - [GREEN] claim text (score: X.XX)\n *\n * ## Possibly Stale (YELLOW)\n * - [YELLOW] claim text (score: X.XX)\n * - file: chunk_preview\n *\n * ## Contradicted (RED)\n * - [RED] claim text (score: X.XX)\n * - file: chunk_preview\n *\n * Summary: X confirmed, Y possibly stale, Z contradicted\n */\nexport function renderDriftReport(report: DriftReport): string {\n const { sourceFile, healthScore, scoredClaims } = report;\n const lines: string[] = [];\n\n const green = scoredClaims.filter((sc) => sc.status === \"GREEN\");\n const yellow = scoredClaims.filter((sc) => sc.status === \"YELLOW\");\n const red = scoredClaims.filter((sc) => sc.status === \"RED\");\n\n // Header\n lines.push(\"# Drift Report\");\n lines.push(\"\");\n lines.push(`**Health Score:** ${healthScore}/100`);\n lines.push(`**File:** ${sourceFile}`);\n lines.push(`**Claims:** ${scoredClaims.length}`);\n lines.push(\"\");\n\n // Render a group of claims\n const renderGroup = (group: ScoredClaim[], status: string) => {\n if (group.length === 0) return;\n const label = STATUS_LABEL[status] ?? status;\n lines.push(`## ${label} (${status})`);\n lines.push(\"\");\n for (const sc of group) {\n lines.push(`- [${sc.status}] ${sc.claim.text} (score: ${sc.score.toFixed(2)})`);\n // Show top 2 evidence items for non-GREEN claims\n if (sc.status !== \"GREEN\") {\n const topEvidence = sc.evidence.slice(0, 2);\n for (const ev of topEvidence) {\n const preview = ev.chunk.replace(/\\s+/g, \" \").trim().slice(0, 80);\n lines.push(` - ${ev.file}: ${preview}`);\n }\n }\n }\n lines.push(\"\");\n };\n\n renderGroup(green, \"GREEN\");\n renderGroup(yellow, \"YELLOW\");\n renderGroup(red, \"RED\");\n\n // Summary\n lines.push(\n `Summary: ${green.length} confirmed, ${yellow.length} possibly stale, ${red.length} contradicted`\n );\n\n return lines.join(\"\\n\");\n}\n","import path from \"node:path\";\nimport { readFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport ora from \"ora\";\nimport chalk from \"chalk\";\nimport { createBridge } from \"../core/ez-search-bridge.js\";\nimport { extractClaims } from \"../core/drift/claim-extractor.js\";\nimport { scoreClaims } from \"../core/drift/claim-scorer.js\";\nimport { buildDriftReport, renderDriftReport, computeHealthScore } from \"../core/drift/report.js\";\n\nconst CANDIDATE_FILES = [\"CLAUDE.md\", \"AGENTS.md\", \".cursorrules\", \"CONTEXT.md\"];\n\nfunction healthColor(score: number): string {\n if (score >= 70) return chalk.green(String(score));\n if (score >= 40) return chalk.yellow(String(score));\n return chalk.red(String(score));\n}\n\nexport async function driftAction(\n pathArg: string,\n options: { file?: string }\n): Promise<void> {\n const projectPath = path.resolve(pathArg);\n const spinner = ora(\"Loading context files...\").start();\n\n try {\n const bridge = await createBridge(projectPath);\n\n // Index check — auto-create if missing\n if (!(await bridge.hasIndex(projectPath))) {\n spinner.text = \"No search index found — creating index...\";\n await bridge.ensureIndex(projectPath);\n spinner.text = \"Index created. Loading context files...\";\n }\n\n // Resolve files\n let filePaths: string[];\n if (options.file) {\n filePaths = [path.resolve(projectPath, options.file)];\n } else {\n filePaths = CANDIDATE_FILES\n .map((name) => path.join(projectPath, name))\n .filter((p) => existsSync(p));\n }\n\n if (filePaths.length === 0) {\n spinner.fail(\"No context files found\");\n console.error(\n chalk.red(\"No CLAUDE.md, AGENTS.md, .cursorrules, or CONTEXT.md found. Use --file to specify one.\")\n );\n process.exit(1);\n }\n\n // Extract claims from each file\n const claimsByFile: Map<string, ReturnType<typeof extractClaims>> = new Map();\n for (const filePath of filePaths) {\n const content = await readFile(filePath, \"utf-8\");\n const claims = extractClaims(content, filePath);\n claimsByFile.set(filePath, claims);\n }\n\n const allClaims = [...claimsByFile.values()].flat();\n spinner.text = `Analyzing ${allClaims.length} claims...`;\n\n // Score claims with progress callback\n const scoredAll = await scoreClaims(allClaims, bridge, (done, total) => {\n spinner.text = `Checking claim ${done}/${total}...`;\n });\n\n // Build and render reports per file\n const reports = filePaths.map((filePath) => {\n const fileClaims = claimsByFile.get(filePath) ?? [];\n const fileScoredClaims = scoredAll.filter((sc) =>\n fileClaims.some((c) => c === sc.claim)\n );\n return buildDriftReport(filePath, fileScoredClaims);\n });\n\n const overallScore = computeHealthScore(scoredAll);\n spinner.succeed(`Drift analysis complete — health score: ${healthColor(overallScore)}/100`);\n\n console.log();\n for (const report of reports) {\n console.log(renderDriftReport(report));\n console.log();\n }\n } catch (err) {\n spinner.fail(\"Drift analysis failed\");\n const message = err instanceof Error ? err.message : String(err);\n console.error(chalk.red(message));\n process.exit(1);\n }\n}\n","/**\n * Updater — targeted regeneration engine for `ez-context update`.\n *\n * Orchestrates:\n * 1. Marker validation (pre-flight check, markers strategy only)\n * 2. Drift detection (skip GREEN files, markers strategy only)\n * 3. File backup (before any write)\n * 4. Re-rendering (via FORMAT_EMITTER_MAP)\n * 5. Write-back (writeWithMarkers for markers strategy, writeFile for direct)\n */\nimport { copyFile, readFile, writeFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport path from \"node:path\";\n\nimport { MARKER_START, MARKER_END, writeWithMarkers } from \"../emitters/writer.js\";\nimport { FORMAT_EMITTER_MAP } from \"../emitters/index.js\";\nimport { extractClaims } from \"./drift/claim-extractor.js\";\nimport { scoreClaims } from \"./drift/claim-scorer.js\";\nimport type { EzSearchBridge } from \"./ez-search-bridge.js\";\nimport type { ConventionRegistry } from \"./schema.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface MarkerValidation {\n valid: boolean;\n mode: \"append\" | \"splice\" | \"invalid\";\n reason?: string;\n startIdx?: number;\n endIdx?: number;\n}\n\nexport type UpdateAction = \"skipped\" | \"updated\" | \"aborted\";\n\nexport interface FileUpdateResult {\n filePath: string;\n action: UpdateAction;\n reason: string;\n backupPath?: string;\n}\n\n// ---------------------------------------------------------------------------\n// validateMarkers\n// ---------------------------------------------------------------------------\n\n/**\n * Pre-flight marker check for updateFile.\n *\n * Unlike writeWithMarkers (which silently appends on unpaired markers),\n * validateMarkers rejects unpaired markers so updateFile can abort safely.\n *\n * Returns:\n * - { valid: true, mode: \"append\" } — no markers, safe to append\n * - { valid: true, mode: \"splice\", startIdx, endIdx } — well-formed pair\n * - { valid: false, mode: \"invalid\", reason } — unpaired or inverted markers\n */\nexport function validateMarkers(content: string): MarkerValidation {\n const startIdx = content.indexOf(MARKER_START);\n const endIdx = content.indexOf(MARKER_END);\n\n const hasStart = startIdx !== -1;\n const hasEnd = endIdx !== -1;\n\n // No markers at all -> safe to append\n if (!hasStart && !hasEnd) {\n return { valid: true, mode: \"append\" };\n }\n\n // Both markers present -> validate ordering\n if (hasStart && hasEnd) {\n if (endIdx < startIdx) {\n return {\n valid: false,\n mode: \"invalid\",\n reason: \"End marker appears before start marker (corrupted file)\",\n };\n }\n return { valid: true, mode: \"splice\", startIdx, endIdx };\n }\n\n // Unpaired: only one marker present\n if (hasStart && !hasEnd) {\n return {\n valid: false,\n mode: \"invalid\",\n reason: \"Unpaired ez-context marker: end marker missing\",\n };\n }\n\n // hasEnd && !hasStart\n return {\n valid: false,\n mode: \"invalid\",\n reason: \"Unpaired ez-context marker: start marker missing\",\n };\n}\n\n// ---------------------------------------------------------------------------\n// backupFile\n// ---------------------------------------------------------------------------\n\n/**\n * Copy filePath to filePath.bak and return the backup path.\n * Returns null if the file does not exist.\n * Overwrites any existing .bak silently (represents state before this run).\n */\nexport async function backupFile(filePath: string): Promise<string | null> {\n if (!existsSync(filePath)) {\n return null;\n }\n\n const backupPath = filePath + \".bak\";\n await copyFile(filePath, backupPath);\n return backupPath;\n}\n\n// ---------------------------------------------------------------------------\n// findFormatEntry\n// ---------------------------------------------------------------------------\n\n/**\n * Look up the FORMAT_EMITTER_MAP entry whose filename suffix matches filePath.\n * Returns undefined if the file doesn't correspond to a known format.\n */\nfunction findFormatEntry(filePath: string) {\n const normalized = path.normalize(filePath);\n for (const entry of Object.values(FORMAT_EMITTER_MAP)) {\n if (normalized.endsWith(path.normalize(entry.filename))) {\n return entry;\n }\n }\n return undefined;\n}\n\n// ---------------------------------------------------------------------------\n// updateFile\n// ---------------------------------------------------------------------------\n\n/**\n * Orchestrate drift detection and targeted re-rendering for a single file.\n *\n * The write strategy is determined by FORMAT_EMITTER_MAP:\n * - \"markers\" strategy: drift detection + writeWithMarkers (default)\n * - \"direct\" strategy: always regenerate + writeFile (full overwrite)\n *\n * Flow for markers strategy:\n * 1. File existence check — skip if missing\n * 2. Marker validation — abort on invalid markers\n * 3. Drift check (splice mode only) — skip if all claims GREEN\n * 4. Backup creation\n * 5. Re-render + writeWithMarkers\n *\n * Flow for direct strategy:\n * 1. File existence check — skip if missing\n * 2. Backup creation\n * 3. Re-render + writeFile (full overwrite)\n *\n * @param filePath Absolute path to the context file\n * @param registry Pre-computed convention registry (NOT extracted per-file)\n * @param bridge EzSearchBridge instance for drift scoring\n * @param confidenceThreshold Confidence floor passed to the renderer (default 0.7)\n */\nexport async function updateFile(\n filePath: string,\n registry: ConventionRegistry,\n bridge: EzSearchBridge,\n confidenceThreshold: number = 0.7\n): Promise<FileUpdateResult> {\n // 1. File existence check\n if (!existsSync(filePath)) {\n return { filePath, action: \"skipped\", reason: \"File does not exist\" };\n }\n\n const formatEntry = findFormatEntry(filePath);\n // Fall back to claude (markers) if the file isn't a known format\n const strategy = formatEntry?.strategy ?? \"markers\";\n const render = formatEntry?.render ?? FORMAT_EMITTER_MAP.claude.render;\n\n // ---------------------------------------------------------------------------\n // Direct strategy: full regeneration, no drift detection\n // ---------------------------------------------------------------------------\n if (strategy === \"direct\") {\n const backupPath = (await backupFile(filePath)) ?? undefined;\n const newContent = render(registry, confidenceThreshold);\n await writeFile(filePath, newContent, \"utf-8\");\n return {\n filePath,\n action: \"updated\",\n reason: \"Re-rendered (direct strategy)\",\n backupPath,\n };\n }\n\n // ---------------------------------------------------------------------------\n // Markers strategy: drift detection + writeWithMarkers\n // ---------------------------------------------------------------------------\n\n // 2. Read content and validate markers\n const content = await readFile(filePath, \"utf-8\");\n const validation = validateMarkers(content);\n\n if (!validation.valid) {\n return { filePath, action: \"aborted\", reason: validation.reason! };\n }\n\n // 3. Drift check (only when markers are already present)\n if (validation.mode === \"splice\") {\n const claims = extractClaims(content, filePath);\n\n // Nothing to check — skip (no claims extracted means no drift to detect)\n if (claims.length === 0) {\n return { filePath, action: \"skipped\", reason: \"No drift detected\" };\n }\n\n const scored = await scoreClaims(claims, bridge);\n const hasDrift = scored.some((s) => s.status !== \"GREEN\");\n\n if (!hasDrift) {\n return { filePath, action: \"skipped\", reason: \"No drift detected\" };\n }\n }\n // mode === \"append\": file has no generated section yet -> always proceed\n\n // 4. Backup before any write\n const backupPath = (await backupFile(filePath)) ?? undefined;\n\n // 5. Re-render + write\n const newContent = render(registry, confidenceThreshold);\n await writeWithMarkers(filePath, newContent);\n\n return {\n filePath,\n action: \"updated\",\n reason: \"Re-rendered drifted sections\",\n backupPath,\n };\n}\n","import path from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport ora from \"ora\";\nimport chalk from \"chalk\";\nimport { createBridge } from \"../core/ez-search-bridge.js\";\nimport { extractConventions } from \"../core/pipeline.js\";\nimport { updateFile } from \"../core/updater.js\";\nimport { extractClaims } from \"../core/drift/claim-extractor.js\";\nimport { scoreClaims } from \"../core/drift/claim-scorer.js\";\nimport { FORMAT_EMITTER_MAP } from \"../emitters/index.js\";\n\nexport async function updateAction(\n pathArg: string,\n options: { file?: string; dryRun?: boolean; yes?: boolean }\n): Promise<void> {\n const projectPath = path.resolve(pathArg);\n const spinner = ora(\"Checking for drift...\").start();\n\n try {\n const bridge = await createBridge(projectPath);\n\n // Guard: no index\n if (!(await bridge.hasIndex(projectPath))) {\n spinner.fail(\"No search index found\");\n console.error(\n chalk.red(\"Run 'ez-context generate' or 'ez-search index .' first to create an index.\")\n );\n process.exit(1);\n }\n\n // Resolve target files\n let filePaths: string[];\n if (options.file) {\n filePaths = [path.resolve(projectPath, options.file)];\n } else {\n filePaths = Object.values(FORMAT_EMITTER_MAP)\n .map((entry) => path.join(projectPath, entry.filename))\n .filter((p) => existsSync(p));\n }\n\n if (filePaths.length === 0) {\n spinner.fail(\"No context files found\");\n console.error(\n chalk.red(\"No generated context files found. Run 'ez-context generate' first, or use --file to specify one.\")\n );\n process.exit(1);\n }\n\n if (options.dryRun) {\n // Dry-run: analyze drift per file without writing\n spinner.succeed(\"Dry run complete\");\n console.log();\n console.log(chalk.bold.yellow(\"╔══════════════════════════════════════╗\"));\n console.log(chalk.bold.yellow(\"║ DRY RUN -- no files will be written ║\"));\n console.log(chalk.bold.yellow(\"╚══════════════════════════════════════╝\"));\n console.log();\n\n for (const filePath of filePaths) {\n const basename = path.basename(filePath);\n const { readFile } = await import(\"node:fs/promises\");\n const content = await readFile(filePath, \"utf-8\");\n const claims = extractClaims(content, filePath);\n\n if (claims.length === 0) {\n console.log(` ${chalk.gray(\"-\")} ${basename} ${chalk.gray(\"(no claims to check)\")}`);\n continue;\n }\n\n const scored = await scoreClaims(claims, bridge);\n const hasDrift = scored.some((s) => s.status !== \"GREEN\");\n\n if (hasDrift) {\n console.log(` ${chalk.yellow(\"~\")} Would update ${chalk.cyan(basename)}`);\n } else {\n console.log(` ${chalk.gray(\"-\")} Up to date: ${chalk.gray(basename)}`);\n }\n }\n\n return;\n }\n\n // Real update: process each file\n spinner.text = \"Extracting conventions...\";\n const registry = await extractConventions(projectPath);\n const results = [];\n for (const filePath of filePaths) {\n const basename = path.basename(filePath);\n spinner.text = `Updating ${basename}...`;\n const result = await updateFile(filePath, registry, bridge);\n results.push(result);\n }\n\n // Summarize results\n const updated = results.filter((r) => r.action === \"updated\");\n const aborted = results.filter((r) => r.action === \"aborted\");\n\n if (updated.length === 0 && aborted.length === 0) {\n spinner.succeed(\"All context files are up to date\");\n } else if (updated.length > 0) {\n spinner.succeed(\n `Updated ${updated.length} file${updated.length === 1 ? \"\" : \"s\"}`\n );\n } else {\n spinner.fail(\"Update incomplete — some files could not be updated\");\n }\n\n // Per-file report\n console.log();\n for (const result of results) {\n const basename = path.basename(result.filePath);\n if (result.action === \"updated\") {\n const backup = result.backupPath ? ` (backup: ${path.basename(result.backupPath)})` : \"\";\n console.log(` ${chalk.green(\"✓\")} ${chalk.cyan(basename)}${chalk.gray(backup)}`);\n } else if (result.action === \"skipped\") {\n console.log(` ${chalk.gray(\"-\")} ${chalk.gray(basename)} ${chalk.gray(`(${result.reason})`)}`);\n } else {\n // aborted\n console.log(` ${chalk.yellow(\"⚠\")} ${chalk.yellow(basename)} ${chalk.yellow(`(${result.reason})`)}`);\n }\n }\n\n if (aborted.length > 0) {\n console.log();\n console.log(\n chalk.yellow(`Warning: ${aborted.length} file${aborted.length === 1 ? \"\" : \"s\"} could not be updated due to marker issues.`)\n );\n }\n\n } catch (err) {\n spinner.fail(\"Update failed\");\n const message = err instanceof Error ? err.message : String(err);\n console.error(chalk.red(message));\n process.exit(1);\n }\n}\n","#!/usr/bin/env node\nimport { Command } from \"commander\";\nimport { generateAction } from \"./commands/generate.js\";\nimport { inspectAction } from \"./commands/inspect.js\";\nimport { driftAction } from \"./commands/drift.js\";\nimport { updateAction } from \"./commands/update.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"ez-context\")\n .description(\"Generate AI context files from any project\")\n .version(\"0.1.0\");\n\nprogram\n .command(\"generate\")\n .description(\"Extract conventions and generate context files\")\n .argument(\"[path]\", \"project root to analyze\", \".\")\n .option(\"--dry-run\", \"preview without writing files\")\n .option(\"-y, --yes\", \"non-interactive mode\")\n .option(\"--output <dir>\", \"output directory\", \".\")\n .option(\"--threshold <number>\", \"confidence threshold 0-1\", \"0.7\")\n .option(\"--format <formats>\", \"output formats: claude,agents,cursor,copilot,skills,rulesync,ruler (comma-separated)\", \"claude,agents\")\n .action(generateAction);\n\nprogram\n .command(\"inspect\")\n .description(\"Display detected conventions\")\n .argument(\"[path]\", \"project root to analyze\", \".\")\n .option(\"--threshold <number>\", \"confidence threshold 0-1\", \"0.7\")\n .action(inspectAction);\n\nprogram\n .command(\"drift\")\n .description(\"Check context files against code for semantic drift\")\n .argument(\"[path]\", \"project root to analyze\", \".\")\n .option(\"--file <contextFile>\", \"specific context file to check\")\n .action(driftAction);\n\nprogram\n .command(\"update\")\n .description(\"Update drifted sections in context files, preserving manual edits\")\n .argument(\"[path]\", \"project root to analyze\", \".\")\n .option(\"--file <contextFile>\", \"specific context file to update\")\n .option(\"--dry-run\", \"preview changes without writing files\")\n .option(\"-y, --yes\", \"non-interactive mode\")\n .action(updateAction);\n\nawait program.parseAsync();\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.js","names":[],"sources":["../src/commands/generate.ts","../src/commands/inspect.ts","../src/core/drift/claim-extractor.ts","../src/core/drift/claim-scorer.ts","../src/core/drift/report.ts","../src/commands/drift.ts","../src/core/updater.ts","../src/commands/update.ts","../src/cli.ts"],"sourcesContent":["import path from \"node:path\";\nimport ora from \"ora\";\nimport chalk from \"chalk\";\nimport { extractConventions } from \"../core/pipeline.js\";\nimport { emit } from \"../emitters/index.js\";\nimport type { EmitOptions, OutputFormat } from \"../emitters/types.js\";\n\nconst DRY_RUN_PREVIEW_LINES = 20;\n\nconst VALID_FORMATS: OutputFormat[] = [\n \"claude\",\n \"agents\",\n \"cursor\",\n \"copilot\",\n \"skills\",\n \"rulesync\",\n \"ruler\",\n];\n\nexport function parseFormats(raw: string): OutputFormat[] {\n const formats = [...new Set(raw.split(\",\").map((s) => s.trim()).filter(Boolean))];\n const invalid = formats.filter((f) => !VALID_FORMATS.includes(f as OutputFormat));\n if (invalid.length > 0) {\n throw new Error(\n `Invalid format(s): ${invalid.join(\", \")}. Valid: ${VALID_FORMATS.join(\", \")}`\n );\n }\n return formats as OutputFormat[];\n}\n\nfunction truncatePreview(content: string): string {\n const lines = content.split(\"\\n\");\n if (lines.length <= DRY_RUN_PREVIEW_LINES) {\n return content;\n }\n const preview = lines.slice(0, DRY_RUN_PREVIEW_LINES).join(\"\\n\");\n return `${preview}\\n... (${lines.length} lines total)`;\n}\n\nexport async function generateAction(\n pathArg: string,\n options: { dryRun?: boolean; yes?: boolean; output?: string; threshold?: string; format?: string }\n): Promise<void> {\n const projectPath = path.resolve(pathArg);\n\n // --yes and non-TTY environments: ora handles non-TTY gracefully by\n // falling back to plain text. No interactive prompts are used anywhere.\n const spinner = ora(\"Analyzing project conventions...\").start();\n\n try {\n const registry = await extractConventions(projectPath);\n const conventionCount = registry.conventions.length;\n spinner.succeed(`Found ${conventionCount} convention${conventionCount === 1 ? \"\" : \"s\"}`);\n\n const confidenceThreshold = parseFloat(options.threshold ?? \"0.7\");\n const outputDir = path.resolve(options.output ?? \".\");\n\n // Parse and validate --format (default: \"claude,agents\")\n const formats = parseFormats(options.format ?? \"claude,agents\");\n\n const emitOptions: EmitOptions = {\n outputDir,\n confidenceThreshold,\n dryRun: options.dryRun ?? false,\n formats,\n };\n\n const isDefault = formats.length === 2 && formats.includes(\"claude\") && formats.includes(\"agents\");\n const genSpinnerText = isDefault\n ? \"Generating context files...\"\n : `Generating ${formats.length} context file${formats.length === 1 ? \"\" : \"s\"}...`;\n const genSpinner = ora(genSpinnerText).start();\n const result = await emit(registry, emitOptions);\n\n if (options.dryRun) {\n genSpinner.succeed(\"Dry run complete\");\n console.log();\n console.log(chalk.bold.yellow(\"╔══════════════════════════════════════╗\"));\n console.log(chalk.bold.yellow(\"║ DRY RUN -- no files will be written ║\"));\n console.log(chalk.bold.yellow(\"╚══════════════════════════════════════╝\"));\n console.log();\n for (const [format, content] of Object.entries(result.rendered)) {\n console.log(chalk.cyan(`--- ${format.toUpperCase()} ---`));\n console.log(truncatePreview(content));\n console.log();\n }\n } else {\n genSpinner.succeed(`Generated ${result.filesWritten.length} file${result.filesWritten.length === 1 ? \"\" : \"s\"}`);\n console.log();\n console.log(chalk.bold.green(\"Generated files:\"));\n for (const filePath of result.filesWritten) {\n const relPath = path.relative(outputDir, filePath);\n console.log(` ${chalk.cyan(relPath)}`);\n }\n }\n } catch (err) {\n spinner.fail(\"Analysis failed\");\n const message = err instanceof Error ? err.message : String(err);\n console.error(chalk.red(message));\n process.exit(1);\n }\n}\n","import path from \"node:path\";\nimport ora from \"ora\";\nimport chalk from \"chalk\";\nimport { extractConventions } from \"../core/pipeline.js\";\nimport type { ConventionEntry } from \"../core/schema.js\";\n\nfunction confidenceDot(confidence: number): string {\n if (confidence >= 0.8) return chalk.green(\"●\");\n if (confidence >= 0.6) return chalk.yellow(\"●\");\n return chalk.red(\"●\");\n}\n\nexport async function inspectAction(\n pathArg: string,\n options: { threshold?: string }\n): Promise<void> {\n const projectPath = path.resolve(pathArg);\n const spinner = ora(\"Analyzing project conventions...\").start();\n\n try {\n const registry = await extractConventions(projectPath);\n const totalCount = registry.conventions.length;\n spinner.succeed(`Extracted ${totalCount} convention${totalCount === 1 ? \"\" : \"s\"}`);\n\n const threshold = parseFloat(options.threshold ?? \"0.7\");\n\n const filtered = registry.conventions.filter(\n (c) => c.confidence >= threshold\n );\n\n if (filtered.length === 0) {\n console.log(\n chalk.yellow(\n `\\nNo conventions found above ${threshold} confidence threshold. Try lowering --threshold.`\n )\n );\n return;\n }\n\n // Group by category\n const byCategory = new Map<string, ConventionEntry[]>();\n for (const convention of filtered) {\n const group = byCategory.get(convention.category) ?? [];\n group.push(convention);\n byCategory.set(convention.category, group);\n }\n\n console.log();\n for (const [category, conventions] of byCategory) {\n console.log(chalk.bold(category.toUpperCase()));\n for (const convention of conventions) {\n const pct = Math.round(convention.confidence * 100);\n console.log(\n ` ${confidenceDot(convention.confidence)} ${convention.pattern} ${chalk.gray(`(${pct}%)`)}`\n );\n }\n console.log();\n }\n\n const categoryCount = byCategory.size;\n console.log(\n chalk.gray(\n `Found ${filtered.length} convention${filtered.length === 1 ? \"\" : \"s\"} across ${categoryCount} categor${categoryCount === 1 ? \"y\" : \"ies\"} (threshold: ${threshold})`\n )\n );\n } catch (err) {\n spinner.fail(\"Analysis failed\");\n const message = err instanceof Error ? err.message : String(err);\n console.error(chalk.red(message));\n process.exit(1);\n }\n}\n","/**\n * Claim extractor — parses markdown context files into individual testable claims.\n *\n * Input: raw markdown string (CLAUDE.md, AGENTS.md, .cursorrules, etc.)\n * Output: Claim[] where each claim is an atomic declarative statement\n *\n * Extraction rules:\n * - Bullet points: ^[-*+]\\s+\n * - Numbered list items: ^\\d+\\.\\s+\n * - Bold/code markers stripped from extracted text\n * - Boilerplate value lines skipped (Language: X, Framework: X, etc.)\n * - ez-context markers and HTML comments skipped\n * - Claims shorter than 10 chars or longer than 300 chars excluded\n * - Current section heading tracked for context\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface Claim {\n text: string; // The claim text (bold/code markers stripped)\n sourceFile: string; // Which file it came from\n sourceLine: number; // 1-based line number\n sourceSection: string; // Nearest parent heading\n}\n\n// ---------------------------------------------------------------------------\n// Filters\n// ---------------------------------------------------------------------------\n\n/**\n * Matches boilerplate key-value lines that are structural metadata, not\n * behavioral claims. Applied AFTER bold/code stripping.\n *\n * Examples skipped:\n * \"Language: TypeScript\"\n * \"Package Manager: bun\"\n */\nconst BOILERPLATE_VALUE =\n /^(Language|Framework|Build|Package Manager|Test Runner|Pattern|Layers):\\s/i;\n\n// ---------------------------------------------------------------------------\n// Core function\n// ---------------------------------------------------------------------------\n\n/**\n * Extract all testable claims from a markdown string.\n *\n * @param content Raw markdown content of the context file\n * @param sourceFile Path to the source file (stored on each claim)\n * @returns Array of extracted claims, filtered and deduplicated\n */\nexport function extractClaims(content: string, sourceFile: string): Claim[] {\n const claims: Claim[] = [];\n const lines = content.split(\"\\n\");\n let currentSection = \"\";\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!.trim();\n const lineNum = i + 1; // 1-based\n\n // Skip blank lines\n if (!line) continue;\n\n // Skip HTML comments (includes ez-context markers like <!-- ez-context:... -->)\n if (line.startsWith(\"<!--\")) continue;\n\n // Skip lines containing ez-context markers (belt-and-suspenders for inline markers)\n if (line.includes(\"ez-context:\")) continue;\n\n // Track section headings — H1, H2, H3\n const heading = line.match(/^#{1,3}\\s+(.+)/);\n if (heading) {\n currentSection = heading[1]!.trim();\n continue;\n }\n\n // Match bullet points or numbered list items\n const bullet = line.match(/^[-*+]\\s+(.+)/);\n const numbered = !bullet ? line.match(/^\\d+\\.\\s+(.+)/) : null;\n const rawText = bullet ? bullet[1]! : numbered ? numbered[1]! : null;\n\n if (!rawText) continue;\n\n // Strip bold markers (**text** -> text) and inline code markers (`text` -> text)\n const text = rawText\n .replace(/\\*\\*([^*]+)\\*\\*/g, \"$1\")\n .replace(/`([^`]+)`/g, \"$1\")\n .trim();\n\n // Apply length filters\n if (text.length < 10 || text.length > 300) continue;\n\n // Skip boilerplate key-value lines\n if (BOILERPLATE_VALUE.test(text)) continue;\n\n claims.push({\n text,\n sourceFile,\n sourceLine: lineNum,\n sourceSection: currentSection,\n });\n }\n\n return claims;\n}\n","/**\n * Claim scorer — compares extracted claims against the code index via semantic search.\n *\n * Each claim is searched against the indexed codebase. The top similarity score\n * determines whether the claim is GREEN (well-supported), YELLOW (possibly stale),\n * or RED (contradicted / not found).\n *\n * Claims are processed in batches to avoid ONNX pipeline contention.\n */\nimport type { Claim } from \"./claim-extractor.js\";\nimport type { SearchResult, EzSearchBridge } from \"../ez-search-bridge.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nexport const GREEN_THRESHOLD = 0.65;\nexport const YELLOW_THRESHOLD = 0.40;\nexport const BATCH_SIZE = 10;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type ClaimStatus = \"GREEN\" | \"YELLOW\" | \"RED\";\n\nexport interface ScoredClaim {\n claim: Claim;\n status: ClaimStatus;\n score: number; // Top bridge.search() score (0.0-1.0)\n evidence: SearchResult[]; // Top k results\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction chunk<T>(arr: T[], size: number): T[][] {\n const chunks: T[][] = [];\n for (let i = 0; i < arr.length; i += size) {\n chunks.push(arr.slice(i, i + size));\n }\n return chunks;\n}\n\nfunction classifyScore(score: number): ClaimStatus {\n if (score >= GREEN_THRESHOLD) return \"GREEN\";\n if (score >= YELLOW_THRESHOLD) return \"YELLOW\";\n return \"RED\";\n}\n\nasync function scoreSingleClaim(\n claim: Claim,\n bridge: EzSearchBridge\n): Promise<ScoredClaim> {\n const evidence = await bridge.search(claim.text, { k: 5 });\n const topScore = evidence.length > 0 ? evidence[0]!.score : 0;\n return {\n claim,\n status: classifyScore(topScore),\n score: topScore,\n evidence,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Score all claims by searching the code index in batches of BATCH_SIZE.\n *\n * @param claims Claims to score\n * @param bridge EzSearchBridge instance bound to the project\n * @param onProgress Optional callback fired after each batch: (done, total)\n * @returns ScoredClaim[] in the same order as input claims\n */\nexport async function scoreClaims(\n claims: Claim[],\n bridge: EzSearchBridge,\n onProgress?: (completed: number, total: number) => void\n): Promise<ScoredClaim[]> {\n const total = claims.length;\n const batches = chunk(claims, BATCH_SIZE);\n const results: ScoredClaim[] = [];\n let completed = 0;\n\n for (const batch of batches) {\n const batchResults = await Promise.all(\n batch.map((claim) => scoreSingleClaim(claim, bridge))\n );\n results.push(...batchResults);\n completed += batch.length;\n onProgress?.(completed, total);\n }\n\n return results;\n}\n","/**\n * Drift report — aggregates scored claims into a health score and markdown report.\n *\n * Health score: mean of per-claim scores scaled 0-100 (rounded).\n * Zero claims yields a health score of 100 (nothing to contradict = healthy).\n *\n * Rendered markdown groups claims by status with evidence for stale/contradicted claims.\n */\nimport type { ScoredClaim } from \"./claim-scorer.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface DriftReport {\n sourceFile: string;\n healthScore: number;\n scoredClaims: ScoredClaim[];\n}\n\n// ---------------------------------------------------------------------------\n// Health score\n// ---------------------------------------------------------------------------\n\n/**\n * Compute the aggregate health score for a set of scored claims.\n * Returns 100 for empty input (no claims = no drift).\n */\nexport function computeHealthScore(scoredClaims: ScoredClaim[]): number {\n if (scoredClaims.length === 0) return 100;\n const mean =\n scoredClaims.reduce((sum, sc) => sum + sc.score, 0) / scoredClaims.length;\n return Math.round(mean * 100);\n}\n\n// ---------------------------------------------------------------------------\n// Report assembly\n// ---------------------------------------------------------------------------\n\n/**\n * Build a DriftReport from a source file path and its scored claims.\n */\nexport function buildDriftReport(\n sourceFile: string,\n scoredClaims: ScoredClaim[]\n): DriftReport {\n return {\n sourceFile,\n healthScore: computeHealthScore(scoredClaims),\n scoredClaims,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Renderer\n// ---------------------------------------------------------------------------\n\nconst STATUS_LABEL: Record<string, string> = {\n GREEN: \"Confirmed\",\n YELLOW: \"Possibly Stale\",\n RED: \"Contradicted\",\n};\n\n/**\n * Render a drift report as a readable markdown string.\n *\n * Layout:\n * # Drift Report\n * Health score, source file, claim count\n *\n * ## Confirmed (GREEN)\n * - [GREEN] claim text (score: X.XX)\n *\n * ## Possibly Stale (YELLOW)\n * - [YELLOW] claim text (score: X.XX)\n * - file: chunk_preview\n *\n * ## Contradicted (RED)\n * - [RED] claim text (score: X.XX)\n * - file: chunk_preview\n *\n * Summary: X confirmed, Y possibly stale, Z contradicted\n */\nexport function renderDriftReport(report: DriftReport): string {\n const { sourceFile, healthScore, scoredClaims } = report;\n const lines: string[] = [];\n\n const green = scoredClaims.filter((sc) => sc.status === \"GREEN\");\n const yellow = scoredClaims.filter((sc) => sc.status === \"YELLOW\");\n const red = scoredClaims.filter((sc) => sc.status === \"RED\");\n\n // Header\n lines.push(\"# Drift Report\");\n lines.push(\"\");\n lines.push(`**Health Score:** ${healthScore}/100`);\n lines.push(`**File:** ${sourceFile}`);\n lines.push(`**Claims:** ${scoredClaims.length}`);\n lines.push(\"\");\n\n // Render a group of claims\n const renderGroup = (group: ScoredClaim[], status: string) => {\n if (group.length === 0) return;\n const label = STATUS_LABEL[status] ?? status;\n lines.push(`## ${label} (${status})`);\n lines.push(\"\");\n for (const sc of group) {\n lines.push(`- [${sc.status}] ${sc.claim.text} (score: ${sc.score.toFixed(2)})`);\n // Show top 2 evidence items for non-GREEN claims\n if (sc.status !== \"GREEN\") {\n const topEvidence = sc.evidence.slice(0, 2);\n for (const ev of topEvidence) {\n const preview = ev.chunk.replace(/\\s+/g, \" \").trim().slice(0, 80);\n lines.push(` - ${ev.file}: ${preview}`);\n }\n }\n }\n lines.push(\"\");\n };\n\n renderGroup(green, \"GREEN\");\n renderGroup(yellow, \"YELLOW\");\n renderGroup(red, \"RED\");\n\n // Summary\n lines.push(\n `Summary: ${green.length} confirmed, ${yellow.length} possibly stale, ${red.length} contradicted`\n );\n\n return lines.join(\"\\n\");\n}\n","import path from \"node:path\";\nimport { readFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport ora from \"ora\";\nimport chalk from \"chalk\";\nimport { createBridge } from \"../core/ez-search-bridge.js\";\nimport { extractClaims } from \"../core/drift/claim-extractor.js\";\nimport { scoreClaims } from \"../core/drift/claim-scorer.js\";\nimport { buildDriftReport, renderDriftReport, computeHealthScore } from \"../core/drift/report.js\";\n\nconst CANDIDATE_FILES = [\"CLAUDE.md\", \"AGENTS.md\", \".cursorrules\", \"CONTEXT.md\"];\n\nfunction healthColor(score: number): string {\n if (score >= 70) return chalk.green(String(score));\n if (score >= 40) return chalk.yellow(String(score));\n return chalk.red(String(score));\n}\n\nexport async function driftAction(\n pathArg: string,\n options: { file?: string }\n): Promise<void> {\n const projectPath = path.resolve(pathArg);\n const spinner = ora(\"Loading context files...\").start();\n\n try {\n const bridge = await createBridge(projectPath);\n\n // Always refresh so search results reflect current file state\n spinner.text = \"Refreshing search index...\";\n await bridge.refreshIndex(projectPath);\n spinner.text = \"Loading context files...\";\n\n // Resolve files\n let filePaths: string[];\n if (options.file) {\n filePaths = [path.resolve(projectPath, options.file)];\n } else {\n filePaths = CANDIDATE_FILES\n .map((name) => path.join(projectPath, name))\n .filter((p) => existsSync(p));\n }\n\n if (filePaths.length === 0) {\n spinner.fail(\"No context files found\");\n console.error(\n chalk.red(\"No CLAUDE.md, AGENTS.md, .cursorrules, or CONTEXT.md found. Use --file to specify one.\")\n );\n process.exit(1);\n }\n\n // Extract claims from each file\n const claimsByFile: Map<string, ReturnType<typeof extractClaims>> = new Map();\n for (const filePath of filePaths) {\n const content = await readFile(filePath, \"utf-8\");\n const claims = extractClaims(content, filePath);\n claimsByFile.set(filePath, claims);\n }\n\n const allClaims = [...claimsByFile.values()].flat();\n spinner.text = `Analyzing ${allClaims.length} claims...`;\n\n // Score claims with progress callback\n const scoredAll = await scoreClaims(allClaims, bridge, (done, total) => {\n spinner.text = `Checking claim ${done}/${total}...`;\n });\n\n // Build and render reports per file\n const reports = filePaths.map((filePath) => {\n const fileClaims = claimsByFile.get(filePath) ?? [];\n const fileScoredClaims = scoredAll.filter((sc) =>\n fileClaims.some((c) => c === sc.claim)\n );\n return buildDriftReport(filePath, fileScoredClaims);\n });\n\n const overallScore = computeHealthScore(scoredAll);\n spinner.succeed(`Drift analysis complete — health score: ${healthColor(overallScore)}/100`);\n\n console.log();\n for (const report of reports) {\n console.log(renderDriftReport(report));\n console.log();\n }\n } catch (err) {\n spinner.fail(\"Drift analysis failed\");\n const message = err instanceof Error ? err.message : String(err);\n console.error(chalk.red(message));\n process.exit(1);\n }\n}\n","/**\n * Updater — targeted regeneration engine for `ez-context update`.\n *\n * Orchestrates:\n * 1. Marker validation (pre-flight check, markers strategy only)\n * 2. Drift detection (skip GREEN files, markers strategy only)\n * 3. File backup (before any write)\n * 4. Re-rendering (via FORMAT_EMITTER_MAP)\n * 5. Write-back (writeWithMarkers for markers strategy, writeFile for direct)\n */\nimport { copyFile, readFile, writeFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport path from \"node:path\";\n\nimport { MARKER_START, MARKER_END, writeWithMarkers } from \"../emitters/writer.js\";\nimport { FORMAT_EMITTER_MAP } from \"../emitters/index.js\";\nimport { extractClaims } from \"./drift/claim-extractor.js\";\nimport { scoreClaims } from \"./drift/claim-scorer.js\";\nimport type { EzSearchBridge } from \"./ez-search-bridge.js\";\nimport type { ConventionRegistry } from \"./schema.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface MarkerValidation {\n valid: boolean;\n mode: \"append\" | \"splice\" | \"invalid\";\n reason?: string;\n startIdx?: number;\n endIdx?: number;\n}\n\nexport type UpdateAction = \"skipped\" | \"updated\" | \"aborted\";\n\nexport interface FileUpdateResult {\n filePath: string;\n action: UpdateAction;\n reason: string;\n backupPath?: string;\n}\n\n// ---------------------------------------------------------------------------\n// validateMarkers\n// ---------------------------------------------------------------------------\n\n/**\n * Pre-flight marker check for updateFile.\n *\n * Unlike writeWithMarkers (which silently appends on unpaired markers),\n * validateMarkers rejects unpaired markers so updateFile can abort safely.\n *\n * Returns:\n * - { valid: true, mode: \"append\" } — no markers, safe to append\n * - { valid: true, mode: \"splice\", startIdx, endIdx } — well-formed pair\n * - { valid: false, mode: \"invalid\", reason } — unpaired or inverted markers\n */\nexport function validateMarkers(content: string): MarkerValidation {\n const startIdx = content.indexOf(MARKER_START);\n const endIdx = content.indexOf(MARKER_END);\n\n const hasStart = startIdx !== -1;\n const hasEnd = endIdx !== -1;\n\n // No markers at all -> safe to append\n if (!hasStart && !hasEnd) {\n return { valid: true, mode: \"append\" };\n }\n\n // Both markers present -> validate ordering\n if (hasStart && hasEnd) {\n if (endIdx < startIdx) {\n return {\n valid: false,\n mode: \"invalid\",\n reason: \"End marker appears before start marker (corrupted file)\",\n };\n }\n return { valid: true, mode: \"splice\", startIdx, endIdx };\n }\n\n // Unpaired: only one marker present\n if (hasStart && !hasEnd) {\n return {\n valid: false,\n mode: \"invalid\",\n reason: \"Unpaired ez-context marker: end marker missing\",\n };\n }\n\n // hasEnd && !hasStart\n return {\n valid: false,\n mode: \"invalid\",\n reason: \"Unpaired ez-context marker: start marker missing\",\n };\n}\n\n// ---------------------------------------------------------------------------\n// backupFile\n// ---------------------------------------------------------------------------\n\n/**\n * Copy filePath to filePath.bak and return the backup path.\n * Returns null if the file does not exist.\n * Overwrites any existing .bak silently (represents state before this run).\n */\nexport async function backupFile(filePath: string): Promise<string | null> {\n if (!existsSync(filePath)) {\n return null;\n }\n\n const backupPath = filePath + \".bak\";\n await copyFile(filePath, backupPath);\n return backupPath;\n}\n\n// ---------------------------------------------------------------------------\n// findFormatEntry\n// ---------------------------------------------------------------------------\n\n/**\n * Look up the FORMAT_EMITTER_MAP entry whose filename suffix matches filePath.\n * Returns undefined if the file doesn't correspond to a known format.\n */\nfunction findFormatEntry(filePath: string) {\n const normalized = path.normalize(filePath);\n for (const entry of Object.values(FORMAT_EMITTER_MAP)) {\n if (normalized.endsWith(path.normalize(entry.filename))) {\n return entry;\n }\n }\n return undefined;\n}\n\n// ---------------------------------------------------------------------------\n// updateFile\n// ---------------------------------------------------------------------------\n\n/**\n * Orchestrate drift detection and targeted re-rendering for a single file.\n *\n * The write strategy is determined by FORMAT_EMITTER_MAP:\n * - \"markers\" strategy: drift detection + writeWithMarkers (default)\n * - \"direct\" strategy: always regenerate + writeFile (full overwrite)\n *\n * Flow for markers strategy:\n * 1. File existence check — skip if missing\n * 2. Marker validation — abort on invalid markers\n * 3. Drift check (splice mode only) — skip if all claims GREEN\n * 4. Backup creation\n * 5. Re-render + writeWithMarkers\n *\n * Flow for direct strategy:\n * 1. File existence check — skip if missing\n * 2. Backup creation\n * 3. Re-render + writeFile (full overwrite)\n *\n * @param filePath Absolute path to the context file\n * @param registry Pre-computed convention registry (NOT extracted per-file)\n * @param bridge EzSearchBridge instance for drift scoring\n * @param confidenceThreshold Confidence floor passed to the renderer (default 0.7)\n */\nexport async function updateFile(\n filePath: string,\n registry: ConventionRegistry,\n bridge: EzSearchBridge,\n confidenceThreshold: number = 0.7\n): Promise<FileUpdateResult> {\n // 1. File existence check\n if (!existsSync(filePath)) {\n return { filePath, action: \"skipped\", reason: \"File does not exist\" };\n }\n\n const formatEntry = findFormatEntry(filePath);\n // Fall back to claude (markers) if the file isn't a known format\n const strategy = formatEntry?.strategy ?? \"markers\";\n const render = formatEntry?.render ?? FORMAT_EMITTER_MAP.claude.render;\n\n // ---------------------------------------------------------------------------\n // Direct strategy: full regeneration, no drift detection\n // ---------------------------------------------------------------------------\n if (strategy === \"direct\") {\n const backupPath = (await backupFile(filePath)) ?? undefined;\n const newContent = render(registry, confidenceThreshold);\n await writeFile(filePath, newContent, \"utf-8\");\n return {\n filePath,\n action: \"updated\",\n reason: \"Re-rendered (direct strategy)\",\n backupPath,\n };\n }\n\n // ---------------------------------------------------------------------------\n // Markers strategy: drift detection + writeWithMarkers\n // ---------------------------------------------------------------------------\n\n // 2. Read content and validate markers\n const content = await readFile(filePath, \"utf-8\");\n const validation = validateMarkers(content);\n\n if (!validation.valid) {\n return { filePath, action: \"aborted\", reason: validation.reason! };\n }\n\n // 3. Drift check (only when markers are already present)\n if (validation.mode === \"splice\") {\n const claims = extractClaims(content, filePath);\n\n // Nothing to check — skip (no claims extracted means no drift to detect)\n if (claims.length === 0) {\n return { filePath, action: \"skipped\", reason: \"No drift detected\" };\n }\n\n const scored = await scoreClaims(claims, bridge);\n const hasDrift = scored.some((s) => s.status !== \"GREEN\");\n\n if (!hasDrift) {\n return { filePath, action: \"skipped\", reason: \"No drift detected\" };\n }\n }\n // mode === \"append\": file has no generated section yet -> always proceed\n\n // 4. Backup before any write\n const backupPath = (await backupFile(filePath)) ?? undefined;\n\n // 5. Re-render + write\n const newContent = render(registry, confidenceThreshold);\n await writeWithMarkers(filePath, newContent);\n\n return {\n filePath,\n action: \"updated\",\n reason: \"Re-rendered drifted sections\",\n backupPath,\n };\n}\n","import path from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport ora from \"ora\";\nimport chalk from \"chalk\";\nimport { createBridge } from \"../core/ez-search-bridge.js\";\nimport { extractConventions } from \"../core/pipeline.js\";\nimport { updateFile } from \"../core/updater.js\";\nimport { extractClaims } from \"../core/drift/claim-extractor.js\";\nimport { scoreClaims } from \"../core/drift/claim-scorer.js\";\nimport { FORMAT_EMITTER_MAP } from \"../emitters/index.js\";\n\nexport async function updateAction(\n pathArg: string,\n options: { file?: string; dryRun?: boolean; yes?: boolean }\n): Promise<void> {\n const projectPath = path.resolve(pathArg);\n const spinner = ora(\"Checking for drift...\").start();\n\n try {\n const bridge = await createBridge(projectPath);\n\n // Always refresh so search results reflect current file state\n spinner.text = \"Refreshing search index...\";\n await bridge.refreshIndex(projectPath);\n\n // Resolve target files\n let filePaths: string[];\n if (options.file) {\n filePaths = [path.resolve(projectPath, options.file)];\n } else {\n filePaths = Object.values(FORMAT_EMITTER_MAP)\n .map((entry) => path.join(projectPath, entry.filename))\n .filter((p) => existsSync(p));\n }\n\n if (filePaths.length === 0) {\n spinner.fail(\"No context files found\");\n console.error(\n chalk.red(\"No generated context files found. Run 'ez-context generate' first, or use --file to specify one.\")\n );\n process.exit(1);\n }\n\n if (options.dryRun) {\n // Dry-run: analyze drift per file without writing\n spinner.succeed(\"Dry run complete\");\n console.log();\n console.log(chalk.bold.yellow(\"╔══════════════════════════════════════╗\"));\n console.log(chalk.bold.yellow(\"║ DRY RUN -- no files will be written ║\"));\n console.log(chalk.bold.yellow(\"╚══════════════════════════════════════╝\"));\n console.log();\n\n for (const filePath of filePaths) {\n const basename = path.basename(filePath);\n const { readFile } = await import(\"node:fs/promises\");\n const content = await readFile(filePath, \"utf-8\");\n const claims = extractClaims(content, filePath);\n\n if (claims.length === 0) {\n console.log(` ${chalk.gray(\"-\")} ${basename} ${chalk.gray(\"(no claims to check)\")}`);\n continue;\n }\n\n const scored = await scoreClaims(claims, bridge);\n const hasDrift = scored.some((s) => s.status !== \"GREEN\");\n\n if (hasDrift) {\n console.log(` ${chalk.yellow(\"~\")} Would update ${chalk.cyan(basename)}`);\n } else {\n console.log(` ${chalk.gray(\"-\")} Up to date: ${chalk.gray(basename)}`);\n }\n }\n\n return;\n }\n\n // Real update: process each file\n spinner.text = \"Extracting conventions...\";\n const registry = await extractConventions(projectPath);\n const results = [];\n for (const filePath of filePaths) {\n const basename = path.basename(filePath);\n spinner.text = `Updating ${basename}...`;\n const result = await updateFile(filePath, registry, bridge);\n results.push(result);\n }\n\n // Summarize results\n const updated = results.filter((r) => r.action === \"updated\");\n const aborted = results.filter((r) => r.action === \"aborted\");\n\n if (updated.length === 0 && aborted.length === 0) {\n spinner.succeed(\"All context files are up to date\");\n } else if (updated.length > 0) {\n spinner.succeed(\n `Updated ${updated.length} file${updated.length === 1 ? \"\" : \"s\"}`\n );\n } else {\n spinner.fail(\"Update incomplete — some files could not be updated\");\n }\n\n // Per-file report\n console.log();\n for (const result of results) {\n const basename = path.basename(result.filePath);\n if (result.action === \"updated\") {\n const backup = result.backupPath ? ` (backup: ${path.basename(result.backupPath)})` : \"\";\n console.log(` ${chalk.green(\"✓\")} ${chalk.cyan(basename)}${chalk.gray(backup)}`);\n } else if (result.action === \"skipped\") {\n console.log(` ${chalk.gray(\"-\")} ${chalk.gray(basename)} ${chalk.gray(`(${result.reason})`)}`);\n } else {\n // aborted\n console.log(` ${chalk.yellow(\"⚠\")} ${chalk.yellow(basename)} ${chalk.yellow(`(${result.reason})`)}`);\n }\n }\n\n if (aborted.length > 0) {\n console.log();\n console.log(\n chalk.yellow(`Warning: ${aborted.length} file${aborted.length === 1 ? \"\" : \"s\"} could not be updated due to marker issues.`)\n );\n }\n\n } catch (err) {\n spinner.fail(\"Update failed\");\n const message = err instanceof Error ? err.message : String(err);\n console.error(chalk.red(message));\n process.exit(1);\n }\n}\n","#!/usr/bin/env node\nimport { Command } from \"commander\";\nimport { generateAction } from \"./commands/generate.js\";\nimport { inspectAction } from \"./commands/inspect.js\";\nimport { driftAction } from \"./commands/drift.js\";\nimport { updateAction } from \"./commands/update.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"ez-context\")\n .description(\"Generate AI context files from any project\")\n .version(\"0.1.0\");\n\nprogram\n .command(\"generate\")\n .description(\"Extract conventions and generate context files\")\n .argument(\"[path]\", \"project root to analyze\", \".\")\n .option(\"--dry-run\", \"preview without writing files\")\n .option(\"-y, --yes\", \"non-interactive mode\")\n .option(\"--output <dir>\", \"output directory\", \".\")\n .option(\"--threshold <number>\", \"confidence threshold 0-1\", \"0.7\")\n .option(\"--format <formats>\", \"output formats: claude,agents,cursor,copilot,skills,rulesync,ruler (comma-separated)\", \"claude,agents\")\n .action(generateAction);\n\nprogram\n .command(\"inspect\")\n .description(\"Display detected conventions\")\n .argument(\"[path]\", \"project root to analyze\", \".\")\n .option(\"--threshold <number>\", \"confidence threshold 0-1\", \"0.7\")\n .action(inspectAction);\n\nprogram\n .command(\"drift\")\n .description(\"Check context files against code for semantic drift\")\n .argument(\"[path]\", \"project root to analyze\", \".\")\n .option(\"--file <contextFile>\", \"specific context file to check\")\n .action(driftAction);\n\nprogram\n .command(\"update\")\n .description(\"Update drifted sections in context files, preserving manual edits\")\n .argument(\"[path]\", \"project root to analyze\", \".\")\n .option(\"--file <contextFile>\", \"specific context file to update\")\n .option(\"--dry-run\", \"preview changes without writing files\")\n .option(\"-y, --yes\", \"non-interactive mode\")\n .action(updateAction);\n\nawait program.parseAsync();\n"],"mappings":"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"}
|
|
@@ -1054,6 +1054,9 @@ var EzSearchBridgeImpl = class {
|
|
|
1054
1054
|
if (await this.hasIndex(projectPath)) return;
|
|
1055
1055
|
await index(projectPath);
|
|
1056
1056
|
}
|
|
1057
|
+
async refreshIndex(projectPath) {
|
|
1058
|
+
await index(projectPath, { clear: true });
|
|
1059
|
+
}
|
|
1057
1060
|
async search(searchQuery, options = {}) {
|
|
1058
1061
|
const { k = 10 } = options;
|
|
1059
1062
|
let raw;
|
|
@@ -1846,4 +1849,4 @@ async function emit(registry, options) {
|
|
|
1846
1849
|
|
|
1847
1850
|
//#endregion
|
|
1848
1851
|
export { EvidenceRefSchema as _, writeWithMarkers as a, ALWAYS_SKIP as c, addConvention as d, createRegistry as f, ConventionRegistrySchema as g, ConventionEntrySchema as h, MARKER_START as i, listProjectFiles as l, ConventionCategorySchema as m, emit as n, extractConventions as o, ArchitectureInfoSchema as p, MARKER_END as r, createBridge as s, FORMAT_EMITTER_MAP as t, runExtractors as u, StackInfoSchema as v };
|
|
1849
|
-
//# sourceMappingURL=emitters-
|
|
1852
|
+
//# sourceMappingURL=emitters-Doc-arnD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emitters-Doc-arnD.js","names":["EVIDENCE","parseToml","yamlLoad","prepData"],"sources":["../src/core/schema.ts","../src/core/registry.ts","../src/extractors/index.ts","../src/extractors/static/package-json.ts","../src/extractors/static/lockfile.ts","../src/extractors/static/tsconfig.ts","../src/extractors/static/go-mod.ts","../src/extractors/static/cargo-toml.ts","../src/extractors/static/ci.ts","../src/utils/fs.ts","../src/extractors/static/project-structure.ts","../src/extractors/code/naming.ts","../src/extractors/code/imports.ts","../src/extractors/code/error-handling.ts","../src/core/ez-search-bridge.ts","../src/extractors/semantic/error-handling.ts","../src/extractors/semantic/architecture.ts","../src/core/pipeline.ts","../src/emitters/writer.ts","../src/emitters/render-helpers.ts","../src/emitters/claude-md.ts","../src/emitters/agents-md.ts","../src/emitters/cursor-mdc.ts","../src/emitters/copilot-md.ts","../src/emitters/skill-md.ts","../src/emitters/rulesync-md.ts","../src/emitters/ruler-md.ts","../src/emitters/index.ts"],"sourcesContent":["import { z } from \"zod\";\n\n// ---------------------------------------------------------------------------\n// Atomic schemas\n// ---------------------------------------------------------------------------\n\nexport const ConventionCategorySchema = z.enum([\n \"stack\",\n \"naming\",\n \"architecture\",\n \"error_handling\",\n \"testing\",\n \"imports\",\n \"other\",\n]);\n\nexport const EvidenceRefSchema = z.object({\n file: z.string(),\n line: z.number().int().positive().nullable(),\n});\n\nexport const ConventionEntrySchema = z.object({\n id: z.uuid(),\n category: ConventionCategorySchema,\n pattern: z.string().min(1),\n confidence: z.number().min(0).max(1),\n evidence: z.array(EvidenceRefSchema),\n metadata: z.record(z.string(), z.unknown()).optional(),\n});\n\nexport const StackInfoSchema = z.object({\n language: z.string(),\n framework: z.string().optional(),\n testRunner: z.string().optional(),\n buildTool: z.string().optional(),\n packageManager: z.string().optional(),\n nodeVersion: z.string().optional(),\n});\n\nexport const ArchitectureInfoSchema = z.object({\n pattern: z.string().optional(),\n layers: z.array(z.string()),\n entryPoints: z.array(z.string()).optional(),\n});\n\n// ---------------------------------------------------------------------------\n// Root registry schema\n// ---------------------------------------------------------------------------\n\nexport const ConventionRegistrySchema = z.object({\n version: z.literal(\"1\"),\n projectPath: z.string(),\n generatedAt: z.string().datetime(),\n stack: StackInfoSchema,\n conventions: z.array(ConventionEntrySchema),\n architecture: ArchitectureInfoSchema,\n metadata: z.record(z.string(), z.unknown()).optional(),\n});\n\n// ---------------------------------------------------------------------------\n// Inferred types (no manual interfaces)\n// ---------------------------------------------------------------------------\n\nexport type ConventionCategory = z.infer<typeof ConventionCategorySchema>;\nexport type EvidenceRef = z.infer<typeof EvidenceRefSchema>;\nexport type ConventionEntry = z.infer<typeof ConventionEntrySchema>;\nexport type StackInfo = z.infer<typeof StackInfoSchema>;\nexport type ArchitectureInfo = z.infer<typeof ArchitectureInfoSchema>;\nexport type ConventionRegistry = z.infer<typeof ConventionRegistrySchema>;\n","import {\n type ConventionEntry,\n type ConventionRegistry,\n ConventionRegistrySchema,\n} from \"./schema.js\";\n\n/**\n * Create a new empty ConventionRegistry for the given project path.\n * The returned registry passes ConventionRegistrySchema validation.\n */\nexport function createRegistry(projectPath: string): ConventionRegistry {\n const registry: ConventionRegistry = {\n version: \"1\",\n projectPath,\n generatedAt: new Date().toISOString(),\n stack: {\n language: \"unknown\",\n },\n conventions: [],\n architecture: {\n layers: [],\n },\n };\n\n const result = ConventionRegistrySchema.safeParse(registry);\n if (!result.success) {\n throw new Error(\n `createRegistry produced invalid registry: ${JSON.stringify(result.error.issues)}`\n );\n }\n\n return result.data;\n}\n\n/**\n * Add a convention entry to the registry, auto-generating a UUID for the id.\n * Returns a new registry (does not mutate the input).\n */\nexport function addConvention(\n registry: ConventionRegistry,\n entry: Omit<ConventionEntry, \"id\">\n): ConventionRegistry {\n const newEntry: ConventionEntry = {\n id: crypto.randomUUID(),\n ...entry,\n };\n\n const updated: ConventionRegistry = {\n ...registry,\n conventions: [...registry.conventions, newEntry],\n };\n\n const result = ConventionRegistrySchema.safeParse(updated);\n if (!result.success) {\n throw new Error(\n `addConvention produced invalid registry: ${JSON.stringify(result.error.issues)}`\n );\n }\n\n return result.data;\n}\n","import { addConvention } from \"../core/registry.js\";\nimport type { ConventionRegistry } from \"../core/schema.js\";\nimport type { Extractor, ExtractionContext } from \"./types.js\";\n\n/**\n * Run all extractors in parallel via Promise.allSettled so that a single\n * failing extractor does not abort the others.\n *\n * Fulfilled entries are added to the registry immutably via `addConvention`.\n * Rejected extractors emit a console.warn and are skipped.\n */\nexport async function runExtractors(\n extractors: Extractor[],\n ctx: ExtractionContext,\n registry: ConventionRegistry\n): Promise<ConventionRegistry> {\n const results = await Promise.allSettled(\n extractors.map((e) => e.extract(ctx).then((entries) => ({ extractor: e, entries })))\n );\n\n let current = registry;\n\n for (const [i, result] of results.entries()) {\n if (result.status === \"fulfilled\") {\n for (const entry of result.value.entries) {\n current = addConvention(current, entry);\n }\n } else {\n console.warn(\n `[runExtractors] Extractor \"${extractors[i]!.name}\" failed:`,\n result.reason\n );\n }\n }\n\n return current;\n}\n","import { access, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { ConventionEntry } from \"../../core/schema.js\";\nimport type { Extractor, ExtractionContext } from \"../types.js\";\n\n// ---------------------------------------------------------------------------\n// Detection maps\n// ---------------------------------------------------------------------------\n\nconst FRAMEWORK_MAP: Record<string, string> = {\n react: \"React\",\n vue: \"Vue\",\n \"@angular/core\": \"Angular\",\n next: \"Next.js\",\n nuxt: \"Nuxt\",\n svelte: \"Svelte\",\n hono: \"Hono\",\n express: \"Express\",\n fastify: \"Fastify\",\n koa: \"Koa\",\n};\n\nconst TEST_RUNNER_MAP: Record<string, string> = {\n vitest: \"Vitest\",\n jest: \"Jest\",\n mocha: \"Mocha\",\n jasmine: \"Jasmine\",\n ava: \"Ava\",\n};\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\ntype PackageJson = {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n scripts?: Record<string, string>;\n type?: string;\n packageManager?: string;\n};\n\nfunction allDeps(pkg: PackageJson): Record<string, string> {\n return { ...pkg.dependencies, ...pkg.devDependencies };\n}\n\ntype Entry = Omit<ConventionEntry, \"id\">;\n\nconst EVIDENCE = [{ file: \"package.json\", line: null }];\n\n// ---------------------------------------------------------------------------\n// Extractor\n// ---------------------------------------------------------------------------\n\nexport const packageJsonExtractor: Extractor = {\n name: \"package-json\",\n\n async extract(ctx: ExtractionContext): Promise<Entry[]> {\n const filePath = join(ctx.projectPath, \"package.json\");\n\n try {\n await access(filePath);\n } catch {\n return [];\n }\n\n let pkg: PackageJson;\n try {\n const raw = await readFile(filePath, \"utf-8\");\n pkg = JSON.parse(raw) as PackageJson;\n } catch {\n return [];\n }\n\n const deps = allDeps(pkg);\n const entries: Entry[] = [];\n\n // --- Language detection ---\n const isTypeScript =\n \"typescript\" in deps || \"@types/node\" in deps;\n const language = isTypeScript ? \"TypeScript\" : \"JavaScript\";\n entries.push({\n category: \"stack\",\n pattern: `Language: ${language}`,\n confidence: 0.95,\n evidence: EVIDENCE,\n metadata: { language },\n });\n\n // --- Framework detection ---\n for (const [pkg_name, label] of Object.entries(FRAMEWORK_MAP)) {\n if (pkg_name in deps) {\n const version = deps[pkg_name];\n entries.push({\n category: \"stack\",\n pattern: `Framework: ${label}`,\n confidence: 1.0,\n evidence: EVIDENCE,\n metadata: { framework: label, version },\n });\n break; // first match wins\n }\n }\n\n // --- Test runner detection ---\n for (const [pkg_name, label] of Object.entries(TEST_RUNNER_MAP)) {\n if (pkg_name in deps) {\n entries.push({\n category: \"testing\",\n pattern: `Test runner: ${label}`,\n confidence: 0.95,\n evidence: EVIDENCE,\n metadata: { testRunner: label },\n });\n break; // first match wins\n }\n }\n\n // --- Package manager detection (corepack \"packageManager\" field) ---\n if (typeof pkg.packageManager === \"string\") {\n const pmName = pkg.packageManager.split(\"@\")[0];\n if (pmName) {\n entries.push({\n category: \"stack\",\n pattern: `Package manager: ${pmName}`,\n confidence: 1.0,\n evidence: EVIDENCE,\n metadata: { packageManager: pmName },\n });\n }\n }\n\n // --- ESM module system detection ---\n if (pkg.type === \"module\") {\n entries.push({\n category: \"imports\",\n pattern: `ES modules (package.json \"type\": \"module\")`,\n confidence: 1.0,\n evidence: EVIDENCE,\n metadata: { moduleSystem: \"esm\" },\n });\n }\n\n // --- Scripts ---\n const scripts = pkg.scripts ?? {};\n for (const [scriptName, command] of Object.entries(scripts)) {\n const isTestScript =\n scriptName === \"test\" || scriptName.startsWith(\"test:\");\n const isBuildOrLint =\n scriptName === \"build\" || scriptName === \"lint\";\n\n if (isTestScript || isBuildOrLint) {\n entries.push({\n category: isTestScript ? \"testing\" : \"stack\",\n pattern: `Script \"${scriptName}\": ${command}`,\n confidence: 1.0,\n evidence: EVIDENCE,\n metadata: { scriptName, command },\n });\n }\n }\n\n return entries;\n },\n};\n","import { access } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { ConventionEntry } from \"../../core/schema.js\";\nimport type { Extractor, ExtractionContext } from \"../types.js\";\n\n// ---------------------------------------------------------------------------\n// Lockfile → package manager mapping (priority order)\n// ---------------------------------------------------------------------------\n\nconst LOCKFILES: Array<{ file: string; manager: string }> = [\n { file: \"bun.lock\", manager: \"bun\" },\n { file: \"bun.lockb\", manager: \"bun\" },\n { file: \"pnpm-lock.yaml\", manager: \"pnpm\" },\n { file: \"yarn.lock\", manager: \"yarn\" },\n { file: \"package-lock.json\", manager: \"npm\" },\n];\n\n// ---------------------------------------------------------------------------\n// Extractor\n// ---------------------------------------------------------------------------\n\ntype Entry = Omit<ConventionEntry, \"id\">;\n\nexport const lockfileExtractor: Extractor = {\n name: \"lockfile\",\n\n async extract(ctx: ExtractionContext): Promise<Entry[]> {\n for (const { file, manager } of LOCKFILES) {\n try {\n await access(join(ctx.projectPath, file));\n return [\n {\n category: \"stack\",\n pattern: `Package manager: ${manager}`,\n confidence: 1.0,\n evidence: [{ file, line: null }],\n metadata: { packageManager: manager },\n },\n ];\n } catch {\n // not found, try next\n }\n }\n\n return [];\n },\n};\n","import { access, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { ConventionEntry } from \"../../core/schema.js\";\nimport type { Extractor, ExtractionContext } from \"../types.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype CompilerOptions = {\n strict?: boolean;\n target?: string;\n module?: string;\n moduleResolution?: string;\n paths?: Record<string, string[]>;\n [key: string]: unknown;\n};\n\ntype TsConfig = {\n compilerOptions?: CompilerOptions;\n [key: string]: unknown;\n};\n\ntype Entry = Omit<ConventionEntry, \"id\">;\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nconst EVIDENCE = [{ file: \"tsconfig.json\", line: null }];\n\n/**\n * Strip single-line // comments and trailing commas so JSON.parse accepts\n * tsconfig.json files that use non-standard JSON.\n */\nfunction stripTsConfigNonStandardJson(raw: string): string {\n return raw\n // Remove single-line comments (// ...)\n .replace(/\\/\\/[^\\n]*/g, \"\")\n // Remove trailing commas before ] or }\n .replace(/,(\\s*[}\\]])/g, \"$1\");\n}\n\n// ---------------------------------------------------------------------------\n// Extractor\n// ---------------------------------------------------------------------------\n\nexport const tsconfigExtractor: Extractor = {\n name: \"tsconfig\",\n\n async extract(ctx: ExtractionContext): Promise<Entry[]> {\n const filePath = join(ctx.projectPath, \"tsconfig.json\");\n\n try {\n await access(filePath);\n } catch {\n return [];\n }\n\n let config: TsConfig;\n try {\n const raw = await readFile(filePath, \"utf-8\");\n config = JSON.parse(stripTsConfigNonStandardJson(raw)) as TsConfig;\n } catch {\n return [];\n }\n\n const co = config.compilerOptions ?? {};\n const entries: Entry[] = [];\n\n // --- Strict mode ---\n if (co.strict === true) {\n entries.push({\n category: \"stack\",\n pattern: \"TypeScript strict mode enabled\",\n confidence: 1.0,\n evidence: EVIDENCE,\n metadata: { strict: true },\n });\n }\n\n // --- Notable compiler options ---\n const notable: Record<string, unknown> = {};\n for (const key of [\"target\", \"module\", \"moduleResolution\"] as const) {\n if (co[key] !== undefined) {\n notable[key] = co[key];\n }\n }\n if (Object.keys(notable).length > 0) {\n entries.push({\n category: \"stack\",\n pattern: \"TypeScript compiler options configured\",\n confidence: 1.0,\n evidence: EVIDENCE,\n metadata: notable,\n });\n }\n\n // --- Path aliases ---\n if (co.paths && Object.keys(co.paths).length > 0) {\n entries.push({\n category: \"imports\",\n pattern: \"Uses TypeScript path aliases\",\n confidence: 1.0,\n evidence: EVIDENCE,\n metadata: { aliases: co.paths },\n });\n }\n\n return entries;\n },\n};\n","import { access, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { ConventionEntry } from \"../../core/schema.js\";\nimport type { Extractor, ExtractionContext } from \"../types.js\";\n\ntype Entry = Omit<ConventionEntry, \"id\">;\n\nexport const goModExtractor: Extractor = {\n name: \"go-mod\",\n\n async extract(ctx: ExtractionContext): Promise<Entry[]> {\n const filePath = join(ctx.projectPath, \"go.mod\");\n\n try {\n await access(filePath);\n } catch {\n return [];\n }\n\n let raw: string;\n try {\n raw = await readFile(filePath, \"utf-8\");\n } catch {\n return [];\n }\n\n const lines = raw.split(\"\\n\");\n\n let moduleName = \"\";\n let goVersion = \"\";\n let dependencyCount = 0;\n let inRequireBlock = false;\n\n for (const line of lines) {\n const trimmed = line.trim();\n\n if (!moduleName) {\n const moduleMatch = trimmed.match(/^module\\s+(\\S+)/);\n if (moduleMatch?.[1]) {\n moduleName = moduleMatch[1];\n continue;\n }\n }\n\n if (!goVersion) {\n const goMatch = trimmed.match(/^go\\s+(\\S+)/);\n if (goMatch?.[1]) {\n goVersion = goMatch[1];\n continue;\n }\n }\n\n // Count deps in require blocks\n if (trimmed === \"require (\") {\n inRequireBlock = true;\n continue;\n }\n if (inRequireBlock) {\n if (trimmed === \")\") {\n inRequireBlock = false;\n } else if (trimmed.length > 0 && !trimmed.startsWith(\"//\")) {\n dependencyCount++;\n }\n continue;\n }\n // Single-line require\n if (trimmed.match(/^require\\s+\\S+\\s+v\\S+/)) {\n dependencyCount++;\n }\n }\n\n if (!moduleName) {\n return [];\n }\n\n return [\n {\n category: \"stack\",\n pattern: `Go project (${moduleName})`,\n confidence: 1.0,\n evidence: [{ file: \"go.mod\", line: null }],\n metadata: {\n language: \"Go\",\n moduleName,\n goVersion: goVersion || null,\n dependencyCount,\n },\n },\n ];\n },\n};\n","import { access, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { parse as parseToml } from \"smol-toml\";\nimport type { ConventionEntry } from \"../../core/schema.js\";\nimport type { Extractor, ExtractionContext } from \"../types.js\";\n\ntype Entry = Omit<ConventionEntry, \"id\">;\n\ntype CargoToml = {\n package?: { name?: string; version?: string };\n dependencies?: Record<string, unknown>;\n [key: string]: unknown;\n};\n\nexport const cargoTomlExtractor: Extractor = {\n name: \"cargo-toml\",\n\n async extract(ctx: ExtractionContext): Promise<Entry[]> {\n const filePath = join(ctx.projectPath, \"Cargo.toml\");\n\n try {\n await access(filePath);\n } catch {\n return [];\n }\n\n let cargo: CargoToml;\n try {\n const raw = await readFile(filePath, \"utf-8\");\n cargo = parseToml(raw) as CargoToml;\n } catch {\n return [];\n }\n\n const packageName = cargo.package?.name;\n if (!packageName) {\n return [];\n }\n\n const dependencyCount = Object.keys(cargo.dependencies ?? {}).length;\n\n return [\n {\n category: \"stack\",\n pattern: `Rust project (${packageName})`,\n confidence: 1.0,\n evidence: [{ file: \"Cargo.toml\", line: null }],\n metadata: {\n language: \"Rust\",\n packageName,\n dependencyCount,\n },\n },\n ];\n },\n};\n","import { readFile } from \"node:fs/promises\";\nimport { join, relative } from \"node:path\";\nimport { globby } from \"globby\";\nimport { load as yamlLoad } from \"js-yaml\";\nimport type { ConventionEntry } from \"../../core/schema.js\";\nimport type { Extractor, ExtractionContext } from \"../types.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype Entry = Omit<ConventionEntry, \"id\">;\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\ntype CommandCategory = \"stack\" | \"testing\";\n\ninterface CommandMatch {\n command: string;\n category: CommandCategory;\n ciFile: string;\n}\n\nconst BUILD_KEYWORDS = [\"build\", \"compile\", \"tsc\", \"tsdown\"];\nconst TEST_KEYWORDS = [\"test\", \"vitest\", \"jest\", \"pytest\", \"cargo test\", \"go test\"];\nconst LINT_KEYWORDS = [\"lint\", \"eslint\", \"biome\", \"clippy\", \"ruff\"];\n\nfunction categorizeCommand(cmd: string): CommandCategory | null {\n const lower = cmd.toLowerCase();\n if (TEST_KEYWORDS.some((kw) => lower.includes(kw))) return \"testing\";\n if (BUILD_KEYWORDS.some((kw) => lower.includes(kw))) return \"stack\";\n if (LINT_KEYWORDS.some((kw) => lower.includes(kw))) return \"stack\";\n return null;\n}\n\n/**\n * Extract run commands from a GitHub Actions workflow YAML.\n * Traverses jobs.*.steps[].run\n */\nfunction extractGithubActionsCommands(doc: unknown, filePath: string): { matched: CommandMatch[]; raw: string[] } {\n const matched: CommandMatch[] = [];\n const raw: string[] = [];\n\n if (!doc || typeof doc !== \"object\") return { matched, raw };\n const record = doc as Record<string, unknown>;\n const jobs = record[\"jobs\"];\n if (!jobs || typeof jobs !== \"object\") return { matched, raw };\n\n for (const job of Object.values(jobs as Record<string, unknown>)) {\n if (!job || typeof job !== \"object\") continue;\n const steps = (job as Record<string, unknown>)[\"steps\"];\n if (!Array.isArray(steps)) continue;\n for (const step of steps) {\n if (!step || typeof step !== \"object\") continue;\n const run = (step as Record<string, unknown>)[\"run\"];\n if (typeof run !== \"string\") continue;\n // A single run block may contain multiple lines\n for (const line of run.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n raw.push(trimmed);\n const category = categorizeCommand(trimmed);\n if (category !== null) {\n matched.push({ command: trimmed, category, ciFile: filePath });\n }\n }\n }\n }\n\n return { matched, raw };\n}\n\n/** Keys that are not job definitions in GitLab CI top-level. */\nconst GITLAB_RESERVED = new Set([\"stages\", \"variables\", \"include\", \"default\", \"workflow\", \"image\", \"services\", \"before_script\", \"after_script\", \"cache\", \"artifacts\"]);\n\n/**\n * Extract script commands from a GitLab CI YAML.\n * Traverses top-level job keys (skipping reserved keys), looks for script arrays.\n */\nfunction extractGitlabCiCommands(doc: unknown, filePath: string): { matched: CommandMatch[]; raw: string[] } {\n const matched: CommandMatch[] = [];\n const raw: string[] = [];\n\n if (!doc || typeof doc !== \"object\") return { matched, raw };\n\n for (const [key, job] of Object.entries(doc as Record<string, unknown>)) {\n if (GITLAB_RESERVED.has(key)) continue;\n if (!job || typeof job !== \"object\") continue;\n const script = (job as Record<string, unknown>)[\"script\"];\n const scripts = Array.isArray(script) ? script : typeof script === \"string\" ? [script] : [];\n for (const cmd of scripts) {\n if (typeof cmd !== \"string\") continue;\n const trimmed = cmd.trim();\n if (!trimmed) continue;\n raw.push(trimmed);\n const category = categorizeCommand(trimmed);\n if (category !== null) {\n matched.push({ command: trimmed, category, ciFile: filePath });\n }\n }\n }\n\n return { matched, raw };\n}\n\n// ---------------------------------------------------------------------------\n// Extractor\n// ---------------------------------------------------------------------------\n\nexport const ciExtractor: Extractor = {\n name: \"ci\",\n\n async extract(ctx: ExtractionContext): Promise<Entry[]> {\n const ciPatterns = [\n \".github/workflows/*.yml\",\n \".github/workflows/*.yaml\",\n \".gitlab-ci.yml\",\n ];\n\n const ciFiles = await globby(ciPatterns, {\n cwd: ctx.projectPath,\n gitignore: false,\n followSymbolicLinks: false,\n absolute: false,\n });\n\n if (ciFiles.length === 0) return [];\n\n const entries: Entry[] = [];\n\n for (const relPath of ciFiles) {\n const absPath = join(ctx.projectPath, relPath);\n let raw: string;\n try {\n raw = await readFile(absPath, \"utf-8\");\n } catch {\n continue;\n }\n\n let doc: unknown;\n try {\n doc = yamlLoad(raw);\n } catch {\n // Malformed YAML: skip file, no throw\n continue;\n }\n\n const isGitlab = relPath.includes(\".gitlab-ci\");\n const { matched, raw: rawCmds } = isGitlab\n ? extractGitlabCiCommands(doc, relPath)\n : extractGithubActionsCommands(doc, relPath);\n\n // Store all raw commands in a single metadata entry per file\n if (rawCmds.length > 0 || matched.length > 0) {\n for (const { command, category } of matched) {\n entries.push({\n category,\n pattern: `CI command: ${command}`,\n confidence: 0.9,\n evidence: [{ file: relative(ctx.projectPath, absPath) || relPath, line: null }],\n metadata: { command, ciFile: relPath, rawCommands: rawCmds },\n });\n }\n }\n }\n\n return entries;\n },\n};\n","import { globby } from \"globby\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Directories and paths that are always excluded from file listings. */\nexport const ALWAYS_SKIP: readonly string[] = [\n \"**/node_modules/**\",\n \"**/dist/**\",\n \"**/generated/**\",\n \"**/.ez-search/**\",\n \"**/.ez-context/**\",\n \"**/.git/**\",\n];\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface ListFilesOptions {\n /** The project root to search from. */\n cwd: string;\n /** File extensions to include (without dot). Defaults to ts, js, json, md. */\n extensions?: string[];\n /** Additional ignore patterns (glob syntax). */\n additionalIgnore?: string[];\n}\n\n// ---------------------------------------------------------------------------\n// Utility\n// ---------------------------------------------------------------------------\n\n/**\n * List project files while respecting .gitignore (INTG-04) and always\n * skipping common generated/build directories.\n *\n * @returns Relative paths sorted alphabetically.\n */\nexport async function listProjectFiles(\n options: ListFilesOptions\n): Promise<string[]> {\n const {\n cwd,\n extensions = [\"ts\", \"js\", \"json\", \"md\"],\n additionalIgnore = [],\n } = options;\n\n const extPattern =\n extensions.length === 1\n ? `**/*.${extensions[0]}`\n : `**/*.{${extensions.join(\",\")}}`;\n\n const files = await globby(extPattern, {\n cwd,\n gitignore: true,\n ignore: [...ALWAYS_SKIP, ...additionalIgnore],\n followSymbolicLinks: false,\n absolute: false,\n });\n\n return files.sort();\n}\n","import { globby } from \"globby\";\nimport { ALWAYS_SKIP } from \"../../utils/fs.js\";\nimport type { ConventionEntry } from \"../../core/schema.js\";\nimport type { Extractor, ExtractionContext } from \"../types.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype Entry = Omit<ConventionEntry, \"id\">;\n\ninterface TestPattern {\n glob: string;\n location: string;\n style: string;\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Top-level test directory prefixes — files here are directory-based, not co-located. */\nconst TEST_DIR_PREFIXES = [\"test/\", \"tests/\", \"__tests__/\"];\n\nconst TEST_PATTERNS: TestPattern[] = [\n { glob: \"**/*.test.{ts,tsx,js,jsx}\", location: \"co-located\", style: \"*.test.ts style\" },\n { glob: \"**/*.spec.{ts,tsx,js,jsx}\", location: \"co-located\", style: \"*.spec.ts style\" },\n { glob: \"test/**/*.{ts,tsx,js,jsx}\", location: \"test/ directory\", style: \"test/ directory\" },\n { glob: \"tests/**/*.{ts,tsx,js,jsx}\", location: \"tests/ directory\", style: \"tests/ directory\" },\n { glob: \"__tests__/**/*.{ts,tsx,js,jsx}\", location: \"__tests__/ directory\", style: \"__tests__/ directory\" },\n];\n\n// ---------------------------------------------------------------------------\n// Extractor\n// ---------------------------------------------------------------------------\n\nexport const projectStructureExtractor: Extractor = {\n name: \"project-structure\",\n\n async extract(ctx: ExtractionContext): Promise<Entry[]> {\n const entries: Entry[] = [];\n\n for (const { glob, location, style } of TEST_PATTERNS) {\n let matches = await globby(glob, {\n cwd: ctx.projectPath,\n gitignore: true,\n ignore: [...ALWAYS_SKIP],\n followSymbolicLinks: false,\n absolute: false,\n });\n\n // For co-located patterns, exclude files that live in dedicated test directories\n if (location === \"co-located\") {\n matches = matches.filter(\n (f) => !TEST_DIR_PREFIXES.some((prefix) => f.startsWith(prefix))\n );\n }\n\n if (matches.length === 0) continue;\n\n const count = matches.length;\n const confidence = Math.min(0.95, 0.5 + count * 0.05);\n const evidenceFiles = matches.slice(0, 5);\n\n entries.push({\n category: \"testing\",\n pattern: `Test files in ${location} (${style})`,\n confidence,\n evidence: evidenceFiles.map((f) => ({ file: f, line: null })),\n metadata: {\n testFileCount: count,\n location,\n style,\n },\n });\n }\n\n return entries;\n },\n};\n","import { Project } from \"ts-morph\";\nimport { listProjectFiles } from \"../../utils/fs.js\";\nimport type { ConventionEntry } from \"../../core/schema.js\";\nimport type { Extractor, ExtractionContext } from \"../types.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype Entry = Omit<ConventionEntry, \"id\">;\ntype CaseKind = \"camelCase\" | \"PascalCase\" | \"snake_case\" | \"UPPER_SNAKE_CASE\";\n\n// ---------------------------------------------------------------------------\n// Case classification\n// ---------------------------------------------------------------------------\n\n/**\n * Classify the naming convention of a single identifier.\n * Returns null for short names (< 4 chars) or unclassifiable names.\n */\nfunction classifyCase(name: string): CaseKind | null {\n if (name.length < 4) return null;\n\n if (/^[A-Z][A-Z0-9_]{3,}$/.test(name)) return \"UPPER_SNAKE_CASE\";\n if (/^[A-Z][a-zA-Z0-9]+$/.test(name)) return \"PascalCase\";\n if (/^[a-z][a-z0-9_]+$/.test(name) && name.includes(\"_\")) return \"snake_case\";\n if (/^[a-z][a-zA-Z0-9]+$/.test(name) && /[A-Z]/.test(name)) return \"camelCase\";\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Extractor\n// ---------------------------------------------------------------------------\n\nexport const namingExtractor: Extractor = {\n name: \"naming\",\n\n async extract(ctx: ExtractionContext): Promise<Entry[]> {\n const files = await listProjectFiles({\n cwd: ctx.projectPath,\n extensions: [\"ts\", \"tsx\", \"js\", \"jsx\"],\n });\n\n if (files.length === 0) return [];\n\n const maxFiles = ctx.options?.maxFilesForAst ?? 200;\n const filesToAnalyse = files.slice(0, maxFiles);\n\n const project = new Project({\n compilerOptions: { allowJs: true, noEmit: true },\n skipFileDependencyResolution: true,\n });\n\n // Absolute paths needed for ts-morph\n const absPaths = filesToAnalyse.map((f) => `${ctx.projectPath}/${f}`);\n project.addSourceFilesAtPaths(absPaths);\n\n // Track counts: { functions, variables, classes } -> { CaseKind -> count }\n const functions: Record<string, number> = {};\n const variables: Record<string, number> = {};\n const classes: Record<string, number> = {};\n const counts: Record<string, Record<string, number>> = { functions, variables, classes };\n\n for (const sf of project.getSourceFiles()) {\n for (const fn of sf.getFunctions()) {\n const name = fn.getName();\n if (!name) continue;\n const kind = classifyCase(name);\n if (kind) functions[kind] = (functions[kind] ?? 0) + 1;\n }\n\n for (const decl of sf.getVariableDeclarations()) {\n const name = decl.getName();\n const kind = classifyCase(name);\n if (kind) variables[kind] = (variables[kind] ?? 0) + 1;\n }\n\n for (const cls of sf.getClasses()) {\n const name = cls.getName();\n if (!name) continue;\n const kind = classifyCase(name);\n if (kind) classes[kind] = (classes[kind] ?? 0) + 1;\n }\n }\n\n const entries: Entry[] = [];\n\n for (const [entityType, caseCounts] of Object.entries(counts)) {\n const total = Object.values(caseCounts).reduce((a, b) => a + b, 0);\n if (total < 3) continue;\n\n // Find dominant case\n let dominant: CaseKind | null = null;\n let dominantCount = 0;\n for (const [kind, count] of Object.entries(caseCounts)) {\n if (count > dominantCount) {\n dominant = kind as CaseKind;\n dominantCount = count;\n }\n }\n\n if (!dominant) continue;\n\n const confidence = Math.min(0.95, dominantCount / total);\n if (confidence < 0.6) continue;\n\n entries.push({\n category: \"naming\",\n pattern: `${entityType} use ${dominant} naming`,\n confidence,\n evidence: [{ file: \"src/**/*.ts\", line: null }],\n metadata: {\n entityType,\n dominantCase: dominant,\n counts: caseCounts,\n sampleSize: total,\n },\n });\n }\n\n return entries;\n },\n};\n","import { join, dirname, resolve } from \"node:path\";\nimport { Project } from \"ts-morph\";\nimport { listProjectFiles } from \"../../utils/fs.js\";\nimport type { ConventionEntry } from \"../../core/schema.js\";\nimport type { Extractor, ExtractionContext } from \"../types.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype Entry = Omit<ConventionEntry, \"id\">;\ntype SourceFile = ReturnType<InstanceType<typeof Project>[\"getSourceFiles\"]>[number];\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Known path alias prefixes used across JS/TS ecosystems. */\nconst ALIAS_PREFIXES = [\"@/\", \"~/\", \"#/\", \"$lib/\"];\n\nfunction hasPathAlias(specifier: string): boolean {\n return ALIAS_PREFIXES.some((prefix) => specifier.startsWith(prefix));\n}\n\n/**\n * Check whether a source file looks like a barrel file:\n * - Has at least one export declaration\n * - Has no function, class, or variable declarations\n */\nfunction isBarrelFile(sourceFile: SourceFile): boolean {\n const hasExports = sourceFile.getExportDeclarations().length > 0;\n if (!hasExports) return false;\n const hasFunctions = sourceFile.getFunctions().length > 0;\n const hasClasses = sourceFile.getClasses().length > 0;\n const hasVars = sourceFile.getVariableDeclarations().length > 0;\n return !hasFunctions && !hasClasses && !hasVars;\n}\n\n/**\n * Build a set of absolute paths for barrel files (index.ts/js variants).\n * Since skipFileDependencyResolution prevents module resolution,\n * we identify barrel files upfront and match imports by path.\n */\nfunction buildBarrelFileSet(project: Project): Set<string> {\n const barrels = new Set<string>();\n for (const sf of project.getSourceFiles()) {\n const filePath = sf.getFilePath();\n const baseName = filePath.split(\"/\").pop() ?? \"\";\n // Only index files can be barrel files (index.ts, index.tsx, index.js, index.jsx)\n if (/^index\\.[tj]sx?$/.test(baseName) && isBarrelFile(sf)) {\n barrels.add(filePath);\n }\n }\n return barrels;\n}\n\n/** Resolve a relative import specifier to candidate absolute paths. */\nfunction resolveRelativeImport(importingFile: string, specifier: string): string[] {\n const dir = dirname(importingFile);\n const base = resolve(dir, specifier);\n const exts = [\".ts\", \".tsx\", \".js\", \".jsx\"];\n const candidates: string[] = [];\n // Direct file: ./foo -> ./foo.ts etc\n for (const ext of exts) candidates.push(base + ext);\n // Directory index: ./foo -> ./foo/index.ts etc\n for (const ext of exts) candidates.push(join(base, \"index\" + ext));\n return candidates;\n}\n\n// ---------------------------------------------------------------------------\n// Extractor\n// ---------------------------------------------------------------------------\n\nexport const importsExtractor: Extractor = {\n name: \"imports\",\n\n async extract(ctx: ExtractionContext): Promise<Entry[]> {\n const files = await listProjectFiles({\n cwd: ctx.projectPath,\n extensions: [\"ts\", \"tsx\", \"js\", \"jsx\"],\n });\n\n if (files.length === 0) return [];\n\n const maxFiles = ctx.options?.maxFilesForAst ?? 200;\n const filesToAnalyse = files.slice(0, maxFiles);\n\n const project = new Project({\n compilerOptions: { allowJs: true, noEmit: true },\n skipFileDependencyResolution: true,\n });\n\n const absPaths = filesToAnalyse.map((f) => `${ctx.projectPath}/${f}`);\n project.addSourceFilesAtPaths(absPaths);\n\n // Pre-compute barrel file set for path-based matching\n const barrelFiles = buildBarrelFileSet(project);\n\n let relativeCount = 0;\n let externalCount = 0;\n let barrelCount = 0;\n let aliasCount = 0;\n\n for (const sf of project.getSourceFiles()) {\n for (const imp of sf.getImportDeclarations()) {\n const specifier = imp.getModuleSpecifierValue();\n\n if (imp.isModuleSpecifierRelative()) {\n relativeCount++;\n\n // Barrel detection via path heuristic\n const candidates = resolveRelativeImport(sf.getFilePath(), specifier);\n if (candidates.some((c) => barrelFiles.has(c))) {\n barrelCount++;\n }\n } else {\n externalCount++;\n\n if (hasPathAlias(specifier)) {\n aliasCount++;\n }\n }\n }\n }\n\n const totalImports = relativeCount + externalCount;\n if (totalImports === 0) return [];\n\n const entries: Entry[] = [];\n const evidence = [{ file: \"src/**/*.ts\", line: null }];\n\n // --- Import organization pattern ---\n const relRatio = relativeCount / totalImports;\n let orgPattern: string;\n if (relRatio >= 0.75) {\n orgPattern = \"Predominantly relative imports\";\n } else if (relRatio <= 0.25) {\n orgPattern = \"Predominantly external imports\";\n } else {\n orgPattern = \"Mix of relative and external imports\";\n }\n\n // Confidence scales with sample size (saturates at 100 imports)\n const sizeConfidence = Math.min(0.95, 0.5 + (totalImports / 100) * 0.45);\n\n entries.push({\n category: \"imports\",\n pattern: orgPattern,\n confidence: sizeConfidence,\n evidence,\n metadata: {\n relativeCount,\n externalCount,\n totalImports,\n relativeRatio: Math.round(relRatio * 100) / 100,\n },\n });\n\n // --- Barrel file usage ---\n if (barrelCount > 0) {\n const barrelRatio = barrelCount / relativeCount;\n entries.push({\n category: \"imports\",\n pattern: \"Uses barrel file (index) imports\",\n confidence: Math.min(0.95, 0.5 + barrelRatio * 0.45),\n evidence,\n metadata: { barrelCount, relativeCount },\n });\n }\n\n // --- Path alias usage ---\n if (aliasCount > 0) {\n entries.push({\n category: \"imports\",\n pattern: \"Uses path aliases (@/ prefix)\",\n confidence: 1.0,\n evidence,\n metadata: { aliasCount },\n });\n }\n\n return entries;\n },\n};\n","import { Project, SyntaxKind } from \"ts-morph\";\nimport { listProjectFiles } from \"../../utils/fs.js\";\nimport type { ConventionEntry } from \"../../core/schema.js\";\nimport type { Extractor, ExtractionContext } from \"../types.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype Entry = Omit<ConventionEntry, \"id\">;\n\n// ---------------------------------------------------------------------------\n// Extractor\n// ---------------------------------------------------------------------------\n\nexport const staticErrorHandlingExtractor: Extractor = {\n name: \"static-error-handling\",\n\n async extract(ctx: ExtractionContext): Promise<Entry[]> {\n const files = await listProjectFiles({\n cwd: ctx.projectPath,\n extensions: [\"ts\", \"tsx\", \"js\", \"jsx\"],\n });\n\n if (files.length === 0) return [];\n\n const maxFiles = ctx.options?.maxFilesForAst ?? 200;\n const filesToAnalyse = files.slice(0, maxFiles);\n\n const project = new Project({\n compilerOptions: { allowJs: true, noEmit: true },\n skipFileDependencyResolution: true,\n });\n\n const absPaths = filesToAnalyse.map((f) => `${ctx.projectPath}/${f}`);\n project.addSourceFilesAtPaths(absPaths);\n\n let tryCatchFileCount = 0;\n let tryCatchTotalCount = 0;\n let customErrorClassCount = 0;\n const tryCatchEvidence: string[] = [];\n const customErrorEvidence: string[] = [];\n\n for (const sf of project.getSourceFiles()) {\n const relPath = sf.getFilePath().replace(ctx.projectPath + \"/\", \"\");\n\n const tryStatements = sf.getDescendantsOfKind(SyntaxKind.TryStatement);\n if (tryStatements.length > 0) {\n tryCatchFileCount++;\n tryCatchTotalCount += tryStatements.length;\n if (tryCatchEvidence.length < 5) tryCatchEvidence.push(relPath);\n }\n\n for (const cls of sf.getClasses()) {\n const heritage = cls.getExtends();\n if (heritage && /\\bError\\b/.test(heritage.getText())) {\n customErrorClassCount++;\n if (customErrorEvidence.length < 5) customErrorEvidence.push(relPath);\n }\n }\n }\n\n const entries: Entry[] = [];\n const totalFiles = filesToAnalyse.length;\n\n // Require at least 2 try/catch occurrences (across 1+ files) for a meaningful pattern\n if (tryCatchTotalCount >= 2) {\n // Confidence based on both file spread and occurrence density\n const fileSpread = tryCatchFileCount / totalFiles;\n const densityBoost = Math.min(0.2, tryCatchTotalCount * 0.05);\n const confidence = Math.min(0.95, 0.5 + fileSpread * 0.35 + densityBoost);\n entries.push({\n category: \"error_handling\",\n pattern: \"try/catch imperative error handling\",\n confidence,\n evidence: tryCatchEvidence.map((file) => ({ file, line: null })),\n metadata: { style: \"try-catch\", fileCount: tryCatchFileCount, totalCount: tryCatchTotalCount },\n });\n }\n\n if (customErrorClassCount >= 1) {\n const confidence = Math.min(0.95, 0.5 + (customErrorClassCount / totalFiles) * 0.45);\n entries.push({\n category: \"error_handling\",\n pattern: \"custom error class hierarchy\",\n confidence,\n evidence: customErrorEvidence.map((file) => ({ file, line: null })),\n metadata: { style: \"custom-error-class\", classCount: customErrorClassCount },\n });\n }\n\n return entries;\n },\n};\n","/**\n * ez-search bridge — thin adapter over @ez-corp/ez-search.\n *\n * This is the ONLY file that imports from @ez-corp/ez-search.\n * All other modules interact with ez-search via the EzSearchBridge interface.\n */\nimport { index, query, EzSearchError } from \"@ez-corp/ez-search\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\n// ---------------------------------------------------------------------------\n// Supporting types\n// ---------------------------------------------------------------------------\n\nexport interface SearchResult {\n file: string;\n chunk: string;\n score: number;\n}\n\n// ---------------------------------------------------------------------------\n// Bridge interface\n// ---------------------------------------------------------------------------\n\nexport interface EzSearchBridge {\n /**\n * Returns true if an .ez-search/ index exists for the project directory.\n */\n hasIndex(projectPath: string): Promise<boolean>;\n\n /**\n * Ensures an index exists: indexes the project if no index is present (EXTR-10).\n */\n ensureIndex(projectPath: string): Promise<void>;\n\n /**\n * Unconditionally re-indexes the project so search results reflect current file state.\n */\n refreshIndex(projectPath: string): Promise<void>;\n\n /**\n * Semantic (and hybrid) search over the indexed project.\n */\n search(query: string, options?: { k?: number }): Promise<SearchResult[]>;\n\n /**\n * Get an embedding vector for the given text.\n * NOTE: @ez-corp/ez-search does not expose a standalone embed API.\n * This method is reserved for future use or a companion embedder.\n */\n embed(text: string): Promise<number[]>;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\nclass EzSearchBridgeImpl implements EzSearchBridge {\n constructor(private readonly projectPath: string) {}\n\n async hasIndex(projectPath: string): Promise<boolean> {\n const indexDir = join(projectPath, \".ez-search\");\n return existsSync(indexDir);\n }\n\n async ensureIndex(projectPath: string): Promise<void> {\n if (await this.hasIndex(projectPath)) {\n return;\n }\n await index(projectPath);\n }\n\n async refreshIndex(projectPath: string): Promise<void> {\n await index(projectPath, { clear: true });\n }\n\n async search(\n searchQuery: string,\n options: { k?: number } = {}\n ): Promise<SearchResult[]> {\n const { k = 10 } = options;\n\n let raw: Awaited<ReturnType<typeof query>>;\n try {\n raw = await query(searchQuery, {\n topK: k,\n projectDir: this.projectPath,\n autoIndex: false,\n });\n } catch (err: unknown) {\n if (err instanceof EzSearchError && err.code === \"NO_INDEX\") {\n return [];\n }\n throw err;\n }\n\n const results: SearchResult[] = [];\n\n for (const hit of raw.code) {\n results.push({ file: hit.file, chunk: hit.text, score: hit.score });\n }\n for (const hit of raw.text) {\n results.push({ file: hit.file, chunk: hit.text, score: hit.score });\n }\n\n results.sort((a, b) => b.score - a.score);\n return results.slice(0, k);\n }\n\n async embed(_text: string): Promise<number[]> {\n // @ez-corp/ez-search does not expose a standalone embed endpoint.\n // This is a planned capability that will be implemented when a\n // companion embedding API becomes available.\n throw new Error(\n \"embed() is not yet supported by the ez-search bridge. \" +\n \"Use search() for semantic retrieval.\"\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create an EzSearchBridge bound to the given project directory.\n */\nexport async function createBridge(\n projectPath: string\n): Promise<EzSearchBridge> {\n return new EzSearchBridgeImpl(projectPath);\n}\n","import { createBridge } from \"../../core/ez-search-bridge.js\";\nimport type { ConventionEntry } from \"../../core/schema.js\";\nimport type { Extractor, ExtractionContext } from \"../types.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype Entry = Omit<ConventionEntry, \"id\">;\n\ntype ErrorStyle = \"try-catch\" | \"result-type\" | \"custom-error-class\" | \"error-boundary\";\n\ninterface PatternDef {\n style: ErrorStyle;\n pattern: string;\n test: (content: string) => boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Pattern definitions\n// ---------------------------------------------------------------------------\n\nconst PATTERNS: PatternDef[] = [\n {\n style: \"try-catch\",\n pattern: \"try/catch imperative error handling\",\n test: (content) => /\\btry\\s*\\{/.test(content) && /\\bcatch\\s*\\(/.test(content),\n },\n {\n style: \"result-type\",\n pattern: \"Result/Either functional error handling\",\n test: (content) =>\n /\\bResult<|\\bOk\\(|\\bErr\\(|\\bisOk\\b|\\bisErr\\b|\\bneverthrow\\b/.test(content),\n },\n {\n style: \"custom-error-class\",\n pattern: \"custom error class hierarchy\",\n test: (content) => /class\\s+\\w+Error\\b|\\bnew\\s+\\w+Error\\(/.test(content),\n },\n {\n style: \"error-boundary\",\n pattern: \"React error boundary components\",\n test: (content) => /\\bErrorBoundary\\b|\\bcomponentDidCatch\\b/.test(content),\n },\n];\n\n// ---------------------------------------------------------------------------\n// Extractor\n// ---------------------------------------------------------------------------\n\nexport const errorHandlingExtractor: Extractor = {\n name: \"error-handling\",\n\n async extract(ctx: ExtractionContext): Promise<Entry[]> {\n const bridge = await createBridge(ctx.projectPath);\n\n if (!(await bridge.hasIndex(ctx.projectPath))) {\n return [];\n }\n\n // Issue targeted search queries\n const [general, resultType, customErrors] = await Promise.all([\n bridge.search(\"error handling try catch throw exception\", { k: 30 }),\n bridge.search(\"Result Ok Err return error value\", { k: 20 }),\n bridge.search(\"custom error class extends Error\", { k: 20 }),\n ]);\n\n // Merge and deduplicate chunks by file (concatenate text for same file)\n const fileContentMap = new Map<string, string>();\n for (const result of [...general, ...resultType, ...customErrors]) {\n const existing = fileContentMap.get(result.file) ?? \"\";\n fileContentMap.set(result.file, existing + \"\\n\" + result.chunk);\n }\n\n const totalUniqueFiles = fileContentMap.size;\n if (totalUniqueFiles === 0) return [];\n\n const entries: Entry[] = [];\n\n for (const patternDef of PATTERNS) {\n const matchingFiles: string[] = [];\n\n for (const [file, content] of fileContentMap) {\n if (patternDef.test(content)) {\n matchingFiles.push(file);\n }\n }\n\n // Require at least 2 distinct files\n if (matchingFiles.length < 2) continue;\n\n const confidence = Math.min(0.95, 0.5 + (matchingFiles.length / totalUniqueFiles) * 0.45);\n\n entries.push({\n category: \"error_handling\",\n pattern: patternDef.pattern,\n confidence,\n evidence: matchingFiles.slice(0, 5).map((file) => ({ file, line: null })),\n metadata: {\n style: patternDef.style,\n fileCount: matchingFiles.length,\n },\n });\n }\n\n return entries;\n },\n};\n","import { createBridge } from \"../../core/ez-search-bridge.js\";\nimport { listProjectFiles } from \"../../utils/fs.js\";\nimport type { ConventionEntry } from \"../../core/schema.js\";\nimport type { Extractor, ExtractionContext } from \"../types.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype Entry = Omit<ConventionEntry, \"id\">;\n\ntype ArchPattern = \"MVC\" | \"feature-based\" | \"layer-based\";\n\n// ---------------------------------------------------------------------------\n// Directory pattern detection\n// ---------------------------------------------------------------------------\n\n/** Get directory paths under src/ (or project root if no src/). */\nfunction extractSourceDirs(files: string[]): Set<string> {\n const dirs = new Set<string>();\n for (const f of files) {\n const parts = f.split(\"/\");\n if (parts.length < 2) continue;\n\n if (parts[0] === \"src\" && parts.length >= 3) {\n // Under src/ -- use \"src/subdir\" as the dir\n dirs.add(`src/${parts[1]}`);\n } else if (parts[0] !== \"src\") {\n // Project root level\n dirs.add(parts[0]!);\n }\n }\n return dirs;\n}\n\n/** Normalise a directory name to lower case for matching. */\nfunction normalise(dir: string): string {\n return dir.split(\"/\").pop()!.toLowerCase();\n}\n\n/** Detect MVC: >= 2 of models/, views/, controllers/, routes/ */\nfunction detectMVC(sourceDirs: Set<string>): string[] {\n const mvc = [\"model\", \"models\", \"view\", \"views\", \"controller\", \"controllers\", \"route\", \"routes\"];\n const found: string[] = [];\n for (const dir of sourceDirs) {\n if (mvc.includes(normalise(dir))) found.push(dir);\n }\n // Deduplicate by canonical name (model/models both count as one)\n const canonical = new Set(found.map((d) => normalise(d).replace(/s$/, \"\")));\n return canonical.size >= 2 ? found : [];\n}\n\n/** Detect feature-based: files under features/, modules/, pages/ with >= 5 files total. */\nfunction detectFeatureBased(files: string[]): string[] {\n const featurePattern = /\\/(features?|modules?|pages?)\\//i;\n const featureDirs = new Set<string>();\n let count = 0;\n for (const f of files) {\n if (featurePattern.test(f)) {\n count++;\n // Extract the feature root dir (e.g. \"src/features\" or \"features\")\n const match = f.match(/^(.*?\\/(features?|modules?|pages?))\\//i);\n if (match) featureDirs.add(match[1]!);\n }\n }\n return count >= 5 ? Array.from(featureDirs) : [];\n}\n\n/** Detect layer-based architecture (DDD / hexagonal / clean arch). */\nfunction detectLayerBased(sourceDirs: Set<string>): string[] {\n const layerPatterns = [\n // DDD\n \"domain\", \"application\", \"infrastructure\",\n // Hexagonal\n \"service\", \"services\", \"repository\", \"repositories\", \"handler\", \"handlers\", \"usecase\", \"usecases\",\n // Clean arch\n \"core\", \"data\", \"presentation\",\n ];\n const found: string[] = [];\n for (const dir of sourceDirs) {\n if (layerPatterns.includes(normalise(dir))) found.push(dir);\n }\n // Need >= 2 distinct layer dirs\n const canonical = new Set(found.map((d) => normalise(d)));\n return canonical.size >= 2 ? found : [];\n}\n\n// ---------------------------------------------------------------------------\n// Extractor\n// ---------------------------------------------------------------------------\n\nexport const architectureExtractor: Extractor = {\n name: \"architecture\",\n\n async extract(ctx: ExtractionContext): Promise<Entry[]> {\n // Signal 1: Directory structure scan (deterministic)\n const files = await listProjectFiles({\n cwd: ctx.projectPath,\n extensions: [\"ts\", \"js\", \"tsx\", \"jsx\", \"py\", \"rb\", \"go\", \"rs\"],\n });\n\n const sourceDirs = extractSourceDirs(files);\n\n // Try each pattern\n const mvcDirs = detectMVC(sourceDirs);\n const featureDirs = detectFeatureBased(files);\n const layerDirs = detectLayerBased(sourceDirs);\n\n let detectedPattern: ArchPattern | null = null;\n let detectedLayers: string[] = [];\n\n if (mvcDirs.length > 0) {\n detectedPattern = \"MVC\";\n detectedLayers = mvcDirs;\n } else if (featureDirs.length > 0) {\n detectedPattern = \"feature-based\";\n detectedLayers = featureDirs;\n } else if (layerDirs.length > 0) {\n detectedPattern = \"layer-based\";\n detectedLayers = layerDirs;\n }\n\n if (!detectedPattern) return [];\n\n // Signal 2: Semantic search confirmation (optional -- adds evidence and boosts confidence)\n const bridge = await createBridge(ctx.projectPath);\n const hasIdx = await bridge.hasIndex(ctx.projectPath);\n\n let confidence = 0.7; // directory-only\n const evidence: { file: string; line: null }[] = detectedLayers\n .slice(0, 3)\n .map((dir) => ({ file: dir, line: null }));\n\n if (hasIdx) {\n const searchResults = await bridge.search(\n \"model view controller route handler service repository\",\n { k: 20 }\n );\n if (searchResults.length > 0) {\n confidence = 0.85;\n for (const r of searchResults.slice(0, 2)) {\n evidence.push({ file: r.file, line: null });\n }\n }\n }\n\n // Deduplicate evidence by file\n const seen = new Set<string>();\n const deduped = evidence.filter(({ file }) => {\n if (seen.has(file)) return false;\n seen.add(file);\n return true;\n });\n\n return [\n {\n category: \"architecture\",\n pattern: patternLabel(detectedPattern),\n confidence,\n evidence: deduped.slice(0, 5),\n metadata: {\n architecturePattern: detectedPattern,\n layers: detectedLayers,\n },\n },\n ];\n },\n};\n\nfunction patternLabel(p: ArchPattern): string {\n switch (p) {\n case \"MVC\":\n return \"MVC architecture pattern\";\n case \"feature-based\":\n return \"Feature-based architecture\";\n case \"layer-based\":\n return \"Layer-based architecture\";\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers exposed for testing\n// ---------------------------------------------------------------------------\n\nexport { extractSourceDirs, detectMVC, detectFeatureBased, detectLayerBased };\n","import { createRegistry } from \"./registry.js\";\nimport { ConventionRegistrySchema } from \"./schema.js\";\nimport type { ConventionEntry, ConventionRegistry, EvidenceRef } from \"./schema.js\";\nimport { runExtractors } from \"../extractors/index.js\";\nimport type { ExtractorOptions } from \"../extractors/types.js\";\nimport { packageJsonExtractor } from \"../extractors/static/package-json.js\";\nimport { lockfileExtractor } from \"../extractors/static/lockfile.js\";\nimport { tsconfigExtractor } from \"../extractors/static/tsconfig.js\";\nimport { goModExtractor } from \"../extractors/static/go-mod.js\";\nimport { cargoTomlExtractor } from \"../extractors/static/cargo-toml.js\";\nimport { ciExtractor } from \"../extractors/static/ci.js\";\nimport { projectStructureExtractor } from \"../extractors/static/project-structure.js\";\nimport { namingExtractor } from \"../extractors/code/naming.js\";\nimport { importsExtractor } from \"../extractors/code/imports.js\";\nimport { staticErrorHandlingExtractor } from \"../extractors/code/error-handling.js\";\nimport { errorHandlingExtractor } from \"../extractors/semantic/error-handling.js\";\nimport { architectureExtractor } from \"../extractors/semantic/architecture.js\";\n\n// ---------------------------------------------------------------------------\n// Extractor registry (ordered by confidence priority for StackInfo population)\n// ---------------------------------------------------------------------------\n\nconst ALL_EXTRACTORS = [\n packageJsonExtractor,\n lockfileExtractor,\n tsconfigExtractor,\n goModExtractor,\n cargoTomlExtractor,\n ciExtractor,\n projectStructureExtractor,\n namingExtractor,\n importsExtractor,\n staticErrorHandlingExtractor,\n errorHandlingExtractor,\n architectureExtractor,\n];\n\n// ---------------------------------------------------------------------------\n// Deduplication helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Deduplicate evidence by file+line combination.\n */\nfunction deduplicateEvidence(evidence: EvidenceRef[]): EvidenceRef[] {\n const seen = new Set<string>();\n return evidence.filter((e) => {\n const key = `${e.file}:${e.line ?? \"null\"}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n\n/**\n * Deduplicate conventions by category+pattern.\n * For duplicates, keep the one with higher confidence.\n * Merge evidence arrays from duplicates.\n */\nfunction deduplicateConventions(\n conventions: ConventionEntry[]\n): ConventionEntry[] {\n const grouped = new Map<string, ConventionEntry>();\n\n for (const entry of conventions) {\n const key = `${entry.category}:${entry.pattern}`;\n const existing = grouped.get(key);\n\n if (!existing) {\n grouped.set(key, entry);\n } else {\n // Keep higher confidence, merge evidence\n const winner: ConventionEntry =\n entry.confidence > existing.confidence ? entry : existing;\n const mergedEvidence = deduplicateEvidence([\n ...existing.evidence,\n ...entry.evidence,\n ]);\n grouped.set(key, { ...winner, evidence: mergedEvidence });\n }\n }\n\n return Array.from(grouped.values());\n}\n\n// ---------------------------------------------------------------------------\n// StackInfo population helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Populate StackInfo from extracted conventions via a post-extraction pass.\n * Conventions are processed in array order; first match wins for each field.\n */\nfunction populateStackInfo(registry: ConventionRegistry): ConventionRegistry {\n const stack = { ...registry.stack };\n\n for (const entry of registry.conventions) {\n const meta = entry.metadata ?? {};\n\n if (entry.category === \"stack\") {\n if (!stack.language || stack.language === \"unknown\") {\n if (typeof meta.language === \"string\") {\n stack.language = meta.language;\n }\n }\n if (!stack.framework && typeof meta.framework === \"string\") {\n stack.framework = meta.framework;\n }\n if (!stack.testRunner && typeof meta.testRunner === \"string\") {\n stack.testRunner = meta.testRunner;\n }\n if (!stack.packageManager && typeof meta.packageManager === \"string\") {\n stack.packageManager = meta.packageManager;\n }\n if (!stack.buildTool) {\n // Detect buildTool from scripts metadata\n if (typeof meta.buildTool === \"string\") {\n stack.buildTool = meta.buildTool;\n } else if (\n typeof meta.scriptName === \"string\" &&\n meta.scriptName === \"build\" &&\n typeof meta.command === \"string\"\n ) {\n // Extract the build tool from the build script command (first word)\n stack.buildTool = (meta.command as string).split(\" \")[0];\n }\n }\n }\n\n if (entry.category === \"testing\") {\n if (!stack.testRunner && typeof meta.testRunner === \"string\") {\n stack.testRunner = meta.testRunner;\n }\n }\n }\n\n return { ...registry, stack };\n}\n\n// ---------------------------------------------------------------------------\n// ArchitectureInfo population helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Populate ArchitectureInfo from extracted conventions via a post-extraction pass.\n * First architecture convention with the relevant metadata wins.\n */\nfunction populateArchitectureInfo(\n registry: ConventionRegistry\n): ConventionRegistry {\n const arch = { ...registry.architecture };\n\n for (const entry of registry.conventions) {\n if (entry.category === \"architecture\") {\n if (\n !arch.pattern &&\n typeof entry.metadata?.architecturePattern === \"string\"\n ) {\n arch.pattern = entry.metadata.architecturePattern;\n }\n if (\n arch.layers.length === 0 &&\n Array.isArray(entry.metadata?.layers)\n ) {\n arch.layers = entry.metadata.layers as string[];\n }\n }\n }\n\n return { ...registry, architecture: arch };\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Run the full extraction pipeline against the given project path.\n *\n * 1. Runs all extractors in parallel (Promise.allSettled)\n * 2. Deduplicates conventions by category+pattern (higher confidence wins)\n * 3. Populates StackInfo from convention metadata\n * 4. Populates ArchitectureInfo from convention metadata\n * 5. Validates and returns the final ConventionRegistry\n */\nexport async function extractConventions(\n projectPath: string,\n options?: ExtractorOptions\n): Promise<ConventionRegistry> {\n const ctx = { projectPath, options };\n const emptyRegistry = createRegistry(projectPath);\n\n // Run all extractors\n const populated = await runExtractors(ALL_EXTRACTORS, ctx, emptyRegistry);\n\n // Deduplicate conventions\n const deduplicated: ConventionRegistry = {\n ...populated,\n conventions: deduplicateConventions(populated.conventions),\n };\n\n // Populate StackInfo from convention metadata\n const withStack = populateStackInfo(deduplicated);\n\n // Populate ArchitectureInfo from convention metadata\n const withArch = populateArchitectureInfo(withStack);\n\n // Validate and return\n return ConventionRegistrySchema.parse(withArch);\n}\n","import { readFile, writeFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\n\nexport const MARKER_START = \"<!-- ez-context:start -->\";\nexport const MARKER_END = \"<!-- ez-context:end -->\";\n\n/**\n * Write content into the marker section of filePath.\n *\n * Three paths:\n * 1. File does not exist: creates it with markers wrapping content.\n * 2. File exists, no markers (or only one marker): appends the section at the end.\n * 3. File exists with both markers: splices new content between existing markers,\n * preserving everything outside.\n */\nexport async function writeWithMarkers(\n filePath: string,\n content: string\n): Promise<void> {\n const wrapped = `${MARKER_START}\\n${content}\\n${MARKER_END}`;\n\n if (!existsSync(filePath)) {\n await writeFile(filePath, wrapped + \"\\n\", \"utf-8\");\n return;\n }\n\n const existing = await readFile(filePath, \"utf-8\");\n const startIdx = existing.indexOf(MARKER_START);\n const endIdx = existing.indexOf(MARKER_END);\n\n // Treat missing or unpaired marker as \"no markers\" — append section\n if (startIdx === -1 || endIdx === -1) {\n const separator = existing.endsWith(\"\\n\") ? \"\\n\" : \"\\n\\n\";\n await writeFile(filePath, existing + separator + wrapped + \"\\n\", \"utf-8\");\n return;\n }\n\n const before = existing.slice(0, startIdx);\n const after = existing.slice(endIdx + MARKER_END.length);\n await writeFile(filePath, before + wrapped + after, \"utf-8\");\n}\n","// Shared rendering helpers for all emitter modules\nimport type { ConventionRegistry, ConventionEntry } from \"../core/schema.js\";\n\n/**\n * Extract script commands from a filtered list of convention entries.\n * Shared by agents-md and renderConventionsBody.\n */\nexport function extractCommands(\n filtered: ConventionEntry[]\n): Array<{ scriptName: string; command: string }> {\n const commands: Array<{ scriptName: string; command: string }> = [];\n for (const entry of filtered) {\n const meta = entry.metadata;\n if (\n meta &&\n typeof meta[\"command\"] === \"string\" &&\n typeof meta[\"scriptName\"] === \"string\"\n ) {\n commands.push({\n scriptName: meta[\"scriptName\"] as string,\n command: meta[\"command\"] as string,\n });\n }\n }\n return commands;\n}\n\n/**\n * Check if a stack-category convention should appear in the Conventions section.\n * Stack entries that map to StackInfo fields (language, framework, etc.) are already\n * shown in the Stack section. Others (e.g. \"TypeScript strict mode\") are convention-worthy.\n */\nfunction isStackConventionWorthy(\n entry: ConventionEntry,\n stack: ConventionRegistry[\"stack\"]\n): boolean {\n if (entry.category !== \"stack\") return false;\n const meta = entry.metadata ?? {};\n // Already represented in Stack section via StackInfo fields\n if (typeof meta[\"language\"] === \"string\" && stack.language !== \"unknown\") return false;\n if (typeof meta[\"framework\"] === \"string\" && stack.framework) return false;\n if (typeof meta[\"buildTool\"] === \"string\" && stack.buildTool) return false;\n if (typeof meta[\"packageManager\"] === \"string\" && stack.packageManager) return false;\n // Script entries are shown in Commands section\n if (typeof meta[\"scriptName\"] === \"string\") return false;\n // Everything else (strict mode, compiler options, etc.) is convention-worthy\n return true;\n}\n\n/**\n * Check if a convention entry is redundant with the Stack or Commands sections.\n * - \"Test runner: X\" in testing category duplicates Stack > Test Runner\n * - Entries with metadata.scriptName duplicate the Commands section\n */\nexport function isRedundantConvention(entry: ConventionEntry): boolean {\n if (entry.category === \"testing\" && entry.pattern.startsWith(\"Test runner:\")) {\n return true;\n }\n if (entry.metadata && typeof entry.metadata[\"scriptName\"] === \"string\") {\n return true;\n }\n return false;\n}\n\n/**\n * Render conventions body as markdown lines.\n * Used by cursor-mdc, skill-md, rulesync-md, ruler-md, copilot-md.\n */\nexport function renderConventionsBody(\n registry: ConventionRegistry,\n confidenceThreshold: number\n): string[] {\n const filtered = registry.conventions.filter(\n (c) => c.confidence >= confidenceThreshold\n );\n\n const lines: string[] = [];\n\n // Stack section\n const s = registry.stack;\n const hasStack =\n s.language !== \"unknown\" ||\n Boolean(s.framework) ||\n Boolean(s.buildTool) ||\n Boolean(s.packageManager) ||\n Boolean(s.testRunner);\n\n if (hasStack) {\n lines.push(\"## Stack\");\n if (s.language !== \"unknown\") lines.push(`- Language: ${s.language}`);\n if (s.framework) lines.push(`- Framework: ${s.framework}`);\n if (s.buildTool) lines.push(`- Build: ${s.buildTool}`);\n if (s.packageManager) lines.push(`- Package Manager: ${s.packageManager}`);\n if (s.testRunner) lines.push(`- Test Runner: ${s.testRunner}`);\n lines.push(\"\");\n }\n\n // Architecture section\n const a = registry.architecture;\n const hasArchitecture =\n Boolean(a.pattern) || (a.layers?.length ?? 0) > 0;\n\n if (hasArchitecture) {\n lines.push(\"## Architecture\");\n if (a.pattern) lines.push(`- Pattern: ${a.pattern}`);\n if (a.layers && a.layers.length > 0) {\n lines.push(`- Layers: ${a.layers.join(\", \")}`);\n }\n lines.push(\"\");\n }\n\n // Conventions section — group by category, exclude architecture and redundant entries.\n // Stack entries without scriptName are kept (e.g. \"TypeScript strict mode enabled\").\n const categoryMap = new Map<string, string[]>();\n for (const entry of filtered) {\n if (entry.category === \"architecture\") continue;\n if (entry.category === \"stack\" && !isStackConventionWorthy(entry, s)) continue;\n if (isRedundantConvention(entry)) continue;\n const list = categoryMap.get(entry.category) ?? [];\n list.push(entry.pattern);\n categoryMap.set(entry.category, list);\n }\n\n if (categoryMap.size > 0) {\n lines.push(\"## Conventions\");\n for (const [category, patterns] of categoryMap) {\n for (const pattern of patterns) {\n lines.push(`- **${category}**: ${pattern}`);\n }\n }\n lines.push(\"\");\n }\n\n // Commands section — from conventions with metadata.command + metadata.scriptName\n const commands = extractCommands(filtered);\n\n if (commands.length > 0) {\n lines.push(\"## Commands\");\n for (const cmd of commands) {\n lines.push(`- \\`${cmd.scriptName}\\`: \\`${cmd.command}\\``);\n }\n lines.push(\"\");\n }\n\n return lines;\n}\n","import type { ConventionRegistry, ConventionEntry } from \"../core/schema.js\";\nimport { isRedundantConvention } from \"./render-helpers.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface ConventionGroup {\n category: string;\n entries: ConventionEntry[];\n}\n\n// ---------------------------------------------------------------------------\n// Data prep\n// ---------------------------------------------------------------------------\n\nfunction prepData(registry: ConventionRegistry, confidenceThreshold: number) {\n const filtered = registry.conventions.filter(\n (c) => c.confidence >= confidenceThreshold\n );\n\n // Group by category, excluding architecture and entries redundant with Stack/Commands.\n // Stack entries not covered by StackInfo fields (e.g. \"TypeScript strict mode\") are kept.\n const categoryMap = new Map<string, ConventionEntry[]>();\n for (const entry of filtered) {\n if (entry.category === \"architecture\") continue;\n if (entry.category === \"stack\") {\n const meta = entry.metadata ?? {};\n // Skip entries already rendered in the Stack section\n if (typeof meta[\"language\"] === \"string\") continue;\n if (typeof meta[\"framework\"] === \"string\") continue;\n if (typeof meta[\"buildTool\"] === \"string\") continue;\n if (typeof meta[\"packageManager\"] === \"string\") continue;\n if (typeof meta[\"scriptName\"] === \"string\") continue;\n }\n if (isRedundantConvention(entry)) continue;\n const list = categoryMap.get(entry.category) ?? [];\n list.push(entry);\n categoryMap.set(entry.category, list);\n }\n\n const conventionGroups: ConventionGroup[] = [];\n for (const [category, entries] of categoryMap) {\n conventionGroups.push({ category, entries });\n }\n\n const hasStack =\n registry.stack.language !== \"unknown\" ||\n Boolean(registry.stack.framework) ||\n Boolean(registry.stack.buildTool) ||\n Boolean(registry.stack.packageManager) ||\n Boolean(registry.stack.testRunner);\n\n const hasArchitecture =\n Boolean(registry.architecture.pattern) ||\n (registry.architecture.layers?.length ?? 0) > 0;\n\n return {\n stack: registry.stack,\n architecture: registry.architecture,\n conventionGroups,\n hasStack,\n hasArchitecture,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Renderer\n// ---------------------------------------------------------------------------\n\nexport function renderClaudeMd(\n registry: ConventionRegistry,\n confidenceThreshold: number\n): string {\n const data = prepData(registry, confidenceThreshold);\n const lines: string[] = [];\n\n lines.push(\"# Project Context\");\n\n // Stack section\n if (data.hasStack) {\n lines.push(\"\");\n lines.push(\"## Stack\");\n if (data.stack.language !== \"unknown\") {\n lines.push(`- Language: ${data.stack.language}`);\n }\n if (data.stack.framework) lines.push(`- Framework: ${data.stack.framework}`);\n if (data.stack.buildTool) lines.push(`- Build: ${data.stack.buildTool}`);\n if (data.stack.packageManager) lines.push(`- Package Manager: ${data.stack.packageManager}`);\n if (data.stack.testRunner) lines.push(`- Test Runner: ${data.stack.testRunner}`);\n }\n\n // Conventions section\n if (data.conventionGroups.length > 0) {\n lines.push(\"\");\n lines.push(\"## Conventions\");\n for (const group of data.conventionGroups) {\n for (const entry of group.entries) {\n lines.push(`- **${group.category}**: ${entry.pattern}`);\n }\n }\n }\n\n // Architecture section\n if (data.hasArchitecture) {\n lines.push(\"\");\n lines.push(\"## Architecture\");\n if (data.architecture.pattern) {\n lines.push(`- Pattern: ${data.architecture.pattern}`);\n }\n if (data.architecture.layers && data.architecture.layers.length > 0) {\n lines.push(`- Layers: ${data.architecture.layers.join(\", \")}`);\n }\n }\n\n return lines.join(\"\\n\") + \"\\n\";\n}\n","import type { ConventionRegistry, ConventionEntry } from \"../core/schema.js\";\nimport { extractCommands } from \"./render-helpers.js\";\n\n// ---------------------------------------------------------------------------\n// Data prep\n// ---------------------------------------------------------------------------\n\nfunction prepData(registry: ConventionRegistry, confidenceThreshold: number) {\n const filtered = registry.conventions.filter(\n (c) => c.confidence >= confidenceThreshold\n );\n\n const commands = extractCommands(filtered);\n\n const byCategory = (cat: string): ConventionEntry[] =>\n filtered.filter((c) => c.category === cat);\n\n const testingConventions = byCategory(\"testing\");\n const namingConventions = byCategory(\"naming\");\n const importConventions = byCategory(\"imports\");\n const gitConventions = filtered.filter(\n (c) =>\n c.pattern.toLowerCase().includes(\"git\") ||\n (c.category === \"other\" && c.pattern.toLowerCase().includes(\"commit\"))\n );\n\n const hasTesting =\n Boolean(registry.stack.testRunner) || testingConventions.length > 0;\n\n const hasProjectStructure =\n Boolean(registry.architecture.pattern) ||\n (registry.architecture.layers?.length ?? 0) > 0;\n\n const hasCodeStyle =\n namingConventions.length > 0 || importConventions.length > 0;\n\n return {\n commands,\n testRunner: registry.stack.testRunner ?? null,\n testingConventions,\n namingConventions,\n importConventions,\n gitConventions,\n architecture: registry.architecture,\n hasTesting,\n hasProjectStructure,\n hasCodeStyle,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Renderer\n// ---------------------------------------------------------------------------\n\nexport function renderAgentsMd(\n registry: ConventionRegistry,\n confidenceThreshold: number\n): string {\n const data = prepData(registry, confidenceThreshold);\n const lines: string[] = [];\n\n lines.push(\"# AGENTS.md\");\n\n // Commands section\n if (data.commands.length > 0) {\n lines.push(\"\");\n lines.push(\"## Commands\");\n for (const cmd of data.commands) {\n lines.push(`- \\`${cmd.scriptName}\\`: \\`${cmd.command}\\``);\n }\n }\n\n // Testing section\n if (data.hasTesting) {\n lines.push(\"\");\n lines.push(\"## Testing\");\n if (data.testRunner) lines.push(`- Test runner: ${data.testRunner}`);\n for (const entry of data.testingConventions) {\n lines.push(`- ${entry.pattern}`);\n }\n }\n\n // Project Structure section\n if (data.hasProjectStructure) {\n lines.push(\"\");\n lines.push(\"## Project Structure\");\n if (data.architecture.pattern) {\n lines.push(`- Architecture: ${data.architecture.pattern}`);\n }\n if (data.architecture.layers && data.architecture.layers.length > 0) {\n lines.push(`- Layers: ${data.architecture.layers.join(\", \")}`);\n }\n }\n\n // Code Style section\n if (data.hasCodeStyle) {\n lines.push(\"\");\n lines.push(\"## Code Style\");\n for (const entry of data.namingConventions) {\n lines.push(`- **naming**: ${entry.pattern}`);\n }\n for (const entry of data.importConventions) {\n lines.push(`- **imports**: ${entry.pattern}`);\n }\n }\n\n // Git Workflow section (optional)\n if (data.gitConventions.length > 0) {\n lines.push(\"\");\n lines.push(\"## Git Workflow\");\n for (const entry of data.gitConventions) {\n lines.push(`- ${entry.pattern}`);\n }\n }\n\n // Boundaries section (always present)\n lines.push(\"\");\n lines.push(\"## Boundaries\");\n lines.push(\"- Do not modify auto-generated sections between ez-context markers\");\n\n return lines.join(\"\\n\") + \"\\n\";\n}\n","import yaml from \"js-yaml\";\nimport type { ConventionRegistry } from \"../core/schema.js\";\nimport { renderConventionsBody } from \"./render-helpers.js\";\n\n/**\n * Render a Cursor MDC rule file (.cursor/rules/ez-context.mdc).\n *\n * Format: YAML frontmatter + markdown body.\n * - description: shown in Cursor UI\n * - globs: empty string (not null/omitted) per Cursor docs\n * - alwaysApply: true ensures conventions are always in context\n */\nexport function renderCursorMdc(\n registry: ConventionRegistry,\n confidenceThreshold: number\n): string {\n const frontmatter = yaml\n .dump({\n description: \"Project conventions extracted by ez-context\",\n globs: \"\",\n alwaysApply: true,\n })\n .trimEnd();\n\n const bodyLines = renderConventionsBody(registry, confidenceThreshold);\n // Remove trailing empty line added by renderConventionsBody\n while (bodyLines.length > 0 && bodyLines[bodyLines.length - 1] === \"\") {\n bodyLines.pop();\n }\n const body = bodyLines.join(\"\\n\");\n\n return `---\\n${frontmatter}\\n---\\n\\n${body}\\n`;\n}\n","import type { ConventionRegistry } from \"../core/schema.js\";\nimport { renderConventionsBody } from \"./render-helpers.js\";\n\n/**\n * Render a GitHub Copilot instructions file (.github/copilot-instructions.md).\n *\n * Format: Plain markdown, no YAML frontmatter.\n * GitHub Copilot reads the entire file; HTML comment markers are used\n * for idempotent updates via writeWithMarkers.\n */\nexport function renderCopilotMd(\n registry: ConventionRegistry,\n confidenceThreshold: number\n): string {\n const lines: string[] = [];\n\n lines.push(\"<!-- Generated by ez-context. Do not edit between markers. -->\");\n lines.push(\"\");\n lines.push(\"# Copilot Instructions\");\n lines.push(\"\");\n\n const bodyLines = renderConventionsBody(registry, confidenceThreshold);\n // Remove trailing empty line\n while (bodyLines.length > 0 && bodyLines[bodyLines.length - 1] === \"\") {\n bodyLines.pop();\n }\n lines.push(...bodyLines);\n\n return lines.join(\"\\n\") + \"\\n\";\n}\n","import yaml from \"js-yaml\";\nimport type { ConventionRegistry } from \"../core/schema.js\";\nimport { renderConventionsBody } from \"./render-helpers.js\";\n\n/**\n * Render a SKILL.md module file (.skills/ez-context/SKILL.md).\n *\n * Format: YAML frontmatter + markdown body.\n * - name: must match directory name (ez-context)\n * - description: max 1024 chars, describes what AND when to use\n * Body stays under 5000 tokens (~3750 words) as per SKILL.md spec.\n */\nexport function renderSkillMd(\n registry: ConventionRegistry,\n confidenceThreshold: number\n): string {\n const description =\n \"Project conventions and coding standards for this codebase. \" +\n \"Use when writing new code, reviewing patterns, or understanding project architecture.\";\n\n const frontmatter = yaml\n .dump({\n name: \"ez-context\",\n description,\n })\n .trimEnd();\n\n const bodyLines = renderConventionsBody(registry, confidenceThreshold);\n // Remove trailing empty line\n while (bodyLines.length > 0 && bodyLines[bodyLines.length - 1] === \"\") {\n bodyLines.pop();\n }\n const body = bodyLines.join(\"\\n\");\n\n return `---\\n${frontmatter}\\n---\\n\\n${body}\\n`;\n}\n","import yaml from \"js-yaml\";\nimport type { ConventionRegistry } from \"../core/schema.js\";\nimport { renderConventionsBody } from \"./render-helpers.js\";\n\n/**\n * Render a Rulesync rule file (.rulesync/rules/ez-context.md).\n *\n * Format: YAML frontmatter + markdown body.\n * - targets: specifies which AI tools receive this rule\n * ez-context writes INTO .rulesync/rules/; Rulesync distributes to tools.\n */\nexport function renderRulesyncMd(\n registry: ConventionRegistry,\n confidenceThreshold: number\n): string {\n const frontmatter = yaml\n .dump({\n description: \"Project conventions extracted by ez-context\",\n targets: [\"cursor\", \"copilot\", \"windsurf\"],\n })\n .trimEnd();\n\n const bodyLines = renderConventionsBody(registry, confidenceThreshold);\n // Remove trailing empty line\n while (bodyLines.length > 0 && bodyLines[bodyLines.length - 1] === \"\") {\n bodyLines.pop();\n }\n const body = bodyLines.join(\"\\n\");\n\n return `---\\n${frontmatter}\\n---\\n\\n${body}\\n`;\n}\n","import type { ConventionRegistry } from \"../core/schema.js\";\nimport { renderConventionsBody } from \"./render-helpers.js\";\n\n/**\n * Render a Ruler rule file (.ruler/ez-context.md).\n *\n * Format: Plain markdown, no YAML frontmatter.\n * Ruler recursively discovers all .md files in .ruler/ and distributes them.\n * ez-context writes a single .ruler/ez-context.md as an additive conventions file.\n */\nexport function renderRulerMd(\n registry: ConventionRegistry,\n confidenceThreshold: number\n): string {\n const lines: string[] = [];\n\n lines.push(\"# Project Conventions (ez-context)\");\n lines.push(\"\");\n\n const bodyLines = renderConventionsBody(registry, confidenceThreshold);\n // Remove trailing empty line\n while (bodyLines.length > 0 && bodyLines[bodyLines.length - 1] === \"\") {\n bodyLines.pop();\n }\n lines.push(...bodyLines);\n\n return lines.join(\"\\n\") + \"\\n\";\n}\n","import path from \"node:path\";\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport type { ConventionRegistry } from \"../core/schema.js\";\nimport type { EmitOptions, EmitResult, OutputFormat } from \"./types.js\";\nimport { writeWithMarkers } from \"./writer.js\";\nimport { renderClaudeMd } from \"./claude-md.js\";\nimport { renderAgentsMd } from \"./agents-md.js\";\nimport { renderCursorMdc } from \"./cursor-mdc.js\";\nimport { renderCopilotMd } from \"./copilot-md.js\";\nimport { renderSkillMd } from \"./skill-md.js\";\nimport { renderRulesyncMd } from \"./rulesync-md.js\";\nimport { renderRulerMd } from \"./ruler-md.js\";\n\nexport { renderClaudeMd } from \"./claude-md.js\";\nexport { renderAgentsMd } from \"./agents-md.js\";\nexport { renderCursorMdc } from \"./cursor-mdc.js\";\nexport { renderCopilotMd } from \"./copilot-md.js\";\nexport { renderSkillMd } from \"./skill-md.js\";\nexport { renderRulesyncMd } from \"./rulesync-md.js\";\nexport { renderRulerMd } from \"./ruler-md.js\";\nexport type { OutputFormat } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Format emitter registry\n// ---------------------------------------------------------------------------\n\ntype WriteStrategy = \"markers\" | \"direct\";\n\ninterface FormatEmitterEntry {\n render: (registry: ConventionRegistry, threshold: number) => string;\n filename: string;\n strategy: WriteStrategy;\n}\n\nexport const FORMAT_EMITTER_MAP: Record<OutputFormat, FormatEmitterEntry> = {\n claude: {\n render: renderClaudeMd,\n filename: \"CLAUDE.md\",\n strategy: \"markers\",\n },\n agents: {\n render: renderAgentsMd,\n filename: \"AGENTS.md\",\n strategy: \"markers\",\n },\n cursor: {\n render: renderCursorMdc,\n filename: path.join(\".cursor\", \"rules\", \"ez-context.mdc\"),\n strategy: \"direct\",\n },\n copilot: {\n render: renderCopilotMd,\n filename: path.join(\".github\", \"copilot-instructions.md\"),\n strategy: \"markers\",\n },\n skills: {\n render: renderSkillMd,\n filename: path.join(\".skills\", \"ez-context\", \"SKILL.md\"),\n strategy: \"direct\",\n },\n rulesync: {\n render: renderRulesyncMd,\n filename: path.join(\".rulesync\", \"rules\", \"ez-context.md\"),\n strategy: \"markers\",\n },\n ruler: {\n render: renderRulerMd,\n filename: path.join(\".ruler\", \"ez-context.md\"),\n strategy: \"direct\",\n },\n};\n\n// ---------------------------------------------------------------------------\n// emit()\n// ---------------------------------------------------------------------------\n\n/**\n * Emit output files for the given ConventionRegistry.\n *\n * Defaults to [\"claude\", \"agents\"] for backward compatibility.\n * In dryRun mode, returns rendered content without writing any files.\n */\nexport async function emit(\n registry: ConventionRegistry,\n options: EmitOptions\n): Promise<EmitResult> {\n const threshold = options.confidenceThreshold ?? 0.7;\n const formats: OutputFormat[] = options.formats ?? [\"claude\", \"agents\"];\n\n // Render all requested formats\n const rendered: Record<string, string> = {};\n for (const format of formats) {\n const entry = FORMAT_EMITTER_MAP[format];\n rendered[format] = entry.render(registry, threshold);\n }\n\n // Backward compat aliases\n const claudeMd = rendered[\"claude\"] ?? \"\";\n const agentsMd = rendered[\"agents\"] ?? \"\";\n\n if (options.dryRun) {\n return { rendered, claudeMd, agentsMd, filesWritten: [] };\n }\n\n // Write files\n const filesWritten: string[] = [];\n for (const format of formats) {\n const entry = FORMAT_EMITTER_MAP[format];\n const filePath = path.join(options.outputDir, entry.filename);\n\n await mkdir(path.dirname(filePath), { recursive: true });\n if (entry.strategy === \"direct\") {\n await writeFile(filePath, rendered[format]!, \"utf-8\");\n } else {\n await writeWithMarkers(filePath, rendered[format]!);\n }\n\n filesWritten.push(filePath);\n }\n\n return { rendered, claudeMd, agentsMd, filesWritten };\n}\n"],"mappings":";;;;;;;;;;;AAMA,MAAa,2BAA2B,EAAE,KAAK;CAC7C;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;AAEF,MAAa,oBAAoB,EAAE,OAAO;CACxC,MAAM,EAAE,QAAQ;CAChB,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC,UAAU;CAC7C,CAAC;AAEF,MAAa,wBAAwB,EAAE,OAAO;CAC5C,IAAI,EAAE,MAAM;CACZ,UAAU;CACV,SAAS,EAAE,QAAQ,CAAC,IAAI,EAAE;CAC1B,YAAY,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE;CACpC,UAAU,EAAE,MAAM,kBAAkB;CACpC,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,SAAS,CAAC,CAAC,UAAU;CACvD,CAAC;AAEF,MAAa,kBAAkB,EAAE,OAAO;CACtC,UAAU,EAAE,QAAQ;CACpB,WAAW,EAAE,QAAQ,CAAC,UAAU;CAChC,YAAY,EAAE,QAAQ,CAAC,UAAU;CACjC,WAAW,EAAE,QAAQ,CAAC,UAAU;CAChC,gBAAgB,EAAE,QAAQ,CAAC,UAAU;CACrC,aAAa,EAAE,QAAQ,CAAC,UAAU;CACnC,CAAC;AAEF,MAAa,yBAAyB,EAAE,OAAO;CAC7C,SAAS,EAAE,QAAQ,CAAC,UAAU;CAC9B,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;CAC3B,aAAa,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,UAAU;CAC5C,CAAC;AAMF,MAAa,2BAA2B,EAAE,OAAO;CAC/C,SAAS,EAAE,QAAQ,IAAI;CACvB,aAAa,EAAE,QAAQ;CACvB,aAAa,EAAE,QAAQ,CAAC,UAAU;CAClC,OAAO;CACP,aAAa,EAAE,MAAM,sBAAsB;CAC3C,cAAc;CACd,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,SAAS,CAAC,CAAC,UAAU;CACvD,CAAC;;;;;;;;AC/CF,SAAgB,eAAe,aAAyC;CACtE,MAAM,WAA+B;EACnC,SAAS;EACT;EACA,8BAAa,IAAI,MAAM,EAAC,aAAa;EACrC,OAAO,EACL,UAAU,WACX;EACD,aAAa,EAAE;EACf,cAAc,EACZ,QAAQ,EAAE,EACX;EACF;CAED,MAAM,SAAS,yBAAyB,UAAU,SAAS;AAC3D,KAAI,CAAC,OAAO,QACV,OAAM,IAAI,MACR,6CAA6C,KAAK,UAAU,OAAO,MAAM,OAAO,GACjF;AAGH,QAAO,OAAO;;;;;;AAOhB,SAAgB,cACd,UACA,OACoB;CACpB,MAAM,WAA4B;EAChC,IAAI,OAAO,YAAY;EACvB,GAAG;EACJ;CAED,MAAM,UAA8B;EAClC,GAAG;EACH,aAAa,CAAC,GAAG,SAAS,aAAa,SAAS;EACjD;CAED,MAAM,SAAS,yBAAyB,UAAU,QAAQ;AAC1D,KAAI,CAAC,OAAO,QACV,OAAM,IAAI,MACR,4CAA4C,KAAK,UAAU,OAAO,MAAM,OAAO,GAChF;AAGH,QAAO,OAAO;;;;;;;;;;;;AChDhB,eAAsB,cACpB,YACA,KACA,UAC6B;CAC7B,MAAM,UAAU,MAAM,QAAQ,WAC5B,WAAW,KAAK,MAAM,EAAE,QAAQ,IAAI,CAAC,MAAM,aAAa;EAAE,WAAW;EAAG;EAAS,EAAE,CAAC,CACrF;CAED,IAAI,UAAU;AAEd,MAAK,MAAM,CAAC,GAAG,WAAW,QAAQ,SAAS,CACzC,KAAI,OAAO,WAAW,YACpB,MAAK,MAAM,SAAS,OAAO,MAAM,QAC/B,WAAU,cAAc,SAAS,MAAM;KAGzC,SAAQ,KACN,8BAA8B,WAAW,GAAI,KAAK,YAClD,OAAO,OACR;AAIL,QAAO;;;;;AC1BT,MAAM,gBAAwC;CAC5C,OAAO;CACP,KAAK;CACL,iBAAiB;CACjB,MAAM;CACN,MAAM;CACN,QAAQ;CACR,MAAM;CACN,SAAS;CACT,SAAS;CACT,KAAK;CACN;AAED,MAAM,kBAA0C;CAC9C,QAAQ;CACR,MAAM;CACN,OAAO;CACP,SAAS;CACT,KAAK;CACN;AAcD,SAAS,QAAQ,KAA0C;AACzD,QAAO;EAAE,GAAG,IAAI;EAAc,GAAG,IAAI;EAAiB;;AAKxD,MAAMA,aAAW,CAAC;CAAE,MAAM;CAAgB,MAAM;CAAM,CAAC;AAMvD,MAAa,uBAAkC;CAC7C,MAAM;CAEN,MAAM,QAAQ,KAA0C;EACtD,MAAM,WAAW,KAAK,IAAI,aAAa,eAAe;AAEtD,MAAI;AACF,SAAM,OAAO,SAAS;UAChB;AACN,UAAO,EAAE;;EAGX,IAAI;AACJ,MAAI;GACF,MAAM,MAAM,MAAM,SAAS,UAAU,QAAQ;AAC7C,SAAM,KAAK,MAAM,IAAI;UACf;AACN,UAAO,EAAE;;EAGX,MAAM,OAAO,QAAQ,IAAI;EACzB,MAAM,UAAmB,EAAE;EAK3B,MAAM,WADJ,gBAAgB,QAAQ,iBAAiB,OACX,eAAe;AAC/C,UAAQ,KAAK;GACX,UAAU;GACV,SAAS,aAAa;GACtB,YAAY;GACZ,UAAUA;GACV,UAAU,EAAE,UAAU;GACvB,CAAC;AAGF,OAAK,MAAM,CAAC,UAAU,UAAU,OAAO,QAAQ,cAAc,CAC3D,KAAI,YAAY,MAAM;GACpB,MAAM,UAAU,KAAK;AACrB,WAAQ,KAAK;IACX,UAAU;IACV,SAAS,cAAc;IACvB,YAAY;IACZ,UAAUA;IACV,UAAU;KAAE,WAAW;KAAO;KAAS;IACxC,CAAC;AACF;;AAKJ,OAAK,MAAM,CAAC,UAAU,UAAU,OAAO,QAAQ,gBAAgB,CAC7D,KAAI,YAAY,MAAM;AACpB,WAAQ,KAAK;IACX,UAAU;IACV,SAAS,gBAAgB;IACzB,YAAY;IACZ,UAAUA;IACV,UAAU,EAAE,YAAY,OAAO;IAChC,CAAC;AACF;;AAKJ,MAAI,OAAO,IAAI,mBAAmB,UAAU;GAC1C,MAAM,SAAS,IAAI,eAAe,MAAM,IAAI,CAAC;AAC7C,OAAI,OACF,SAAQ,KAAK;IACX,UAAU;IACV,SAAS,oBAAoB;IAC7B,YAAY;IACZ,UAAUA;IACV,UAAU,EAAE,gBAAgB,QAAQ;IACrC,CAAC;;AAKN,MAAI,IAAI,SAAS,SACf,SAAQ,KAAK;GACX,UAAU;GACV,SAAS;GACT,YAAY;GACZ,UAAUA;GACV,UAAU,EAAE,cAAc,OAAO;GAClC,CAAC;EAIJ,MAAM,UAAU,IAAI,WAAW,EAAE;AACjC,OAAK,MAAM,CAAC,YAAY,YAAY,OAAO,QAAQ,QAAQ,EAAE;GAC3D,MAAM,eACJ,eAAe,UAAU,WAAW,WAAW,QAAQ;AAIzD,OAAI,gBAFF,eAAe,WAAW,eAAe,OAGzC,SAAQ,KAAK;IACX,UAAU,eAAe,YAAY;IACrC,SAAS,WAAW,WAAW,KAAK;IACpC,YAAY;IACZ,UAAUA;IACV,UAAU;KAAE;KAAY;KAAS;IAClC,CAAC;;AAIN,SAAO;;CAEV;;;;AC3JD,MAAM,YAAsD;CAC1D;EAAE,MAAM;EAAY,SAAS;EAAO;CACpC;EAAE,MAAM;EAAa,SAAS;EAAO;CACrC;EAAE,MAAM;EAAkB,SAAS;EAAQ;CAC3C;EAAE,MAAM;EAAa,SAAS;EAAQ;CACtC;EAAE,MAAM;EAAqB,SAAS;EAAO;CAC9C;AAQD,MAAa,oBAA+B;CAC1C,MAAM;CAEN,MAAM,QAAQ,KAA0C;AACtD,OAAK,MAAM,EAAE,MAAM,aAAa,UAC9B,KAAI;AACF,SAAM,OAAO,KAAK,IAAI,aAAa,KAAK,CAAC;AACzC,UAAO,CACL;IACE,UAAU;IACV,SAAS,oBAAoB;IAC7B,YAAY;IACZ,UAAU,CAAC;KAAE;KAAM,MAAM;KAAM,CAAC;IAChC,UAAU,EAAE,gBAAgB,SAAS;IACtC,CACF;UACK;AAKV,SAAO,EAAE;;CAEZ;;;;ACjBD,MAAM,WAAW,CAAC;CAAE,MAAM;CAAiB,MAAM;CAAM,CAAC;;;;;AAMxD,SAAS,6BAA6B,KAAqB;AACzD,QAAO,IAEJ,QAAQ,eAAe,GAAG,CAE1B,QAAQ,gBAAgB,KAAK;;AAOlC,MAAa,oBAA+B;CAC1C,MAAM;CAEN,MAAM,QAAQ,KAA0C;EACtD,MAAM,WAAW,KAAK,IAAI,aAAa,gBAAgB;AAEvD,MAAI;AACF,SAAM,OAAO,SAAS;UAChB;AACN,UAAO,EAAE;;EAGX,IAAI;AACJ,MAAI;GACF,MAAM,MAAM,MAAM,SAAS,UAAU,QAAQ;AAC7C,YAAS,KAAK,MAAM,6BAA6B,IAAI,CAAC;UAChD;AACN,UAAO,EAAE;;EAGX,MAAM,KAAK,OAAO,mBAAmB,EAAE;EACvC,MAAM,UAAmB,EAAE;AAG3B,MAAI,GAAG,WAAW,KAChB,SAAQ,KAAK;GACX,UAAU;GACV,SAAS;GACT,YAAY;GACZ,UAAU;GACV,UAAU,EAAE,QAAQ,MAAM;GAC3B,CAAC;EAIJ,MAAM,UAAmC,EAAE;AAC3C,OAAK,MAAM,OAAO;GAAC;GAAU;GAAU;GAAmB,CACxD,KAAI,GAAG,SAAS,OACd,SAAQ,OAAO,GAAG;AAGtB,MAAI,OAAO,KAAK,QAAQ,CAAC,SAAS,EAChC,SAAQ,KAAK;GACX,UAAU;GACV,SAAS;GACT,YAAY;GACZ,UAAU;GACV,UAAU;GACX,CAAC;AAIJ,MAAI,GAAG,SAAS,OAAO,KAAK,GAAG,MAAM,CAAC,SAAS,EAC7C,SAAQ,KAAK;GACX,UAAU;GACV,SAAS;GACT,YAAY;GACZ,UAAU;GACV,UAAU,EAAE,SAAS,GAAG,OAAO;GAChC,CAAC;AAGJ,SAAO;;CAEV;;;;ACxGD,MAAa,iBAA4B;CACvC,MAAM;CAEN,MAAM,QAAQ,KAA0C;EACtD,MAAM,WAAW,KAAK,IAAI,aAAa,SAAS;AAEhD,MAAI;AACF,SAAM,OAAO,SAAS;UAChB;AACN,UAAO,EAAE;;EAGX,IAAI;AACJ,MAAI;AACF,SAAM,MAAM,SAAS,UAAU,QAAQ;UACjC;AACN,UAAO,EAAE;;EAGX,MAAM,QAAQ,IAAI,MAAM,KAAK;EAE7B,IAAI,aAAa;EACjB,IAAI,YAAY;EAChB,IAAI,kBAAkB;EACtB,IAAI,iBAAiB;AAErB,OAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,UAAU,KAAK,MAAM;AAE3B,OAAI,CAAC,YAAY;IACf,MAAM,cAAc,QAAQ,MAAM,kBAAkB;AACpD,QAAI,cAAc,IAAI;AACpB,kBAAa,YAAY;AACzB;;;AAIJ,OAAI,CAAC,WAAW;IACd,MAAM,UAAU,QAAQ,MAAM,cAAc;AAC5C,QAAI,UAAU,IAAI;AAChB,iBAAY,QAAQ;AACpB;;;AAKJ,OAAI,YAAY,aAAa;AAC3B,qBAAiB;AACjB;;AAEF,OAAI,gBAAgB;AAClB,QAAI,YAAY,IACd,kBAAiB;aACR,QAAQ,SAAS,KAAK,CAAC,QAAQ,WAAW,KAAK,CACxD;AAEF;;AAGF,OAAI,QAAQ,MAAM,wBAAwB,CACxC;;AAIJ,MAAI,CAAC,WACH,QAAO,EAAE;AAGX,SAAO,CACL;GACE,UAAU;GACV,SAAS,eAAe,WAAW;GACnC,YAAY;GACZ,UAAU,CAAC;IAAE,MAAM;IAAU,MAAM;IAAM,CAAC;GAC1C,UAAU;IACR,UAAU;IACV;IACA,WAAW,aAAa;IACxB;IACD;GACF,CACF;;CAEJ;;;;AC5ED,MAAa,qBAAgC;CAC3C,MAAM;CAEN,MAAM,QAAQ,KAA0C;EACtD,MAAM,WAAW,KAAK,IAAI,aAAa,aAAa;AAEpD,MAAI;AACF,SAAM,OAAO,SAAS;UAChB;AACN,UAAO,EAAE;;EAGX,IAAI;AACJ,MAAI;AAEF,WAAQC,MADI,MAAM,SAAS,UAAU,QAAQ,CACvB;UAChB;AACN,UAAO,EAAE;;EAGX,MAAM,cAAc,MAAM,SAAS;AACnC,MAAI,CAAC,YACH,QAAO,EAAE;EAGX,MAAM,kBAAkB,OAAO,KAAK,MAAM,gBAAgB,EAAE,CAAC,CAAC;AAE9D,SAAO,CACL;GACE,UAAU;GACV,SAAS,iBAAiB,YAAY;GACtC,YAAY;GACZ,UAAU,CAAC;IAAE,MAAM;IAAc,MAAM;IAAM,CAAC;GAC9C,UAAU;IACR,UAAU;IACV;IACA;IACD;GACF,CACF;;CAEJ;;;;AC9BD,MAAM,iBAAiB;CAAC;CAAS;CAAW;CAAO;CAAS;AAC5D,MAAM,gBAAgB;CAAC;CAAQ;CAAU;CAAQ;CAAU;CAAc;CAAU;AACnF,MAAM,gBAAgB;CAAC;CAAQ;CAAU;CAAS;CAAU;CAAO;AAEnE,SAAS,kBAAkB,KAAqC;CAC9D,MAAM,QAAQ,IAAI,aAAa;AAC/B,KAAI,cAAc,MAAM,OAAO,MAAM,SAAS,GAAG,CAAC,CAAE,QAAO;AAC3D,KAAI,eAAe,MAAM,OAAO,MAAM,SAAS,GAAG,CAAC,CAAE,QAAO;AAC5D,KAAI,cAAc,MAAM,OAAO,MAAM,SAAS,GAAG,CAAC,CAAE,QAAO;AAC3D,QAAO;;;;;;AAOT,SAAS,6BAA6B,KAAc,UAA8D;CAChH,MAAM,UAA0B,EAAE;CAClC,MAAM,MAAgB,EAAE;AAExB,KAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;EAAE;EAAS;EAAK;CAE5D,MAAM,OADS,IACK;AACpB,KAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;EAAE;EAAS;EAAK;AAE9D,MAAK,MAAM,OAAO,OAAO,OAAO,KAAgC,EAAE;AAChE,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU;EACrC,MAAM,QAAS,IAAgC;AAC/C,MAAI,CAAC,MAAM,QAAQ,MAAM,CAAE;AAC3B,OAAK,MAAM,QAAQ,OAAO;AACxB,OAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;GACvC,MAAM,MAAO,KAAiC;AAC9C,OAAI,OAAO,QAAQ,SAAU;AAE7B,QAAK,MAAM,QAAQ,IAAI,MAAM,KAAK,EAAE;IAClC,MAAM,UAAU,KAAK,MAAM;AAC3B,QAAI,CAAC,QAAS;AACd,QAAI,KAAK,QAAQ;IACjB,MAAM,WAAW,kBAAkB,QAAQ;AAC3C,QAAI,aAAa,KACf,SAAQ,KAAK;KAAE,SAAS;KAAS;KAAU,QAAQ;KAAU,CAAC;;;;AAMtE,QAAO;EAAE;EAAS;EAAK;;;AAIzB,MAAM,kBAAkB,IAAI,IAAI;CAAC;CAAU;CAAa;CAAW;CAAW;CAAY;CAAS;CAAY;CAAiB;CAAgB;CAAS;CAAY,CAAC;;;;;AAMtK,SAAS,wBAAwB,KAAc,UAA8D;CAC3G,MAAM,UAA0B,EAAE;CAClC,MAAM,MAAgB,EAAE;AAExB,KAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;EAAE;EAAS;EAAK;AAE5D,MAAK,MAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,IAA+B,EAAE;AACvE,MAAI,gBAAgB,IAAI,IAAI,CAAE;AAC9B,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU;EACrC,MAAM,SAAU,IAAgC;EAChD,MAAM,UAAU,MAAM,QAAQ,OAAO,GAAG,SAAS,OAAO,WAAW,WAAW,CAAC,OAAO,GAAG,EAAE;AAC3F,OAAK,MAAM,OAAO,SAAS;AACzB,OAAI,OAAO,QAAQ,SAAU;GAC7B,MAAM,UAAU,IAAI,MAAM;AAC1B,OAAI,CAAC,QAAS;AACd,OAAI,KAAK,QAAQ;GACjB,MAAM,WAAW,kBAAkB,QAAQ;AAC3C,OAAI,aAAa,KACf,SAAQ,KAAK;IAAE,SAAS;IAAS;IAAU,QAAQ;IAAU,CAAC;;;AAKpE,QAAO;EAAE;EAAS;EAAK;;AAOzB,MAAa,cAAyB;CACpC,MAAM;CAEN,MAAM,QAAQ,KAA0C;EAOtD,MAAM,UAAU,MAAM,OANH;GACjB;GACA;GACA;GACD,EAEwC;GACvC,KAAK,IAAI;GACT,WAAW;GACX,qBAAqB;GACrB,UAAU;GACX,CAAC;AAEF,MAAI,QAAQ,WAAW,EAAG,QAAO,EAAE;EAEnC,MAAM,UAAmB,EAAE;AAE3B,OAAK,MAAM,WAAW,SAAS;GAC7B,MAAM,UAAU,KAAK,IAAI,aAAa,QAAQ;GAC9C,IAAI;AACJ,OAAI;AACF,UAAM,MAAM,SAAS,SAAS,QAAQ;WAChC;AACN;;GAGF,IAAI;AACJ,OAAI;AACF,UAAMC,KAAS,IAAI;WACb;AAEN;;GAIF,MAAM,EAAE,SAAS,KAAK,YADL,QAAQ,SAAS,aAAa,GAE3C,wBAAwB,KAAK,QAAQ,GACrC,6BAA6B,KAAK,QAAQ;AAG9C,OAAI,QAAQ,SAAS,KAAK,QAAQ,SAAS,EACzC,MAAK,MAAM,EAAE,SAAS,cAAc,QAClC,SAAQ,KAAK;IACX;IACA,SAAS,eAAe;IACxB,YAAY;IACZ,UAAU,CAAC;KAAE,MAAM,SAAS,IAAI,aAAa,QAAQ,IAAI;KAAS,MAAM;KAAM,CAAC;IAC/E,UAAU;KAAE;KAAS,QAAQ;KAAS,aAAa;KAAS;IAC7D,CAAC;;AAKR,SAAO;;CAEV;;;;;ACnKD,MAAa,cAAiC;CAC5C;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;AAyBD,eAAsB,iBACpB,SACmB;CACnB,MAAM,EACJ,KACA,aAAa;EAAC;EAAM;EAAM;EAAQ;EAAK,EACvC,mBAAmB,EAAE,KACnB;AAeJ,SARc,MAAM,OAJlB,WAAW,WAAW,IAClB,QAAQ,WAAW,OACnB,SAAS,WAAW,KAAK,IAAI,CAAC,IAEG;EACrC;EACA,WAAW;EACX,QAAQ,CAAC,GAAG,aAAa,GAAG,iBAAiB;EAC7C,qBAAqB;EACrB,UAAU;EACX,CAAC,EAEW,MAAM;;;;;;ACvCrB,MAAM,oBAAoB;CAAC;CAAS;CAAU;CAAa;AAE3D,MAAM,gBAA+B;CACnC;EAAE,MAAM;EAA6B,UAAU;EAAc,OAAO;EAAmB;CACvF;EAAE,MAAM;EAA6B,UAAU;EAAc,OAAO;EAAmB;CACvF;EAAE,MAAM;EAA6B,UAAU;EAAmB,OAAO;EAAmB;CAC5F;EAAE,MAAM;EAA8B,UAAU;EAAoB,OAAO;EAAoB;CAC/F;EAAE,MAAM;EAAkC,UAAU;EAAwB,OAAO;EAAwB;CAC5G;AAMD,MAAa,4BAAuC;CAClD,MAAM;CAEN,MAAM,QAAQ,KAA0C;EACtD,MAAM,UAAmB,EAAE;AAE3B,OAAK,MAAM,EAAE,MAAM,UAAU,WAAW,eAAe;GACrD,IAAI,UAAU,MAAM,OAAO,MAAM;IAC/B,KAAK,IAAI;IACT,WAAW;IACX,QAAQ,CAAC,GAAG,YAAY;IACxB,qBAAqB;IACrB,UAAU;IACX,CAAC;AAGF,OAAI,aAAa,aACf,WAAU,QAAQ,QACf,MAAM,CAAC,kBAAkB,MAAM,WAAW,EAAE,WAAW,OAAO,CAAC,CACjE;AAGH,OAAI,QAAQ,WAAW,EAAG;GAE1B,MAAM,QAAQ,QAAQ;GACtB,MAAM,aAAa,KAAK,IAAI,KAAM,KAAM,QAAQ,IAAK;GACrD,MAAM,gBAAgB,QAAQ,MAAM,GAAG,EAAE;AAEzC,WAAQ,KAAK;IACX,UAAU;IACV,SAAS,iBAAiB,SAAS,IAAI,MAAM;IAC7C;IACA,UAAU,cAAc,KAAK,OAAO;KAAE,MAAM;KAAG,MAAM;KAAM,EAAE;IAC7D,UAAU;KACR,eAAe;KACf;KACA;KACD;IACF,CAAC;;AAGJ,SAAO;;CAEV;;;;;;;;AC3DD,SAAS,aAAa,MAA+B;AACnD,KAAI,KAAK,SAAS,EAAG,QAAO;AAE5B,KAAI,uBAAuB,KAAK,KAAK,CAAE,QAAO;AAC9C,KAAI,sBAAsB,KAAK,KAAK,CAAE,QAAO;AAC7C,KAAI,oBAAoB,KAAK,KAAK,IAAI,KAAK,SAAS,IAAI,CAAE,QAAO;AACjE,KAAI,sBAAsB,KAAK,KAAK,IAAI,QAAQ,KAAK,KAAK,CAAE,QAAO;AAEnE,QAAO;;AAOT,MAAa,kBAA6B;CACxC,MAAM;CAEN,MAAM,QAAQ,KAA0C;EACtD,MAAM,QAAQ,MAAM,iBAAiB;GACnC,KAAK,IAAI;GACT,YAAY;IAAC;IAAM;IAAO;IAAM;IAAM;GACvC,CAAC;AAEF,MAAI,MAAM,WAAW,EAAG,QAAO,EAAE;EAEjC,MAAM,WAAW,IAAI,SAAS,kBAAkB;EAChD,MAAM,iBAAiB,MAAM,MAAM,GAAG,SAAS;EAE/C,MAAM,UAAU,IAAI,QAAQ;GAC1B,iBAAiB;IAAE,SAAS;IAAM,QAAQ;IAAM;GAChD,8BAA8B;GAC/B,CAAC;EAGF,MAAM,WAAW,eAAe,KAAK,MAAM,GAAG,IAAI,YAAY,GAAG,IAAI;AACrE,UAAQ,sBAAsB,SAAS;EAGvC,MAAM,YAAoC,EAAE;EAC5C,MAAM,YAAoC,EAAE;EAC5C,MAAM,UAAkC,EAAE;EAC1C,MAAM,SAAiD;GAAE;GAAW;GAAW;GAAS;AAExF,OAAK,MAAM,MAAM,QAAQ,gBAAgB,EAAE;AACzC,QAAK,MAAM,MAAM,GAAG,cAAc,EAAE;IAClC,MAAM,OAAO,GAAG,SAAS;AACzB,QAAI,CAAC,KAAM;IACX,MAAM,OAAO,aAAa,KAAK;AAC/B,QAAI,KAAM,WAAU,SAAS,UAAU,SAAS,KAAK;;AAGvD,QAAK,MAAM,QAAQ,GAAG,yBAAyB,EAAE;IAE/C,MAAM,OAAO,aADA,KAAK,SAAS,CACI;AAC/B,QAAI,KAAM,WAAU,SAAS,UAAU,SAAS,KAAK;;AAGvD,QAAK,MAAM,OAAO,GAAG,YAAY,EAAE;IACjC,MAAM,OAAO,IAAI,SAAS;AAC1B,QAAI,CAAC,KAAM;IACX,MAAM,OAAO,aAAa,KAAK;AAC/B,QAAI,KAAM,SAAQ,SAAS,QAAQ,SAAS,KAAK;;;EAIrD,MAAM,UAAmB,EAAE;AAE3B,OAAK,MAAM,CAAC,YAAY,eAAe,OAAO,QAAQ,OAAO,EAAE;GAC7D,MAAM,QAAQ,OAAO,OAAO,WAAW,CAAC,QAAQ,GAAG,MAAM,IAAI,GAAG,EAAE;AAClE,OAAI,QAAQ,EAAG;GAGf,IAAI,WAA4B;GAChC,IAAI,gBAAgB;AACpB,QAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,WAAW,CACpD,KAAI,QAAQ,eAAe;AACzB,eAAW;AACX,oBAAgB;;AAIpB,OAAI,CAAC,SAAU;GAEf,MAAM,aAAa,KAAK,IAAI,KAAM,gBAAgB,MAAM;AACxD,OAAI,aAAa,GAAK;AAEtB,WAAQ,KAAK;IACX,UAAU;IACV,SAAS,GAAG,WAAW,OAAO,SAAS;IACvC;IACA,UAAU,CAAC;KAAE,MAAM;KAAe,MAAM;KAAM,CAAC;IAC/C,UAAU;KACR;KACA,cAAc;KACd,QAAQ;KACR,YAAY;KACb;IACF,CAAC;;AAGJ,SAAO;;CAEV;;;;;ACzGD,MAAM,iBAAiB;CAAC;CAAM;CAAM;CAAM;CAAQ;AAElD,SAAS,aAAa,WAA4B;AAChD,QAAO,eAAe,MAAM,WAAW,UAAU,WAAW,OAAO,CAAC;;;;;;;AAQtE,SAAS,aAAa,YAAiC;AAErD,KAAI,EADe,WAAW,uBAAuB,CAAC,SAAS,GAC9C,QAAO;CACxB,MAAM,eAAe,WAAW,cAAc,CAAC,SAAS;CACxD,MAAM,aAAa,WAAW,YAAY,CAAC,SAAS;CACpD,MAAM,UAAU,WAAW,yBAAyB,CAAC,SAAS;AAC9D,QAAO,CAAC,gBAAgB,CAAC,cAAc,CAAC;;;;;;;AAQ1C,SAAS,mBAAmB,SAA+B;CACzD,MAAM,0BAAU,IAAI,KAAa;AACjC,MAAK,MAAM,MAAM,QAAQ,gBAAgB,EAAE;EACzC,MAAM,WAAW,GAAG,aAAa;EACjC,MAAM,WAAW,SAAS,MAAM,IAAI,CAAC,KAAK,IAAI;AAE9C,MAAI,mBAAmB,KAAK,SAAS,IAAI,aAAa,GAAG,CACvD,SAAQ,IAAI,SAAS;;AAGzB,QAAO;;;AAIT,SAAS,sBAAsB,eAAuB,WAA6B;CAEjF,MAAM,OAAO,QADD,QAAQ,cAAc,EACR,UAAU;CACpC,MAAM,OAAO;EAAC;EAAO;EAAQ;EAAO;EAAO;CAC3C,MAAM,aAAuB,EAAE;AAE/B,MAAK,MAAM,OAAO,KAAM,YAAW,KAAK,OAAO,IAAI;AAEnD,MAAK,MAAM,OAAO,KAAM,YAAW,KAAK,KAAK,MAAM,UAAU,IAAI,CAAC;AAClE,QAAO;;AAOT,MAAa,mBAA8B;CACzC,MAAM;CAEN,MAAM,QAAQ,KAA0C;EACtD,MAAM,QAAQ,MAAM,iBAAiB;GACnC,KAAK,IAAI;GACT,YAAY;IAAC;IAAM;IAAO;IAAM;IAAM;GACvC,CAAC;AAEF,MAAI,MAAM,WAAW,EAAG,QAAO,EAAE;EAEjC,MAAM,WAAW,IAAI,SAAS,kBAAkB;EAChD,MAAM,iBAAiB,MAAM,MAAM,GAAG,SAAS;EAE/C,MAAM,UAAU,IAAI,QAAQ;GAC1B,iBAAiB;IAAE,SAAS;IAAM,QAAQ;IAAM;GAChD,8BAA8B;GAC/B,CAAC;EAEF,MAAM,WAAW,eAAe,KAAK,MAAM,GAAG,IAAI,YAAY,GAAG,IAAI;AACrE,UAAQ,sBAAsB,SAAS;EAGvC,MAAM,cAAc,mBAAmB,QAAQ;EAE/C,IAAI,gBAAgB;EACpB,IAAI,gBAAgB;EACpB,IAAI,cAAc;EAClB,IAAI,aAAa;AAEjB,OAAK,MAAM,MAAM,QAAQ,gBAAgB,CACvC,MAAK,MAAM,OAAO,GAAG,uBAAuB,EAAE;GAC5C,MAAM,YAAY,IAAI,yBAAyB;AAE/C,OAAI,IAAI,2BAA2B,EAAE;AACnC;AAIA,QADmB,sBAAsB,GAAG,aAAa,EAAE,UAAU,CACtD,MAAM,MAAM,YAAY,IAAI,EAAE,CAAC,CAC5C;UAEG;AACL;AAEA,QAAI,aAAa,UAAU,CACzB;;;EAMR,MAAM,eAAe,gBAAgB;AACrC,MAAI,iBAAiB,EAAG,QAAO,EAAE;EAEjC,MAAM,UAAmB,EAAE;EAC3B,MAAM,WAAW,CAAC;GAAE,MAAM;GAAe,MAAM;GAAM,CAAC;EAGtD,MAAM,WAAW,gBAAgB;EACjC,IAAI;AACJ,MAAI,YAAY,IACd,cAAa;WACJ,YAAY,IACrB,cAAa;MAEb,cAAa;EAIf,MAAM,iBAAiB,KAAK,IAAI,KAAM,KAAO,eAAe,MAAO,IAAK;AAExE,UAAQ,KAAK;GACX,UAAU;GACV,SAAS;GACT,YAAY;GACZ;GACA,UAAU;IACR;IACA;IACA;IACA,eAAe,KAAK,MAAM,WAAW,IAAI,GAAG;IAC7C;GACF,CAAC;AAGF,MAAI,cAAc,GAAG;GACnB,MAAM,cAAc,cAAc;AAClC,WAAQ,KAAK;IACX,UAAU;IACV,SAAS;IACT,YAAY,KAAK,IAAI,KAAM,KAAM,cAAc,IAAK;IACpD;IACA,UAAU;KAAE;KAAa;KAAe;IACzC,CAAC;;AAIJ,MAAI,aAAa,EACf,SAAQ,KAAK;GACX,UAAU;GACV,SAAS;GACT,YAAY;GACZ;GACA,UAAU,EAAE,YAAY;GACzB,CAAC;AAGJ,SAAO;;CAEV;;;;ACxKD,MAAa,+BAA0C;CACrD,MAAM;CAEN,MAAM,QAAQ,KAA0C;EACtD,MAAM,QAAQ,MAAM,iBAAiB;GACnC,KAAK,IAAI;GACT,YAAY;IAAC;IAAM;IAAO;IAAM;IAAM;GACvC,CAAC;AAEF,MAAI,MAAM,WAAW,EAAG,QAAO,EAAE;EAEjC,MAAM,WAAW,IAAI,SAAS,kBAAkB;EAChD,MAAM,iBAAiB,MAAM,MAAM,GAAG,SAAS;EAE/C,MAAM,UAAU,IAAI,QAAQ;GAC1B,iBAAiB;IAAE,SAAS;IAAM,QAAQ;IAAM;GAChD,8BAA8B;GAC/B,CAAC;EAEF,MAAM,WAAW,eAAe,KAAK,MAAM,GAAG,IAAI,YAAY,GAAG,IAAI;AACrE,UAAQ,sBAAsB,SAAS;EAEvC,IAAI,oBAAoB;EACxB,IAAI,qBAAqB;EACzB,IAAI,wBAAwB;EAC5B,MAAM,mBAA6B,EAAE;EACrC,MAAM,sBAAgC,EAAE;AAExC,OAAK,MAAM,MAAM,QAAQ,gBAAgB,EAAE;GACzC,MAAM,UAAU,GAAG,aAAa,CAAC,QAAQ,IAAI,cAAc,KAAK,GAAG;GAEnE,MAAM,gBAAgB,GAAG,qBAAqB,WAAW,aAAa;AACtE,OAAI,cAAc,SAAS,GAAG;AAC5B;AACA,0BAAsB,cAAc;AACpC,QAAI,iBAAiB,SAAS,EAAG,kBAAiB,KAAK,QAAQ;;AAGjE,QAAK,MAAM,OAAO,GAAG,YAAY,EAAE;IACjC,MAAM,WAAW,IAAI,YAAY;AACjC,QAAI,YAAY,YAAY,KAAK,SAAS,SAAS,CAAC,EAAE;AACpD;AACA,SAAI,oBAAoB,SAAS,EAAG,qBAAoB,KAAK,QAAQ;;;;EAK3E,MAAM,UAAmB,EAAE;EAC3B,MAAM,aAAa,eAAe;AAGlC,MAAI,sBAAsB,GAAG;GAE3B,MAAM,aAAa,oBAAoB;GACvC,MAAM,eAAe,KAAK,IAAI,IAAK,qBAAqB,IAAK;GAC7D,MAAM,aAAa,KAAK,IAAI,KAAM,KAAM,aAAa,MAAO,aAAa;AACzE,WAAQ,KAAK;IACX,UAAU;IACV,SAAS;IACT;IACA,UAAU,iBAAiB,KAAK,UAAU;KAAE;KAAM,MAAM;KAAM,EAAE;IAChE,UAAU;KAAE,OAAO;KAAa,WAAW;KAAmB,YAAY;KAAoB;IAC/F,CAAC;;AAGJ,MAAI,yBAAyB,GAAG;GAC9B,MAAM,aAAa,KAAK,IAAI,KAAM,KAAO,wBAAwB,aAAc,IAAK;AACpF,WAAQ,KAAK;IACX,UAAU;IACV,SAAS;IACT;IACA,UAAU,oBAAoB,KAAK,UAAU;KAAE;KAAM,MAAM;KAAM,EAAE;IACnE,UAAU;KAAE,OAAO;KAAsB,YAAY;KAAuB;IAC7E,CAAC;;AAGJ,SAAO;;CAEV;;;;;;;;;;ACpCD,IAAM,qBAAN,MAAmD;CACjD,YAAY,AAAiB,aAAqB;EAArB;;CAE7B,MAAM,SAAS,aAAuC;AAEpD,SAAO,WADU,KAAK,aAAa,aAAa,CACrB;;CAG7B,MAAM,YAAY,aAAoC;AACpD,MAAI,MAAM,KAAK,SAAS,YAAY,CAClC;AAEF,QAAM,MAAM,YAAY;;CAG1B,MAAM,aAAa,aAAoC;AACrD,QAAM,MAAM,aAAa,EAAE,OAAO,MAAM,CAAC;;CAG3C,MAAM,OACJ,aACA,UAA0B,EAAE,EACH;EACzB,MAAM,EAAE,IAAI,OAAO;EAEnB,IAAI;AACJ,MAAI;AACF,SAAM,MAAM,MAAM,aAAa;IAC7B,MAAM;IACN,YAAY,KAAK;IACjB,WAAW;IACZ,CAAC;WACK,KAAc;AACrB,OAAI,eAAe,iBAAiB,IAAI,SAAS,WAC/C,QAAO,EAAE;AAEX,SAAM;;EAGR,MAAM,UAA0B,EAAE;AAElC,OAAK,MAAM,OAAO,IAAI,KACpB,SAAQ,KAAK;GAAE,MAAM,IAAI;GAAM,OAAO,IAAI;GAAM,OAAO,IAAI;GAAO,CAAC;AAErE,OAAK,MAAM,OAAO,IAAI,KACpB,SAAQ,KAAK;GAAE,MAAM,IAAI;GAAM,OAAO,IAAI;GAAM,OAAO,IAAI;GAAO,CAAC;AAGrE,UAAQ,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM;AACzC,SAAO,QAAQ,MAAM,GAAG,EAAE;;CAG5B,MAAM,MAAM,OAAkC;AAI5C,QAAM,IAAI,MACR,6FAED;;;;;;AAWL,eAAsB,aACpB,aACyB;AACzB,QAAO,IAAI,mBAAmB,YAAY;;;;;AC5G5C,MAAM,WAAyB;CAC7B;EACE,OAAO;EACP,SAAS;EACT,OAAO,YAAY,aAAa,KAAK,QAAQ,IAAI,eAAe,KAAK,QAAQ;EAC9E;CACD;EACE,OAAO;EACP,SAAS;EACT,OAAO,YACL,6DAA6D,KAAK,QAAQ;EAC7E;CACD;EACE,OAAO;EACP,SAAS;EACT,OAAO,YAAY,wCAAwC,KAAK,QAAQ;EACzE;CACD;EACE,OAAO;EACP,SAAS;EACT,OAAO,YAAY,0CAA0C,KAAK,QAAQ;EAC3E;CACF;AAMD,MAAa,yBAAoC;CAC/C,MAAM;CAEN,MAAM,QAAQ,KAA0C;EACtD,MAAM,SAAS,MAAM,aAAa,IAAI,YAAY;AAElD,MAAI,CAAE,MAAM,OAAO,SAAS,IAAI,YAAY,CAC1C,QAAO,EAAE;EAIX,MAAM,CAAC,SAAS,YAAY,gBAAgB,MAAM,QAAQ,IAAI;GAC5D,OAAO,OAAO,4CAA4C,EAAE,GAAG,IAAI,CAAC;GACpE,OAAO,OAAO,oCAAoC,EAAE,GAAG,IAAI,CAAC;GAC5D,OAAO,OAAO,oCAAoC,EAAE,GAAG,IAAI,CAAC;GAC7D,CAAC;EAGF,MAAM,iCAAiB,IAAI,KAAqB;AAChD,OAAK,MAAM,UAAU;GAAC,GAAG;GAAS,GAAG;GAAY,GAAG;GAAa,EAAE;GACjE,MAAM,WAAW,eAAe,IAAI,OAAO,KAAK,IAAI;AACpD,kBAAe,IAAI,OAAO,MAAM,WAAW,OAAO,OAAO,MAAM;;EAGjE,MAAM,mBAAmB,eAAe;AACxC,MAAI,qBAAqB,EAAG,QAAO,EAAE;EAErC,MAAM,UAAmB,EAAE;AAE3B,OAAK,MAAM,cAAc,UAAU;GACjC,MAAM,gBAA0B,EAAE;AAElC,QAAK,MAAM,CAAC,MAAM,YAAY,eAC5B,KAAI,WAAW,KAAK,QAAQ,CAC1B,eAAc,KAAK,KAAK;AAK5B,OAAI,cAAc,SAAS,EAAG;GAE9B,MAAM,aAAa,KAAK,IAAI,KAAM,KAAO,cAAc,SAAS,mBAAoB,IAAK;AAEzF,WAAQ,KAAK;IACX,UAAU;IACV,SAAS,WAAW;IACpB;IACA,UAAU,cAAc,MAAM,GAAG,EAAE,CAAC,KAAK,UAAU;KAAE;KAAM,MAAM;KAAM,EAAE;IACzE,UAAU;KACR,OAAO,WAAW;KAClB,WAAW,cAAc;KAC1B;IACF,CAAC;;AAGJ,SAAO;;CAEV;;;;;ACzFD,SAAS,kBAAkB,OAA8B;CACvD,MAAM,uBAAO,IAAI,KAAa;AAC9B,MAAK,MAAM,KAAK,OAAO;EACrB,MAAM,QAAQ,EAAE,MAAM,IAAI;AAC1B,MAAI,MAAM,SAAS,EAAG;AAEtB,MAAI,MAAM,OAAO,SAAS,MAAM,UAAU,EAExC,MAAK,IAAI,OAAO,MAAM,KAAK;WAClB,MAAM,OAAO,MAEtB,MAAK,IAAI,MAAM,GAAI;;AAGvB,QAAO;;;AAIT,SAAS,UAAU,KAAqB;AACtC,QAAO,IAAI,MAAM,IAAI,CAAC,KAAK,CAAE,aAAa;;;AAI5C,SAAS,UAAU,YAAmC;CACpD,MAAM,MAAM;EAAC;EAAS;EAAU;EAAQ;EAAS;EAAc;EAAe;EAAS;EAAS;CAChG,MAAM,QAAkB,EAAE;AAC1B,MAAK,MAAM,OAAO,WAChB,KAAI,IAAI,SAAS,UAAU,IAAI,CAAC,CAAE,OAAM,KAAK,IAAI;AAInD,QADkB,IAAI,IAAI,MAAM,KAAK,MAAM,UAAU,EAAE,CAAC,QAAQ,MAAM,GAAG,CAAC,CAAC,CAC1D,QAAQ,IAAI,QAAQ,EAAE;;;AAIzC,SAAS,mBAAmB,OAA2B;CACrD,MAAM,iBAAiB;CACvB,MAAM,8BAAc,IAAI,KAAa;CACrC,IAAI,QAAQ;AACZ,MAAK,MAAM,KAAK,MACd,KAAI,eAAe,KAAK,EAAE,EAAE;AAC1B;EAEA,MAAM,QAAQ,EAAE,MAAM,yCAAyC;AAC/D,MAAI,MAAO,aAAY,IAAI,MAAM,GAAI;;AAGzC,QAAO,SAAS,IAAI,MAAM,KAAK,YAAY,GAAG,EAAE;;;AAIlD,SAAS,iBAAiB,YAAmC;CAC3D,MAAM,gBAAgB;EAEpB;EAAU;EAAe;EAEzB;EAAW;EAAY;EAAc;EAAgB;EAAW;EAAY;EAAW;EAEvF;EAAQ;EAAQ;EACjB;CACD,MAAM,QAAkB,EAAE;AAC1B,MAAK,MAAM,OAAO,WAChB,KAAI,cAAc,SAAS,UAAU,IAAI,CAAC,CAAE,OAAM,KAAK,IAAI;AAI7D,QADkB,IAAI,IAAI,MAAM,KAAK,MAAM,UAAU,EAAE,CAAC,CAAC,CACxC,QAAQ,IAAI,QAAQ,EAAE;;AAOzC,MAAa,wBAAmC;CAC9C,MAAM;CAEN,MAAM,QAAQ,KAA0C;EAEtD,MAAM,QAAQ,MAAM,iBAAiB;GACnC,KAAK,IAAI;GACT,YAAY;IAAC;IAAM;IAAM;IAAO;IAAO;IAAM;IAAM;IAAM;IAAK;GAC/D,CAAC;EAEF,MAAM,aAAa,kBAAkB,MAAM;EAG3C,MAAM,UAAU,UAAU,WAAW;EACrC,MAAM,cAAc,mBAAmB,MAAM;EAC7C,MAAM,YAAY,iBAAiB,WAAW;EAE9C,IAAI,kBAAsC;EAC1C,IAAI,iBAA2B,EAAE;AAEjC,MAAI,QAAQ,SAAS,GAAG;AACtB,qBAAkB;AAClB,oBAAiB;aACR,YAAY,SAAS,GAAG;AACjC,qBAAkB;AAClB,oBAAiB;aACR,UAAU,SAAS,GAAG;AAC/B,qBAAkB;AAClB,oBAAiB;;AAGnB,MAAI,CAAC,gBAAiB,QAAO,EAAE;EAG/B,MAAM,SAAS,MAAM,aAAa,IAAI,YAAY;EAClD,MAAM,SAAS,MAAM,OAAO,SAAS,IAAI,YAAY;EAErD,IAAI,aAAa;EACjB,MAAM,WAA2C,eAC9C,MAAM,GAAG,EAAE,CACX,KAAK,SAAS;GAAE,MAAM;GAAK,MAAM;GAAM,EAAE;AAE5C,MAAI,QAAQ;GACV,MAAM,gBAAgB,MAAM,OAAO,OACjC,0DACA,EAAE,GAAG,IAAI,CACV;AACD,OAAI,cAAc,SAAS,GAAG;AAC5B,iBAAa;AACb,SAAK,MAAM,KAAK,cAAc,MAAM,GAAG,EAAE,CACvC,UAAS,KAAK;KAAE,MAAM,EAAE;KAAM,MAAM;KAAM,CAAC;;;EAMjD,MAAM,uBAAO,IAAI,KAAa;EAC9B,MAAM,UAAU,SAAS,QAAQ,EAAE,WAAW;AAC5C,OAAI,KAAK,IAAI,KAAK,CAAE,QAAO;AAC3B,QAAK,IAAI,KAAK;AACd,UAAO;IACP;AAEF,SAAO,CACL;GACE,UAAU;GACV,SAAS,aAAa,gBAAgB;GACtC;GACA,UAAU,QAAQ,MAAM,GAAG,EAAE;GAC7B,UAAU;IACR,qBAAqB;IACrB,QAAQ;IACT;GACF,CACF;;CAEJ;AAED,SAAS,aAAa,GAAwB;AAC5C,SAAQ,GAAR;EACE,KAAK,MACH,QAAO;EACT,KAAK,gBACH,QAAO;EACT,KAAK,cACH,QAAO;;;;;;AC1Jb,MAAM,iBAAiB;CACrB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;AASD,SAAS,oBAAoB,UAAwC;CACnE,MAAM,uBAAO,IAAI,KAAa;AAC9B,QAAO,SAAS,QAAQ,MAAM;EAC5B,MAAM,MAAM,GAAG,EAAE,KAAK,GAAG,EAAE,QAAQ;AACnC,MAAI,KAAK,IAAI,IAAI,CAAE,QAAO;AAC1B,OAAK,IAAI,IAAI;AACb,SAAO;GACP;;;;;;;AAQJ,SAAS,uBACP,aACmB;CACnB,MAAM,0BAAU,IAAI,KAA8B;AAElD,MAAK,MAAM,SAAS,aAAa;EAC/B,MAAM,MAAM,GAAG,MAAM,SAAS,GAAG,MAAM;EACvC,MAAM,WAAW,QAAQ,IAAI,IAAI;AAEjC,MAAI,CAAC,SACH,SAAQ,IAAI,KAAK,MAAM;OAClB;GAEL,MAAM,SACJ,MAAM,aAAa,SAAS,aAAa,QAAQ;GACnD,MAAM,iBAAiB,oBAAoB,CACzC,GAAG,SAAS,UACZ,GAAG,MAAM,SACV,CAAC;AACF,WAAQ,IAAI,KAAK;IAAE,GAAG;IAAQ,UAAU;IAAgB,CAAC;;;AAI7D,QAAO,MAAM,KAAK,QAAQ,QAAQ,CAAC;;;;;;AAWrC,SAAS,kBAAkB,UAAkD;CAC3E,MAAM,QAAQ,EAAE,GAAG,SAAS,OAAO;AAEnC,MAAK,MAAM,SAAS,SAAS,aAAa;EACxC,MAAM,OAAO,MAAM,YAAY,EAAE;AAEjC,MAAI,MAAM,aAAa,SAAS;AAC9B,OAAI,CAAC,MAAM,YAAY,MAAM,aAAa,WACxC;QAAI,OAAO,KAAK,aAAa,SAC3B,OAAM,WAAW,KAAK;;AAG1B,OAAI,CAAC,MAAM,aAAa,OAAO,KAAK,cAAc,SAChD,OAAM,YAAY,KAAK;AAEzB,OAAI,CAAC,MAAM,cAAc,OAAO,KAAK,eAAe,SAClD,OAAM,aAAa,KAAK;AAE1B,OAAI,CAAC,MAAM,kBAAkB,OAAO,KAAK,mBAAmB,SAC1D,OAAM,iBAAiB,KAAK;AAE9B,OAAI,CAAC,MAAM,WAET;QAAI,OAAO,KAAK,cAAc,SAC5B,OAAM,YAAY,KAAK;aAEvB,OAAO,KAAK,eAAe,YAC3B,KAAK,eAAe,WACpB,OAAO,KAAK,YAAY,SAGxB,OAAM,YAAa,KAAK,QAAmB,MAAM,IAAI,CAAC;;;AAK5D,MAAI,MAAM,aAAa,WACrB;OAAI,CAAC,MAAM,cAAc,OAAO,KAAK,eAAe,SAClD,OAAM,aAAa,KAAK;;;AAK9B,QAAO;EAAE,GAAG;EAAU;EAAO;;;;;;AAW/B,SAAS,yBACP,UACoB;CACpB,MAAM,OAAO,EAAE,GAAG,SAAS,cAAc;AAEzC,MAAK,MAAM,SAAS,SAAS,YAC3B,KAAI,MAAM,aAAa,gBAAgB;AACrC,MACE,CAAC,KAAK,WACN,OAAO,MAAM,UAAU,wBAAwB,SAE/C,MAAK,UAAU,MAAM,SAAS;AAEhC,MACE,KAAK,OAAO,WAAW,KACvB,MAAM,QAAQ,MAAM,UAAU,OAAO,CAErC,MAAK,SAAS,MAAM,SAAS;;AAKnC,QAAO;EAAE,GAAG;EAAU,cAAc;EAAM;;;;;;;;;;;AAgB5C,eAAsB,mBACpB,aACA,SAC6B;CAK7B,MAAM,YAAY,MAAM,cAAc,gBAJ1B;EAAE;EAAa;EAAS,EACd,eAAe,YAAY,CAGwB;CAYzE,MAAM,WAAW,yBAHC,kBANuB;EACvC,GAAG;EACH,aAAa,uBAAuB,UAAU,YAAY;EAC3D,CAGgD,CAGG;AAGpD,QAAO,yBAAyB,MAAM,SAAS;;;;;AC7MjD,MAAa,eAAe;AAC5B,MAAa,aAAa;;;;;;;;;;AAW1B,eAAsB,iBACpB,UACA,SACe;CACf,MAAM,UAAU,GAAG,aAAa,IAAI,QAAQ,IAAI;AAEhD,KAAI,CAAC,WAAW,SAAS,EAAE;AACzB,QAAM,UAAU,UAAU,UAAU,MAAM,QAAQ;AAClD;;CAGF,MAAM,WAAW,MAAM,SAAS,UAAU,QAAQ;CAClD,MAAM,WAAW,SAAS,QAAQ,aAAa;CAC/C,MAAM,SAAS,SAAS,QAAQ,WAAW;AAG3C,KAAI,aAAa,MAAM,WAAW,IAAI;AAEpC,QAAM,UAAU,UAAU,YADR,SAAS,SAAS,KAAK,GAAG,OAAO,UACF,UAAU,MAAM,QAAQ;AACzE;;CAGF,MAAM,SAAS,SAAS,MAAM,GAAG,SAAS;CAC1C,MAAM,QAAQ,SAAS,MAAM,SAAS,GAAkB;AACxD,OAAM,UAAU,UAAU,SAAS,UAAU,OAAO,QAAQ;;;;;;;;;AChC9D,SAAgB,gBACd,UACgD;CAChD,MAAM,WAA2D,EAAE;AACnE,MAAK,MAAM,SAAS,UAAU;EAC5B,MAAM,OAAO,MAAM;AACnB,MACE,QACA,OAAO,KAAK,eAAe,YAC3B,OAAO,KAAK,kBAAkB,SAE9B,UAAS,KAAK;GACZ,YAAY,KAAK;GACjB,SAAS,KAAK;GACf,CAAC;;AAGN,QAAO;;;;;;;AAQT,SAAS,wBACP,OACA,OACS;AACT,KAAI,MAAM,aAAa,QAAS,QAAO;CACvC,MAAM,OAAO,MAAM,YAAY,EAAE;AAEjC,KAAI,OAAO,KAAK,gBAAgB,YAAY,MAAM,aAAa,UAAW,QAAO;AACjF,KAAI,OAAO,KAAK,iBAAiB,YAAY,MAAM,UAAW,QAAO;AACrE,KAAI,OAAO,KAAK,iBAAiB,YAAY,MAAM,UAAW,QAAO;AACrE,KAAI,OAAO,KAAK,sBAAsB,YAAY,MAAM,eAAgB,QAAO;AAE/E,KAAI,OAAO,KAAK,kBAAkB,SAAU,QAAO;AAEnD,QAAO;;;;;;;AAQT,SAAgB,sBAAsB,OAAiC;AACrE,KAAI,MAAM,aAAa,aAAa,MAAM,QAAQ,WAAW,eAAe,CAC1E,QAAO;AAET,KAAI,MAAM,YAAY,OAAO,MAAM,SAAS,kBAAkB,SAC5D,QAAO;AAET,QAAO;;;;;;AAOT,SAAgB,sBACd,UACA,qBACU;CACV,MAAM,WAAW,SAAS,YAAY,QACnC,MAAM,EAAE,cAAc,oBACxB;CAED,MAAM,QAAkB,EAAE;CAG1B,MAAM,IAAI,SAAS;AAQnB,KANE,EAAE,aAAa,aACf,QAAQ,EAAE,UAAU,IACpB,QAAQ,EAAE,UAAU,IACpB,QAAQ,EAAE,eAAe,IACzB,QAAQ,EAAE,WAAW,EAET;AACZ,QAAM,KAAK,WAAW;AACtB,MAAI,EAAE,aAAa,UAAW,OAAM,KAAK,eAAe,EAAE,WAAW;AACrE,MAAI,EAAE,UAAW,OAAM,KAAK,gBAAgB,EAAE,YAAY;AAC1D,MAAI,EAAE,UAAW,OAAM,KAAK,YAAY,EAAE,YAAY;AACtD,MAAI,EAAE,eAAgB,OAAM,KAAK,sBAAsB,EAAE,iBAAiB;AAC1E,MAAI,EAAE,WAAY,OAAM,KAAK,kBAAkB,EAAE,aAAa;AAC9D,QAAM,KAAK,GAAG;;CAIhB,MAAM,IAAI,SAAS;AAInB,KAFE,QAAQ,EAAE,QAAQ,KAAK,EAAE,QAAQ,UAAU,KAAK,GAE7B;AACnB,QAAM,KAAK,kBAAkB;AAC7B,MAAI,EAAE,QAAS,OAAM,KAAK,cAAc,EAAE,UAAU;AACpD,MAAI,EAAE,UAAU,EAAE,OAAO,SAAS,EAChC,OAAM,KAAK,aAAa,EAAE,OAAO,KAAK,KAAK,GAAG;AAEhD,QAAM,KAAK,GAAG;;CAKhB,MAAM,8BAAc,IAAI,KAAuB;AAC/C,MAAK,MAAM,SAAS,UAAU;AAC5B,MAAI,MAAM,aAAa,eAAgB;AACvC,MAAI,MAAM,aAAa,WAAW,CAAC,wBAAwB,OAAO,EAAE,CAAE;AACtE,MAAI,sBAAsB,MAAM,CAAE;EAClC,MAAM,OAAO,YAAY,IAAI,MAAM,SAAS,IAAI,EAAE;AAClD,OAAK,KAAK,MAAM,QAAQ;AACxB,cAAY,IAAI,MAAM,UAAU,KAAK;;AAGvC,KAAI,YAAY,OAAO,GAAG;AACxB,QAAM,KAAK,iBAAiB;AAC5B,OAAK,MAAM,CAAC,UAAU,aAAa,YACjC,MAAK,MAAM,WAAW,SACpB,OAAM,KAAK,OAAO,SAAS,MAAM,UAAU;AAG/C,QAAM,KAAK,GAAG;;CAIhB,MAAM,WAAW,gBAAgB,SAAS;AAE1C,KAAI,SAAS,SAAS,GAAG;AACvB,QAAM,KAAK,cAAc;AACzB,OAAK,MAAM,OAAO,SAChB,OAAM,KAAK,OAAO,IAAI,WAAW,QAAQ,IAAI,QAAQ,IAAI;AAE3D,QAAM,KAAK,GAAG;;AAGhB,QAAO;;;;;AChIT,SAASC,WAAS,UAA8B,qBAA6B;CAC3E,MAAM,WAAW,SAAS,YAAY,QACnC,MAAM,EAAE,cAAc,oBACxB;CAID,MAAM,8BAAc,IAAI,KAAgC;AACxD,MAAK,MAAM,SAAS,UAAU;AAC5B,MAAI,MAAM,aAAa,eAAgB;AACvC,MAAI,MAAM,aAAa,SAAS;GAC9B,MAAM,OAAO,MAAM,YAAY,EAAE;AAEjC,OAAI,OAAO,KAAK,gBAAgB,SAAU;AAC1C,OAAI,OAAO,KAAK,iBAAiB,SAAU;AAC3C,OAAI,OAAO,KAAK,iBAAiB,SAAU;AAC3C,OAAI,OAAO,KAAK,sBAAsB,SAAU;AAChD,OAAI,OAAO,KAAK,kBAAkB,SAAU;;AAE9C,MAAI,sBAAsB,MAAM,CAAE;EAClC,MAAM,OAAO,YAAY,IAAI,MAAM,SAAS,IAAI,EAAE;AAClD,OAAK,KAAK,MAAM;AAChB,cAAY,IAAI,MAAM,UAAU,KAAK;;CAGvC,MAAM,mBAAsC,EAAE;AAC9C,MAAK,MAAM,CAAC,UAAU,YAAY,YAChC,kBAAiB,KAAK;EAAE;EAAU;EAAS,CAAC;CAG9C,MAAM,WACJ,SAAS,MAAM,aAAa,aAC5B,QAAQ,SAAS,MAAM,UAAU,IACjC,QAAQ,SAAS,MAAM,UAAU,IACjC,QAAQ,SAAS,MAAM,eAAe,IACtC,QAAQ,SAAS,MAAM,WAAW;CAEpC,MAAM,kBACJ,QAAQ,SAAS,aAAa,QAAQ,KACrC,SAAS,aAAa,QAAQ,UAAU,KAAK;AAEhD,QAAO;EACL,OAAO,SAAS;EAChB,cAAc,SAAS;EACvB;EACA;EACA;EACD;;AAOH,SAAgB,eACd,UACA,qBACQ;CACR,MAAM,OAAOA,WAAS,UAAU,oBAAoB;CACpD,MAAM,QAAkB,EAAE;AAE1B,OAAM,KAAK,oBAAoB;AAG/B,KAAI,KAAK,UAAU;AACjB,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,WAAW;AACtB,MAAI,KAAK,MAAM,aAAa,UAC1B,OAAM,KAAK,eAAe,KAAK,MAAM,WAAW;AAElD,MAAI,KAAK,MAAM,UAAW,OAAM,KAAK,gBAAgB,KAAK,MAAM,YAAY;AAC5E,MAAI,KAAK,MAAM,UAAW,OAAM,KAAK,YAAY,KAAK,MAAM,YAAY;AACxE,MAAI,KAAK,MAAM,eAAgB,OAAM,KAAK,sBAAsB,KAAK,MAAM,iBAAiB;AAC5F,MAAI,KAAK,MAAM,WAAY,OAAM,KAAK,kBAAkB,KAAK,MAAM,aAAa;;AAIlF,KAAI,KAAK,iBAAiB,SAAS,GAAG;AACpC,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,iBAAiB;AAC5B,OAAK,MAAM,SAAS,KAAK,iBACvB,MAAK,MAAM,SAAS,MAAM,QACxB,OAAM,KAAK,OAAO,MAAM,SAAS,MAAM,MAAM,UAAU;;AAM7D,KAAI,KAAK,iBAAiB;AACxB,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,kBAAkB;AAC7B,MAAI,KAAK,aAAa,QACpB,OAAM,KAAK,cAAc,KAAK,aAAa,UAAU;AAEvD,MAAI,KAAK,aAAa,UAAU,KAAK,aAAa,OAAO,SAAS,EAChE,OAAM,KAAK,aAAa,KAAK,aAAa,OAAO,KAAK,KAAK,GAAG;;AAIlE,QAAO,MAAM,KAAK,KAAK,GAAG;;;;;AC5G5B,SAAS,SAAS,UAA8B,qBAA6B;CAC3E,MAAM,WAAW,SAAS,YAAY,QACnC,MAAM,EAAE,cAAc,oBACxB;CAED,MAAM,WAAW,gBAAgB,SAAS;CAE1C,MAAM,cAAc,QAClB,SAAS,QAAQ,MAAM,EAAE,aAAa,IAAI;CAE5C,MAAM,qBAAqB,WAAW,UAAU;CAChD,MAAM,oBAAoB,WAAW,SAAS;CAC9C,MAAM,oBAAoB,WAAW,UAAU;CAC/C,MAAM,iBAAiB,SAAS,QAC7B,MACC,EAAE,QAAQ,aAAa,CAAC,SAAS,MAAM,IACtC,EAAE,aAAa,WAAW,EAAE,QAAQ,aAAa,CAAC,SAAS,SAAS,CACxE;CAED,MAAM,aACJ,QAAQ,SAAS,MAAM,WAAW,IAAI,mBAAmB,SAAS;CAEpE,MAAM,sBACJ,QAAQ,SAAS,aAAa,QAAQ,KACrC,SAAS,aAAa,QAAQ,UAAU,KAAK;CAEhD,MAAM,eACJ,kBAAkB,SAAS,KAAK,kBAAkB,SAAS;AAE7D,QAAO;EACL;EACA,YAAY,SAAS,MAAM,cAAc;EACzC;EACA;EACA;EACA;EACA,cAAc,SAAS;EACvB;EACA;EACA;EACD;;AAOH,SAAgB,eACd,UACA,qBACQ;CACR,MAAM,OAAO,SAAS,UAAU,oBAAoB;CACpD,MAAM,QAAkB,EAAE;AAE1B,OAAM,KAAK,cAAc;AAGzB,KAAI,KAAK,SAAS,SAAS,GAAG;AAC5B,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,cAAc;AACzB,OAAK,MAAM,OAAO,KAAK,SACrB,OAAM,KAAK,OAAO,IAAI,WAAW,QAAQ,IAAI,QAAQ,IAAI;;AAK7D,KAAI,KAAK,YAAY;AACnB,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,aAAa;AACxB,MAAI,KAAK,WAAY,OAAM,KAAK,kBAAkB,KAAK,aAAa;AACpE,OAAK,MAAM,SAAS,KAAK,mBACvB,OAAM,KAAK,KAAK,MAAM,UAAU;;AAKpC,KAAI,KAAK,qBAAqB;AAC5B,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,uBAAuB;AAClC,MAAI,KAAK,aAAa,QACpB,OAAM,KAAK,mBAAmB,KAAK,aAAa,UAAU;AAE5D,MAAI,KAAK,aAAa,UAAU,KAAK,aAAa,OAAO,SAAS,EAChE,OAAM,KAAK,aAAa,KAAK,aAAa,OAAO,KAAK,KAAK,GAAG;;AAKlE,KAAI,KAAK,cAAc;AACrB,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,gBAAgB;AAC3B,OAAK,MAAM,SAAS,KAAK,kBACvB,OAAM,KAAK,iBAAiB,MAAM,UAAU;AAE9C,OAAK,MAAM,SAAS,KAAK,kBACvB,OAAM,KAAK,kBAAkB,MAAM,UAAU;;AAKjD,KAAI,KAAK,eAAe,SAAS,GAAG;AAClC,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,kBAAkB;AAC7B,OAAK,MAAM,SAAS,KAAK,eACvB,OAAM,KAAK,KAAK,MAAM,UAAU;;AAKpC,OAAM,KAAK,GAAG;AACd,OAAM,KAAK,gBAAgB;AAC3B,OAAM,KAAK,qEAAqE;AAEhF,QAAO,MAAM,KAAK,KAAK,GAAG;;;;;;;;;;;;;AC5G5B,SAAgB,gBACd,UACA,qBACQ;CACR,MAAM,cAAc,KACjB,KAAK;EACJ,aAAa;EACb,OAAO;EACP,aAAa;EACd,CAAC,CACD,SAAS;CAEZ,MAAM,YAAY,sBAAsB,UAAU,oBAAoB;AAEtE,QAAO,UAAU,SAAS,KAAK,UAAU,UAAU,SAAS,OAAO,GACjE,WAAU,KAAK;AAIjB,QAAO,QAAQ,YAAY,WAFd,UAAU,KAAK,KAAK,CAEU;;;;;;;;;;;;ACrB7C,SAAgB,gBACd,UACA,qBACQ;CACR,MAAM,QAAkB,EAAE;AAE1B,OAAM,KAAK,iEAAiE;AAC5E,OAAM,KAAK,GAAG;AACd,OAAM,KAAK,yBAAyB;AACpC,OAAM,KAAK,GAAG;CAEd,MAAM,YAAY,sBAAsB,UAAU,oBAAoB;AAEtE,QAAO,UAAU,SAAS,KAAK,UAAU,UAAU,SAAS,OAAO,GACjE,WAAU,KAAK;AAEjB,OAAM,KAAK,GAAG,UAAU;AAExB,QAAO,MAAM,KAAK,KAAK,GAAG;;;;;;;;;;;;;AChB5B,SAAgB,cACd,UACA,qBACQ;CAKR,MAAM,cAAc,KACjB,KAAK;EACJ,MAAM;EACN,aANF;EAOC,CAAC,CACD,SAAS;CAEZ,MAAM,YAAY,sBAAsB,UAAU,oBAAoB;AAEtE,QAAO,UAAU,SAAS,KAAK,UAAU,UAAU,SAAS,OAAO,GACjE,WAAU,KAAK;AAIjB,QAAO,QAAQ,YAAY,WAFd,UAAU,KAAK,KAAK,CAEU;;;;;;;;;;;;ACvB7C,SAAgB,iBACd,UACA,qBACQ;CACR,MAAM,cAAc,KACjB,KAAK;EACJ,aAAa;EACb,SAAS;GAAC;GAAU;GAAW;GAAW;EAC3C,CAAC,CACD,SAAS;CAEZ,MAAM,YAAY,sBAAsB,UAAU,oBAAoB;AAEtE,QAAO,UAAU,SAAS,KAAK,UAAU,UAAU,SAAS,OAAO,GACjE,WAAU,KAAK;AAIjB,QAAO,QAAQ,YAAY,WAFd,UAAU,KAAK,KAAK,CAEU;;;;;;;;;;;;ACnB7C,SAAgB,cACd,UACA,qBACQ;CACR,MAAM,QAAkB,EAAE;AAE1B,OAAM,KAAK,qCAAqC;AAChD,OAAM,KAAK,GAAG;CAEd,MAAM,YAAY,sBAAsB,UAAU,oBAAoB;AAEtE,QAAO,UAAU,SAAS,KAAK,UAAU,UAAU,SAAS,OAAO,GACjE,WAAU,KAAK;AAEjB,OAAM,KAAK,GAAG,UAAU;AAExB,QAAO,MAAM,KAAK,KAAK,GAAG;;;;;ACQ5B,MAAa,qBAA+D;CAC1E,QAAQ;EACN,QAAQ;EACR,UAAU;EACV,UAAU;EACX;CACD,QAAQ;EACN,QAAQ;EACR,UAAU;EACV,UAAU;EACX;CACD,QAAQ;EACN,QAAQ;EACR,UAAU,KAAK,KAAK,WAAW,SAAS,iBAAiB;EACzD,UAAU;EACX;CACD,SAAS;EACP,QAAQ;EACR,UAAU,KAAK,KAAK,WAAW,0BAA0B;EACzD,UAAU;EACX;CACD,QAAQ;EACN,QAAQ;EACR,UAAU,KAAK,KAAK,WAAW,cAAc,WAAW;EACxD,UAAU;EACX;CACD,UAAU;EACR,QAAQ;EACR,UAAU,KAAK,KAAK,aAAa,SAAS,gBAAgB;EAC1D,UAAU;EACX;CACD,OAAO;EACL,QAAQ;EACR,UAAU,KAAK,KAAK,UAAU,gBAAgB;EAC9C,UAAU;EACX;CACF;;;;;;;AAYD,eAAsB,KACpB,UACA,SACqB;CACrB,MAAM,YAAY,QAAQ,uBAAuB;CACjD,MAAM,UAA0B,QAAQ,WAAW,CAAC,UAAU,SAAS;CAGvE,MAAM,WAAmC,EAAE;AAC3C,MAAK,MAAM,UAAU,QAEnB,UAAS,UADK,mBAAmB,QACR,OAAO,UAAU,UAAU;CAItD,MAAM,WAAW,SAAS,aAAa;CACvC,MAAM,WAAW,SAAS,aAAa;AAEvC,KAAI,QAAQ,OACV,QAAO;EAAE;EAAU;EAAU;EAAU,cAAc,EAAE;EAAE;CAI3D,MAAM,eAAyB,EAAE;AACjC,MAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,QAAQ,mBAAmB;EACjC,MAAM,WAAW,KAAK,KAAK,QAAQ,WAAW,MAAM,SAAS;AAE7D,QAAM,MAAM,KAAK,QAAQ,SAAS,EAAE,EAAE,WAAW,MAAM,CAAC;AACxD,MAAI,MAAM,aAAa,SACrB,OAAM,UAAU,UAAU,SAAS,SAAU,QAAQ;MAErD,OAAM,iBAAiB,UAAU,SAAS,QAAS;AAGrD,eAAa,KAAK,SAAS;;AAG7B,QAAO;EAAE;EAAU;EAAU;EAAU;EAAc"}
|
package/dist/index.d.ts
CHANGED
|
@@ -151,6 +151,10 @@ interface EzSearchBridge {
|
|
|
151
151
|
* Ensures an index exists: indexes the project if no index is present (EXTR-10).
|
|
152
152
|
*/
|
|
153
153
|
ensureIndex(projectPath: string): Promise<void>;
|
|
154
|
+
/**
|
|
155
|
+
* Unconditionally re-indexes the project so search results reflect current file state.
|
|
156
|
+
*/
|
|
157
|
+
refreshIndex(projectPath: string): Promise<void>;
|
|
154
158
|
/**
|
|
155
159
|
* Semantic (and hybrid) search over the indexed project.
|
|
156
160
|
*/
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/core/schema.ts","../src/core/registry.ts","../src/extractors/types.ts","../src/core/pipeline.ts","../src/core/ez-search-bridge.ts","../src/utils/fs.ts","../src/extractors/index.ts","../src/emitters/types.ts","../src/emitters/index.ts","../src/emitters/writer.ts"],"mappings":";;;cAMa,wBAAA,EAAwB,CAAA,CAAA,OAAA;;;;;;;;;cAUxB,iBAAA,EAAiB,CAAA,CAAA,SAAA;;;;cAKjB,qBAAA,EAAqB,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;cASrB,eAAA,EAAe,CAAA,CAAA,SAAA;;;;;;;;cASf,sBAAA,EAAsB,CAAA,CAAA,SAAA;;;;;cAUtB,wBAAA,EAAwB,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAczB,kBAAA,GAAqB,CAAA,CAAE,KAAA,QAAa,wBAAA;AAAA,KACpC,WAAA,GAAc,CAAA,CAAE,KAAA,QAAa,iBAAA;AAAA,KAC7B,eAAA,GAAkB,CAAA,CAAE,KAAA,QAAa,qBAAA;AAAA,KACjC,SAAA,GAAY,CAAA,CAAE,KAAA,QAAa,eAAA;AAAA,KAC3B,gBAAA,GAAmB,CAAA,CAAE,KAAA,QAAa,sBAAA;AAAA,KAClC,kBAAA,GAAqB,CAAA,CAAE,KAAA,QAAa,wBAAA;;;;;AA9DhD;;iBCIgB,cAAA,CAAe,WAAA,WAAsB,kBAAA;;;;;iBA4BrC,aAAA,CACd,QAAA,EAAU,kBAAA,EACV,KAAA,EAAO,IAAA,CAAK,eAAA,UACX,kBAAA;;;UCnCc,gBAAA;;EAEf,cAAA;AAAA;AAAA,UAOe,iBAAA;EFToB;EEWnC,WAAA;EACA,OAAA,GAAU,gBAAA;AAAA;;;;;;UAYK,SAAA;;EAEf,IAAA;EACA,OAAA,CAAQ,GAAA,EAAK,iBAAA,GAAoB,OAAA,CAAQ,IAAA,CAAK,eAAA;AAAA;;;;AF3BhD;;;;;;;;iBGmLsB,kBAAA,CACpB,WAAA,UACA,OAAA,GAAU,gBAAA,GACT,OAAA,CAAQ,kBAAA;;;UC9KM,YAAA;EACf,IAAA;EACA,KAAA;EACA,KAAA;AAAA;AAAA,UAOe,cAAA;EJlBoB;;;EIsBnC,QAAA,CAAS,WAAA,WAAsB,OAAA;;;;EAK/B,WAAA,CAAY,WAAA,WAAsB,OAAA;;;;EAKlC,MAAA,CAAO,KAAA,UAAe,OAAA;IAAY,CAAA;EAAA,IAAe,OAAA,CAAQ,YAAA;;;;;;EAOzD,KAAA,CAAM,IAAA,WAAe,OAAA;AAAA;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/core/schema.ts","../src/core/registry.ts","../src/extractors/types.ts","../src/core/pipeline.ts","../src/core/ez-search-bridge.ts","../src/utils/fs.ts","../src/extractors/index.ts","../src/emitters/types.ts","../src/emitters/index.ts","../src/emitters/writer.ts"],"mappings":";;;cAMa,wBAAA,EAAwB,CAAA,CAAA,OAAA;;;;;;;;;cAUxB,iBAAA,EAAiB,CAAA,CAAA,SAAA;;;;cAKjB,qBAAA,EAAqB,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;cASrB,eAAA,EAAe,CAAA,CAAA,SAAA;;;;;;;;cASf,sBAAA,EAAsB,CAAA,CAAA,SAAA;;;;;cAUtB,wBAAA,EAAwB,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAczB,kBAAA,GAAqB,CAAA,CAAE,KAAA,QAAa,wBAAA;AAAA,KACpC,WAAA,GAAc,CAAA,CAAE,KAAA,QAAa,iBAAA;AAAA,KAC7B,eAAA,GAAkB,CAAA,CAAE,KAAA,QAAa,qBAAA;AAAA,KACjC,SAAA,GAAY,CAAA,CAAE,KAAA,QAAa,eAAA;AAAA,KAC3B,gBAAA,GAAmB,CAAA,CAAE,KAAA,QAAa,sBAAA;AAAA,KAClC,kBAAA,GAAqB,CAAA,CAAE,KAAA,QAAa,wBAAA;;;;;AA9DhD;;iBCIgB,cAAA,CAAe,WAAA,WAAsB,kBAAA;;;;;iBA4BrC,aAAA,CACd,QAAA,EAAU,kBAAA,EACV,KAAA,EAAO,IAAA,CAAK,eAAA,UACX,kBAAA;;;UCnCc,gBAAA;;EAEf,cAAA;AAAA;AAAA,UAOe,iBAAA;EFToB;EEWnC,WAAA;EACA,OAAA,GAAU,gBAAA;AAAA;;;;;;UAYK,SAAA;;EAEf,IAAA;EACA,OAAA,CAAQ,GAAA,EAAK,iBAAA,GAAoB,OAAA,CAAQ,IAAA,CAAK,eAAA;AAAA;;;;AF3BhD;;;;;;;;iBGmLsB,kBAAA,CACpB,WAAA,UACA,OAAA,GAAU,gBAAA,GACT,OAAA,CAAQ,kBAAA;;;UC9KM,YAAA;EACf,IAAA;EACA,KAAA;EACA,KAAA;AAAA;AAAA,UAOe,cAAA;EJlBoB;;;EIsBnC,QAAA,CAAS,WAAA,WAAsB,OAAA;;;;EAK/B,WAAA,CAAY,WAAA,WAAsB,OAAA;;;;EAKlC,YAAA,CAAa,WAAA,WAAsB,OAAA;EJtBxB;;;EI2BX,MAAA,CAAO,KAAA,UAAe,OAAA;IAAY,CAAA;EAAA,IAAe,OAAA,CAAQ,YAAA;;;;;;EAOzD,KAAA,CAAM,IAAA,WAAe,OAAA;AAAA;;;;iBA6ED,YAAA,CACpB,WAAA,WACC,OAAA,CAAQ,cAAA;;;;cC1HE,WAAA;AAAA,UAaI,gBAAA;ELdJ;EKgBX,GAAA;;EAEA,UAAA;ELlBmC;EKoBnC,gBAAA;AAAA;;;;;;;iBAaoB,gBAAA,CACpB,OAAA,EAAS,gBAAA,GACR,OAAA;;;;ALnCH;;;;;;iBMKsB,aAAA,CACpB,UAAA,EAAY,SAAA,IACZ,GAAA,EAAK,iBAAA,EACL,QAAA,EAAU,kBAAA,GACT,OAAA,CAAQ,kBAAA;;;KCbC,YAAA;AAAA,UASK,WAAA;;EAEf,SAAA;EPCA;EOCA,mBAAA;EPTmC;EOWnC,MAAA;EPXmC;EOanC,OAAA,GAAU,YAAA;AAAA;AAAA,UAGK,UAAA;;EAEf,QAAA,EAAU,MAAA;;EAEV,YAAA;;EAEA,QAAA;EPZW;EOcX,QAAA;AAAA;;;;APdF;;;;;iBQkEsB,IAAA,CACpB,QAAA,EAAU,kBAAA,EACV,OAAA,EAAS,WAAA,GACR,OAAA,CAAQ,UAAA;;;cClFE,YAAA;AAAA,cACA,UAAA;;ATEb;;;;;;;;iBSSsB,gBAAA,CACpB,QAAA,UACA,OAAA,WACC,OAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { _ as EvidenceRefSchema, a as writeWithMarkers, c as ALWAYS_SKIP, d as addConvention, f as createRegistry, g as ConventionRegistrySchema, h as ConventionEntrySchema, i as MARKER_START, l as listProjectFiles, m as ConventionCategorySchema, n as emit, o as extractConventions, p as ArchitectureInfoSchema, r as MARKER_END, s as createBridge, u as runExtractors, v as StackInfoSchema } from "./emitters-
|
|
1
|
+
import { _ as EvidenceRefSchema, a as writeWithMarkers, c as ALWAYS_SKIP, d as addConvention, f as createRegistry, g as ConventionRegistrySchema, h as ConventionEntrySchema, i as MARKER_START, l as listProjectFiles, m as ConventionCategorySchema, n as emit, o as extractConventions, p as ArchitectureInfoSchema, r as MARKER_END, s as createBridge, u as runExtractors, v as StackInfoSchema } from "./emitters-Doc-arnD.js";
|
|
2
2
|
|
|
3
3
|
export { ALWAYS_SKIP, ArchitectureInfoSchema, ConventionCategorySchema, ConventionEntrySchema, ConventionRegistrySchema, EvidenceRefSchema, MARKER_END, MARKER_START, StackInfoSchema, addConvention, createBridge, createRegistry, emit, extractConventions, listProjectFiles, runExtractors, writeWithMarkers };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ez-corp/ez-context",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Extract coding conventions and generate AI context files (CLAUDE.md, AGENTS.md, Cursor rules, Copilot instructions) with semantic drift detection",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "ISC",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"emitters-D6bP4xWs.js","names":["EVIDENCE","parseToml","yamlLoad","prepData"],"sources":["../src/core/schema.ts","../src/core/registry.ts","../src/extractors/index.ts","../src/extractors/static/package-json.ts","../src/extractors/static/lockfile.ts","../src/extractors/static/tsconfig.ts","../src/extractors/static/go-mod.ts","../src/extractors/static/cargo-toml.ts","../src/extractors/static/ci.ts","../src/utils/fs.ts","../src/extractors/static/project-structure.ts","../src/extractors/code/naming.ts","../src/extractors/code/imports.ts","../src/extractors/code/error-handling.ts","../src/core/ez-search-bridge.ts","../src/extractors/semantic/error-handling.ts","../src/extractors/semantic/architecture.ts","../src/core/pipeline.ts","../src/emitters/writer.ts","../src/emitters/render-helpers.ts","../src/emitters/claude-md.ts","../src/emitters/agents-md.ts","../src/emitters/cursor-mdc.ts","../src/emitters/copilot-md.ts","../src/emitters/skill-md.ts","../src/emitters/rulesync-md.ts","../src/emitters/ruler-md.ts","../src/emitters/index.ts"],"sourcesContent":["import { z } from \"zod\";\n\n// ---------------------------------------------------------------------------\n// Atomic schemas\n// ---------------------------------------------------------------------------\n\nexport const ConventionCategorySchema = z.enum([\n \"stack\",\n \"naming\",\n \"architecture\",\n \"error_handling\",\n \"testing\",\n \"imports\",\n \"other\",\n]);\n\nexport const EvidenceRefSchema = z.object({\n file: z.string(),\n line: z.number().int().positive().nullable(),\n});\n\nexport const ConventionEntrySchema = z.object({\n id: z.uuid(),\n category: ConventionCategorySchema,\n pattern: z.string().min(1),\n confidence: z.number().min(0).max(1),\n evidence: z.array(EvidenceRefSchema),\n metadata: z.record(z.string(), z.unknown()).optional(),\n});\n\nexport const StackInfoSchema = z.object({\n language: z.string(),\n framework: z.string().optional(),\n testRunner: z.string().optional(),\n buildTool: z.string().optional(),\n packageManager: z.string().optional(),\n nodeVersion: z.string().optional(),\n});\n\nexport const ArchitectureInfoSchema = z.object({\n pattern: z.string().optional(),\n layers: z.array(z.string()),\n entryPoints: z.array(z.string()).optional(),\n});\n\n// ---------------------------------------------------------------------------\n// Root registry schema\n// ---------------------------------------------------------------------------\n\nexport const ConventionRegistrySchema = z.object({\n version: z.literal(\"1\"),\n projectPath: z.string(),\n generatedAt: z.string().datetime(),\n stack: StackInfoSchema,\n conventions: z.array(ConventionEntrySchema),\n architecture: ArchitectureInfoSchema,\n metadata: z.record(z.string(), z.unknown()).optional(),\n});\n\n// ---------------------------------------------------------------------------\n// Inferred types (no manual interfaces)\n// ---------------------------------------------------------------------------\n\nexport type ConventionCategory = z.infer<typeof ConventionCategorySchema>;\nexport type EvidenceRef = z.infer<typeof EvidenceRefSchema>;\nexport type ConventionEntry = z.infer<typeof ConventionEntrySchema>;\nexport type StackInfo = z.infer<typeof StackInfoSchema>;\nexport type ArchitectureInfo = z.infer<typeof ArchitectureInfoSchema>;\nexport type ConventionRegistry = z.infer<typeof ConventionRegistrySchema>;\n","import {\n type ConventionEntry,\n type ConventionRegistry,\n ConventionRegistrySchema,\n} from \"./schema.js\";\n\n/**\n * Create a new empty ConventionRegistry for the given project path.\n * The returned registry passes ConventionRegistrySchema validation.\n */\nexport function createRegistry(projectPath: string): ConventionRegistry {\n const registry: ConventionRegistry = {\n version: \"1\",\n projectPath,\n generatedAt: new Date().toISOString(),\n stack: {\n language: \"unknown\",\n },\n conventions: [],\n architecture: {\n layers: [],\n },\n };\n\n const result = ConventionRegistrySchema.safeParse(registry);\n if (!result.success) {\n throw new Error(\n `createRegistry produced invalid registry: ${JSON.stringify(result.error.issues)}`\n );\n }\n\n return result.data;\n}\n\n/**\n * Add a convention entry to the registry, auto-generating a UUID for the id.\n * Returns a new registry (does not mutate the input).\n */\nexport function addConvention(\n registry: ConventionRegistry,\n entry: Omit<ConventionEntry, \"id\">\n): ConventionRegistry {\n const newEntry: ConventionEntry = {\n id: crypto.randomUUID(),\n ...entry,\n };\n\n const updated: ConventionRegistry = {\n ...registry,\n conventions: [...registry.conventions, newEntry],\n };\n\n const result = ConventionRegistrySchema.safeParse(updated);\n if (!result.success) {\n throw new Error(\n `addConvention produced invalid registry: ${JSON.stringify(result.error.issues)}`\n );\n }\n\n return result.data;\n}\n","import { addConvention } from \"../core/registry.js\";\nimport type { ConventionRegistry } from \"../core/schema.js\";\nimport type { Extractor, ExtractionContext } from \"./types.js\";\n\n/**\n * Run all extractors in parallel via Promise.allSettled so that a single\n * failing extractor does not abort the others.\n *\n * Fulfilled entries are added to the registry immutably via `addConvention`.\n * Rejected extractors emit a console.warn and are skipped.\n */\nexport async function runExtractors(\n extractors: Extractor[],\n ctx: ExtractionContext,\n registry: ConventionRegistry\n): Promise<ConventionRegistry> {\n const results = await Promise.allSettled(\n extractors.map((e) => e.extract(ctx).then((entries) => ({ extractor: e, entries })))\n );\n\n let current = registry;\n\n for (const [i, result] of results.entries()) {\n if (result.status === \"fulfilled\") {\n for (const entry of result.value.entries) {\n current = addConvention(current, entry);\n }\n } else {\n console.warn(\n `[runExtractors] Extractor \"${extractors[i]!.name}\" failed:`,\n result.reason\n );\n }\n }\n\n return current;\n}\n","import { access, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { ConventionEntry } from \"../../core/schema.js\";\nimport type { Extractor, ExtractionContext } from \"../types.js\";\n\n// ---------------------------------------------------------------------------\n// Detection maps\n// ---------------------------------------------------------------------------\n\nconst FRAMEWORK_MAP: Record<string, string> = {\n react: \"React\",\n vue: \"Vue\",\n \"@angular/core\": \"Angular\",\n next: \"Next.js\",\n nuxt: \"Nuxt\",\n svelte: \"Svelte\",\n hono: \"Hono\",\n express: \"Express\",\n fastify: \"Fastify\",\n koa: \"Koa\",\n};\n\nconst TEST_RUNNER_MAP: Record<string, string> = {\n vitest: \"Vitest\",\n jest: \"Jest\",\n mocha: \"Mocha\",\n jasmine: \"Jasmine\",\n ava: \"Ava\",\n};\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\ntype PackageJson = {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n scripts?: Record<string, string>;\n type?: string;\n packageManager?: string;\n};\n\nfunction allDeps(pkg: PackageJson): Record<string, string> {\n return { ...pkg.dependencies, ...pkg.devDependencies };\n}\n\ntype Entry = Omit<ConventionEntry, \"id\">;\n\nconst EVIDENCE = [{ file: \"package.json\", line: null }];\n\n// ---------------------------------------------------------------------------\n// Extractor\n// ---------------------------------------------------------------------------\n\nexport const packageJsonExtractor: Extractor = {\n name: \"package-json\",\n\n async extract(ctx: ExtractionContext): Promise<Entry[]> {\n const filePath = join(ctx.projectPath, \"package.json\");\n\n try {\n await access(filePath);\n } catch {\n return [];\n }\n\n let pkg: PackageJson;\n try {\n const raw = await readFile(filePath, \"utf-8\");\n pkg = JSON.parse(raw) as PackageJson;\n } catch {\n return [];\n }\n\n const deps = allDeps(pkg);\n const entries: Entry[] = [];\n\n // --- Language detection ---\n const isTypeScript =\n \"typescript\" in deps || \"@types/node\" in deps;\n const language = isTypeScript ? \"TypeScript\" : \"JavaScript\";\n entries.push({\n category: \"stack\",\n pattern: `Language: ${language}`,\n confidence: 0.95,\n evidence: EVIDENCE,\n metadata: { language },\n });\n\n // --- Framework detection ---\n for (const [pkg_name, label] of Object.entries(FRAMEWORK_MAP)) {\n if (pkg_name in deps) {\n const version = deps[pkg_name];\n entries.push({\n category: \"stack\",\n pattern: `Framework: ${label}`,\n confidence: 1.0,\n evidence: EVIDENCE,\n metadata: { framework: label, version },\n });\n break; // first match wins\n }\n }\n\n // --- Test runner detection ---\n for (const [pkg_name, label] of Object.entries(TEST_RUNNER_MAP)) {\n if (pkg_name in deps) {\n entries.push({\n category: \"testing\",\n pattern: `Test runner: ${label}`,\n confidence: 0.95,\n evidence: EVIDENCE,\n metadata: { testRunner: label },\n });\n break; // first match wins\n }\n }\n\n // --- Package manager detection (corepack \"packageManager\" field) ---\n if (typeof pkg.packageManager === \"string\") {\n const pmName = pkg.packageManager.split(\"@\")[0];\n if (pmName) {\n entries.push({\n category: \"stack\",\n pattern: `Package manager: ${pmName}`,\n confidence: 1.0,\n evidence: EVIDENCE,\n metadata: { packageManager: pmName },\n });\n }\n }\n\n // --- ESM module system detection ---\n if (pkg.type === \"module\") {\n entries.push({\n category: \"imports\",\n pattern: `ES modules (package.json \"type\": \"module\")`,\n confidence: 1.0,\n evidence: EVIDENCE,\n metadata: { moduleSystem: \"esm\" },\n });\n }\n\n // --- Scripts ---\n const scripts = pkg.scripts ?? {};\n for (const [scriptName, command] of Object.entries(scripts)) {\n const isTestScript =\n scriptName === \"test\" || scriptName.startsWith(\"test:\");\n const isBuildOrLint =\n scriptName === \"build\" || scriptName === \"lint\";\n\n if (isTestScript || isBuildOrLint) {\n entries.push({\n category: isTestScript ? \"testing\" : \"stack\",\n pattern: `Script \"${scriptName}\": ${command}`,\n confidence: 1.0,\n evidence: EVIDENCE,\n metadata: { scriptName, command },\n });\n }\n }\n\n return entries;\n },\n};\n","import { access } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { ConventionEntry } from \"../../core/schema.js\";\nimport type { Extractor, ExtractionContext } from \"../types.js\";\n\n// ---------------------------------------------------------------------------\n// Lockfile → package manager mapping (priority order)\n// ---------------------------------------------------------------------------\n\nconst LOCKFILES: Array<{ file: string; manager: string }> = [\n { file: \"bun.lock\", manager: \"bun\" },\n { file: \"bun.lockb\", manager: \"bun\" },\n { file: \"pnpm-lock.yaml\", manager: \"pnpm\" },\n { file: \"yarn.lock\", manager: \"yarn\" },\n { file: \"package-lock.json\", manager: \"npm\" },\n];\n\n// ---------------------------------------------------------------------------\n// Extractor\n// ---------------------------------------------------------------------------\n\ntype Entry = Omit<ConventionEntry, \"id\">;\n\nexport const lockfileExtractor: Extractor = {\n name: \"lockfile\",\n\n async extract(ctx: ExtractionContext): Promise<Entry[]> {\n for (const { file, manager } of LOCKFILES) {\n try {\n await access(join(ctx.projectPath, file));\n return [\n {\n category: \"stack\",\n pattern: `Package manager: ${manager}`,\n confidence: 1.0,\n evidence: [{ file, line: null }],\n metadata: { packageManager: manager },\n },\n ];\n } catch {\n // not found, try next\n }\n }\n\n return [];\n },\n};\n","import { access, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { ConventionEntry } from \"../../core/schema.js\";\nimport type { Extractor, ExtractionContext } from \"../types.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype CompilerOptions = {\n strict?: boolean;\n target?: string;\n module?: string;\n moduleResolution?: string;\n paths?: Record<string, string[]>;\n [key: string]: unknown;\n};\n\ntype TsConfig = {\n compilerOptions?: CompilerOptions;\n [key: string]: unknown;\n};\n\ntype Entry = Omit<ConventionEntry, \"id\">;\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nconst EVIDENCE = [{ file: \"tsconfig.json\", line: null }];\n\n/**\n * Strip single-line // comments and trailing commas so JSON.parse accepts\n * tsconfig.json files that use non-standard JSON.\n */\nfunction stripTsConfigNonStandardJson(raw: string): string {\n return raw\n // Remove single-line comments (// ...)\n .replace(/\\/\\/[^\\n]*/g, \"\")\n // Remove trailing commas before ] or }\n .replace(/,(\\s*[}\\]])/g, \"$1\");\n}\n\n// ---------------------------------------------------------------------------\n// Extractor\n// ---------------------------------------------------------------------------\n\nexport const tsconfigExtractor: Extractor = {\n name: \"tsconfig\",\n\n async extract(ctx: ExtractionContext): Promise<Entry[]> {\n const filePath = join(ctx.projectPath, \"tsconfig.json\");\n\n try {\n await access(filePath);\n } catch {\n return [];\n }\n\n let config: TsConfig;\n try {\n const raw = await readFile(filePath, \"utf-8\");\n config = JSON.parse(stripTsConfigNonStandardJson(raw)) as TsConfig;\n } catch {\n return [];\n }\n\n const co = config.compilerOptions ?? {};\n const entries: Entry[] = [];\n\n // --- Strict mode ---\n if (co.strict === true) {\n entries.push({\n category: \"stack\",\n pattern: \"TypeScript strict mode enabled\",\n confidence: 1.0,\n evidence: EVIDENCE,\n metadata: { strict: true },\n });\n }\n\n // --- Notable compiler options ---\n const notable: Record<string, unknown> = {};\n for (const key of [\"target\", \"module\", \"moduleResolution\"] as const) {\n if (co[key] !== undefined) {\n notable[key] = co[key];\n }\n }\n if (Object.keys(notable).length > 0) {\n entries.push({\n category: \"stack\",\n pattern: \"TypeScript compiler options configured\",\n confidence: 1.0,\n evidence: EVIDENCE,\n metadata: notable,\n });\n }\n\n // --- Path aliases ---\n if (co.paths && Object.keys(co.paths).length > 0) {\n entries.push({\n category: \"imports\",\n pattern: \"Uses TypeScript path aliases\",\n confidence: 1.0,\n evidence: EVIDENCE,\n metadata: { aliases: co.paths },\n });\n }\n\n return entries;\n },\n};\n","import { access, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { ConventionEntry } from \"../../core/schema.js\";\nimport type { Extractor, ExtractionContext } from \"../types.js\";\n\ntype Entry = Omit<ConventionEntry, \"id\">;\n\nexport const goModExtractor: Extractor = {\n name: \"go-mod\",\n\n async extract(ctx: ExtractionContext): Promise<Entry[]> {\n const filePath = join(ctx.projectPath, \"go.mod\");\n\n try {\n await access(filePath);\n } catch {\n return [];\n }\n\n let raw: string;\n try {\n raw = await readFile(filePath, \"utf-8\");\n } catch {\n return [];\n }\n\n const lines = raw.split(\"\\n\");\n\n let moduleName = \"\";\n let goVersion = \"\";\n let dependencyCount = 0;\n let inRequireBlock = false;\n\n for (const line of lines) {\n const trimmed = line.trim();\n\n if (!moduleName) {\n const moduleMatch = trimmed.match(/^module\\s+(\\S+)/);\n if (moduleMatch?.[1]) {\n moduleName = moduleMatch[1];\n continue;\n }\n }\n\n if (!goVersion) {\n const goMatch = trimmed.match(/^go\\s+(\\S+)/);\n if (goMatch?.[1]) {\n goVersion = goMatch[1];\n continue;\n }\n }\n\n // Count deps in require blocks\n if (trimmed === \"require (\") {\n inRequireBlock = true;\n continue;\n }\n if (inRequireBlock) {\n if (trimmed === \")\") {\n inRequireBlock = false;\n } else if (trimmed.length > 0 && !trimmed.startsWith(\"//\")) {\n dependencyCount++;\n }\n continue;\n }\n // Single-line require\n if (trimmed.match(/^require\\s+\\S+\\s+v\\S+/)) {\n dependencyCount++;\n }\n }\n\n if (!moduleName) {\n return [];\n }\n\n return [\n {\n category: \"stack\",\n pattern: `Go project (${moduleName})`,\n confidence: 1.0,\n evidence: [{ file: \"go.mod\", line: null }],\n metadata: {\n language: \"Go\",\n moduleName,\n goVersion: goVersion || null,\n dependencyCount,\n },\n },\n ];\n },\n};\n","import { access, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { parse as parseToml } from \"smol-toml\";\nimport type { ConventionEntry } from \"../../core/schema.js\";\nimport type { Extractor, ExtractionContext } from \"../types.js\";\n\ntype Entry = Omit<ConventionEntry, \"id\">;\n\ntype CargoToml = {\n package?: { name?: string; version?: string };\n dependencies?: Record<string, unknown>;\n [key: string]: unknown;\n};\n\nexport const cargoTomlExtractor: Extractor = {\n name: \"cargo-toml\",\n\n async extract(ctx: ExtractionContext): Promise<Entry[]> {\n const filePath = join(ctx.projectPath, \"Cargo.toml\");\n\n try {\n await access(filePath);\n } catch {\n return [];\n }\n\n let cargo: CargoToml;\n try {\n const raw = await readFile(filePath, \"utf-8\");\n cargo = parseToml(raw) as CargoToml;\n } catch {\n return [];\n }\n\n const packageName = cargo.package?.name;\n if (!packageName) {\n return [];\n }\n\n const dependencyCount = Object.keys(cargo.dependencies ?? {}).length;\n\n return [\n {\n category: \"stack\",\n pattern: `Rust project (${packageName})`,\n confidence: 1.0,\n evidence: [{ file: \"Cargo.toml\", line: null }],\n metadata: {\n language: \"Rust\",\n packageName,\n dependencyCount,\n },\n },\n ];\n },\n};\n","import { readFile } from \"node:fs/promises\";\nimport { join, relative } from \"node:path\";\nimport { globby } from \"globby\";\nimport { load as yamlLoad } from \"js-yaml\";\nimport type { ConventionEntry } from \"../../core/schema.js\";\nimport type { Extractor, ExtractionContext } from \"../types.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype Entry = Omit<ConventionEntry, \"id\">;\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\ntype CommandCategory = \"stack\" | \"testing\";\n\ninterface CommandMatch {\n command: string;\n category: CommandCategory;\n ciFile: string;\n}\n\nconst BUILD_KEYWORDS = [\"build\", \"compile\", \"tsc\", \"tsdown\"];\nconst TEST_KEYWORDS = [\"test\", \"vitest\", \"jest\", \"pytest\", \"cargo test\", \"go test\"];\nconst LINT_KEYWORDS = [\"lint\", \"eslint\", \"biome\", \"clippy\", \"ruff\"];\n\nfunction categorizeCommand(cmd: string): CommandCategory | null {\n const lower = cmd.toLowerCase();\n if (TEST_KEYWORDS.some((kw) => lower.includes(kw))) return \"testing\";\n if (BUILD_KEYWORDS.some((kw) => lower.includes(kw))) return \"stack\";\n if (LINT_KEYWORDS.some((kw) => lower.includes(kw))) return \"stack\";\n return null;\n}\n\n/**\n * Extract run commands from a GitHub Actions workflow YAML.\n * Traverses jobs.*.steps[].run\n */\nfunction extractGithubActionsCommands(doc: unknown, filePath: string): { matched: CommandMatch[]; raw: string[] } {\n const matched: CommandMatch[] = [];\n const raw: string[] = [];\n\n if (!doc || typeof doc !== \"object\") return { matched, raw };\n const record = doc as Record<string, unknown>;\n const jobs = record[\"jobs\"];\n if (!jobs || typeof jobs !== \"object\") return { matched, raw };\n\n for (const job of Object.values(jobs as Record<string, unknown>)) {\n if (!job || typeof job !== \"object\") continue;\n const steps = (job as Record<string, unknown>)[\"steps\"];\n if (!Array.isArray(steps)) continue;\n for (const step of steps) {\n if (!step || typeof step !== \"object\") continue;\n const run = (step as Record<string, unknown>)[\"run\"];\n if (typeof run !== \"string\") continue;\n // A single run block may contain multiple lines\n for (const line of run.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n raw.push(trimmed);\n const category = categorizeCommand(trimmed);\n if (category !== null) {\n matched.push({ command: trimmed, category, ciFile: filePath });\n }\n }\n }\n }\n\n return { matched, raw };\n}\n\n/** Keys that are not job definitions in GitLab CI top-level. */\nconst GITLAB_RESERVED = new Set([\"stages\", \"variables\", \"include\", \"default\", \"workflow\", \"image\", \"services\", \"before_script\", \"after_script\", \"cache\", \"artifacts\"]);\n\n/**\n * Extract script commands from a GitLab CI YAML.\n * Traverses top-level job keys (skipping reserved keys), looks for script arrays.\n */\nfunction extractGitlabCiCommands(doc: unknown, filePath: string): { matched: CommandMatch[]; raw: string[] } {\n const matched: CommandMatch[] = [];\n const raw: string[] = [];\n\n if (!doc || typeof doc !== \"object\") return { matched, raw };\n\n for (const [key, job] of Object.entries(doc as Record<string, unknown>)) {\n if (GITLAB_RESERVED.has(key)) continue;\n if (!job || typeof job !== \"object\") continue;\n const script = (job as Record<string, unknown>)[\"script\"];\n const scripts = Array.isArray(script) ? script : typeof script === \"string\" ? [script] : [];\n for (const cmd of scripts) {\n if (typeof cmd !== \"string\") continue;\n const trimmed = cmd.trim();\n if (!trimmed) continue;\n raw.push(trimmed);\n const category = categorizeCommand(trimmed);\n if (category !== null) {\n matched.push({ command: trimmed, category, ciFile: filePath });\n }\n }\n }\n\n return { matched, raw };\n}\n\n// ---------------------------------------------------------------------------\n// Extractor\n// ---------------------------------------------------------------------------\n\nexport const ciExtractor: Extractor = {\n name: \"ci\",\n\n async extract(ctx: ExtractionContext): Promise<Entry[]> {\n const ciPatterns = [\n \".github/workflows/*.yml\",\n \".github/workflows/*.yaml\",\n \".gitlab-ci.yml\",\n ];\n\n const ciFiles = await globby(ciPatterns, {\n cwd: ctx.projectPath,\n gitignore: false,\n followSymbolicLinks: false,\n absolute: false,\n });\n\n if (ciFiles.length === 0) return [];\n\n const entries: Entry[] = [];\n\n for (const relPath of ciFiles) {\n const absPath = join(ctx.projectPath, relPath);\n let raw: string;\n try {\n raw = await readFile(absPath, \"utf-8\");\n } catch {\n continue;\n }\n\n let doc: unknown;\n try {\n doc = yamlLoad(raw);\n } catch {\n // Malformed YAML: skip file, no throw\n continue;\n }\n\n const isGitlab = relPath.includes(\".gitlab-ci\");\n const { matched, raw: rawCmds } = isGitlab\n ? extractGitlabCiCommands(doc, relPath)\n : extractGithubActionsCommands(doc, relPath);\n\n // Store all raw commands in a single metadata entry per file\n if (rawCmds.length > 0 || matched.length > 0) {\n for (const { command, category } of matched) {\n entries.push({\n category,\n pattern: `CI command: ${command}`,\n confidence: 0.9,\n evidence: [{ file: relative(ctx.projectPath, absPath) || relPath, line: null }],\n metadata: { command, ciFile: relPath, rawCommands: rawCmds },\n });\n }\n }\n }\n\n return entries;\n },\n};\n","import { globby } from \"globby\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Directories and paths that are always excluded from file listings. */\nexport const ALWAYS_SKIP: readonly string[] = [\n \"**/node_modules/**\",\n \"**/dist/**\",\n \"**/generated/**\",\n \"**/.ez-search/**\",\n \"**/.ez-context/**\",\n \"**/.git/**\",\n];\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface ListFilesOptions {\n /** The project root to search from. */\n cwd: string;\n /** File extensions to include (without dot). Defaults to ts, js, json, md. */\n extensions?: string[];\n /** Additional ignore patterns (glob syntax). */\n additionalIgnore?: string[];\n}\n\n// ---------------------------------------------------------------------------\n// Utility\n// ---------------------------------------------------------------------------\n\n/**\n * List project files while respecting .gitignore (INTG-04) and always\n * skipping common generated/build directories.\n *\n * @returns Relative paths sorted alphabetically.\n */\nexport async function listProjectFiles(\n options: ListFilesOptions\n): Promise<string[]> {\n const {\n cwd,\n extensions = [\"ts\", \"js\", \"json\", \"md\"],\n additionalIgnore = [],\n } = options;\n\n const extPattern =\n extensions.length === 1\n ? `**/*.${extensions[0]}`\n : `**/*.{${extensions.join(\",\")}}`;\n\n const files = await globby(extPattern, {\n cwd,\n gitignore: true,\n ignore: [...ALWAYS_SKIP, ...additionalIgnore],\n followSymbolicLinks: false,\n absolute: false,\n });\n\n return files.sort();\n}\n","import { globby } from \"globby\";\nimport { ALWAYS_SKIP } from \"../../utils/fs.js\";\nimport type { ConventionEntry } from \"../../core/schema.js\";\nimport type { Extractor, ExtractionContext } from \"../types.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype Entry = Omit<ConventionEntry, \"id\">;\n\ninterface TestPattern {\n glob: string;\n location: string;\n style: string;\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Top-level test directory prefixes — files here are directory-based, not co-located. */\nconst TEST_DIR_PREFIXES = [\"test/\", \"tests/\", \"__tests__/\"];\n\nconst TEST_PATTERNS: TestPattern[] = [\n { glob: \"**/*.test.{ts,tsx,js,jsx}\", location: \"co-located\", style: \"*.test.ts style\" },\n { glob: \"**/*.spec.{ts,tsx,js,jsx}\", location: \"co-located\", style: \"*.spec.ts style\" },\n { glob: \"test/**/*.{ts,tsx,js,jsx}\", location: \"test/ directory\", style: \"test/ directory\" },\n { glob: \"tests/**/*.{ts,tsx,js,jsx}\", location: \"tests/ directory\", style: \"tests/ directory\" },\n { glob: \"__tests__/**/*.{ts,tsx,js,jsx}\", location: \"__tests__/ directory\", style: \"__tests__/ directory\" },\n];\n\n// ---------------------------------------------------------------------------\n// Extractor\n// ---------------------------------------------------------------------------\n\nexport const projectStructureExtractor: Extractor = {\n name: \"project-structure\",\n\n async extract(ctx: ExtractionContext): Promise<Entry[]> {\n const entries: Entry[] = [];\n\n for (const { glob, location, style } of TEST_PATTERNS) {\n let matches = await globby(glob, {\n cwd: ctx.projectPath,\n gitignore: true,\n ignore: [...ALWAYS_SKIP],\n followSymbolicLinks: false,\n absolute: false,\n });\n\n // For co-located patterns, exclude files that live in dedicated test directories\n if (location === \"co-located\") {\n matches = matches.filter(\n (f) => !TEST_DIR_PREFIXES.some((prefix) => f.startsWith(prefix))\n );\n }\n\n if (matches.length === 0) continue;\n\n const count = matches.length;\n const confidence = Math.min(0.95, 0.5 + count * 0.05);\n const evidenceFiles = matches.slice(0, 5);\n\n entries.push({\n category: \"testing\",\n pattern: `Test files in ${location} (${style})`,\n confidence,\n evidence: evidenceFiles.map((f) => ({ file: f, line: null })),\n metadata: {\n testFileCount: count,\n location,\n style,\n },\n });\n }\n\n return entries;\n },\n};\n","import { Project } from \"ts-morph\";\nimport { listProjectFiles } from \"../../utils/fs.js\";\nimport type { ConventionEntry } from \"../../core/schema.js\";\nimport type { Extractor, ExtractionContext } from \"../types.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype Entry = Omit<ConventionEntry, \"id\">;\ntype CaseKind = \"camelCase\" | \"PascalCase\" | \"snake_case\" | \"UPPER_SNAKE_CASE\";\n\n// ---------------------------------------------------------------------------\n// Case classification\n// ---------------------------------------------------------------------------\n\n/**\n * Classify the naming convention of a single identifier.\n * Returns null for short names (< 4 chars) or unclassifiable names.\n */\nfunction classifyCase(name: string): CaseKind | null {\n if (name.length < 4) return null;\n\n if (/^[A-Z][A-Z0-9_]{3,}$/.test(name)) return \"UPPER_SNAKE_CASE\";\n if (/^[A-Z][a-zA-Z0-9]+$/.test(name)) return \"PascalCase\";\n if (/^[a-z][a-z0-9_]+$/.test(name) && name.includes(\"_\")) return \"snake_case\";\n if (/^[a-z][a-zA-Z0-9]+$/.test(name) && /[A-Z]/.test(name)) return \"camelCase\";\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Extractor\n// ---------------------------------------------------------------------------\n\nexport const namingExtractor: Extractor = {\n name: \"naming\",\n\n async extract(ctx: ExtractionContext): Promise<Entry[]> {\n const files = await listProjectFiles({\n cwd: ctx.projectPath,\n extensions: [\"ts\", \"tsx\", \"js\", \"jsx\"],\n });\n\n if (files.length === 0) return [];\n\n const maxFiles = ctx.options?.maxFilesForAst ?? 200;\n const filesToAnalyse = files.slice(0, maxFiles);\n\n const project = new Project({\n compilerOptions: { allowJs: true, noEmit: true },\n skipFileDependencyResolution: true,\n });\n\n // Absolute paths needed for ts-morph\n const absPaths = filesToAnalyse.map((f) => `${ctx.projectPath}/${f}`);\n project.addSourceFilesAtPaths(absPaths);\n\n // Track counts: { functions, variables, classes } -> { CaseKind -> count }\n const functions: Record<string, number> = {};\n const variables: Record<string, number> = {};\n const classes: Record<string, number> = {};\n const counts: Record<string, Record<string, number>> = { functions, variables, classes };\n\n for (const sf of project.getSourceFiles()) {\n for (const fn of sf.getFunctions()) {\n const name = fn.getName();\n if (!name) continue;\n const kind = classifyCase(name);\n if (kind) functions[kind] = (functions[kind] ?? 0) + 1;\n }\n\n for (const decl of sf.getVariableDeclarations()) {\n const name = decl.getName();\n const kind = classifyCase(name);\n if (kind) variables[kind] = (variables[kind] ?? 0) + 1;\n }\n\n for (const cls of sf.getClasses()) {\n const name = cls.getName();\n if (!name) continue;\n const kind = classifyCase(name);\n if (kind) classes[kind] = (classes[kind] ?? 0) + 1;\n }\n }\n\n const entries: Entry[] = [];\n\n for (const [entityType, caseCounts] of Object.entries(counts)) {\n const total = Object.values(caseCounts).reduce((a, b) => a + b, 0);\n if (total < 3) continue;\n\n // Find dominant case\n let dominant: CaseKind | null = null;\n let dominantCount = 0;\n for (const [kind, count] of Object.entries(caseCounts)) {\n if (count > dominantCount) {\n dominant = kind as CaseKind;\n dominantCount = count;\n }\n }\n\n if (!dominant) continue;\n\n const confidence = Math.min(0.95, dominantCount / total);\n if (confidence < 0.6) continue;\n\n entries.push({\n category: \"naming\",\n pattern: `${entityType} use ${dominant} naming`,\n confidence,\n evidence: [{ file: \"src/**/*.ts\", line: null }],\n metadata: {\n entityType,\n dominantCase: dominant,\n counts: caseCounts,\n sampleSize: total,\n },\n });\n }\n\n return entries;\n },\n};\n","import { join, dirname, resolve } from \"node:path\";\nimport { Project } from \"ts-morph\";\nimport { listProjectFiles } from \"../../utils/fs.js\";\nimport type { ConventionEntry } from \"../../core/schema.js\";\nimport type { Extractor, ExtractionContext } from \"../types.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype Entry = Omit<ConventionEntry, \"id\">;\ntype SourceFile = ReturnType<InstanceType<typeof Project>[\"getSourceFiles\"]>[number];\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Known path alias prefixes used across JS/TS ecosystems. */\nconst ALIAS_PREFIXES = [\"@/\", \"~/\", \"#/\", \"$lib/\"];\n\nfunction hasPathAlias(specifier: string): boolean {\n return ALIAS_PREFIXES.some((prefix) => specifier.startsWith(prefix));\n}\n\n/**\n * Check whether a source file looks like a barrel file:\n * - Has at least one export declaration\n * - Has no function, class, or variable declarations\n */\nfunction isBarrelFile(sourceFile: SourceFile): boolean {\n const hasExports = sourceFile.getExportDeclarations().length > 0;\n if (!hasExports) return false;\n const hasFunctions = sourceFile.getFunctions().length > 0;\n const hasClasses = sourceFile.getClasses().length > 0;\n const hasVars = sourceFile.getVariableDeclarations().length > 0;\n return !hasFunctions && !hasClasses && !hasVars;\n}\n\n/**\n * Build a set of absolute paths for barrel files (index.ts/js variants).\n * Since skipFileDependencyResolution prevents module resolution,\n * we identify barrel files upfront and match imports by path.\n */\nfunction buildBarrelFileSet(project: Project): Set<string> {\n const barrels = new Set<string>();\n for (const sf of project.getSourceFiles()) {\n const filePath = sf.getFilePath();\n const baseName = filePath.split(\"/\").pop() ?? \"\";\n // Only index files can be barrel files (index.ts, index.tsx, index.js, index.jsx)\n if (/^index\\.[tj]sx?$/.test(baseName) && isBarrelFile(sf)) {\n barrels.add(filePath);\n }\n }\n return barrels;\n}\n\n/** Resolve a relative import specifier to candidate absolute paths. */\nfunction resolveRelativeImport(importingFile: string, specifier: string): string[] {\n const dir = dirname(importingFile);\n const base = resolve(dir, specifier);\n const exts = [\".ts\", \".tsx\", \".js\", \".jsx\"];\n const candidates: string[] = [];\n // Direct file: ./foo -> ./foo.ts etc\n for (const ext of exts) candidates.push(base + ext);\n // Directory index: ./foo -> ./foo/index.ts etc\n for (const ext of exts) candidates.push(join(base, \"index\" + ext));\n return candidates;\n}\n\n// ---------------------------------------------------------------------------\n// Extractor\n// ---------------------------------------------------------------------------\n\nexport const importsExtractor: Extractor = {\n name: \"imports\",\n\n async extract(ctx: ExtractionContext): Promise<Entry[]> {\n const files = await listProjectFiles({\n cwd: ctx.projectPath,\n extensions: [\"ts\", \"tsx\", \"js\", \"jsx\"],\n });\n\n if (files.length === 0) return [];\n\n const maxFiles = ctx.options?.maxFilesForAst ?? 200;\n const filesToAnalyse = files.slice(0, maxFiles);\n\n const project = new Project({\n compilerOptions: { allowJs: true, noEmit: true },\n skipFileDependencyResolution: true,\n });\n\n const absPaths = filesToAnalyse.map((f) => `${ctx.projectPath}/${f}`);\n project.addSourceFilesAtPaths(absPaths);\n\n // Pre-compute barrel file set for path-based matching\n const barrelFiles = buildBarrelFileSet(project);\n\n let relativeCount = 0;\n let externalCount = 0;\n let barrelCount = 0;\n let aliasCount = 0;\n\n for (const sf of project.getSourceFiles()) {\n for (const imp of sf.getImportDeclarations()) {\n const specifier = imp.getModuleSpecifierValue();\n\n if (imp.isModuleSpecifierRelative()) {\n relativeCount++;\n\n // Barrel detection via path heuristic\n const candidates = resolveRelativeImport(sf.getFilePath(), specifier);\n if (candidates.some((c) => barrelFiles.has(c))) {\n barrelCount++;\n }\n } else {\n externalCount++;\n\n if (hasPathAlias(specifier)) {\n aliasCount++;\n }\n }\n }\n }\n\n const totalImports = relativeCount + externalCount;\n if (totalImports === 0) return [];\n\n const entries: Entry[] = [];\n const evidence = [{ file: \"src/**/*.ts\", line: null }];\n\n // --- Import organization pattern ---\n const relRatio = relativeCount / totalImports;\n let orgPattern: string;\n if (relRatio >= 0.75) {\n orgPattern = \"Predominantly relative imports\";\n } else if (relRatio <= 0.25) {\n orgPattern = \"Predominantly external imports\";\n } else {\n orgPattern = \"Mix of relative and external imports\";\n }\n\n // Confidence scales with sample size (saturates at 100 imports)\n const sizeConfidence = Math.min(0.95, 0.5 + (totalImports / 100) * 0.45);\n\n entries.push({\n category: \"imports\",\n pattern: orgPattern,\n confidence: sizeConfidence,\n evidence,\n metadata: {\n relativeCount,\n externalCount,\n totalImports,\n relativeRatio: Math.round(relRatio * 100) / 100,\n },\n });\n\n // --- Barrel file usage ---\n if (barrelCount > 0) {\n const barrelRatio = barrelCount / relativeCount;\n entries.push({\n category: \"imports\",\n pattern: \"Uses barrel file (index) imports\",\n confidence: Math.min(0.95, 0.5 + barrelRatio * 0.45),\n evidence,\n metadata: { barrelCount, relativeCount },\n });\n }\n\n // --- Path alias usage ---\n if (aliasCount > 0) {\n entries.push({\n category: \"imports\",\n pattern: \"Uses path aliases (@/ prefix)\",\n confidence: 1.0,\n evidence,\n metadata: { aliasCount },\n });\n }\n\n return entries;\n },\n};\n","import { Project, SyntaxKind } from \"ts-morph\";\nimport { listProjectFiles } from \"../../utils/fs.js\";\nimport type { ConventionEntry } from \"../../core/schema.js\";\nimport type { Extractor, ExtractionContext } from \"../types.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype Entry = Omit<ConventionEntry, \"id\">;\n\n// ---------------------------------------------------------------------------\n// Extractor\n// ---------------------------------------------------------------------------\n\nexport const staticErrorHandlingExtractor: Extractor = {\n name: \"static-error-handling\",\n\n async extract(ctx: ExtractionContext): Promise<Entry[]> {\n const files = await listProjectFiles({\n cwd: ctx.projectPath,\n extensions: [\"ts\", \"tsx\", \"js\", \"jsx\"],\n });\n\n if (files.length === 0) return [];\n\n const maxFiles = ctx.options?.maxFilesForAst ?? 200;\n const filesToAnalyse = files.slice(0, maxFiles);\n\n const project = new Project({\n compilerOptions: { allowJs: true, noEmit: true },\n skipFileDependencyResolution: true,\n });\n\n const absPaths = filesToAnalyse.map((f) => `${ctx.projectPath}/${f}`);\n project.addSourceFilesAtPaths(absPaths);\n\n let tryCatchFileCount = 0;\n let tryCatchTotalCount = 0;\n let customErrorClassCount = 0;\n const tryCatchEvidence: string[] = [];\n const customErrorEvidence: string[] = [];\n\n for (const sf of project.getSourceFiles()) {\n const relPath = sf.getFilePath().replace(ctx.projectPath + \"/\", \"\");\n\n const tryStatements = sf.getDescendantsOfKind(SyntaxKind.TryStatement);\n if (tryStatements.length > 0) {\n tryCatchFileCount++;\n tryCatchTotalCount += tryStatements.length;\n if (tryCatchEvidence.length < 5) tryCatchEvidence.push(relPath);\n }\n\n for (const cls of sf.getClasses()) {\n const heritage = cls.getExtends();\n if (heritage && /\\bError\\b/.test(heritage.getText())) {\n customErrorClassCount++;\n if (customErrorEvidence.length < 5) customErrorEvidence.push(relPath);\n }\n }\n }\n\n const entries: Entry[] = [];\n const totalFiles = filesToAnalyse.length;\n\n // Require at least 2 try/catch occurrences (across 1+ files) for a meaningful pattern\n if (tryCatchTotalCount >= 2) {\n // Confidence based on both file spread and occurrence density\n const fileSpread = tryCatchFileCount / totalFiles;\n const densityBoost = Math.min(0.2, tryCatchTotalCount * 0.05);\n const confidence = Math.min(0.95, 0.5 + fileSpread * 0.35 + densityBoost);\n entries.push({\n category: \"error_handling\",\n pattern: \"try/catch imperative error handling\",\n confidence,\n evidence: tryCatchEvidence.map((file) => ({ file, line: null })),\n metadata: { style: \"try-catch\", fileCount: tryCatchFileCount, totalCount: tryCatchTotalCount },\n });\n }\n\n if (customErrorClassCount >= 1) {\n const confidence = Math.min(0.95, 0.5 + (customErrorClassCount / totalFiles) * 0.45);\n entries.push({\n category: \"error_handling\",\n pattern: \"custom error class hierarchy\",\n confidence,\n evidence: customErrorEvidence.map((file) => ({ file, line: null })),\n metadata: { style: \"custom-error-class\", classCount: customErrorClassCount },\n });\n }\n\n return entries;\n },\n};\n","/**\n * ez-search bridge — thin adapter over @ez-corp/ez-search.\n *\n * This is the ONLY file that imports from @ez-corp/ez-search.\n * All other modules interact with ez-search via the EzSearchBridge interface.\n */\nimport { index, query, EzSearchError } from \"@ez-corp/ez-search\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\n// ---------------------------------------------------------------------------\n// Supporting types\n// ---------------------------------------------------------------------------\n\nexport interface SearchResult {\n file: string;\n chunk: string;\n score: number;\n}\n\n// ---------------------------------------------------------------------------\n// Bridge interface\n// ---------------------------------------------------------------------------\n\nexport interface EzSearchBridge {\n /**\n * Returns true if an .ez-search/ index exists for the project directory.\n */\n hasIndex(projectPath: string): Promise<boolean>;\n\n /**\n * Ensures an index exists: indexes the project if no index is present (EXTR-10).\n */\n ensureIndex(projectPath: string): Promise<void>;\n\n /**\n * Semantic (and hybrid) search over the indexed project.\n */\n search(query: string, options?: { k?: number }): Promise<SearchResult[]>;\n\n /**\n * Get an embedding vector for the given text.\n * NOTE: @ez-corp/ez-search does not expose a standalone embed API.\n * This method is reserved for future use or a companion embedder.\n */\n embed(text: string): Promise<number[]>;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\nclass EzSearchBridgeImpl implements EzSearchBridge {\n constructor(private readonly projectPath: string) {}\n\n async hasIndex(projectPath: string): Promise<boolean> {\n const indexDir = join(projectPath, \".ez-search\");\n return existsSync(indexDir);\n }\n\n async ensureIndex(projectPath: string): Promise<void> {\n if (await this.hasIndex(projectPath)) {\n return;\n }\n await index(projectPath);\n }\n\n async search(\n searchQuery: string,\n options: { k?: number } = {}\n ): Promise<SearchResult[]> {\n const { k = 10 } = options;\n\n let raw: Awaited<ReturnType<typeof query>>;\n try {\n raw = await query(searchQuery, {\n topK: k,\n projectDir: this.projectPath,\n autoIndex: false,\n });\n } catch (err: unknown) {\n if (err instanceof EzSearchError && err.code === \"NO_INDEX\") {\n return [];\n }\n throw err;\n }\n\n const results: SearchResult[] = [];\n\n for (const hit of raw.code) {\n results.push({ file: hit.file, chunk: hit.text, score: hit.score });\n }\n for (const hit of raw.text) {\n results.push({ file: hit.file, chunk: hit.text, score: hit.score });\n }\n\n results.sort((a, b) => b.score - a.score);\n return results.slice(0, k);\n }\n\n async embed(_text: string): Promise<number[]> {\n // @ez-corp/ez-search does not expose a standalone embed endpoint.\n // This is a planned capability that will be implemented when a\n // companion embedding API becomes available.\n throw new Error(\n \"embed() is not yet supported by the ez-search bridge. \" +\n \"Use search() for semantic retrieval.\"\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create an EzSearchBridge bound to the given project directory.\n */\nexport async function createBridge(\n projectPath: string\n): Promise<EzSearchBridge> {\n return new EzSearchBridgeImpl(projectPath);\n}\n","import { createBridge } from \"../../core/ez-search-bridge.js\";\nimport type { ConventionEntry } from \"../../core/schema.js\";\nimport type { Extractor, ExtractionContext } from \"../types.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype Entry = Omit<ConventionEntry, \"id\">;\n\ntype ErrorStyle = \"try-catch\" | \"result-type\" | \"custom-error-class\" | \"error-boundary\";\n\ninterface PatternDef {\n style: ErrorStyle;\n pattern: string;\n test: (content: string) => boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Pattern definitions\n// ---------------------------------------------------------------------------\n\nconst PATTERNS: PatternDef[] = [\n {\n style: \"try-catch\",\n pattern: \"try/catch imperative error handling\",\n test: (content) => /\\btry\\s*\\{/.test(content) && /\\bcatch\\s*\\(/.test(content),\n },\n {\n style: \"result-type\",\n pattern: \"Result/Either functional error handling\",\n test: (content) =>\n /\\bResult<|\\bOk\\(|\\bErr\\(|\\bisOk\\b|\\bisErr\\b|\\bneverthrow\\b/.test(content),\n },\n {\n style: \"custom-error-class\",\n pattern: \"custom error class hierarchy\",\n test: (content) => /class\\s+\\w+Error\\b|\\bnew\\s+\\w+Error\\(/.test(content),\n },\n {\n style: \"error-boundary\",\n pattern: \"React error boundary components\",\n test: (content) => /\\bErrorBoundary\\b|\\bcomponentDidCatch\\b/.test(content),\n },\n];\n\n// ---------------------------------------------------------------------------\n// Extractor\n// ---------------------------------------------------------------------------\n\nexport const errorHandlingExtractor: Extractor = {\n name: \"error-handling\",\n\n async extract(ctx: ExtractionContext): Promise<Entry[]> {\n const bridge = await createBridge(ctx.projectPath);\n\n if (!(await bridge.hasIndex(ctx.projectPath))) {\n return [];\n }\n\n // Issue targeted search queries\n const [general, resultType, customErrors] = await Promise.all([\n bridge.search(\"error handling try catch throw exception\", { k: 30 }),\n bridge.search(\"Result Ok Err return error value\", { k: 20 }),\n bridge.search(\"custom error class extends Error\", { k: 20 }),\n ]);\n\n // Merge and deduplicate chunks by file (concatenate text for same file)\n const fileContentMap = new Map<string, string>();\n for (const result of [...general, ...resultType, ...customErrors]) {\n const existing = fileContentMap.get(result.file) ?? \"\";\n fileContentMap.set(result.file, existing + \"\\n\" + result.chunk);\n }\n\n const totalUniqueFiles = fileContentMap.size;\n if (totalUniqueFiles === 0) return [];\n\n const entries: Entry[] = [];\n\n for (const patternDef of PATTERNS) {\n const matchingFiles: string[] = [];\n\n for (const [file, content] of fileContentMap) {\n if (patternDef.test(content)) {\n matchingFiles.push(file);\n }\n }\n\n // Require at least 2 distinct files\n if (matchingFiles.length < 2) continue;\n\n const confidence = Math.min(0.95, 0.5 + (matchingFiles.length / totalUniqueFiles) * 0.45);\n\n entries.push({\n category: \"error_handling\",\n pattern: patternDef.pattern,\n confidence,\n evidence: matchingFiles.slice(0, 5).map((file) => ({ file, line: null })),\n metadata: {\n style: patternDef.style,\n fileCount: matchingFiles.length,\n },\n });\n }\n\n return entries;\n },\n};\n","import { createBridge } from \"../../core/ez-search-bridge.js\";\nimport { listProjectFiles } from \"../../utils/fs.js\";\nimport type { ConventionEntry } from \"../../core/schema.js\";\nimport type { Extractor, ExtractionContext } from \"../types.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype Entry = Omit<ConventionEntry, \"id\">;\n\ntype ArchPattern = \"MVC\" | \"feature-based\" | \"layer-based\";\n\n// ---------------------------------------------------------------------------\n// Directory pattern detection\n// ---------------------------------------------------------------------------\n\n/** Get directory paths under src/ (or project root if no src/). */\nfunction extractSourceDirs(files: string[]): Set<string> {\n const dirs = new Set<string>();\n for (const f of files) {\n const parts = f.split(\"/\");\n if (parts.length < 2) continue;\n\n if (parts[0] === \"src\" && parts.length >= 3) {\n // Under src/ -- use \"src/subdir\" as the dir\n dirs.add(`src/${parts[1]}`);\n } else if (parts[0] !== \"src\") {\n // Project root level\n dirs.add(parts[0]!);\n }\n }\n return dirs;\n}\n\n/** Normalise a directory name to lower case for matching. */\nfunction normalise(dir: string): string {\n return dir.split(\"/\").pop()!.toLowerCase();\n}\n\n/** Detect MVC: >= 2 of models/, views/, controllers/, routes/ */\nfunction detectMVC(sourceDirs: Set<string>): string[] {\n const mvc = [\"model\", \"models\", \"view\", \"views\", \"controller\", \"controllers\", \"route\", \"routes\"];\n const found: string[] = [];\n for (const dir of sourceDirs) {\n if (mvc.includes(normalise(dir))) found.push(dir);\n }\n // Deduplicate by canonical name (model/models both count as one)\n const canonical = new Set(found.map((d) => normalise(d).replace(/s$/, \"\")));\n return canonical.size >= 2 ? found : [];\n}\n\n/** Detect feature-based: files under features/, modules/, pages/ with >= 5 files total. */\nfunction detectFeatureBased(files: string[]): string[] {\n const featurePattern = /\\/(features?|modules?|pages?)\\//i;\n const featureDirs = new Set<string>();\n let count = 0;\n for (const f of files) {\n if (featurePattern.test(f)) {\n count++;\n // Extract the feature root dir (e.g. \"src/features\" or \"features\")\n const match = f.match(/^(.*?\\/(features?|modules?|pages?))\\//i);\n if (match) featureDirs.add(match[1]!);\n }\n }\n return count >= 5 ? Array.from(featureDirs) : [];\n}\n\n/** Detect layer-based architecture (DDD / hexagonal / clean arch). */\nfunction detectLayerBased(sourceDirs: Set<string>): string[] {\n const layerPatterns = [\n // DDD\n \"domain\", \"application\", \"infrastructure\",\n // Hexagonal\n \"service\", \"services\", \"repository\", \"repositories\", \"handler\", \"handlers\", \"usecase\", \"usecases\",\n // Clean arch\n \"core\", \"data\", \"presentation\",\n ];\n const found: string[] = [];\n for (const dir of sourceDirs) {\n if (layerPatterns.includes(normalise(dir))) found.push(dir);\n }\n // Need >= 2 distinct layer dirs\n const canonical = new Set(found.map((d) => normalise(d)));\n return canonical.size >= 2 ? found : [];\n}\n\n// ---------------------------------------------------------------------------\n// Extractor\n// ---------------------------------------------------------------------------\n\nexport const architectureExtractor: Extractor = {\n name: \"architecture\",\n\n async extract(ctx: ExtractionContext): Promise<Entry[]> {\n // Signal 1: Directory structure scan (deterministic)\n const files = await listProjectFiles({\n cwd: ctx.projectPath,\n extensions: [\"ts\", \"js\", \"tsx\", \"jsx\", \"py\", \"rb\", \"go\", \"rs\"],\n });\n\n const sourceDirs = extractSourceDirs(files);\n\n // Try each pattern\n const mvcDirs = detectMVC(sourceDirs);\n const featureDirs = detectFeatureBased(files);\n const layerDirs = detectLayerBased(sourceDirs);\n\n let detectedPattern: ArchPattern | null = null;\n let detectedLayers: string[] = [];\n\n if (mvcDirs.length > 0) {\n detectedPattern = \"MVC\";\n detectedLayers = mvcDirs;\n } else if (featureDirs.length > 0) {\n detectedPattern = \"feature-based\";\n detectedLayers = featureDirs;\n } else if (layerDirs.length > 0) {\n detectedPattern = \"layer-based\";\n detectedLayers = layerDirs;\n }\n\n if (!detectedPattern) return [];\n\n // Signal 2: Semantic search confirmation (optional -- adds evidence and boosts confidence)\n const bridge = await createBridge(ctx.projectPath);\n const hasIdx = await bridge.hasIndex(ctx.projectPath);\n\n let confidence = 0.7; // directory-only\n const evidence: { file: string; line: null }[] = detectedLayers\n .slice(0, 3)\n .map((dir) => ({ file: dir, line: null }));\n\n if (hasIdx) {\n const searchResults = await bridge.search(\n \"model view controller route handler service repository\",\n { k: 20 }\n );\n if (searchResults.length > 0) {\n confidence = 0.85;\n for (const r of searchResults.slice(0, 2)) {\n evidence.push({ file: r.file, line: null });\n }\n }\n }\n\n // Deduplicate evidence by file\n const seen = new Set<string>();\n const deduped = evidence.filter(({ file }) => {\n if (seen.has(file)) return false;\n seen.add(file);\n return true;\n });\n\n return [\n {\n category: \"architecture\",\n pattern: patternLabel(detectedPattern),\n confidence,\n evidence: deduped.slice(0, 5),\n metadata: {\n architecturePattern: detectedPattern,\n layers: detectedLayers,\n },\n },\n ];\n },\n};\n\nfunction patternLabel(p: ArchPattern): string {\n switch (p) {\n case \"MVC\":\n return \"MVC architecture pattern\";\n case \"feature-based\":\n return \"Feature-based architecture\";\n case \"layer-based\":\n return \"Layer-based architecture\";\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers exposed for testing\n// ---------------------------------------------------------------------------\n\nexport { extractSourceDirs, detectMVC, detectFeatureBased, detectLayerBased };\n","import { createRegistry } from \"./registry.js\";\nimport { ConventionRegistrySchema } from \"./schema.js\";\nimport type { ConventionEntry, ConventionRegistry, EvidenceRef } from \"./schema.js\";\nimport { runExtractors } from \"../extractors/index.js\";\nimport type { ExtractorOptions } from \"../extractors/types.js\";\nimport { packageJsonExtractor } from \"../extractors/static/package-json.js\";\nimport { lockfileExtractor } from \"../extractors/static/lockfile.js\";\nimport { tsconfigExtractor } from \"../extractors/static/tsconfig.js\";\nimport { goModExtractor } from \"../extractors/static/go-mod.js\";\nimport { cargoTomlExtractor } from \"../extractors/static/cargo-toml.js\";\nimport { ciExtractor } from \"../extractors/static/ci.js\";\nimport { projectStructureExtractor } from \"../extractors/static/project-structure.js\";\nimport { namingExtractor } from \"../extractors/code/naming.js\";\nimport { importsExtractor } from \"../extractors/code/imports.js\";\nimport { staticErrorHandlingExtractor } from \"../extractors/code/error-handling.js\";\nimport { errorHandlingExtractor } from \"../extractors/semantic/error-handling.js\";\nimport { architectureExtractor } from \"../extractors/semantic/architecture.js\";\n\n// ---------------------------------------------------------------------------\n// Extractor registry (ordered by confidence priority for StackInfo population)\n// ---------------------------------------------------------------------------\n\nconst ALL_EXTRACTORS = [\n packageJsonExtractor,\n lockfileExtractor,\n tsconfigExtractor,\n goModExtractor,\n cargoTomlExtractor,\n ciExtractor,\n projectStructureExtractor,\n namingExtractor,\n importsExtractor,\n staticErrorHandlingExtractor,\n errorHandlingExtractor,\n architectureExtractor,\n];\n\n// ---------------------------------------------------------------------------\n// Deduplication helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Deduplicate evidence by file+line combination.\n */\nfunction deduplicateEvidence(evidence: EvidenceRef[]): EvidenceRef[] {\n const seen = new Set<string>();\n return evidence.filter((e) => {\n const key = `${e.file}:${e.line ?? \"null\"}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n\n/**\n * Deduplicate conventions by category+pattern.\n * For duplicates, keep the one with higher confidence.\n * Merge evidence arrays from duplicates.\n */\nfunction deduplicateConventions(\n conventions: ConventionEntry[]\n): ConventionEntry[] {\n const grouped = new Map<string, ConventionEntry>();\n\n for (const entry of conventions) {\n const key = `${entry.category}:${entry.pattern}`;\n const existing = grouped.get(key);\n\n if (!existing) {\n grouped.set(key, entry);\n } else {\n // Keep higher confidence, merge evidence\n const winner: ConventionEntry =\n entry.confidence > existing.confidence ? entry : existing;\n const mergedEvidence = deduplicateEvidence([\n ...existing.evidence,\n ...entry.evidence,\n ]);\n grouped.set(key, { ...winner, evidence: mergedEvidence });\n }\n }\n\n return Array.from(grouped.values());\n}\n\n// ---------------------------------------------------------------------------\n// StackInfo population helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Populate StackInfo from extracted conventions via a post-extraction pass.\n * Conventions are processed in array order; first match wins for each field.\n */\nfunction populateStackInfo(registry: ConventionRegistry): ConventionRegistry {\n const stack = { ...registry.stack };\n\n for (const entry of registry.conventions) {\n const meta = entry.metadata ?? {};\n\n if (entry.category === \"stack\") {\n if (!stack.language || stack.language === \"unknown\") {\n if (typeof meta.language === \"string\") {\n stack.language = meta.language;\n }\n }\n if (!stack.framework && typeof meta.framework === \"string\") {\n stack.framework = meta.framework;\n }\n if (!stack.testRunner && typeof meta.testRunner === \"string\") {\n stack.testRunner = meta.testRunner;\n }\n if (!stack.packageManager && typeof meta.packageManager === \"string\") {\n stack.packageManager = meta.packageManager;\n }\n if (!stack.buildTool) {\n // Detect buildTool from scripts metadata\n if (typeof meta.buildTool === \"string\") {\n stack.buildTool = meta.buildTool;\n } else if (\n typeof meta.scriptName === \"string\" &&\n meta.scriptName === \"build\" &&\n typeof meta.command === \"string\"\n ) {\n // Extract the build tool from the build script command (first word)\n stack.buildTool = (meta.command as string).split(\" \")[0];\n }\n }\n }\n\n if (entry.category === \"testing\") {\n if (!stack.testRunner && typeof meta.testRunner === \"string\") {\n stack.testRunner = meta.testRunner;\n }\n }\n }\n\n return { ...registry, stack };\n}\n\n// ---------------------------------------------------------------------------\n// ArchitectureInfo population helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Populate ArchitectureInfo from extracted conventions via a post-extraction pass.\n * First architecture convention with the relevant metadata wins.\n */\nfunction populateArchitectureInfo(\n registry: ConventionRegistry\n): ConventionRegistry {\n const arch = { ...registry.architecture };\n\n for (const entry of registry.conventions) {\n if (entry.category === \"architecture\") {\n if (\n !arch.pattern &&\n typeof entry.metadata?.architecturePattern === \"string\"\n ) {\n arch.pattern = entry.metadata.architecturePattern;\n }\n if (\n arch.layers.length === 0 &&\n Array.isArray(entry.metadata?.layers)\n ) {\n arch.layers = entry.metadata.layers as string[];\n }\n }\n }\n\n return { ...registry, architecture: arch };\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Run the full extraction pipeline against the given project path.\n *\n * 1. Runs all extractors in parallel (Promise.allSettled)\n * 2. Deduplicates conventions by category+pattern (higher confidence wins)\n * 3. Populates StackInfo from convention metadata\n * 4. Populates ArchitectureInfo from convention metadata\n * 5. Validates and returns the final ConventionRegistry\n */\nexport async function extractConventions(\n projectPath: string,\n options?: ExtractorOptions\n): Promise<ConventionRegistry> {\n const ctx = { projectPath, options };\n const emptyRegistry = createRegistry(projectPath);\n\n // Run all extractors\n const populated = await runExtractors(ALL_EXTRACTORS, ctx, emptyRegistry);\n\n // Deduplicate conventions\n const deduplicated: ConventionRegistry = {\n ...populated,\n conventions: deduplicateConventions(populated.conventions),\n };\n\n // Populate StackInfo from convention metadata\n const withStack = populateStackInfo(deduplicated);\n\n // Populate ArchitectureInfo from convention metadata\n const withArch = populateArchitectureInfo(withStack);\n\n // Validate and return\n return ConventionRegistrySchema.parse(withArch);\n}\n","import { readFile, writeFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\n\nexport const MARKER_START = \"<!-- ez-context:start -->\";\nexport const MARKER_END = \"<!-- ez-context:end -->\";\n\n/**\n * Write content into the marker section of filePath.\n *\n * Three paths:\n * 1. File does not exist: creates it with markers wrapping content.\n * 2. File exists, no markers (or only one marker): appends the section at the end.\n * 3. File exists with both markers: splices new content between existing markers,\n * preserving everything outside.\n */\nexport async function writeWithMarkers(\n filePath: string,\n content: string\n): Promise<void> {\n const wrapped = `${MARKER_START}\\n${content}\\n${MARKER_END}`;\n\n if (!existsSync(filePath)) {\n await writeFile(filePath, wrapped + \"\\n\", \"utf-8\");\n return;\n }\n\n const existing = await readFile(filePath, \"utf-8\");\n const startIdx = existing.indexOf(MARKER_START);\n const endIdx = existing.indexOf(MARKER_END);\n\n // Treat missing or unpaired marker as \"no markers\" — append section\n if (startIdx === -1 || endIdx === -1) {\n const separator = existing.endsWith(\"\\n\") ? \"\\n\" : \"\\n\\n\";\n await writeFile(filePath, existing + separator + wrapped + \"\\n\", \"utf-8\");\n return;\n }\n\n const before = existing.slice(0, startIdx);\n const after = existing.slice(endIdx + MARKER_END.length);\n await writeFile(filePath, before + wrapped + after, \"utf-8\");\n}\n","// Shared rendering helpers for all emitter modules\nimport type { ConventionRegistry, ConventionEntry } from \"../core/schema.js\";\n\n/**\n * Extract script commands from a filtered list of convention entries.\n * Shared by agents-md and renderConventionsBody.\n */\nexport function extractCommands(\n filtered: ConventionEntry[]\n): Array<{ scriptName: string; command: string }> {\n const commands: Array<{ scriptName: string; command: string }> = [];\n for (const entry of filtered) {\n const meta = entry.metadata;\n if (\n meta &&\n typeof meta[\"command\"] === \"string\" &&\n typeof meta[\"scriptName\"] === \"string\"\n ) {\n commands.push({\n scriptName: meta[\"scriptName\"] as string,\n command: meta[\"command\"] as string,\n });\n }\n }\n return commands;\n}\n\n/**\n * Check if a stack-category convention should appear in the Conventions section.\n * Stack entries that map to StackInfo fields (language, framework, etc.) are already\n * shown in the Stack section. Others (e.g. \"TypeScript strict mode\") are convention-worthy.\n */\nfunction isStackConventionWorthy(\n entry: ConventionEntry,\n stack: ConventionRegistry[\"stack\"]\n): boolean {\n if (entry.category !== \"stack\") return false;\n const meta = entry.metadata ?? {};\n // Already represented in Stack section via StackInfo fields\n if (typeof meta[\"language\"] === \"string\" && stack.language !== \"unknown\") return false;\n if (typeof meta[\"framework\"] === \"string\" && stack.framework) return false;\n if (typeof meta[\"buildTool\"] === \"string\" && stack.buildTool) return false;\n if (typeof meta[\"packageManager\"] === \"string\" && stack.packageManager) return false;\n // Script entries are shown in Commands section\n if (typeof meta[\"scriptName\"] === \"string\") return false;\n // Everything else (strict mode, compiler options, etc.) is convention-worthy\n return true;\n}\n\n/**\n * Check if a convention entry is redundant with the Stack or Commands sections.\n * - \"Test runner: X\" in testing category duplicates Stack > Test Runner\n * - Entries with metadata.scriptName duplicate the Commands section\n */\nexport function isRedundantConvention(entry: ConventionEntry): boolean {\n if (entry.category === \"testing\" && entry.pattern.startsWith(\"Test runner:\")) {\n return true;\n }\n if (entry.metadata && typeof entry.metadata[\"scriptName\"] === \"string\") {\n return true;\n }\n return false;\n}\n\n/**\n * Render conventions body as markdown lines.\n * Used by cursor-mdc, skill-md, rulesync-md, ruler-md, copilot-md.\n */\nexport function renderConventionsBody(\n registry: ConventionRegistry,\n confidenceThreshold: number\n): string[] {\n const filtered = registry.conventions.filter(\n (c) => c.confidence >= confidenceThreshold\n );\n\n const lines: string[] = [];\n\n // Stack section\n const s = registry.stack;\n const hasStack =\n s.language !== \"unknown\" ||\n Boolean(s.framework) ||\n Boolean(s.buildTool) ||\n Boolean(s.packageManager) ||\n Boolean(s.testRunner);\n\n if (hasStack) {\n lines.push(\"## Stack\");\n if (s.language !== \"unknown\") lines.push(`- Language: ${s.language}`);\n if (s.framework) lines.push(`- Framework: ${s.framework}`);\n if (s.buildTool) lines.push(`- Build: ${s.buildTool}`);\n if (s.packageManager) lines.push(`- Package Manager: ${s.packageManager}`);\n if (s.testRunner) lines.push(`- Test Runner: ${s.testRunner}`);\n lines.push(\"\");\n }\n\n // Architecture section\n const a = registry.architecture;\n const hasArchitecture =\n Boolean(a.pattern) || (a.layers?.length ?? 0) > 0;\n\n if (hasArchitecture) {\n lines.push(\"## Architecture\");\n if (a.pattern) lines.push(`- Pattern: ${a.pattern}`);\n if (a.layers && a.layers.length > 0) {\n lines.push(`- Layers: ${a.layers.join(\", \")}`);\n }\n lines.push(\"\");\n }\n\n // Conventions section — group by category, exclude architecture and redundant entries.\n // Stack entries without scriptName are kept (e.g. \"TypeScript strict mode enabled\").\n const categoryMap = new Map<string, string[]>();\n for (const entry of filtered) {\n if (entry.category === \"architecture\") continue;\n if (entry.category === \"stack\" && !isStackConventionWorthy(entry, s)) continue;\n if (isRedundantConvention(entry)) continue;\n const list = categoryMap.get(entry.category) ?? [];\n list.push(entry.pattern);\n categoryMap.set(entry.category, list);\n }\n\n if (categoryMap.size > 0) {\n lines.push(\"## Conventions\");\n for (const [category, patterns] of categoryMap) {\n for (const pattern of patterns) {\n lines.push(`- **${category}**: ${pattern}`);\n }\n }\n lines.push(\"\");\n }\n\n // Commands section — from conventions with metadata.command + metadata.scriptName\n const commands = extractCommands(filtered);\n\n if (commands.length > 0) {\n lines.push(\"## Commands\");\n for (const cmd of commands) {\n lines.push(`- \\`${cmd.scriptName}\\`: \\`${cmd.command}\\``);\n }\n lines.push(\"\");\n }\n\n return lines;\n}\n","import type { ConventionRegistry, ConventionEntry } from \"../core/schema.js\";\nimport { isRedundantConvention } from \"./render-helpers.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface ConventionGroup {\n category: string;\n entries: ConventionEntry[];\n}\n\n// ---------------------------------------------------------------------------\n// Data prep\n// ---------------------------------------------------------------------------\n\nfunction prepData(registry: ConventionRegistry, confidenceThreshold: number) {\n const filtered = registry.conventions.filter(\n (c) => c.confidence >= confidenceThreshold\n );\n\n // Group by category, excluding architecture and entries redundant with Stack/Commands.\n // Stack entries not covered by StackInfo fields (e.g. \"TypeScript strict mode\") are kept.\n const categoryMap = new Map<string, ConventionEntry[]>();\n for (const entry of filtered) {\n if (entry.category === \"architecture\") continue;\n if (entry.category === \"stack\") {\n const meta = entry.metadata ?? {};\n // Skip entries already rendered in the Stack section\n if (typeof meta[\"language\"] === \"string\") continue;\n if (typeof meta[\"framework\"] === \"string\") continue;\n if (typeof meta[\"buildTool\"] === \"string\") continue;\n if (typeof meta[\"packageManager\"] === \"string\") continue;\n if (typeof meta[\"scriptName\"] === \"string\") continue;\n }\n if (isRedundantConvention(entry)) continue;\n const list = categoryMap.get(entry.category) ?? [];\n list.push(entry);\n categoryMap.set(entry.category, list);\n }\n\n const conventionGroups: ConventionGroup[] = [];\n for (const [category, entries] of categoryMap) {\n conventionGroups.push({ category, entries });\n }\n\n const hasStack =\n registry.stack.language !== \"unknown\" ||\n Boolean(registry.stack.framework) ||\n Boolean(registry.stack.buildTool) ||\n Boolean(registry.stack.packageManager) ||\n Boolean(registry.stack.testRunner);\n\n const hasArchitecture =\n Boolean(registry.architecture.pattern) ||\n (registry.architecture.layers?.length ?? 0) > 0;\n\n return {\n stack: registry.stack,\n architecture: registry.architecture,\n conventionGroups,\n hasStack,\n hasArchitecture,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Renderer\n// ---------------------------------------------------------------------------\n\nexport function renderClaudeMd(\n registry: ConventionRegistry,\n confidenceThreshold: number\n): string {\n const data = prepData(registry, confidenceThreshold);\n const lines: string[] = [];\n\n lines.push(\"# Project Context\");\n\n // Stack section\n if (data.hasStack) {\n lines.push(\"\");\n lines.push(\"## Stack\");\n if (data.stack.language !== \"unknown\") {\n lines.push(`- Language: ${data.stack.language}`);\n }\n if (data.stack.framework) lines.push(`- Framework: ${data.stack.framework}`);\n if (data.stack.buildTool) lines.push(`- Build: ${data.stack.buildTool}`);\n if (data.stack.packageManager) lines.push(`- Package Manager: ${data.stack.packageManager}`);\n if (data.stack.testRunner) lines.push(`- Test Runner: ${data.stack.testRunner}`);\n }\n\n // Conventions section\n if (data.conventionGroups.length > 0) {\n lines.push(\"\");\n lines.push(\"## Conventions\");\n for (const group of data.conventionGroups) {\n for (const entry of group.entries) {\n lines.push(`- **${group.category}**: ${entry.pattern}`);\n }\n }\n }\n\n // Architecture section\n if (data.hasArchitecture) {\n lines.push(\"\");\n lines.push(\"## Architecture\");\n if (data.architecture.pattern) {\n lines.push(`- Pattern: ${data.architecture.pattern}`);\n }\n if (data.architecture.layers && data.architecture.layers.length > 0) {\n lines.push(`- Layers: ${data.architecture.layers.join(\", \")}`);\n }\n }\n\n return lines.join(\"\\n\") + \"\\n\";\n}\n","import type { ConventionRegistry, ConventionEntry } from \"../core/schema.js\";\nimport { extractCommands } from \"./render-helpers.js\";\n\n// ---------------------------------------------------------------------------\n// Data prep\n// ---------------------------------------------------------------------------\n\nfunction prepData(registry: ConventionRegistry, confidenceThreshold: number) {\n const filtered = registry.conventions.filter(\n (c) => c.confidence >= confidenceThreshold\n );\n\n const commands = extractCommands(filtered);\n\n const byCategory = (cat: string): ConventionEntry[] =>\n filtered.filter((c) => c.category === cat);\n\n const testingConventions = byCategory(\"testing\");\n const namingConventions = byCategory(\"naming\");\n const importConventions = byCategory(\"imports\");\n const gitConventions = filtered.filter(\n (c) =>\n c.pattern.toLowerCase().includes(\"git\") ||\n (c.category === \"other\" && c.pattern.toLowerCase().includes(\"commit\"))\n );\n\n const hasTesting =\n Boolean(registry.stack.testRunner) || testingConventions.length > 0;\n\n const hasProjectStructure =\n Boolean(registry.architecture.pattern) ||\n (registry.architecture.layers?.length ?? 0) > 0;\n\n const hasCodeStyle =\n namingConventions.length > 0 || importConventions.length > 0;\n\n return {\n commands,\n testRunner: registry.stack.testRunner ?? null,\n testingConventions,\n namingConventions,\n importConventions,\n gitConventions,\n architecture: registry.architecture,\n hasTesting,\n hasProjectStructure,\n hasCodeStyle,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Renderer\n// ---------------------------------------------------------------------------\n\nexport function renderAgentsMd(\n registry: ConventionRegistry,\n confidenceThreshold: number\n): string {\n const data = prepData(registry, confidenceThreshold);\n const lines: string[] = [];\n\n lines.push(\"# AGENTS.md\");\n\n // Commands section\n if (data.commands.length > 0) {\n lines.push(\"\");\n lines.push(\"## Commands\");\n for (const cmd of data.commands) {\n lines.push(`- \\`${cmd.scriptName}\\`: \\`${cmd.command}\\``);\n }\n }\n\n // Testing section\n if (data.hasTesting) {\n lines.push(\"\");\n lines.push(\"## Testing\");\n if (data.testRunner) lines.push(`- Test runner: ${data.testRunner}`);\n for (const entry of data.testingConventions) {\n lines.push(`- ${entry.pattern}`);\n }\n }\n\n // Project Structure section\n if (data.hasProjectStructure) {\n lines.push(\"\");\n lines.push(\"## Project Structure\");\n if (data.architecture.pattern) {\n lines.push(`- Architecture: ${data.architecture.pattern}`);\n }\n if (data.architecture.layers && data.architecture.layers.length > 0) {\n lines.push(`- Layers: ${data.architecture.layers.join(\", \")}`);\n }\n }\n\n // Code Style section\n if (data.hasCodeStyle) {\n lines.push(\"\");\n lines.push(\"## Code Style\");\n for (const entry of data.namingConventions) {\n lines.push(`- **naming**: ${entry.pattern}`);\n }\n for (const entry of data.importConventions) {\n lines.push(`- **imports**: ${entry.pattern}`);\n }\n }\n\n // Git Workflow section (optional)\n if (data.gitConventions.length > 0) {\n lines.push(\"\");\n lines.push(\"## Git Workflow\");\n for (const entry of data.gitConventions) {\n lines.push(`- ${entry.pattern}`);\n }\n }\n\n // Boundaries section (always present)\n lines.push(\"\");\n lines.push(\"## Boundaries\");\n lines.push(\"- Do not modify auto-generated sections between ez-context markers\");\n\n return lines.join(\"\\n\") + \"\\n\";\n}\n","import yaml from \"js-yaml\";\nimport type { ConventionRegistry } from \"../core/schema.js\";\nimport { renderConventionsBody } from \"./render-helpers.js\";\n\n/**\n * Render a Cursor MDC rule file (.cursor/rules/ez-context.mdc).\n *\n * Format: YAML frontmatter + markdown body.\n * - description: shown in Cursor UI\n * - globs: empty string (not null/omitted) per Cursor docs\n * - alwaysApply: true ensures conventions are always in context\n */\nexport function renderCursorMdc(\n registry: ConventionRegistry,\n confidenceThreshold: number\n): string {\n const frontmatter = yaml\n .dump({\n description: \"Project conventions extracted by ez-context\",\n globs: \"\",\n alwaysApply: true,\n })\n .trimEnd();\n\n const bodyLines = renderConventionsBody(registry, confidenceThreshold);\n // Remove trailing empty line added by renderConventionsBody\n while (bodyLines.length > 0 && bodyLines[bodyLines.length - 1] === \"\") {\n bodyLines.pop();\n }\n const body = bodyLines.join(\"\\n\");\n\n return `---\\n${frontmatter}\\n---\\n\\n${body}\\n`;\n}\n","import type { ConventionRegistry } from \"../core/schema.js\";\nimport { renderConventionsBody } from \"./render-helpers.js\";\n\n/**\n * Render a GitHub Copilot instructions file (.github/copilot-instructions.md).\n *\n * Format: Plain markdown, no YAML frontmatter.\n * GitHub Copilot reads the entire file; HTML comment markers are used\n * for idempotent updates via writeWithMarkers.\n */\nexport function renderCopilotMd(\n registry: ConventionRegistry,\n confidenceThreshold: number\n): string {\n const lines: string[] = [];\n\n lines.push(\"<!-- Generated by ez-context. Do not edit between markers. -->\");\n lines.push(\"\");\n lines.push(\"# Copilot Instructions\");\n lines.push(\"\");\n\n const bodyLines = renderConventionsBody(registry, confidenceThreshold);\n // Remove trailing empty line\n while (bodyLines.length > 0 && bodyLines[bodyLines.length - 1] === \"\") {\n bodyLines.pop();\n }\n lines.push(...bodyLines);\n\n return lines.join(\"\\n\") + \"\\n\";\n}\n","import yaml from \"js-yaml\";\nimport type { ConventionRegistry } from \"../core/schema.js\";\nimport { renderConventionsBody } from \"./render-helpers.js\";\n\n/**\n * Render a SKILL.md module file (.skills/ez-context/SKILL.md).\n *\n * Format: YAML frontmatter + markdown body.\n * - name: must match directory name (ez-context)\n * - description: max 1024 chars, describes what AND when to use\n * Body stays under 5000 tokens (~3750 words) as per SKILL.md spec.\n */\nexport function renderSkillMd(\n registry: ConventionRegistry,\n confidenceThreshold: number\n): string {\n const description =\n \"Project conventions and coding standards for this codebase. \" +\n \"Use when writing new code, reviewing patterns, or understanding project architecture.\";\n\n const frontmatter = yaml\n .dump({\n name: \"ez-context\",\n description,\n })\n .trimEnd();\n\n const bodyLines = renderConventionsBody(registry, confidenceThreshold);\n // Remove trailing empty line\n while (bodyLines.length > 0 && bodyLines[bodyLines.length - 1] === \"\") {\n bodyLines.pop();\n }\n const body = bodyLines.join(\"\\n\");\n\n return `---\\n${frontmatter}\\n---\\n\\n${body}\\n`;\n}\n","import yaml from \"js-yaml\";\nimport type { ConventionRegistry } from \"../core/schema.js\";\nimport { renderConventionsBody } from \"./render-helpers.js\";\n\n/**\n * Render a Rulesync rule file (.rulesync/rules/ez-context.md).\n *\n * Format: YAML frontmatter + markdown body.\n * - targets: specifies which AI tools receive this rule\n * ez-context writes INTO .rulesync/rules/; Rulesync distributes to tools.\n */\nexport function renderRulesyncMd(\n registry: ConventionRegistry,\n confidenceThreshold: number\n): string {\n const frontmatter = yaml\n .dump({\n description: \"Project conventions extracted by ez-context\",\n targets: [\"cursor\", \"copilot\", \"windsurf\"],\n })\n .trimEnd();\n\n const bodyLines = renderConventionsBody(registry, confidenceThreshold);\n // Remove trailing empty line\n while (bodyLines.length > 0 && bodyLines[bodyLines.length - 1] === \"\") {\n bodyLines.pop();\n }\n const body = bodyLines.join(\"\\n\");\n\n return `---\\n${frontmatter}\\n---\\n\\n${body}\\n`;\n}\n","import type { ConventionRegistry } from \"../core/schema.js\";\nimport { renderConventionsBody } from \"./render-helpers.js\";\n\n/**\n * Render a Ruler rule file (.ruler/ez-context.md).\n *\n * Format: Plain markdown, no YAML frontmatter.\n * Ruler recursively discovers all .md files in .ruler/ and distributes them.\n * ez-context writes a single .ruler/ez-context.md as an additive conventions file.\n */\nexport function renderRulerMd(\n registry: ConventionRegistry,\n confidenceThreshold: number\n): string {\n const lines: string[] = [];\n\n lines.push(\"# Project Conventions (ez-context)\");\n lines.push(\"\");\n\n const bodyLines = renderConventionsBody(registry, confidenceThreshold);\n // Remove trailing empty line\n while (bodyLines.length > 0 && bodyLines[bodyLines.length - 1] === \"\") {\n bodyLines.pop();\n }\n lines.push(...bodyLines);\n\n return lines.join(\"\\n\") + \"\\n\";\n}\n","import path from \"node:path\";\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport type { ConventionRegistry } from \"../core/schema.js\";\nimport type { EmitOptions, EmitResult, OutputFormat } from \"./types.js\";\nimport { writeWithMarkers } from \"./writer.js\";\nimport { renderClaudeMd } from \"./claude-md.js\";\nimport { renderAgentsMd } from \"./agents-md.js\";\nimport { renderCursorMdc } from \"./cursor-mdc.js\";\nimport { renderCopilotMd } from \"./copilot-md.js\";\nimport { renderSkillMd } from \"./skill-md.js\";\nimport { renderRulesyncMd } from \"./rulesync-md.js\";\nimport { renderRulerMd } from \"./ruler-md.js\";\n\nexport { renderClaudeMd } from \"./claude-md.js\";\nexport { renderAgentsMd } from \"./agents-md.js\";\nexport { renderCursorMdc } from \"./cursor-mdc.js\";\nexport { renderCopilotMd } from \"./copilot-md.js\";\nexport { renderSkillMd } from \"./skill-md.js\";\nexport { renderRulesyncMd } from \"./rulesync-md.js\";\nexport { renderRulerMd } from \"./ruler-md.js\";\nexport type { OutputFormat } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Format emitter registry\n// ---------------------------------------------------------------------------\n\ntype WriteStrategy = \"markers\" | \"direct\";\n\ninterface FormatEmitterEntry {\n render: (registry: ConventionRegistry, threshold: number) => string;\n filename: string;\n strategy: WriteStrategy;\n}\n\nexport const FORMAT_EMITTER_MAP: Record<OutputFormat, FormatEmitterEntry> = {\n claude: {\n render: renderClaudeMd,\n filename: \"CLAUDE.md\",\n strategy: \"markers\",\n },\n agents: {\n render: renderAgentsMd,\n filename: \"AGENTS.md\",\n strategy: \"markers\",\n },\n cursor: {\n render: renderCursorMdc,\n filename: path.join(\".cursor\", \"rules\", \"ez-context.mdc\"),\n strategy: \"direct\",\n },\n copilot: {\n render: renderCopilotMd,\n filename: path.join(\".github\", \"copilot-instructions.md\"),\n strategy: \"markers\",\n },\n skills: {\n render: renderSkillMd,\n filename: path.join(\".skills\", \"ez-context\", \"SKILL.md\"),\n strategy: \"direct\",\n },\n rulesync: {\n render: renderRulesyncMd,\n filename: path.join(\".rulesync\", \"rules\", \"ez-context.md\"),\n strategy: \"markers\",\n },\n ruler: {\n render: renderRulerMd,\n filename: path.join(\".ruler\", \"ez-context.md\"),\n strategy: \"direct\",\n },\n};\n\n// ---------------------------------------------------------------------------\n// emit()\n// ---------------------------------------------------------------------------\n\n/**\n * Emit output files for the given ConventionRegistry.\n *\n * Defaults to [\"claude\", \"agents\"] for backward compatibility.\n * In dryRun mode, returns rendered content without writing any files.\n */\nexport async function emit(\n registry: ConventionRegistry,\n options: EmitOptions\n): Promise<EmitResult> {\n const threshold = options.confidenceThreshold ?? 0.7;\n const formats: OutputFormat[] = options.formats ?? [\"claude\", \"agents\"];\n\n // Render all requested formats\n const rendered: Record<string, string> = {};\n for (const format of formats) {\n const entry = FORMAT_EMITTER_MAP[format];\n rendered[format] = entry.render(registry, threshold);\n }\n\n // Backward compat aliases\n const claudeMd = rendered[\"claude\"] ?? \"\";\n const agentsMd = rendered[\"agents\"] ?? \"\";\n\n if (options.dryRun) {\n return { rendered, claudeMd, agentsMd, filesWritten: [] };\n }\n\n // Write files\n const filesWritten: string[] = [];\n for (const format of formats) {\n const entry = FORMAT_EMITTER_MAP[format];\n const filePath = path.join(options.outputDir, entry.filename);\n\n await mkdir(path.dirname(filePath), { recursive: true });\n if (entry.strategy === \"direct\") {\n await writeFile(filePath, rendered[format]!, \"utf-8\");\n } else {\n await writeWithMarkers(filePath, rendered[format]!);\n }\n\n filesWritten.push(filePath);\n }\n\n return { rendered, claudeMd, agentsMd, filesWritten };\n}\n"],"mappings":";;;;;;;;;;;AAMA,MAAa,2BAA2B,EAAE,KAAK;CAC7C;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;AAEF,MAAa,oBAAoB,EAAE,OAAO;CACxC,MAAM,EAAE,QAAQ;CAChB,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC,UAAU;CAC7C,CAAC;AAEF,MAAa,wBAAwB,EAAE,OAAO;CAC5C,IAAI,EAAE,MAAM;CACZ,UAAU;CACV,SAAS,EAAE,QAAQ,CAAC,IAAI,EAAE;CAC1B,YAAY,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE;CACpC,UAAU,EAAE,MAAM,kBAAkB;CACpC,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,SAAS,CAAC,CAAC,UAAU;CACvD,CAAC;AAEF,MAAa,kBAAkB,EAAE,OAAO;CACtC,UAAU,EAAE,QAAQ;CACpB,WAAW,EAAE,QAAQ,CAAC,UAAU;CAChC,YAAY,EAAE,QAAQ,CAAC,UAAU;CACjC,WAAW,EAAE,QAAQ,CAAC,UAAU;CAChC,gBAAgB,EAAE,QAAQ,CAAC,UAAU;CACrC,aAAa,EAAE,QAAQ,CAAC,UAAU;CACnC,CAAC;AAEF,MAAa,yBAAyB,EAAE,OAAO;CAC7C,SAAS,EAAE,QAAQ,CAAC,UAAU;CAC9B,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;CAC3B,aAAa,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,UAAU;CAC5C,CAAC;AAMF,MAAa,2BAA2B,EAAE,OAAO;CAC/C,SAAS,EAAE,QAAQ,IAAI;CACvB,aAAa,EAAE,QAAQ;CACvB,aAAa,EAAE,QAAQ,CAAC,UAAU;CAClC,OAAO;CACP,aAAa,EAAE,MAAM,sBAAsB;CAC3C,cAAc;CACd,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,SAAS,CAAC,CAAC,UAAU;CACvD,CAAC;;;;;;;;AC/CF,SAAgB,eAAe,aAAyC;CACtE,MAAM,WAA+B;EACnC,SAAS;EACT;EACA,8BAAa,IAAI,MAAM,EAAC,aAAa;EACrC,OAAO,EACL,UAAU,WACX;EACD,aAAa,EAAE;EACf,cAAc,EACZ,QAAQ,EAAE,EACX;EACF;CAED,MAAM,SAAS,yBAAyB,UAAU,SAAS;AAC3D,KAAI,CAAC,OAAO,QACV,OAAM,IAAI,MACR,6CAA6C,KAAK,UAAU,OAAO,MAAM,OAAO,GACjF;AAGH,QAAO,OAAO;;;;;;AAOhB,SAAgB,cACd,UACA,OACoB;CACpB,MAAM,WAA4B;EAChC,IAAI,OAAO,YAAY;EACvB,GAAG;EACJ;CAED,MAAM,UAA8B;EAClC,GAAG;EACH,aAAa,CAAC,GAAG,SAAS,aAAa,SAAS;EACjD;CAED,MAAM,SAAS,yBAAyB,UAAU,QAAQ;AAC1D,KAAI,CAAC,OAAO,QACV,OAAM,IAAI,MACR,4CAA4C,KAAK,UAAU,OAAO,MAAM,OAAO,GAChF;AAGH,QAAO,OAAO;;;;;;;;;;;;AChDhB,eAAsB,cACpB,YACA,KACA,UAC6B;CAC7B,MAAM,UAAU,MAAM,QAAQ,WAC5B,WAAW,KAAK,MAAM,EAAE,QAAQ,IAAI,CAAC,MAAM,aAAa;EAAE,WAAW;EAAG;EAAS,EAAE,CAAC,CACrF;CAED,IAAI,UAAU;AAEd,MAAK,MAAM,CAAC,GAAG,WAAW,QAAQ,SAAS,CACzC,KAAI,OAAO,WAAW,YACpB,MAAK,MAAM,SAAS,OAAO,MAAM,QAC/B,WAAU,cAAc,SAAS,MAAM;KAGzC,SAAQ,KACN,8BAA8B,WAAW,GAAI,KAAK,YAClD,OAAO,OACR;AAIL,QAAO;;;;;AC1BT,MAAM,gBAAwC;CAC5C,OAAO;CACP,KAAK;CACL,iBAAiB;CACjB,MAAM;CACN,MAAM;CACN,QAAQ;CACR,MAAM;CACN,SAAS;CACT,SAAS;CACT,KAAK;CACN;AAED,MAAM,kBAA0C;CAC9C,QAAQ;CACR,MAAM;CACN,OAAO;CACP,SAAS;CACT,KAAK;CACN;AAcD,SAAS,QAAQ,KAA0C;AACzD,QAAO;EAAE,GAAG,IAAI;EAAc,GAAG,IAAI;EAAiB;;AAKxD,MAAMA,aAAW,CAAC;CAAE,MAAM;CAAgB,MAAM;CAAM,CAAC;AAMvD,MAAa,uBAAkC;CAC7C,MAAM;CAEN,MAAM,QAAQ,KAA0C;EACtD,MAAM,WAAW,KAAK,IAAI,aAAa,eAAe;AAEtD,MAAI;AACF,SAAM,OAAO,SAAS;UAChB;AACN,UAAO,EAAE;;EAGX,IAAI;AACJ,MAAI;GACF,MAAM,MAAM,MAAM,SAAS,UAAU,QAAQ;AAC7C,SAAM,KAAK,MAAM,IAAI;UACf;AACN,UAAO,EAAE;;EAGX,MAAM,OAAO,QAAQ,IAAI;EACzB,MAAM,UAAmB,EAAE;EAK3B,MAAM,WADJ,gBAAgB,QAAQ,iBAAiB,OACX,eAAe;AAC/C,UAAQ,KAAK;GACX,UAAU;GACV,SAAS,aAAa;GACtB,YAAY;GACZ,UAAUA;GACV,UAAU,EAAE,UAAU;GACvB,CAAC;AAGF,OAAK,MAAM,CAAC,UAAU,UAAU,OAAO,QAAQ,cAAc,CAC3D,KAAI,YAAY,MAAM;GACpB,MAAM,UAAU,KAAK;AACrB,WAAQ,KAAK;IACX,UAAU;IACV,SAAS,cAAc;IACvB,YAAY;IACZ,UAAUA;IACV,UAAU;KAAE,WAAW;KAAO;KAAS;IACxC,CAAC;AACF;;AAKJ,OAAK,MAAM,CAAC,UAAU,UAAU,OAAO,QAAQ,gBAAgB,CAC7D,KAAI,YAAY,MAAM;AACpB,WAAQ,KAAK;IACX,UAAU;IACV,SAAS,gBAAgB;IACzB,YAAY;IACZ,UAAUA;IACV,UAAU,EAAE,YAAY,OAAO;IAChC,CAAC;AACF;;AAKJ,MAAI,OAAO,IAAI,mBAAmB,UAAU;GAC1C,MAAM,SAAS,IAAI,eAAe,MAAM,IAAI,CAAC;AAC7C,OAAI,OACF,SAAQ,KAAK;IACX,UAAU;IACV,SAAS,oBAAoB;IAC7B,YAAY;IACZ,UAAUA;IACV,UAAU,EAAE,gBAAgB,QAAQ;IACrC,CAAC;;AAKN,MAAI,IAAI,SAAS,SACf,SAAQ,KAAK;GACX,UAAU;GACV,SAAS;GACT,YAAY;GACZ,UAAUA;GACV,UAAU,EAAE,cAAc,OAAO;GAClC,CAAC;EAIJ,MAAM,UAAU,IAAI,WAAW,EAAE;AACjC,OAAK,MAAM,CAAC,YAAY,YAAY,OAAO,QAAQ,QAAQ,EAAE;GAC3D,MAAM,eACJ,eAAe,UAAU,WAAW,WAAW,QAAQ;AAIzD,OAAI,gBAFF,eAAe,WAAW,eAAe,OAGzC,SAAQ,KAAK;IACX,UAAU,eAAe,YAAY;IACrC,SAAS,WAAW,WAAW,KAAK;IACpC,YAAY;IACZ,UAAUA;IACV,UAAU;KAAE;KAAY;KAAS;IAClC,CAAC;;AAIN,SAAO;;CAEV;;;;AC3JD,MAAM,YAAsD;CAC1D;EAAE,MAAM;EAAY,SAAS;EAAO;CACpC;EAAE,MAAM;EAAa,SAAS;EAAO;CACrC;EAAE,MAAM;EAAkB,SAAS;EAAQ;CAC3C;EAAE,MAAM;EAAa,SAAS;EAAQ;CACtC;EAAE,MAAM;EAAqB,SAAS;EAAO;CAC9C;AAQD,MAAa,oBAA+B;CAC1C,MAAM;CAEN,MAAM,QAAQ,KAA0C;AACtD,OAAK,MAAM,EAAE,MAAM,aAAa,UAC9B,KAAI;AACF,SAAM,OAAO,KAAK,IAAI,aAAa,KAAK,CAAC;AACzC,UAAO,CACL;IACE,UAAU;IACV,SAAS,oBAAoB;IAC7B,YAAY;IACZ,UAAU,CAAC;KAAE;KAAM,MAAM;KAAM,CAAC;IAChC,UAAU,EAAE,gBAAgB,SAAS;IACtC,CACF;UACK;AAKV,SAAO,EAAE;;CAEZ;;;;ACjBD,MAAM,WAAW,CAAC;CAAE,MAAM;CAAiB,MAAM;CAAM,CAAC;;;;;AAMxD,SAAS,6BAA6B,KAAqB;AACzD,QAAO,IAEJ,QAAQ,eAAe,GAAG,CAE1B,QAAQ,gBAAgB,KAAK;;AAOlC,MAAa,oBAA+B;CAC1C,MAAM;CAEN,MAAM,QAAQ,KAA0C;EACtD,MAAM,WAAW,KAAK,IAAI,aAAa,gBAAgB;AAEvD,MAAI;AACF,SAAM,OAAO,SAAS;UAChB;AACN,UAAO,EAAE;;EAGX,IAAI;AACJ,MAAI;GACF,MAAM,MAAM,MAAM,SAAS,UAAU,QAAQ;AAC7C,YAAS,KAAK,MAAM,6BAA6B,IAAI,CAAC;UAChD;AACN,UAAO,EAAE;;EAGX,MAAM,KAAK,OAAO,mBAAmB,EAAE;EACvC,MAAM,UAAmB,EAAE;AAG3B,MAAI,GAAG,WAAW,KAChB,SAAQ,KAAK;GACX,UAAU;GACV,SAAS;GACT,YAAY;GACZ,UAAU;GACV,UAAU,EAAE,QAAQ,MAAM;GAC3B,CAAC;EAIJ,MAAM,UAAmC,EAAE;AAC3C,OAAK,MAAM,OAAO;GAAC;GAAU;GAAU;GAAmB,CACxD,KAAI,GAAG,SAAS,OACd,SAAQ,OAAO,GAAG;AAGtB,MAAI,OAAO,KAAK,QAAQ,CAAC,SAAS,EAChC,SAAQ,KAAK;GACX,UAAU;GACV,SAAS;GACT,YAAY;GACZ,UAAU;GACV,UAAU;GACX,CAAC;AAIJ,MAAI,GAAG,SAAS,OAAO,KAAK,GAAG,MAAM,CAAC,SAAS,EAC7C,SAAQ,KAAK;GACX,UAAU;GACV,SAAS;GACT,YAAY;GACZ,UAAU;GACV,UAAU,EAAE,SAAS,GAAG,OAAO;GAChC,CAAC;AAGJ,SAAO;;CAEV;;;;ACxGD,MAAa,iBAA4B;CACvC,MAAM;CAEN,MAAM,QAAQ,KAA0C;EACtD,MAAM,WAAW,KAAK,IAAI,aAAa,SAAS;AAEhD,MAAI;AACF,SAAM,OAAO,SAAS;UAChB;AACN,UAAO,EAAE;;EAGX,IAAI;AACJ,MAAI;AACF,SAAM,MAAM,SAAS,UAAU,QAAQ;UACjC;AACN,UAAO,EAAE;;EAGX,MAAM,QAAQ,IAAI,MAAM,KAAK;EAE7B,IAAI,aAAa;EACjB,IAAI,YAAY;EAChB,IAAI,kBAAkB;EACtB,IAAI,iBAAiB;AAErB,OAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,UAAU,KAAK,MAAM;AAE3B,OAAI,CAAC,YAAY;IACf,MAAM,cAAc,QAAQ,MAAM,kBAAkB;AACpD,QAAI,cAAc,IAAI;AACpB,kBAAa,YAAY;AACzB;;;AAIJ,OAAI,CAAC,WAAW;IACd,MAAM,UAAU,QAAQ,MAAM,cAAc;AAC5C,QAAI,UAAU,IAAI;AAChB,iBAAY,QAAQ;AACpB;;;AAKJ,OAAI,YAAY,aAAa;AAC3B,qBAAiB;AACjB;;AAEF,OAAI,gBAAgB;AAClB,QAAI,YAAY,IACd,kBAAiB;aACR,QAAQ,SAAS,KAAK,CAAC,QAAQ,WAAW,KAAK,CACxD;AAEF;;AAGF,OAAI,QAAQ,MAAM,wBAAwB,CACxC;;AAIJ,MAAI,CAAC,WACH,QAAO,EAAE;AAGX,SAAO,CACL;GACE,UAAU;GACV,SAAS,eAAe,WAAW;GACnC,YAAY;GACZ,UAAU,CAAC;IAAE,MAAM;IAAU,MAAM;IAAM,CAAC;GAC1C,UAAU;IACR,UAAU;IACV;IACA,WAAW,aAAa;IACxB;IACD;GACF,CACF;;CAEJ;;;;AC5ED,MAAa,qBAAgC;CAC3C,MAAM;CAEN,MAAM,QAAQ,KAA0C;EACtD,MAAM,WAAW,KAAK,IAAI,aAAa,aAAa;AAEpD,MAAI;AACF,SAAM,OAAO,SAAS;UAChB;AACN,UAAO,EAAE;;EAGX,IAAI;AACJ,MAAI;AAEF,WAAQC,MADI,MAAM,SAAS,UAAU,QAAQ,CACvB;UAChB;AACN,UAAO,EAAE;;EAGX,MAAM,cAAc,MAAM,SAAS;AACnC,MAAI,CAAC,YACH,QAAO,EAAE;EAGX,MAAM,kBAAkB,OAAO,KAAK,MAAM,gBAAgB,EAAE,CAAC,CAAC;AAE9D,SAAO,CACL;GACE,UAAU;GACV,SAAS,iBAAiB,YAAY;GACtC,YAAY;GACZ,UAAU,CAAC;IAAE,MAAM;IAAc,MAAM;IAAM,CAAC;GAC9C,UAAU;IACR,UAAU;IACV;IACA;IACD;GACF,CACF;;CAEJ;;;;AC9BD,MAAM,iBAAiB;CAAC;CAAS;CAAW;CAAO;CAAS;AAC5D,MAAM,gBAAgB;CAAC;CAAQ;CAAU;CAAQ;CAAU;CAAc;CAAU;AACnF,MAAM,gBAAgB;CAAC;CAAQ;CAAU;CAAS;CAAU;CAAO;AAEnE,SAAS,kBAAkB,KAAqC;CAC9D,MAAM,QAAQ,IAAI,aAAa;AAC/B,KAAI,cAAc,MAAM,OAAO,MAAM,SAAS,GAAG,CAAC,CAAE,QAAO;AAC3D,KAAI,eAAe,MAAM,OAAO,MAAM,SAAS,GAAG,CAAC,CAAE,QAAO;AAC5D,KAAI,cAAc,MAAM,OAAO,MAAM,SAAS,GAAG,CAAC,CAAE,QAAO;AAC3D,QAAO;;;;;;AAOT,SAAS,6BAA6B,KAAc,UAA8D;CAChH,MAAM,UAA0B,EAAE;CAClC,MAAM,MAAgB,EAAE;AAExB,KAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;EAAE;EAAS;EAAK;CAE5D,MAAM,OADS,IACK;AACpB,KAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;EAAE;EAAS;EAAK;AAE9D,MAAK,MAAM,OAAO,OAAO,OAAO,KAAgC,EAAE;AAChE,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU;EACrC,MAAM,QAAS,IAAgC;AAC/C,MAAI,CAAC,MAAM,QAAQ,MAAM,CAAE;AAC3B,OAAK,MAAM,QAAQ,OAAO;AACxB,OAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;GACvC,MAAM,MAAO,KAAiC;AAC9C,OAAI,OAAO,QAAQ,SAAU;AAE7B,QAAK,MAAM,QAAQ,IAAI,MAAM,KAAK,EAAE;IAClC,MAAM,UAAU,KAAK,MAAM;AAC3B,QAAI,CAAC,QAAS;AACd,QAAI,KAAK,QAAQ;IACjB,MAAM,WAAW,kBAAkB,QAAQ;AAC3C,QAAI,aAAa,KACf,SAAQ,KAAK;KAAE,SAAS;KAAS;KAAU,QAAQ;KAAU,CAAC;;;;AAMtE,QAAO;EAAE;EAAS;EAAK;;;AAIzB,MAAM,kBAAkB,IAAI,IAAI;CAAC;CAAU;CAAa;CAAW;CAAW;CAAY;CAAS;CAAY;CAAiB;CAAgB;CAAS;CAAY,CAAC;;;;;AAMtK,SAAS,wBAAwB,KAAc,UAA8D;CAC3G,MAAM,UAA0B,EAAE;CAClC,MAAM,MAAgB,EAAE;AAExB,KAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;EAAE;EAAS;EAAK;AAE5D,MAAK,MAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,IAA+B,EAAE;AACvE,MAAI,gBAAgB,IAAI,IAAI,CAAE;AAC9B,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU;EACrC,MAAM,SAAU,IAAgC;EAChD,MAAM,UAAU,MAAM,QAAQ,OAAO,GAAG,SAAS,OAAO,WAAW,WAAW,CAAC,OAAO,GAAG,EAAE;AAC3F,OAAK,MAAM,OAAO,SAAS;AACzB,OAAI,OAAO,QAAQ,SAAU;GAC7B,MAAM,UAAU,IAAI,MAAM;AAC1B,OAAI,CAAC,QAAS;AACd,OAAI,KAAK,QAAQ;GACjB,MAAM,WAAW,kBAAkB,QAAQ;AAC3C,OAAI,aAAa,KACf,SAAQ,KAAK;IAAE,SAAS;IAAS;IAAU,QAAQ;IAAU,CAAC;;;AAKpE,QAAO;EAAE;EAAS;EAAK;;AAOzB,MAAa,cAAyB;CACpC,MAAM;CAEN,MAAM,QAAQ,KAA0C;EAOtD,MAAM,UAAU,MAAM,OANH;GACjB;GACA;GACA;GACD,EAEwC;GACvC,KAAK,IAAI;GACT,WAAW;GACX,qBAAqB;GACrB,UAAU;GACX,CAAC;AAEF,MAAI,QAAQ,WAAW,EAAG,QAAO,EAAE;EAEnC,MAAM,UAAmB,EAAE;AAE3B,OAAK,MAAM,WAAW,SAAS;GAC7B,MAAM,UAAU,KAAK,IAAI,aAAa,QAAQ;GAC9C,IAAI;AACJ,OAAI;AACF,UAAM,MAAM,SAAS,SAAS,QAAQ;WAChC;AACN;;GAGF,IAAI;AACJ,OAAI;AACF,UAAMC,KAAS,IAAI;WACb;AAEN;;GAIF,MAAM,EAAE,SAAS,KAAK,YADL,QAAQ,SAAS,aAAa,GAE3C,wBAAwB,KAAK,QAAQ,GACrC,6BAA6B,KAAK,QAAQ;AAG9C,OAAI,QAAQ,SAAS,KAAK,QAAQ,SAAS,EACzC,MAAK,MAAM,EAAE,SAAS,cAAc,QAClC,SAAQ,KAAK;IACX;IACA,SAAS,eAAe;IACxB,YAAY;IACZ,UAAU,CAAC;KAAE,MAAM,SAAS,IAAI,aAAa,QAAQ,IAAI;KAAS,MAAM;KAAM,CAAC;IAC/E,UAAU;KAAE;KAAS,QAAQ;KAAS,aAAa;KAAS;IAC7D,CAAC;;AAKR,SAAO;;CAEV;;;;;ACnKD,MAAa,cAAiC;CAC5C;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;AAyBD,eAAsB,iBACpB,SACmB;CACnB,MAAM,EACJ,KACA,aAAa;EAAC;EAAM;EAAM;EAAQ;EAAK,EACvC,mBAAmB,EAAE,KACnB;AAeJ,SARc,MAAM,OAJlB,WAAW,WAAW,IAClB,QAAQ,WAAW,OACnB,SAAS,WAAW,KAAK,IAAI,CAAC,IAEG;EACrC;EACA,WAAW;EACX,QAAQ,CAAC,GAAG,aAAa,GAAG,iBAAiB;EAC7C,qBAAqB;EACrB,UAAU;EACX,CAAC,EAEW,MAAM;;;;;;ACvCrB,MAAM,oBAAoB;CAAC;CAAS;CAAU;CAAa;AAE3D,MAAM,gBAA+B;CACnC;EAAE,MAAM;EAA6B,UAAU;EAAc,OAAO;EAAmB;CACvF;EAAE,MAAM;EAA6B,UAAU;EAAc,OAAO;EAAmB;CACvF;EAAE,MAAM;EAA6B,UAAU;EAAmB,OAAO;EAAmB;CAC5F;EAAE,MAAM;EAA8B,UAAU;EAAoB,OAAO;EAAoB;CAC/F;EAAE,MAAM;EAAkC,UAAU;EAAwB,OAAO;EAAwB;CAC5G;AAMD,MAAa,4BAAuC;CAClD,MAAM;CAEN,MAAM,QAAQ,KAA0C;EACtD,MAAM,UAAmB,EAAE;AAE3B,OAAK,MAAM,EAAE,MAAM,UAAU,WAAW,eAAe;GACrD,IAAI,UAAU,MAAM,OAAO,MAAM;IAC/B,KAAK,IAAI;IACT,WAAW;IACX,QAAQ,CAAC,GAAG,YAAY;IACxB,qBAAqB;IACrB,UAAU;IACX,CAAC;AAGF,OAAI,aAAa,aACf,WAAU,QAAQ,QACf,MAAM,CAAC,kBAAkB,MAAM,WAAW,EAAE,WAAW,OAAO,CAAC,CACjE;AAGH,OAAI,QAAQ,WAAW,EAAG;GAE1B,MAAM,QAAQ,QAAQ;GACtB,MAAM,aAAa,KAAK,IAAI,KAAM,KAAM,QAAQ,IAAK;GACrD,MAAM,gBAAgB,QAAQ,MAAM,GAAG,EAAE;AAEzC,WAAQ,KAAK;IACX,UAAU;IACV,SAAS,iBAAiB,SAAS,IAAI,MAAM;IAC7C;IACA,UAAU,cAAc,KAAK,OAAO;KAAE,MAAM;KAAG,MAAM;KAAM,EAAE;IAC7D,UAAU;KACR,eAAe;KACf;KACA;KACD;IACF,CAAC;;AAGJ,SAAO;;CAEV;;;;;;;;AC3DD,SAAS,aAAa,MAA+B;AACnD,KAAI,KAAK,SAAS,EAAG,QAAO;AAE5B,KAAI,uBAAuB,KAAK,KAAK,CAAE,QAAO;AAC9C,KAAI,sBAAsB,KAAK,KAAK,CAAE,QAAO;AAC7C,KAAI,oBAAoB,KAAK,KAAK,IAAI,KAAK,SAAS,IAAI,CAAE,QAAO;AACjE,KAAI,sBAAsB,KAAK,KAAK,IAAI,QAAQ,KAAK,KAAK,CAAE,QAAO;AAEnE,QAAO;;AAOT,MAAa,kBAA6B;CACxC,MAAM;CAEN,MAAM,QAAQ,KAA0C;EACtD,MAAM,QAAQ,MAAM,iBAAiB;GACnC,KAAK,IAAI;GACT,YAAY;IAAC;IAAM;IAAO;IAAM;IAAM;GACvC,CAAC;AAEF,MAAI,MAAM,WAAW,EAAG,QAAO,EAAE;EAEjC,MAAM,WAAW,IAAI,SAAS,kBAAkB;EAChD,MAAM,iBAAiB,MAAM,MAAM,GAAG,SAAS;EAE/C,MAAM,UAAU,IAAI,QAAQ;GAC1B,iBAAiB;IAAE,SAAS;IAAM,QAAQ;IAAM;GAChD,8BAA8B;GAC/B,CAAC;EAGF,MAAM,WAAW,eAAe,KAAK,MAAM,GAAG,IAAI,YAAY,GAAG,IAAI;AACrE,UAAQ,sBAAsB,SAAS;EAGvC,MAAM,YAAoC,EAAE;EAC5C,MAAM,YAAoC,EAAE;EAC5C,MAAM,UAAkC,EAAE;EAC1C,MAAM,SAAiD;GAAE;GAAW;GAAW;GAAS;AAExF,OAAK,MAAM,MAAM,QAAQ,gBAAgB,EAAE;AACzC,QAAK,MAAM,MAAM,GAAG,cAAc,EAAE;IAClC,MAAM,OAAO,GAAG,SAAS;AACzB,QAAI,CAAC,KAAM;IACX,MAAM,OAAO,aAAa,KAAK;AAC/B,QAAI,KAAM,WAAU,SAAS,UAAU,SAAS,KAAK;;AAGvD,QAAK,MAAM,QAAQ,GAAG,yBAAyB,EAAE;IAE/C,MAAM,OAAO,aADA,KAAK,SAAS,CACI;AAC/B,QAAI,KAAM,WAAU,SAAS,UAAU,SAAS,KAAK;;AAGvD,QAAK,MAAM,OAAO,GAAG,YAAY,EAAE;IACjC,MAAM,OAAO,IAAI,SAAS;AAC1B,QAAI,CAAC,KAAM;IACX,MAAM,OAAO,aAAa,KAAK;AAC/B,QAAI,KAAM,SAAQ,SAAS,QAAQ,SAAS,KAAK;;;EAIrD,MAAM,UAAmB,EAAE;AAE3B,OAAK,MAAM,CAAC,YAAY,eAAe,OAAO,QAAQ,OAAO,EAAE;GAC7D,MAAM,QAAQ,OAAO,OAAO,WAAW,CAAC,QAAQ,GAAG,MAAM,IAAI,GAAG,EAAE;AAClE,OAAI,QAAQ,EAAG;GAGf,IAAI,WAA4B;GAChC,IAAI,gBAAgB;AACpB,QAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,WAAW,CACpD,KAAI,QAAQ,eAAe;AACzB,eAAW;AACX,oBAAgB;;AAIpB,OAAI,CAAC,SAAU;GAEf,MAAM,aAAa,KAAK,IAAI,KAAM,gBAAgB,MAAM;AACxD,OAAI,aAAa,GAAK;AAEtB,WAAQ,KAAK;IACX,UAAU;IACV,SAAS,GAAG,WAAW,OAAO,SAAS;IACvC;IACA,UAAU,CAAC;KAAE,MAAM;KAAe,MAAM;KAAM,CAAC;IAC/C,UAAU;KACR;KACA,cAAc;KACd,QAAQ;KACR,YAAY;KACb;IACF,CAAC;;AAGJ,SAAO;;CAEV;;;;;ACzGD,MAAM,iBAAiB;CAAC;CAAM;CAAM;CAAM;CAAQ;AAElD,SAAS,aAAa,WAA4B;AAChD,QAAO,eAAe,MAAM,WAAW,UAAU,WAAW,OAAO,CAAC;;;;;;;AAQtE,SAAS,aAAa,YAAiC;AAErD,KAAI,EADe,WAAW,uBAAuB,CAAC,SAAS,GAC9C,QAAO;CACxB,MAAM,eAAe,WAAW,cAAc,CAAC,SAAS;CACxD,MAAM,aAAa,WAAW,YAAY,CAAC,SAAS;CACpD,MAAM,UAAU,WAAW,yBAAyB,CAAC,SAAS;AAC9D,QAAO,CAAC,gBAAgB,CAAC,cAAc,CAAC;;;;;;;AAQ1C,SAAS,mBAAmB,SAA+B;CACzD,MAAM,0BAAU,IAAI,KAAa;AACjC,MAAK,MAAM,MAAM,QAAQ,gBAAgB,EAAE;EACzC,MAAM,WAAW,GAAG,aAAa;EACjC,MAAM,WAAW,SAAS,MAAM,IAAI,CAAC,KAAK,IAAI;AAE9C,MAAI,mBAAmB,KAAK,SAAS,IAAI,aAAa,GAAG,CACvD,SAAQ,IAAI,SAAS;;AAGzB,QAAO;;;AAIT,SAAS,sBAAsB,eAAuB,WAA6B;CAEjF,MAAM,OAAO,QADD,QAAQ,cAAc,EACR,UAAU;CACpC,MAAM,OAAO;EAAC;EAAO;EAAQ;EAAO;EAAO;CAC3C,MAAM,aAAuB,EAAE;AAE/B,MAAK,MAAM,OAAO,KAAM,YAAW,KAAK,OAAO,IAAI;AAEnD,MAAK,MAAM,OAAO,KAAM,YAAW,KAAK,KAAK,MAAM,UAAU,IAAI,CAAC;AAClE,QAAO;;AAOT,MAAa,mBAA8B;CACzC,MAAM;CAEN,MAAM,QAAQ,KAA0C;EACtD,MAAM,QAAQ,MAAM,iBAAiB;GACnC,KAAK,IAAI;GACT,YAAY;IAAC;IAAM;IAAO;IAAM;IAAM;GACvC,CAAC;AAEF,MAAI,MAAM,WAAW,EAAG,QAAO,EAAE;EAEjC,MAAM,WAAW,IAAI,SAAS,kBAAkB;EAChD,MAAM,iBAAiB,MAAM,MAAM,GAAG,SAAS;EAE/C,MAAM,UAAU,IAAI,QAAQ;GAC1B,iBAAiB;IAAE,SAAS;IAAM,QAAQ;IAAM;GAChD,8BAA8B;GAC/B,CAAC;EAEF,MAAM,WAAW,eAAe,KAAK,MAAM,GAAG,IAAI,YAAY,GAAG,IAAI;AACrE,UAAQ,sBAAsB,SAAS;EAGvC,MAAM,cAAc,mBAAmB,QAAQ;EAE/C,IAAI,gBAAgB;EACpB,IAAI,gBAAgB;EACpB,IAAI,cAAc;EAClB,IAAI,aAAa;AAEjB,OAAK,MAAM,MAAM,QAAQ,gBAAgB,CACvC,MAAK,MAAM,OAAO,GAAG,uBAAuB,EAAE;GAC5C,MAAM,YAAY,IAAI,yBAAyB;AAE/C,OAAI,IAAI,2BAA2B,EAAE;AACnC;AAIA,QADmB,sBAAsB,GAAG,aAAa,EAAE,UAAU,CACtD,MAAM,MAAM,YAAY,IAAI,EAAE,CAAC,CAC5C;UAEG;AACL;AAEA,QAAI,aAAa,UAAU,CACzB;;;EAMR,MAAM,eAAe,gBAAgB;AACrC,MAAI,iBAAiB,EAAG,QAAO,EAAE;EAEjC,MAAM,UAAmB,EAAE;EAC3B,MAAM,WAAW,CAAC;GAAE,MAAM;GAAe,MAAM;GAAM,CAAC;EAGtD,MAAM,WAAW,gBAAgB;EACjC,IAAI;AACJ,MAAI,YAAY,IACd,cAAa;WACJ,YAAY,IACrB,cAAa;MAEb,cAAa;EAIf,MAAM,iBAAiB,KAAK,IAAI,KAAM,KAAO,eAAe,MAAO,IAAK;AAExE,UAAQ,KAAK;GACX,UAAU;GACV,SAAS;GACT,YAAY;GACZ;GACA,UAAU;IACR;IACA;IACA;IACA,eAAe,KAAK,MAAM,WAAW,IAAI,GAAG;IAC7C;GACF,CAAC;AAGF,MAAI,cAAc,GAAG;GACnB,MAAM,cAAc,cAAc;AAClC,WAAQ,KAAK;IACX,UAAU;IACV,SAAS;IACT,YAAY,KAAK,IAAI,KAAM,KAAM,cAAc,IAAK;IACpD;IACA,UAAU;KAAE;KAAa;KAAe;IACzC,CAAC;;AAIJ,MAAI,aAAa,EACf,SAAQ,KAAK;GACX,UAAU;GACV,SAAS;GACT,YAAY;GACZ;GACA,UAAU,EAAE,YAAY;GACzB,CAAC;AAGJ,SAAO;;CAEV;;;;ACxKD,MAAa,+BAA0C;CACrD,MAAM;CAEN,MAAM,QAAQ,KAA0C;EACtD,MAAM,QAAQ,MAAM,iBAAiB;GACnC,KAAK,IAAI;GACT,YAAY;IAAC;IAAM;IAAO;IAAM;IAAM;GACvC,CAAC;AAEF,MAAI,MAAM,WAAW,EAAG,QAAO,EAAE;EAEjC,MAAM,WAAW,IAAI,SAAS,kBAAkB;EAChD,MAAM,iBAAiB,MAAM,MAAM,GAAG,SAAS;EAE/C,MAAM,UAAU,IAAI,QAAQ;GAC1B,iBAAiB;IAAE,SAAS;IAAM,QAAQ;IAAM;GAChD,8BAA8B;GAC/B,CAAC;EAEF,MAAM,WAAW,eAAe,KAAK,MAAM,GAAG,IAAI,YAAY,GAAG,IAAI;AACrE,UAAQ,sBAAsB,SAAS;EAEvC,IAAI,oBAAoB;EACxB,IAAI,qBAAqB;EACzB,IAAI,wBAAwB;EAC5B,MAAM,mBAA6B,EAAE;EACrC,MAAM,sBAAgC,EAAE;AAExC,OAAK,MAAM,MAAM,QAAQ,gBAAgB,EAAE;GACzC,MAAM,UAAU,GAAG,aAAa,CAAC,QAAQ,IAAI,cAAc,KAAK,GAAG;GAEnE,MAAM,gBAAgB,GAAG,qBAAqB,WAAW,aAAa;AACtE,OAAI,cAAc,SAAS,GAAG;AAC5B;AACA,0BAAsB,cAAc;AACpC,QAAI,iBAAiB,SAAS,EAAG,kBAAiB,KAAK,QAAQ;;AAGjE,QAAK,MAAM,OAAO,GAAG,YAAY,EAAE;IACjC,MAAM,WAAW,IAAI,YAAY;AACjC,QAAI,YAAY,YAAY,KAAK,SAAS,SAAS,CAAC,EAAE;AACpD;AACA,SAAI,oBAAoB,SAAS,EAAG,qBAAoB,KAAK,QAAQ;;;;EAK3E,MAAM,UAAmB,EAAE;EAC3B,MAAM,aAAa,eAAe;AAGlC,MAAI,sBAAsB,GAAG;GAE3B,MAAM,aAAa,oBAAoB;GACvC,MAAM,eAAe,KAAK,IAAI,IAAK,qBAAqB,IAAK;GAC7D,MAAM,aAAa,KAAK,IAAI,KAAM,KAAM,aAAa,MAAO,aAAa;AACzE,WAAQ,KAAK;IACX,UAAU;IACV,SAAS;IACT;IACA,UAAU,iBAAiB,KAAK,UAAU;KAAE;KAAM,MAAM;KAAM,EAAE;IAChE,UAAU;KAAE,OAAO;KAAa,WAAW;KAAmB,YAAY;KAAoB;IAC/F,CAAC;;AAGJ,MAAI,yBAAyB,GAAG;GAC9B,MAAM,aAAa,KAAK,IAAI,KAAM,KAAO,wBAAwB,aAAc,IAAK;AACpF,WAAQ,KAAK;IACX,UAAU;IACV,SAAS;IACT;IACA,UAAU,oBAAoB,KAAK,UAAU;KAAE;KAAM,MAAM;KAAM,EAAE;IACnE,UAAU;KAAE,OAAO;KAAsB,YAAY;KAAuB;IAC7E,CAAC;;AAGJ,SAAO;;CAEV;;;;;;;;;;ACzCD,IAAM,qBAAN,MAAmD;CACjD,YAAY,AAAiB,aAAqB;EAArB;;CAE7B,MAAM,SAAS,aAAuC;AAEpD,SAAO,WADU,KAAK,aAAa,aAAa,CACrB;;CAG7B,MAAM,YAAY,aAAoC;AACpD,MAAI,MAAM,KAAK,SAAS,YAAY,CAClC;AAEF,QAAM,MAAM,YAAY;;CAG1B,MAAM,OACJ,aACA,UAA0B,EAAE,EACH;EACzB,MAAM,EAAE,IAAI,OAAO;EAEnB,IAAI;AACJ,MAAI;AACF,SAAM,MAAM,MAAM,aAAa;IAC7B,MAAM;IACN,YAAY,KAAK;IACjB,WAAW;IACZ,CAAC;WACK,KAAc;AACrB,OAAI,eAAe,iBAAiB,IAAI,SAAS,WAC/C,QAAO,EAAE;AAEX,SAAM;;EAGR,MAAM,UAA0B,EAAE;AAElC,OAAK,MAAM,OAAO,IAAI,KACpB,SAAQ,KAAK;GAAE,MAAM,IAAI;GAAM,OAAO,IAAI;GAAM,OAAO,IAAI;GAAO,CAAC;AAErE,OAAK,MAAM,OAAO,IAAI,KACpB,SAAQ,KAAK;GAAE,MAAM,IAAI;GAAM,OAAO,IAAI;GAAM,OAAO,IAAI;GAAO,CAAC;AAGrE,UAAQ,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM;AACzC,SAAO,QAAQ,MAAM,GAAG,EAAE;;CAG5B,MAAM,MAAM,OAAkC;AAI5C,QAAM,IAAI,MACR,6FAED;;;;;;AAWL,eAAsB,aACpB,aACyB;AACzB,QAAO,IAAI,mBAAmB,YAAY;;;;;ACnG5C,MAAM,WAAyB;CAC7B;EACE,OAAO;EACP,SAAS;EACT,OAAO,YAAY,aAAa,KAAK,QAAQ,IAAI,eAAe,KAAK,QAAQ;EAC9E;CACD;EACE,OAAO;EACP,SAAS;EACT,OAAO,YACL,6DAA6D,KAAK,QAAQ;EAC7E;CACD;EACE,OAAO;EACP,SAAS;EACT,OAAO,YAAY,wCAAwC,KAAK,QAAQ;EACzE;CACD;EACE,OAAO;EACP,SAAS;EACT,OAAO,YAAY,0CAA0C,KAAK,QAAQ;EAC3E;CACF;AAMD,MAAa,yBAAoC;CAC/C,MAAM;CAEN,MAAM,QAAQ,KAA0C;EACtD,MAAM,SAAS,MAAM,aAAa,IAAI,YAAY;AAElD,MAAI,CAAE,MAAM,OAAO,SAAS,IAAI,YAAY,CAC1C,QAAO,EAAE;EAIX,MAAM,CAAC,SAAS,YAAY,gBAAgB,MAAM,QAAQ,IAAI;GAC5D,OAAO,OAAO,4CAA4C,EAAE,GAAG,IAAI,CAAC;GACpE,OAAO,OAAO,oCAAoC,EAAE,GAAG,IAAI,CAAC;GAC5D,OAAO,OAAO,oCAAoC,EAAE,GAAG,IAAI,CAAC;GAC7D,CAAC;EAGF,MAAM,iCAAiB,IAAI,KAAqB;AAChD,OAAK,MAAM,UAAU;GAAC,GAAG;GAAS,GAAG;GAAY,GAAG;GAAa,EAAE;GACjE,MAAM,WAAW,eAAe,IAAI,OAAO,KAAK,IAAI;AACpD,kBAAe,IAAI,OAAO,MAAM,WAAW,OAAO,OAAO,MAAM;;EAGjE,MAAM,mBAAmB,eAAe;AACxC,MAAI,qBAAqB,EAAG,QAAO,EAAE;EAErC,MAAM,UAAmB,EAAE;AAE3B,OAAK,MAAM,cAAc,UAAU;GACjC,MAAM,gBAA0B,EAAE;AAElC,QAAK,MAAM,CAAC,MAAM,YAAY,eAC5B,KAAI,WAAW,KAAK,QAAQ,CAC1B,eAAc,KAAK,KAAK;AAK5B,OAAI,cAAc,SAAS,EAAG;GAE9B,MAAM,aAAa,KAAK,IAAI,KAAM,KAAO,cAAc,SAAS,mBAAoB,IAAK;AAEzF,WAAQ,KAAK;IACX,UAAU;IACV,SAAS,WAAW;IACpB;IACA,UAAU,cAAc,MAAM,GAAG,EAAE,CAAC,KAAK,UAAU;KAAE;KAAM,MAAM;KAAM,EAAE;IACzE,UAAU;KACR,OAAO,WAAW;KAClB,WAAW,cAAc;KAC1B;IACF,CAAC;;AAGJ,SAAO;;CAEV;;;;;ACzFD,SAAS,kBAAkB,OAA8B;CACvD,MAAM,uBAAO,IAAI,KAAa;AAC9B,MAAK,MAAM,KAAK,OAAO;EACrB,MAAM,QAAQ,EAAE,MAAM,IAAI;AAC1B,MAAI,MAAM,SAAS,EAAG;AAEtB,MAAI,MAAM,OAAO,SAAS,MAAM,UAAU,EAExC,MAAK,IAAI,OAAO,MAAM,KAAK;WAClB,MAAM,OAAO,MAEtB,MAAK,IAAI,MAAM,GAAI;;AAGvB,QAAO;;;AAIT,SAAS,UAAU,KAAqB;AACtC,QAAO,IAAI,MAAM,IAAI,CAAC,KAAK,CAAE,aAAa;;;AAI5C,SAAS,UAAU,YAAmC;CACpD,MAAM,MAAM;EAAC;EAAS;EAAU;EAAQ;EAAS;EAAc;EAAe;EAAS;EAAS;CAChG,MAAM,QAAkB,EAAE;AAC1B,MAAK,MAAM,OAAO,WAChB,KAAI,IAAI,SAAS,UAAU,IAAI,CAAC,CAAE,OAAM,KAAK,IAAI;AAInD,QADkB,IAAI,IAAI,MAAM,KAAK,MAAM,UAAU,EAAE,CAAC,QAAQ,MAAM,GAAG,CAAC,CAAC,CAC1D,QAAQ,IAAI,QAAQ,EAAE;;;AAIzC,SAAS,mBAAmB,OAA2B;CACrD,MAAM,iBAAiB;CACvB,MAAM,8BAAc,IAAI,KAAa;CACrC,IAAI,QAAQ;AACZ,MAAK,MAAM,KAAK,MACd,KAAI,eAAe,KAAK,EAAE,EAAE;AAC1B;EAEA,MAAM,QAAQ,EAAE,MAAM,yCAAyC;AAC/D,MAAI,MAAO,aAAY,IAAI,MAAM,GAAI;;AAGzC,QAAO,SAAS,IAAI,MAAM,KAAK,YAAY,GAAG,EAAE;;;AAIlD,SAAS,iBAAiB,YAAmC;CAC3D,MAAM,gBAAgB;EAEpB;EAAU;EAAe;EAEzB;EAAW;EAAY;EAAc;EAAgB;EAAW;EAAY;EAAW;EAEvF;EAAQ;EAAQ;EACjB;CACD,MAAM,QAAkB,EAAE;AAC1B,MAAK,MAAM,OAAO,WAChB,KAAI,cAAc,SAAS,UAAU,IAAI,CAAC,CAAE,OAAM,KAAK,IAAI;AAI7D,QADkB,IAAI,IAAI,MAAM,KAAK,MAAM,UAAU,EAAE,CAAC,CAAC,CACxC,QAAQ,IAAI,QAAQ,EAAE;;AAOzC,MAAa,wBAAmC;CAC9C,MAAM;CAEN,MAAM,QAAQ,KAA0C;EAEtD,MAAM,QAAQ,MAAM,iBAAiB;GACnC,KAAK,IAAI;GACT,YAAY;IAAC;IAAM;IAAM;IAAO;IAAO;IAAM;IAAM;IAAM;IAAK;GAC/D,CAAC;EAEF,MAAM,aAAa,kBAAkB,MAAM;EAG3C,MAAM,UAAU,UAAU,WAAW;EACrC,MAAM,cAAc,mBAAmB,MAAM;EAC7C,MAAM,YAAY,iBAAiB,WAAW;EAE9C,IAAI,kBAAsC;EAC1C,IAAI,iBAA2B,EAAE;AAEjC,MAAI,QAAQ,SAAS,GAAG;AACtB,qBAAkB;AAClB,oBAAiB;aACR,YAAY,SAAS,GAAG;AACjC,qBAAkB;AAClB,oBAAiB;aACR,UAAU,SAAS,GAAG;AAC/B,qBAAkB;AAClB,oBAAiB;;AAGnB,MAAI,CAAC,gBAAiB,QAAO,EAAE;EAG/B,MAAM,SAAS,MAAM,aAAa,IAAI,YAAY;EAClD,MAAM,SAAS,MAAM,OAAO,SAAS,IAAI,YAAY;EAErD,IAAI,aAAa;EACjB,MAAM,WAA2C,eAC9C,MAAM,GAAG,EAAE,CACX,KAAK,SAAS;GAAE,MAAM;GAAK,MAAM;GAAM,EAAE;AAE5C,MAAI,QAAQ;GACV,MAAM,gBAAgB,MAAM,OAAO,OACjC,0DACA,EAAE,GAAG,IAAI,CACV;AACD,OAAI,cAAc,SAAS,GAAG;AAC5B,iBAAa;AACb,SAAK,MAAM,KAAK,cAAc,MAAM,GAAG,EAAE,CACvC,UAAS,KAAK;KAAE,MAAM,EAAE;KAAM,MAAM;KAAM,CAAC;;;EAMjD,MAAM,uBAAO,IAAI,KAAa;EAC9B,MAAM,UAAU,SAAS,QAAQ,EAAE,WAAW;AAC5C,OAAI,KAAK,IAAI,KAAK,CAAE,QAAO;AAC3B,QAAK,IAAI,KAAK;AACd,UAAO;IACP;AAEF,SAAO,CACL;GACE,UAAU;GACV,SAAS,aAAa,gBAAgB;GACtC;GACA,UAAU,QAAQ,MAAM,GAAG,EAAE;GAC7B,UAAU;IACR,qBAAqB;IACrB,QAAQ;IACT;GACF,CACF;;CAEJ;AAED,SAAS,aAAa,GAAwB;AAC5C,SAAQ,GAAR;EACE,KAAK,MACH,QAAO;EACT,KAAK,gBACH,QAAO;EACT,KAAK,cACH,QAAO;;;;;;AC1Jb,MAAM,iBAAiB;CACrB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;AASD,SAAS,oBAAoB,UAAwC;CACnE,MAAM,uBAAO,IAAI,KAAa;AAC9B,QAAO,SAAS,QAAQ,MAAM;EAC5B,MAAM,MAAM,GAAG,EAAE,KAAK,GAAG,EAAE,QAAQ;AACnC,MAAI,KAAK,IAAI,IAAI,CAAE,QAAO;AAC1B,OAAK,IAAI,IAAI;AACb,SAAO;GACP;;;;;;;AAQJ,SAAS,uBACP,aACmB;CACnB,MAAM,0BAAU,IAAI,KAA8B;AAElD,MAAK,MAAM,SAAS,aAAa;EAC/B,MAAM,MAAM,GAAG,MAAM,SAAS,GAAG,MAAM;EACvC,MAAM,WAAW,QAAQ,IAAI,IAAI;AAEjC,MAAI,CAAC,SACH,SAAQ,IAAI,KAAK,MAAM;OAClB;GAEL,MAAM,SACJ,MAAM,aAAa,SAAS,aAAa,QAAQ;GACnD,MAAM,iBAAiB,oBAAoB,CACzC,GAAG,SAAS,UACZ,GAAG,MAAM,SACV,CAAC;AACF,WAAQ,IAAI,KAAK;IAAE,GAAG;IAAQ,UAAU;IAAgB,CAAC;;;AAI7D,QAAO,MAAM,KAAK,QAAQ,QAAQ,CAAC;;;;;;AAWrC,SAAS,kBAAkB,UAAkD;CAC3E,MAAM,QAAQ,EAAE,GAAG,SAAS,OAAO;AAEnC,MAAK,MAAM,SAAS,SAAS,aAAa;EACxC,MAAM,OAAO,MAAM,YAAY,EAAE;AAEjC,MAAI,MAAM,aAAa,SAAS;AAC9B,OAAI,CAAC,MAAM,YAAY,MAAM,aAAa,WACxC;QAAI,OAAO,KAAK,aAAa,SAC3B,OAAM,WAAW,KAAK;;AAG1B,OAAI,CAAC,MAAM,aAAa,OAAO,KAAK,cAAc,SAChD,OAAM,YAAY,KAAK;AAEzB,OAAI,CAAC,MAAM,cAAc,OAAO,KAAK,eAAe,SAClD,OAAM,aAAa,KAAK;AAE1B,OAAI,CAAC,MAAM,kBAAkB,OAAO,KAAK,mBAAmB,SAC1D,OAAM,iBAAiB,KAAK;AAE9B,OAAI,CAAC,MAAM,WAET;QAAI,OAAO,KAAK,cAAc,SAC5B,OAAM,YAAY,KAAK;aAEvB,OAAO,KAAK,eAAe,YAC3B,KAAK,eAAe,WACpB,OAAO,KAAK,YAAY,SAGxB,OAAM,YAAa,KAAK,QAAmB,MAAM,IAAI,CAAC;;;AAK5D,MAAI,MAAM,aAAa,WACrB;OAAI,CAAC,MAAM,cAAc,OAAO,KAAK,eAAe,SAClD,OAAM,aAAa,KAAK;;;AAK9B,QAAO;EAAE,GAAG;EAAU;EAAO;;;;;;AAW/B,SAAS,yBACP,UACoB;CACpB,MAAM,OAAO,EAAE,GAAG,SAAS,cAAc;AAEzC,MAAK,MAAM,SAAS,SAAS,YAC3B,KAAI,MAAM,aAAa,gBAAgB;AACrC,MACE,CAAC,KAAK,WACN,OAAO,MAAM,UAAU,wBAAwB,SAE/C,MAAK,UAAU,MAAM,SAAS;AAEhC,MACE,KAAK,OAAO,WAAW,KACvB,MAAM,QAAQ,MAAM,UAAU,OAAO,CAErC,MAAK,SAAS,MAAM,SAAS;;AAKnC,QAAO;EAAE,GAAG;EAAU,cAAc;EAAM;;;;;;;;;;;AAgB5C,eAAsB,mBACpB,aACA,SAC6B;CAK7B,MAAM,YAAY,MAAM,cAAc,gBAJ1B;EAAE;EAAa;EAAS,EACd,eAAe,YAAY,CAGwB;CAYzE,MAAM,WAAW,yBAHC,kBANuB;EACvC,GAAG;EACH,aAAa,uBAAuB,UAAU,YAAY;EAC3D,CAGgD,CAGG;AAGpD,QAAO,yBAAyB,MAAM,SAAS;;;;;AC7MjD,MAAa,eAAe;AAC5B,MAAa,aAAa;;;;;;;;;;AAW1B,eAAsB,iBACpB,UACA,SACe;CACf,MAAM,UAAU,GAAG,aAAa,IAAI,QAAQ,IAAI;AAEhD,KAAI,CAAC,WAAW,SAAS,EAAE;AACzB,QAAM,UAAU,UAAU,UAAU,MAAM,QAAQ;AAClD;;CAGF,MAAM,WAAW,MAAM,SAAS,UAAU,QAAQ;CAClD,MAAM,WAAW,SAAS,QAAQ,aAAa;CAC/C,MAAM,SAAS,SAAS,QAAQ,WAAW;AAG3C,KAAI,aAAa,MAAM,WAAW,IAAI;AAEpC,QAAM,UAAU,UAAU,YADR,SAAS,SAAS,KAAK,GAAG,OAAO,UACF,UAAU,MAAM,QAAQ;AACzE;;CAGF,MAAM,SAAS,SAAS,MAAM,GAAG,SAAS;CAC1C,MAAM,QAAQ,SAAS,MAAM,SAAS,GAAkB;AACxD,OAAM,UAAU,UAAU,SAAS,UAAU,OAAO,QAAQ;;;;;;;;;AChC9D,SAAgB,gBACd,UACgD;CAChD,MAAM,WAA2D,EAAE;AACnE,MAAK,MAAM,SAAS,UAAU;EAC5B,MAAM,OAAO,MAAM;AACnB,MACE,QACA,OAAO,KAAK,eAAe,YAC3B,OAAO,KAAK,kBAAkB,SAE9B,UAAS,KAAK;GACZ,YAAY,KAAK;GACjB,SAAS,KAAK;GACf,CAAC;;AAGN,QAAO;;;;;;;AAQT,SAAS,wBACP,OACA,OACS;AACT,KAAI,MAAM,aAAa,QAAS,QAAO;CACvC,MAAM,OAAO,MAAM,YAAY,EAAE;AAEjC,KAAI,OAAO,KAAK,gBAAgB,YAAY,MAAM,aAAa,UAAW,QAAO;AACjF,KAAI,OAAO,KAAK,iBAAiB,YAAY,MAAM,UAAW,QAAO;AACrE,KAAI,OAAO,KAAK,iBAAiB,YAAY,MAAM,UAAW,QAAO;AACrE,KAAI,OAAO,KAAK,sBAAsB,YAAY,MAAM,eAAgB,QAAO;AAE/E,KAAI,OAAO,KAAK,kBAAkB,SAAU,QAAO;AAEnD,QAAO;;;;;;;AAQT,SAAgB,sBAAsB,OAAiC;AACrE,KAAI,MAAM,aAAa,aAAa,MAAM,QAAQ,WAAW,eAAe,CAC1E,QAAO;AAET,KAAI,MAAM,YAAY,OAAO,MAAM,SAAS,kBAAkB,SAC5D,QAAO;AAET,QAAO;;;;;;AAOT,SAAgB,sBACd,UACA,qBACU;CACV,MAAM,WAAW,SAAS,YAAY,QACnC,MAAM,EAAE,cAAc,oBACxB;CAED,MAAM,QAAkB,EAAE;CAG1B,MAAM,IAAI,SAAS;AAQnB,KANE,EAAE,aAAa,aACf,QAAQ,EAAE,UAAU,IACpB,QAAQ,EAAE,UAAU,IACpB,QAAQ,EAAE,eAAe,IACzB,QAAQ,EAAE,WAAW,EAET;AACZ,QAAM,KAAK,WAAW;AACtB,MAAI,EAAE,aAAa,UAAW,OAAM,KAAK,eAAe,EAAE,WAAW;AACrE,MAAI,EAAE,UAAW,OAAM,KAAK,gBAAgB,EAAE,YAAY;AAC1D,MAAI,EAAE,UAAW,OAAM,KAAK,YAAY,EAAE,YAAY;AACtD,MAAI,EAAE,eAAgB,OAAM,KAAK,sBAAsB,EAAE,iBAAiB;AAC1E,MAAI,EAAE,WAAY,OAAM,KAAK,kBAAkB,EAAE,aAAa;AAC9D,QAAM,KAAK,GAAG;;CAIhB,MAAM,IAAI,SAAS;AAInB,KAFE,QAAQ,EAAE,QAAQ,KAAK,EAAE,QAAQ,UAAU,KAAK,GAE7B;AACnB,QAAM,KAAK,kBAAkB;AAC7B,MAAI,EAAE,QAAS,OAAM,KAAK,cAAc,EAAE,UAAU;AACpD,MAAI,EAAE,UAAU,EAAE,OAAO,SAAS,EAChC,OAAM,KAAK,aAAa,EAAE,OAAO,KAAK,KAAK,GAAG;AAEhD,QAAM,KAAK,GAAG;;CAKhB,MAAM,8BAAc,IAAI,KAAuB;AAC/C,MAAK,MAAM,SAAS,UAAU;AAC5B,MAAI,MAAM,aAAa,eAAgB;AACvC,MAAI,MAAM,aAAa,WAAW,CAAC,wBAAwB,OAAO,EAAE,CAAE;AACtE,MAAI,sBAAsB,MAAM,CAAE;EAClC,MAAM,OAAO,YAAY,IAAI,MAAM,SAAS,IAAI,EAAE;AAClD,OAAK,KAAK,MAAM,QAAQ;AACxB,cAAY,IAAI,MAAM,UAAU,KAAK;;AAGvC,KAAI,YAAY,OAAO,GAAG;AACxB,QAAM,KAAK,iBAAiB;AAC5B,OAAK,MAAM,CAAC,UAAU,aAAa,YACjC,MAAK,MAAM,WAAW,SACpB,OAAM,KAAK,OAAO,SAAS,MAAM,UAAU;AAG/C,QAAM,KAAK,GAAG;;CAIhB,MAAM,WAAW,gBAAgB,SAAS;AAE1C,KAAI,SAAS,SAAS,GAAG;AACvB,QAAM,KAAK,cAAc;AACzB,OAAK,MAAM,OAAO,SAChB,OAAM,KAAK,OAAO,IAAI,WAAW,QAAQ,IAAI,QAAQ,IAAI;AAE3D,QAAM,KAAK,GAAG;;AAGhB,QAAO;;;;;AChIT,SAASC,WAAS,UAA8B,qBAA6B;CAC3E,MAAM,WAAW,SAAS,YAAY,QACnC,MAAM,EAAE,cAAc,oBACxB;CAID,MAAM,8BAAc,IAAI,KAAgC;AACxD,MAAK,MAAM,SAAS,UAAU;AAC5B,MAAI,MAAM,aAAa,eAAgB;AACvC,MAAI,MAAM,aAAa,SAAS;GAC9B,MAAM,OAAO,MAAM,YAAY,EAAE;AAEjC,OAAI,OAAO,KAAK,gBAAgB,SAAU;AAC1C,OAAI,OAAO,KAAK,iBAAiB,SAAU;AAC3C,OAAI,OAAO,KAAK,iBAAiB,SAAU;AAC3C,OAAI,OAAO,KAAK,sBAAsB,SAAU;AAChD,OAAI,OAAO,KAAK,kBAAkB,SAAU;;AAE9C,MAAI,sBAAsB,MAAM,CAAE;EAClC,MAAM,OAAO,YAAY,IAAI,MAAM,SAAS,IAAI,EAAE;AAClD,OAAK,KAAK,MAAM;AAChB,cAAY,IAAI,MAAM,UAAU,KAAK;;CAGvC,MAAM,mBAAsC,EAAE;AAC9C,MAAK,MAAM,CAAC,UAAU,YAAY,YAChC,kBAAiB,KAAK;EAAE;EAAU;EAAS,CAAC;CAG9C,MAAM,WACJ,SAAS,MAAM,aAAa,aAC5B,QAAQ,SAAS,MAAM,UAAU,IACjC,QAAQ,SAAS,MAAM,UAAU,IACjC,QAAQ,SAAS,MAAM,eAAe,IACtC,QAAQ,SAAS,MAAM,WAAW;CAEpC,MAAM,kBACJ,QAAQ,SAAS,aAAa,QAAQ,KACrC,SAAS,aAAa,QAAQ,UAAU,KAAK;AAEhD,QAAO;EACL,OAAO,SAAS;EAChB,cAAc,SAAS;EACvB;EACA;EACA;EACD;;AAOH,SAAgB,eACd,UACA,qBACQ;CACR,MAAM,OAAOA,WAAS,UAAU,oBAAoB;CACpD,MAAM,QAAkB,EAAE;AAE1B,OAAM,KAAK,oBAAoB;AAG/B,KAAI,KAAK,UAAU;AACjB,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,WAAW;AACtB,MAAI,KAAK,MAAM,aAAa,UAC1B,OAAM,KAAK,eAAe,KAAK,MAAM,WAAW;AAElD,MAAI,KAAK,MAAM,UAAW,OAAM,KAAK,gBAAgB,KAAK,MAAM,YAAY;AAC5E,MAAI,KAAK,MAAM,UAAW,OAAM,KAAK,YAAY,KAAK,MAAM,YAAY;AACxE,MAAI,KAAK,MAAM,eAAgB,OAAM,KAAK,sBAAsB,KAAK,MAAM,iBAAiB;AAC5F,MAAI,KAAK,MAAM,WAAY,OAAM,KAAK,kBAAkB,KAAK,MAAM,aAAa;;AAIlF,KAAI,KAAK,iBAAiB,SAAS,GAAG;AACpC,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,iBAAiB;AAC5B,OAAK,MAAM,SAAS,KAAK,iBACvB,MAAK,MAAM,SAAS,MAAM,QACxB,OAAM,KAAK,OAAO,MAAM,SAAS,MAAM,MAAM,UAAU;;AAM7D,KAAI,KAAK,iBAAiB;AACxB,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,kBAAkB;AAC7B,MAAI,KAAK,aAAa,QACpB,OAAM,KAAK,cAAc,KAAK,aAAa,UAAU;AAEvD,MAAI,KAAK,aAAa,UAAU,KAAK,aAAa,OAAO,SAAS,EAChE,OAAM,KAAK,aAAa,KAAK,aAAa,OAAO,KAAK,KAAK,GAAG;;AAIlE,QAAO,MAAM,KAAK,KAAK,GAAG;;;;;AC5G5B,SAAS,SAAS,UAA8B,qBAA6B;CAC3E,MAAM,WAAW,SAAS,YAAY,QACnC,MAAM,EAAE,cAAc,oBACxB;CAED,MAAM,WAAW,gBAAgB,SAAS;CAE1C,MAAM,cAAc,QAClB,SAAS,QAAQ,MAAM,EAAE,aAAa,IAAI;CAE5C,MAAM,qBAAqB,WAAW,UAAU;CAChD,MAAM,oBAAoB,WAAW,SAAS;CAC9C,MAAM,oBAAoB,WAAW,UAAU;CAC/C,MAAM,iBAAiB,SAAS,QAC7B,MACC,EAAE,QAAQ,aAAa,CAAC,SAAS,MAAM,IACtC,EAAE,aAAa,WAAW,EAAE,QAAQ,aAAa,CAAC,SAAS,SAAS,CACxE;CAED,MAAM,aACJ,QAAQ,SAAS,MAAM,WAAW,IAAI,mBAAmB,SAAS;CAEpE,MAAM,sBACJ,QAAQ,SAAS,aAAa,QAAQ,KACrC,SAAS,aAAa,QAAQ,UAAU,KAAK;CAEhD,MAAM,eACJ,kBAAkB,SAAS,KAAK,kBAAkB,SAAS;AAE7D,QAAO;EACL;EACA,YAAY,SAAS,MAAM,cAAc;EACzC;EACA;EACA;EACA;EACA,cAAc,SAAS;EACvB;EACA;EACA;EACD;;AAOH,SAAgB,eACd,UACA,qBACQ;CACR,MAAM,OAAO,SAAS,UAAU,oBAAoB;CACpD,MAAM,QAAkB,EAAE;AAE1B,OAAM,KAAK,cAAc;AAGzB,KAAI,KAAK,SAAS,SAAS,GAAG;AAC5B,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,cAAc;AACzB,OAAK,MAAM,OAAO,KAAK,SACrB,OAAM,KAAK,OAAO,IAAI,WAAW,QAAQ,IAAI,QAAQ,IAAI;;AAK7D,KAAI,KAAK,YAAY;AACnB,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,aAAa;AACxB,MAAI,KAAK,WAAY,OAAM,KAAK,kBAAkB,KAAK,aAAa;AACpE,OAAK,MAAM,SAAS,KAAK,mBACvB,OAAM,KAAK,KAAK,MAAM,UAAU;;AAKpC,KAAI,KAAK,qBAAqB;AAC5B,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,uBAAuB;AAClC,MAAI,KAAK,aAAa,QACpB,OAAM,KAAK,mBAAmB,KAAK,aAAa,UAAU;AAE5D,MAAI,KAAK,aAAa,UAAU,KAAK,aAAa,OAAO,SAAS,EAChE,OAAM,KAAK,aAAa,KAAK,aAAa,OAAO,KAAK,KAAK,GAAG;;AAKlE,KAAI,KAAK,cAAc;AACrB,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,gBAAgB;AAC3B,OAAK,MAAM,SAAS,KAAK,kBACvB,OAAM,KAAK,iBAAiB,MAAM,UAAU;AAE9C,OAAK,MAAM,SAAS,KAAK,kBACvB,OAAM,KAAK,kBAAkB,MAAM,UAAU;;AAKjD,KAAI,KAAK,eAAe,SAAS,GAAG;AAClC,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,kBAAkB;AAC7B,OAAK,MAAM,SAAS,KAAK,eACvB,OAAM,KAAK,KAAK,MAAM,UAAU;;AAKpC,OAAM,KAAK,GAAG;AACd,OAAM,KAAK,gBAAgB;AAC3B,OAAM,KAAK,qEAAqE;AAEhF,QAAO,MAAM,KAAK,KAAK,GAAG;;;;;;;;;;;;;AC5G5B,SAAgB,gBACd,UACA,qBACQ;CACR,MAAM,cAAc,KACjB,KAAK;EACJ,aAAa;EACb,OAAO;EACP,aAAa;EACd,CAAC,CACD,SAAS;CAEZ,MAAM,YAAY,sBAAsB,UAAU,oBAAoB;AAEtE,QAAO,UAAU,SAAS,KAAK,UAAU,UAAU,SAAS,OAAO,GACjE,WAAU,KAAK;AAIjB,QAAO,QAAQ,YAAY,WAFd,UAAU,KAAK,KAAK,CAEU;;;;;;;;;;;;ACrB7C,SAAgB,gBACd,UACA,qBACQ;CACR,MAAM,QAAkB,EAAE;AAE1B,OAAM,KAAK,iEAAiE;AAC5E,OAAM,KAAK,GAAG;AACd,OAAM,KAAK,yBAAyB;AACpC,OAAM,KAAK,GAAG;CAEd,MAAM,YAAY,sBAAsB,UAAU,oBAAoB;AAEtE,QAAO,UAAU,SAAS,KAAK,UAAU,UAAU,SAAS,OAAO,GACjE,WAAU,KAAK;AAEjB,OAAM,KAAK,GAAG,UAAU;AAExB,QAAO,MAAM,KAAK,KAAK,GAAG;;;;;;;;;;;;;AChB5B,SAAgB,cACd,UACA,qBACQ;CAKR,MAAM,cAAc,KACjB,KAAK;EACJ,MAAM;EACN,aANF;EAOC,CAAC,CACD,SAAS;CAEZ,MAAM,YAAY,sBAAsB,UAAU,oBAAoB;AAEtE,QAAO,UAAU,SAAS,KAAK,UAAU,UAAU,SAAS,OAAO,GACjE,WAAU,KAAK;AAIjB,QAAO,QAAQ,YAAY,WAFd,UAAU,KAAK,KAAK,CAEU;;;;;;;;;;;;ACvB7C,SAAgB,iBACd,UACA,qBACQ;CACR,MAAM,cAAc,KACjB,KAAK;EACJ,aAAa;EACb,SAAS;GAAC;GAAU;GAAW;GAAW;EAC3C,CAAC,CACD,SAAS;CAEZ,MAAM,YAAY,sBAAsB,UAAU,oBAAoB;AAEtE,QAAO,UAAU,SAAS,KAAK,UAAU,UAAU,SAAS,OAAO,GACjE,WAAU,KAAK;AAIjB,QAAO,QAAQ,YAAY,WAFd,UAAU,KAAK,KAAK,CAEU;;;;;;;;;;;;ACnB7C,SAAgB,cACd,UACA,qBACQ;CACR,MAAM,QAAkB,EAAE;AAE1B,OAAM,KAAK,qCAAqC;AAChD,OAAM,KAAK,GAAG;CAEd,MAAM,YAAY,sBAAsB,UAAU,oBAAoB;AAEtE,QAAO,UAAU,SAAS,KAAK,UAAU,UAAU,SAAS,OAAO,GACjE,WAAU,KAAK;AAEjB,OAAM,KAAK,GAAG,UAAU;AAExB,QAAO,MAAM,KAAK,KAAK,GAAG;;;;;ACQ5B,MAAa,qBAA+D;CAC1E,QAAQ;EACN,QAAQ;EACR,UAAU;EACV,UAAU;EACX;CACD,QAAQ;EACN,QAAQ;EACR,UAAU;EACV,UAAU;EACX;CACD,QAAQ;EACN,QAAQ;EACR,UAAU,KAAK,KAAK,WAAW,SAAS,iBAAiB;EACzD,UAAU;EACX;CACD,SAAS;EACP,QAAQ;EACR,UAAU,KAAK,KAAK,WAAW,0BAA0B;EACzD,UAAU;EACX;CACD,QAAQ;EACN,QAAQ;EACR,UAAU,KAAK,KAAK,WAAW,cAAc,WAAW;EACxD,UAAU;EACX;CACD,UAAU;EACR,QAAQ;EACR,UAAU,KAAK,KAAK,aAAa,SAAS,gBAAgB;EAC1D,UAAU;EACX;CACD,OAAO;EACL,QAAQ;EACR,UAAU,KAAK,KAAK,UAAU,gBAAgB;EAC9C,UAAU;EACX;CACF;;;;;;;AAYD,eAAsB,KACpB,UACA,SACqB;CACrB,MAAM,YAAY,QAAQ,uBAAuB;CACjD,MAAM,UAA0B,QAAQ,WAAW,CAAC,UAAU,SAAS;CAGvE,MAAM,WAAmC,EAAE;AAC3C,MAAK,MAAM,UAAU,QAEnB,UAAS,UADK,mBAAmB,QACR,OAAO,UAAU,UAAU;CAItD,MAAM,WAAW,SAAS,aAAa;CACvC,MAAM,WAAW,SAAS,aAAa;AAEvC,KAAI,QAAQ,OACV,QAAO;EAAE;EAAU;EAAU;EAAU,cAAc,EAAE;EAAE;CAI3D,MAAM,eAAyB,EAAE;AACjC,MAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,QAAQ,mBAAmB;EACjC,MAAM,WAAW,KAAK,KAAK,QAAQ,WAAW,MAAM,SAAS;AAE7D,QAAM,MAAM,KAAK,QAAQ,SAAS,EAAE,EAAE,WAAW,MAAM,CAAC;AACxD,MAAI,MAAM,aAAa,SACrB,OAAM,UAAU,UAAU,SAAS,SAAU,QAAQ;MAErD,OAAM,iBAAiB,UAAU,SAAS,QAAS;AAGrD,eAAa,KAAK,SAAS;;AAG7B,QAAO;EAAE;EAAU;EAAU;EAAU;EAAc"}
|