Skip to content

Commit 0782281

Browse files
authored
feat: interactive CLI, slug-based orgs, evals support (#7)
## Summary - **Interactive CLI for all commands** — `pull`, `push`, `apply`, `call`, `cleanup` now prompt for org selection and offer a searchable multi-select resource picker when run without arguments. Direct mode (`npm run push -- <org>`) still works for scripting/CI. - **Slug-based orgs replace fixed dev/stg/prod** — Resources are scoped by org name (e.g. `my-org`, `production`) instead of hardcoded environments. Each org gets its own `.env.<org>`, `.vapi-state.<org>.json`, and `resources/<org>/` directory. An interactive `npm run setup` wizard handles first-time configuration. - **Evals as a first-class resource type** — Added `evals` throughout the pipeline: types, state, pull, push, delete, resource loading, and the eval runner (`npm run eval`). ### Details - `npm run setup` — interactive wizard: API key validation with region auto-detection, org naming, searchable resource picker with dependency detection - `searchableCheckbox` — custom `@inquirer/core` prompt with type-to-search, space-to-toggle, Ctrl+A, grouped display, ESC-to-go-back - All `-cmd.ts` wrappers detect whether a slug arg is present — if yes, forward to core script; if no, enter interactive mode - `shouldApplyResourceType` in push.ts now skips resource types not relevant to the selected file paths (less noise) - Removed all 30+ env-specific npm scripts (`push:dev`, `pull:stg:force`, etc.) — replaced by 7 universal commands - README fully rewritten for the new workflow ## Test plan - [ ] `npm run setup` — configure a new org end-to-end - [ ] `npm run pull` — interactive org selection, resource picker with ✔ local markers, ESC to go back - [ ] `npm run push` — interactive with git status indicators, selective push of individual files - [ ] `npm run push -- <org>` — direct mode still works - [ ] `npm run push -- <org> resources/<org>/assistants/foo.md` — single-file push only loads relevant resource type - [ ] `npm run apply` — interactive apply (pull → push) - [ ] `npm run call` — interactive assistant/squad picker from state file - [ ] `npm run cleanup` — interactive dry-run then confirm - [ ] `npm run eval -- <org> -s <squad>` — eval runner works with slug-based env - [ ] `npm run build` — clean compile (no new type errors) Made with [Cursor](https://cursor.com)
2 parents eb29042 + ab62ba6 commit 0782281

49 files changed

Lines changed: 3798 additions & 808 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
1-
# Vapi GitOps — copy values into environment-specific files (never commit real keys).
1+
# Vapi GitOps — copy values into org-specific files (never commit real keys).
22
#
3-
# This repo loads secrets from `.env.<environment>` when you run commands, e.g.:
4-
# - `.env.dev` → `npm run push:dev`, `npm run pull:dev`, etc.
5-
# - `.env.stg` → `npm run push:stg`, …
6-
# - `.env.prod` → `npm run push:prod`, …
3+
# This repo loads secrets from `.env.<org>` when you run commands, e.g.:
4+
# - `.env.my-org` → `npm run push -- my-org`, `npm run pull -- my-org`, etc.
5+
# - `.env.my-org-prod` → `npm run push -- my-org-prod`, …
76
#
8-
# You can optionally add `.env.<environment>.local` or `.env.local` for overrides
7+
# You can optionally add `.env.<org>.local` or `.env.local` for overrides
98
# (see `src/config.ts`).
109
#
11-
# Different Vapi organizations (dev vs prod workspaces) need different private API keys.
12-
# Create a separate file per environment and paste the private key for that org only.
10+
# Different Vapi organizations need different private API keys.
11+
# Create a separate file per org slug and paste the private key for that org only.
12+
# Run `npm run setup` for an interactive wizard that creates these files.
1313

1414
# Required: Vapi private API key for the organization you are syncing to.
1515
VAPI_TOKEN=your-vapi-private-key-here
1616

17-
# Optional: defaults to https://api.vapi.ai if unset (use for local/API proxies only).
17+
# Optional: API base URL — defaults to US (https://api.vapi.ai).
18+
# Set to the EU endpoint for EU-region orgs.
1819
# VAPI_BASE_URL=https://api.vapi.ai
20+
# VAPI_BASE_URL=https://api.eu.vapi.ai

.gitignore

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,9 @@
22
node_modules/
33

44
# Environment files (secrets - never commit these!)
5+
# Covers dev/stg/prod and any org slug (e.g. .env.roofr-production)
56
.env
6-
.env.dev
7-
.env.staging
8-
.env.stg
9-
.env.prod
10-
.env.local
11-
.env.*.local
12-
13-
# Keep the example file
7+
.env.*
148
!.env.example
159

1610
# IDE
@@ -24,5 +18,7 @@ Thumbs.db
2418
# Logs
2519
*.log
2620

21+
tmp/
22+
2723
# Local agent state
2824
.claude/

.vapi-state.dev.json

Lines changed: 0 additions & 11 deletions
This file was deleted.

.vapi-state.prod.json

Lines changed: 0 additions & 11 deletions
This file was deleted.

AGENTS.md

Lines changed: 50 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ This project manages **Vapi voice agent configurations** as code. All resources
66

77
**Prompt quality:** Whenever you create a new assistant or change an existing assistant’s system prompt, read **`docs/Vapi Prompt Optimization Guide.md`** first. It goes deeper on structure, voice constraints, tool usage, and evaluation than the summary in this file.
88

9-
**Environment-scoped resources:** Resources live in `resources/<env>/` (e.g. `resources/dev/`, `resources/prod/`). Each environment directory is isolated — `push:dev` only touches `resources/dev/`, `push:prod` only touches `resources/prod/`. See **`docs/environment-scoped-resources.md`** for the full promotion workflow and rationale.
9+
**Org-scoped resources:** Resources live in `resources/<org>/` (e.g. `resources/my-org/`, `resources/my-org-prod/`). Each org directory is isolated — `npm run push -- my-org` only touches `resources/my-org/`. Run `npm run setup` to create a new org.
1010

11-
**Template-safe first run:** In a fresh clone, prefer `npm run pull:dev:bootstrap` (or the matching env) to refresh `.vapi-state.<env>.json` and credential mappings without materializing the target org's resources into `resources/<env>/`. `push:<env>` will auto-run the same bootstrap sync when it detects empty or stale state for the resources being applied.
11+
**Template-safe first run:** In a fresh clone, prefer `npm run pull -- <org> --bootstrap` to refresh `.vapi-state.<org>.json` and credential mappings without materializing the target org's resources into `resources/<org>/`. `npm run push -- <org>` will auto-run the same bootstrap sync when it detects empty or stale state for the resources being applied.
1212

1313
**Excluding resources from sync (`.vapi-ignore`):** To prevent specific resources from being pulled at all (e.g. assistants owned by another team or legacy resources you don't want to manage), create `resources/<env>/.vapi-ignore` with gitignore-style patterns. See `resources/<env>/.vapi-ignore.example` for syntax and examples. Ignored resources are silently skipped on every pull and never tracked in state — distinct from "locally deleted" which keeps an entry in state.
1414

@@ -38,20 +38,20 @@ This project manages **Vapi voice agent configurations** as code. All resources
3838

3939
| I want to... | What to do |
4040
| ----------------------------------- | ----------------------------------------------------------------------------- |
41-
| Edit an assistant's system prompt | Edit the markdown body in `resources/<env>/assistants/<name>.md` |
42-
| Change assistant settings | Edit the YAML frontmatter in the same `.md` file |
43-
| Add a new tool | Create `resources/<env>/tools/<name>.yml` |
44-
| Add a new assistant | Create `resources/<env>/assistants/<name>.md` |
45-
| Create a multi-agent squad | Create `resources/<env>/squads/<name>.yml` |
46-
| Add post-call analysis | Create `resources/<env>/structuredOutputs/<name>.yml` |
47-
| Write test simulations | Create files under `resources/<env>/simulations/` |
48-
| Promote resources across envs | Copy files from `resources/dev/` to `resources/stg/` or `resources/prod/` |
49-
| Test webhook event delivery locally | Run `npm run mock:webhook` and tunnel with ngrok |
50-
| Push changes to Vapi | `npm run push:dev` or `npm run push:prod` |
51-
| Pull latest from Vapi | `npm run pull:dev`, `npm run pull:dev:force`, or `npm run pull:dev:bootstrap` |
52-
| Pull one known remote resource | `npm run pull:dev -- assistants --id <uuid>` |
53-
| Push only one file | `npm run push:dev resources/dev/assistants/my-agent.md` |
54-
| Test a call | `npm run call:dev -- -a <assistant-name>` |
41+
| Edit an assistant's system prompt | Edit the markdown body in `resources/<org>/assistants/<name>.md` |
42+
| Change assistant settings | Edit the YAML frontmatter in the same `.md` file |
43+
| Add a new tool | Create `resources/<org>/tools/<name>.yml` |
44+
| Add a new assistant | Create `resources/<org>/assistants/<name>.md` |
45+
| Create a multi-agent squad | Create `resources/<org>/squads/<name>.yml` |
46+
| Add post-call analysis | Create `resources/<org>/structuredOutputs/<name>.yml` |
47+
| Write test simulations | Create files under `resources/<org>/simulations/` |
48+
| Promote resources across orgs | Copy files between `resources/<org-a>/` and `resources/<org-b>/` |
49+
| Test webhook event delivery locally | Run `npm run mock:webhook` and tunnel with ngrok |
50+
| Push changes to Vapi | `npm run push -- <org>` |
51+
| Pull latest from Vapi | `npm run pull -- <org>`, `--force`, or `--bootstrap` |
52+
| Pull one known remote resource | `npm run pull -- <org> --type assistants --id <uuid>` |
53+
| Push only one file | `npm run push -- <org> resources/<org>/assistants/my-agent.md` |
54+
| Test a call | `npm run call -- <org> -a <assistant-name>` |
5555

5656
---
5757

@@ -60,7 +60,6 @@ This project manages **Vapi voice agent configurations** as code. All resources
6060
```
6161
docs/
6262
├── Vapi Prompt Optimization Guide.md # In-depth prompt authoring
63-
├── environment-scoped-resources.md # Environment isolation & promotion workflow
6463
├── changelog.md # Template for tracking per-customer config changes
6564
└── learnings/ # Gotchas, recipes, and troubleshooting
6665
├── README.md # Task-routed index — start here
@@ -80,15 +79,14 @@ docs/
8079
└── voicemail-detection.md # Voicemail vs human classification
8180
8281
resources/
83-
├── dev/ # Dev environment resources (push:dev reads here)
82+
├── <org>/ # Org-scoped resources (npm run push -- <org> reads here)
8483
│ ├── assistants/
8584
│ ├── tools/
8685
│ ├── squads/
8786
│ ├── structuredOutputs/
87+
│ ├── evals/
8888
│ └── simulations/
89-
├── stg/ # Staging environment resources (push:stg reads here)
90-
│ └── (same structure)
91-
└── prod/ # Production environment resources (push:prod reads here)
89+
└── <another-org>/ # Another org (each is isolated)
9290
└── (same structure)
9391
9492
scripts/
@@ -103,7 +101,7 @@ scripts/
103101

104102
Assistants are voice agents that handle phone calls. They are defined as **Markdown files with YAML frontmatter**.
105103

106-
**File:** `resources/<env>/assistants/<name>.md`
104+
**File:** `resources/<org>/assistants/<name>.md`
107105

108106
```markdown
109107
---
@@ -315,7 +313,7 @@ artifactPlan:
315313

316314
Tools are functions the assistant can call during a conversation.
317315

318-
**File:** `resources/<env>/tools/<name>.yml`
316+
**File:** `resources/<org>/tools/<name>.yml`
319317

320318
#### Function Tool (calls a webhook)
321319

@@ -414,7 +412,7 @@ function:
414412

415413
Structured outputs extract data from call transcripts after the call ends. They run LLM analysis on the conversation.
416414

417-
**File:** `resources/<env>/structuredOutputs/<name>.yml`
415+
**File:** `resources/<org>/structuredOutputs/<name>.yml`
418416

419417
#### Boolean Output (yes/no evaluation)
420418

@@ -496,12 +494,12 @@ schema:
496494

497495
Squads define multi-agent systems where assistants can hand off to each other.
498496

499-
**File:** `resources/<env>/squads/<name>.yml`
497+
**File:** `resources/<org>/squads/<name>.yml`
500498

501499
```yaml
502500
name: My Squad
503501
members:
504-
- assistantId: intake-agent-a1b2c3d4 # References resources/<env>/assistants/<id>.md
502+
- assistantId: intake-agent-a1b2c3d4 # References resources/<org>/assistants/<id>.md
505503
assistantOverrides: # Override assistant settings within this squad
506504
metadata:
507505
position: # Visual position in dashboard editor
@@ -647,10 +645,10 @@ Resources reference each other by **filename without extension**:
647645

648646
| From | Field | References | Example |
649647
| ------------- | ------------------------------------ | ----------------------- | ----------------------------------------- |
650-
| Assistant | `model.toolIds[]` | Tool files | `- end-call-tool` |
651-
| Assistant | `artifactPlan.structuredOutputIds[]` | Structured Output files | `- customer-data` |
652-
| Squad | `members[].assistantId` | Assistant files | `assistantId: intake-agent-a1b2c3d4` |
653-
| Squad handoff | `destinations[].assistantName` | Assistant `name` field | `assistantName: Booking Assistant` |
648+
| Assistant | `model.toolIds[]` | Tool files | `- end-call-tool` |
649+
| Assistant | `artifactPlan.structuredOutputIds[]` | Structured Output files | `- customer-data` |
650+
| Squad | `members[].assistantId` | Assistant files | `assistantId: intake-agent-a1b2c3d4` |
651+
| Squad handoff | `destinations[].assistantName` | Assistant `name` field | `assistantName: Booking Assistant` |
654652
| Simulation | `personalityId` | Personality files | `personalityId: skeptical-sam-a0000001` |
655653
| Simulation | `scenarioId` | Scenario files | `scenarioId: happy-path-booking-a0000002` |
656654
| Suite | `simulationIds[]` | Simulation test files | `- booking-test-1-a0000001` |
@@ -739,26 +737,35 @@ Concrete example conversations showing expected behavior.
739737
## Available Commands
740738

741739
```bash
740+
# Setup
741+
npm run setup # Interactive wizard: API key, org slug, resource selection
742+
742743
# Sync
743-
npm run pull:dev # Pull from Vapi (preserve local changes)
744-
npm run pull:dev:force # Pull from Vapi (overwrite everything)
745-
npm run pull:dev:bootstrap # Refresh state without writing remote resources locally
746-
npm run pull:dev -- squads --id <uuid> # Pull one known remote resource by UUID
747-
# `--id` requires exactly one resource type; it will error if omitted or combined with multiple types
748-
npm run push:dev # Push all local changes to Vapi
749-
npm run push:dev assistants # Push only assistants
750-
npm run push:dev resources/dev/assistants/my-agent.md # Push single file
744+
npm run pull -- <org> # Pull from Vapi (preserve local changes)
745+
npm run pull -- <org> --force # Pull from Vapi (overwrite everything)
746+
npm run pull -- <org> --bootstrap # Refresh state without writing remote resources locally
747+
npm run pull -- <org> --type squads --id <uuid> # Pull one known remote resource by UUID
748+
npm run push -- <org> # Push all local changes to Vapi
749+
npm run push -- <org> assistants # Push only assistants
750+
npm run push -- <org> resources/<org>/assistants/my-agent.md # Push single file
751+
npm run apply -- <org> # Pull then push (full sync)
751752
752753
# Testing
753-
npm run call:dev -- -a <assistant-name> # Call an assistant via WebSocket
754-
npm run call:dev -- -s <squad-name> # Call a squad via WebSocket
755-
npm run mock:webhook # Run local webhook receiver for server message testing
754+
npm run call -- <org> -a <assistant-name> # Call an assistant via WebSocket
755+
npm run call -- <org> -s <squad-name> # Call a squad via WebSocket
756+
npm run eval -- <org> -s <squad-name> # Run evals against a squad
757+
npm run eval -- <org> -a <assistant-name> # Run evals against an assistant
758+
npm run mock:webhook # Run local webhook receiver for server message testing
759+
760+
# Maintenance
761+
npm run cleanup -- <org> # Dry-run: show orphaned remote resources
762+
npm run cleanup -- <org> --force # Delete orphaned remote resources
756763
757764
# Build
758-
npm run build # Type-check
765+
npm run build # Type-check
759766
```
760767

761-
Replace `dev` with `prod` for production environment.
768+
All commands accept an org slug (e.g. `my-org`). Running without arguments launches interactive mode.
762769

763770
---
764771

0 commit comments

Comments
 (0)