final gaps: .gitattributes, .dockerignore, structured logger, clean stray files

This commit is contained in:
artale 2026-06-13 13:35:40 +02:00
parent be7354ee38
commit e92eaeaefc
4 changed files with 74 additions and 42 deletions

13
.dockerignore Normal file
View File

@ -0,0 +1,13 @@
node_modules/
.git/
.gitignore
.gitattributes
*.md
src/
tsconfig.json
vitest.config.ts
.env
.env.local
LOGS/
TEMP/
.familiar-test

2
.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
# Auto-detect text files and normalize to LF
* text=auto

View File

@ -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/`

59
src/core/logger.ts Normal file
View File

@ -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<LogLevel, string> = {
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<string, unknown>): 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<string, unknown>): void { this.write("info", message, data); }
warn(message: string, data?: Record<string, unknown>): void { this.write("warn", message, data); }
error(message: string, data?: Record<string, unknown>): void { this.write("error", message, data); }
debug(message: string, data?: Record<string, unknown>): void { this.write("debug", message, data); }
}
export const logger = new Logger();