# gcalcli Plugin — Setup Guide

The `gcalcli` plugin gives Furoshiki real-time Google Calendar awareness. It injects your agenda into conversations and reflections, and exposes calendar tools the model can invoke on request.

---

## Prerequisites

### 1. Install gcalcli

```bash
pip install gcalcli
# or
pipx install gcalcli
```

### 2. OAuth client (recommended)

Create an OAuth **Desktop** client in [Google Cloud Console](https://console.cloud.google.com/apis/credentials) with the Calendar API enabled.

**Easiest:** open the dashboard → **Work & extensions → Plugins → gcalcli → Configure** and set **Google OAuth client ID** and **Google OAuth client secret**. The secret field never shows a saved value; leave it blank on save to keep the current secret.

**Alternatively**, put credentials in **`~/.furoshiki/.env`** (see `.env.template` in the repo):

```bash
export GCALCLI_CLIENT_ID=....apps.googleusercontent.com
export GCALCLI_CLIENT_SECRET=...
```

You can use `FUROSHIKI_GCALCLI_CLIENT_ID` / `FUROSHIKI_GCALCLI_CLIENT_SECRET` instead if you prefer a `FUROSHIKI_*` prefix.

The plugin resolves each field independently: **non-empty dashboard values override `.env`**. When both client ID and secret are available after that merge, the plugin **prepends** `--client-id` and `--client-secret` to every `gcalcli` invocation so token refresh matches your OAuth app.

**Headless / automation limits:** Routine **access-token refresh** is automatic once you have a valid refresh token. If Google returns `invalid_grant` (revoked/expired refresh token), a human must complete **`gcalcli init`** again (browser/consent). Furoshiki cannot bypass Google’s OAuth consent screen.

### 3. Authenticate with Google Calendar

From a shell where `.env` is loaded (or after exporting the same variables):

```bash
set -a && source ~/.furoshiki/.env && set +a   # export vars from .env
gcalcli init
# Follow the OAuth flow — opens a browser window
```

Verify it works:

```bash
gcalcli agenda
gcalcli list
```

Both should return your calendar data without errors.

---

## Plugin Installation

The plugin ships pre-installed at `~/.furoshiki/plugins/gcalcli/`. If you need to re-install:

```bash
# Create the directory
mkdir -p ~/.furoshiki/plugins/gcalcli/scripts/callable

# Copy files from this repo's examples
cp -r examples/plugins/gcalcli/* ~/.furoshiki/plugins/gcalcli/
```

Verify the plugin is discovered:

```bash
furoshiki extensions plugins list
# should show: gcalcli  active  pre_conversation, pre_reflection, cron, callable(4)
```

---

## Register the cron snapshot task

```bash
furoshiki extensions plugins reload
# Output: ✓ Brain scheduler updated: 1 plugin task(s)
```

The snapshot task runs every 30 minutes and caches agenda data so the `pre_conversation` hook never blocks on a live gcalcli call.

---

## Configuration (Dashboard)

1. Open the dashboard: `http://localhost:7432`
2. Navigate to **Work & extensions → Plugins → gcalcli**
3. Click **Configure**
4. Adjust settings:

| Setting | Default | Description |
|---------|---------|-------------|
| `gcalcli_path` | `gcalcli` | Full path if gcalcli is not on `$PATH` |
| `oauth_client_id` | (empty) | Google OAuth client ID; overrides `GCALCLI_CLIENT_ID` when set |
| `oauth_client_secret` | (empty) | Client secret (password field); leave blank on save to keep saved value; falls back to `GCALCLI_CLIENT_SECRET` |
| `agenda_days` | 2 | Days forward to fetch for conversation context |
| `calendars` | (all) | Leave empty to include all; one calendar name per line |
| `max_context_events` | 20 | Max events injected into a prompt |
| `pre_conversation_enabled` | true | Inject agenda before conversations |
| `pre_reflection_enabled` | true | Inject calendar context before reflection |
| `snapshot_enabled` | true | Keep a 30-min cached snapshot |
| `reflection_days_back` | 7 | Past days for reflection context |
| `reflection_days_forward` | 7 | Future days for reflection context |

5. Click **Save**

Settings are written to `~/.furoshiki/plugins/gcalcli/settings.json` and take effect on the next hook invocation.

---

## Testing

### Test pre_conversation hook

```bash
python3 ~/.furoshiki/plugins/gcalcli/scripts/agenda.py
```

Expected: JSON with `{"label": "your calendar", "content": "...", "priority": 40}`

### Test pre_reflection hook

```bash
python3 ~/.furoshiki/plugins/gcalcli/scripts/reflection_agenda.py
```

### Test snapshot

```bash
python3 ~/.furoshiki/plugins/gcalcli/scripts/snapshot.py
cat ~/.furoshiki/plugins/gcalcli/snapshot.json | python3 -m json.tool | head -20
```

### Test callable tools

```bash
# Get agenda
python3 ~/.furoshiki/plugins/gcalcli/scripts/callable/get_agenda.py

# Get agenda for specific dates
python3 ~/.furoshiki/plugins/gcalcli/scripts/callable/get_agenda.py \
  --args '{"start_date":"2026-04-14","end_date":"2026-04-18"}'

# Search
python3 ~/.furoshiki/plugins/gcalcli/scripts/callable/search_events.py \
  --args '{"query":"meeting"}'

# List calendars
python3 ~/.furoshiki/plugins/gcalcli/scripts/callable/list_calendars.py

# Add event (test carefully — this actually adds to your calendar!)
python3 ~/.furoshiki/plugins/gcalcli/scripts/callable/add_event.py \
  --args '{"event_text":"Test event tomorrow at 3pm"}'
```

---

## How it works in conversation

Once the plugin is active:

1. **Every conversation** — Furoshiki automatically has your next 2 days of agenda in context. No user action needed.

2. **On request** — The model can call `calendar_get_agenda`, `calendar_search`, `calendar_add_event`, or `calendar_list_calendars` when you ask calendar-related questions.

   Examples that trigger calendar tools:
   - "What do I have on Thursday?"
   - "Am I free tomorrow afternoon?"
   - "Schedule a call with Alex next Monday at 2pm"
   - "Find my dentist appointment"
   - "What calendars do I have?"

3. **During reflection** — The 7-day window of past and future events is available as context.

---

## Troubleshooting

### gcalcli not found

If `gcalcli` is not on the subprocess `$PATH`:

1. Find the path: `which gcalcli`
2. Set it in dashboard: Work & extensions → Plugins → gcalcli → Configure → `gcalcli_path` → `/full/path/to/gcalcli`

### OAuth token expired

If events stop showing:

```bash
gcalcli init   # re-authenticate
gcalcli agenda # verify it works
```

The snapshot cron hook will emit a `mind_queue` notification if it detects an auth error.

**Note:** Current gcalcli versions do not provide a `login` command. If you see `invalid choice: 'login'`, use `gcalcli init` (same OAuth flow). A `RefreshError` / `invalid_grant` from Google means the saved refresh token was revoked or expired — re-run `gcalcli init`; Furoshiki cannot refresh Google OAuth tokens without you completing that flow again.

### Calendar name not matching

Calendar names in `settings.json` must exactly match what `gcalcli list` shows. Run:

```bash
python3 ~/.furoshiki/plugins/gcalcli/scripts/callable/list_calendars.py
```

Use those exact names in the dashboard settings.

---

## Files

```
~/.furoshiki/plugins/gcalcli/
  PLUGIN.md                           # manifest (hooks, permissions, settings_schema)
  settings.json                       # written by dashboard (or hand-edit)
  snapshot.json                       # auto-written by cron hook every 30 min
  scripts/
    _gcal_lib.py                      # shared: run gcalcli, parse TSV, settings, snapshot I/O
    agenda.py                         # pre_conversation hook
    reflection_agenda.py              # pre_reflection hook
    snapshot.py                       # cron hook (every 30 min)
    callable/
      get_agenda.py                   # callable: get events for date range
      add_event.py                    # callable: add event via gcalcli quick
      search_events.py                # callable: search by keyword
      list_calendars.py               # callable: list available calendars
```
