#!/usr/bin/env node import { spawn } from "child_process" import { existsSync, mkdirSync, writeFileSync, readFileSync } from "fs" import { join, dirname } from "path" import { homedir } from "os" import * as readline from "readline" import { fileURLToPath } from "url" const __filename = fileURLToPath(import.meta.url) const __dirname = dirname(__filename) const TFCODE_DIR = join(homedir(), ".tfcode") const TOOLS_FILE = join(TFCODE_DIR, "tools.json") const CREDENTIALS_FILE = join(TFCODE_DIR, "credentials.json") const CONFIG_FILE = join(TFCODE_DIR, "config.json") const COLORS = { reset: "\x1b[0m", bold: "\x1b[1m", green: "\x1b[32m", red: "\x1b[31m", cyan: "\x1b[36m", dim: "\x1b[90m", yellow: "\x1b[33m", magenta: "\x1b[35m", } function log(msg) { console.log(msg) } function success(msg) { console.log(`${COLORS.green}✓${COLORS.reset} ${msg}`) } function error(msg) { console.error(`${COLORS.red}✗${COLORS.reset} ${msg}`) } function info(msg) { console.log(`${COLORS.cyan}ℹ${COLORS.reset} ${msg}`) } function ensureConfigDir() { if (!existsSync(TFCODE_DIR)) mkdirSync(TFCODE_DIR, { recursive: true }) } function loadConfig() { const envConfig = { workspace_id: process.env.TF_WORKSPACE_ID, api_key: process.env.TF_API_KEY, region: process.env.TF_REGION, } if (envConfig.workspace_id && envConfig.api_key) return envConfig if (existsSync(CONFIG_FILE)) { try { return JSON.parse(readFileSync(CONFIG_FILE, "utf-8")) } catch {} } return null } function saveConfig(config) { ensureConfigDir() writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2)) } function runPythonSync(method, config = null) { const wsId = config?.workspace_id || process.env.TF_WORKSPACE_ID || "" const apiKey = config?.api_key || process.env.TF_API_KEY || "" const region = config?.region || process.env.TF_REGION || "au" const pythonCode = ` import json, sys, os try: os.environ["TF_WORKSPACE_ID"] = "${wsId}" os.environ["TF_API_KEY"] = "${apiKey}" os.environ["TF_REGION"] = "${region}" from tf_sync.config import load_config, validate_credentials, Region from tf_sync.tools import sync_tools from tf_sync.config import get_region_urls method = "${method}" if method == "validate": config = load_config() result = validate_credentials(config) urls = get_region_urls(config.region) print(json.dumps({"success": result.success, "workspace_id": result.workspace_id, "workspace_name": result.workspace_name, "error": result.error, "base_url": urls["base_url"]})) elif method == "sync": config = load_config() result = sync_tools(config) tools_data = [{"id": t.id, "name": t.name, "description": t.description, "tool_type": t.tool_type.value, "request_type": t.request_type.value if t.request_type else None, "url": t.url, "auth_via": t.auth_via, "interpolation_string": t.interpolation_string, "goals": t.goals, "temperature": t.temperature, "max_tokens": t.max_tokens, "llm_base_model": t.llm_base_model, "llm_provider": t.llm_provider} for t in result.tools] print(json.dumps({"success": result.success, "tools": tools_data, "by_type": result.by_type, "error": result.error})) except Exception as e: print(json.dumps({"success": False, "error": str(e)})) ` return new Promise((resolve, reject) => { const proc = spawn(process.env.TFCODE_PYTHON_PATH || "python3", ["-c", pythonCode], { env: { ...process.env } }) let stdout = "", stderr = "" proc.stdout.on("data", (d) => (stdout += d)) proc.stderr.on("data", (d) => (stderr += d)) proc.on("close", (code) => { if (code !== 0 && !stdout) reject(new Error(`Python failed: ${stderr}`)) else try { resolve(JSON.parse(stdout.trim())) } catch (e) { reject(new Error(`Parse error: ${stdout}`)) } }) proc.on("error", reject) }) } function loadCachedTools() { if (!existsSync(TOOLS_FILE)) return null try { return JSON.parse(readFileSync(TOOLS_FILE, "utf-8")) } catch { return null } } function saveToolsCache(tools) { ensureConfigDir() writeFileSync(TOOLS_FILE, JSON.stringify(tools, null, 2)) } async function question(prompt) { return new Promise((resolve) => { const rl = readline.createInterface({ input: process.stdin, output: process.stdout }) rl.question(prompt, (answer) => { rl.close() resolve(answer.trim()) }) }) } async function select(prompt, options) { log("") log(prompt) log("") options.forEach((opt, i) => log(` ${COLORS.cyan}${i + 1}.${COLORS.reset} ${opt}`)) log("") const answer = await question("Select (1-" + options.length + "): ") const idx = parseInt(answer) - 1 return idx >= 0 && idx < options.length ? idx : 0 } async function interactiveSetup() { log("") log(`${COLORS.bold}${COLORS.magenta}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.reset}`) log(`${COLORS.bold}${COLORS.magenta} tfcode Setup${COLORS.reset}`) log(`${COLORS.bold}${COLORS.magenta}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.reset}`) log("") log("This will guide you through setting up your ToothFairyAI credentials.") log("") log(`${COLORS.dim}You can find your credentials at:${COLORS.reset}`) log(`${COLORS.dim} https://app.toothfairyai.com → Settings → API Keys${COLORS.reset}`) log("") log(`${COLORS.bold}Step 1: Workspace ID${COLORS.reset}`) log(`${COLORS.dim}This is your workspace UUID${COLORS.reset}`) log("") const workspaceId = await question("Enter your Workspace ID: ") if (!workspaceId) { error("Workspace ID is required") process.exit(1) } log("") log(`${COLORS.bold}Step 2: API Key${COLORS.reset}`) log(`${COLORS.dim}Paste or type your API key${COLORS.reset}`) log("") const apiKey = await question("Enter your API Key: ") if (!apiKey) { error("API Key is required") process.exit(1) } log("") log(`${COLORS.bold}Step 3: Region${COLORS.reset}`) const regions = ["dev (Development)", "au (Australia)", "eu (Europe)", "us (United States)"] const regionIdx = await select("Select your region:", regions) const region = ["dev", "au", "eu", "us"][regionIdx] log("") log(`${COLORS.bold}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.reset}`) log("") log(`${COLORS.bold}Summary:${COLORS.reset}`) log(` Workspace ID: ${workspaceId}`) log(` API Key: ***${apiKey.slice(-4)}`) log(` Region: ${region}`) log("") const confirm = await question("Save these credentials? (Y/n): ") if (confirm.toLowerCase() === "n" || confirm.toLowerCase() === "no") { log("Setup cancelled.") return } const config = { workspace_id: workspaceId, api_key: apiKey, region } saveConfig(config) success("Credentials saved to ~/.tfcode/config.json") log("") const testNow = await question("Validate credentials now? (Y/n): ") if (testNow.toLowerCase() === "n" || testNow.toLowerCase() === "no") return log("") info("Validating credentials...") log("") try { const result = await runPythonSync("validate", config) if (result.success) { success("Credentials valid!") log(` API URL: ${result.base_url}`) log(` Workspace ID: ${result.workspace_id}`) log("") const syncNow = await question("Sync tools now? (Y/n): ") if (syncNow.toLowerCase() === "n" || syncNow.toLowerCase() === "no") return log("") info("Syncing tools...") log("") const syncResult = await runPythonSync("sync", config) if (syncResult.success) { saveToolsCache(syncResult) success(`Synced ${syncResult.tools.length} tools`) if (syncResult.by_type && Object.keys(syncResult.by_type).length > 0) { log("") log("By type:") for (const [type, count] of Object.entries(syncResult.by_type)) log(` ${type}: ${count}`) } } else { error(`Sync failed: ${syncResult.error}`) } } else { error(`Validation failed: ${result.error}`) } } catch (e) { error(`Failed: ${e.message}`) } log("") log(`${COLORS.bold}${COLORS.green}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.reset}`) log(`${COLORS.bold}${COLORS.green} Setup Complete!${COLORS.reset}`) log(`${COLORS.bold}${COLORS.green}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.reset}`) log("") log("Commands:") log(` ${COLORS.cyan}tfcode validate${COLORS.reset} Check credentials`) log(` ${COLORS.cyan}tfcode sync${COLORS.reset} Sync tools`) log(` ${COLORS.cyan}tfcode tools list${COLORS.reset} List tools`) log("") } function showHelp() { log("") log(`${COLORS.bold}tfcode${COLORS.reset} - ToothFairyAI's AI coding agent`) log("") log("Commands:") log(` ${COLORS.cyan}tfcode setup${COLORS.reset} Interactive credential setup`) log(` ${COLORS.cyan}tfcode validate${COLORS.reset} Test credentials`) log(` ${COLORS.cyan}tfcode sync${COLORS.reset} Sync tools from workspace`) log(` ${COLORS.cyan}tfcode tools list${COLORS.reset} List synced tools`) log(` ${COLORS.cyan}tfcode test-agent ${COLORS.reset} Test agent prompt injection`) log(` ${COLORS.cyan}tfcode debug${COLORS.reset} Show debug info`) log(` ${COLORS.cyan}tfcode --help${COLORS.reset} Show this help`) log(` ${COLORS.cyan}tfcode --version${COLORS.reset} Show version`) log("") log(`${COLORS.dim}For full TUI, run from source:${COLORS.reset}`) log(`${COLORS.dim} bun run packages/tfcode/src/index.ts${COLORS.reset}`) log("") } function showDebugInfo() { log("") log(`${COLORS.bold}Debug Information${COLORS.reset}`) log("") log(` ${COLORS.bold}TFCODE_DIR:${COLORS.reset} ${TFCODE_DIR}`) log(` ${COLORS.bold}TOOLS_FILE:${COLORS.reset} ${TOOLS_FILE}`) log(` ${COLORS.bold}CONFIG_FILE:${COLORS.reset} ${CONFIG_FILE}`) log(` ${COLORS.bold}CREDENTIALS_FILE:${COLORS.reset} ${CREDENTIALS_FILE}`) log("") log(` ${COLORS.bold}tools.json exists:${COLORS.reset} ${existsSync(TOOLS_FILE)}`) if (existsSync(TOOLS_FILE)) { const tools = loadCachedTools() log(` ${COLORS.bold}tools.json valid:${COLORS.reset} ${tools?.success ?? false}`) log(` ${COLORS.bold}tools count:${COLORS.reset} ${tools?.tools?.length ?? 0}`) const coderAgents = tools?.tools?.filter((t) => t.tool_type === "coder_agent") ?? [] log(` ${COLORS.bold}coder_agent count:${COLORS.reset} ${coderAgents.length}`) if (coderAgents.length > 0) { log("") log(` ${COLORS.bold}Coder Agents:${COLORS.reset}`) coderAgents.forEach((a) => { log(` - ${a.name} (id: ${a.id})`) log(` interpolation_string: ${a.interpolation_string ? "YES" : "NO"}`) log(` goals: ${a.goals ? "YES" : "NO"}`) log(` llm_provider: ${a.llm_provider ?? "(null)"}`) log(` llm_base_model: ${a.llm_base_model ?? "(null)"}`) }) } } log("") } function testAgentPrompt(agentId) { const tools = loadCachedTools() if (!tools?.success) { error("No tools. Run: tfcode sync") process.exit(1) } const agent = tools.tools.find((t) => t.id === agentId || t.name === agentId) if (!agent) { error(`Agent not found: ${agentId}`) log("") log("Available coder agents:") tools.tools .filter((t) => t.tool_type === "coder_agent") .forEach((t) => { log(` ${COLORS.cyan}${t.id}${COLORS.reset} - ${t.name}`) }) process.exit(1) } log("") log(`${COLORS.bold}${COLORS.magenta}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.reset}`) log(`${COLORS.bold}${COLORS.magenta} Agent Data from tools.json${COLORS.reset}`) log(`${COLORS.bold}${COLORS.magenta}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.reset}`) log("") log(` ${COLORS.bold}id:${COLORS.reset} ${agent.id}`) log(` ${COLORS.bold}name:${COLORS.reset} ${agent.name}`) log(` ${COLORS.bold}description:${COLORS.reset} ${agent.description || "(none)"}`) log(` ${COLORS.bold}tool_type:${COLORS.reset} ${agent.tool_type}`) log(` ${COLORS.bold}auth_via:${COLORS.reset} ${agent.auth_via}`) log("") log(` ${COLORS.bold}interpolation_string:${COLORS.reset}`) if (agent.interpolation_string) { log(` ${agent.interpolation_string.substring(0, 200)}${agent.interpolation_string.length > 200 ? "..." : ""}`) } else { log(` ${COLORS.dim}(none)${COLORS.reset}`) } log("") log(` ${COLORS.bold}goals:${COLORS.reset}`) if (agent.goals) { log(` ${agent.goals.substring(0, 200)}${agent.goals.length > 200 ? "..." : ""}`) } else { log(` ${COLORS.dim}(none)${COLORS.reset}`) } log("") log(` ${COLORS.bold}temperature:${COLORS.reset} ${agent.temperature ?? "(none)"}`) log(` ${COLORS.bold}max_tokens:${COLORS.reset} ${agent.max_tokens ?? "(none)"}`) log(` ${COLORS.bold}llm_base_model:${COLORS.reset} ${agent.llm_base_model ?? "(none)"}`) log(` ${COLORS.bold}llm_provider:${COLORS.reset} ${agent.llm_provider ?? "(none)"}`) log("") // Build highlighted instructions const isTFProvider = !agent.llm_provider || agent.llm_provider === "toothfairyai" || agent.llm_provider === "tf" const hasPrompt = agent.interpolation_string && agent.interpolation_string.trim().length > 0 const hasGoals = agent.goals && agent.goals.trim().length > 0 log(`${COLORS.bold}${COLORS.magenta}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.reset}`) log(`${COLORS.bold}${COLORS.magenta} Model Mapping${COLORS.reset}`) log(`${COLORS.bold}${COLORS.magenta}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.reset}`) log("") log(` ${COLORS.bold}isTFProvider:${COLORS.reset} ${isTFProvider}`) log( ` ${COLORS.bold}mapped model:${COLORS.reset} ${isTFProvider && agent.llm_base_model ? `toothfairyai/${agent.llm_base_model}` : "(no mapping)"}`, ) log("") log(`${COLORS.bold}${COLORS.magenta}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.reset}`) log(`${COLORS.bold}${COLORS.magenta} Highlighted Instructions Preview${COLORS.reset}`) log(`${COLORS.bold}${COLORS.magenta}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.reset}`) log("") if (!hasPrompt && !hasGoals) { log( ` ${COLORS.dim}(No interpolation_string or goals - no highlighted instructions will be generated)${COLORS.reset}`, ) } else { log("") log("═══════════════════════════════════════════════════════════════════════════════") log("⚠️ ULTRA IMPORTANT - AGENT CONFIGURATION ⚠️") log("═══════════════════════════════════════════════════════════════════════════════") log("") log(`You are acting as the agent: "${agent.name}"`) if (agent.description) { log(`Description: ${agent.description}`) } log("") log("The following instructions and goals are MANDATORY and MUST be followed") log("with the HIGHEST PRIORITY. These override any conflicting default behaviors.") log("═══════════════════════════════════════════════════════════════════════════════") if (hasPrompt) { log("") log("┌─────────────────────────────────────────────────────────────────────────────┐") log(`│ 🎯 AGENT "${agent.name}" INSTRUCTIONS (CRITICAL - MUST FOLLOW) │`) log("└─────────────────────────────────────────────────────────────────────────────┘") log("") log(agent.interpolation_string) } if (hasGoals) { log("") log("┌─────────────────────────────────────────────────────────────────────────────┐") log(`│ 🎯 AGENT "${agent.name}" GOALS (CRITICAL - MUST ACHIEVE) │`) log("└─────────────────────────────────────────────────────────────────────────────┘") log("") log(agent.goals) } log("") log("═══════════════════════════════════════════════════════════════════════════════") log(`⚠️ END OF ULTRA IMPORTANT AGENT "${agent.name}" CONFIGURATION ⚠️`) log("═══════════════════════════════════════════════════════════════════════════════") } log("") } const args = process.argv.slice(2) const command = args[0] if (args.includes("--help") || args.includes("-h")) { showHelp() } else if (args.includes("--version") || args.includes("-v")) { log("tfcode v1.0.0-beta.9") } else if (command === "setup") { interactiveSetup() } else if (command === "validate") { ;(async () => { const config = loadConfig() if (!config) { error("No credentials. Run: tfcode setup") process.exit(1) } info("Validating...") try { const result = await runPythonSync("validate", config) if (result.success) { success("Credentials valid") log(` API URL: ${result.base_url}`) } else { error(`Failed: ${result.error}`) process.exit(1) } } catch (e) { error(`Failed: ${e.message}`) process.exit(1) } })() } else if (command === "sync") { ;(async () => { const config = loadConfig() if (!config) { error("No credentials. Run: tfcode setup") process.exit(1) } info("Syncing tools...") try { const result = await runPythonSync("sync", config) if (result.success) { saveToolsCache(result) success(`Synced ${result.tools.length} tools`) if (result.by_type) { log("") log("By type:") for (const [t, c] of Object.entries(result.by_type)) log(` ${t}: ${c}`) } } else { error(`Failed: ${result.error}`) process.exit(1) } } catch (e) { error(`Failed: ${e.message}`) process.exit(1) } })() } else if (command === "tools" && args[1] === "list") { const cached = loadCachedTools() if (!cached?.success) { error("No tools. Run: tfcode sync") process.exit(1) } let tools = cached.tools if (args[2] === "--type" && args[3]) tools = tools.filter((t) => t.tool_type === args[3]) log(`\n${tools.length} tool(s):\n`) for (const t of tools) { log(` ${COLORS.cyan}${t.name}${COLORS.reset}`) log(` Type: ${t.tool_type}`) if (t.description) log(` ${COLORS.dim}${t.description.slice(0, 60)}${COLORS.reset}`) log(` Auth: ${t.auth_via}\n`) } } else if (command === "test-agent") { const agentId = args[1] if (!agentId) { error("Usage: tfcode test-agent ") process.exit(1) } testAgentPrompt(agentId) } else if (command === "debug") { showDebugInfo() } else if (!command) { // Show help instead of trying TUI (TUI requires full build) showHelp() } else { error(`Unknown command: ${command}`) showHelp() process.exit(1) }