candidate-kb
Manage candidate knowledge base. Use when ingesting CVs, adding profile information, or querying candidate details for resume/cover letter generation.
Rotates the active Stripe account of a SpecBox project safely. Wraps the switch_stripe_account MCP tool with a UX layer: shows current alias store, asks for from/to, runs dry-run, formats the plan in Markdown, asks for literal confirmation, executes, and surfaces the rollback runbook if something...
---
name: stripe-switch-account
description: >
Rotates the active Stripe account of a SpecBox project safely. Wraps the
switch_stripe_account MCP tool with a UX layer: shows current alias store,
asks for from/to, runs dry-run, formats the plan in Markdown, asks for
literal confirmation, executes, and surfaces the rollback runbook if
something fails. Supports both account_mode='standard' (SaaS, e-commerce)
and 'connect' (marketplace). Use when the user says "switch stripe
account", "rotar cuenta stripe", "cambiar cuenta stripe", "cambiar de cuenta
de stripe", "rotate stripe account", "stripe credentials rotation".
context: direct
allowed-tools: Read, Grep, Glob, Bash(*), Write, Edit, mcp__specbox-stripe__*, mcp__specbox-supabase__*
---
# /stripe-switch-account — Stripe credentials rotation skill
Companion of `/stripe-connect` and `/stripe-standard`. Where those scaffold a
brand-new integration, this one rotates an already-deployed integration to a
**different Stripe account**. Common cases:
- Test → live (going to production for the first time)
- Live → live (changing legal entity, splitting from a former partner)
- Live → test (rolling back to a sandbox after an incident)
The skill never moves money, customers or subscriptions across accounts —
those are immutable once created. What it rotates is the **infrastructure**:
which account the platform's Edge Functions talk to, where the webhooks
fire, which `sk_*` lives in Supabase secrets.
## Uso
```
/stripe-switch-account
```
Sin argumentos. La skill detecta el alias store, pregunta from/to, propone un
plan, pide confirmación literal y ejecuta.
## Pre-requisitos
1. **Alias store** con al menos 2 entradas. Crear con:
```
mcp__specbox-stripe__store_stripe_alias_tool({
alias_name: "prod",
stripe_api_key: "sk_live_...",
project_path: "/abs/path/to/project"
})
```
La store vive cifrada en `.claude/secrets/stripe_aliases.enc.json`.
2. **specbox-stripe-mcp v0.3+** registrado en `.claude/settings.local.json`.
3. **(Recomendado)** `specbox-supabase-mcp` registrado para auto-rotación de
Edge Function secrets. Si falta, la skill genera `doc/PENDING_SWITCH_SECRETS.md`
y guía al usuario para hacerlo manualmente.
---
## Paso 0 — Preflight
### 0.1 Validar proyecto SpecBox
```bash
test -f .claude/settings.local.json || test -f .claude/settings.json || {
echo "ERROR: No es un proyecto SpecBox. Ejecuta onboard_project primero."
exit 1
}
```
### 0.2 Detectar `specbox-stripe-mcp`
Comprobar que el MCP está registrado: `grep -q specbox-stripe .claude/settings.local.json`.
Si falta → abortar con instrucciones de instalación:
```
ERROR: specbox-stripe-mcp no está registrado en este proyecto.
Añádelo a .claude/settings.local.json bajo "mcpServers".
Mínimo requerido: v0.3.0 (incluye switch_stripe_account).
```
### 0.3 Detectar el modo del proyecto
Leer `package.json` / `pubspec.yaml` / Edge Functions existentes:
- ¿Hay `supabase/functions/stripe-webhook` con un solo endpoint? → `account_mode='standard'`.
- ¿Hay 2 webhooks (platform + connect) o referencias a `application_fee_percent`? → `account_mode='connect'`.
Si la heurística es ambigua, preguntar al usuario.
### 0.4 Listar aliases disponibles
```
mcp__specbox-stripe__list_stripe_aliases_tool_mcp({ project_path: "{abs}" })
```
Si hay menos de 2 → abortar:
```
ERROR: Necesitas al menos 2 aliases (origen + destino).
Tienes registrados: [lista]
Crea otro con store_stripe_alias_tool antes de switch.
```
---
## Paso 1 — UX inicial
Mostrar al usuario:
```
🔄 Stripe Switch Account
Aliases registrados en este proyecto:
• prod (live, last used 2026-04-29 14:22)
• staging (test, last used 2026-04-15 10:01)
• legacy (live, never used)
¿De qué alias migras? (from): prod
¿A qué alias migras? (to): staging
¿Modo de cuenta? standard
¿Qué hacer con la cuenta de origen tras el switch?
(1) keep_old_active — no tocar nada (DEFAULT, recomendado para rollback rápido)
(2) archive_products_only — archivar los products SpecBox, dejar webhooks
(3) deactivate_webhooks_only — deshabilitar webhooks, dejar products
(4) full_archive — todo lo anterior + cancelar subs SpecBox-managed (irreversible)
Elección: 1
```
Validar before continuar:
- `from != to`
- `from` y `to` existen en alias_store
- En `connect` → preguntar también `connect_events`/`connect_url` si difieren
---
## Paso 2 — Dry-run obligatorio
```
mcp__specbox-stripe__switch_stripe_account_tool({
from_alias: "prod",
to_alias: "staging",
account_mode: "standard",
project_path: "{abs}",
platform_url: "https://{ref}.supabase.co/functions/v1/stripe-webhook",
platform_events: [...read from project's events.yaml or defaults...],
scope_action: "keep_old_active",
dry_run: true
})
```
Formatear la respuesta como Markdown:
```
📋 Plan de migración (dry-run)
ORIGEN (prod, sk_live_***)
Webhook endpoints: 1 SpecBox-managed
Products: 3
Subscriptions activas: 27
Customers: 142
DESTINO (staging, sk_test_***)
Webhook endpoints: 0
Products: 0
ACCIONES PROPUESTAS
1. Crear 1 webhook endpoint en destino (events: ...)
2. Replicar catálogo (3 products + sus prices) ← solo si pasaste catalog
3. Inyectar secrets en Supabase (3 keys) ← si specbox-supabase MCP detectado
↳ si no: doc/PENDING_SWITCH_SECRETS.md
4. scope_action sobre origen: keep_old_active (no toca nada)
Duración estimada: ~12 segundos
⚠️ IMPORTANTE: Las subscripciones, customers, y métodos de pago de la
cuenta origen NO se mueven. Siguen vivos en prod. Si alguien tenía
sesión iniciada y va a renovar, el cobro irá a prod, NO a staging.
Este flow es para infrastructure rotation, no migración de business data.
```
---
## Paso 3 — Confirmación literal
```
Para ejecutar este switch, escribe EXACTAMENTE:
Yes, switch from prod to staging
(con esos espacios y mayúsculas; un fallo de typing aborta el switch)
```
Si el usuario escribe cualquier otra cosa → abortar sin tocar nada.
Si `scope_action='full_archive'` → segunda confirmación con
`confirm_token="I understand this archives all SpecBox resources in the source account"`.
---
## Paso 4 — Ejecutar real
```
mcp__specbox-stripe__switch_stripe_account_tool({
...mismo payload que dry-run...
dry_run: false
})
```
Streaming progress al usuario:
```
✓ Pre-flight OK
✓ Webhook endpoint creado en destino: we_1TabcXyz
✓ 3/3 products replicados en destino
✓ Secrets inyectados en Supabase (3 nombres: STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, STRIPE_PUBLISHABLE_KEY)
✓ scope_action keep_old_active: cuenta origen intacta
✅ Switch completado en 11.2s.
```
Si algún paso falla:
```
✗ Webhook endpoint creado en destino
✗ Products replicación: FAILED (E_LIMIT_REACHED)
🔄 Iniciando rollback automático...
✓ Webhook destino eliminado
⚠ Rollback completo. Sistema de vuelta al estado pre-switch.
Revisa los logs y vuelve a intentar.
```
Si rollback también falla:
```
🚨 Rollback parcialmente fallido. Estado del sistema mixto.
Runbook generado en: doc/SWITCH_FAILURE_RUNBOOK.md
Sigue las instrucciones manualmente.
```
---
## Paso 5 — Smoke-test post-switch
Sugerir al usuario:
```
Próximos pasos:
1. Reload de Edge Functions (Supabase los recoge automáticamente).
2. stripe listen --forward-to {platform_url} (con la NUEVA cuenta)
3. Test de un signup nuevo:
- Card 4242 4242 4242 4242
- Verifica que aparece en https://dashboard.stripe.com/{test|live}/customers de la cuenta DESTINO.
4. Si todo OK y scope_action era 'keep_old_active' → considera ejecutar
/stripe-switch-account de nuevo con scope_action='archive_products_only'
una vez confirmes que no necesitas rollback.
```
Si el usuario responde "rollback please":
```
/stripe-switch-account
from: staging
to: prod
```
Es exactamente el mismo flow al revés (siempre que `prod` aún esté `keep_old_active`).
---
## Anexo A — Errores comunes
| Error | Causa | Fix |
|-------|-------|-----|
| `E_ALIAS_NOT_FOUND` | El alias no está en la store | Crear con `store_stripe_alias_tool` |
| `E_DECRYPT_FAILED` | macOS Keychain inaccesible o passphrase distinto | Re-ingresar passphrase / re-crear keychain entry |
| `E_INVALID_KEY` (en alias) | sk_test_/sk_live_ malformado | Pegar de nuevo desde el dashboard |
| `E_LIVE_MODE_NOT_ALLOWED` | sk_live_* sin opt-in | Pasar `allow_live_mode=true` + `live_mode_confirm_token="I acknowledge this affects real money"` |
| `E_LIMIT_REACHED` | >16 webhook endpoints en la cuenta destino | Borrar los huérfanos: `stripe webhooks list --limit=100` y purga manual |
| `E_SUPABASE_WRITE_FAILED` | PAT expirado / project_ref incorrecto | Revisar `SUPABASE_ACCESS_TOKEN` |
| `E_CONFIRMATION_REQUIRED` | Token literal incorrecto | Copiar el token exactamente como aparece en el mensaje de error |
---
## Anexo B — `.gitignore` enforcement
La store cifrada en `.claude/secrets/stripe_aliases.enc.json` se añade a
`.gitignore` automáticamente la primera vez que se llama a
`store_stripe_alias_tool`. La skill lo verifica antes de cualquier acción.
Si por alguna razón está commiteada al repo, **revoca todas las keys de
Stripe inmediatamente y rota** — el archivo cifrado no debería bastar para
recuperarlas, pero asume compromiso.
---
## Referencias
- Tool docs: `mcp__specbox-stripe__switch_stripe_account_tool`
- Alias store: `packages/specbox-stripe-mcp/src/specbox_stripe_mcp/lib/alias_store.py`
- Skills hermanas: `/stripe-connect` (marketplace), `/stripe-standard` (no-Connect)
- Tracking: SpecBox board `ff-bc73b5d69f91`, US-STRIPE-SWITCH-ACCOUNT
---
name: stripe-switch-account
description: >
Rotates the active Stripe account of a SpecBox project safely. Wraps the
switch_stripe_account MCP tool with a UX layer: shows current alias store,
asks for from/to, runs dry-run, formats the plan in Markdown, asks for
literal confirmation, executes, and surfaces the rollback runbook if
something fails. Supports both account_mode='standard' (SaaS, e-commerce)
and 'connect' (marketplace). Use when the user says "switch stripe
account", "rotar cuenta stripe", "cambiar cuenta stripe", "cambiar de cuenta
de stripe", "rotate stripe account", "stripe credentials rotation".
context: direct
allowed-tools: Read, Grep, Glob, Bash(*), Write, Edit, mcp__specbox-stripe__*, mcp__specbox-supabase__*
---
# /stripe-switch-account — Stripe credentials rotation skill
Companion of `/stripe-connect` and `/stripe-standard`. Where those scaffold a
brand-new integration, this one rotates an already-deployed integration to a
**different Stripe account**. Common cases:
- Test → live (going to production for the first time)
- Live → live (changing legal entity, splitting from a former partner)
- Live → test (rolling back to a sandbox after an incident)
The skill never moves money, customers or subscriptions across accounts —
those are immutable once created. What it rotates is the **infrastructure**:
which account the platform's Edge Functions talk to, where the webhooks
fire, which `sk_*` lives in Supabase secrets.
## Uso
```
/stripe-switch-account
```
Sin argumentos. La skill detecta el alias store, pregunta from/to, propone un
plan, pide confirmación literal y ejecuta.
## Pre-requisitos
1. **Alias store** con al menos 2 entradas. Crear con:
```
mcp__specbox-stripe__store_stripe_alias_tool({
alias_name: "prod",
stripe_api_key: "sk_live_...",
project_path: "/abs/path/to/project"
})
```
La store vive cifrada en `.claude/secrets/stripe_aliases.enc.json`.
2. **specbox-stripe-mcp v0.3+** registrado en `.claude/settings.local.json`.
3. **(Recomendado)** `specbox-supabase-mcp` registrado para auto-rotación de
Edge Function secrets. Si falta, la skill genera `doc/PENDING_SWITCH_SECRETS.md`
y guía al usuario para hacerlo manualmente.
---
## Paso 0 — Preflight
### 0.1 Validar proyecto SpecBox
```bash
test -f .claude/settings.local.json || test -f .claude/settings.json || {
echo "ERROR: No es un proyecto SpecBox. Ejecuta onboard_project primero."
exit 1
}
```
### 0.2 Detectar `specbox-stripe-mcp`
Comprobar que el MCP está registrado: `grep -q specbox-stripe .claude/settings.local.json`.
Si falta → abortar con instrucciones de instalación:
```
ERROR: specbox-stripe-mcp no está registrado en este proyecto.
Añádelo a .claude/settings.local.json bajo "mcpServers".
Mínimo requerido: v0.3.0 (incluye switch_stripe_account).
```
### 0.3 Detectar el modo del proyecto
Leer `package.json` / `pubspec.yaml` / Edge Functions existentes:
- ¿Hay `supabase/functions/stripe-webhook` con un solo endpoint? → `account_mode='standard'`.
- ¿Hay 2 webhooks (platform + connect) o referencias a `application_fee_percent`? → `account_mode='connect'`.
Si la heurística es ambigua, preguntar al usuario.
### 0.4 Listar aliases disponibles
```
mcp__specbox-stripe__list_stripe_aliases_tool_mcp({ project_path: "{abs}" })
```
Si hay menos de 2 → abortar:
```
ERROR: Necesitas al menos 2 aliases (origen + destino).
Tienes registrados: [lista]
Crea otro con store_stripe_alias_tool antes de switch.
```
---
## Paso 1 — UX inicial
Mostrar al usuario:
```
🔄 Stripe Switch Account
Aliases registrados en este proyecto:
• prod (live, last used 2026-04-29 14:22)
• staging (test, last used 2026-04-15 10:01)
• legacy (live, never used)
¿De qué alias migras? (from): prod
¿A qué alias migras? (to): staging
¿Modo de cuenta? standard
¿Qué hacer con la cuenta de origen tras el switch?
(1) keep_old_active — no tocar nada (DEFAULT, recomendado para rollback rápido)
(2) archive_products_only — archivar los products SpecBox, dejar webhooks
(3) deactivate_webhooks_only — deshabilitar webhooks, dejar products
(4) full_archive — todo lo anterior + cancelar subs SpecBox-managed (irreversible)
Elección: 1
```
Validar before continuar:
- `from != to`
- `from` y `to` existen en alias_store
- En `connect` → preguntar también `connect_events`/`connect_url` si difieren
---
## Paso 2 — Dry-run obligatorio
```
mcp__specbox-stripe__switch_stripe_account_tool({
from_alias: "prod",
to_alias: "staging",
account_mode: "standard",
project_path: "{abs}",
platform_url: "https://{ref}.supabase.co/functions/v1/stripe-webhook",
platform_events: [...read from project's events.yaml or defaults...],
scope_action: "keep_old_active",
dry_run: true
})
```
Formatear la respuesta como Markdown:
```
📋 Plan de migración (dry-run)
ORIGEN (prod, sk_live_***)
Webhook endpoints: 1 SpecBox-managed
Products: 3
Subscriptions activas: 27
Customers: 142
DESTINO (staging, sk_test_***)
Webhook endpoints: 0
Products: 0
ACCIONES PROPUESTAS
1. Crear 1 webhook endpoint en destino (events: ...)
2. Replicar catálogo (3 products + sus prices) ← solo si pasaste catalog
3. Inyectar secrets en Supabase (3 keys) ← si specbox-supabase MCP detectado
↳ si no: doc/PENDING_SWITCH_SECRETS.md
4. scope_action sobre origen: keep_old_active (no toca nada)
Duración estimada: ~12 segundos
⚠️ IMPORTANTE: Las subscripciones, customers, y métodos de pago de la
cuenta origen NO se mueven. Siguen vivos en prod. Si alguien tenía
sesión iniciada y va a renovar, el cobro irá a prod, NO a staging.
Este flow es para infrastructure rotation, no migración de business data.
```
---
## Paso 3 — Confirmación literal
```
Para ejecutar este switch, escribe EXACTAMENTE:
Yes, switch from prod to staging
(con esos espacios y mayúsculas; un fallo de typing aborta el switch)
```
Si el usuario escribe cualquier otra cosa → abortar sin tocar nada.
Si `scope_action='full_archive'` → segunda confirmación con
`confirm_token="I understand this archives all SpecBox resources in the source account"`.
---
## Paso 4 — Ejecutar real
```
mcp__specbox-stripe__switch_stripe_account_tool({
...mismo payload que dry-run...
dry_run: false
})
```
Streaming progress al usuario:
```
✓ Pre-flight OK
✓ Webhook endpoint creado en destino: we_1TabcXyz
✓ 3/3 products replicados en destino
✓ Secrets inyectados en Supabase (3 nombres: STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, STRIPE_PUBLISHABLE_KEY)
✓ scope_action keep_old_active: cuenta origen intacta
✅ Switch completado en 11.2s.
```
Si algún paso falla:
```
✗ Webhook endpoint creado en destino
✗ Products replicación: FAILED (E_LIMIT_REACHED)
🔄 Iniciando rollback automático...
✓ Webhook destino eliminado
⚠ Rollback completo. Sistema de vuelta al estado pre-switch.
Revisa los logs y vuelve a intentar.
```
Si rollback también falla:
```
🚨 Rollback parcialmente fallido. Estado del sistema mixto.
Runbook generado en: doc/SWITCH_FAILURE_RUNBOOK.md
Sigue las instrucciones manualmente.
```
---
## Paso 5 — Smoke-test post-switch
Sugerir al usuario:
```
Próximos pasos:
1. Reload de Edge Functions (Supabase los recoge automáticamente).
2. stripe listen --forward-to {platform_url} (con la NUEVA cuenta)
3. Test de un signup nuevo:
- Card 4242 4242 4242 4242
- Verifica que aparece en https://dashboard.stripe.com/{test|live}/customers de la cuenta DESTINO.
4. Si todo OK y scope_action era 'keep_old_active' → considera ejecutar
/stripe-switch-account de nuevo con scope_action='archive_products_only'
una vez confirmes que no necesitas rollback.
```
Si el usuario responde "rollback please":
```
/stripe-switch-account
from: staging
to: prod
```
Es exactamente el mismo flow al revés (siempre que `prod` aún esté `keep_old_active`).
---
## Anexo A — Errores comunes
| Error | Causa | Fix |
|-------|-------|-----|
| `E_ALIAS_NOT_FOUND` | El alias no está en la store | Crear con `store_stripe_alias_tool` |
| `E_DECRYPT_FAILED` | macOS Keychain inaccesible o passphrase distinto | Re-ingresar passphrase / re-crear keychain entry |
| `E_INVALID_KEY` (en alias) | sk_test_/sk_live_ malformado | Pegar de nuevo desde el dashboard |
| `E_LIVE_MODE_NOT_ALLOWED` | sk_live_* sin opt-in | Pasar `allow_live_mode=true` + `live_mode_confirm_token="I acknowledge this affects real money"` |
| `E_LIMIT_REACHED` | >16 webhook endpoints en la cuenta destino | Borrar los huérfanos: `stripe webhooks list --limit=100` y purga manual |
| `E_SUPABASE_WRITE_FAILED` | PAT expirado / project_ref incorrecto | Revisar `SUPABASE_ACCESS_TOKEN` |
| `E_CONFIRMATION_REQUIRED` | Token literal incorrecto | Copiar el token exactamente como aparece en el mensaje de error |
---
## Anexo B — `.gitignore` enforcement
La store cifrada en `.claude/secrets/stripe_aliases.enc.json` se añade a
`.gitignore` automáticamente la primera vez que se llama a
`store_stripe_alias_tool`. La skill lo verifica antes de cualquier acción.
Si por alguna razón está commiteada al repo, **revoca todas las keys de
Stripe inmediatamente y rota** — el archivo cifrado no debería bastar para
recuperarlas, pero asume compromiso.
---
## Referencias
- Tool docs: `mcp__specbox-stripe__switch_stripe_account_tool`
- Alias store: `packages/specbox-stripe-mcp/src/specbox_stripe_mcp/lib/alias_store.py`
- Skills hermanas: `/stripe-connect` (marketplace), `/stripe-standard` (no-Connect)
- Tracking: SpecBox board `ff-bc73b5d69f91`, US-STRIPE-SWITCH-ACCOUNT
Imported from the listed AgentSkill.sh category. Complete supplied package files and notices are retained. Required external tools and credentials are not installed. No bundled code was executed during import. Individual agent task behavior has not been tested.