Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
f302bc4
Refactor plan
djanogly Mar 5, 2026
691e831
unify-markdown-rendering-utility & unify-inbox-widget-notification-cues
djanogly Mar 5, 2026
f6662a5
decompose web inbox page
djanogly Mar 5, 2026
4b9c028
Archive proposals
djanogly Mar 5, 2026
2108053
modularize convex notifications domain
djanogly Mar 5, 2026
10f5c45
Harden convex types
djanogly Mar 5, 2026
c612275
archive proposals
djanogly Mar 5, 2026
1efae89
Split convex series engine
djanogly Mar 5, 2026
d8a8237
Archive proposals
djanogly Mar 5, 2026
bd310b0
Shared utility updates
djanogly Mar 5, 2026
6dcf67b
archive proposals
djanogly Mar 5, 2026
465b38e
Help center refactor
djanogly Mar 5, 2026
dc3b4d8
Archive proposals
djanogly Mar 5, 2026
05d1366
split-react-native-sdk-orchestrator & decompose-react-native-sdk-mess…
djanogly Mar 5, 2026
8b3eab6
centralize-visitor-readable-id-generator
djanogly Mar 5, 2026
bc29b26
Audience rules and type fix
djanogly Mar 5, 2026
be8f489
settings and survey decomp
djanogly Mar 5, 2026
77071fd
web and convex
djanogly Mar 5, 2026
82b832f
Reporting
djanogly Mar 5, 2026
f790616
Widget and convex auth wrapper - final high wrapper list
djanogly Mar 5, 2026
680b227
remaining slices
djanogly Mar 5, 2026
f006ac3
roadmap
djanogly Mar 5, 2026
b61d550
Convex notif triggers +
djanogly Mar 6, 2026
3b2cf56
web tour editor & outbound
djanogly Mar 6, 2026
616f01f
share outbound logic
djanogly Mar 6, 2026
5fa2c68
archive proposals and handoff context
djanogly Mar 6, 2026
4f42c85
Outbound continued
djanogly Mar 6, 2026
327e113
outbound and campaign schema refactor
djanogly Mar 6, 2026
d372f4b
convex schema
djanogly Mar 6, 2026
6f7d11d
Simplify knowledge base proposal
djanogly Mar 6, 2026
78b9d46
Fix E2E
djanogly Mar 6, 2026
85e278d
assess progress
djanogly Mar 6, 2026
3da8fd3
widget hooks
djanogly Mar 6, 2026
aeaac47
tour route part 1
djanogly Mar 7, 2026
f3efa43
tour overlay
djanogly Mar 7, 2026
500e212
series runtime
djanogly Mar 7, 2026
feb97d2
tour progress
djanogly Mar 7, 2026
2e63d31
Settings pt1
djanogly Mar 7, 2026
50814f5
messenger settings
djanogly Mar 7, 2026
48dcd6e
messenger
djanogly Mar 7, 2026
bd310e6
Pr/merge knowledge (#11)
djanogly Mar 8, 2026
da2ef41
fix convex type issues
djanogly Mar 9, 2026
0c3a0c7
Fix failing convex tests
djanogly Mar 9, 2026
0151847
fix builds. Need to work on sdk-core
djanogly Mar 9, 2026
cb1ac2a
fix sdk-core build
djanogly Mar 9, 2026
872a00e
Fix rn-sdk build
djanogly Mar 9, 2026
7573eb3
fix types for api calls in web
djanogly Mar 9, 2026
75de6a8
fix mobile and rn-sdk typecheck
djanogly Mar 9, 2026
6a1a024
fix widget typecheck
djanogly Mar 9, 2026
584383a
pnpm install
djanogly Mar 9, 2026
7423c2c
fix failing vitests except ConversationView
djanogly Mar 9, 2026
965452f
Add proposal for fixing skipped vitests
djanogly Mar 9, 2026
6beebed
Restore playwright widget tests and fix comments
djanogly Mar 10, 2026
d032dda
remove unused functions
djanogly Mar 10, 2026
c6c0053
delete temp file
djanogly Mar 10, 2026
1a15b6b
fix types
djanogly Mar 10, 2026
de7eee2
types
djanogly Mar 10, 2026
79f5e2c
fix for auth guard CI
djanogly Mar 10, 2026
10036d1
Relax CI
djanogly Mar 10, 2026
711dc3d
fix regressions + type refactor proposal
djanogly Mar 10, 2026
cdb5e18
update dependency allowlist
djanogly Mar 10, 2026
b538a50
Update todo list
djanogly Mar 10, 2026
cba2b66
Remove some unknowns and anys
djanogly Mar 10, 2026
7342af2
New refactor proposals
djanogly Mar 10, 2026
cb1868a
First convex backend cluster move away from getInternalRef(name: string)
djanogly Mar 10, 2026
f30dde2
close-repo-wide-convex-ref-hardening-gaps & introduce-widget-local-co…
djanogly Mar 10, 2026
4b1812a
web local convex wrapper hooks complete - all tests passing
djanogly Mar 11, 2026
11ac6ca
refactor RN-SDK - needs testing
djanogly Mar 11, 2026
cecd2a2
Archive Proposals
djanogly Mar 11, 2026
fba7e62
Refactor Proposals
djanogly Mar 11, 2026
f9bc0f4
Stale registry cleanup
djanogly Mar 11, 2026
ff57d7b
broaden CI linting and typechecks
djanogly Mar 11, 2026
92497f1
fix tuple issue
djanogly Mar 11, 2026
286ed94
Merge pull request #15 from opencom-org/pr/refactor-2
djanogly Mar 11, 2026
a4b3252
Stop makeFunctionReference being defined inside components (#17)
djanogly Mar 11, 2026
40d28ef
Complete proposal reduce convex ref escape hatches (#16)
djanogly Mar 11, 2026
7b8b949
update widget refactor proposal and roadmap with suggestion
djanogly Mar 11, 2026
71743f3
Expanding widget convex wrapper hooks (#18)
djanogly Mar 11, 2026
abd3bd1
Update docs (#19)
djanogly Mar 11, 2026
c02f3f1
Web local wrappers complete
djanogly Mar 11, 2026
fd2c878
Web local wrappers complete (#20)
djanogly Mar 11, 2026
e934025
sdk-core string ref replacements
djanogly Mar 11, 2026
746fd8c
Add comments explaining TS2589 error with api.x usage
djanogly Mar 11, 2026
21248b2
Merge pull request #21 from opencom-org/openspec/replace-sdk-core-str…
djanogly Mar 11, 2026
344c0d2
update convex
djanogly Mar 11, 2026
d55fed3
Merge pull request #22 from opencom-org/openspec/introduce-mobile-loc…
djanogly Mar 11, 2026
309c93d
Archive proposals
djanogly Mar 11, 2026
d4e7fed
New proposals
djanogly Mar 11, 2026
8b7fe2a
Manual edits to email capture widget (#23)
djanogly Mar 12, 2026
0929ee8
Add chat and ticket file uploads for Web & Widget (Mobile and RN-sdk …
djanogly Mar 12, 2026
e829a9d
Set up proper RAG with convex vector search (#25)
djanogly Mar 13, 2026
5307830
Address small tweaks / PR comments
djanogly Mar 13, 2026
76d1d58
CI checks
djanogly Mar 13, 2026
17f50df
explicit type for JSX element
djanogly Mar 13, 2026
59ced04
Add refresh button for embeddings (#28)
djanogly Mar 13, 2026
dacf352
Update convex and improve inbox thread pane
djanogly Mar 13, 2026
f75edd3
Insert links to knowledge base from admin chat, and use vector search…
djanogly Mar 14, 2026
189b4f3
Fix/ai model selector (#33)
djanogly Apr 16, 2026
2425492
update and add ai guidance (#34)
djanogly Apr 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
288 changes: 288 additions & 0 deletions .agents/skills/convex-create-component/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,288 @@
---
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.
---

# Convex Create Component

Create reusable Convex components with clear boundaries and a small app-facing API.

## When to Use

- Creating a new Convex component in an existing app
- Extracting reusable backend logic into a component
- Building a third-party integration that should own its own tables and workflows
- Packaging Convex functionality for reuse across multiple apps

## When Not to Use

- One-off business logic that belongs in the main app
- Thin utilities that do not need Convex tables or functions
- App-level orchestration that should stay in `convex/`
- Cases where a normal TypeScript library is enough

## Workflow

1. Ask the user what they are building and what the end goal is. If the repo already makes the answer obvious, say so and confirm before proceeding.
2. Choose the shape using the decision tree below and read the matching reference file.
3. Decide whether a component is justified. Prefer normal app code or a regular library if the feature does not need isolated tables, backend functions, or reusable persistent state.
4. Make a short plan for:
- what tables the component owns
- what public functions it exposes
- what data must be passed in from the app (auth, env vars, parent IDs)
- what stays in the app as wrappers or HTTP mounts
5. Create the component structure with `convex.config.ts`, `schema.ts`, and function files.
6. Implement functions using the component's own `./_generated/server` imports, not the app's generated files.
7. Wire the component into the app with `app.use(...)`. If the app does not already have `convex/convex.config.ts`, create it.
8. Call the component from the app through `components.<name>` using `ctx.runQuery`, `ctx.runMutation`, or `ctx.runAction`.
9. If React clients, HTTP callers, or public APIs need access, create wrapper functions in the app instead of exposing component functions directly.
10. Run `npx convex dev` and fix codegen, type, or boundary issues before finishing.

## Choose the Shape

Ask the user, then pick one path:

| Goal | Shape | Reference |
| ------------------------------------------------- | ---------------- | ----------------------------------- |
| Component for this app only | Local | `references/local-components.md` |
| Publish or share across apps | Packaged | `references/packaged-components.md` |
| User explicitly needs local + shared library code | Hybrid | `references/hybrid-components.md` |
| Not sure | Default to local | `references/local-components.md` |

Read exactly one reference file before proceeding.

## Default Approach

Unless the user explicitly wants an npm package, default to a local component:

- Put it under `convex/components/<componentName>/`
- Define it with `defineComponent(...)` in its own `convex.config.ts`
- Install it from the app's `convex/convex.config.ts` with `app.use(...)`
- Let `npx convex dev` generate the component's own `_generated/` files

## Component Skeleton

A minimal local component with a table and two functions, plus the app wiring.

```ts
// convex/components/notifications/convex.config.ts
import { defineComponent } from "convex/server";

export default defineComponent("notifications");
```

```ts
// convex/components/notifications/schema.ts
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";

export default defineSchema({
notifications: defineTable({
userId: v.string(),
message: v.string(),
read: v.boolean(),
}).index("by_user", ["userId"]),
});
```

```ts
// convex/components/notifications/lib.ts
import { v } from "convex/values";
import { mutation, query } from "./_generated/server.js";

export const send = mutation({
args: { userId: v.string(), message: v.string() },
returns: v.id("notifications"),
handler: async (ctx, args) => {
return await ctx.db.insert("notifications", {
userId: args.userId,
message: args.message,
read: false,
});
},
});

export const listUnread = query({
args: { userId: v.string() },
returns: v.array(
v.object({
_id: v.id("notifications"),
_creationTime: v.number(),
userId: v.string(),
message: v.string(),
read: v.boolean(),
}),
),
handler: async (ctx, args) => {
return await ctx.db
.query("notifications")
.withIndex("by_user", (q) => q.eq("userId", args.userId))
.filter((q) => q.eq(q.field("read"), false))
.collect();
},
});
```

```ts
// convex/convex.config.ts
import { defineApp } from "convex/server";
import notifications from "./components/notifications/convex.config.js";

const app = defineApp();
app.use(notifications);

export default app;
```

```ts
// convex/notifications.ts (app-side wrapper)
import { v } from "convex/values";
import { mutation, query } from "./_generated/server";
import { components } from "./_generated/api";
import { getAuthUserId } from "@convex-dev/auth/server";

export const sendNotification = mutation({
args: { message: v.string() },
returns: v.null(),
handler: async (ctx, args) => {
const userId = await getAuthUserId(ctx);
if (!userId) throw new Error("Not authenticated");

await ctx.runMutation(components.notifications.lib.send, {
userId,
message: args.message,
});
return null;
},
});

export const myUnread = query({
args: {},
handler: async (ctx) => {
const userId = await getAuthUserId(ctx);
if (!userId) throw new Error("Not authenticated");

return await ctx.runQuery(components.notifications.lib.listUnread, {
userId,
});
},
});
```

Note the reference path shape: a function in `convex/components/notifications/lib.ts` is called as `components.notifications.lib.send` from the app.

## Critical Rules

- Keep authentication in the app, because `ctx.auth` is not available inside components.
- Keep environment access in the app, because component functions cannot read `process.env`.
- Pass parent app IDs across the boundary as strings, because `Id` types become plain strings in the app-facing `ComponentApi`.
- Do not use `v.id("parentTable")` for app-owned tables inside component args or schema, because the component has no access to the app's table namespace.
- Import `query`, `mutation`, and `action` from the component's own `./_generated/server`, not the app's generated files.
- Do not expose component functions directly to clients. Create app wrappers when client access is needed, because components are internal and need auth/env wiring the app provides.
- If the component defines HTTP handlers, mount the routes in the app's `convex/http.ts`, because components cannot register their own HTTP routes.
- If the component needs pagination, use `paginator` from `convex-helpers` instead of built-in `.paginate()`, because `.paginate()` does not work across the component boundary.
- Add `args` and `returns` validators to all public component functions, because the component boundary requires explicit type contracts.

## Patterns

### Authentication and environment access

```ts
// Bad: component code cannot rely on app auth or env
const identity = await ctx.auth.getUserIdentity();
const apiKey = process.env.OPENAI_API_KEY;
```

```ts
// Good: the app resolves auth and env, then passes explicit values
const userId = await getAuthUserId(ctx);
if (!userId) throw new Error("Not authenticated");

await ctx.runAction(components.translator.translate, {
userId,
apiKey: process.env.OPENAI_API_KEY,
text: args.text,
});
```

### Client-facing API

```ts
// Bad: assuming a component function is directly callable by clients
export const send = components.notifications.send;
```

```ts
// Good: re-export through an app mutation or query
export const sendNotification = mutation({
args: { message: v.string() },
returns: v.null(),
handler: async (ctx, args) => {
const userId = await getAuthUserId(ctx);
if (!userId) throw new Error("Not authenticated");

await ctx.runMutation(components.notifications.lib.send, {
userId,
message: args.message,
});
return null;
},
});
```

### IDs across the boundary

```ts
// Bad: parent app table IDs are not valid component validators
args: {
userId: v.id("users");
}
```

```ts
// Good: treat parent-owned IDs as strings at the boundary
args: {
userId: v.string();
}
```

### Advanced Patterns

For additional patterns including function handles for callbacks, deriving validators from schema, static configuration with a globals table, and class-based client wrappers, see `references/advanced-patterns.md`.

## Validation

Try validation in this order:

1. `npx convex codegen --component-dir convex/components/<name>`
2. `npx convex codegen`
3. `npx convex dev`

Important:

- Fresh repos may fail these commands until `CONVEX_DEPLOYMENT` is configured.
- Until codegen runs, component-local `./_generated/*` imports and app-side `components.<name>...` references will not typecheck.
- If validation blocks on Convex login or deployment setup, stop and ask the user for that exact step instead of guessing.

## Reference Files

Read exactly one of these after the user confirms the goal:

- `references/local-components.md`
- `references/packaged-components.md`
- `references/hybrid-components.md`

Official docs: [Authoring Components](https://docs.convex.dev/components/authoring)

## Checklist

- [ ] Asked the user what they want to build and confirmed the shape
- [ ] Read the matching reference file
- [ ] Confirmed a component is the right abstraction
- [ ] Planned tables, public API, boundaries, and app wrappers
- [ ] Component lives under `convex/components/<name>/` (or package layout if publishing)
- [ ] Component imports from its own `./_generated/server`
- [ ] Auth, env access, and HTTP routes stay in the app
- [ ] Parent app IDs cross the boundary as `v.string()`
- [ ] Public functions have `args` and `returns` validators
- [ ] Ran `npx convex dev` and fixed codegen or type issues
10 changes: 10 additions & 0 deletions .agents/skills/convex-create-component/agents/openai.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
interface:
display_name: "Convex Create Component"
short_description: "Design and build reusable Convex components with clear boundaries."
icon_small: "./assets/icon.svg"
icon_large: "./assets/icon.svg"
brand_color: "#14B8A6"
default_prompt: "Help me create a Convex component for this feature. First check that a component is actually justified, then design the tables, API surface, and app-facing wrappers before implementing it."

policy:
allow_implicit_invocation: true
3 changes: 3 additions & 0 deletions .agents/skills/convex-create-component/assets/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading