From e92eaeaefcf0b07de98d914784e2e905cd9e9b03 Mon Sep 17 00:00:00 2001 From: artale Date: Sat, 13 Jun 2026 13:35:40 +0200 Subject: [PATCH] final gaps: .gitattributes, .dockerignore, structured logger, clean stray files --- .dockerignore | 13 ++++++++++ .gitattributes | 2 ++ CLAUDE.md | 42 --------------------------------- src/core/logger.ts | 59 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 42 deletions(-) create mode 100644 .dockerignore create mode 100644 .gitattributes delete mode 100644 CLAUDE.md create mode 100644 src/core/logger.ts diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..3817d40 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,13 @@ +node_modules/ +.git/ +.gitignore +.gitattributes +*.md +src/ +tsconfig.json +vitest.config.ts +.env +.env.local +LOGS/ +TEMP/ +.familiar-test diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..a955e55 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto-detect text files and normalize to LF +* text=auto diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 1be5cb3..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,42 +0,0 @@ -# fable-agent — Project Instructions - -## Project -TypeScript self-improving agent system implementing the Fable 5 compound stack. All model calls route through the local proxy at `localhost:18901`. - -## Build / Test -- `npm run build` -- `node dist/index.js demo` -- `npx tsc --noEmit` - -## Model Routing - -All model calls route through `http://localhost:18901/v1`. - -### Working verified routes -| Alias | Provider | Notes | -|-------|----------|-------| -| `go-dsv4-flash` | Go API | Primary cheap/fast route | -| `claude-sonnet-4-6` | Zen API | Code review, bounded subtasks | -| `fi-gemini` | Free provider | Gemini free tier | -| `fi-mistral` | Free provider | Mistral free tier | -| `nemotron-3-ultra-free` | Free provider | Nvidia free tier | - -### Provider groups -- **Go API**: `go-*` models (12 models, $10/mo flat) -- **Zen API**: `claude-*`, `gpt-*`, `gemini-*`, etc. -- **CrofAI**: fallback provider on 429 -- **Free providers (`fi-*`)**: free-tier models - -The proxy currently exposes 58 model aliases across these providers. The model router tracks 35 models with real pricing. - -## Proxy Configuration -- Proxy config: `~/.config/grok-proxy.cjs` -- Provider keys: `~/.config/infer/keys.env`, `~/.config/fi/keys.env` -- Restart proxy after config changes. - -## Project Structure -- CLI: `dist/index.js` -- Source: `src/` -- State: `~/.fable-agent/` -- Skills: `SKILLS/` -- Phases: `PHASES/` diff --git a/src/core/logger.ts b/src/core/logger.ts new file mode 100644 index 0000000..dfed690 --- /dev/null +++ b/src/core/logger.ts @@ -0,0 +1,59 @@ +/** + * Structured logger — replaces console.log across the system. + * Writes JSONL logs for machine parsing + human-readable terminal output. + */ + +import * as fs from "node:fs"; +import * as path from "node:path"; + +export type LogLevel = "debug" | "info" | "warn" | "error"; + +const LOG_DIR = path.join( + process.env.FABLE_DATA_DIR || process.env.HOME || process.env.USERPROFILE || ".", ".fable-agent", "logs" +); + +const COLORS: Record = { + debug: "\x1b[90m", // gray + info: "\x1b[36m", // cyan + warn: "\x1b[33m", // yellow + error: "\x1b[31m", // red +}; +const RESET = "\x1b[0m"; + +class Logger { + private logFile: string; + + constructor() { + try { + if (!fs.existsSync(LOG_DIR)) fs.mkdirSync(LOG_DIR, { recursive: true }); + } catch {} + this.logFile = path.join(LOG_DIR, `${new Date().toISOString().slice(0, 10)}.jsonl`); + } + + private write(level: LogLevel, message: string, data?: Record): void { + const entry = { + timestamp: new Date().toISOString(), + level, + message, + ...data, + }; + + // JSONL for machine parsing + try { + fs.appendFileSync(this.logFile, JSON.stringify(entry) + "\n", "utf-8"); + } catch {} + + // Colored terminal output + const prefix = level === "error" ? "✗" : level === "warn" ? "⚠" : level === "debug" ? "·" : "✓"; + if (level !== "debug" || process.env.FABLE_DEBUG) { + process.stdout.write(`${COLORS[level]}${prefix}${RESET} ${message}\n`); + } + } + + info(message: string, data?: Record): void { this.write("info", message, data); } + warn(message: string, data?: Record): void { this.write("warn", message, data); } + error(message: string, data?: Record): void { this.write("error", message, data); } + debug(message: string, data?: Record): void { this.write("debug", message, data); } +} + +export const logger = new Logger();