diff --git a/.env.example b/.env.example
index e0a1ee0..76d8fea 100644
--- a/.env.example
+++ b/.env.example
@@ -60,7 +60,7 @@ FORENSIC_AUDIT_MAX_ENTRIES=50
# === AI SEMANTIC SEARCH ===
# Enable semantic search (highly recommended for better results)
-AI_EMBEDDINGS_ENABLED=true
+REMOVE_AI_EMBEDDINGS_ENABLED=true
AI_EMBEDDINGS_ENDPOINT=https://api.mistral.ai/v1/embeddings
AI_EMBEDDINGS_API_KEY=your-embeddings-api-key-here
AI_EMBEDDINGS_MODEL=mistral-embed
@@ -122,8 +122,8 @@ AI_EMBEDDINGS_BATCH_SIZE=10
AI_EMBEDDINGS_BATCH_DELAY_MS=1000
# === Context Management ===
-AI_MAX_CONTEXT_TOKENS=4000
-AI_MAX_PROMPT_TOKENS=2500
+REMOVE_AI_MAX_CONTEXT_TOKENS=4000
+REMOVE_AI_MAX_PROMPT_TOKENS=2500
# === Confidence Scoring ===
CONFIDENCE_SEMANTIC_WEIGHT=0.5
diff --git a/find-duplicates.mjs b/find-duplicates.mjs
new file mode 100644
index 0000000..00dfa39
--- /dev/null
+++ b/find-duplicates.mjs
@@ -0,0 +1,333 @@
+#!/usr/bin/env node
+// find-duplicate-functions.mjs
+// Usage:
+// node find-duplicate-functions.mjs [rootDir] [--mode exact|struct] [--min-lines N] [--json]
+// Example:
+// node find-duplicate-functions.mjs . --mode struct --min-lines 3
+
+import fs from "fs";
+import path from "path";
+import * as url from "url";
+import ts from "typescript";
+
+const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
+
+/** -------- CLI OPTIONS -------- */
+const args = process.argv.slice(2);
+let rootDir = ".";
+let mode = "struct"; // "exact" | "struct"
+let minLines = 3;
+let outputJson = false;
+
+for (let i = 0; i < args.length; i++) {
+ const a = args[i];
+ if (!a.startsWith("--") && rootDir === ".") {
+ rootDir = a;
+ } else if (a === "--mode") {
+ mode = (args[++i] || "struct").toLowerCase();
+ if (!["exact", "struct"].includes(mode)) {
+ console.error("Invalid --mode. Use 'exact' or 'struct'.");
+ process.exit(1);
+ }
+ } else if (a === "--min-lines") {
+ minLines = parseInt(args[++i] || "3", 10);
+ } else if (a === "--json") {
+ outputJson = true;
+ }
+}
+
+/** -------- FILE DISCOVERY -------- */
+const DEFAULT_IGNORES = new Set([
+ "node_modules",
+ ".git",
+ ".next",
+ ".vercel",
+ "dist",
+ "build",
+ ".astro", // Astro's generated cache dir
+]);
+
+const VALID_EXTS = new Set([".ts", ".tsx", ".astro", ".mts", ".cts"]);
+
+function walk(dir) {
+ /** @type {string[]} */
+ const out = [];
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
+ for (const e of entries) {
+ const p = path.join(dir, e.name);
+ if (e.isDirectory()) {
+ if (DEFAULT_IGNORES.has(e.name)) continue;
+ out.push(...walk(p));
+ } else if (e.isFile() && VALID_EXTS.has(path.extname(e.name))) {
+ out.push(p);
+ }
+ }
+ return out;
+}
+
+/** -------- ASTRO CODE EXTRACTION --------
+ * Extract TS/JS code from:
+ * - frontmatter: --- ... ---
+ * -
+ */
+function extractCodeFromAstro(source) {
+ /** @type {{code:string, offset:number}[]} */
+ const blocks = [];
+
+ // Frontmatter (must be at top in Astro)
+ // Match the FIRST pair of --- ... ---
+ const fm = source.startsWith("---")
+ ? (() => {
+ const end = source.indexOf("\n---", 3);
+ if (end !== -1) {
+ const front = source.slice(3, end + 1); // include trailing \n
+ return { start: 0, end: end + 4, code: front };
+ }
+ return null;
+ })()
+ : null;
+ if (fm) {
+ // offset for line numbers is after the first '---\n'
+ blocks.push({ code: fm.code, offset: 4 }); // rough; we’ll fix line numbers via positions later
+ }
+
+ //
+ const scriptRe = /
\ No newline at end of file
diff --git a/src/components/ContributionButton.astro b/src/components/ContributionButton.astro
index 5434ff6..1b1a610 100644
--- a/src/components/ContributionButton.astro
+++ b/src/components/ContributionButton.astro
@@ -1,5 +1,5 @@
---
-// src/components/ContributionButton.astro - CLEANED: Removed duplicate auth script
+// src/components/ContributionButton.astro
export interface Props {
type: 'edit' | 'new' | 'write';
toolName?: string;
diff --git a/src/components/ShareButton.astro b/src/components/ShareButton.astro
index ef041f4..47aca10 100644
--- a/src/components/ShareButton.astro
+++ b/src/components/ShareButton.astro
@@ -1,5 +1,5 @@
---
-import { createToolSlug } from '../utils/toolHelpers.js';
+import { createToolSlug } from '../utils/clientUtils.js';
export interface Props {
toolName: string;
diff --git a/src/components/TargetedScenarios.astro b/src/components/TargetedScenarios.astro
index 2a4eb4f..8db7a33 100644
--- a/src/components/TargetedScenarios.astro
+++ b/src/components/TargetedScenarios.astro
@@ -4,7 +4,6 @@ import { getToolsData } from '../utils/dataService.js';
const data = await getToolsData();
const scenarios = data.scenarios || [];
-// Configuration
const maxDisplayed = 9;
const displayedScenarios = scenarios.slice(0, maxDisplayed);
---
diff --git a/src/components/ToolFilters.astro b/src/components/ToolFilters.astro
index c8ba300..502ae3f 100644
--- a/src/components/ToolFilters.astro
+++ b/src/components/ToolFilters.astro
@@ -306,8 +306,7 @@ const sortedTags = Object.entries(tagFrequency)