{"id":50732495,"url":"https://github.com/avoidwork/madz","last_synced_at":"2026-06-10T10:02:02.183Z","repository":{"id":361953717,"uuid":"1248389007","full_name":"avoidwork/madz","owner":"avoidwork","description":"A personality-driven AI harness channeling Mads Mikkelsen’s cinematic soul.","archived":false,"fork":false,"pushed_at":"2026-06-09T01:12:05.000Z","size":734,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-09T02:23:31.135Z","etag":null,"topics":["ai","assistant","cli","engine","harness","orchestrator","orchestrator-engine"],"latest_commit_sha":null,"homepage":"https://denkerne.ai","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/avoidwork.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-05-24T15:24:10.000Z","updated_at":"2026-06-09T01:10:14.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/avoidwork/madz","commit_stats":null,"previous_names":["avoidwork/madz"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/avoidwork/madz","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/avoidwork%2Fmadz","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/avoidwork%2Fmadz/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/avoidwork%2Fmadz/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/avoidwork%2Fmadz/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/avoidwork","download_url":"https://codeload.github.com/avoidwork/madz/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/avoidwork%2Fmadz/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34146870,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-10T02:00:07.152Z","response_time":89,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","assistant","cli","engine","harness","orchestrator","orchestrator-engine"],"created_at":"2026-06-10T10:02:00.353Z","updated_at":"2026-06-10T10:02:02.174Z","avatar_url":"https://github.com/avoidwork.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Madz\n\n**A personality-driven AI harness channeling Mads Mikkelsen's cinematic soul.** | [denkerne.ai](https://denkerne.ai)\n\n[![License: BSD-3-Clause](https://img.shields.io/badge/License-BSD--3--Clause-blue.svg)](LICENSE)\n[![Node.js \u003e= 24](https://img.shields.io/badge/node-%3E%3D24-brightgreen)](https://nodejs.org)\n[![Tests](https://img.shields.io/badge/tests-passing-brightgreen)](#testing)\n[![Coverage](https://img.shields.io/badge/coverage-93.37%25-brightgreen)](#testing)\n\n`madz` is a Node.js AI harness that combines a terminal-based UI with structured skill execution and a distinctive personality. Drawn from Mads Mikkelsen's most iconic roles, it speaks with calm, precision, and quiet intensity — solving problems with style, remembering your context, safely running your skills, and automating the mundane. Everything is persisted as version-controllable Markdown files, making it easy to audit with `git log` and re-load across sessions. Built on LangGraph, OpenTelemetry, and Ink — with persistent memory, sandboxed skill execution, cron scheduling, and a React-powered TUI.\n\n## Table of Contents\n\n- [Overview](#overview)\n- [Coming soon](#coming-soon)\n- [Quick Start](#quick-start)\n  - [Prerequisites](#prerequisites)\n  - [Installation](#installation)\n  - [Configuration](#configuration)\n  - [Running](#running)\n  - [TUI Navigation](#tui-navigation)\n- [Docker](#docker)\n  - [Building](#building)\n  - [Running](#running)\n  - [SSH Access](#ssh-access)\n  - [Environment Variables](#environment-variables)\n- [Features](#features)\n  - [Onboarding](#onboarding)\n  - [LLM Provider Abstraction](#llm-provider-abstraction)\n  - [Agent](#agent)\n  - [Built-in Tools](#built-in-tools)\n  - [Skills Registry](#skills-registry)\n  - [Permission Gating](#permission-gating)\n  - [Memory System](#memory-system)\n  - [Sandbox RTE](#sandbox-rte)\n  - [Telemetry](#telemetry)\n  - [Cron Scheduler](#cron-scheduler)\n- [Directory Structure](#directory-structure)\n- [Config Reference](#config-reference)\n- [Testing](#testing)\n- [Development](#development)\n  - [Extending Skills](#extending-skills)\n  - [Environment Variables](#environment-variables)\n- [License](#license)\n\n## Overview\n\n- 🧠 **Remembers everything** → Persistent memory across sessions\n- 🎭 **Personality with purpose** → Mads Mikkelsen's cinematic soul — quiet intensity, elegant precision\n- 🛠️ **Runs your custom skills** → Safely execute plugins \u0026 tools in a sandboxed runtime\n- ⏱️ **Automates your routines** → Declare cron jobs in YAML and run on autopilot\n- 💬 **Orchestrates conversations** → Multi-turn LLM chats with context-window management\n\n## Coming soon\n\n- Automatic compaction for longer sessions\n- Faster rendering and snappier interactions\n- Session browsing with interactive menu\n\n### Docker Quick Start (Recommended)\n\n```bash\ndocker pull avoidwork/madz:latest\ndocker run -d \\\n  --name madz \\\n  -p 2222:22 \\\n  -v ./memory:/app/memory \\\n  -v ./skills:/app/skills \\\n  -e OPENAI_API_KEY=\"your-key\" \\\n  avoidwork/madz:latest\nssh -p 2222 madz@localhost\n```\n\nThe full `docker run` command with all optional variables is in the [Docker Environment Variables](#environment-variables) section below.\n\n### Prerequisites\n\n- **Node.js** 24 or later\n- **npm** (included with Node.js)\n- An LLM provider API key (e.g., `OPENAI_API_KEY`)\n\n### Installation\n\n```bash\ngit clone https://github.com/avoidwork/madz.git\ncd madz\nnpm install\n```\n\n### Configuration\n\nCopy `config.yaml` and set your LLM provider credentials. Environment variable references (`${VAR_NAME}`) are resolved at load time.\n\n**Docker** — pass environment variables via `docker run` or provide `config.yaml` via a volume mount. All configurable variables are listed in the [Docker Environment Variables](#environment-variables) section.\n\nFor the full configuration reference with defaults, see the [Config Reference](#config-reference) table at the end of this document.\n\n### Running\n\n**Interactive TUI:**\n\n```bash\nnpm start\n# or\nnode index.js --mode interactive\n```\n\n**Single prompt (CLI mode):**\n\n```bash\nnode index.js \"What's the CPU load?\"\n```\n\n**Batch / pipeline output:**\n\n```bash\nnode index.js \"Summarize memory/_index.md\" --json\n```\n\n### TUI Navigation\n\n| Key       | Action                         |\n|-----------|--------------------------------|\n| `↑/↓`     | Scroll conversation history    |\n| `:help`   | Show available commands        |\n| `:config set \u003ckey\u003e \u003cvalue\u003e` | Mutate config at runtime |\n| `:skill \u003cname\u003e` | Invoke a discovered skill      |\n| `:schedule pause` / `resume` | Control the cron scheduler |\n| `:clear`  | Clear conversation history     |\n| `:new`    | Start a fresh session          |\n\n## Docker\n\n### Building\n\nBuild a single-architecture image:\n\n```bash\nnpm run docker:build\n```\n\nFor multi-architecture builds (requires `docker buildx create --name multiarch --use`):\n\n```bash\nnpm run docker:build:all          # amd64 + arm64\nnpm run docker:build:amd64        # amd64 only\nnpm run docker:build:arm64        # arm64 only\n```\n\n### Running\n\nPull the prebuilt image (or [build locally](#building) for a custom image), then run:\n\n```bash\ndocker pull avoidwork/madz:latest\ndocker run -d \\\n  --name madz \\\n  -p 2222:22 \\\n  -v ./memory:/app/memory \\\n  -v ./skills:/app/skills \\\n  -e OPENAI_API_KEY=\"abc\" \\\n  -e OPENAI_MODEL=Qwen/Qwen3.6-35B-A3B-FP8 \\\n  -e OPENAI_BASE_URL=http://your.inference.lan:8000/v1 \\\n  -e OPENAI_MAX_TOKENS=61440 \\\n  -e SEARXNG_URL=https://your.searxng.lan/search \\\n  avoidwork/madz:latest\n```\n\nThe example above maps the container SSH port `22` to the host port `2222` to avoid conflicts with any local SSH service. Change the host port as needed (`\u003chost_port\u003e:22`).\n\n### SSH Access\n\nThe container includes `sshd` listening on port `22`. The `madz` user has **no password** — connect with:\n\n```bash\nssh -p 2222 madz@localhost\n```\n\nOnce deployed, the user connects as `madz` (no password) on the remapped port. On first login the `madz` user automatically `cd`s into `/app` and runs `npm start`. To get an interactive shell:\n\n```bash\n# Run the app in the background\nnpm start \u0026\n\n# Or start a fresh shell session\n/bin/sh\n```\n\nVolume mounts (`memory/`, `skills/`) are owned by the `madz` user with group `node` for shared write access.\n\n### Environment Variables\n\nAll configuration is controlled via environment variables in the `docker run` command. Variable names follow `UPPER_SNAKE_CASE` of the config key path (e.g., `sandbox.timeout.seconds` → `SANDBOX_TIMEOUT_SECONDS`). Container keys like `providers`, `credentials`, `timeout`, and `search` are dropped from the env var name.\n\n**Essential:**\n\n| Variable             | Required | Default                  | Description                     |\n|----------------------|----------|--------------------------|---------------------------------|\n| `OPENAI_API_KEY`     | Yes      | *(empty)*                | LLM provider API key            |\n\n**Optional — Providers:**\n\n| Variable                           | Default          | Description                             |\n|------------------------------------|------------------|-----------------------------------------|\n| `OPENAI_BASE_URL`                  | `https://api.openai.com/v1` | API endpoint URL                      |\n| `OPENAI_MODEL`                     | `gpt-4o`         | Model name                              |\n| `OPENAI_TEMPERATURE`               | `0.7`            | Sampling temperature (0–2)              |\n| `OPENAI_MAX_TOKENS`                | `4096`           | Max output tokens                       |\n| `OPENAI_REQUESTS_PER_MINUTE`       | `60`             | Rate limit for API calls                |\n| `OPENROUTER_API_KEY`               | *(empty)*        | OpenRouter API key                      |\n| `OPENROUTER_MODEL`                 | `openrouter/auto` | OpenRouter model name                  |\n\n**Optional — Tools:**\n\n| Variable               | Default | Description                              |\n|------------------------|---------|------------------------------------------|\n| `FAL_API_KEY`          | *(empty)* | Fal.ai API key (image generation)       |\n| `EXA_API_KEY`          | *(empty)* | Exa search API key                     |\n| `FIRECRAWL_API_KEY`    | *(empty)* | Firecrawl API key                      |\n| `TAVILY_API_KEY`       | *(empty)* | Tavily search API key                  |\n| `PARALLEL_API_KEY`     | *(empty)* | Parallel search API key                |\n| `SEARXNG_URL`          | *(empty)* | SearXNG search instance URL             |\n| `BING_API_KEY`         | *(empty)* | Bing search API key                    |\n| `CUSTOM_SEARCH_URL`    | *(empty)* | Custom search engine URL                 |\n| `CUSTOM_SEARCH_METHOD` | *(empty)* | Custom search HTTP method                 |\n| `CUSTOM_SEARCH_HEADERS` | *(empty)* | Custom search headers (JSON string)    |\n| `CUSTOM_SEARCH_QUERY_KEY` | *(empty)* | Custom search query key              |\n| `CUSTOM_SEARCH_TITLE_FIELD` | *(empty)* | Custom search title field           |\n| `CUSTOM_SEARCH_URL_FIELD` | *(empty)* | Custom search URL field               |\n| `CUSTOM_SEARCH_DESCRIPTION_FIELD` | *(empty)* | Custom search description field |\n\n**Optional — Sandbox:**\n\n| Variable                         | Default                           | Description                            |\n|----------------------------------|-----------------------------------|----------------------------------------|\n| `SANDBOX_PATHS`                  | `memory/, skills/, tmp/`          | Allowed filesystem paths (comma-separated) |\n| `SANDBOX_TIMEOUT_SECONDS`        | `30`                              | Max execution time in seconds            |\n| `SANDBOX_GRACE_PERIOD`           | `5`                               | Kill grace period in seconds             |\n| `SANDBOX_MEMORY_LIMIT`           | `512m`                            | Heap limit (`--max-old-space-size`)     |\n| `SANDBOX_URL_FILTER`             | `true`                            | Outbound URL blocking                 |\n| `SANDBOX_PYTHON_IMPORT_HOOK`     | `true`                            | Prevent subprocess import              |\n| `SANDBOX_ENV_ALLOWLIST`          | `PATH, HOME, NODE_ENV`            | Allowed env vars (comma-separated)      |\n| `SANDBOX_PERMISSIONS`            | *(none)*                          | Permission grants                      |\n| `SANDBOX_MAX_READ_SIZE`          | `1mb`                             | Max file read size                      |\n| `SANDBOX_SKILL_SCAN_PATHS`       | `skills/, .agents/skills/`        | Skill scan paths (comma-separated)       |\n| `SANDBOX_TRUST_PROJECT_SKILLS`   | `true`                            | Trust skills in project root            |\n\n**Optional — Memory:**\n\n| Variable              | Default               | Description                        |\n|-----------------------|-----------------------|------------------------------------|\n| `MEMORY_DIRECTORY`    | `memory/`             | Base directory for persistence     |\n| `MEMORY_CONTEXT_DIR`  | `memory/context/`     | Context file directory             |\n| `MEMORY_TOOLS_DIR`    | `memory/tools/`       | Tool metadata directory            |\n| `MEMORY_ERRORS_DIR`   | `memory/errors/`      | Error log directory                |\n| `MEMORY_SCHEDULES_DIR`| `memory/schedules/`   | Cron result files directory        |\n\n**Optional — Telemetry:**\n\n| Variable                        | Default                    | Description                        |\n|---------------------------------|----------------------------|------------------------------------|\n| `TELEMETRY_ENABLED`             | `false`                    | Enable OpenTelemetry export          |\n| `TELEMETRY_EXPORTER_PROTOCOL`   | `console`                  | Exporter protocol                    |\n| `TELEMETRY_EXPORTER_ENDPOINT`   | `http://localhost:4318`    | OTLP endpoint URL                    |\n| `TELEMETRY_EXPORTER_MAX_SIZE`   | `512`                      | Batch size before flush              |\n| `TELEMETRY_EXPORTER_SCHEDULED_DELAY` | `5000`                | Scheduled flush interval in ms       |\n| `TELEMETRY_SAMPLING_RATIO`      | `0.1`                      | Trace probability                    |\n\n**Optional — Schedules:**\n\n| Variable                  | Default         | Description                        |\n|---------------------------|-----------------|------------------------------------|\n| `SCHEDULES_MAX_CONCURRENT`| `1`             | Max parallel scheduled runs         |\n| `SCHEDULES_MODE`          | `inprocess`     | Scheduling backend                  |\n\n**Optional — TUI:**\n\n| Variable           | Default  | Description                         |\n|--------------------|----------|-------------------------------------|\n| `TUI_NAME`         | `madz`   | TUI identifier in banner            |\n| `TUI_CURSOR_CHAR`  | `█`      | Cursor character                    |\n\n**Optional — Agent:**\n\n| Variable                 | Default | Description                          |\n|--------------------------|---------|--------------------------------------|\n| `AGENT_RECURSION_LIMIT`  | `30`    | Max graph execution steps per call   |\n\n**Optional — Persistence:**\n\n| Variable              | Default                  | Description                    |\n|-----------------------|--------------------------|--------------------------------|\n| `PERSISTENCE_MODE`    | `memory`                 | Storage backend               |\n| `PERSISTENCE_SQLITE_PATH` | `memory/checkpoints.db` | SQLite checkpointer path   |\n\n**Alternative: inline env var references in `config.yaml`:**\n\nInstead of passing env vars to `docker run`, reference them directly in `config.yaml`:\n\n```yaml\nproviders:\n  openai:\n    credentials:\n      apiKey: \"${OPENAI_API_KEY}\"\n```\n\nThis is the recommended approach for container deployments — keep secrets out of `docker inspect` output.\n\n## Features\n\n### Onboarding\n\nOn first launch, `madz` starts an interactive onboarding flow that collects your profile — attractor (primary interest), expertise areas, dev tools, communication style preferences. This profile is stored as `memory/context/profile.md` and is loaded into the system prompt every session, making madz deeply personalized from the very first message. To re-trigger onboarding, delete `memory/context/profile.md` and restart.\n\n### LLM Provider Abstraction\n\nConfigurable provider dispatch with rate limiting and context-window trimming. Supports OpenAI-compatible APIs.\n\n### Agent\n\nWraps `@langchain/langgraph/prebuilt`'s `createReactAgentGraph` to produce a compiled ReAct agent that interleaves LLM reasoning with tool invocations. `createReactAgent(model, tools)` builds the agent from a provider model and a permission-gated tool array. `callReactAgent(agent, message)` runs the ReAct loop and returns the agent's final response.\n\n### Built-in Tools\n\nBundled LangChain tools gated by sandbox permissions:\n\n| Category | Tools |\n|----------|-------|\n| **Filesystem** | `read_file`, `write_file` (500KB cap), `patch` (9-strategy fuzzy matching + unified diff), `search_files` (ripgrep with native fs fallback) |\n| **Terminal** | `terminal` — shell command execution (foreground/background); `process` — background process management (list, poll, wait, kill, write, pause, resume) |\n| **Task Management** | `todo` — CRUD list persisted to `memory/tools/todo.json` |\n| **Memory** | `memory` — persistent memory tool with CRUD (create, read, update, delete, list). Each memory is stored as an individual `.md` file in `memory/context/` with `createdDate` and `updatedDate` metadata. Memories are long-term, core \"canon\" that shapes your interaction with madz — important personal details, preferences, and context that matter. Loaded into the system prompt at the start of every session. |\n| **Search** | `session_search` — query past conversations by keyword, ID, or browse |\n| **Clarification** | `clarify` — sends clarification questions to the user |\n| **Skills** | `skills_list` — lists discovered skills; `skill_view` — views skill metadata and SKILL.md; `create_skill` — creates spec-compliant skill directories with SKILL.md frontmatter (requires `filesystem:write`) |\n| **Code** | `code` — code execution and analysis |\n| **Web** | `web` — outbound HTTP with timeout, URL allowlist filtering, multi-engine search backends |\n| **Media** | `image` — image generation via fal.ai; `vision` — vision/language analysis via OpenAI; `tts` — text-to-speech via OpenAI TTS |\n| **Agents** | `moa` — multi-agent orchestration |\n| **Cron** | `cron` — cron job utilities |\n\n### Skills Registry\n\nAuto-discovers Agent Skills spec-compliant skills from a `skills/` directory structure. Each skill directory contains a `SKILL.md` file with YAML frontmatter (`name` required, 1-64 lowercase alphanumeric + hyphens; `description` required, 1-1024 characters; optional `license`, `compatibility`, `metadata`). Supports optional `scripts/` subdirectory containing executable scripts (detected by extension: `.py`, `.sh`, `.js`, `.rb`, `.ts`). The `create_skill` tool lets agents create new skills programmatically — validating spec constraints before writing `SKILL.md` and optionally scaffolding a `scripts/` directory.\n\n### Permission Gating\n\nBuilt-in tools are registered only when their required permissions are enabled for the session. Tools like `clarify` have zero permissions and always register.\n\n| Permission Required | Tools |\n|---------------------|-------|\n| `filesystem:read` | `read_file`, `search_files`, `skills_list`, `skill_view`, `session_search` |\n| `filesystem:write` | `write_file`, `patch`, `todo`, `memory`, `create_skill` |\n| `filesystem:exec` + `process:spawn` | `terminal` |\n| `process:spawn` | `process` |\n| *(none)* | `clarify` |\n\n### Memory System\n\n`madz` operates on a dual-layer memory architecture that evolves naturally over time:\n\n**Canonical Memories**\nSet explicitly by the user, these form the enduring foundation of the system. Stored as individual Markdown files in `memory/context/`, each carries `createdDate` and `updatedDate` metadata in YAML frontmatter. At the start of every session, canonical memories are loaded and appended to the system prompt, ensuring core context, preferences, and personal details remain consistent across interactions.\n\n**Ephemeral Memories**\nCaptured autonomously by the harness during operation, these record patterns, milestones, emotional tones, and recurring themes. Stored temporarily with automatic expiration, they act as a living lens — subtly influencing how `madz` approaches future tasks, adapts its tone, and anticipates needs. They are not hardcoded; they evolve organically as the relationship deepens.\n\nTogether, these layers create a system that remembers what matters while naturally adapting to how you work. When you update or delete a canonical memory, follow it with `:new` so the current session reflects the change immediately.\n\n**Memory tool actions:** `create` (new memory), `read` (get by key), `update` (modify by key), `delete` (remove by key), `list` (all memories, optional query filter)\n\n### Sandbox RTE\n\nSkills run in isolated forked processes with time limits, memory caps, and allowlists for filesystem paths and outbound URLs. Blocked schemes: `file://`, `gopher://`, `dict://`.\n\n### Telemetry\n\nOptional `@opentelemetry/sdk-node` integration. Configurable exporter (console, OTLP HTTP, OTLP gRPC), probability sampling, and automatic redaction of sensitive fields (API keys, auth headers).\n\n### Cron Scheduler\n\nRecurring job definitions in `config.yaml`. Supports both in-process scheduling and delegation to the system crontab (`mode: \"system\"`). Each invocation inherits the current session's memory context and sandbox permissions. Max-concurrency control prevents run overlap.\n\n## Directory Structure\n\n```\n/\n├── index.js                    # Application entry point\n├── config.yaml                 # Centralized configuration\n├── .husky/                     # Git hooks (lint, fmt, tests)\n├── src/\n│   ├── agent/                  # ReAct agent wrapper (LangGraph)\n│   ├── config/                 # YAML parsing \u0026 Zod schema validation\n│   ├── memory/                 # Markdown file persistence\n│   ├── provider/               # LLM model factory (OpenAI)\n│   ├── skills/                 # Agent Skills spec discovery, validation \u0026 permissions\n│   ├── sandbox/                # Process sandboxing \u0026 capability enforcement\n│   ├── scheduler/              # Cron-based job runner\n│   ├── session/                # Per-session state \u0026 context windows\n│   ├── telemetry/              # OpenTelemetry tracing \u0026 redaction\n│   ├── tools/                  # Built-in LangChain tools\n│   └── tui/                    # Ink React terminal UI\n├── tests/\n│   ├── unit/                   # Unit tests per module\n│   └── integration/            # End-to-end flow tests\n└── memory/                     # Persistent markdown storage\n```\n\n## Config Reference\n\n| Section        | Key                                | Default                                   | Description                                       |\n|----------------|------------------------------------|-------------------------------------------|---------------------------------------------------|\n| `providers`    | `openai.type`                      | `openai`                                  | LLM provider type                                 |\n|                | `openai.base_url`                  | `https://api.openai.com/v1`               | API endpoint URL                                  |\n|                | `openai.model`                     | `gpt-4o`                                  | Model name                                        |\n|                | `openai.credentials.apiKey`        | *(empty)*                                 | API key for authentication                        |\n|                | `openai.temperature`               | `0.7`                                     | Sampling temperature (0–2)                        |\n|                | `openai.maxTokens`                 | `4096`                                    | Max output tokens                                 |\n|                | `openai.rateLimit.requestsPerMinute` | `120`                                   | Rate limit for API calls                          |\n| `sandbox`      | `paths`                            | `[\"memory/\", \"skills/\", \"src/\", \"/tmp\"]`  | Allowed filesystem paths                          |\n|                | `timeout.seconds`                  | `30`                                      | Max execution time in seconds                     |\n|                | `timeout.gracePeriod`              | `5`                                       | Kill grace period in seconds                      |\n|                | `memoryLimit`                      | `\"128mb\"`                                  | Heap limit (`--max-old-space-size`)               |\n|                | `safety.urlFilter`                 | `true`                                    | Outbound URL blocking                             |\n|                | `safety.pythonImportHook`          | `true`                                    | Prevent subprocess import                         |\n|                | `env.allowlist`                    | `[\"PATH\", \"HOME\", \"NODE_ENV\"]`            | Allowed environment variables                     |\n|                | `permissions`                      | `[\"filesystem:read\", ...]`                | Permission grants                                 |\n|                | `maxReadSize`                      | `\"10mb\"`                                   | Max file read size                                |\n| `memory`       | `directory`                        | `memory/`                                 | Base directory for persistence                    |\n|                | `contextDir`                       | `memory/context/`                         | Context file directory                            |\n|                | `toolsDir`                         | `memory/tools/`                           | Tool metadata directory                           |\n|                | `errorsDir`                        | `memory/errors/`                          | Error log directory                               |\n|                | `schedulesDir`                     | `memory/schedules/`                       | Cron result files directory                       |\n| `telemetry`    | `enabled`                          | `false`                                   | Enable OpenTelemetry export                       |\n|                | `exporter.protocol`                | `console`                                 | Exporter protocol (`console`, `http`, `grpc`)     |\n|                | `exporter.endpoint`                | `http://localhost:4318`                   | OTLP endpoint URL                                 |\n|                | `exporter.batch.maxSize`           | `512`                                     | Batch size before flush                           |\n|                | `exporter.batch.scheduledDelay`    | `5000`                                    | Scheduled flush interval in ms                    |\n|                | `sampling.ratio`                   | `0.1`                                     | Trace probability                                 |\n|                | `redact.paths`                     | `[\"credentials.apiKey\", ...]`             | Sensitive field paths for redaction               |\n| `schedules`    | `maxConcurrent`                    | `1`                                       | Max parallel scheduled runs                       |\n|                | `mode`                             | `inprocess`                               | Scheduling backend (`inprocess`, `system`)        |\n| `tui`          | `name`                             | `madz`                                  | TUI identifier in banner                          |\n|                | `cursorChar`                       | `█`                                     | Cursor character                                  |\n| `agent`        | `recursionLimit`                   | `30`                                    | Max graph execution steps per agent call          |\n| `persistence`  | `mode`                             | `memory`                                | Storage backend (`memory`, `sqlite`)              |\n|                | `sqlite_path`                      | `memory/checkpoints.db`                   | SQLite checkpointer file path                     |\n\n## Testing\n\n```bash\n# Run all tests\nnpm run test\n\n# Generate coverage report\nnpm run coverage\n\n# Auto-fix lint \u0026 formatting\nnpm run fix\n\n# Check lint \u0026 formatting (no fix)\nnpm run lint\n```\n\nThe pre-commit hook runs linting, formatting, and tests (targeting 100% code coverage). A commit will fail if any gate does not pass.\n\n## Development\n\n```bash\nnpm install\nnpm run fix          # Format and lint-fix all files\nnpm run test         # Verify changes\nnpm run coverage     # Generate and verify 100% coverage\n```\n\n### Extending Skills\n\nSkills follow the [Agent Skills spec](https://agentskills.io/specification). Each skill is a directory under `skills/` containing a `SKILL.md` file with YAML frontmatter.\n\n**Programmatic creation:** Use the `create_skill` tool to create new skills from within agent conversations. The tool validates the name (lowercase alphanumeric + hyphens, 1-64 chars), description (1-1024 chars), and optional fields (`license`, `compatibility`, `metadata`) against spec constraints before writing `SKILL.md`. It can optionally scaffold a `scripts/` subdirectory with a `README.md` placeholder for executable scripts.\n\n**Manual creation:**\n\n1. Create a directory under `skills/your-skill/`.\n2. Add a `SKILL.md` file with YAML frontmatter:\n   ```yaml\n   ---\n   name: your-skill\n   description: What this skill does and when to use it.\n   license: Apache-2.0          # optional\n    compatibility: Node.js 24+   # optional, max 500 chars\n   metadata:\n     author: me\n     version: \"1.0\"             # optional string map\n   ---\n   Step-by-step instructions for the agent...\n   ```\n3. (Optional) Place executable scripts under `skills/your-skill/scripts/`. Supported extensions: `.py` (Python 3), `.sh` (Bash), `.js`/`.mjs` (Node.js), `.rb` (Ruby), `.ts` (Node.js + tsx).\n4. Restart the harness — the skills registry auto-discovers new skills on boot.\n\n### Environment Variables\n\n`madz` supports two environment variable patterns:\n\n1. **Direct override** — set env vars to override `config.yaml` values. Names follow `UPPER_SNAKE_CASE` of the config key path (e.g., `sandbox.timeout.seconds` → `SANDBOX_TIMEOUT_SECONDS`). Docker users: see the [Environment Variables](#environment-variables) section under Docker for the full table.\n2. **Inline reference in `config.yaml`** — use `${VAR_NAME}` syntax in config values:\n\n```yaml\nproviders:\n  openai:\n    credentials:\n      apiKey: \"${OPENAI_API_KEY}\"\n```\n\nFor Docker-specific configuration, see the [Environment Variables](#environment-variables) section under Docker.\n\nSee [Config Reference](#config-reference) for the full list of configuration keys and their defaults.\n\n## License\n\nLicensed under the [BSD-3-Clause](LICENSE) License.\n\nCopyright (c) 2026 Jason Mulligan.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Favoidwork%2Fmadz","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Favoidwork%2Fmadz","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Favoidwork%2Fmadz/lists"}