Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,15 @@ NOTION_PARENT_PAGE_ID=
AGENT_NAME=Coach
TIMEZONE=Europe/London

# Model ids. Sonnet is a good default; the fast model is used for trivial
# logging messages to save on usage.
# Model ids. CLAUDE_MODEL handles everything by default; set it to
# claude-opus-4-8 for Opus. The fast model is only used when FAST_TIER_ENABLED.
CLAUDE_MODEL=claude-sonnet-4-6
CLAUDE_MODEL_FAST=claude-haiku-4-5

# Route short, transactional logging messages to CLAUDE_MODEL_FAST to save usage.
# Off by default so behaviour is consistent (every message uses CLAUDE_MODEL).
FAST_TIER_ENABLED=false

# Reasoning effort for the standard model: low | medium | high | xhigh | max.
# Lower is faster and cheaper with less deep thinking. Default medium.
REASONING_EFFORT=medium
Expand Down
19 changes: 19 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: CI

on:
push:
branches: [master]
pull_request:

jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "24"
cache: npm
- run: npm ci
- run: npm run build
- run: npm test
6 changes: 2 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,10 @@ All Notion work goes through the bundled helper `scripts/notion.mjs` (run via Ba

The Dashboard page is a live snapshot and summary of what is happening: current program and week, active goals, this week's training, latest body stats, and the next session. It also doubles as the central index of the workspace: keep a small, tasteful set of links near the top to the key pages and databases (Programs, Goals, Body Stats, Workout Log, Knowledge Base) so the user can jump anywhere from one place. Keep that index compact (a single linked row or a small callout), never a cluttered wall of links. Treat the whole page as something you keep in sync, not a one-off build. Whenever you change the underlying data, refresh the affected tile in the same turn, before you reply, so the Dashboard never goes stale:

- Logged a session: refresh the `thisWeek` tile (re-query the Workout Log for the current week: sessions done, last session, next session).
- Added or changed a goal: refresh the `goals` tile (re-query the Goals database).
- Logged body weight or measurements: refresh the `bodyStats` tile.
- Logged a session, changed a goal, or logged body weight or measurements: run `node scripts/notion.mjs sync-dashboard` once. It re-derives the `thisWeek`, `goals`, and `bodyStats` tiles straight from the databases (counts and values come from queries, never hand-typed), so it is the preferred, fabrication-proof way to keep those three tiles current. Pass `--now <YYYY-MM-DD>` (the date from the Telegram header) so "this week" is correct in the user's timezone.
- Changed the program, started a new week, or anything that touches the hero (program, week, dates): rebuild the relevant tile, or rebuild the whole Dashboard if several tiles are affected at once (`node scripts/setup-workspace.mjs --rebuild-dashboard`).

Use `scripts/notion.mjs refresh-tile --tile <name>` with tile ids resolved from `data/notion-ids.json` (`__dashboard.columns`). Never edit blocks in place; replace the whole tile. The notion-formatting skill has the commands and the recommended tile content. If no Dashboard exists yet, run setup-notion. Do not announce the Dashboard update to the user unless they ask; just keep it accurate.
For one-off or prose tiles use `scripts/notion.mjs refresh-tile --tile <name>` with tile ids resolved from `data/notion-ids.json` (`__dashboard.columns`); for the three data tiles prefer `sync-dashboard`. Never edit blocks in place; replace the whole tile (both commands do). The notion-formatting skill has the commands and the recommended tile content. If no Dashboard exists yet, run setup-notion. Do not announce the Dashboard update to the user unless they ask; just keep it accurate.

## Workflow

Expand Down
18 changes: 11 additions & 7 deletions coach-plugin/skills/notion-formatting/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,17 +68,21 @@ Note: Notion's public API cannot create or configure database *views* (grouping,

## Keep the Dashboard in sync

If the workspace has a Dashboard page (built by setup-notion), keep its tiles current after you change the underlying data, so the user never has to ask. Use the `refresh-tile` command, which replaces the whole tile (never edit blocks in place; their ids shift). Tile ids are cached in `data/notion-ids.json`, so refer to tiles by name:
If the workspace has a Dashboard page (built by setup-notion), keep its tiles current after you change the underlying data, so the user never has to ask.

**Preferred for the three data tiles (This Week, Goals, Body Stats):** run one command.

```bash
node scripts/notion.mjs refresh-tile --tile goals --md "> [🎯] **Goals**\n- Squat 100kg by Sept"
node scripts/notion.mjs refresh-tile --tile bodyStats --md "..."
node scripts/notion.mjs refresh-tile --tile thisWeek --md "..."
node scripts/notion.mjs sync-dashboard --now 2026-06-30 # --now is the date from the Telegram header
```

- After adding or changing a goal, refresh the `goals` tile (re-query the Goals DB).
- After logging body weight or stats, refresh the `bodyStats` tile.
- After logging a session, refresh the `thisWeek` tile (re-query the Workout Log for the current week: sessions completed, last session, next session).
`sync-dashboard` re-derives all three tiles directly from the databases (session counts, goal progress, latest body stats), so the numbers are always consistent with the data and never hand-typed. Run it after logging a session, changing a goal, or logging body stats.

For one-off or prose tiles, `refresh-tile` replaces a single tile from your own markdown (never edit blocks in place; their ids shift). Tile ids are cached in `data/notion-ids.json`, so refer to tiles by name:

```bash
node scripts/notion.mjs refresh-tile --tile goals --md "> [🎯] **Goals**\n- Squat 100kg by Sept"
```

Rebuild the whole Dashboard (`node scripts/setup-workspace.mjs --rebuild-dashboard`) when the program changes, the week advances, or more than three tiles need updating at once. If no Dashboard exists, do not invent one unprompted; offer to build it via setup-notion.

Expand Down
9 changes: 5 additions & 4 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ what gets built.
|---|---|---|
| `AGENT_NAME` | `Coach` | What the coach calls itself (plain-coach mode). |
| `TIMEZONE` | `Europe/London` | Timezone for reminders and the message-header clock. |
| `CLAUDE_MODEL` | `claude-sonnet-4-6` | Model for real coaching, planning, and advice. |
| `CLAUDE_MODEL_FAST` | `claude-haiku-4-5` | Cheaper model used for trivial logging messages. |
| `CLAUDE_MODEL` | `claude-sonnet-4-6` | Model for real coaching, planning, and advice (and for everything unless the fast tier is enabled). Set to `claude-opus-4-8` for Opus. |
| `CLAUDE_MODEL_FAST` | `claude-haiku-4-5` | Cheaper model used for trivial logging messages, only when `FAST_TIER_ENABLED=true`. |
| `FAST_TIER_ENABLED` | `false` | When `true`, short transactional logging messages on new chats route to `CLAUDE_MODEL_FAST` to save subscription usage. Off by default so every message uses `CLAUDE_MODEL` for consistent behaviour. |
| `REASONING_EFFORT` | `medium` | Thinking depth for the standard model: `low \| medium \| high \| xhigh \| max`. Lower is faster and cheaper. |
| `PERSONALITIES_ENABLED` | `true` | Persona overlays (owner gets Arnold, others a stable random character). `false` keeps everyone on the plain coach. See [customization.md](./customization.md). |

Expand All @@ -52,9 +53,9 @@ cache (`data/notion-ids.json`) also lives here.
| Variable | Default | What it is |
|---|---|---|
| `MODE` | `polling` (or `webhook` if `PUBLIC_BASE_URL` is set) | `polling` long-polls Telegram (always-on hosts); `webhook` runs an HTTP server for scale-to-zero hosts. |
| `PUBLIC_BASE_URL` | (none) | Required in webhook mode: the service's public HTTPS URL. |
| `PUBLIC_BASE_URL` | (none) | Required in webhook mode: the service's public HTTPS URL. Startup fails fast if `MODE=webhook` and this is unset. |
| `PORT` | `8080` | HTTP port (health checks, webhook, scheduler). |
| `CRON_SECRET` | (none) | Required in webhook mode: bearer secret an external scheduler sends to `POST /cron/run`. |
| `CRON_SECRET` | (none) | Required in webhook mode: bearer secret an external scheduler sends to `POST /cron/run`. Startup fails fast if `MODE=webhook` and this is unset, so the reminder endpoint is never left unauthenticated. |

See [deployment.md](./deployment.md) for how the two modes map to hosts.

Expand Down
Loading
Loading