Skip to content
Open
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
2 changes: 1 addition & 1 deletion .agents/skills/convex-create-component/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: convex-create-component
description: Designs and builds Convex components with isolated tables, clear boundaries, and app-facing wrappers. Use this skill when creating a new Convex component, extracting reusable backend logic into a component, building a third-party integration that owns its own tables, packaging Convex functionality for reuse, or when the user mentions defineComponent, app.use, ComponentApi, ctx.runQuery/runMutation across component boundaries, or wants to separate concerns into isolated Convex modules.
description: Builds reusable Convex components with isolated tables and app-facing APIs. Use for new components, reusable backend modules, integrations, or component boundary work.
---

# Convex Create Component
Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/convex-migration-helper/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: convex-migration-helper
description: Plans and executes safe Convex schema and data migrations using the widen-migrate-narrow workflow and the @convex-dev/migrations component. Use this skill when a deployment fails schema validation, existing documents need backfilling, fields need adding or removing or changing type, tables need splitting or merging, or a zero-downtime migration strategy is needed. Also use when the user mentions breaking schema changes, multi-deploy rollouts, or data transformations on existing Convex tables.
description: Plans Convex schema and data migrations with widen-migrate-narrow and @convex-dev/migrations. Use for breaking schema changes, backfills, table reshaping, or zero-downtime rollouts.
---

# Convex Migration Helper
Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/convex-performance-audit/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: convex-performance-audit
description: Audits and optimizes Convex application performance across hot-path reads, write contention, subscription cost, and function limits. Use this skill when a Convex feature is slow or expensive, npx convex insights shows high bytes or documents read, OCC conflict errors or mutation retries appear, subscriptions or UI updates are costly, functions hit execution or transaction limits, or the user mentions performance, latency, read amplification, or invalidation problems in a Convex app.
description: Audits Convex performance for reads, subscriptions, write contention, and function limits. Use for slow features, insights findings, OCC conflicts, or read amplification.
---

# Convex Performance Audit
Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/convex-quickstart/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: convex-quickstart
description: Initializes a new Convex project from scratch or adds Convex to an existing app. Use this skill when starting a new project with Convex, scaffolding with npm create convex@latest, adding Convex to an existing React, Next.js, Vue, Svelte, or other frontend, wiring up ConvexProvider, configuring environment variables for the deployment URL, or running npx convex dev for the first time, even if the user just says "set up Convex" or "add a backend."
description: Creates or adds Convex to an app. Use for new Convex projects, npm create convex@latest, frontend setup, env vars, or the first npx convex dev run.
---

# Convex Quickstart
Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/convex-setup-auth/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: convex-setup-auth
description: Sets up Convex authentication with user management, identity mapping, and access control. Use this skill when adding login or signup to a Convex app, configuring Convex Auth, Clerk, WorkOS AuthKit, Auth0, or custom JWT providers, wiring auth.config.ts, protecting queries and mutations with ctx.auth.getUserIdentity(), creating a users table with identity mapping, or setting up role-based access control, even if the user just says "add auth" or "make it require login."
description: Sets up Convex auth, identity mapping, and access control. Use for login, auth providers, users tables, protected functions, or roles in a Convex app.
---

# Convex Authentication Setup
Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/convex/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: convex
description: Routing skill for Convex work in this repo. Use when the user explicitly invokes the `convex` skill, asks which Convex workflow or skill to use, or says they are working on a Convex app without naming a specific task yet. Do not prefer this skill when the request is clearly about setting up Convex, authentication, components, migrations, or performance.
description: Routes general Convex requests to the right project skill. Use when the user asks which Convex skill to use or gives an underspecified Convex app task.
---

# Convex
Expand Down
25 changes: 25 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
node_modules
npm-debug.log*
*.log

.git
.gitignore
.DS_Store

.env
.env.local
.env.*.local
.convex

dist
build
debug/dist
.vite
.cache
*.tsbuildinfo

data
*.db
*.db-journal
*.sqlite
*.sqlite-journal
30 changes: 30 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
FROM node:22-bookworm-slim

WORKDIR /app

ENV NODE_ENV=development

RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates curl git gnupg \
&& curl -fsSL https://ngrok-agent.s3.amazonaws.com/ngrok.asc | tee /etc/apt/trusted.gpg.d/ngrok.asc >/dev/null \
&& echo "deb https://ngrok-agent.s3.amazonaws.com buster main" | tee /etc/apt/sources.list.d/ngrok.list \
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 ngrok APT repo uses buster dist on a bookworm base image

node:22-bookworm-slim is Debian 12. Pinning the ngrok APT source to buster (Debian 10) is what ngrok's own docs prescribe (they only publish one dist), but it can cause apt-get update to emit warnings on stricter APT configurations and may silently break if ngrok ever publishes a bookworm entry that conflicts. Adding a comment explaining why buster is intentional would prevent future confusion.

&& apt-get update \
&& apt-get install -y --no-install-recommends ngrok \
&& rm -rf /var/lib/apt/lists/*

# Create non-root user — Claude Code blocks bypassPermissions when running as root
RUN useradd -m -u 1001 boop

COPY package*.json ./
RUN npm ci && npm install -g @anthropic-ai/claude-code

COPY . .

RUN mkdir -p /home/boop/.claude /home/boop/.convex /home/boop/.sendblue \
&& chown -R boop:boop /app /home/boop

USER boop

EXPOSE 3456 5173

CMD ["npm", "run", "dev"]
1 change: 1 addition & 0 deletions debug/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export default defineConfig(({ mode }) => {
envDir: PROJECT_ROOT,
plugins: [react(), tailwindcss()],
server: {
host: true,
port: 5173,
proxy: {
"/api": {
Expand Down
38 changes: 38 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
services:
boop:
build:
context: .
image: boop-agent:local
command: npm run dev
init: true
restart: unless-stopped
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 restart: unless-stopped loops on first-time setup failure

scripts/dev.mjs exits with code 1 when convex/_generated/api.js doesn't exist yet (standard on a fresh clone). With restart: unless-stopped, the container will restart endlessly until the types are generated, which can't happen inside this same container without npx convex dev --once being run first. Consider using restart: "no" for the initial experience, or documenting a docker-compose run boop npx convex dev --once prerequisite step.

stop_signal: SIGINT
env_file:
- .env.local
environment:
NODE_ENV: development
BOOP_UPSTREAM_CHECK: "false"
ports:
- "3456:3456"
- "5173:5173"
volumes:
- .:/app
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Bind-mount ownership mismatch breaks writes on Linux hosts

The .:/app bind mount exposes all source files owned by the host user (e.g. UID 1000). The container runs as boop (UID 1001), so any write attempted during dev — Vite's .vite cache, Convex's convex/_generated/ type files, *.tsbuildinfo — will fail with EACCES on Linux. macOS users won't hit this because Docker Desktop virtualises the FS, but CI and Linux developers will.

The common fix is to propagate the caller's UID/GID into the compose service:

    user: "${UID:-1001}:${GID:-1001}"

Or set UID and GID explicitly in a .env (beside docker-compose.yml) and document the requirement. The useradd -u 1001 in the Dockerfile would also need to match whatever UID is chosen, or the container can drop the fixed UID and rely entirely on the compose override.

- boop_node_modules:/app/node_modules
- boop_npm_cache:/home/boop/.npm
- ${HOME}/.convex:/home/boop/.convex
- ${HOME}/.claude:/home/boop/.claude
- ${HOME}/.sendblue:/home/boop/.sendblue
healthcheck:
test:
[
"CMD-SHELL",
"node -e \"fetch('http://127.0.0.1:3456/health').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))\"",
]
interval: 30s
timeout: 5s
retries: 3
start_period: 30s

volumes:
boop_node_modules:
boop_npm_cache:
88 changes: 88 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 12 additions & 1 deletion scripts/dev.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ const debugChild = run(
/Local:\s+http/,
);
const children = [serverChild, convexChild, debugChild];
const criticalChildren = [serverChild, convexChild, debugChild];

let ngrokUrlReady = Promise.resolve(null);
if (useNgrok && ngrokInstalled) {
Expand All @@ -220,6 +221,16 @@ if (useNgrok && ngrokInstalled) {
: ["http", port, "--log=stdout", "--log-format=term", "--log-level=info"];
const ngrokChild = run("ngrok", "ngrok", args);
children.push(ngrokChild);
ngrokChild.on("exit", (code) => {
if (!shuttingDown && code !== null && code !== 0) {
console.error(
`${C.ngrok}ngrok${C.reset} │ exited with code ${code}; keeping local dev services running.`,
);
console.error(
`${C.dim} iMessage replies via Sendblue need a public tunnel. Check ngrok auth/network, set a stable PUBLIC_URL, or use npm run dev:parallel for local-only work.${C.reset}`,
);
}
});
ngrokUrlReady = waitForNgrokUrl().catch(() => null);
}

Expand Down Expand Up @@ -299,7 +310,7 @@ const shutdown = (code = 0) => {
};
process.on("SIGINT", () => shutdown(0));
process.on("SIGTERM", () => shutdown(0));
for (const c of children) {
for (const c of criticalChildren) {
c.on("exit", (code) => {
if (!shuttingDown && code !== null && code !== 0) {
console.error(`\nA child process exited with code ${code}. Shutting down.`);
Expand Down
12 changes: 6 additions & 6 deletions skills-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,32 @@
"convex": {
"source": "get-convex/agent-skills",
"sourceType": "github",
"computedHash": "613ee9955985085d0fca8f96e1fc6d7cfd204dffa203499a1d508b8def76577b"
"computedHash": "70ecfb9cd4439ccbf6570b6dc23eab53f7ce7dcf70ef63bbfdf8f4f21353dfb4"
},
"convex-create-component": {
"source": "get-convex/agent-skills",
"sourceType": "github",
"computedHash": "d110fca7f65b4919367e6fc63a93bf54abea2cf5e4e097234c947559ffa6e527"
"computedHash": "e4ad9cbe6d2bb0d5171dfd04019bc4ff228f26fb52312429376c885d2ec4935a"
},
"convex-migration-helper": {
"source": "get-convex/agent-skills",
"sourceType": "github",
"computedHash": "46d1ac354eefbed05e1367d828e893816c13302276080bfaf6bcd828281be486"
"computedHash": "c6416032d2f2e947ebe9d6b2389d89592d0229a0e6c4202f9a1197f2bd76019f"
},
"convex-performance-audit": {
"source": "get-convex/agent-skills",
"sourceType": "github",
"computedHash": "30ea0d3c259df011e44ea9b70502ab272f5ac3bd1fb3672ae18489ba99b2c4ae"
"computedHash": "c048b44beca5616108bfebc9822b6238cbff5c99facb88b3cf3d3a2af0dac502"
},
"convex-quickstart": {
"source": "get-convex/agent-skills",
"sourceType": "github",
"computedHash": "8ae9e1b02f526ea65e7895fac82af74142cd8e70e364d9dae9dbf79a296fb5ef"
"computedHash": "c95728c430a441325c865b06f0f0e912923c34deecbf6f24e9f03e13046b469c"
},
"convex-setup-auth": {
"source": "get-convex/agent-skills",
"sourceType": "github",
"computedHash": "e719d31d1ab0d19ca7b942d1154d3ff436b5c156900eea9866c2aaeb910a1388"
"computedHash": "f60559165edd5b616fda726ed5726c798e33f905361ed9892ab6013e53ab2588"
}
}
}