From 5e94dd7c1754120d0003ca4a92ac9f1ee611f818 Mon Sep 17 00:00:00 2001 From: Philipp Wenger Lebron Date: Fri, 25 Sep 2026 10:56:21 +0200 Subject: [PATCH 1/3] fix: correctness bugs + Claude Code drift since June - proactive: weekly review ran Saturday on macOS (launchd Weekday 6); now 5 - proactive run-task.sh + telegram poll.sh: extend PATH so launchd/cron find claude - agent template renamed to _agent-template.md.template so it no longer registers as a live agent - agents: model sonnet -> inherit (follow the session model); README documents the Pro-limits swap back - integrations: bump pins (playwright 0.0.82, scrapling-fetch 0.2.4, time 2026.8.18, exa 3.4.1); drop mcp-server-fetch (WebFetch covers it); drop the empty google category, point at Claude's connectors - install-integrations: create .mcp.json only when a server is added; fix the `claude mcp list` hint - install: native installer first (curl -fsSL https://claude.ai/install.sh | bash), npm as fallback - docs URLs -> code.claude.com / platform.claude.com; model section rewritten (default is Opus 5.5) - SessionStart matcher covers clear and fork - add-agent + customization guide: routing lives in CONSTITUTION.md, _orchestrator-reference is not a registry Co-Authored-By: Claude Opus 5.5 (1M context) --- ...emplate.md => _agent-template.md.template} | 2 +- .claude/agents/analyst.md | 2 +- .claude/agents/comms-meetings.md | 2 +- .claude/agents/crm-relationships.md | 2 +- .claude/agents/daily-copilot.md | 2 +- .claude/agents/first-principles.md | 2 +- .claude/agents/principal-engineer.md | 2 +- .claude/agents/second-brain.md | 2 +- .claude/agents/strategy-advisor.md | 2 +- .claude/settings.json | 2 +- .claude/skills/add-agent/SKILL.md | 6 +-- CONTRIBUTING.md | 2 +- README.md | 32 +++++------ docs/customization-guide.md | 4 +- docs/onboarding.md | 5 +- modules/integrations/README.md | 54 +++++++++---------- modules/proactive/install.sh | 2 +- modules/proactive/run-task.sh | 5 ++ modules/telegram/poll.sh | 4 ++ scripts/init.sh | 2 +- scripts/install-integrations.sh | 48 +++++++++-------- scripts/integrations-catalog.json | 23 +++----- setup.sh | 2 +- 23 files changed, 104 insertions(+), 105 deletions(-) rename .claude/agents/{_agent-template.md => _agent-template.md.template} (99%) diff --git a/.claude/agents/_agent-template.md b/.claude/agents/_agent-template.md.template similarity index 99% rename from .claude/agents/_agent-template.md rename to .claude/agents/_agent-template.md.template index 96998ff..5745e87 100644 --- a/.claude/agents/_agent-template.md +++ b/.claude/agents/_agent-template.md.template @@ -2,7 +2,7 @@ name: agent-name description: One or two sentences. State WHEN to use this agent, not just what it is. The main session routes by matching a request against this text, so name the triggers, domains, and verbs that should land here. Note what this agent is NOT for, to keep routing clean. tools: Read, Glob, Grep -model: sonnet +model: inherit --- # Agent Name diff --git a/.claude/agents/analyst.md b/.claude/agents/analyst.md index 4f72bc7..b0359a3 100644 --- a/.claude/agents/analyst.md +++ b/.claude/agents/analyst.md @@ -2,7 +2,7 @@ name: analyst description: Metrics, KPIs, and data-backed analysis. Use for pipeline and revenue figures, dashboards, forecasts, trend and cohort analysis, variance investigation, and any question that needs numbers decomposed and compared rather than guessed. tools: Read, Write, Edit, Glob, Grep, Bash, WebFetch, WebSearch -model: sonnet +model: inherit --- # Analyst diff --git a/.claude/agents/comms-meetings.md b/.claude/agents/comms-meetings.md index 2f0da0e..79c11ef 100644 --- a/.claude/agents/comms-meetings.md +++ b/.claude/agents/comms-meetings.md @@ -2,7 +2,7 @@ name: comms-meetings description: Email triage, meeting preparation, daily and weekly briefings, communication drafting, and follow-up tracking. Use for "what's in my inbox", "prep me for my meeting with X", "draft a reply to Y", "what am I waiting on", and "who owes me a response". tools: Read, Write, Edit, Glob, Grep, Bash, WebSearch -model: sonnet +model: inherit --- # Comms & Meetings diff --git a/.claude/agents/crm-relationships.md b/.claude/agents/crm-relationships.md index cf308cf..8cca401 100644 --- a/.claude/agents/crm-relationships.md +++ b/.claude/agents/crm-relationships.md @@ -2,7 +2,7 @@ name: crm-relationships description: Contacts, pipeline, and relationship tracking. Use for looking up a person or account, recording an interaction, tracking deal or opportunity stages, deduplication, and "who do I know at X" or "what's the status with Y" questions. Works from vault files by default; uses a CRM integration if one is enabled. tools: Read, Write, Edit, Glob, Grep, Bash -model: sonnet +model: inherit --- # CRM & Relationships diff --git a/.claude/agents/daily-copilot.md b/.claude/agents/daily-copilot.md index 2808aee..2f8f9df 100644 --- a/.claude/agents/daily-copilot.md +++ b/.claude/agents/daily-copilot.md @@ -2,7 +2,7 @@ name: daily-copilot description: The everyday work partner. Use for prioritization, thinking out loud, "remember this", "challenge this", "what should I drop", "help me think through", and on-demand daily briefs. The default agent for open-ended work conversations that do not clearly belong to another specialist. tools: Read, Write, Edit, Glob, Grep, Bash, WebSearch -model: sonnet +model: inherit --- # Daily Copilot diff --git a/.claude/agents/first-principles.md b/.claude/agents/first-principles.md index 54b4f58..22bbf02 100644 --- a/.claude/agents/first-principles.md +++ b/.claude/agents/first-principles.md @@ -2,7 +2,7 @@ name: first-principles description: Rigorous abstract reasoning. Use to reason a problem from scratch, decompose a concept to its fundamentals, calibrate a prediction, run scenario analysis under deep uncertainty, or synthesize opposing views. For thinking about which framework even applies, not applying a known one to a decision. tools: Read, Glob, Grep, WebFetch, WebSearch -model: sonnet +model: inherit --- # First Principles diff --git a/.claude/agents/principal-engineer.md b/.claude/agents/principal-engineer.md index d592ac7..536daee 100644 --- a/.claude/agents/principal-engineer.md +++ b/.claude/agents/principal-engineer.md @@ -2,7 +2,7 @@ name: principal-engineer description: Senior engineering voice for technical decisions. Use to design a system, review code or architecture, choose between technologies, assess technical debt, or do a security review. Reasons against modern best practices. Advice only; produces recommendations, not commits. tools: Read, Glob, Grep, Bash, WebFetch, WebSearch -model: sonnet +model: inherit --- # Principal Engineer diff --git a/.claude/agents/second-brain.md b/.claude/agents/second-brain.md index 86fc352..fed9e70 100644 --- a/.claude/agents/second-brain.md +++ b/.claude/agents/second-brain.md @@ -2,7 +2,7 @@ name: second-brain description: Knowledge base management. Use for taking notes, creating and retrieving meeting records, research synthesis, finding what was discussed with a person or on a topic, and keeping the vault healthy (wikilinks, frontmatter, archival). Read-only by default; writes happen on explicit intent. tools: Read, Write, Edit, Glob, Grep, Bash -model: sonnet +model: inherit --- # Second Brain diff --git a/.claude/agents/strategy-advisor.md b/.claude/agents/strategy-advisor.md index a8634db..2ade169 100644 --- a/.claude/agents/strategy-advisor.md +++ b/.claude/agents/strategy-advisor.md @@ -2,7 +2,7 @@ name: strategy-advisor description: Strategic sparring partner. Use when bringing a plan, idea, or decision for critique, or for pre-mortems, steelmans, second-order analysis, and "should I do X". Challenges assumptions and surfaces what is being missed. Advice only; does not execute, write files, or send anything. tools: Read, Glob, Grep, WebFetch, WebSearch -model: sonnet +model: inherit --- # Strategy Advisor diff --git a/.claude/settings.json b/.claude/settings.json index 83a907f..9e40b68 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -16,7 +16,7 @@ "hooks": { "SessionStart": [ { - "matcher": "startup|resume|compact", + "matcher": "startup|resume|clear|compact|fork", "hooks": [ { "type": "command", "command": "bash \"$CLAUDE_PROJECT_DIR/hooks/hook-session-start.sh\"" } ] diff --git a/.claude/skills/add-agent/SKILL.md b/.claude/skills/add-agent/SKILL.md index 5a32277..5e30337 100644 --- a/.claude/skills/add-agent/SKILL.md +++ b/.claude/skills/add-agent/SKILL.md @@ -13,10 +13,10 @@ Create a new agent so the assistant grows with the user's work. This is the main - **Name** — kebab-case, e.g. `recruiting` or `product-research`. - **Purpose and triggers** — what it owns, and the phrases or domains that should route to it. This becomes the `description`, which is what actually drives routing. - **Tools** — the minimum it needs (4 to 6). Advice-only agents get read tools only: `Read, Glob, Grep, WebFetch, WebSearch`. - - **Model** — `sonnet` is the default; `opus` for genuinely hard reasoning. + - **Model**: `inherit` is the default (the agent follows the session's model); pin `sonnet` for a cheap, high-volume agent or `opus` for one that always needs hard reasoning. 2. **Check for overlap.** Read the existing agents in `.claude/agents/`. If the new agent overlaps an existing one, say so and suggest either editing the existing agent or sharpening both descriptions so routing stays clean. -3. **Scaffold.** Copy `.claude/agents/_agent-template.md` to `.claude/agents/.md` and fill it in: frontmatter, Purpose, When to use / when not to, Output format, Safety. -4. **Register it.** Add a one-line row for the new agent to `.claude/agents/_orchestrator-reference.md` and to the routing section of `CONSTITUTION.md`. +3. **Scaffold.** Copy `.claude/agents/_agent-template.md.template` to `.claude/agents/.md` and fill it in: frontmatter, Purpose, When to use / when not to, Output format, Safety. +4. **Register it.** Add a one-line routing rule for the new agent to the Agent Architecture & Routing section of `CONSTITUTION.md`. (`_orchestrator-reference.md` describes coordination patterns; it is not a list of agents.) 5. **Test routing.** Suggest two or three prompts that should land on the new agent, and one that should *not* (to confirm it does not over-trigger). ## Principles diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 27435fc..bcc49c1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,7 +6,7 @@ This kit is built to be changed. "Contributing" here means two things: making it The kit assumes you will edit it. Nothing here is sacred. -- **Agents**: edit any file in `.claude/agents/`. Add a new one with the `/add-agent` skill, or copy `.claude/agents/_agent-template.md`. +- **Agents**: edit any file in `.claude/agents/`. Add a new one with the `/add-agent` skill, or copy `.claude/agents/_agent-template.md.template`. - **Skills**: each directory in `.claude/skills/` is one slash command. Copy an existing one to make a new workflow. - **Hooks**: `hooks/` holds safety guards and lifecycle scripts. They are wired in `.claude/settings.json`. Add, remove, or disable any of them. - **Constitution**: `CONSTITUTION.md` is the behavior contract. Change it to match how you actually work. diff --git a/README.md b/README.md index e0326e1..17c8f25 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [![GitHub stars](https://img.shields.io/github/stars/chacosoldier/compabob?style=social)](https://github.com/chacosoldier/compabob/stargazers) [![Last commit](https://img.shields.io/github/last-commit/chacosoldier/compabob)](https://github.com/chacosoldier/compabob/commits/main) -**A customizable [Claude Code](https://docs.anthropic.com/en/docs/claude-code) setup for knowledge workers.** Clone it, run `./setup.sh`, name your assistant, and in about ten minutes you have a working AI work partner: it knows your role, remembers what you tell it, routes work to specialized agents, and never sends anything without your sign-off. +**A customizable [Claude Code](https://code.claude.com/docs) setup for knowledge workers.** Clone it, run `./setup.sh`, name your assistant, and in about ten minutes you have a working AI work partner: it knows your role, remembers what you tell it, routes work to specialized agents, and never sends anything without your sign-off. ## Why @@ -51,7 +51,7 @@ cd compabob ./setup.sh ``` -Then run `claude`. You need the [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code), `git`, `bash`, `python3` (3.10+), and a paid Claude plan. Optional but recommended: [VS Code](https://code.visualstudio.com) with the Claude Code extension (see [Recommended setup](#recommended-setup) below). Optional: [Obsidian](https://obsidian.md) to browse the knowledge base as a graph. Never used a terminal? Use the walkthrough below instead. +Then run `claude`. You need the [Claude Code CLI](https://code.claude.com/docs), `git`, `bash`, `python3` (3.10+), and a paid Claude plan. Optional but recommended: [VS Code](https://code.visualstudio.com) with the Claude Code extension (see [Recommended setup](#recommended-setup) below). Optional: [Obsidian](https://obsidian.md) to browse the knowledge base as a graph. Never used a terminal? Use the walkthrough below instead. ## Recommended setup @@ -82,21 +82,21 @@ Homebrew installs the other tools for you. Paste this one line and press Enter: It asks for your Mac password (as you type it, nothing appears on screen, that is normal) and takes a few minutes. When it finishes it prints a short "Next steps" block with two lines to run. Paste and run those two lines too: they add Homebrew to your path. -### 3. Install git, Node, and Python +### 3. Install git, Python, and Node ```bash -brew install git node python +brew install git python node ``` -The three tools Compabob is built on. One command, a few minutes. +git and Python run the kit. Node runs some of the optional integrations. One command, a few minutes. ### 4. Install Claude Code ```bash -npm install -g @anthropic-ai/claude-code +curl -fsSL https://claude.ai/install.sh | bash ``` -This is the assistant itself. If anything goes wrong here, the official install guide is at [docs.anthropic.com/en/docs/claude-code](https://docs.anthropic.com/en/docs/claude-code). +This is the assistant itself, installed with Anthropic's native installer, which keeps itself up to date. If you prefer npm, `npm install -g @anthropic-ai/claude-code` also works (it needs Node 22 or newer). If anything goes wrong here, the official install guide is at [code.claude.com/docs](https://code.claude.com/docs/en/setup). ### 5. Download Compabob @@ -131,7 +131,7 @@ Run `bash scripts/init.sh`. It checks your setup and tells you, in plain languag ### Linux and Windows -- **Linux**: skip Homebrew. Install the tools with your package manager, for example `sudo apt install git nodejs npm python3`, then do steps 4 to 7. +- **Linux**: skip Homebrew. Install the tools with your package manager, for example `sudo apt install git python3 curl` (add `nodejs npm` if you want the optional integrations), then do steps 4 to 7. ### If something looks wrong on Linux @@ -162,15 +162,17 @@ That second-brain loop is the core value. Once it feels useful, expand from ther ## Choosing the Claude model -Compabob does not pin a model. It uses whatever Claude Code is configured to use — by default that is Sonnet, which is the right balance for most work. +Compabob follows whatever model Claude Code is set to. Every agent uses `model: inherit`, so when you switch models, the agents switch with you. Claude Code's default today is Opus 5.5 on every paid plan. Three ways to change it: -- **Per session** — type `/model` inside Claude Code and pick from the list, or start the CLI with a flag: `claude --model sonnet`. -- **Per project** — add a `"model"` field to `.claude/settings.json` (alongside `outputStyle`). Aliases `"sonnet"`, `"opus"`, `"haiku"` are accepted, or paste a full model ID such as `"claude-sonnet-4-6"`. -- **Globally** — set the `ANTHROPIC_MODEL` environment variable. +- **Per session**: type `/model` inside Claude Code and pick from the list, or start the CLI with a flag: `claude --model sonnet`. +- **Per project**: add a `"model"` field to `.claude/settings.json` (alongside `outputStyle`). Aliases such as `"sonnet"`, `"opus"`, `"haiku"` are accepted, or paste a full model ID such as `"claude-sonnet-5"`. +- **Globally**: set the `ANTHROPIC_MODEL` environment variable. -Current model IDs are listed in the [Anthropic docs](https://docs.anthropic.com/en/docs/about-claude/models). If a model is retired, the CLI falls back to the closest current one and prints a warning. +**On a Pro plan**, the larger model uses up your limits faster, and agents that inherit it do too. If you hit limits, pin the agents to a smaller model: change `model: inherit` to `model: sonnet` in the frontmatter of the files in `.claude/agents/`. The scheduled modules (`proactive`, `telegram`, `linkedin-outreach`) already run on Sonnet to keep unattended runs cheap. + +Current model IDs are listed in the [model overview](https://platform.claude.com/docs/en/models/overview). ## What is inside @@ -236,7 +238,7 @@ The core runs with zero external services. Modules add capability when you want |--------|--------|--------------| | `proactive` | Available | Scheduled automation: a morning brief and weekly review on a timer | | `telegram` | Available | A Telegram bot: inbound messages drafted for your approval, never auto-sent | -| `integrations` | Available | MCP tools: browser automation, web search, Gmail, Calendar, utilities | +| `integrations` | Available | MCP tools: browser automation, web search, utilities. Gmail and Calendar via Claude's own connectors | | `linkedin-outreach` | Available | Drafts one LinkedIn connection-invitation card a day from a queue, for your review and manual send | | `crm-merge` | Available | Folds Google + LinkedIn + vault contacts into one local CRM (SQLite + offline HTML browser), deduped across sources. No creds | | `lead-pipeline` | Available | Builds a ranked outbound list: discover, clean, dedup against your CRM, enrich, score. One CSV per stage | @@ -253,7 +255,7 @@ Found a bug or a rough edge? [Open an issue](https://github.com/chacosoldier/com ## Lineage -This kit is distilled from a personal Claude Code setup the author refined over a year of daily use: the constitution, the agent fleet, the safety hooks, the memory system. The personal-life domains were stripped out and the patterns generalized into a clean starting point, and the result is what you are reading. Credit to the [Claude Code](https://docs.anthropic.com/en/docs/claude-code) team for the harness this builds on. +This kit is distilled from a personal Claude Code setup the author refined over a year of daily use: the constitution, the agent fleet, the safety hooks, the memory system. The personal-life domains were stripped out and the patterns generalized into a clean starting point, and the result is what you are reading. Credit to the [Claude Code](https://code.claude.com/docs) team for the harness this builds on. ## Author diff --git a/docs/customization-guide.md b/docs/customization-guide.md index fc5a34d..69ed23c 100644 --- a/docs/customization-guide.md +++ b/docs/customization-guide.md @@ -32,7 +32,7 @@ The shipped seeds (`vault.example/`, `memory.example/`, `config/user.config.yaml Each file in `.claude/agents/` is one agent. -**To add one:** run the `/add-agent` skill, or copy `.claude/agents/_agent-template.md`. Then register it in `CONSTITUTION.md` (routing rules) and `.claude/agents/_orchestrator-reference.md`. +**To add one:** run the `/add-agent` skill, or copy `.claude/agents/_agent-template.md.template`. Then add its routing line to `CONSTITUTION.md` (Agent Architecture & Routing). `_orchestrator-reference.md` is not a registry: it describes how the main session coordinates agents, not which ones exist. **To edit one:** just edit its file. The `description` is the most important line: it decides what routes to the agent. Write it as "use for X, Y, Z" and name what it is *not* for. @@ -71,7 +71,7 @@ The core agents work from files. To give one a live data source, the pattern is This is also how an MCP server would slot in: the agent calls it, the agent's description and instructions tell it when. Keep tool descriptions specific, schemas strict, and the tool count per agent low. -For ready-made MCP servers, the kit ships an installer: `bash scripts/install-integrations.sh` wires browser automation, web search, Gmail, Calendar, and utility servers into `.mcp.json` for you. See [`modules/integrations/README.md`](../modules/integrations/README.md). Use the installer for those; use the CLI-client pattern above when you need a bespoke connector. +For ready-made MCP servers, the kit ships an installer: `bash scripts/install-integrations.sh` wires browser automation, web search, and utility servers into `.mcp.json` for you. Gmail and Calendar come from Claude's own connectors, see the module README. See [`modules/integrations/README.md`](../modules/integrations/README.md). Use the installer for those; use the CLI-client pattern above when you need a bespoke connector. ## Personas diff --git a/docs/onboarding.md b/docs/onboarding.md index cab5401..ad385de 100644 --- a/docs/onboarding.md +++ b/docs/onboarding.md @@ -100,8 +100,9 @@ Also in the first week: ## When you outgrow the core - **Add an agent** for a part of your work the core does not cover: run `/add-agent`. -- **Connect external tools** (browser automation, web search, Gmail, Calendar): - run `bash scripts/install-integrations.sh`. See +- **Connect external tools** (browser automation, web search): run + `bash scripts/install-integrations.sh`. For Gmail and Calendar, enable + Claude's own connectors. See [modules/integrations/README.md](../modules/integrations/README.md). - **Turn on scheduled briefs**: enable the [`proactive`](../modules/README.md) module. - **Chat with the assistant from your phone**: enable the diff --git a/modules/integrations/README.md b/modules/integrations/README.md index 045b173..43727b8 100644 --- a/modules/integrations/README.md +++ b/modules/integrations/README.md @@ -16,18 +16,20 @@ It asks which categories you want, then configures them. `setup.sh` also offers to run it at the end of first-time setup. Re-run it any time to add more; it never removes or overwrites a server you already have. -## The four categories +## The three categories | Category | Servers | Key needed? | |----------|---------|-------------| | `web` | Playwright (browser automation), scrapling-fetch (stealth web fetch) | No | -| `utility` | time (timezone and date math), fetch (basic web fetch) | No | -| `search` | exa (semantic web search) | Yes — an API key | -| `google` | Gmail, Google Calendar | Yes — OAuth | +| `utility` | time (timezone and date math) | No | +| `search` | exa (semantic web search) | Yes, an API key | + +Plain web fetching needs no server: Claude Code's built-in WebFetch covers it. +Gmail and Calendar are not in the installer; see [Google Workspace](#google-workspace). **Keyless** categories (`web`, `utility`) are configured completely by the -installer: it writes the `.mcp.json` entry and you are done. **Keyed** categories -(`search`, `google`) are configured as far as the installer can, then it points +installer: it writes the `.mcp.json` entry and you are done. The **keyed** +category (`search`) is configured as far as the installer can, then it points you here to finish the credential step. Nothing is downloaded during install. An MCP server fetches itself, at the @@ -57,41 +59,33 @@ install the runtime the server just works. ## Google Workspace -Gmail and Calendar are the most involved integration, because Google requires -an OAuth client. Budget 15-20 minutes the first time. - -1. In the [Google Cloud Console](https://console.cloud.google.com), create a - project, enable the Gmail API and the Google Calendar API, and create an - **OAuth 2.0 Client ID** (application type: Desktop app). -2. Pick a Google Workspace MCP server. The MCP ecosystem moves quickly, so - rather than pin one here, search the [MCP server directory](https://modelcontextprotocol.io) - for a current, well-maintained Gmail / Google Workspace server and follow its - own setup instructions. -3. That server will ask for your OAuth client credentials and run a one-time - browser consent flow. Put any secrets in `.env` (git-ignored), never in a - committed file. -4. Add the server to `.mcp.json` under `mcpServers`, following the format the - other entries use. - -Because this one is provider-specific and changes often, it is deliberately a -guided manual step rather than an automated install. +The simplest path for Gmail and Google Calendar is Anthropic's own connectors, +not a self-hosted MCP server. Enable them at [claude.ai](https://claude.ai) under +Settings, then Connectors. Claude Code sessions signed in with the same account +pick them up, with no OAuth client of your own to maintain. + +Two limits: connectors ask you to re-authorize in the browser from time to time, +and they are not available to headless runs (the `proactive` module), so a +scheduled brief works from your local files instead. ## Verify -Inside a Claude Code session in the kit directory: +In a terminal, in the kit directory: -```text +```bash claude mcp list ``` -It lists the servers Claude Code loaded from `.mcp.json`. A keyless server -should appear; a keyed one appears once its credential is in place. +It lists the servers from `.mcp.json`. Until you start `claude` in this folder +and approve the project's MCP servers, they show as pending. Inside a session, +`/mcp` shows the same list with live status. A keyed server connects once its +credential is in place. ## How agents use integrations The core agents check for a relevant integration and use it when present: -`comms-meetings` will use Gmail and Calendar, `analyst` and `second-brain` will -use web search and fetch. Without an integration they work from local files and +`comms-meetings` will use Gmail and Calendar connectors, `analyst` and +`second-brain` will use web search. Without an integration they work from local files and say which live source is missing. Enabling one is purely additive. ## Disable diff --git a/modules/proactive/install.sh b/modules/proactive/install.sh index 1e48760..a4e4984 100755 --- a/modules/proactive/install.sh +++ b/modules/proactive/install.sh @@ -47,7 +47,7 @@ EOF echo " generated $plist" } make_plist morning-brief 7 0 - make_plist weekly-review 17 0 6 # Weekday 6 = Friday + make_plist weekly-review 17 0 5 # launchd Weekday: 0 or 7 = Sunday, so 5 = Friday echo echo "To activate, run:" echo " cp $GEN_DIR/*.plist ~/Library/LaunchAgents/" diff --git a/modules/proactive/run-task.sh b/modules/proactive/run-task.sh index 86de8b2..f06dd6d 100755 --- a/modules/proactive/run-task.sh +++ b/modules/proactive/run-task.sh @@ -6,6 +6,11 @@ set -euo pipefail PROJECT_DIR="$(cd "$(dirname "$0")/../.." && pwd)" + +# launchd and cron run with a minimal PATH; put common tool dirs on it so the +# claude and python3 binaries are found when this runs unattended. +export PATH="/opt/homebrew/bin:/usr/local/bin:$HOME/.local/bin:$HOME/.npm-global/bin:$PATH" + TASK="${1:-}" if [ -z "$TASK" ]; then echo "usage: run-task.sh " >&2 diff --git a/modules/telegram/poll.sh b/modules/telegram/poll.sh index fa6f64b..d79bc68 100755 --- a/modules/telegram/poll.sh +++ b/modules/telegram/poll.sh @@ -13,6 +13,10 @@ MODULE_DIR="$(cd "$(dirname "$0")" && pwd)" PROJECT_DIR="$(cd "$MODULE_DIR/../.." && pwd)" cd "$PROJECT_DIR" +# launchd and cron run with a minimal PATH; put common tool dirs on it so the +# claude and python3 binaries are found when this runs unattended. +export PATH="/opt/homebrew/bin:/usr/local/bin:$HOME/.local/bin:$HOME/.npm-global/bin:$PATH" + # --- secrets --------------------------------------------------------------- [ -f .env ] && { set -a; . ./.env; set +a; } TOKEN="${TELEGRAM_BOT_TOKEN:-}" diff --git a/scripts/init.sh b/scripts/init.sh index 8ca66d3..47797ce 100755 --- a/scripts/init.sh +++ b/scripts/init.sh @@ -17,7 +17,7 @@ printf "%s\n\n" "$(date '+%Y-%m-%d %H:%M')" # 1. Tooling command -v python3 >/dev/null 2>&1 && ok "python3 ($(python3 --version 2>&1))" || fail "python3 not found" -command -v claude >/dev/null 2>&1 && ok "claude CLI found" || warn "claude CLI not found (npm install -g @anthropic-ai/claude-code)" +command -v claude >/dev/null 2>&1 && ok "claude CLI found" || warn "claude CLI not found (install: curl -fsSL https://claude.ai/install.sh | bash)" # 2. Setup has run — your personal files exist if [ -d vault ] && [ -d memory ] && [ -f config/user.config.yaml ]; then diff --git a/scripts/install-integrations.sh b/scripts/install-integrations.sh index f940db8..52c2c74 100755 --- a/scripts/install-integrations.sh +++ b/scripts/install-integrations.sh @@ -2,8 +2,8 @@ # Compabob — integration installer. # # Adds MCP servers to .mcp.json from scripts/integrations-catalog.json. -# Keyless integrations (web, utility) are configured fully. Keyed ones (search, -# google) are configured as far as possible and the remaining key/OAuth step is +# Keyless integrations (web, utility) are configured fully. The keyed one +# (search) is configured as far as possible and the remaining key step is # printed for you to finish. Nothing is downloaded here: MCP servers fetch # themselves the first time Claude Code uses them. # @@ -27,7 +27,7 @@ warn() { printf ' \033[1;33mwarn\033[0m %s\n' "$1"; } if [ "${1:-}" = "--help" ] || [ "${1:-}" = "-h" ]; then bold "Compabob — integration installer" - echo "Adds MCP servers to .mcp.json. Categories: web, utility, search, google." + echo "Adds MCP servers to .mcp.json. Categories: web, utility, search." echo " install-integrations.sh web utility configure those categories" echo " install-integrations.sh interactive picker" exit 0 @@ -53,9 +53,8 @@ else case "$ans" in [Yy]*) CHOSEN+=("$1");; esac } ask_cat web "Playwright + scrapling-fetch (no key)" - ask_cat utility "time + fetch (no key)" + ask_cat utility "time and timezone math (no key)" ask_cat search "exa web search (needs an API key)" - ask_cat google "Gmail + Calendar (needs OAuth)" echo fi @@ -64,12 +63,6 @@ if [ "${#CHOSEN[@]}" -eq 0 ]; then exit 0 fi -# --- ensure .mcp.json exists (only now that we know we'll touch it) ------- -if [ ! -f "$MCP_FILE" ]; then - if [ -f "$MCP_SEED" ]; then cp "$MCP_SEED" "$MCP_FILE"; else printf '{\n "mcpServers": {}\n}\n' > "$MCP_FILE"; fi - ok "created $MCP_FILE" -fi - # --- preflight: runtimes (warn only, never block) ------------------------- command -v npx >/dev/null 2>&1 && ok "npx found" \ || warn "npx not found — install Node.js; npx-based servers stay registered but will not run until you do" @@ -78,16 +71,24 @@ command -v uvx >/dev/null 2>&1 && ok "uvx found" \ echo # --- merge .mcp.json + report (Python does the JSON work) ----------------- -python3 - "$CATALOG" "$MCP_FILE" "$CONFIG" "${CHOSEN[@]}" <<'PYEOF' -import json, sys +# .mcp.json is created only when at least one server is actually added, so an +# unknown category or an all-skipped run never leaves an empty file behind. +python3 - "$CATALOG" "$MCP_FILE" "$MCP_SEED" "$CONFIG" "${CHOSEN[@]}" <<'PYEOF' +import json, os, sys -catalog_path, mcp_path, config_path, *chosen = sys.argv[1:] +catalog_path, mcp_path, seed_path, config_path, *chosen = sys.argv[1:] GREEN, YELLOW, BOLD, NC = "\033[0;32m", "\033[1;33m", "\033[1m", "\033[0m" catalog = json.load(open(catalog_path)) cats = {c["id"]: c for c in catalog["categories"]} -mcp = json.load(open(mcp_path)) +existed = os.path.exists(mcp_path) +if existed: + mcp = json.load(open(mcp_path)) +elif os.path.exists(seed_path): + mcp = json.load(open(seed_path)) +else: + mcp = {} mcp.setdefault("mcpServers", {}) added, skipped, guided, unknown = [], [], [], [] @@ -107,9 +108,12 @@ for cid in chosen: # idempotent write: deterministic output, so a re-run with the same choices # produces a byte-identical file. -with open(mcp_path, "w") as f: - json.dump(mcp, f, indent=2) - f.write("\n") +if added: + with open(mcp_path, "w") as f: + json.dump(mcp, f, indent=2) + f.write("\n") + if not existed: + print(f" {GREEN}ok{NC} created {mcp_path}") for name in added: print(f" {GREEN}ok{NC} added MCP server: {name}") for name in skipped: print(f" {GREEN}ok{NC} already present, kept: {name}") @@ -117,11 +121,10 @@ for cid in unknown: print(f" {YELLOW}warn{NC} unknown category (ignored): {cid # If nothing was added, skipped, OR queued for guided setup (e.g. every # requested category was unknown), nothing actually happened. Fail loud so the -# user notices instead of seeing a misleading "Done.". Note: `google` only -# guides, so a google-only selection still counts as a real action. +# user notices instead of seeing a misleading "Done.". if not added and not skipped and not guided: print() - print(f" {YELLOW}warn{NC} nothing was installed — valid categories are: web | utility | search | google") + print(f" {YELLOW}warn{NC} nothing was installed. Valid categories are: web | utility | search") sys.exit(1) if guided: @@ -153,5 +156,6 @@ PYEOF echo bold "Done." -echo "Review .mcp.json, then inside a Claude Code session run: claude mcp list" +echo "Review .mcp.json, then in a terminal in this folder run: claude mcp list" +echo "(Servers show as pending until you start claude here and approve the project's MCP servers.)" echo "Re-run any time to add more: bash scripts/install-integrations.sh" diff --git a/scripts/integrations-catalog.json b/scripts/integrations-catalog.json index 8c959f3..6b544b3 100644 --- a/scripts/integrations-catalog.json +++ b/scripts/integrations-catalog.json @@ -1,5 +1,5 @@ { - "_comment": "Source of truth for the integration picker used by scripts/install-integrations.sh. Versions are pinned on purpose: bump them here deliberately, never use @latest, so a clone is reproducible and an upstream release cannot break a stranger's first run. Versions verified against npm and PyPI on 2026-05-20.", + "_comment": "Source of truth for the integration picker used by scripts/install-integrations.sh. Versions are pinned on purpose: bump them here deliberately, never use @latest, so a clone is reproducible and an upstream release cannot break a stranger's first run. Versions verified against npm and PyPI on 2026-09-25.", "categories": [ { "id": "web", @@ -9,27 +9,23 @@ "servers": { "playwright": { "command": "npx", - "args": ["-y", "@playwright/mcp@0.0.75"] + "args": ["-y", "@playwright/mcp@0.0.82"] }, "scrapling-fetch": { "command": "uvx", - "args": ["scrapling-fetch-mcp@0.2.2"] + "args": ["scrapling-fetch-mcp@0.2.4"] } } }, { "id": "utility", - "label": "Utility (time, fetch)", + "label": "Utility (time and timezone math)", "requires_keys": false, "needs": ["uvx"], "servers": { "time": { "command": "uvx", - "args": ["mcp-server-time@2026.1.26"] - }, - "fetch": { - "command": "uvx", - "args": ["mcp-server-fetch@2025.4.7"] + "args": ["mcp-server-time@2026.8.18"] } } }, @@ -43,17 +39,10 @@ "servers": { "exa": { "command": "npx", - "args": ["-y", "exa-mcp-server@3.2.1"], + "args": ["-y", "exa-mcp-server@3.4.1"], "env": { "EXA_API_KEY": "${EXA_API_KEY}" } } } - }, - { - "id": "google", - "label": "Google Workspace (Gmail, Calendar, needs OAuth)", - "requires_keys": true, - "doc_anchor": "google-workspace", - "servers": {} } ] } diff --git a/setup.sh b/setup.sh index d61c00b..931fdff 100755 --- a/setup.sh +++ b/setup.sh @@ -21,7 +21,7 @@ echo command -v python3 >/dev/null 2>&1 && ok "python3 found" || warn "python3 not found — install Python 3.10+" command -v git >/dev/null 2>&1 && ok "git found" || warn "git not found" command -v claude >/dev/null 2>&1 && ok "claude CLI found" \ - || warn "claude CLI not found — install: npm install -g @anthropic-ai/claude-code" + || warn "claude CLI not found. Install: curl -fsSL https://claude.ai/install.sh | bash" echo # --- 2. Prompt ------------------------------------------------------------- From 0f45cc12448fd31470c0aedd94c57dee082bb1f9 Mon Sep 17 00:00:00 2001 From: Philipp Wenger Lebron Date: Fri, 25 Sep 2026 10:59:46 +0200 Subject: [PATCH 2/3] refactor: simplify the kit (remove dead parts, shared shell lib, config migration) - remove the tool-scope guard (hook + checker + yaml): it was never wired in settings.json, needed jq, and matched no context the kit sets; an unexecuted security control is worse than none because it is believed - remove the README-only stub modules (extra-agents, team, whatsapp); their rationale moves to a "Not built" section in modules/README.md - scripts/lib/common.sh: shared ok/warn/bold/say + set_config_flag - scripts/migrate-config.sh: adds new config keys inside their block, never changes a user value, writes a .bak; update.sh and init.sh call it - config template: crm_merge + lead_pipeline flags, six personas, drop dead preferences.timezone and the stub flags - update.sh: clear message for a local-only branch or detached HEAD - init.sh: Python 3.10 check, agent count excludes template/reference files - transcribe + dictation: module venvs (PEP 668); record.py re-execs into its venv - doc drift: persona count, "config/ is yours" wording, integrations and telegram claims, Spanish README scope, Windows section, SECURITY.md, leftover references in mcp-debug and memory-debt Co-Authored-By: Claude Opus 5.5 (1M context) --- .claude/skills/mcp-debug/SKILL.md | 2 +- .claude/skills/memory-debt/SKILL.md | 2 +- .github/SECURITY.md | 2 +- CHANGELOG.md | 11 --- CLAUDE.md | 2 +- README.md | 12 ++- config/user.config.yaml.template | 19 ++-- docs/architecture.md | 2 +- docs/customization-guide.md | 4 +- docs/i18n/README.es.md | 2 +- docs/onboarding.md | 6 +- hooks/hook-tool-scope-guard.sh | 87 ----------------- hooks/tool_scope_check.py | 66 ------------- hooks/tool_scopes.yaml | 140 ---------------------------- modules/README.md | 11 ++- modules/dictation/README.md | 2 +- modules/dictation/install.sh | 6 +- modules/extra-agents/README.md | 24 ----- modules/team/README.md | 23 ----- modules/telegram/README.md | 4 +- modules/transcribe/README.md | 15 ++- modules/transcribe/record.py | 15 ++- modules/whatsapp/README.md | 33 ------- scripts/init.sh | 19 +++- scripts/install-integrations.sh | 37 +++----- scripts/lib/common.sh | 32 +++++++ scripts/lib/personas.sh | 4 +- scripts/migrate-config.sh | 80 ++++++++++++++++ setup.sh | 7 +- update.sh | 41 +++++--- 30 files changed, 240 insertions(+), 470 deletions(-) delete mode 100755 hooks/hook-tool-scope-guard.sh delete mode 100755 hooks/tool_scope_check.py delete mode 100644 hooks/tool_scopes.yaml delete mode 100644 modules/extra-agents/README.md delete mode 100644 modules/team/README.md delete mode 100644 modules/whatsapp/README.md create mode 100755 scripts/lib/common.sh create mode 100755 scripts/migrate-config.sh diff --git a/.claude/skills/mcp-debug/SKILL.md b/.claude/skills/mcp-debug/SKILL.md index bc7e0e2..2df9c90 100644 --- a/.claude/skills/mcp-debug/SKILL.md +++ b/.claude/skills/mcp-debug/SKILL.md @@ -101,7 +101,7 @@ Status: 1. Run the status check (Mode 1) for the full server list. -2. If tool-usage telemetry exists (some projects log to `data/performance/session-log.jsonl` or similar), summarise the last 7 days: +2. If you keep tool-usage telemetry (this kit does not ship any), summarise the last 7 days: - Tools called per server. - Error rate per server. - Most-used and least-used servers. diff --git a/.claude/skills/memory-debt/SKILL.md b/.claude/skills/memory-debt/SKILL.md index accb8ab..99d86f5 100644 --- a/.claude/skills/memory-debt/SKILL.md +++ b/.claude/skills/memory-debt/SKILL.md @@ -82,7 +82,7 @@ Final summary: X applied, Y skipped, Z marked obsolete, W silent fixes. - **Never auto-apply.** Always present each item for approval before modifying any file. - **Read before writing.** If the content already exists, mark APPLIED silently — never duplicate. -- **MEMORY.md line cap.** If the constitution sets a limit (commonly ~200 lines), and writing the proposal would breach it, suggest a topic file in `memory/topics/` and a one-line index pointer instead of inlining. +- **MEMORY.md size cap.** MEMORY.md loads every session, so keep it under about 12KB. If writing the proposal would push it past that, suggest a topic file in `memory/topics/` and a one-line index pointer instead of inlining. - **Diary edits are minimal.** Only flip checkboxes and update Memory Debt table status. Never rewrite proposal text. - **Oldest first.** Process chronologically so the longest-overdue debt clears first. diff --git a/.github/SECURITY.md b/.github/SECURITY.md index 7cb7ab6..0b2f956 100644 --- a/.github/SECURITY.md +++ b/.github/SECURITY.md @@ -4,7 +4,7 @@ Compabob runs entirely on your own machine, under your own Claude Code subscript ## Supported version -Only the latest `main` is supported. The kit is a rolling template, not a versioned product. If you found an issue, check it still reproduces on the current `main` before reporting. +Releases are tagged (see the CHANGELOG), but only the latest `main` is supported: fixes land there, not on old tags. If you found an issue, check it still reproduces on the current `main` before reporting. ## Reporting a vulnerability diff --git a/CHANGELOG.md b/CHANGELOG.md index f7cde59..3b3aeac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,17 +52,6 @@ All notable changes to Compabob are recorded here. Format follows as PENDING / STALE / OBSOLETE / APPLIED, then in `apply` mode walks through approvals one by one. Closes the loop between `/reflect` (proposes) and `memory/`. -- **Tool-scope-guard hook** (`hooks/hook-tool-scope-guard.sh` + - `tool_scope_check.py` + `tool_scopes.yaml`) — a context-scoped PreToolUse - guard. Reasoning stays unconstrained; only the outward action surface - (sends, mutations, external writes) is bounded per execution context. - Pre-wired contexts: `interactive` (allow-all), `pulse` (overnight headless, - no sends), `morning-briefing` (read inboxes, write vault), `email-triage` - (label only, no send), `news-digest` (fetch + write only). Context is - selected via `$CLAUDE_CONTEXT` env var or `/tmp/claude-context-` - file. Fail-open by design; blocks are logged to - `data/performance/tool-scope-blocks.jsonl`. Opt-in: add the hook entry to - `.claude/settings.json` under `PreToolUse` to activate. ### Documented diff --git a/CLAUDE.md b/CLAUDE.md index e9bc449..fbe5d92 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,4 +10,4 @@ At the start of a session, read `memory/MEMORY.md`. It records the user's name, ## Working directory -The knowledge base is `vault/`. Persistent facts are in `memory/`. Your own configuration is in `.claude/` and `config/`. The directories `vault/`, `memory/`, and `config/` are the user's own data and live outside the kit's version control. Everything else is the kit, and updates upstream. +The knowledge base is `vault/`. Persistent facts are in `memory/`. Your own configuration is in `.claude/` and `config/`. `vault/`, `memory/`, and `config/user.config.yaml` are the user's own data and live outside the kit's version control. Everything else is the kit, and updates upstream. diff --git a/README.md b/README.md index 17c8f25..07dc37a 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,7 @@ From here on, every command runs inside this `compabob` folder. If you open a fr ./setup.sh ``` -It asks about you (assistant name, your name, role, working language), then about your work (which of five persona presets best fits you, and one sentence about what you focus on), then offers an optional integrations step you can skip and run later. Seven prompts in total. About a minute. +It asks about you (assistant name, your name, role, working language), then about your work (which of six persona presets best fits you, and one sentence about what you focus on), then offers an optional integrations step you can skip and run later. Seven prompts in total. About a minute. ### 7. Start your assistant @@ -140,7 +140,10 @@ Run `bash scripts/init.sh`. It checks your setup and tells you, in plain languag - **Arch**: `sudo pacman -S git nodejs npm python` works as-is. - **`python3` vs `python`**: confirm `python3 --version` returns 3.10+ before running `./setup.sh`. - **Permissions**: if `./setup.sh` says "Permission denied," run `chmod +x setup.sh` first. -- **Windows**: install [WSL](https://learn.microsoft.com/windows/wsl/install) first (in PowerShell: `wsl --install`, then restart). Open the Ubuntu terminal it gives you and follow the Linux steps. Native Windows (PowerShell/cmd) is not supported. + +### Windows + +- Install [WSL](https://learn.microsoft.com/windows/wsl/install) first (in PowerShell: `wsl --install`, then restart). Open the Ubuntu terminal it gives you and follow the Linux steps. Native Windows (PowerShell/cmd) is not supported. ## Updating @@ -244,10 +247,9 @@ The core runs with zero external services. Modules add capability when you want | `lead-pipeline` | Available | Builds a ranked outbound list: discover, clean, dedup against your CRM, enrich, score. One CSV per stage | | `memory-search` | Available | Real index over `memory/` and `vault/` so retrieval ranks by relevance (FTS5 by default; semantic via Ollama if installed) | | `transcribe` | Available — **highly encouraged** | One-hotkey local call recording + transcription (mlx-whisper). Captures both sides, copies to clipboard, files a transcript into `vault/raw/meetings/`. No cloud, no key, audio stays on your machine | -| `extra-agents` | Roadmap | An agent gallery: designer, evaluator, sales coach, project manager | -| `whatsapp` | Roadmap | WhatsApp channel — not built (account-ban risk); use Telegram instead | +| `dictation` | Available | Cleanup endpoint for your dictation app: fixes punctuation and fillers, and a glossary that learns the words it keeps getting wrong | -See [modules/README.md](modules/README.md) to enable one. +See [modules/README.md](modules/README.md) to enable one, and for what is deliberately not built yet (an agent gallery, team mode, WhatsApp). ## Feedback diff --git a/config/user.config.yaml.template b/config/user.config.yaml.template index 7bb0afe..9188ca5 100644 --- a/config/user.config.yaml.template +++ b/config/user.config.yaml.template @@ -1,8 +1,10 @@ # Your configuration. setup.sh copies this to config/user.config.yaml and fills # the placeholders. That file is git-ignored: it is yours. Edit it freely. +# ./update.sh adds any new module flags a kit update introduces (it never +# changes a value you set); run bash scripts/migrate-config.sh to do it by hand. # # YAML note: any `"` or `\` inside a name is escaped (e.g. `O\"Brien`, -# `path\\to\\thing`). That is standard YAML — the assistant reads the +# `path\\to\\thing`). That is standard YAML; the assistant reads the # unescaped value. You do not need to "fix" it. user: @@ -16,21 +18,18 @@ assistant: # The persona preset picked at setup. It only seeds role-and-priorities.md and # records your choice here; every agent stays available. See config/personas/. persona: - preset: "" # generalist | consultant | engineer | sales | founder + preset: "" # generalist | consultant | engineer | sales | founder | researcher # Opt-in modules. To enable one, set it to true and follow modules//README.md. # Setting a flag here does not install anything on its own; each module has its # own enable step. modules: proactive: false # scheduled automation (morning brief, weekly review) - telegram: false # Telegram bot — inbound messages drafted for your approval - integrations: false # MCP integrations (web, Google Workspace, search, utility) + telegram: false # Telegram bot: inbound messages drafted for your approval + integrations: false # MCP integrations (web + browser, search, utility) linkedin_outreach: false # daily LinkedIn outreach drafting (queue-driven, draft-first) - memory_search: false # semantic search over memory and the vault + crm_merge: false # one local CRM from Google + LinkedIn + vault contacts + lead_pipeline: false # ranked outbound lists, deduped against your CRM + memory_search: false # search index over memory and the vault (keyword; hybrid with Ollama) transcribe: false # local call recording + transcription (highly encouraged) dictation: false # dictation cleanup proxy + self-growing glossary - extra_agents: false # the extra-agents gallery - team: false # multi-user team mode - -preferences: - timezone: "" # e.g. "Europe/Berlin"; leave blank to use the system zone diff --git a/docs/architecture.md b/docs/architecture.md index e293f71..7634093 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -20,7 +20,7 @@ modules/ Opt-in extensions. A hard line runs through the directory tree, and it is what makes the kit safe to update: - **The kit** (version-controlled, updates from upstream): `CONSTITUTION.md`, `CLAUDE.md`, `.claude/agents/`, `.claude/skills/`, `.claude/output-styles/`, `hooks/`, `modules/`, `scripts/`, `docs/`, and the `*.example` seeds. -- **Your data** (git-ignored, never touched by an update): `vault/`, `memory/`, `config/`, and `.claude/settings.local.json`. `setup.sh` creates these from the `*.example` seeds on first run. +- **Your data** (git-ignored, never touched by an update): `vault/`, `memory/`, `config/user.config.yaml`, `.mcp.json`, `.env`, and `.claude/settings.local.json`. `setup.sh` creates these from the `*.example` seeds on first run. Kit files contain no personal placeholders. `setup.sh` personalizes only the seed copies. `update.sh` pulls new kit versions and physically cannot reach your data, because your data is not in git. This separation is deliberate: it lets the kit improve over time without ever putting a user's knowledge base at risk. diff --git a/docs/customization-guide.md b/docs/customization-guide.md index 69ed23c..269d292 100644 --- a/docs/customization-guide.md +++ b/docs/customization-guide.md @@ -6,14 +6,14 @@ The kit is built to be changed. This is how. Nothing here is sacred; if a defaul Two kinds of thing live side by side, and the difference is what makes updates safe: -- **Your data** — `vault/`, `memory/`, and `config/` (plus `.claude/settings.local.json`). `setup.sh` creates these from the shipped `*.example` seeds. They are git-ignored: not part of the kit's version control, so a kit update cannot touch them. Your knowledge base, your assistant's memory, and your settings all live here. Customize them however you like. +- **Your data**: `vault/`, `memory/`, and `config/user.config.yaml` (plus `.mcp.json`, `.env`, and `.claude/settings.local.json`). `setup.sh` creates these from the shipped `*.example` seeds. They are git-ignored: not part of the kit's version control, so a kit update cannot touch them. Your knowledge base, your assistant's memory, and your settings all live here. Customize them however you like. - **The kit** — `CONSTITUTION.md`, `.claude/agents/`, `.claude/skills/`, `hooks/`, `modules/`, the scripts, the docs. Version-controlled, and updated from upstream when you run `./update.sh`. `setup.sh` personalizes only your data (it fills your name, role, and language into the seed copies). It never edits a kit file. That is what keeps the kit cleanly updatable. ## Updating -Run `./update.sh` to pull the latest kit. It sets aside any kit edits you have made, pulls, and re-applies them. Your `vault/`, `memory/`, and `config/` are git-ignored, so they are never at risk, whatever happens. +Run `./update.sh` to pull the latest kit. It sets aside any kit edits you have made, pulls, and re-applies them. Your `vault/`, `memory/`, and `config/user.config.yaml` are git-ignored, so they are never at risk, whatever happens. If the new kit adds a config key (a new module flag, say), `update.sh` adds it to your config with its default and leaves every value you set alone. - **Adding** your own agents, skills, or hooks never conflicts: upstream does not have those files. - **Editing a kit file** (an agent, the constitution) can produce a merge conflict on that one file when you update. That is normal git: `update.sh` names the file, you resolve it, done. Your data is untouched throughout. diff --git a/docs/i18n/README.es.md b/docs/i18n/README.es.md index 44b93c6..cc52e09 100644 --- a/docs/i18n/README.es.md +++ b/docs/i18n/README.es.md @@ -37,4 +37,4 @@ cd compabob ./setup.sh ``` -> Esta es una traducción parcial (las primeras seis secciones) del [README en inglés](../../README.md). Para la guía completa, incluyendo el tutorial paso a paso, las skills, los módulos y el resto, sigue el documento original. Traducción inicial aportada por [@MD-Mushfiqur123](https://github.com/MD-Mushfiqur123) en [#17](https://github.com/chacosoldier/compabob/pull/17). +> Esta es una traducción parcial de la introducción del [README en inglés](../../README.md); la sección de comparación ("How this differs") no está traducida. Para la guía completa, incluyendo el tutorial paso a paso, las skills, los módulos y el resto, sigue el documento original. Traducción inicial aportada por [@MD-Mushfiqur123](https://github.com/MD-Mushfiqur123) en [#17](https://github.com/chacosoldier/compabob/pull/17). diff --git a/docs/onboarding.md b/docs/onboarding.md index ad385de..ed9455b 100644 --- a/docs/onboarding.md +++ b/docs/onboarding.md @@ -27,11 +27,11 @@ cd compabob New to the terminal? The [README walkthrough](../README.md#full-walkthrough-no-terminal-experience-needed) covers installing everything from scratch. `setup.sh` asks for your assistant's name, your name, role, and language, and -which **persona** best fits your work (consultant, engineer, sales, founder, or -generalist). The persona pre-fills `memory/topics/role-and-priorities.md` with +which **persona** best fits your work (consultant, engineer, sales, founder, +researcher, or generalist). The persona pre-fills `memory/topics/role-and-priorities.md` with role-appropriate starter content so the assistant has something real to reason about on day one. It then creates your personal `vault/`, `memory/`, and -`config/` from the shipped seeds. +`config/user.config.yaml` from the shipped seeds. At the end it offers to set up integrations. You can skip that and do it later. diff --git a/hooks/hook-tool-scope-guard.sh b/hooks/hook-tool-scope-guard.sh deleted file mode 100755 index 950f195..0000000 --- a/hooks/hook-tool-scope-guard.sh +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/env bash -# PreToolUse hook: context-scoped action-surface guard. -# -# Checks each tool call against hooks/tool_scopes.yaml for the active context. -# Reasoning is unconstrained; only the action surface (sends, mutations, -# external writes) is bounded per context. -# -# Context detection order: -# 1. $CLAUDE_CONTEXT env var (set by cron scripts / orchestrators) -# 2. /tmp/claude-context- (written by skills at session start) -# 3. Default: "interactive" (allow_all — no behavior change) -# -# Fail-open: YAML parse errors, unknown contexts, or missing scopes file all -# fall through as allow. A broken guard must never block real work. -# -# Blocks are logged to data/performance/tool-scope-blocks.jsonl so operators -# can tighten the scopes file based on what was actually attempted. - -set -uo pipefail - -PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(cd "$(dirname "$0")/.." && pwd)}" -SCOPES_FILE="$PROJECT_DIR/hooks/tool_scopes.yaml" -CHECKER="$PROJECT_DIR/hooks/tool_scope_check.py" -LOG_FILE="$PROJECT_DIR/data/performance/tool-scope-blocks.jsonl" -TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ") - -INPUT=$(cat) -TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""' 2>/dev/null) -SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // "unknown"' 2>/dev/null) - -# No tool name — fail open. -[ -z "$TOOL_NAME" ] && exit 0 - -# Detect active context. -CONTEXT="${CLAUDE_CONTEXT:-}" -if [ -z "$CONTEXT" ] && [ -f "/tmp/claude-context-${SESSION_ID}" ]; then - CONTEXT=$(cat "/tmp/claude-context-${SESSION_ID}" 2>/dev/null | tr -d '[:space:]') -fi -CONTEXT="${CONTEXT:-interactive}" - -# Scopes file or checker missing — fail open. -[ ! -f "$SCOPES_FILE" ] && exit 0 -[ ! -f "$CHECKER" ] && exit 0 - -# Run checker. Prefer `uv run --script` (PEP-723 inline deps, no global install); -# fall back to plain `python3` if uv is not available and pyyaml is on the path. -if command -v uv >/dev/null 2>&1; then - RESULT=$(uv run --script "$CHECKER" "$TOOL_NAME" "$CONTEXT" "$SCOPES_FILE" 2>/dev/null) -else - RESULT=$(python3 "$CHECKER" "$TOOL_NAME" "$CONTEXT" "$SCOPES_FILE" 2>/dev/null) -fi - -# Parse verdict — default allow on any failure. -VERDICT=$(echo "$RESULT" | python3 -c \ - "import json,sys; print(json.load(sys.stdin).get('verdict','allow'))" 2>/dev/null \ - || echo "allow") -REASON=$(echo "$RESULT" | python3 -c \ - "import json,sys; print(json.load(sys.stdin).get('reason','parse_error'))" 2>/dev/null \ - || echo "parse_error") - -if [ "$VERDICT" = "deny" ]; then - mkdir -p "$(dirname "$LOG_FILE")" 2>/dev/null || true - printf '%s\n' \ - "{\"ts\":\"$TIMESTAMP\",\"session\":\"$SESSION_ID\",\"context\":\"$CONTEXT\",\"tool\":\"$TOOL_NAME\",\"reason\":\"$REASON\"}" \ - >> "$LOG_FILE" 2>/dev/null || true - - # Build a safe JSON message (python handles quoting). - MSG=$(python3 -c \ - "import json,sys; t,c=sys.argv[1],sys.argv[2]; print(json.dumps(f'Tool {t!r} is not permitted in context {c!r}. Edit hooks/tool_scopes.yaml to update the allowlist.'))" \ - "$TOOL_NAME" "$CONTEXT" 2>/dev/null \ - || echo '"Tool blocked by scope guard."') - - python3 -c " -import json, sys -msg = $MSG -print(json.dumps({ - 'hookSpecificOutput': { - 'hookEventName': 'PreToolUse', - 'permissionDecision': 'deny', - 'permissionDecisionReason': msg - } -})) -" 2>/dev/null - exit 0 -fi - -exit 0 diff --git a/hooks/tool_scope_check.py b/hooks/tool_scope_check.py deleted file mode 100755 index 4a69cf5..0000000 --- a/hooks/tool_scope_check.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python3 -# /// script -# requires-python = ">=3.9" -# dependencies = ["pyyaml"] -# /// -"""Check whether a tool is permitted under the active context. - -Usage: python3 tool_scope_check.py -Stdout: JSON {"verdict": "allow"|"deny", "reason": ""} -Exit: always 0 (callers check verdict, not exit code). - -Fail-open: if anything goes wrong (missing args, YAML parse error, unknown -context), the verdict is "allow" with an explanatory reason. A broken guard -must never block real work — operators inspect logs to tighten it later. -""" -import fnmatch -import json -import sys - - -def main() -> None: - if len(sys.argv) < 4: - print(json.dumps({"verdict": "allow", "reason": "missing_args"})) - return - - tool, context, scopes_path = sys.argv[1], sys.argv[2], sys.argv[3] - - try: - import yaml # noqa: PLC0415 — intentional late import (uv-injected dep) - with open(scopes_path) as f: - scopes = yaml.safe_load(f) - except Exception as e: - print(json.dumps({"verdict": "allow", "reason": f"yaml_error:{e}"})) - return - - contexts = scopes.get("contexts", {}) - - if context not in contexts: - print(json.dumps({"verdict": "allow", "reason": f"unknown_context:{context}"})) - return - - ctx = contexts[context] - policy = ctx.get("policy", "allow_all") - - if policy == "allow_all": - print(json.dumps({"verdict": "allow", "reason": "allow_all_context"})) - return - - blocked = ctx.get("blocked_tools") or [] - allowed = ctx.get("allowed_tools") or [] - - # Explicit block list wins over allowlist. - for pattern in blocked: - if fnmatch.fnmatch(tool, pattern): - print(json.dumps({"verdict": "deny", "reason": f"blocked:{pattern}"})) - return - - for pattern in allowed: - if fnmatch.fnmatch(tool, pattern): - print(json.dumps({"verdict": "allow", "reason": f"allowed:{pattern}"})) - return - - print(json.dumps({"verdict": "deny", "reason": "not_in_allowlist"})) - - -main() diff --git a/hooks/tool_scopes.yaml b/hooks/tool_scopes.yaml deleted file mode 100644 index f42e34d..0000000 --- a/hooks/tool_scopes.yaml +++ /dev/null @@ -1,140 +0,0 @@ -# tool_scopes.yaml — bound the action surface per execution context. -# -# Reasoning is unconstrained; only outward actions (sends, mutations, writes -# to shared systems) are bounded per context. Edit this file at admin time -# instead of changing hook code at runtime. -# -# Context detection (checked in order): -# 1. $CLAUDE_CONTEXT env var — set by cron / orchestrator before launch -# 2. /tmp/claude-context- — written by skills at session start -# 3. Default: "interactive" — fail-open; preserves unguarded behavior -# -# Policy types: -# allow_all — unrestricted (interactive default). -# allowlist — only tools matching an allowed_tools pattern pass. -# -# Tool patterns support fnmatch globs (e.g. `mcp__gmail__*`). -# `blocked_tools` is checked BEFORE `allowed_tools` (explicit deny wins). -# -# Tweak the contexts below to match the cron jobs and headless tasks you -# actually run. The examples are realistic starting points for a personal -# assistant kit: an overnight pulse, a morning briefing, an email triage, -# and a news digest. - -default_behavior: allow # fail-open if context unknown or YAML unreadable - -contexts: - - # ── Interactive ───────────────────────────────────────────────────────────── - interactive: - description: "Full interactive session with the user — no restrictions." - policy: allow_all - - # ── Overnight pulse / headless task processing ────────────────────────────── - pulse: - description: > - Headless overnight processing. Broad read access and vault writes for - staging. No outbound sends. No calendar or external state mutations. - policy: allowlist - allowed_tools: - # Core filesystem - - Read - - Write - - Edit - - Glob - - Grep - - Bash - # Sub-agents (each child inherits CLAUDE_CONTEXT). - - Agent - - Task - # Time - - mcp__time__get_current_time - - mcp__time__convert_time - # Mail — read-only - - mcp__*__search_threads - - mcp__*__get_thread - - mcp__*__list_labels - - mcp__*__list_drafts - # Calendar — read-only - - mcp__*__list_events - - mcp__*__get_event - - mcp__*__list_calendars - - mcp__*__suggest_time - # Web research - - WebFetch - - WebSearch - blocked_tools: - # No outbound mail under any circumstances. - - mcp__*__create_draft - - mcp__*__send_* - - mcp__*__label_* - - mcp__*__unlabel_* - # No calendar mutations. - - mcp__*__create_event - - mcp__*__update_event - - mcp__*__delete_event - - mcp__*__respond_to_event - - # ── Morning briefing ──────────────────────────────────────────────────────── - morning-briefing: - description: > - Read inboxes + calendar, write a briefing into the vault. No sends, - no calendar mutations, no external outbound. - policy: allowlist - allowed_tools: - - Read - - Write - - Edit - - Glob - - Grep - - Bash - - Agent - - mcp__time__get_current_time - - mcp__*__search_threads - - mcp__*__get_thread - - mcp__*__list_labels - - mcp__*__list_events - - mcp__*__get_event - - mcp__*__list_calendars - blocked_tools: - - mcp__*__create_draft - - mcp__*__send_* - - mcp__*__create_event - - mcp__*__update_event - - mcp__*__delete_event - - mcp__*__respond_to_event - - # ── Email triage ──────────────────────────────────────────────────────────── - email-triage: - description: > - Read, label, classify mail. Drafts allowed only via a separate review - flow — this context never sends. - policy: allowlist - allowed_tools: - - Read - - Write - - Bash - - mcp__*__search_threads - - mcp__*__get_thread - - mcp__*__list_labels - - mcp__*__label_message - - mcp__*__label_thread - - mcp__*__unlabel_message - - mcp__*__unlabel_thread - blocked_tools: - - mcp__*__create_draft - - mcp__*__send_* - - # ── News digest ───────────────────────────────────────────────────────────── - news-digest: - description: > - Fetch + summarise the news, write a digest to the vault. No outbound. - policy: allowlist - allowed_tools: - - Read - - Write - - Edit - - Bash - - WebFetch - - WebSearch - - mcp__time__get_current_time diff --git a/modules/README.md b/modules/README.md index d30cecf..eeb3643 100644 --- a/modules/README.md +++ b/modules/README.md @@ -13,9 +13,14 @@ The core kit runs with zero external services. Modules add capability when you w | [`memory-search/`](memory-search/) | **Available** | A search index over memory and the vault: ranked keyword search out of the box, semantic (search by meaning) with Ollama. Built by the `/index-memory` skill. | | [`transcribe/`](transcribe/) | **Available — highly encouraged** | One-hotkey local call recording + transcription (mlx-whisper). Captures both sides, copies the text to your clipboard, and files a markdown transcript into `vault/raw/meetings/`. No cloud, no API key, audio never leaves your machine. | | [`dictation/`](dictation/) | **Available** | A cleanup endpoint your dictation app (e.g. [Handy](https://github.com/cjpais/Handy)) posts to: it fixes punctuation/fillers and rewrites words it keeps getting wrong (your names, jargon) using a glossary that **grows itself** from recurring corrections. OpenAI-compatible upstream (Groq default, Ollama for fully local). | -| [`extra-agents/`](extra-agents/) | Roadmap | A gallery of additional agents (designer, evaluator, sales coach, project manager) to copy into `.claude/agents/`. | -| [`whatsapp/`](whatsapp/) | Roadmap | A WhatsApp channel. Not built: unofficial bridges risk an account ban. Use the Telegram module instead. | -| [`team/`](team/) | Deferred | Multi-user team mode: shared, department, and personal tiers. See its README for why it is not in this release. | + +## Not built (on purpose, for now) + +- **Extra agents gallery** (designer, evaluator, sales coach, project manager). The eight core agents cover the common shape of knowledge work; role-specific agents are one `/add-agent` away, so a gallery waits for demand. +- **Team mode** (shared, department, and personal tiers). It needs a permission model, per-user isolation, and governance, each worth real hardening. Until then, each person runs their own copy; a shared vault can be a separate, jointly-owned folder. +- **WhatsApp.** No official API for personal accounts, and unofficial bridges risk an account ban. Use the Telegram module; if you already have a WhatsApp Business Cloud API number, adapt `telegram/` as the reference. + +Vote or offer help on the [roadmap issue](https://github.com/chacosoldier/compabob/issues/12). ## How to enable a module diff --git a/modules/dictation/README.md b/modules/dictation/README.md index 0104a54..da8f511 100644 --- a/modules/dictation/README.md +++ b/modules/dictation/README.md @@ -33,7 +33,7 @@ module *is* that endpoint: ## Prerequisites ```bash -pip3 install pyyaml # the only dependency +python3 -m venv modules/dictation/.venv && modules/dictation/.venv/bin/pip install pyyaml # the only dependency ``` A local dictation app that supports an OpenAI-compatible post-processing diff --git a/modules/dictation/install.sh b/modules/dictation/install.sh index 1f3fbd2..2d61fee 100644 --- a/modules/dictation/install.sh +++ b/modules/dictation/install.sh @@ -17,9 +17,11 @@ PROXY="$MODULE_DIR/proxy.py" LEARN="$MODULE_DIR/learn.py" mkdir -p "$GEN_DIR" "$LOG_DIR" -PY="$(command -v python3 || true)" +# Prefer the module venv (see README), which holds pyyaml; fall back to python3. +PY="$MODULE_DIR/.venv/bin/python" +[ -x "$PY" ] || PY="$(command -v python3 || true)" if [ -z "$PY" ]; then - echo "python3 not found on PATH. Install it (and 'pip3 install pyyaml') first." + echo "python3 not found on PATH. Install it, then create the module venv (see README)." exit 1 fi diff --git a/modules/extra-agents/README.md b/modules/extra-agents/README.md deleted file mode 100644 index 34033dc..0000000 --- a/modules/extra-agents/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# Module: Extra Agents (roadmap) - -**Status: planned, not yet built.** - -## What it will add - -A gallery of additional agents to copy into `.claude/agents/` when your work calls for them: - -- **designer** — visual assets, decks, diagrams, simple graphics. -- **evaluator** — adversarial review: checks a draft, a report, or a plan before you act on it. -- **sales-coach** — deal strategy, objection handling, competitive framing. -- **project-manager** — planning, status tracking, dependency and risk surfacing. - -## How it will work - -Each agent will be a single `.md` file you copy into `.claude/agents/`, then register in `CONSTITUTION.md` and `_orchestrator-reference.md`. The `/add-agent` skill already does that registration for you. - -## Why it is not in this release - -The eight core agents cover the common shape of knowledge work. More agents are valuable but role-specific, and adding all of them by default would clutter routing for people who do not need them. A gallery you pull from on purpose is the right model. - -## Build your own meanwhile - -You do not need this module to add an agent. Run `/add-agent`, or copy `.claude/agents/_agent-template.md`. That is the intended way to make the kit yours. This gallery just saves you writing the common ones from scratch. diff --git a/modules/team/README.md b/modules/team/README.md deleted file mode 100644 index 8e85b17..0000000 --- a/modules/team/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Module: Team Mode (deferred) - -**Status: deferred to a future release.** - -## What it would add - -Multi-user mode: turn the single-user assistant into a setup a whole team or company can share, with three tiers of configuration: - -- **Shared** — agents, skills, and knowledge common to everyone. -- **Department** — scoped to a group, owned by that group's lead. -- **Personal** — each member's own agents and memory, private to them. - -Plus the governance that a shared setup needs: who can change what, how personal knowledge gets promoted to shared, and how each member's data stays isolated from the others. - -## Why it is deferred - -Team mode is a substantial addition. It needs a permission model, per-user isolation, and a governance process, and each of those deserves real hardening rather than a quick port. The priority for this release is to get the single-user experience genuinely right. A multi-user layer built on a weak single-user core would inherit every rough edge and multiply it. - -Shipping the solo kit well first, then adding team mode as its own considered release, is the deliberate sequence. - -## In the meantime - -The single-user kit is complete and useful on its own. If a team wants to adopt it now, the simplest path is for each person to run their own copy. A shared knowledge base can be a separate, jointly-owned vault that individual setups point at. diff --git a/modules/telegram/README.md b/modules/telegram/README.md index 9606acc..fd0c20f 100644 --- a/modules/telegram/README.md +++ b/modules/telegram/README.md @@ -63,8 +63,8 @@ opt-in on purpose. After activating, set `telegram: true` in bash modules/telegram/send.sh draft--.md ``` -If you ask the assistant in a session to send a Telegram reply, the `comms-guard` -hook blocks the send until you explicitly approve it. That is intended. +The assistant cannot send for you: the `comms-guard` hook blocks any `send.sh` +call made from a session. Sending is always the command above, run by you. ## Cost note diff --git a/modules/transcribe/README.md b/modules/transcribe/README.md index 82f511d..8550816 100644 --- a/modules/transcribe/README.md +++ b/modules/transcribe/README.md @@ -32,9 +32,14 @@ not just what you say. ```bash brew install blackhole-2ch # virtual audio device (captures system audio) brew install ffmpeg # recording -pip3 install mlx-whisper # local transcription (Apple Silicon) +python3 -m venv modules/transcribe/.venv && modules/transcribe/.venv/bin/pip install mlx-whisper ``` +The last line installs mlx-whisper (local transcription, Apple Silicon) into a +venv next to the module; a plain `pip3 install` fails on Homebrew Python +(PEP 668). `record.py` switches to that venv on its own, so you keep running it +with any `python3`. + Then, once, in **Audio MIDI Setup** (macOS): 1. Create a **Multi-Output Device** = your speakers + **BlackHole 2ch**, so you @@ -79,6 +84,10 @@ The transcript lands in `vault/raw/meetings/` (override with `--out`) and is already on your clipboard, so you can paste it straight into a note or a chat with your assistant. +`vault/raw/meetings/` is an inbox: nothing processes it automatically. Ask your +assistant to "file the latest call transcript" and the `second-brain` agent +turns it into a meeting note with decisions and follow-ups. + ## Optional: bind it to a hotkey Running a command twice is the whole interface, which makes it a natural fit for @@ -88,9 +97,9 @@ starts and stops because the script toggles: ```lua -- ~/.hammerspoon/init.lua hs.hotkey.bind({"ctrl", "shift"}, "R", function() - hs.task.new("/usr/bin/python3", + hs.task.new("/usr/bin/env", nil, - {os.getenv("HOME") .. "/compabob/modules/transcribe/record.py"} + {"python3", os.getenv("HOME") .. "/compabob/modules/transcribe/record.py"} ):start() end) ``` diff --git a/modules/transcribe/record.py b/modules/transcribe/record.py index 64d42be..458b92e 100644 --- a/modules/transcribe/record.py +++ b/modules/transcribe/record.py @@ -18,7 +18,8 @@ - BlackHole 2ch installed (brew install blackhole-2ch) - A Multi-Output / Aggregate device (macOS Audio MIDI Setup) - ffmpeg (brew install ffmpeg) - - mlx-whisper (pip3 install mlx-whisper) + - mlx-whisper in modules/transcribe/.venv (see README; this script re-runs + itself with that venv's python when it exists) Device names vary per machine. Run `record.py devices` to see yours, then override any of these via environment variable if they differ from the defaults: @@ -292,5 +293,17 @@ def main(): start_recording(args.device, args.model, args.title, inbox) +def _reexec_in_module_venv(): + """Re-run with modules/transcribe/.venv's python if it exists and we are not it. + + mlx-whisper lives in that venv (Homebrew Python refuses a global pip install), + so a hotkey or a bare `python3 record.py` still finds it. + """ + venv_py = Path(__file__).resolve().parent / ".venv" / "bin" / "python" + if venv_py.exists() and Path(sys.prefix).resolve() != venv_py.parent.parent.resolve(): + os.execv(str(venv_py), [str(venv_py), str(Path(__file__).resolve()), *sys.argv[1:]]) + + if __name__ == "__main__": + _reexec_in_module_venv() main() diff --git a/modules/whatsapp/README.md b/modules/whatsapp/README.md deleted file mode 100644 index dc17c63..0000000 --- a/modules/whatsapp/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# Module: WhatsApp (roadmap — documented, intentionally not built) - -**Status: roadmap.** There is no code in this module on purpose. This file -explains why, and what would change that. - -## Why it is not built - -WhatsApp has no general-purpose official API for an individual's personal -account. The two real options both have problems for a kit like this: - -- **WhatsApp Business Platform (Cloud API)** — official, but built for - businesses. It needs a Meta Business account, a registered business phone - number, and template-message approval for anything proactive. That is heavy - overhead for a personal assistant. -- **Unofficial bridges** — libraries that reverse-engineer the WhatsApp Web - session. They work, but they violate WhatsApp's terms of service and **put - your account at risk of being banned.** This kit will not ship code that can - get your primary messaging account suspended. - -## What would make it viable - -If you already have a **WhatsApp Business Cloud API** number, a connector is -straightforward: a bot token in `.env`, polling or a webhook, and the same -draft-first safety contract the rest of the kit uses. It would slot in almost -exactly like the Telegram module. If that describes you, use -[`modules/telegram/`](../telegram/) as the reference implementation and adapt it. - -## Recommended path today - -Use the **Telegram module** ([`modules/telegram/`](../telegram/)). Telegram has a -clean, official Bot API, no account-ban risk, and the kit ships it as a working, -draft-first integration. For most people that covers the "chat with my assistant" -use case completely. diff --git a/scripts/init.sh b/scripts/init.sh index 47797ce..c909561 100755 --- a/scripts/init.sh +++ b/scripts/init.sh @@ -12,16 +12,27 @@ ok() { printf " ${GREEN}ok${NC} %s\n" "$1"; } warn() { printf " ${YELLOW}warn${NC} %s\n" "$1"; WARN=$((WARN+1)); } fail() { printf " ${RED}fail${NC} %s\n" "$1"; FAIL=$((FAIL+1)); } -printf "\n${BOLD}Compabob — init check${NC}\n" +printf "\n${BOLD}Compabob: init check${NC}\n" printf "%s\n\n" "$(date '+%Y-%m-%d %H:%M')" # 1. Tooling -command -v python3 >/dev/null 2>&1 && ok "python3 ($(python3 --version 2>&1))" || fail "python3 not found" +if command -v python3 >/dev/null 2>&1; then + if python3 -c 'import sys; sys.exit(sys.version_info < (3, 10))' 2>/dev/null; then + ok "python3 ($(python3 --version 2>&1))" + else + warn "$(python3 --version 2>&1) is older than 3.10; some modules need 3.10+ (brew install python)" + fi +else + fail "python3 not found" +fi command -v claude >/dev/null 2>&1 && ok "claude CLI found" || warn "claude CLI not found (install: curl -fsSL https://claude.ai/install.sh | bash)" # 2. Setup has run — your personal files exist if [ -d vault ] && [ -d memory ] && [ -f config/user.config.yaml ]; then - ok "setup has run (vault/, memory/, config present)" + ok "setup has run (vault/, memory/, config/user.config.yaml present)" + if MISSING_CFG=$(bash scripts/migrate-config.sh --check 2>/dev/null); then :; else + warn "$MISSING_CFG. Run: bash scripts/migrate-config.sh" + fi else fail "setup has not run — run ./setup.sh" fi @@ -66,7 +77,7 @@ PY fi # 6. Agents and skills -AGENTS=$(find .claude/agents -maxdepth 1 -name '*.md' 2>/dev/null | wc -l | tr -d ' ') +AGENTS=$(find .claude/agents -maxdepth 1 -name '*.md' ! -name '_*' ! -name 'error-handling.md' 2>/dev/null | wc -l | tr -d ' ') SKILLS=$(find .claude/skills -maxdepth 2 -name 'SKILL.md' 2>/dev/null | wc -l | tr -d ' ') [ "$AGENTS" -gt 0 ] && ok "$AGENTS agent file(s)" || fail "no agents in .claude/agents/" [ "$SKILLS" -gt 0 ] && ok "$SKILLS skill(s)" || warn "no skills in .claude/skills/" diff --git a/scripts/install-integrations.sh b/scripts/install-integrations.sh index 52c2c74..80f869a 100755 --- a/scripts/install-integrations.sh +++ b/scripts/install-integrations.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Compabob — integration installer. +# Compabob: integration installer. # # Adds MCP servers to .mcp.json from scripts/integrations-catalog.json. # Keyless integrations (web, utility) are configured fully. The keyed one @@ -21,12 +21,11 @@ MCP_FILE=".mcp.json" MCP_SEED=".mcp.json.example" CONFIG="config/user.config.yaml" -bold() { printf '\033[1m%s\033[0m\n' "$1"; } -ok() { printf ' \033[0;32mok\033[0m %s\n' "$1"; } -warn() { printf ' \033[1;33mwarn\033[0m %s\n' "$1"; } +# shellcheck source=scripts/lib/common.sh +source scripts/lib/common.sh if [ "${1:-}" = "--help" ] || [ "${1:-}" = "-h" ]; then - bold "Compabob — integration installer" + bold "Compabob: integration installer" echo "Adds MCP servers to .mcp.json. Categories: web, utility, search." echo " install-integrations.sh web utility configure those categories" echo " install-integrations.sh interactive picker" @@ -37,7 +36,7 @@ fi command -v python3 >/dev/null 2>&1 || { echo "python3 is required" >&2; exit 1; } bold "" -bold "Compabob — integrations" +bold "Compabob: integrations" echo # --- choose categories ----------------------------------------------------- @@ -73,10 +72,10 @@ echo # --- merge .mcp.json + report (Python does the JSON work) ----------------- # .mcp.json is created only when at least one server is actually added, so an # unknown category or an all-skipped run never leaves an empty file behind. -python3 - "$CATALOG" "$MCP_FILE" "$MCP_SEED" "$CONFIG" "${CHOSEN[@]}" <<'PYEOF' +python3 - "$CATALOG" "$MCP_FILE" "$MCP_SEED" "${CHOSEN[@]}" <<'PYEOF' import json, os, sys -catalog_path, mcp_path, seed_path, config_path, *chosen = sys.argv[1:] +catalog_path, mcp_path, seed_path, *chosen = sys.argv[1:] GREEN, YELLOW, BOLD, NC = "\033[0;32m", "\033[1;33m", "\033[1m", "\033[0m" catalog = json.load(open(catalog_path)) @@ -135,25 +134,13 @@ if guided: print(f" Walkthrough: modules/integrations/README.md#{cat.get('doc_anchor','')}") if cat.get("env_hint"): print(f" Provide {cat['env_hint']} (see .env.example).") - -# flip integrations: false -> true in the user config, if it exists -try: - lines = open(config_path).read().splitlines() - out, flipped = [], False - for ln in lines: - if (not flipped and ln.lstrip().startswith("integrations:") - and "false" in ln): - out.append(ln.replace("false", "true", 1)); flipped = True - else: - out.append(ln) - if flipped: - open(config_path, "w").write("\n".join(out) + "\n") - print() - print(f" {GREEN}ok{NC} set integrations: true in {config_path}") -except FileNotFoundError: - pass PYEOF +if set_config_flag integrations true "$CONFIG"; then + echo + ok "set integrations: true in $CONFIG" +fi + echo bold "Done." echo "Review .mcp.json, then in a terminal in this folder run: claude mcp list" diff --git a/scripts/lib/common.sh b/scripts/lib/common.sh new file mode 100755 index 0000000..0406d94 --- /dev/null +++ b/scripts/lib/common.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +# Compabob: shared helpers for the kit's shell scripts. Sourced, not run. +# source "$PROJECT_DIR/scripts/lib/common.sh" + +bold() { printf '\033[1m%s\033[0m\n' "$1"; } +ok() { printf ' \033[0;32mok\033[0m %s\n' "$1"; } +warn() { printf ' \033[1;33mwarn\033[0m %s\n' "$1"; } +say() { printf ' %s\n' "$1"; } + +# set_config_flag [config-file] +# Sets the first `key: true|false` line in the user config, keeping its +# indentation and trailing comment. Returns 0 when it changed the value, +# 2 when the value was already set, 1 when the file or key is missing. +set_config_flag() { + local key="$1" val="$2" cfg="${3:-config/user.config.yaml}" + [ -f "$cfg" ] || return 1 + python3 - "$cfg" "$key" "$val" <<'PY' +import pathlib, re, sys +path, key, val = pathlib.Path(sys.argv[1]), sys.argv[2], sys.argv[3] +lines = path.read_text(encoding="utf-8").splitlines() +pat = re.compile(r"^(\s*" + re.escape(key) + r":\s*)(true|false)\b(.*)$") +for i, line in enumerate(lines): + m = pat.match(line) + if m: + if m.group(2) == val: + sys.exit(2) + lines[i] = m.group(1) + val + m.group(3) + path.write_text("\n".join(lines) + "\n", encoding="utf-8") + sys.exit(0) +sys.exit(1) +PY +} diff --git a/scripts/lib/personas.sh b/scripts/lib/personas.sh index 154b20b..f5ad998 100755 --- a/scripts/lib/personas.sh +++ b/scripts/lib/personas.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Compabob — persona preset helper. Sourced by setup.sh; not run directly. +# Compabob: persona preset helper. Sourced by setup.sh (after scripts/lib/common.sh); not run directly. # # apply_persona # - seeds memory/topics/role-and-priorities.md from config/personas/.md, @@ -13,7 +13,7 @@ apply_persona() { local persona_file="config/personas/$pid.md" if [ ! -f "$persona_file" ]; then - printf ' \033[1;33mwarn\033[0m persona "%s" not found — skipping persona setup\n' "$pid" + warn "persona \"$pid\" not found, skipping persona setup" return 0 fi diff --git a/scripts/migrate-config.sh b/scripts/migrate-config.sh new file mode 100755 index 0000000..7f971eb --- /dev/null +++ b/scripts/migrate-config.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash +# Compabob: bring config/user.config.yaml up to date with the kit's template. +# +# Adds keys a kit update introduced (for example a new module flag) with their +# default value and comment. It never changes or removes a line you already +# have, and it writes config/user.config.yaml.bak before touching the file. +# +# usage: bash scripts/migrate-config.sh add missing keys +# bash scripts/migrate-config.sh --check list missing keys, change nothing +# (exit 1 when something is missing) +set -uo pipefail + +PROJECT_DIR="$(cd "$(dirname "$0")/.." && pwd)" +cd "$PROJECT_DIR" + +TEMPLATE="config/user.config.yaml.template" +CONFIG="config/user.config.yaml" +[ -f "$TEMPLATE" ] || { echo "missing $TEMPLATE" >&2; exit 1; } +[ -f "$CONFIG" ] || exit 0 # setup has not run yet: nothing to migrate + +python3 - "$TEMPLATE" "$CONFIG" "${1:-}" <<'PY' +import pathlib, re, shutil, sys + +template_path, config_path, mode = sys.argv[1], sys.argv[2], sys.argv[3] +TOP = re.compile(r"^([A-Za-z_][\w-]*):") +CHILD = re.compile(r"^\s+([A-Za-z_][\w-]*):") + + +def blocks(lines): + """Map each top-level key to (start, end): its line and its indented children.""" + out, i = {}, 0 + while i < len(lines): + m = TOP.match(lines[i]) + if not m: + i += 1 + continue + start, j, end = i, i + 1, i + while j < len(lines) and (lines[j].startswith((" ", "\t")) or not lines[j].strip()): + if lines[j].strip(): + end = j + j += 1 + out[m.group(1)] = (start, end) + i = j + return out + + +tpl = pathlib.Path(template_path).read_text(encoding="utf-8").splitlines() +cfg = pathlib.Path(config_path).read_text(encoding="utf-8").splitlines() +tpl_blocks, cfg_blocks = blocks(tpl), blocks(cfg) + +inserts = [] # (after_line_index, [lines], label) +appends = [] # ([lines], label) +for key, (ts, te) in tpl_blocks.items(): + if key not in cfg_blocks: + appends.append((tpl[ts:te + 1], key)) + continue + cs, ce = cfg_blocks[key] + have = {CHILD.match(l).group(1) for l in cfg[cs + 1:ce + 1] if CHILD.match(l)} + missing = [l for l in tpl[ts + 1:te + 1] if CHILD.match(l) and CHILD.match(l).group(1) not in have] + if missing: + inserts.append((ce, missing, key)) + +labels = [f"{k}.{CHILD.match(l).group(1)}" for _, ls, k in inserts for l in ls] + [k for _, k in appends] +if not labels: + sys.exit(0) + +if mode == "--check": + print("config/user.config.yaml is missing: " + ", ".join(labels)) + sys.exit(1) + +shutil.copyfile(config_path, config_path + ".bak") +for after, lines, _ in sorted(inserts, key=lambda t: t[0], reverse=True): + cfg[after + 1:after + 1] = lines +for lines, _ in appends: + if cfg and cfg[-1].strip(): + cfg.append("") + cfg.extend(lines) +pathlib.Path(config_path).write_text("\n".join(cfg) + "\n", encoding="utf-8") +print(" added to config/user.config.yaml: " + ", ".join(labels) + " (backup: user.config.yaml.bak)") +PY diff --git a/setup.sh b/setup.sh index 931fdff..298998c 100755 --- a/setup.sh +++ b/setup.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # Compabob — one-time setup. -# Creates your personal files (vault/, memory/, config/) from the shipped *.example +# Creates your personal files (vault/, memory/, config/user.config.yaml) from the shipped *.example # seeds and fills in your details. Those files live outside git, so a kit update can # never touch them. Safe to re-run: it never overwrites files you already have. set -euo pipefail @@ -8,9 +8,8 @@ set -euo pipefail PROJECT_DIR="$(cd "$(dirname "$0")" && pwd)" cd "$PROJECT_DIR" -bold() { printf '\033[1m%s\033[0m\n' "$1"; } -ok() { printf ' \033[0;32mok\033[0m %s\n' "$1"; } -warn() { printf ' \033[1;33mwarn\033[0m %s\n' "$1"; } +# shellcheck source=scripts/lib/common.sh +source scripts/lib/common.sh bold "" bold "Compabob — setup" diff --git a/update.sh b/update.sh index d231455..d9ca445 100755 --- a/update.sh +++ b/update.sh @@ -1,15 +1,15 @@ #!/usr/bin/env bash -# Compabob — update to the latest kit version. -# Your vault/, memory/, and config/ are git-ignored and are NEVER touched by this. +# Compabob: update to the latest kit version. +# Your vault/, memory/, config/user.config.yaml, .mcp.json, and .env are +# git-ignored and are NEVER touched by this (new config keys are only added). set -uo pipefail cd "$(dirname "$0")" - -bold() { printf '\033[1m%s\033[0m\n' "$1"; } -say() { printf ' %s\n' "$1"; } +# shellcheck source=scripts/lib/common.sh +source scripts/lib/common.sh bold "" -bold "Compabob — update" +bold "Compabob: update" echo "Your data (vault/, memory/, config/user.config.yaml, .mcp.json, .env) is git-ignored." echo "An update cannot touch those files." echo @@ -20,21 +20,32 @@ if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then exit 1 fi -BRANCH="$(git branch --show-current 2>/dev/null || echo main)" +BRANCH="$(git branch --show-current 2>/dev/null)" +if [ -z "$BRANCH" ]; then + echo "You are not on a branch (detached HEAD). Run 'git checkout main', then update again." + exit 1 +fi + +git ls-remote --exit-code --heads origin "$BRANCH" >/dev/null 2>&1 +case $? in + 0) ;; + 2) echo "You are on branch '$BRANCH', which does not exist upstream." + echo "Updates come from main: run 'git checkout main', then update again." + exit 1 ;; + *) echo "Could not reach the remote. Check your internet connection and try again." + exit 1 ;; +esac if ! git fetch --quiet origin "$BRANCH" 2>/dev/null; then - echo "Could not reach the remote. Check your internet connection and try again." + echo "Could not fetch from the remote. Check your internet connection and try again." exit 1 fi LOCAL="$(git rev-parse @ 2>/dev/null || echo "")" REMOTE="$(git rev-parse "origin/$BRANCH" 2>/dev/null || echo "")" -if [ -z "$REMOTE" ]; then - echo "No remote branch origin/$BRANCH found. Nothing to update." - exit 0 -fi if [ "$LOCAL" = "$REMOTE" ]; then bold "Already up to date." + bash scripts/migrate-config.sh || true exit 0 fi @@ -56,7 +67,7 @@ if git merge --no-edit "origin/$BRANCH" >/dev/null 2>&1; then else echo echo "The update needs a manual merge. Run 'git status' to see which files." - echo "Your vault/, memory/, and config/ are safe regardless." + echo "Your vault/, memory/, and config/user.config.yaml are safe regardless." [ "$STASHED" = 1 ] && echo "Your set-aside edits are saved: restore them with 'git stash pop'." exit 1 fi @@ -72,6 +83,10 @@ if [ "$STASHED" = 1 ]; then fi fi +# New kit versions can add config keys (e.g. a new module flag). Add any that +# are missing; existing values are never changed. +bash scripts/migrate-config.sh || true + echo bold "Update complete." echo "Run 'bash scripts/init.sh' to confirm everything is healthy." From 0fd96273c99594b06bc6165a5f38c2b54014b555 Mon Sep 17 00:00:00 2001 From: Philipp Wenger Lebron Date: Fri, 25 Sep 2026 11:00:46 +0200 Subject: [PATCH 3/3] docs(changelog): cut 1.2.0 Complete the Unreleased section (dictation, weekly-review, researcher persona, Spanish README, and Linux troubleshooting shipped without entries), add this release's changes, and fix the footer links (1.1.1 was missing; Unreleased now compares from 1.2.0). Co-Authored-By: Claude Opus 5.5 (1M context) --- CHANGELOG.md | 89 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 83 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b3aeac..9d28f8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,36 @@ All notable changes to Compabob are recorded here. Format follows ## [Unreleased] +## [1.2.0] - 2026-09-25 + +Maintenance release: the modules and skills that shipped since 1.1.1, fixes +for bugs found in a full audit, the Claude Code changes since June, and a +smaller kit. Safe to pull with `./update.sh`: your `vault/`, `memory/`, +`config/user.config.yaml`, `.mcp.json`, and `.env` are untouched, and any new +config flags are added for you. + +**If you edited kit files**, `update.sh` may ask you to merge the ones this +release also changed: every agent in `.claude/agents/`, `.claude/settings.json`, +`CLAUDE.md`, the `add-agent`, `mcp-debug`, and `memory-debt` skills, `setup.sh`, +`update.sh`, everything in `scripts/` and `docs/`, and the proactive, telegram, +transcribe, dictation, and integrations modules. `git diff --stat v1.1.1 v1.2.0` +lists them all. Personas and the other skills are unchanged. + ### Added +- **`dictation` module**: a cleanup endpoint your dictation app (e.g. Handy) + posts to. It fixes punctuation and fillers, and rewrites words it keeps + getting wrong using a glossary that grows itself from recurring corrections. + OpenAI-compatible upstream (Groq by default, Ollama for fully local). +- **`/weekly-review` skill**: end-of-week review of what got done, what + slipped, decisions, commitments, and next week's top three (#16). +- **`researcher` persona preset** for academia and R&D (#19). +- **Spanish README intro** at `docs/i18n/README.es.md` (#23). +- **Linux and WSL troubleshooting** section in the README (#14). +- **`scripts/migrate-config.sh`**: adds config keys a kit update introduces, + inside the right block, with a `.bak`; never changes a value you set. + `update.sh` runs it, and `scripts/init.sh` warns when keys are missing. +- **`scripts/lib/common.sh`**: shared output helpers and `set_config_flag`. - **`transcribe` module (highly encouraged)** — one-hotkey local call recording + transcription. A toggle script drives `ffmpeg` to capture audio and `mlx-whisper` to transcribe it on-device: first run records, second run stops, @@ -53,12 +81,59 @@ All notable changes to Compabob are recorded here. Format follows approvals one by one. Closes the loop between `/reflect` (proposes) and `memory/`. -### Documented +### Changed + +- **Agents follow your model.** Every agent uses `model: inherit` instead of + `sonnet`, so switching models in Claude Code switches the agents too. On a + Pro plan, see the README for pinning them back to Sonnet. Scheduled modules + still run on Sonnet. +- **Install Claude Code with the native installer** + (`curl -fsSL https://claude.ai/install.sh | bash`); npm stays documented as a + fallback (Node 22+). Docs links point at code.claude.com. +- **README model section rewritten** (closes #9): Claude Code's default is now + Opus 5.5 on every paid plan; how to change it per session, per project, or + globally. +- **Integrations**: pins bumped (`@playwright/mcp` 0.0.82, `scrapling-fetch-mcp` + 0.2.4, `mcp-server-time` 2026.8.18, `exa-mcp-server` 3.4.1, which needs + Node 20+). Gmail and Calendar now point at Claude's own connectors. +- **SessionStart hook** also runs after `/clear` and in forked sessions. +- **Config template**: flags for `crm_merge` and `lead_pipeline`; persona list + shows all six. +- **`transcribe` and `dictation`** install their Python dependency into a + module venv (Homebrew Python refuses a global `pip install`); `record.py` + switches to its venv on its own, so hotkeys keep working. +- **`update.sh`** says what to do on a local-only branch or detached HEAD + instead of blaming the network. +- **`scripts/init.sh`** checks for Python 3.10+ and counts only real agents. + +### Removed + +- **Tool-scope guard hook** (never released): it was not wired into + `settings.json`, needed `jq`, and matched no context the kit sets. +- **Stub modules** `extra-agents`, `team`, and `whatsapp`, which were README + files only. Their reasoning now lives in the "Not built" section of + `modules/README.md`; the roadmap is issue #12. +- **`mcp-server-fetch`** from the integrations catalog (Claude Code's built-in + WebFetch covers it) and the empty `google` category. +- **Dead config keys**: `preferences.timezone`, `extra_agents`, `team`. Existing + configs keep them; nothing reads them. + +### Fixed -- **Choosing the Claude model** — new README section explains how to swap - the default model per session (`/model` or `--model`), per project - (`.claude/settings.json` → `"model"`), or globally (`ANTHROPIC_MODEL` env - var), with a pointer to the Anthropic model list. Closes #9. +- **Weekly review ran on Saturday on macOS**: launchd counts Sunday as 0, so + `Weekday 6` was Saturday. Now Friday. +- **Scheduled runs could not find `claude`** under launchd's minimal PATH + (`proactive`, `telegram`). +- **The agent template registered as a real agent**. It is now + `.claude/agents/_agent-template.md.template`. +- **`install-integrations.sh` left an empty `.mcp.json`** on runs that added + nothing. +- **Docs claimed `config/` is git-ignored**; only `config/user.config.yaml` is. +- **`/add-agent` and the customization guide** pointed at + `_orchestrator-reference.md` as an agent registry; routing lives in + `CONSTITUTION.md`. +- **Telegram README** said the hook "blocks until you approve"; the hook always + blocks, and you send with `send.sh` yourself. ## [1.1.1] — 2026-05-24 @@ -191,6 +266,8 @@ Initial public release at [github.com/chacosoldier/compabob](https://github.com/ - `docs/architecture.md`, `docs/onboarding.md`, `docs/customization-guide.md`, `docs/how-to-improve-memory.md`. -[Unreleased]: https://github.com/chacosoldier/compabob/compare/v1.1.0...HEAD +[Unreleased]: https://github.com/chacosoldier/compabob/compare/v1.2.0...HEAD +[1.2.0]: https://github.com/chacosoldier/compabob/compare/v1.1.1...v1.2.0 +[1.1.1]: https://github.com/chacosoldier/compabob/compare/v1.1.0...v1.1.1 [1.1.0]: https://github.com/chacosoldier/compabob/compare/v1.0.0...v1.1.0 [1.0.0]: https://github.com/chacosoldier/compabob/releases/tag/v1.0.0