diff --git a/DOCKER_MULTI_BOT_GUIDE.md b/DOCKER_MULTI_BOT_GUIDE.md index 2361421..7fa4bfc 100644 --- a/DOCKER_MULTI_BOT_GUIDE.md +++ b/DOCKER_MULTI_BOT_GUIDE.md @@ -59,20 +59,28 @@ Each bot runs in its own Docker container with: ``` nanobot/ ├── .env.shared # Shared settings (API keys, model, etc.) -├── .env.user1 # Bot 1 overrides -├── .env.user2 # Bot 2 overrides -├── .env.user3 # Bot 3 overrides +├── .env.user1 # Bot 1 (@ilia) overrides +├── .env.user2 # Bot 2 (@family) overrides +├── .env.user3 # Bot 3 (@wife) overrides +├── agent_workspaces/ # Templates copied by scripts/init-agent-workspaces.sh +├── scripts/init-agent-workspaces.sh ├── docker-compose.multi.env.yml # Production compose file ├── docker-compose.multi.dev.yml # Development compose file │ -└── ~/.nanobot-user1/ - └── config.json # Bot 1 channel config (Telegram token, allowFrom) -└── ~/.nanobot-user2/ - └── config.json # Bot 2 channel config +├── ~/.nanobot/workspaces/ +│ ├── ilia/ # Mounted as /workspace for user1 — AGENTS.md, memory/, … +│ ├── family/ # user2 +│ └── wife/ # user3 +├── ~/.nanobot-user1/ +│ └── config.json # Bot 1 channel config (Telegram token, allowFrom) +├── ~/.nanobot-user2/ +│ └── config.json # Bot 2 channel config └── ~/.nanobot-user3/ └── config.json # Bot 3 channel config ``` +`./workspace` in the repo remains for **single-bot** `docker-compose.yml` only; multi-bot uses `~/.nanobot/workspaces/*` per container. + ### Configuration Loading 1. **Docker Compose** loads environment files: @@ -91,6 +99,31 @@ nanobot/ ## Setup Instructions +### Step 0: Per-agent workspaces (personalities + isolated memory) + +Multi-bot compose mounts **separate** workspace directories so each bot has its own `AGENTS.md`, `SOUL.md`, `USER.md`, and `memory/` (no shared `./workspace`). + +On the host, from the repo root: + +```bash +./scripts/init-agent-workspaces.sh +``` + +This creates: + +``` +~/.nanobot/workspaces/ + ilia/ # nanobot-user1 — dev / infra persona + family/ # nanobot-user2 — household persona + wife/ # nanobot-user3 — personal assistant persona +``` + +Templates live in-repo under `agent_workspaces/`. Re-run the script anytime: it **skips** files that already exist. Adjust ownership if Docker runs as root: + +```bash +sudo chown -R "$(whoami):$(whoami)" ~/.nanobot/workspaces +``` + ### Step 1: Create Environment Files Run the setup script: diff --git a/agent_workspaces/README.md b/agent_workspaces/README.md new file mode 100644 index 0000000..9711a41 --- /dev/null +++ b/agent_workspaces/README.md @@ -0,0 +1,30 @@ +# Agent workspace skeletons + +These directories are **templates** for per-agent workspaces on the host: + +`~/.nanobot/workspaces/ilia/` +`~/.nanobot/workspaces/family/` +`~/.nanobot/workspaces/wife/` + +Each contains bootstrap files (`AGENTS.md`, `USER.md`, `SOUL.md`) and `memory/` (`MEMORY.md`, `HISTORY.md`) loaded by nanobot’s `ContextBuilder` and `MemoryStore`. + +## Initialise on the host + +From the repo root (after clone): + +```bash +chmod +x scripts/init-agent-workspaces.sh +./scripts/init-agent-workspaces.sh +``` + +Override destination root (default `$HOME/.nanobot`): + +```bash +NANOBOT_HOME=/path/to/.nanobot ./scripts/init-agent-workspaces.sh +``` + +The script **does not overwrite** existing files so you can safely re-run after editing. + +## Docker + +Multi-bot compose mounts each path into `/workspace` in the matching container. See `DOCKER_MULTI_BOT_GUIDE.md`. diff --git a/agent_workspaces/family/AGENTS.md b/agent_workspaces/family/AGENTS.md new file mode 100644 index 0000000..78446fc --- /dev/null +++ b/agent_workspaces/family/AGENTS.md @@ -0,0 +1,14 @@ +# @family — Agent instructions + +You are the **family** assistant: shared calendar, household coordination, and kid- or home-related questions. + +## Scope +- Schedules, reminders, and “what’s this week” style questions. +- Simple web lookups (school, activities, recipes) when tools allow. +- Warm, inclusive language for all family members. + +## Out of scope +- Production servers, SSH, Proxmox, or source-code repositories unless explicitly asked by an adult and tools are available. + +## Tone +Friendly, organized, patient. Offer clear summaries and next steps. diff --git a/agent_workspaces/family/SOUL.md b/agent_workspaces/family/SOUL.md new file mode 100644 index 0000000..1c05ed5 --- /dev/null +++ b/agent_workspaces/family/SOUL.md @@ -0,0 +1,7 @@ +# Personality — @family + +**Voice:** Warm, clear, and reassuring. Good with busy parents and kids’ contexts. + +**Values:** Inclusivity, clarity on dates/times, respect for privacy between family members where relevant. + +**Avoid:** Cold or corporate tone; assumption that everyone shares one email account. diff --git a/agent_workspaces/family/USER.md b/agent_workspaces/family/USER.md new file mode 100644 index 0000000..f881775 --- /dev/null +++ b/agent_workspaces/family/USER.md @@ -0,0 +1,8 @@ +# User profile — Family + +This workspace represents the **household** (not one individual). List members, ages if relevant, schools, and recurring commitments. + +## Edit this file +- Family members and how you refer to them. +- Default calendar names or shared inboxes (if any). +- Anything the agent should know for scheduling and coordination. diff --git a/agent_workspaces/family/memory/HISTORY.md b/agent_workspaces/family/memory/HISTORY.md new file mode 100644 index 0000000..3af5dd8 --- /dev/null +++ b/agent_workspaces/family/memory/HISTORY.md @@ -0,0 +1,3 @@ +# Event log — Family + +Append-only style log for this household agent. diff --git a/agent_workspaces/family/memory/MEMORY.md b/agent_workspaces/family/memory/MEMORY.md new file mode 100644 index 0000000..d46ca3a --- /dev/null +++ b/agent_workspaces/family/memory/MEMORY.md @@ -0,0 +1,5 @@ +# Long-term memory — Family + +Household-level facts (recurring events, preferences, school names). **Do not store secrets** (passwords, full IDs). + +_Empty placeholder — add bullet facts here over time._ diff --git a/agent_workspaces/ilia/AGENTS.md b/agent_workspaces/ilia/AGENTS.md new file mode 100644 index 0000000..bf66933 --- /dev/null +++ b/agent_workspaces/ilia/AGENTS.md @@ -0,0 +1,14 @@ +# @ilia — Agent instructions + +You are the personal assistant for **Ilia**. You focus on development, homelab infrastructure, code review, and technical research. + +## Scope +- Software development (Gitea, PRs, issues, shell, git) and clear technical explanations. +- Homelab / Proxmox / networking when those tools are available. +- Email and calendar for Ilia’s accounts when configured. + +## Tone +Concise, accurate, and direct. Prefer actionable steps over long preambles. + +## Tools +Use nanobot tools as configured for this instance. Do not assume tools that are not in your tool list. diff --git a/agent_workspaces/ilia/SOUL.md b/agent_workspaces/ilia/SOUL.md new file mode 100644 index 0000000..1298f6e --- /dev/null +++ b/agent_workspaces/ilia/SOUL.md @@ -0,0 +1,7 @@ +# Personality — @ilia + +**Voice:** Technical, calm, efficient. Short paragraphs. No fluff. + +**Values:** Correctness, security-minded defaults, reproducible steps. + +**Avoid:** Unnecessary apologies, over-explaining basic concepts unless asked. diff --git a/agent_workspaces/ilia/USER.md b/agent_workspaces/ilia/USER.md new file mode 100644 index 0000000..2441e2f --- /dev/null +++ b/agent_workspaces/ilia/USER.md @@ -0,0 +1,7 @@ +# User profile — Ilia + +**Name:** Ilia +**Role:** Primary operator of this nanobot stack; dev and infra. + +## Edit this file +Add preferences, timezone, important contacts, repos, and anything this agent should remember about *you* (not generic assistant behavior — that belongs in `SOUL.md` / `AGENTS.md`). diff --git a/agent_workspaces/ilia/memory/HISTORY.md b/agent_workspaces/ilia/memory/HISTORY.md new file mode 100644 index 0000000..d7ac4b4 --- /dev/null +++ b/agent_workspaces/ilia/memory/HISTORY.md @@ -0,0 +1,3 @@ +# Event log — Ilia + +Append-only style log. Search with grep when recalling past events. diff --git a/agent_workspaces/ilia/memory/MEMORY.md b/agent_workspaces/ilia/memory/MEMORY.md new file mode 100644 index 0000000..3e1b0f7 --- /dev/null +++ b/agent_workspaces/ilia/memory/MEMORY.md @@ -0,0 +1,5 @@ +# Long-term memory — Ilia + +Facts and preferences worth keeping across sessions. The agent may update this file when you confirm something should be remembered. + +_Empty placeholder — add bullet facts here over time._ diff --git a/agent_workspaces/wife/AGENTS.md b/agent_workspaces/wife/AGENTS.md new file mode 100644 index 0000000..da3f53b --- /dev/null +++ b/agent_workspaces/wife/AGENTS.md @@ -0,0 +1,11 @@ +# @wife — Agent instructions + +You are the personal assistant for **Ilia’s wife**. Focus on her calendar, email (when connected), daily tasks, and practical lookups. + +## Scope +- Scheduling, reminders, messages, and life-admin tasks. +- Summaries of mail or web pages when tools allow. +- Respectful, private handling of personal topics. + +## Tone +Supportive and efficient. Match the user’s formality preferences over time. diff --git a/agent_workspaces/wife/SOUL.md b/agent_workspaces/wife/SOUL.md new file mode 100644 index 0000000..478d6ed --- /dev/null +++ b/agent_workspaces/wife/SOUL.md @@ -0,0 +1,7 @@ +# Personality — @wife + +**Voice:** Friendly, attentive, and tactful. + +**Values:** Privacy, accuracy on appointments and commitments, gentle reminders. + +**Avoid:** Dismissive or overly technical jargon unless the user prefers it. diff --git a/agent_workspaces/wife/USER.md b/agent_workspaces/wife/USER.md new file mode 100644 index 0000000..5eefb6e --- /dev/null +++ b/agent_workspaces/wife/USER.md @@ -0,0 +1,6 @@ +# User profile — Wife + +**Name:** _(preferred name / how to address her)_ + +## Edit this file +Add preferences, timezone, health or routine notes *you are comfortable storing in plain text*, and communication preferences. diff --git a/agent_workspaces/wife/memory/HISTORY.md b/agent_workspaces/wife/memory/HISTORY.md new file mode 100644 index 0000000..47078d6 --- /dev/null +++ b/agent_workspaces/wife/memory/HISTORY.md @@ -0,0 +1,3 @@ +# Event log — Wife + +Append-only style log. Search with grep when recalling past events. diff --git a/agent_workspaces/wife/memory/MEMORY.md b/agent_workspaces/wife/memory/MEMORY.md new file mode 100644 index 0000000..2fc7cf1 --- /dev/null +++ b/agent_workspaces/wife/memory/MEMORY.md @@ -0,0 +1,5 @@ +# Long-term memory — Wife + +Facts and preferences worth keeping across sessions. The agent may update when you confirm. + +_Empty placeholder — add bullet facts here over time._ diff --git a/docker-compose.multi.dev.yml b/docker-compose.multi.dev.yml index 41ef6f9..7cd0095 100644 --- a/docker-compose.multi.dev.yml +++ b/docker-compose.multi.dev.yml @@ -1,3 +1,4 @@ +# user1=@ilia, user2=@family, user3=@wife — workspaces ~/.nanobot/workspaces/{ilia,family,wife} # Development version - mounts source code for live updates # Use this when developing nanobot code # Changes to nanobot/ directory will be picked up automatically (may need container restart) @@ -15,6 +16,7 @@ services: - .env.user1 volumes: - ~/.nanobot-user1:/root/.nanobot + - ~/.nanobot/workspaces/ilia:/workspace # Mount source code for development (changes picked up immediately) - ./nanobot:/app/nanobot:ro # Read-only mount (safer) # Or use this for read-write (if you edit inside container): @@ -42,6 +44,7 @@ services: - .env.user2 volumes: - ~/.nanobot-user2:/root/.nanobot + - ~/.nanobot/workspaces/family:/workspace - ./nanobot:/app/nanobot:ro ports: - "18791:18790" @@ -66,6 +69,7 @@ services: - .env.user3 volumes: - ~/.nanobot-user3:/root/.nanobot + - ~/.nanobot/workspaces/wife:/workspace - ./nanobot:/app/nanobot:ro ports: - "18792:18790" diff --git a/docker-compose.multi.env.yml b/docker-compose.multi.env.yml index 9c6d47c..1b7d99b 100644 --- a/docker-compose.multi.env.yml +++ b/docker-compose.multi.env.yml @@ -1,3 +1,4 @@ +# Multi-bot: user1 = @ilia, user2 = @family, user3 = @wife (see ~/.nanobot/workspaces/* and scripts/init-agent-workspaces.sh). # Using separate env files per container: # - .env.shared: Common settings (API keys, model, etc.) - loaded first # - .env.user1, .env.user2, .env.user3: Bot-specific overrides - loaded after @@ -16,6 +17,7 @@ services: - .env.user1 # Bot-specific overrides (loaded second, overrides shared) volumes: - ~/.nanobot-user1:/root/.nanobot + - ~/.nanobot/workspaces/ilia:/workspace ports: - "18790:18790" deploy: @@ -39,6 +41,7 @@ services: - .env.user2 # Bot-specific overrides (loaded second, overrides shared) volumes: - ~/.nanobot-user2:/root/.nanobot + - ~/.nanobot/workspaces/family:/workspace ports: - "18791:18790" deploy: @@ -62,6 +65,7 @@ services: - .env.user3 # Bot-specific overrides (loaded second, overrides shared) volumes: - ~/.nanobot-user3:/root/.nanobot + - ~/.nanobot/workspaces/wife:/workspace ports: - "18792:18790" deploy: diff --git a/docker-compose.multi.yml b/docker-compose.multi.yml index 914758d..4cc46aa 100644 --- a/docker-compose.multi.yml +++ b/docker-compose.multi.yml @@ -1,3 +1,6 @@ +# Multi-bot: nanobot-user1 = @ilia, user2 = @family, user3 = @wife. +# Each container uses ~/.nanobot/workspaces// → /workspace (run scripts/init-agent-workspaces.sh first). + services: nanobot-user1: build: @@ -6,8 +9,13 @@ services: container_name: nanobot-user1 command: ["gateway"] restart: unless-stopped + env_file: + - .env.shared + - .env.user1 volumes: - ~/.nanobot-user1:/root/.nanobot + # @ilia — isolated workspace + memory (host: ~/.nanobot/workspaces/ilia) + - ~/.nanobot/workspaces/ilia:/workspace ports: - "18790:18790" deploy: @@ -26,8 +34,13 @@ services: container_name: nanobot-user2 command: ["gateway"] restart: unless-stopped + env_file: + - .env.shared + - .env.user2 volumes: - ~/.nanobot-user2:/root/.nanobot + # @family — isolated workspace + memory + - ~/.nanobot/workspaces/family:/workspace ports: - "18791:18790" deploy: @@ -46,8 +59,13 @@ services: container_name: nanobot-user3 command: ["gateway"] restart: unless-stopped + env_file: + - .env.shared + - .env.user3 volumes: - ~/.nanobot-user3:/root/.nanobot + # @wife — isolated workspace + memory + - ~/.nanobot/workspaces/wife:/workspace ports: - "18792:18790" deploy: diff --git a/docs/mcp_and_skills_backlog.md b/docs/mcp_and_skills_backlog.md new file mode 100644 index 0000000..84ba698 --- /dev/null +++ b/docs/mcp_and_skills_backlog.md @@ -0,0 +1,573 @@ +# MCP Integrations & Skills Backlog + +> **Living document** — update this file as items are implemented, reprioritized, or new candidates emerge. +> +> Last updated: 2026-03-30 + +--- + +## Table of Contents + +1. [Current State](#current-state) +2. [Security: Local-Clone Policy](#security-local-clone-policy) +3. [Shortlist — Next Phase](#shortlist--next-phase) +4. [Backlog — Later](#backlog--later) +5. [Skill Catalog](#skill-catalog) +6. [Phase 1 Priorities](#phase-1-priorities) +7. [Implementation Notes](#implementation-notes) + +--- + +## Current State + +| Category | What we have today | +|---|---| +| **Built-in tools** | `filesystem` (read/write/edit/list), `exec` (shell), `web` (search + fetch), `message`, `spawn`, `cron`, `email` (IMAP), `calendar` (Google Calendar via built-in tool) | +| **MCP servers** | 1 connected — Gmail MCP (`@gongrzhe/server-gmail-autoauth-mcp`, stdio/npx). See [docs/gmail_mcp_setup.md](gmail_mcp_setup.md). | +| **Skills** | 10 bundled in `nanobot/skills/`: `github`, `gitea`, `calendar`, `cron`, `weather`, `summarize`, `tmux`, `clawhub`, `skill-creator`, `memory` | +| **Agent architecture** | 3 named agents, each running as a **separate Docker container** with its own workspace, personality, and memory (Option B). See below. | +| **Config schema** | `tools.mcpServers` → `MCPServerConfig` (stdio or HTTP), `tools.toolProfiles` → `ToolProfileConfig` can further filter tools within a single agent. See `nanobot/config/schema.py`. | + +### Agent Workspaces + +Each agent is a separate nanobot instance (Docker container) with an isolated workspace under `~/.nanobot/workspaces/`. The workspace contains bootstrap files (`AGENTS.md`, `SOUL.md`, `USER.md`) that define the agent's personality and instructions, plus a `memory/` directory for long-term memory that is private to that agent. + +``` +~/.nanobot/workspaces/ +├── ilia/ # @ilia — personal dev, infra, research +│ ├── AGENTS.md # Dev/infra-focused instructions +│ ├── USER.md # Ilia's profile, preferences +│ ├── SOUL.md # Personality: technical, concise +│ └── memory/ +│ └── MEMORY.md +├── family/ # @family — shared household agent +│ ├── AGENTS.md # Family scheduling, coordination +│ ├── USER.md # Family members, kids' info +│ ├── SOUL.md # Personality: warm, organized +│ └── memory/ +│ └── MEMORY.md +└── wife/ # @wife — personal assistant for wife + ├── AGENTS.md # Personal tasks, calendar, email + ├── USER.md # Wife's profile, preferences + ├── SOUL.md # Personality: friendly, helpful + └── memory/ + └── MEMORY.md +``` + +Each container mounts its workspace and its own `config.json` (with agent-specific MCP servers, channels, and `allowFrom` lists). Compose service names are `nanobot-user1` … `user3`. + +| Service | Persona | Config dir | Workspace (host → `/workspace`) | Typical channels | +|---|---|---|---|---| +| `nanobot-user1` | @ilia | `~/.nanobot-user1/` | `~/.nanobot/workspaces/ilia` | Telegram, email (Ilia) | +| `nanobot-user2` | @family | `~/.nanobot-user2/` | `~/.nanobot/workspaces/family` | Family Telegram | +| `nanobot-user3` | @wife | `~/.nanobot-user3/` | `~/.nanobot/workspaces/wife` | Telegram, email (wife) | + +_Use `scripts/init-agent-workspaces.sh` to create the three workspace trees under `~/.nanobot/workspaces/`._ + +--- + +## Security: Local-Clone Policy + +All new MCP servers are **cloned locally** into the repository rather than fetched at runtime from npm/PyPI registries. This gives us: + +- **Audit control** — we can review every line before running it. +- **Reproducibility** — pinned commits, no surprise upstream updates. +- **Air-gap friendliness** — works on isolated networks after initial clone. + +### Directory layout + +``` +nanobot/ +├── mcp-servers/ # <-- NEW: local MCP server clones +│ ├── gitea-mcp/ # git clone from gitea.com/gitea/gitea-mcp +│ ├── google-calendar-mcp/ # git clone from github.com/nspady/google-calendar-mcp +│ ├── mcp-proxmox/ # git clone from github.com/antonio-mello-ai/mcp-proxmox +│ └── fetch-browser/ # git clone from github.com/TheSethRose/Fetch-Browser +├── nanobot/ +├── docs/ +└── ... +``` + +### Config pattern (local stdio) + +```jsonc +{ + "tools": { + "mcpServers": { + "gitea": { + "command": "./mcp-servers/gitea-mcp/gitea-mcp", + "args": ["--token", "$NANOBOT_GITLE_TOKEN", "--url", "http://10.0.30.169:3000"], + "env": {} + } + } + } +} +``` + +Each server's README in `mcp-servers//` documents build steps, required env vars, and update procedure. + +--- + +## Shortlist — Next Phase + +These are the 4 MCP servers we plan to integrate in the immediate next phase. Each entry is detailed enough to create implementation tickets directly. + +--- + +### S1. Gitea MCP + +| Field | Detail | +|---|---| +| **Upstream** | `gitea.com/gitea/gitea-mcp` (official, Go, v1.0.2, 56 stars, Apache-2.0) | +| **Transport** | Stdio (recommended) or SSE | +| **Auth** | Gitea personal-access token — reuse existing `$NANOBOT_GITLE_TOKEN` | +| **Complexity** | **Low** — token and network route to `http://10.0.30.169:3000` already exist | +| **Replaces** | Current curl-based `gitea` skill and hardcoded API commands in `AGENTS.md` | +| **Target agents** | `@ilia` only (dev tooling; not exposed to `@family` or `@wife`) | + +#### User stories + +- **US-G1**: As `@ilia`, I can say "list open PRs on nanobot" and get a formatted summary without writing curl commands. +- **US-G2**: As `@ilia`, I can say "search code for `MCPServerConfig`" and the agent returns matching files and lines from Gitea. +- **US-G3**: As `@ilia`, I can say "create an issue titled 'Add Proxmox MCP' with label `enhancement`" and the agent creates it in Gitea. +- **US-G4**: As `@ilia`, I can say "show diff for PR #42" and get a readable summary of changes. + +#### Technical notes + +- **Build**: Go 1.24+. Clone, `go build`, produces single binary `gitea-mcp`. +- **Local clone path**: `mcp-servers/gitea-mcp/` +- **Config entry**: + ```jsonc + "gitea": { + "command": "./mcp-servers/gitea-mcp/gitea-mcp", + "args": [], + "env": { + "GITEA_URL": "http://10.0.30.169:3000", + "GITEA_TOKEN": "$NANOBOT_GITLE_TOKEN" + } + } + ``` +- **Expected tool names**: `mcp_gitea_list_repos`, `mcp_gitea_search_code`, `mcp_gitea_create_issue`, `mcp_gitea_list_pulls`, etc. +- **Safety**: Read operations are safe. Issue/PR creation and file writes should require user confirmation via tool-profile constraints. + +--- + +### S2. Google Calendar MCP + +| Field | Detail | +|---|---| +| **Upstream** | `github.com/nspady/google-calendar-mcp` (TypeScript, v2.6.1, 1071 stars, MIT) | +| **Transport** | Stdio via `node` | +| **Auth** | Google OAuth2 (same pattern as Gmail MCP — credentials in `~/.gmail-mcp/`) | +| **Complexity** | **Medium** — OAuth flow is already a solved pattern from Gmail MCP setup; multi-calendar config adds small overhead | +| **Complements** | Existing built-in `calendar` tool; MCP version adds multi-calendar, recurring events, and free/busy queries | +| **Target agents** | All three — `@ilia`, `@family`, `@wife` (each with their own calendar scope) | + +#### User stories + +- **US-C1**: As `@family`, I can ask "what's on the family calendar this week?" and get a merged view of all family members' events. +- **US-C2**: As `@ilia`, I can say "find a free 1-hour slot tomorrow afternoon" and the agent checks busy/free across my calendars. +- **US-C3**: As `@family`, I can say "add 'Soccer practice' to the family calendar on Saturday at 10am" and it creates the event. +- **US-C4**: As `@ilia`, I can say "reschedule my 2pm meeting to 4pm" and the agent updates the event after confirmation. +- **US-C5**: As `@wife`, I can say "what do I have on Thursday?" and see only events on my personal calendar. + +#### Technical notes + +- **Build**: `npm install` in cloned repo, run via `node dist/index.js`. +- **Local clone path**: `mcp-servers/google-calendar-mcp/` +- **OAuth setup**: Same Google Cloud project as Gmail MCP. Enable Calendar API, reuse existing OAuth client. Token stored alongside Gmail tokens. +- **Config entry**: + ```jsonc + "google_calendar": { + "command": "node", + "args": ["./mcp-servers/google-calendar-mcp/dist/index.js"], + "env": { + "GOOGLE_OAUTH_CREDENTIALS": "~/.gmail-mcp/gcp-oauth.keys.json" + } + } + ``` +- **Expected tool names**: `mcp_google_calendar_list_events`, `mcp_google_calendar_create_event`, `mcp_google_calendar_freebusy`, `mcp_google_calendar_update_event`, `mcp_google_calendar_delete_event` +- **Migration path**: Phase out built-in `calendar` tool once MCP version is validated. Keep both available during transition via tool profiles. + +--- + +### S3. Proxmox MCP + +| Field | Detail | +|---|---| +| **Upstream** | `github.com/antonio-mello-ai/mcp-proxmox` (Python, pip-installable, MIT) | +| **Transport** | Stdio via `python -m mcp_proxmox` | +| **Auth** | Proxmox API token (user `nanobot@pam!mcp-token` + secret) | +| **Complexity** | **Medium** — requires network route to Proxmox cluster API, API token creation on Proxmox, and careful permission scoping | +| **New capability** | Homelab infrastructure visibility and management from chat | +| **Target agents** | `@ilia` only (infrastructure admin; never exposed to `@family` or `@wife`) | + +#### User stories + +- **US-P1**: As `@ilia`, I can say "show me the status of all VMs" and get a table of names, states, CPU, and RAM usage. +- **US-P2**: As `@ilia`, I can say "how much storage is left on the cluster?" and get aggregate numbers. +- **US-P3**: As `@ilia`, I can say "restart the dev-runner VM" and the agent does so after asking for confirmation. +- **US-P4**: As `@ilia`, I can say "take a snapshot of the nanobot VM before I upgrade" and the agent creates a named snapshot. + +#### Technical notes + +- **Build**: `pip install -e ./mcp-servers/mcp-proxmox/` into nanobot's venv, or use a dedicated venv. +- **Local clone path**: `mcp-servers/mcp-proxmox/` +- **Proxmox setup**: + 1. Create API token: Datacenter → Permissions → API Tokens → Add (`nanobot@pam`, token ID `mcp-token`). + 2. Assign minimum roles: `PVEAuditor` for read-only, `PVEVMAdmin` for lifecycle ops (Phase 1 starts read-only). + 3. Store token secret in `~/.nanobot/config.json` env or in a `.env` file. +- **Config entry**: + ```jsonc + "proxmox": { + "command": "python", + "args": ["-m", "mcp_proxmox"], + "env": { + "PROXMOX_HOST": "https://10.0.30.1:8006", + "PROXMOX_TOKEN_ID": "nanobot@pam!mcp-token", + "PROXMOX_TOKEN_SECRET": "$PROXMOX_TOKEN_SECRET", + "PROXMOX_VERIFY_SSL": "false" + } + } + ``` +- **Expected tool names**: `mcp_proxmox_list_nodes`, `mcp_proxmox_list_vms`, `mcp_proxmox_list_containers`, `mcp_proxmox_vm_status`, `mcp_proxmox_start_vm`, `mcp_proxmox_stop_vm`, `mcp_proxmox_create_snapshot`, `mcp_proxmox_list_storage` +- **Safety**: Phase 1 deploys with `PVEAuditor` role (read-only). Write operations (start/stop/snapshot) added in Phase 2 behind confirmation prompts. Restricted to `@ilia` profile only — never exposed to `@family`. + +--- + +### S4. Web Fetch / Scraping MCP + +| Field | Detail | +|---|---| +| **Upstream** | `github.com/TheSethRose/Fetch-Browser` (TypeScript, headless Chromium, MIT) | +| **Alt candidate** | `github.com/odgrim/mcp-fetch` (TypeScript, Puppeteer, simpler) | +| **Transport** | Stdio via `node` | +| **Auth** | None — no API keys required | +| **Complexity** | **Low** — clone, `npm install`, run; headless Chromium bundled by Puppeteer/Playwright | +| **Augments** | Built-in `web_fetch` tool (which does basic HTTP GET without JS rendering) | +| **Target agents** | All three — `@ilia`, `@family`, `@wife` | + +#### User stories + +- **US-W1**: As `@ilia`, I can say "fetch the Proxmox release notes page and summarize what's new" and the agent renders the JS-heavy page and extracts content. +- **US-W2**: As `@family`, I can say "get the lunch menu from the school website" and the agent scrapes the dynamically loaded content. +- **US-W3**: As `@ilia`, I can say "grab the pricing table from this SaaS page" and get structured data back. +- **US-W4**: As `@wife`, I can say "find me the best-rated recipe for lasagna" and the agent fetches and summarizes real recipe pages. + +#### Technical notes + +- **Build**: `npm install` in cloned repo. +- **Local clone path**: `mcp-servers/fetch-browser/` +- **Config entry**: + ```jsonc + "web_scraper": { + "command": "node", + "args": ["./mcp-servers/fetch-browser/dist/index.js"], + "env": {} + } + ``` +- **Expected tool names**: `mcp_web_scraper_fetch_url`, `mcp_web_scraper_search_google`, `mcp_web_scraper_screenshot` +- **Resource note**: Headless Chromium uses ~200–400 MB RAM per instance. Consider setting a process timeout or pool limit. +- **Safety**: Read-only by nature. No write side-effects. Safe for both `@ilia` and `@family`. + +--- + +## Backlog — Later + +Items below are future candidates, not yet scheduled. Grouped by domain. Each includes a candidate upstream project where one exists. + +### Family / Life + +| # | Integration | Upstream candidate | Notes | +|---|---|---|---| +| B-F1 | **CalDAV MCP** | `github.com/dominik1001/caldav-mcp` (Python, v0.4.0) | Universal calendar protocol. Enables Nextcloud, iCloud, ownCloud calendars. Useful if family moves off Google. | +| B-F2 | **Shared Todo / Household Tasks MCP** | `github.com/thijs-hakkenberg/mcp_todo` (Python, git-backed) | Git-backed collaborative task list with assignees, due dates, priorities, Kanban web UI, and Telegram bot. Good fit for family chores and grocery lists. | +| B-F3 | **Microsoft To Do MCP** | `github.com/akkilesh-a/microsoft-todo-mcp-server-self-hosted` (TypeScript) | Self-hosted HTTP transport. 15 tools for full task CRUD. Only relevant if family adopts Microsoft ecosystem. | +| B-F4 | **Home Assistant MCP** | TBD (community projects emerging) | Smart home control — lights, thermostat, locks, sensors. Requires Home Assistant instance on LAN. | +| B-F5 | **Shared Documents MCP** | TBD (Nextcloud WebDAV or Google Drive MCP) | Access family shared documents, photos, notes from chat. | + +### Research + +| # | Integration | Upstream candidate | Notes | +|---|---|---|---| +| B-R1 | **PDF RAG MCP** | `github.com/wesleygriffin/pdfrag` (Python, ChromaDB + sentence-transformers) | Semantic search over PDF papers. OCR support for scanned docs. Persistent vector index. | +| B-R2 | **Knowledge Base / Notes RAG MCP** | `github.com/alejandro-ao/RAG-MCP` (Python, FastMCP + ChromaDB) | Ingest markdown notes, docs, slides. Query with natural language. Supports LlamaParse for multi-format ETL. | +| B-R3 | **Zotero / Reference Manager MCP** | TBD | If user manages academic references in Zotero. Would expose library search, citation export, PDF retrieval. | +| B-R4 | **Arxiv / Semantic Scholar MCP** | TBD (API wrappers exist) | Direct paper search and metadata retrieval from academic APIs. | + +### Dev / Infra + +| # | Integration | Upstream candidate | Notes | +|---|---|---|---| +| B-D1 | **Filesystem MCP** | `github.com/mark3labs/mcp-filesystem-server` (Go, 622 stars) | Richer file ops than nanobot built-in (search, diff, metadata, copy trees). Useful for workspace automation. | +| B-D2 | **Docker / Portainer MCP** | `github.com/AI-Engineerings-at/homelab-mcp-bundle` (includes Portainer) | Container lifecycle, image management, compose operations. | +| B-D3 | **CI/CD Pipeline MCP** | TBD (Gitea Actions API or Drone) | Query pipeline status, trigger builds, view logs. Partially achievable through Gitea MCP's API. | +| B-D4 | **Logs & Monitoring MCP** | `github.com/AI-Engineerings-at/homelab-mcp-bundle` (includes Grafana, Uptime Kuma) | Query Grafana dashboards, check uptime monitors, search Loki logs. | +| B-D5 | **Backup Status MCP** | TBD (Proxmox Backup Server API or restic wrapper) | Check last backup timestamps, success/failure, storage usage. Could be a thin wrapper skill rather than full MCP. | +| B-D6 | **Database MCP** | TBD (PostgreSQL / SQLite MCP servers exist) | Run read-only queries against app databases for debugging and reporting. | + +--- + +## Skill Catalog + +Skills are higher-level task patterns that compose one or more tools (built-in or MCP) into a reusable workflow. Each skill lives as a `SKILL.md` in `nanobot/skills//` and is loaded by the skills system. + +Because agents are **separate containers with separate workspaces**, a skill is available to an agent only if (a) the skill file is present in that workspace's `skills/` dir or in the shared bundled skills, and (b) the MCP servers it depends on are configured in that agent's `config.json`. + +### Legend + +| Column | Meaning | +|---|---| +| **Skill** | Natural-language trigger name | +| **Description** | What the skill does | +| **MCP deps** | Which MCP servers must be connected in the agent's config | +| **Built-in deps** | Which nanobot built-in tools are also needed | +| **Target agents** | Which agent containers should have this skill deployed (`@ilia`, `@family`, `@wife`) | +| **Safety tier** | `read-only` / `write-confirm` (mutates after user confirmation) / `admin` (restricted + confirm) | + +--- + +### Scheduling Skills + +| Skill | Description | MCP deps | Built-in deps | Target agents | Safety tier | +|---|---|---|---|---|---| +| **Plan my week** | List events across all calendars for the next 7 days, highlight conflicts, suggest time blocks for focus work | Google Calendar MCP | — | `@ilia`, `@family`, `@wife` | read-only | +| **Reschedule meeting** | Find a specific event, propose 3 alternative conflict-free times, update the event after user picks one | Google Calendar MCP | — | `@ilia`, `@wife` | write-confirm | +| **Find conflict-free times** | Query free/busy across calendars for a given duration and date range, return available slots | Google Calendar MCP | — | `@ilia`, `@family`, `@wife` | read-only | + +### Email Skills + +| Skill | Description | MCP deps | Built-in deps | Target agents | Safety tier | +|---|---|---|---|---|---| +| **Triage inbox** | Fetch unread emails, categorize by urgency (action-required / FYI / low-priority), surface top action items | Gmail MCP | `read_emails` | `@ilia`, `@wife` | read-only | +| **Draft replies** | For each action-required email, generate a draft reply. Present drafts for user approval before sending | Gmail MCP | — | `@ilia`, `@wife` | write-confirm | +| **Summarize today's mail** | Produce a concise digest of all emails received today, grouped by sender or topic | Gmail MCP | `read_emails` | `@ilia`, `@family`, `@wife` | read-only | + +### Research Skills + +| Skill | Description | MCP deps | Built-in deps | Target agents | Safety tier | +|---|---|---|---|---|---| +| **Find relevant papers** | Web-search for papers on a given topic, fetch top results, return title + abstract + URL for each | Web Fetch MCP | `web_search` | `@ilia` | read-only | +| **Summarize URL/PDF** | Fetch a URL (with JS rendering if needed) or read a local PDF, produce a structured summary | Web Fetch MCP | `read_file` | `@ilia`, `@family`, `@wife` | read-only | +| **Generate experiment checklist** | Given a goal description, produce a structured checklist of steps, tools needed, and success criteria | — | — | `@ilia` | read-only | + +### Infra Skills + +| Skill | Description | MCP deps | Built-in deps | Target agents | Safety tier | +|---|---|---|---|---|---| +| **Show VM status** | List all VMs/containers across Proxmox nodes with state, CPU%, RAM%, and uptime | Proxmox MCP | — | `@ilia` | read-only | +| **Restart non-critical service** | Stop and start a VM by name, but only if it is tagged `non-critical`. Refuse if tagged `critical`. Requires confirmation | Proxmox MCP | — | `@ilia` | admin | +| **Summarize cluster resources** | Aggregate CPU, RAM, and storage usage across all Proxmox nodes, flag any node above 80% utilization | Proxmox MCP | — | `@ilia` | read-only | +| **Pre-upgrade snapshot** | Before a maintenance window, create a named snapshot of specified VMs. Requires confirmation | Proxmox MCP | — | `@ilia` | admin | + +### Dev Skills + +| Skill | Description | MCP deps | Built-in deps | Target agents | Safety tier | +|---|---|---|---|---|---| +| **Summarize open PRs** | List all open PRs on the nanobot repo with title, author, age, review status, and CI state | Gitea MCP | — | `@ilia` | read-only | +| **Triage Gitea issues** | Fetch open issues, group by label, suggest priority ordering based on age and activity | Gitea MCP | — | `@ilia` | read-only | +| **Search codebase** | Search Gitea-hosted code for a symbol or string pattern, return matching files and line numbers | Gitea MCP | — | `@ilia` | read-only | +| **Create issue from chat** | Turn a conversation excerpt into a well-formatted Gitea issue with title, description, and labels. Requires confirmation | Gitea MCP | — | `@ilia` | write-confirm | + +--- + +## Phase 1 Priorities + +These are the items we commit to implementing first, chosen for maximum daily value with manageable complexity. + +### Phase 1 MCP Integrations + +| Priority | MCP Server | Rationale | +|---|---|---| +| **P1** | **Gitea MCP** | Directly replaces fragile curl-based Gitea access scattered across `AGENTS.md` and the `gitea` skill. Token and network route already exist. Aligns with daily dev workflow — PRs, issues, code search are used every day. | +| **P2** | **Google Calendar MCP** | Complements the existing built-in `calendar` tool with multi-calendar views and free/busy queries. OAuth is already a solved pattern from Gmail MCP. Deployed to all three agents — `@ilia` (work calendar), `@family` (shared family calendar), `@wife` (personal calendar). | +| **P3** | **Proxmox MCP** | Homelab infrastructure is checked frequently but currently requires opening the Proxmox web UI. Starting with read-only (`PVEAuditor`) makes it safe to deploy immediately. Write ops follow in a later phase. | + +### Phase 1 Skills + +| Priority | Skill | MCP dep | Agents | Safety | Why first | +|---|---|---|---|---|---| +| **S1** | Summarize open PRs | Gitea MCP | `@ilia` | read-only | Used daily; validates Gitea MCP end-to-end | +| **S2** | Plan my week | Google Calendar MCP | `@ilia`, `@family`, `@wife` | read-only | High value for every agent; validates Calendar MCP | +| **S3** | Triage inbox | Gmail MCP (already live) | `@ilia`, `@wife` | read-only | Formalizes an existing ad-hoc pattern; no new MCP needed | +| **S4** | Show VM status | Proxmox MCP | `@ilia` | read-only | Safe first infra skill; validates Proxmox MCP | +| **S5** | Summarize today's mail | Gmail MCP (already live) | `@ilia`, `@family`, `@wife` | read-only | Daily value for all agents; no new MCP needed | + +--- + +## Implementation Notes + +### Local clone workflow + +```bash +# One-time setup +mkdir -p mcp-servers && cd mcp-servers + +# Gitea MCP (Go) +git clone https://gitea.com/gitea/gitea-mcp.git +cd gitea-mcp && go build -o gitea-mcp . && cd .. + +# Google Calendar MCP (TypeScript) +git clone https://github.com/nspady/google-calendar-mcp.git +cd google-calendar-mcp && npm install && npm run build && cd .. + +# Proxmox MCP (Python) +git clone https://github.com/antonio-mello-ai/mcp-proxmox.git +cd mcp-proxmox && pip install -e . && cd .. + +# Fetch Browser (TypeScript) +git clone https://github.com/TheSethRose/Fetch-Browser.git fetch-browser +cd fetch-browser && npm install && npm run build && cd .. +``` + +To update a server: `cd mcp-servers/ && git pull && `. Pin to a known-good commit with `git checkout ` for production stability. + +### Per-agent MCP wiring + +Since each agent is a separate Docker container, MCP servers are configured in each agent's own `config.json`. An agent only gets the MCP servers listed in its config -- no routing needed. + +**`~/.nanobot-user1/config.json`** (@ilia — all MCP servers): + +```jsonc +{ + "tools": { + "mcpServers": { + "gmail_mcp": { "command": "npx", "args": ["-y", "@gongrzhe/server-gmail-autoauth-mcp"] }, + "gitea": { "command": "./mcp-servers/gitea-mcp/gitea-mcp", "args": [], "env": { "GITEA_URL": "http://10.0.30.169:3000", "GITEA_TOKEN": "$NANOBOT_GITLE_TOKEN" } }, + "google_calendar": { "command": "node", "args": ["./mcp-servers/google-calendar-mcp/dist/index.js"], "env": { "GOOGLE_OAUTH_CREDENTIALS": "~/.gmail-mcp/gcp-oauth.keys.json" } }, + "proxmox": { "command": "python", "args": ["-m", "mcp_proxmox"], "env": { "PROXMOX_HOST": "https://10.0.30.1:8006", "PROXMOX_TOKEN_ID": "nanobot@pam!mcp-token", "PROXMOX_TOKEN_SECRET": "$PROXMOX_TOKEN_SECRET", "PROXMOX_VERIFY_SSL": "false" } }, + "web_scraper": { "command": "node", "args": ["./mcp-servers/fetch-browser/dist/index.js"], "env": {} } + } + } +} +``` + +**`~/.nanobot-user2/config.json`** (@family — scheduling + web only, no dev/infra): + +```jsonc +{ + "tools": { + "mcpServers": { + "google_calendar": { "command": "node", "args": ["./mcp-servers/google-calendar-mcp/dist/index.js"], "env": { "GOOGLE_OAUTH_CREDENTIALS": "~/.gmail-mcp/gcp-oauth.keys.json" } }, + "web_scraper": { "command": "node", "args": ["./mcp-servers/fetch-browser/dist/index.js"], "env": {} } + } + } +} +``` + +**`~/.nanobot-user3/config.json`** (@wife — email + calendar + web, no dev/infra): + +```jsonc +{ + "tools": { + "mcpServers": { + "gmail_mcp": { "command": "npx", "args": ["-y", "@gongrzhe/server-gmail-autoauth-mcp"] }, + "google_calendar": { "command": "node", "args": ["./mcp-servers/google-calendar-mcp/dist/index.js"], "env": { "GOOGLE_OAUTH_CREDENTIALS": "~/.gmail-mcp/gcp-oauth.keys.json" } }, + "web_scraper": { "command": "node", "args": ["./mcp-servers/fetch-browser/dist/index.js"], "env": {} } + } + } +} +``` + +**MCP server allocation summary:** + +| MCP Server | `@ilia` | `@family` | `@wife` | +|---|---|---|---| +| Gmail MCP | yes | -- | yes | +| Gitea MCP | yes | -- | -- | +| Google Calendar MCP | yes | yes | yes | +| Proxmox MCP | yes | -- | -- | +| Web Fetch MCP | yes | yes | yes | + +Key points: +- `@family` and `@wife` never see Gitea or Proxmox tools -- those MCP servers are simply absent from their configs. +- `@family` has no email MCP (it's a shared household bot, not tied to one inbox). It still has the built-in `calendar` and `web` tools. +- Each container spawns its own MCP server processes via stdio from the shared `mcp-servers/` directory (mounted read-only into all containers). + +### Safety tiers + +| Tier | Behavior | Implementation | +|---|---|---| +| **read-only** | Tool executes immediately, no confirmation prompt | Default for query/list/search operations | +| **write-confirm** | Agent presents a summary of what it will do, waits for user "yes" before executing | Enforced in SKILL.md instructions: "Before calling `create_event`, show the user the details and ask for confirmation" | +| **admin** | Same as write-confirm but tool is only available in the `@ilia` container | Enforced by omitting the MCP server from other agents' `config.json` + SKILL.md confirmation instructions | + +With separate containers, the strongest security boundary is **not configuring an MCP server at all** in an agent's config. Proxmox and Gitea are never in `@family` or `@wife` configs, so those agents physically cannot call those tools. + +Phase 1 deploys **only read-only skills**. Write skills (draft replies, reschedule meeting, create issue, restart VM) are Phase 2 once we validate the read paths. + +### Skill file template + +New skills follow the existing format in `nanobot/skills/`: + +```markdown +--- +name: summarize-open-prs +description: "List and summarize all open pull requests on the nanobot Gitea repo." +metadata: {"nanobot":{"emoji":"📋","requires":{"mcp":["gitea"]}}} +--- + +# Summarize Open PRs + +## When to use +User asks about open PRs, pending reviews, or code review status. + +## Steps +1. Call `mcp_gitea_list_pulls` with state=open. +2. For each PR, extract: title, author, created date, review status, CI status. +3. Format as a numbered list sorted by age (oldest first). +4. Highlight PRs with no reviews or failing CI. + +## Safety +Read-only. No confirmation needed. +``` + +### Docker considerations + +All three containers (`nanobot-user1`, `nanobot-user2`, `nanobot-user3`) share the same Docker image. MCP server processes are spawned inside each container as needed. The Dockerfile must include: +- **Go** (for Gitea MCP binary — or copy pre-built binary) +- **Node.js 18+** (for Calendar MCP and Fetch Browser) +- **Python pip deps** (for Proxmox MCP — install into the same venv or a sidecar) +- **Chromium** (for Fetch Browser headless rendering — `npx puppeteer browsers install chrome` or use Playwright) + +The `mcp-servers/` directory is mounted read-only into all containers so each agent can spawn the MCP servers listed in its config. Alternatively, build MCP binaries in a multi-stage Docker build and copy only the artifacts into the image. + +**Volume mounts (per container)** — compose services remain `nanobot-user1` / `user2` / `user3`; they map to `@ilia` / `@family` / `@wife` workspaces. + +```yaml +nanobot-user1: # @ilia + volumes: + - ~/.nanobot-user1:/root/.nanobot + - ~/.nanobot/workspaces/ilia:/workspace + # Optional: ./mcp-servers:/app/mcp-servers:ro + +nanobot-user2: # @family + volumes: + - ~/.nanobot-user2:/root/.nanobot + - ~/.nanobot/workspaces/family:/workspace + +nanobot-user3: # @wife + volumes: + - ~/.nanobot-user3:/root/.nanobot + - ~/.nanobot/workspaces/wife:/workspace +``` + +### Rollout sequence + +``` +Week 1: Clone repos, build locally, verify each MCP server starts and lists tools +Week 2: Wire Gitea MCP + "Summarize open PRs" skill, validate end-to-end +Week 3: Wire Calendar MCP + "Plan my week" skill, formalize "Triage inbox" skill +Week 4: Wire Proxmox MCP (read-only) + "Show VM status" skill +Week 5: Add "Summarize today's mail" skill, integrate Web Fetch MCP +Week 6: Retrospective, update this document, plan Phase 2 write-skills +``` + +--- + +## Changelog + +| Date | Change | +|---|---| +| 2026-03-30 | Updated to reflect multi-container workspace architecture (Option B). Added `@wife` as third agent. Rewrote per-agent MCP wiring with separate config.json per container. Updated skill assignments across all three agents. | +| 2026-03-30 | Initial version — shortlist (4 MCP), backlog (16 ideas), skill catalog (16 skills), Phase 1 defined (3 MCP + 5 skills) | diff --git a/scripts/init-agent-workspaces.sh b/scripts/init-agent-workspaces.sh new file mode 100755 index 0000000..853cb5d --- /dev/null +++ b/scripts/init-agent-workspaces.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +# Create ~/.nanobot/workspaces/{ilia,family,wife}/ from repo templates (Option B). +# Does not overwrite existing files — safe to re-run. + +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +NANOBOT_HOME="${NANOBOT_HOME:-$HOME/.nanobot}" +DEST="${NANOBOT_HOME}/workspaces" +SKEL="${REPO_ROOT}/agent_workspaces" + +if [[ ! -d "${SKEL}/ilia" ]]; then + echo "error: missing ${SKEL}/ilia — run from nanobot repo root" >&2 + exit 1 +fi + +install_skel() { + local agent="$1" + local d="${DEST}/${agent}" + mkdir -p "${d}/memory" + for f in AGENTS.md USER.md SOUL.md; do + if [[ ! -f "${d}/${f}" ]]; then + cp "${SKEL}/${agent}/${f}" "${d}/${f}" + echo "created ${d}/${f}" + else + echo "skip (exists): ${d}/${f}" + fi + done + for f in MEMORY.md HISTORY.md; do + if [[ ! -f "${d}/memory/${f}" ]]; then + cp "${SKEL}/${agent}/memory/${f}" "${d}/memory/${f}" + echo "created ${d}/memory/${f}" + else + echo "skip (exists): ${d}/memory/${f}" + fi + done +} + +echo "NANOBOT_HOME=${NANOBOT_HOME}" +echo "DEST=${DEST}" +mkdir -p "${DEST}" + +for agent in ilia family wife; do + echo "--- ${agent} ---" + install_skel "${agent}" +done + +echo "done. Fix ownership if needed, e.g.:" +echo " sudo chown -R \"\$(whoami):\$(whoami)\" \"${DEST}\""