diff --git a/.gitignore b/.gitignore index 818d5df..d764d4f 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,4 @@ coverage/ # Local data volumes (if anyone bind-mounts under repo) data/ postgres-data/ +.claude/ diff --git a/apps/web/drizzle/0002_snippets_scope.sql b/apps/web/drizzle/0002_snippets_scope.sql index 8cf04a1..6762e41 100644 --- a/apps/web/drizzle/0002_snippets_scope.sql +++ b/apps/web/drizzle/0002_snippets_scope.sql @@ -35,5 +35,5 @@ CREATE UNIQUE INDEX "snippets_user_project_name_uq" ON "snippets" ("user_id", "project_id", "name") WHERE scope = 'project' AND deleted_at IS NULL; -CREATE INDEX "snippets_user_idx" ON "snippets" ("user_id"); -CREATE INDEX "snippets_project_idx" ON "snippets" ("project_id"); +CREATE INDEX IF NOT EXISTS "snippets_user_idx" ON "snippets" ("user_id"); +CREATE INDEX IF NOT EXISTS "snippets_project_idx" ON "snippets" ("project_id"); diff --git a/apps/web/lib/mcp/tools.ts b/apps/web/lib/mcp/tools.ts index 0620899..927b3d2 100644 --- a/apps/web/lib/mcp/tools.ts +++ b/apps/web/lib/mcp/tools.ts @@ -337,7 +337,7 @@ const memoryUpdate: ToolDef = { project: { type: "string", description: - "Project key the memory should attach to (required and only valid when scope='project'). Project is upserted if it doesn't exist.", + "Project key the memory should attach to (required and only valid when scope='project'). The project must already exist — call `project.identify` first if it doesn't.", }, }, required: ["id"], diff --git a/apps/web/lib/memory-actions.ts b/apps/web/lib/memory-actions.ts index 6c3ca2e..0f83dc4 100644 --- a/apps/web/lib/memory-actions.ts +++ b/apps/web/lib/memory-actions.ts @@ -185,7 +185,10 @@ export async function updateMemoryAction(formData: FormData) { } } - await db.update(memories).set(update).where(eq(memories.id, parsed.data.id)); + await db + .update(memories) + .set(update) + .where(and(eq(memories.id, parsed.data.id), eq(memories.userId, userId))); const auditFields = Object.keys(update).filter((k) => k !== "updatedAt"); const auditPayload: Record = { fields: auditFields }; diff --git a/apps/web/lib/snippets.ts b/apps/web/lib/snippets.ts index 74126d2..bf31d10 100644 --- a/apps/web/lib/snippets.ts +++ b/apps/web/lib/snippets.ts @@ -156,7 +156,10 @@ export async function putSnippet( updatedAt: new Date(), }; if (description !== undefined) updateValues.description = description; - await db.update(snippets).set(updateValues).where(eq(snippets.id, existing.id)); + await db + .update(snippets) + .set(updateValues) + .where(and(eq(snippets.id, existing.id), eq(snippets.userId, userId))); const refreshed = await findSnippet(userId, name, scope, projectId); return { snippet: refreshed!, inserted: false }; } diff --git a/packages/schemas/src/index.ts b/packages/schemas/src/index.ts index 3b15aa7..b980fb6 100644 --- a/packages/schemas/src/index.ts +++ b/packages/schemas/src/index.ts @@ -61,10 +61,9 @@ export const MemoryUpdateInput = z.object({ (v) => v.scope !== "project" || (v.project !== undefined && v.project !== ""), { message: "scope='project' requires a non-empty project key" }, ) - .refine( - (v) => v.scope !== "user" || v.project === undefined || v.project === "", - { message: "scope='user' cannot have a project key" }, - ); + .refine((v) => v.scope !== "user" || v.project === undefined, { + message: "scope='user' cannot have a project key", + }); export type MemoryUpdateInput = z.infer; export const MemorySearchInput = z.object({