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
20 changes: 20 additions & 0 deletions .agents/plugins/marketplace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "pipedrive",
"interface": {
"displayName": "Create pipedrive app"
},
"plugins": [
{
"name": "create-pipedrive-app",
"source": {
"source": "local",
"path": "./plugins/create-pipedrive-app"
},
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_INSTALL"
},
"category": "Coding"
}
]
}
17 changes: 17 additions & 0 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "pipedrive",
"owner": {
"name": "Pipedrive",
"url": "https://www.pipedrive.com/"
},
"metadata": {
"description": "Pipedrive developer tools for Claude Code."
},
"plugins": [
{
"name": "create-pipedrive-app",
"source": "./plugin",
"description": "Slash commands for scaffolding and extending Pipedrive Marketplace integration projects"
}
]
}
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,8 @@ docs/superpowers/
*-app/
apps/

# repo-local Codex plugin
!plugins/create-pipedrive-app/
!plugins/create-pipedrive-app/**

.idea/
62 changes: 36 additions & 26 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ npm run typecheck # type-check without emitting
npm run lint # ESLint
npm run format # Prettier (120 char width, tabs, trailing commas)
npm test # Vitest suite
npm run generate # generate test project in apps/test-app/ (gitignored)
npm run generate # generate test project in apps/test-app/ (gitignored)
npm run clean # delete generated apps/ directory
```

Run a single test file:
Expand All @@ -33,28 +34,36 @@ The tool is **CLI-first**, with an **AI plugin layer** built on top:
### Interactive prompts (CLI)

The CLI asks for:
- Backend: Node.js/Express, Node.js/Fastify, or PHP/Laravel
- Project name
- Database: Postgres, MySQL, or SQLite
- App Extensions frontend: multi-select of `custom-panel` and/or `custom-modal` (or neither)
- Webhooks: Yes/No

`GeneratorOptions.appExtensions` is `AppExtensionType[]` where `AppExtensionType = 'custom-panel' | 'custom-modal'`. Check membership with `.includes('custom-panel')`, not boolean equality.
`GeneratorOptions.appExtensions` is `AppExtensionType[]` where `AppExtensionType` is derived from the `APP_EXTENSION_TYPES` const (`'custom-panel' | 'custom-modal'`). Use `isAppExtensionType(value)` to validate CLI input. Check membership with `.includes('custom-panel')`, not boolean equality.

### CLI subcommands

Beyond the interactive main flow, the CLI supports subcommands invoked by the AI plugin skills:

```bash
npx create-pipedrive-app add-app-extension --app-extensions custom-panel|custom-modal [--output-dir <dir>]
```

Subcommand dispatch happens in `dispatchSubcommand()` in `cli.ts` before the interactive flow runs.

### Generator flow

```
cli.ts (collects prompts)
→ prompts/ (projectName, database, appExtensions, webhooks)
→ prompts/ (projectName, database, appExtensions)
→ nodeGenerator (orchestrates sub-generators via NodeProjectBuilder)
→ oauth.ts, database.ts, app.ts
→ webhooks.ts (conditional)
→ oauth.ts, database.ts, app.ts, pipedriveClient.ts, crypto.ts
→ appExtensions.ts (conditional)
→ appExtensions/panel.ts — backend router + React snippet contributions
→ appExtensions/modal.ts — backend router + React snippet contributions
→ appExtensions/frontend.ts — Vite + React frontend (index.html, App.tsx, etc.)
→ appExtensions/sdk.ts — usePipedriveSdk hook wrapper
→ appExtensions/router.ts — shared Express static-file router
→ serverEntry, packageJson, tsConfig, envExample, dockerCompose
→ serverEntry, packageJson, tsConfig, envExample, dockerCompose, readme
```

**There is no template directory.** Generators build file content as strings using `dedent()`, with conditional string interpolation for optional features. The `src/utils/writeFile.ts` utility writes files, creates parent directories, and auto-formats output with Prettier — generated code is formatted automatically without an explicit format step.
Expand All @@ -67,9 +76,8 @@ cli.ts (collects prompts)
app.ts # Express app, mounts all routers
index.ts # Server entry with DB retry loop
oauth/ # Authorization redirect, callback, token exchange, refresh
pipedrive-client/ # Official API client wrapper
pipedrive/ # Official API client wrapper (getClient, token refresh via onTokenUpdate)
database/ # Drizzle schema, migrations, db setup
webhooks/ # Optional webhook handlers
app-extensions/
panel/ # Express router serving built frontend (custom-panel)
modal/ # Express router serving built frontend (custom-modal)
Expand All @@ -78,7 +86,6 @@ cli.ts (collects prompts)
.env.example
README.md
docker-compose.yml
marketplace-checklist.md
```

## App Extensions pattern
Expand All @@ -105,7 +112,7 @@ The scaffold generator uses `NodeProjectBuilder` + `BuildStep` (`src/generators/
2. Add a named method to `NodeProjectBuilder`: `addMyFeature(): this { return this.addStep(new MyFeatureStep()); }`
3. Call it in `index.ts`, via `when()` if conditional:
```ts
.when(options.webhooks, b => b.addWebhooks())
.when(options.appExtensions.length > 0, b => b.addAppExtensions())
```

**Rule: never put conditional logic inside `execute()`** — use `when()` at the call site. Steps must be unconditional internally; the builder chain controls what runs.
Expand All @@ -122,21 +129,20 @@ The scaffold generator uses `NodeProjectBuilder` + `BuildStep` (`src/generators/
- **New App Extension type**: add a file under `src/generators/node/appExtensions/` that exports an `async generate*` function and a `*ReactSnippets()` function returning `ReactSnippetContribution`, then wire it in `appExtensions.ts` and `frontend.ts`
- **Modify generated scaffold**: edit template strings in the corresponding generator file

## Core Modules
## Generated Project Modules

### OAuth (`backend/oauth/`)
Full OAuth 2.0: app registration guidance, authorization redirect, callback handling, token exchange, token refresh, state validation.
### OAuth (`src/oauth/`)
Full OAuth 2.0: authorization redirect, callback handling, token exchange, HMAC-signed state parameter with TTL validation.

### Database (`backend/database/`)
### Database (`src/database/`)
Uses **Drizzle ORM** (`drizzle-orm` + `drizzle-kit`) for schema definition and migrations. Supports Postgres, MySQL, and SQLite with the same TypeScript API.

Structure:
- `schema.ts` — Drizzle table definitions (tenants, oauth_tokens, installations)
- `migrations/` — SQL migration files managed by `drizzle-kit`
- `db.ts` — driver setup (selects `postgres-js`, `mysql2`, or `better-sqlite3` based on chosen DB)

### Pipedrive API client (`backend/pipedrive-client/`)
Wrapper around the official Pipedrive Node.js client with preconfigured authentication and helpers for common API calls.
### Pipedrive API client (`src/pipedrive/`)
Wraps the official Pipedrive Node.js SDK. `getClient(companyId)` loads the stored OAuth token and configures `onTokenUpdate` so the SDK handles token refresh automatically.

### App Extensions frontend (`frontend/app-extension-ui/`)
Generated when the user selects `custom-panel` and/or `custom-modal`. Iframe-based React + Vite UI using the App Extensions SDK (`usePipedriveSdk` hook), with: SDK initialization, theme handling, resize, snackbar, confirmation dialog, signed token, and extension-type-specific actions (open modal from panel, close modal).
Expand All @@ -145,11 +151,15 @@ Generated when the user selects `custom-panel` and/or `custom-modal`. Iframe-bas

Vitest. Tests generate files into a `tmpdir()/cpa-app-test` directory, read them back to verify content, and clean up in `afterEach`.

## AI Plugin Commands (future layer)
## AI Plugin Layer

```
/pipedrive-new-app
/pipedrive-add-oauth
/pipedrive-add-app-extension
/pipedrive-review-marketplace-readiness
```
The `plugin/` directory is shipped with the npm package (`"files": ["dist", "plugin"]`) and contains Claude Code skills that wrap the CLI for AI-assisted development. Each skill is a markdown instruction document in `plugin/skills/<name>/SKILL.md`.

| Skill | Purpose |
|-------|---------|
| `pipedrive-new-app` | Scaffold a new app interactively via `npx create-pipedrive-app` |
| `pipedrive-add-app-extension` | Add a panel or modal extension via the `add-app-extension` subcommand |
| `pipedrive-api` | Guide on using the Pipedrive REST API and SDK within a generated project |
| `pipedrive-review-marketplace-readiness` | Gap analysis before marketplace submission: checks token refresh, HTTPS, error handling, and rate limit handling |

Skills use `allowed-tools` frontmatter to restrict which Claude tools they can invoke. Adding a new skill means creating a `SKILL.md` in a new subdirectory under `plugin/skills/`.
69 changes: 46 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,63 +1,86 @@
# create-pipedrive-app

CLI scaffolding tool for Pipedrive Marketplace integrations.
CLI scaffolding tool for Pipedrive Marketplace integrations. Generates an Express + TypeScript + Drizzle ORM project with OAuth 2.0, a Pipedrive API client, and optional App Extensions frontend (React + Vite). The CLI can be used standalone without any AI tooling.

## Usage

```bash
npx create-pipedrive-app <project-name>
npx create-pipedrive-app
```

The CLI will prompt for:
The CLI prompts for:

- **Project name**
- **Database**: Postgres, MySQL, or SQLite
- **App Extensions**: custom panel, custom modal, or none
- **Webhooks**: yes or no
- **App Extensions**: custom panel, custom modal, or neither

## Generated project

```
<project-name>/
src/
index.ts # server entry point (port 3000)
app.ts # Express app with OAuth router (+ optional webhooks/extensions)
index.ts # server entry point
app.ts # Express app with OAuth router (+ optional App Extensions)
oauth/ # OAuth 2.0 install, callback, token exchange, refresh
pipedrive/ # Pipedrive API client wrapper
database/ # Drizzle ORM schema, migrations, db driver
pipedrive-client/ # Pipedrive API client wrapper
webhooks/ # Webhook handlers (if selected)
app-extensions/ # App Extensions handlers (if selected)
frontend/
app-extension-ui/ # React + Vite iframe UI (if App Extensions selected)
.env.example
docker-compose.yml # Postgres, MySQL, and/or App Extensions UI (if applicable)
docker-compose.yml
README.md
package.json
tsconfig.json
```

The generated project uses **Express + TypeScript + Drizzle ORM** (ESM, Node.js).

When App Extensions are selected, the generated project also includes a shared React iframe app for custom panels and custom modals. `docker-compose up --watch` starts the backend and Vite dev server in containers with Compose Watch. Local Developer Hub iframe URLs should point at an HTTPS tunnel to the Vite dev server, for example `https://<your-vite-tunnel>/extensions/panel` or `https://<your-vite-tunnel>/extensions/modal`. After `npm run build`, production iframe URLs can point at the backend-hosted `/extensions/panel` and `/extensions/modal` routes.
When App Extensions are selected, the project includes a shared React iframe app for custom panels and modals. `docker-compose up --watch` starts the backend and Vite dev server in containers with Compose Watch. Local Developer Hub iframe URLs must point at an HTTPS tunnel to the Vite dev serverfor example `https://<your-vite-tunnel>/extensions/panel` or `https://<your-vite-tunnel>/extensions/modal`. After `npm run build`, production iframe URLs can point at the backend-hosted routes.

## Next steps after generation
## Requirements

- Node.js (to run `npx create-pipedrive-app`)
- Docker (for Postgres/MySQL databases and App Extensions development)

## Using with an AI coding assistant

The package ships plugins for **Claude Code** and **Codex** that wrap the CLI with guided slash commands. The plugins require the CLI — they call `npx create-pipedrive-app` under the hood.

### Claude Code

This repository acts as the Claude Code plugin marketplace. Claude reads the marketplace catalog from `.claude-plugin/marketplace.json`, then installs the plugin from `plugin/`.

After this repository is public, install the plugin with:

```bash
cd <project-name>
cp .env.example .env
docker-compose up -d db # if Postgres or MySQL was selected
npm install
npm run dev
claude plugin marketplace add pipedrive/create-pipedrive-app
claude plugin install create-pipedrive-app@pipedrive
```

Inside Claude Code, use the equivalent slash commands:

```text
/plugin marketplace add pipedrive/create-pipedrive-app
/plugin install create-pipedrive-app@pipedrive
/reload-plugins
```

If App Extensions were selected, use Compose Watch instead of the local dev server:
The plugin calls `npx create-pipedrive-app` under the hood, so the npm package must also be published or otherwise available to users.

### Codex

```bash
docker-compose up --watch
codex plugin install create-pipedrive-app
```

Fill in `PIPEDRIVE_CLIENT_ID`, `PIPEDRIVE_CLIENT_SECRET`, and `DATABASE_URL` in `.env`.
### Available commands

## Requirements
Both plugins expose the same slash commands:

- Node.js 18+
- Docker (if using Postgres, MySQL, or App Extensions frontend development)
| Command | What it does |
|---------|-------------|
| `/pipedrive-new-app` | Scaffold a new integration project |
| `/pipedrive-add-app-extension` | Add a custom panel or modal extension |
| `/pipedrive-api` | Get guidance on using the Pipedrive API |
| `/pipedrive-review-marketplace-readiness` | Check for gaps before submitting to the marketplace |
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
"version": "0.1.0",
"description": "Scaffold a production-ready Pipedrive Marketplace app",
"type": "module",
"files": [
"dist",
"plugin",
"plugins"
],
"bin": {
"create-pipedrive-app": "./dist/cli.js"
},
Expand Down
8 changes: 8 additions & 0 deletions plugin/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "create-pipedrive-app",
"description": "Slash commands for scaffolding and extending Pipedrive Marketplace integration projects",
"version": "0.1.0",
"author": {
"name": "Pipedrive"
}
}
Loading
Loading