# Pull Prompt

> Pulls a client's system prompt from Supabase and saves a structured markdown breakdown to their clients/ folder. Use when the user says "pull prompt", "get prompt", "show prompt", or wants to see/export the modules and config for a client's AI voice assistant. Also used by sync workflows to check prompt freshness and re-pull if changed.

- Skill: `ivangit-avoca/pull-prompt` (Agent Skill)
- Install (CLI): `npx skillmds@latest add ivangit-avoca/pull-prompt`
- Raw SKILL.md: https://api.skillmd.com/api/skills/ivangit-avoca/pull-prompt/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: ivangit-avoca (https://skillmd.com/u/ivangit-avoca)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/ivangit-avoca/pull-prompt

---


# Pull prompt from Supabase

## When this applies

- User says **"pull prompt for {client}"**, **"get prompt for {client}"**, or similar.
- User wants to review, compare, or export a client's current AI assistant prompt and configuration.
- Called by the **sync workflow** or **auto-sync rule** to check if prompts need refreshing (see "Freshness check mode" below).

## MCP server

- **Supabase** (`user-Supabase`): `execute_sql` with `project_id` for the Avoca production database.
- Discover `project_id` via `list_projects` if unknown. The production project is named **"avoca"**.

## Inputs

| Input | Required | Source |
|-------|----------|--------|
| Client name or team ID | Yes | User provides, or match from `clients/` folder name |
| Prompt name | No | If multiple prompts exist for the team, ask which one |

## Workflow

### Step 1 — Resolve the client

Match the client to their `clients/{Name}-{ID}/` folder. Extract the **team ID** (numeric suffix). If the user gave a name, glob `clients/` for a match. If they gave an ID directly, use it.

Read `avoca-client.json` from the client folder if it exists — it may have `avoca_client_id`.

### Step 2 — Query system prompts for the team

```sql
SELECT sp.id, sp.name, sp.status, sp.template_id, sp.current_version,
       sp.updated_at, sp.variable_overrides,
       spc.company_name
FROM system_prompts sp
JOIN system_prompt_configs spc ON sp.config_id = spc.id
WHERE sp.is_deleted = false
  AND sp.team_id = {team_id}
ORDER BY sp.updated_at DESC;
```

If **multiple prompts** exist, list them and ask the user which one to pull (or pull all if they say so). If only one, proceed automatically.

### Step 3 — Pull the full data

Run these three queries in parallel for the chosen prompt (by `sp.id`):

**A) Sections (modules):**

```sql
SELECT
  s->>'name' AS module_name,
  s->>'order' AS module_order,
  s->>'enabled' AS enabled,
  s->>'isCustom' AS is_custom,
  s->>'isLocked' AS is_locked,
  s->>'content' AS content,
  s->>'originalContent' AS original_content
FROM system_prompts, jsonb_array_elements(sections) AS s
WHERE id = {prompt_id}
ORDER BY (s->>'order')::int;
```

Note: this may return a large result. If it times out or truncates, batch by pulling sections in groups (e.g., `WHERE (s->>'order')::int BETWEEN 0 AND 19`, then 20–39, then 40+).

**B) Config (business variables):**

```sql
SELECT spc.*
FROM system_prompt_configs spc
JOIN system_prompts sp ON sp.config_id = spc.id
WHERE sp.id = {prompt_id};
```

**C) Template name:**

```sql
SELECT name, version FROM system_prompt_templates
WHERE id = (SELECT template_id FROM system_prompts WHERE id = {prompt_id});
```

### Step 4 — Build the markdown file

Save to: `clients/{Name}-{ID}/prompts/{prompt-name-slug}.md`

Slugify the prompt name (lowercase, hyphens, no special chars). If a file already exists, overwrite it (this is a fresh pull).

#### File structure

```markdown
# {Prompt Name} — {Company Name}

**Team ID:** {team_id}
**Prompt ID:** {prompt_id}
**Template:** {template_name} (v{version})
**Status:** {status}
**Version:** {current_version}
**Last updated:** {updated_at}
**Pulled:** {today's date and time}

---

## Business config

| Field | Value |
|-------|-------|
| Company name | {company_name} |
| Assistant name | {assistant_name} |
| Location | {company_location} |
| Timezone | {timezone} |
| Trades | {trades_provided.text} |
| Service hours | {service_hours} |
| Live rep hours | {live_representative_hours} |
| Company phone | {company_phone_number} |
| Website | {website} |
| Fees name | {fees_name} |
| Intro line | {ai_intro_line} |
| Closing line | {offer_closing_line} |
| Country | {country} |
| Tech arrival notice | {technician_arrival_duration} |
| Emergency re-fetch | {emergency_re_fetch_mode} |

---

## Policy snapshot

Decode `agent_config_variables` into a human-readable table:

| Policy | Setting |
|--------|---------|
| Booking | {enabled/disabled} — {offer_availabilities.type} |
| Transferring | {enabled/disabled}{, time-based if applicable} |
| Commercial | {action} |
| Cancellation | {action} |
| Rescheduling | {action} |
| ETAs | {action} |
| New customers | {action} |
| Renters | {action} |
| Membership confirmation | {enabled/disabled} |
| Equipment age question | {enabled/disabled} |
| AI disclosure | {enabled/disabled} — {timing} |

---

## Module index

| # | Module | Enabled | Custom | Size |
|---|--------|---------|--------|------|
| 0 | MODULE: VARIABLES | ✅ | — | 4.6 KB |
| 1 | SYSTEM CAPABILITIES | ✅ | — | 1.4 KB |
| ... | ... | ... | ... | ... |

Use ✅ for enabled, ❌ for disabled. Use ✏️ for custom modules.

---

## Modules

For each module, output:

### {order}. {Module Name}

{If disabled: **⚠️ DISABLED**}
{If custom: **✏️ CUSTOM OVERRIDE** — content differs from template}

{content — the rendered module text, as-is}

---

## Services provided

{services_provided — full text}

## Services NOT provided

| Service | Description |
|---------|-------------|
| {service} | {description} |

## FAQ

{faq}

## Membership info

{membership_information}
```

### Step 5 — Custom diff detection

For each module where `isCustom` is true OR where `content` differs from `originalContent`:
- Note it in the module index with ✏️
- In the module section, add a collapsed diff block:

```markdown
<details>
<summary>Diff from template</summary>

**Original:**
{originalContent}

**Current:**
{content}

</details>
```

### Step 6 — Variable overrides

If `system_prompts.variable_overrides` is non-null, add a section:

```markdown
## Variable overrides

| Variable | Override value |
|----------|---------------|
| ... | ... |
```

### Step 7 — Confirm

Tell the user:
- File path saved
- How many modules (total / enabled / disabled / custom)
- Any notable config (e.g., "transfers disabled", "booking type: specific_time")

## Pulling multiple prompts

If the user says "pull all prompts" for a client with multiple prompts, loop through each and create separate files. Summarize all in one message.

## Comparing prompts across clients

If the user says "compare prompts for {client A} and {client B}":
1. Pull both using the workflow above (if not already saved locally).
2. Produce a diff summary focusing on:
   - Modules that differ in content
   - Modules enabled in one but disabled in the other
   - Config differences (services, hours, policies)
3. Save as `clients/{Name-A}-{ID}/prompts/compare-vs-{Name-B}.md` or output in chat per user preference.

## Step 8 — Update avoca-client.json

After writing the markdown file, update `avoca-client.json` in the client folder with prompt metadata so future syncs can check freshness.

Read the existing file, merge in the `prompts` key, and write it back:

```json
{
  "avoca_client_id": "2339",
  "prompts": {
    "457": {
      "name": "John Goudie",
      "version": 7,
      "updated_at": "2026-04-02T02:52:32Z",
      "pulled_at": "2026-04-05T22:00:00Z",
      "file": "prompts/john-goudie.md"
    }
  }
}
```

Fields per prompt entry:
- `name` — prompt name from `system_prompts.name`
- `version` — `current_version` from `system_prompts`
- `updated_at` — `updated_at` from `system_prompts` (ISO 8601)
- `pulled_at` — current timestamp when the pull happened (ISO 8601)
- `file` — relative path to the markdown file within the client folder

If the client has multiple prompts, each gets its own key under `prompts` (keyed by prompt ID).

---

## Freshness check mode

When called from the **sync workflow** or **auto-sync rule** (not a direct user request), use this lightweight mode instead of a full pull.

### When to use

- The sync workflow reads `avoca-client.json` and finds a `prompts` section.
- It calls this skill in "check" mode for that client.

### Procedure

1. Read `avoca-client.json` from the client folder. If no `prompts` key exists, **skip entirely** — the user hasn't opted in to prompt tracking for this client.

2. Extract the team ID and the stored `updated_at` for each prompt.

3. Run a single cheap query:

```sql
SELECT id, name, current_version, updated_at
FROM system_prompts
WHERE team_id = {team_id} AND is_deleted = false;
```

4. Compare each prompt's `updated_at` from Supabase against the stored `updated_at` in `avoca-client.json`.

5. **If unchanged:** Do nothing. Optionally log "prompt is current" if reporting to the user.

6. **If changed:** Run the full pull workflow (Steps 2–8 above) for the changed prompt(s). This overwrites the markdown file and updates `avoca-client.json` with the new version/timestamp.

7. **If a new prompt appears** (ID not in `avoca-client.json`): Ignore it — don't auto-pull prompts the user hasn't explicitly pulled before.

### Output in check mode

Keep it minimal. Examples:
- _(nothing — prompt is current, no output needed during silent sync)_
- `Prompt "John Goudie" updated (v7 → v8) — re-pulled to prompts/john-goudie.md`

---

## Safety

- Read-only: never write to Supabase.
- Large prompts may exceed query result limits — batch section pulls if needed.
- Don't dump the full `final_prompt` text unless the user specifically asks for it; the module breakdown is the point.

## Output

Keep chat output concise. Example:

> Pulled **"John Goudie"** prompt to `clients/John Goudie-2339/prompts/john-goudie.md`
> 54 modules (53 enabled, 1 disabled) · Template: Modular v3.0.87 · Last updated 4/2/2026

