create-aron-app 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -2
- package/templates/_base/.cursor/agents/skills/clerk/SKILL.md +89 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-backend-api/SKILL.md +142 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-backend-api/scripts/api-specs-context.sh +30 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-backend-api/scripts/execute-request.sh +88 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-backend-api/scripts/extract-endpoint-detail.sh +165 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-backend-api/scripts/extract-tag-endpoints.sh +208 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-backend-api/scripts/extract-tags.js +14 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-custom-ui/SKILL.md +157 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-custom-ui/core-2/custom-sign-in.md +224 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-custom-ui/core-2/custom-sign-up.md +190 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-custom-ui/core-3/custom-sign-in.md +314 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-custom-ui/core-3/custom-sign-up.md +259 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-custom-ui/core-3/show-component.md +125 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-nextjs-patterns/SKILL.md +94 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-nextjs-patterns/references/api-routes.md +50 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-nextjs-patterns/references/caching-auth.md +56 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-nextjs-patterns/references/middleware-strategies.md +68 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-nextjs-patterns/references/server-actions.md +56 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-nextjs-patterns/references/server-vs-client.md +104 -0
- package/templates/_base/.cursor/agents/skills/clerk/clerk-webhooks/SKILL.md +131 -0
- package/templates/_base/.cursor/agents/skills/shadcn/SKILL.md +241 -0
- package/templates/_base/.cursor/agents/skills/shadcn/agents/openai.yml +5 -0
- package/templates/_base/.cursor/agents/skills/shadcn/assets/shadcn-small.png +0 -0
- package/templates/_base/.cursor/agents/skills/shadcn/assets/shadcn.png +0 -0
- package/templates/_base/.cursor/agents/skills/shadcn/cli.md +257 -0
- package/templates/_base/.cursor/agents/skills/shadcn/customization.md +202 -0
- package/templates/_base/.cursor/agents/skills/shadcn/evals/evals.json +47 -0
- package/templates/_base/.cursor/agents/skills/shadcn/mcp.md +94 -0
- package/templates/_base/.cursor/agents/skills/shadcn/rules/base-vs-radix.md +306 -0
- package/templates/_base/.cursor/agents/skills/shadcn/rules/composition.md +195 -0
- package/templates/_base/.cursor/agents/skills/shadcn/rules/forms.md +192 -0
- package/templates/_base/.cursor/agents/skills/shadcn/rules/icons.md +101 -0
- package/templates/_base/.cursor/agents/skills/shadcn/rules/styling.md +162 -0
- package/templates/_base/.cursor/commands/builder.md +0 -0
- package/templates/_base/.cursor/commands/pr.md +7 -0
- package/templates/_base/.cursor/rules/api_architecture.mdc +268 -0
- package/templates/_base/.cursor/rules/coding_standards.mdc +64 -0
- package/templates/_base/.cursor/rules/convex_rules.mdc +675 -0
- package/templates/_base/.cursor/rules/frontend_rules.mdc +268 -0
- package/templates/_base/.env.convex.example +3 -0
- package/templates/_base/.github/workflows/ci.yml +29 -0
- package/templates/_base/.nvmrc +1 -0
- package/templates/_base/.vscode/settings.json +9 -0
- package/templates/_base/apps/api/auth.config.ts +18 -0
- package/templates/_base/apps/api/functions.ts +99 -0
- package/templates/_base/apps/api/project.json +22 -0
- package/templates/_base/apps/api/schema.ts +11 -0
- package/templates/_base/apps/api/todos/crud.ts +81 -0
- package/templates/_base/apps/api/todos/schema.ts +11 -0
- package/templates/_base/apps/api/todos/types.ts +22 -0
- package/templates/_base/apps/api/tsconfig.json +23 -0
- package/templates/_base/apps/api/types.ts +16 -0
- package/templates/_base/biome.json +114 -0
- package/templates/_base/convex.json +4 -0
- package/templates/_base/emails/project.json +16 -0
- package/templates/_base/emails/tsconfig.json +5 -0
- package/templates/_base/emails/welcome_email.tsx +53 -0
- package/templates/_base/nx.json +29 -0
- package/templates/_base/package.json +73 -0
- package/templates/_base/scripts/sync_convex_env.ts +63 -0
- package/templates/_base/shared/assets/image.d.ts +4 -0
- package/templates/_base/shared/assets/src/styles/global.css +73 -0
- package/templates/_base/shared/assets/tsconfig.json +5 -0
- package/templates/_base/shared/ui/src/base/alert_dialog.tsx +139 -0
- package/templates/_base/shared/ui/src/base/badge.tsx +33 -0
- package/templates/_base/shared/ui/src/base/basic_data_table.tsx +61 -0
- package/templates/_base/shared/ui/src/base/button.tsx +69 -0
- package/templates/_base/shared/ui/src/base/button_group.tsx +82 -0
- package/templates/_base/shared/ui/src/base/card.tsx +79 -0
- package/templates/_base/shared/ui/src/base/checkbox.tsx +26 -0
- package/templates/_base/shared/ui/src/base/command.tsx +165 -0
- package/templates/_base/shared/ui/src/base/dialog.tsx +129 -0
- package/templates/_base/shared/ui/src/base/dropdown_menu.tsx +232 -0
- package/templates/_base/shared/ui/src/base/form.tsx +161 -0
- package/templates/_base/shared/ui/src/base/input.tsx +129 -0
- package/templates/_base/shared/ui/src/base/label.tsx +19 -0
- package/templates/_base/shared/ui/src/base/popover.tsx +46 -0
- package/templates/_base/shared/ui/src/base/radio_group.tsx +49 -0
- package/templates/_base/shared/ui/src/base/resizable.tsx +55 -0
- package/templates/_base/shared/ui/src/base/scroll_area.tsx +44 -0
- package/templates/_base/shared/ui/src/base/select.tsx +151 -0
- package/templates/_base/shared/ui/src/base/separator.tsx +32 -0
- package/templates/_base/shared/ui/src/base/sheet.tsx +130 -0
- package/templates/_base/shared/ui/src/base/side_bar.tsx +688 -0
- package/templates/_base/shared/ui/src/base/skeleton.tsx +7 -0
- package/templates/_base/shared/ui/src/base/spinner.tsx +20 -0
- package/templates/_base/shared/ui/src/base/switch.tsx +27 -0
- package/templates/_base/shared/ui/src/base/table.tsx +91 -0
- package/templates/_base/shared/ui/src/base/text_area.tsx +21 -0
- package/templates/_base/shared/ui/src/base/tooltip.tsx +31 -0
- package/templates/_base/shared/ui/src/base/utils.ts +17 -0
- package/templates/_base/shared/ui/src/hooks/use_keyboard_press.tsx +48 -0
- package/templates/_base/shared/ui/src/hooks/use_keyboard_release.tsx +48 -0
- package/templates/_base/shared/ui/src/hooks/use_mobile.tsx +25 -0
- package/templates/_base/shared/ui/src/hooks/use_mouse_click.tsx +44 -0
- package/templates/_base/shared/ui/src/hooks/use_mouse_location.tsx +55 -0
- package/templates/_base/shared/ui/src/hooks/use_outside_click.tsx +29 -0
- package/templates/_base/shared/ui/src/hooks/use_query_params.tsx +33 -0
- package/templates/_base/shared/ui/tsconfig.json +8 -0
- package/templates/_base/shared/utils/src/convex.ts +3 -0
- package/templates/_base/shared/utils/src/time.ts +12 -0
- package/templates/_base/shared/utils/tsconfig.json +5 -0
- package/templates/_base/skills-lock.json +35 -0
- package/templates/_base/tsconfig.base.json +34 -0
- package/templates/nextjs/.env.example +8 -0
- package/templates/nextjs/index.d.ts +6 -0
- package/templates/nextjs/next-env.d.ts +5 -0
- package/templates/nextjs/next.config.js +22 -0
- package/templates/nextjs/postcss.config.js +17 -0
- package/templates/nextjs/project.json +22 -0
- package/templates/nextjs/src/app/(auth)/layout.tsx +21 -0
- package/templates/nextjs/src/app/(auth)/not-allowed/page.tsx +22 -0
- package/templates/nextjs/src/app/(auth)/sign-in/[[...sign-in]]/page.tsx +15 -0
- package/templates/nextjs/src/app/(dashboard)/layout.tsx +27 -0
- package/templates/nextjs/src/app/(dashboard)/page.tsx +5 -0
- package/templates/nextjs/src/app/(dashboard)/todos/[id]/page.tsx +23 -0
- package/templates/nextjs/src/app/(dashboard)/todos/page.tsx +16 -0
- package/templates/nextjs/src/app/app.css +3 -0
- package/templates/nextjs/src/app/layout.tsx +26 -0
- package/templates/nextjs/src/convex.ts +11 -0
- package/templates/nextjs/src/middleware.ts +18 -0
- package/templates/nextjs/src/providers/convex_provider.tsx +44 -0
- package/templates/nextjs/src/surfaces/home_surface.tsx +22 -0
- package/templates/nextjs/src/surfaces/todos/all_todos_surface.tsx +97 -0
- package/templates/nextjs/src/surfaces/todos/create_todo_sheet.tsx +107 -0
- package/templates/nextjs/src/surfaces/todos/single_todo_surface.tsx +90 -0
- package/templates/nextjs/src/ui/sidebar/nav_link.tsx +36 -0
- package/templates/nextjs/src/ui/sidebar/sidebar.tsx +125 -0
- package/templates/nextjs/src/utils/font.ts +9 -0
- package/templates/nextjs/tsconfig.json +42 -0
- package/templates/react-router/.env.example +8 -0
- package/templates/react-router/postcss.config.js +15 -0
- package/templates/react-router/project.json +23 -0
- package/templates/react-router/public/favicon.ico +0 -0
- package/templates/react-router/react-router.config.ts +9 -0
- package/templates/react-router/src/app.css +3 -0
- package/templates/react-router/src/components/error_boundary.tsx +33 -0
- package/templates/react-router/src/layouts/sidebar/sidebar_aside/sidebar_aside.tsx +76 -0
- package/templates/react-router/src/layouts/sidebar/sidebar_aside/user_menu.tsx +36 -0
- package/templates/react-router/src/layouts/sidebar/sidebar_layout.tsx +22 -0
- package/templates/react-router/src/providers/api_auth_provider.tsx +38 -0
- package/templates/react-router/src/root.tsx +37 -0
- package/templates/react-router/src/routes/auth/layout.tsx +13 -0
- package/templates/react-router/src/routes/auth/sign-in.tsx +13 -0
- package/templates/react-router/src/routes/index.tsx +9 -0
- package/templates/react-router/src/routes/layout.tsx +26 -0
- package/templates/react-router/src/routes/todos/[id].tsx +22 -0
- package/templates/react-router/src/routes/todos/index.tsx +13 -0
- package/templates/react-router/src/routes.ts +12 -0
- package/templates/react-router/src/surfaces/home_surface.tsx +20 -0
- package/templates/react-router/src/surfaces/todos/all_todos_surface.tsx +87 -0
- package/templates/react-router/src/surfaces/todos/create_todo_sheet.tsx +102 -0
- package/templates/react-router/src/surfaces/todos/single_todo_surface.tsx +81 -0
- package/templates/react-router/tsconfig.json +20 -0
- package/templates/react-router/vite.config.ts +40 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-aron-app",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Scaffold a new Convex + Next.js + Clerk full-stack project",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -8,11 +8,14 @@
|
|
|
8
8
|
"create-aron-app": "./dist/index.js"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
|
-
"dist"
|
|
11
|
+
"dist",
|
|
12
|
+
"templates"
|
|
12
13
|
],
|
|
13
14
|
"scripts": {
|
|
14
15
|
"build": "bun build src/index.ts --target=node --outfile=dist/index.js --minify && echo '#!/usr/bin/env node' | cat - dist/index.js > /tmp/create-tmp.js && mv /tmp/create-tmp.js dist/index.js && chmod +x dist/index.js",
|
|
15
16
|
"start": "bun run src/index.ts",
|
|
17
|
+
"patch": "npm version patch",
|
|
18
|
+
"publish": "npm publish --access public",
|
|
16
19
|
"prepublishOnly": "bun run build"
|
|
17
20
|
},
|
|
18
21
|
"dependencies": {
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: clerk
|
|
3
|
+
description: Clerk authentication router. Use when user asks about adding authentication, setting up Clerk, custom sign-in flows, Swift or native iOS auth, native Android auth, Next.js patterns, organizations, syncing users, or testing. Automatically routes to the specific skill based on their task.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Clerk Skills Router
|
|
7
|
+
|
|
8
|
+
## Version Detection
|
|
9
|
+
|
|
10
|
+
Check `package.json` to determine the Clerk SDK version. This determines which patterns to use:
|
|
11
|
+
|
|
12
|
+
| Package | Core 2 (LTS until Jan 2027) | Current |
|
|
13
|
+
|---------|----------------------------|---------|
|
|
14
|
+
| `@clerk/nextjs` | v5–v6 | v7+ |
|
|
15
|
+
| `@clerk/react` or `@clerk/clerk-react` | v5–v6 | v7+ |
|
|
16
|
+
| `@clerk/expo` or `@clerk/clerk-expo` | v1–v2 | v3+ |
|
|
17
|
+
| `@clerk/react-router` | v1–v2 | v3+ |
|
|
18
|
+
| `@clerk/tanstack-react-start` | < v0.26.0 | v0.26.0+ |
|
|
19
|
+
|
|
20
|
+
**Default to current** if the version is unclear or the project is new. Core 2 packages use `@clerk/clerk-react` and `@clerk/clerk-expo` (with `clerk-` prefix); current packages use `@clerk/react` and `@clerk/expo`.
|
|
21
|
+
|
|
22
|
+
All skills are written for the current SDK. When something differs in Core 2, it's noted inline with `> **Core 2 ONLY (skip if current SDK):**` callouts. The exception is `clerk-custom-ui`, which has separate `core-2/` and `core-3/` directories for custom flow hooks since those APIs are entirely different between versions.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## By Task
|
|
27
|
+
|
|
28
|
+
**Adding Clerk to your project** → Use `clerk-setup`
|
|
29
|
+
- Framework detection and quickstart
|
|
30
|
+
- Environment setup, API keys, Keyless flow
|
|
31
|
+
- Migration from other auth providers
|
|
32
|
+
|
|
33
|
+
**Custom sign-in/sign-up UI** → Use `clerk-custom-ui`
|
|
34
|
+
- Custom authentication flows with `useSignIn` / `useSignUp` hooks
|
|
35
|
+
- Appearance and styling (themes, colors, layout)
|
|
36
|
+
- `<Show>` component for conditional rendering
|
|
37
|
+
|
|
38
|
+
**Advanced Next.js patterns** → Use `clerk-nextjs-patterns`
|
|
39
|
+
- Server vs Client auth APIs
|
|
40
|
+
- Middleware strategies
|
|
41
|
+
- Server Actions, caching
|
|
42
|
+
- API route protection
|
|
43
|
+
|
|
44
|
+
**B2B / Organizations** → Use `clerk-orgs`
|
|
45
|
+
- Multi-tenant apps
|
|
46
|
+
- Organization slugs in URLs
|
|
47
|
+
- Roles, permissions, RBAC
|
|
48
|
+
- Member management
|
|
49
|
+
|
|
50
|
+
**Webhooks** → Use `clerk-webhooks`
|
|
51
|
+
- Real-time events
|
|
52
|
+
- Data syncing
|
|
53
|
+
- Notifications & integrations
|
|
54
|
+
|
|
55
|
+
**E2E Testing** → Use `clerk-testing`
|
|
56
|
+
- Playwright/Cypress setup
|
|
57
|
+
- Auth flow testing
|
|
58
|
+
- Test utilities
|
|
59
|
+
|
|
60
|
+
**Swift / native iOS auth** → Use `clerk-swift`
|
|
61
|
+
- Native iOS Swift and SwiftUI projects
|
|
62
|
+
- ClerkKit and ClerkKitUI implementation guidance
|
|
63
|
+
- Source-driven patterns from `clerk-ios`
|
|
64
|
+
|
|
65
|
+
**Android / native mobile auth** → Use `clerk-android`
|
|
66
|
+
- Native Android Kotlin and Jetpack Compose projects
|
|
67
|
+
- `clerk-android-api` and `clerk-android-ui` implementation guidance
|
|
68
|
+
- Source-driven patterns from `clerk-android`
|
|
69
|
+
- Do not use for Expo or React Native projects
|
|
70
|
+
|
|
71
|
+
**Backend REST API** → Use `clerk-backend-api`
|
|
72
|
+
- Browse API tags and endpoints
|
|
73
|
+
- Inspect endpoint schemas
|
|
74
|
+
- Execute API requests with scope enforcement
|
|
75
|
+
|
|
76
|
+
## Quick Navigation
|
|
77
|
+
|
|
78
|
+
If you know your task, you can directly access:
|
|
79
|
+
- `/clerk-setup` - Framework setup
|
|
80
|
+
- `/clerk-custom-ui` - Custom flows & appearance
|
|
81
|
+
- `/clerk-nextjs-patterns` - Next.js patterns
|
|
82
|
+
- `/clerk-orgs` - Organizations
|
|
83
|
+
- `/clerk-webhooks` - Webhooks
|
|
84
|
+
- `/clerk-testing` - Testing
|
|
85
|
+
- `/clerk-swift` - Swift/native iOS
|
|
86
|
+
- `/clerk-android` - Native Android
|
|
87
|
+
- `/clerk-backend-api` - Backend REST API
|
|
88
|
+
|
|
89
|
+
Or describe what you need and I'll recommend the right one.
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: clerk-backend-api
|
|
3
|
+
description: "Clerk backend REST API"
|
|
4
|
+
argument-hint: "[endpointOrTag] [method]"
|
|
5
|
+
allowed-tools: Bash, Read, Grep, Skill, WebFetch
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Options context
|
|
9
|
+
|
|
10
|
+
User Prompt: $ARGUMENTS
|
|
11
|
+
|
|
12
|
+
## API specs context
|
|
13
|
+
|
|
14
|
+
Before doing anything, fetch the available spec versions and tags by running:
|
|
15
|
+
```bash
|
|
16
|
+
bash scripts/api-specs-context.sh
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Use the output to determine the latest version and available tags.
|
|
20
|
+
|
|
21
|
+
**Caching:** If you already fetched the spec context earlier in this conversation, do NOT fetch it again. Reuse the version and tags from the previous call.
|
|
22
|
+
|
|
23
|
+
## Rules
|
|
24
|
+
|
|
25
|
+
- Always disregard endpoints/schemas related to `platform`.
|
|
26
|
+
- Always confirm before performing write requests.
|
|
27
|
+
- For write operations (POST/PUT/PATCH/DELETE), check if `CLERK_BAPI_SCOPES` includes the required scope. If not, ask the user upfront: "This is a write/delete operation and your current scopes don't allow it. Run with --admin to bypass?" Do NOT attempt the request first and fail — ask before executing.
|
|
28
|
+
|
|
29
|
+
## Modes
|
|
30
|
+
|
|
31
|
+
Determine the active mode based on the user prompt in [Options context](#options-context):
|
|
32
|
+
|
|
33
|
+
| Mode | Trigger | Behavior |
|
|
34
|
+
|------|---------|----------|
|
|
35
|
+
| `help` | Prompt is empty, or contains only `help` / `-h` / `--help` | Print usage examples (step 0) |
|
|
36
|
+
| `browse` | Prompt is `tags`, or a tag name (e.g. `Users`) | List all tags or endpoints for a tag |
|
|
37
|
+
| `execute` | Specific endpoint (e.g. `GET /users`) or natural language action (e.g. "get user john_doe") | Look up endpoint, execute request |
|
|
38
|
+
| `detail` | Endpoint + `help` / `-h` / `--help` (e.g. `GET /users help`) | Show endpoint schema, don't execute |
|
|
39
|
+
|
|
40
|
+
## Your Task
|
|
41
|
+
|
|
42
|
+
Use the **LATEST VERSION** from [API specs context](#api-specs-context) by default. If the user specifies a different version (e.g. `--version 2024-10-01`), use that version instead.
|
|
43
|
+
|
|
44
|
+
Determine the active mode, then follow the applicable steps below.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
### 0. Print usage
|
|
49
|
+
|
|
50
|
+
**Modes:** `help` only — **Skip** for `browse`, `execute`, and `detail`.
|
|
51
|
+
|
|
52
|
+
Print the following examples to the user verbatim:
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
Browse
|
|
56
|
+
/clerk-backend-api tags — list all tags
|
|
57
|
+
/clerk-backend-api Users — browse endpoints for the Users tag
|
|
58
|
+
/clerk-backend-api Users version 2025-11-10.yml — browse using a different version
|
|
59
|
+
|
|
60
|
+
Execute
|
|
61
|
+
/clerk-backend-api GET /users — fetch all users
|
|
62
|
+
/clerk-backend-api get user john_doe — natural language works too
|
|
63
|
+
/clerk-backend-api POST /invitations — create an invitation
|
|
64
|
+
|
|
65
|
+
Inspect
|
|
66
|
+
/clerk-backend-api GET /users help — show endpoint schema without executing
|
|
67
|
+
/clerk-backend-api POST /invitations -h — view request/response details
|
|
68
|
+
|
|
69
|
+
Options
|
|
70
|
+
--admin — bypass scope restrictions for write/delete
|
|
71
|
+
--version [date], version [date] — use a specific spec version
|
|
72
|
+
--help, -h, help — inspect endpoint instead of executing
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Stop here.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
### 1. Fetch tags
|
|
80
|
+
|
|
81
|
+
**Modes:** `browse` (when prompt is `tags` or no tag specified) — **Skip** for `help`, `execute`, and `detail`.
|
|
82
|
+
|
|
83
|
+
If using a non-latest version, fetch tags for that version:
|
|
84
|
+
```bash
|
|
85
|
+
curl -s https://raw.githubusercontent.com/clerk/openapi-specs/main/bapi/${version_name} | node scripts/extract-tags.js
|
|
86
|
+
```
|
|
87
|
+
Otherwise, use the **TAGS** already in [API specs context](#api-specs-context).
|
|
88
|
+
|
|
89
|
+
Share tags in a table and prompt the user to select a query.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
### 2. Fetch tag endpoints
|
|
94
|
+
|
|
95
|
+
**Modes:** `browse` (when a tag name is provided) — **Skip** for `help`, `execute`, and `detail`.
|
|
96
|
+
|
|
97
|
+
Fetch all endpoints for the identified tag:
|
|
98
|
+
```bash
|
|
99
|
+
curl -s https://raw.githubusercontent.com/clerk/openapi-specs/main/bapi/${version_name} | bash scripts/extract-tag-endpoints.sh "${tag_name}"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Share the results (endpoints, schemas, parameters) with the user.
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
### 3. Fetch endpoint detail
|
|
107
|
+
|
|
108
|
+
**Modes:** `execute`, `detail` — **Skip** for `help` and `browse`.
|
|
109
|
+
|
|
110
|
+
For natural language prompts in `execute` mode, first identify the matching endpoint by searching the tags in context. Fetch tag endpoints if needed to resolve the exact path and method.
|
|
111
|
+
|
|
112
|
+
Extract the full endpoint definition:
|
|
113
|
+
```bash
|
|
114
|
+
curl -s https://raw.githubusercontent.com/clerk/openapi-specs/main/bapi/${version_name} | bash scripts/extract-endpoint-detail.sh "${path}" "${method}"
|
|
115
|
+
```
|
|
116
|
+
- `${path}` — e.g. `/users/{user_id}`
|
|
117
|
+
- `${method}` — lowercase, e.g. `get`
|
|
118
|
+
|
|
119
|
+
**`detail` mode:** Share the endpoint definition and schemas with the user. Stop here.
|
|
120
|
+
|
|
121
|
+
**`execute` mode:** Continue to step 4.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
### 4. Execute request
|
|
126
|
+
|
|
127
|
+
**Modes:** `execute` only.
|
|
128
|
+
|
|
129
|
+
Use the endpoint definition from step 3 to build the request:
|
|
130
|
+
|
|
131
|
+
1. Identify required and optional parameters from the spec.
|
|
132
|
+
2. Ask the user for any required path/query/body parameters.
|
|
133
|
+
3. Execute via the request script:
|
|
134
|
+
```bash
|
|
135
|
+
bash scripts/execute-request.sh [--admin] ${METHOD} "${path}" ['${body_json}']
|
|
136
|
+
```
|
|
137
|
+
- `--admin` — pass this if the user confirmed admin bypass (see Rules)
|
|
138
|
+
- `${METHOD}` — uppercase HTTP method
|
|
139
|
+
- `${path}` — resolved path with parameters filled in (e.g. `/users/user_abc123`)
|
|
140
|
+
- `${body_json}` — optional JSON body for POST/PUT/PATCH
|
|
141
|
+
|
|
142
|
+
4. Share the response with the user.
|
package/templates/_base/.cursor/agents/skills/clerk/clerk-backend-api/scripts/api-specs-context.sh
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# Fetches all available BAPI spec versions, determines the latest,
|
|
4
|
+
# and extracts tags from it. Output is used as skill context.
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
API_URL="https://api.github.com/repos/clerk/openapi-specs/contents/bapi"
|
|
9
|
+
RAW_BASE="https://raw.githubusercontent.com/clerk/openapi-specs/main/bapi"
|
|
10
|
+
|
|
11
|
+
# Fetch version list, parse dates, sort, pick latest
|
|
12
|
+
versions=$(curl -s "$API_URL" | node -e "
|
|
13
|
+
let d='';
|
|
14
|
+
process.stdin.on('data',c=>d+=c);
|
|
15
|
+
process.stdin.on('end',()=>{
|
|
16
|
+
const items = JSON.parse(d)
|
|
17
|
+
.map(i=>i.name)
|
|
18
|
+
.filter(n=>/^\d{4}-\d{2}-\d{2}\.yml$/.test(n))
|
|
19
|
+
.sort();
|
|
20
|
+
items.forEach(n=>console.log(n));
|
|
21
|
+
});
|
|
22
|
+
")
|
|
23
|
+
|
|
24
|
+
latest=$(echo "$versions" | tail -1)
|
|
25
|
+
|
|
26
|
+
echo "AVAILABLE VERSIONS: $(echo "$versions" | tr '\n' ' ')"
|
|
27
|
+
echo "LATEST VERSION: $latest"
|
|
28
|
+
echo ""
|
|
29
|
+
echo "TAGS:"
|
|
30
|
+
curl -s "${RAW_BASE}/${latest}" | node "$(dirname "$0")/extract-tags.js"
|
package/templates/_base/.cursor/agents/skills/clerk/clerk-backend-api/scripts/execute-request.sh
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# Execute a Clerk Backend API request with scope enforcement.
|
|
4
|
+
#
|
|
5
|
+
# Usage: bash execute-request.sh [--admin] <METHOD> <PATH> [BODY]
|
|
6
|
+
#
|
|
7
|
+
# Scope enforcement:
|
|
8
|
+
# GET — always allowed
|
|
9
|
+
# POST, PUT, PATCH — requires CLERK_BAPI_SCOPES="write" or --admin flag
|
|
10
|
+
# DELETE — requires CLERK_BAPI_SCOPES="write,delete" or --admin flag
|
|
11
|
+
|
|
12
|
+
set -euo pipefail
|
|
13
|
+
|
|
14
|
+
# Walk up from $PWD to find .env/.env.local (mirrors Clerk CLI behavior).
|
|
15
|
+
# Stops at the first directory that provides CLERK_SECRET_KEY.
|
|
16
|
+
_dir="$PWD"
|
|
17
|
+
while true; do
|
|
18
|
+
for _envfile in "$_dir/.env" "$_dir/.env.local"; do
|
|
19
|
+
if [[ -f "$_envfile" ]]; then
|
|
20
|
+
set -a
|
|
21
|
+
source "$_envfile"
|
|
22
|
+
set +a
|
|
23
|
+
fi
|
|
24
|
+
done
|
|
25
|
+
[[ -n "${CLERK_SECRET_KEY:-}" ]] && break
|
|
26
|
+
_parent="$(dirname "$_dir")"
|
|
27
|
+
[[ "$_parent" == "$_dir" ]] && break
|
|
28
|
+
_dir="$_parent"
|
|
29
|
+
done
|
|
30
|
+
unset _dir _parent _envfile
|
|
31
|
+
|
|
32
|
+
# Parse --admin flag
|
|
33
|
+
ADMIN=false
|
|
34
|
+
if [[ "${1:-}" == "--admin" ]]; then
|
|
35
|
+
ADMIN=true
|
|
36
|
+
shift
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
METHOD="${1:?Usage: execute-request.sh [--admin] <METHOD> <PATH> [BODY]}"
|
|
40
|
+
PATH_ARG="${2:?Usage: execute-request.sh [--admin] <METHOD> <PATH> [BODY]}"
|
|
41
|
+
BODY="${3:-}"
|
|
42
|
+
|
|
43
|
+
METHOD_UPPER=$(echo "$METHOD" | tr '[:lower:]' '[:upper:]')
|
|
44
|
+
SCOPES="${CLERK_BAPI_SCOPES:-}"
|
|
45
|
+
|
|
46
|
+
# Scope check
|
|
47
|
+
if [[ "$ADMIN" == false ]]; then
|
|
48
|
+
case "$METHOD_UPPER" in
|
|
49
|
+
GET)
|
|
50
|
+
;; # always allowed
|
|
51
|
+
POST|PUT|PATCH)
|
|
52
|
+
if [[ "$SCOPES" != *"write"* ]]; then
|
|
53
|
+
echo "ERROR: $METHOD_UPPER requests require CLERK_BAPI_SCOPES=\"write\" or --admin flag." >&2
|
|
54
|
+
echo "Current CLERK_BAPI_SCOPES: \"$SCOPES\"" >&2
|
|
55
|
+
exit 1
|
|
56
|
+
fi
|
|
57
|
+
;;
|
|
58
|
+
DELETE)
|
|
59
|
+
if [[ "$SCOPES" != *"write"* ]] || [[ "$SCOPES" != *"delete"* ]]; then
|
|
60
|
+
echo "ERROR: DELETE requests require CLERK_BAPI_SCOPES=\"write,delete\" or --admin flag." >&2
|
|
61
|
+
echo "Current CLERK_BAPI_SCOPES: \"$SCOPES\"" >&2
|
|
62
|
+
exit 1
|
|
63
|
+
fi
|
|
64
|
+
;;
|
|
65
|
+
*)
|
|
66
|
+
echo "ERROR: Unknown HTTP method: $METHOD_UPPER" >&2
|
|
67
|
+
exit 1
|
|
68
|
+
;;
|
|
69
|
+
esac
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
# Base URL: use CLERK_REST_API_URL if set, otherwise default to production
|
|
73
|
+
BASE_URL="${CLERK_REST_API_URL:-https://api.clerk.com}"
|
|
74
|
+
|
|
75
|
+
# Build curl command
|
|
76
|
+
CURL_ARGS=(
|
|
77
|
+
-s
|
|
78
|
+
-X "$METHOD_UPPER"
|
|
79
|
+
"${BASE_URL}/v1${PATH_ARG}"
|
|
80
|
+
-H "Authorization: Bearer ${CLERK_SECRET_KEY:?CLERK_SECRET_KEY is not set}"
|
|
81
|
+
-H "Content-Type: application/json"
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
if [[ -n "$BODY" ]]; then
|
|
85
|
+
CURL_ARGS+=(-d "$BODY")
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
curl "${CURL_ARGS[@]}"
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# extract-endpoint-detail.sh
|
|
3
|
+
#
|
|
4
|
+
# Extracts full details for a specific endpoint from an OpenAPI YAML spec (stdin),
|
|
5
|
+
# including parameters, request body, responses, and all referenced component schemas.
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# curl -s <spec-url> | bash extract-endpoint-detail.sh "/users/{user_id}/billing/subscription" "get"
|
|
9
|
+
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
|
|
12
|
+
ENDPOINT="${1:?Usage: extract-endpoint-detail.sh <path> <method>}"
|
|
13
|
+
METHOD="${2:?Usage: extract-endpoint-detail.sh <path> <method>}"
|
|
14
|
+
TMPDIR_WORK=$(mktemp -d)
|
|
15
|
+
trap 'rm -rf "$TMPDIR_WORK"' EXIT
|
|
16
|
+
|
|
17
|
+
SPEC="$TMPDIR_WORK/spec.yml"
|
|
18
|
+
cat > "$SPEC"
|
|
19
|
+
|
|
20
|
+
node - "$ENDPOINT" "$METHOD" "$SPEC" <<'SCRIPT'
|
|
21
|
+
const fs = require("fs");
|
|
22
|
+
const endpoint = process.argv[2];
|
|
23
|
+
const method = process.argv[3].toLowerCase();
|
|
24
|
+
const specFile = process.argv[4];
|
|
25
|
+
const lines = fs.readFileSync(specFile, "utf8").split("\n");
|
|
26
|
+
|
|
27
|
+
const httpMethods = ["get", "post", "put", "patch", "delete", "options", "head"];
|
|
28
|
+
|
|
29
|
+
// Locate paths: and components: sections
|
|
30
|
+
let pathsStart = -1, pathsEnd = -1, componentsStart = -1;
|
|
31
|
+
for (let i = 0; i < lines.length; i++) {
|
|
32
|
+
if (/^paths:\s*$/.test(lines[i])) pathsStart = i;
|
|
33
|
+
else if (pathsStart >= 0 && pathsEnd < 0 && /^\S/.test(lines[i]) && i > pathsStart) pathsEnd = i;
|
|
34
|
+
if (/^components:\s*$/.test(lines[i])) componentsStart = i;
|
|
35
|
+
}
|
|
36
|
+
if (pathsEnd < 0) pathsEnd = lines.length;
|
|
37
|
+
|
|
38
|
+
// Find the target path + method block
|
|
39
|
+
let targetStart = -1, targetEnd = -1;
|
|
40
|
+
let currentPath = null;
|
|
41
|
+
|
|
42
|
+
for (let i = pathsStart + 1; i < pathsEnd; i++) {
|
|
43
|
+
const line = lines[i];
|
|
44
|
+
|
|
45
|
+
// Path line: exactly 2 spaces + /
|
|
46
|
+
if (/^ {2}\/\S/.test(line)) {
|
|
47
|
+
currentPath = line.trim().replace(/:$/, "");
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Method line: exactly 4 spaces + method name
|
|
52
|
+
const methodMatch = line.match(/^ {4}(\w+):\s*$/);
|
|
53
|
+
if (methodMatch && httpMethods.includes(methodMatch[1])) {
|
|
54
|
+
if (currentPath === endpoint && methodMatch[1] === method) {
|
|
55
|
+
targetStart = i;
|
|
56
|
+
// Find end of this method block
|
|
57
|
+
for (let j = i + 1; j < pathsEnd; j++) {
|
|
58
|
+
const nextLine = lines[j];
|
|
59
|
+
// New method or new path
|
|
60
|
+
if (/^ {2}\/\S/.test(nextLine) || (/^ {4}\w+:\s*$/.test(nextLine) && httpMethods.some(m => nextLine.trim().startsWith(m + ":")))) {
|
|
61
|
+
targetEnd = j;
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (targetEnd < 0) targetEnd = pathsEnd;
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (targetStart < 0) {
|
|
72
|
+
console.error(`Endpoint not found: ${method.toUpperCase()} ${endpoint}`);
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const blockLines = lines.slice(targetStart, targetEnd);
|
|
77
|
+
|
|
78
|
+
// Collect all $refs from the block
|
|
79
|
+
const allRefs = new Set();
|
|
80
|
+
for (const bl of blockLines) {
|
|
81
|
+
const refMatch = bl.match(/\$ref:\s*['"]?(#\/[^'"}\s]+)['"]?/);
|
|
82
|
+
if (refMatch) allRefs.add(refMatch[1]);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Resolve a $ref path to the raw YAML lines for that component
|
|
86
|
+
function resolveRef(ref) {
|
|
87
|
+
const parts = ref.replace("#/", "").split("/");
|
|
88
|
+
// Find the component in the file by walking indentation
|
|
89
|
+
let searchStart = 0;
|
|
90
|
+
for (let p = 0; p < parts.length; p++) {
|
|
91
|
+
const indent = p * 2;
|
|
92
|
+
const target = " ".repeat(indent) + parts[p] + ":";
|
|
93
|
+
let found = false;
|
|
94
|
+
for (let i = searchStart; i < lines.length; i++) {
|
|
95
|
+
if (lines[i].startsWith(target) && (lines[i] === target || lines[i][target.length] === " ")) {
|
|
96
|
+
searchStart = i + 1;
|
|
97
|
+
found = true;
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (!found) return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Collect lines for this component (until same or lower indent)
|
|
105
|
+
const componentStart = searchStart - 1;
|
|
106
|
+
const baseIndent = parts.length * 2;
|
|
107
|
+
const result = [];
|
|
108
|
+
for (let i = searchStart; i < lines.length; i++) {
|
|
109
|
+
const line = lines[i];
|
|
110
|
+
if (line.trim() === "") { result.push(line); continue; }
|
|
111
|
+
const lineIndent = line.length - line.trimStart().length;
|
|
112
|
+
if (lineIndent < baseIndent) break;
|
|
113
|
+
result.push(line);
|
|
114
|
+
}
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Recursively resolve refs from component bodies
|
|
119
|
+
function collectDeepRefs(refSet, visited) {
|
|
120
|
+
const toProcess = [...refSet].filter(r => !visited.has(r));
|
|
121
|
+
for (const ref of toProcess) {
|
|
122
|
+
visited.add(ref);
|
|
123
|
+
const body = resolveRef(ref);
|
|
124
|
+
if (!body) continue;
|
|
125
|
+
for (const bl of body) {
|
|
126
|
+
const refMatch = bl.match(/\$ref:\s*['"]?(#\/[^'"}\s]+)['"]?/);
|
|
127
|
+
if (refMatch && !visited.has(refMatch[1])) {
|
|
128
|
+
refSet.add(refMatch[1]);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// Recurse if new refs were found
|
|
133
|
+
const newRefs = [...refSet].filter(r => !visited.has(r));
|
|
134
|
+
if (newRefs.length > 0) collectDeepRefs(refSet, visited);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
collectDeepRefs(allRefs, new Set());
|
|
138
|
+
|
|
139
|
+
// Output
|
|
140
|
+
console.log(`## \`${method.toUpperCase()}\` \`${endpoint}\`\n`);
|
|
141
|
+
console.log("### Endpoint Definition\n");
|
|
142
|
+
console.log("```yaml");
|
|
143
|
+
for (const bl of blockLines) {
|
|
144
|
+
console.log(bl);
|
|
145
|
+
}
|
|
146
|
+
console.log("```\n");
|
|
147
|
+
|
|
148
|
+
if (allRefs.size > 0) {
|
|
149
|
+
console.log(`### Referenced Components (${allRefs.size})\n`);
|
|
150
|
+
const sorted = [...allRefs].sort();
|
|
151
|
+
for (const ref of sorted) {
|
|
152
|
+
const name = ref.split("/").pop();
|
|
153
|
+
const category = ref.replace("#/", "").split("/").slice(0, -1).join("/");
|
|
154
|
+
console.log(`#### \`${name}\` (${category})\n`);
|
|
155
|
+
const body = resolveRef(ref);
|
|
156
|
+
if (body) {
|
|
157
|
+
console.log("```yaml");
|
|
158
|
+
for (const bl of body) console.log(bl);
|
|
159
|
+
console.log("```\n");
|
|
160
|
+
} else {
|
|
161
|
+
console.log("_(could not resolve)_\n");
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
SCRIPT
|