feat(snippets): named reusable artifacts with scope mirroring memories
Snippets are exact-name-keyed templates (PR formats, checklists, style rules) — prescriptive artifacts the user wants applied consistently. Distinct from memories, which are searched descriptive prose. - Migration 0002 adds scope/project_id/deleted_at to snippets, partial unique indexes per scope, and a scope/project CHECK constraint mirroring memories_scope_project_chk. - New @shared-memory/schemas: SnippetName/Put/Get/List/Delete inputs with shared scope-project refinement. - lib/snippets.ts: get/put/list/softDelete helpers used by both the Web Server Actions and the MCP tool handlers. - Four new MCP tools (snippet.put/get/list/delete) with directive descriptions contrasting against memory.* (exact-name lookup vs search; templates vs facts). - /snippets pages: list, new, [name] with edit + delete and a scope-picker when a name lives in multiple scopes. - Nav: Snippets link between Memories and Projects. Design note: when a name exists in both user and project scope, snippet.get without an explicit scope prefers project (if project key given) then falls back to user. The detail page shows a picker when the name is ambiguous and no scope query param was supplied. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -114,15 +114,22 @@ export const snippets = pgTable(
|
||||
userId: uuid("user_id")
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: "cascade" }),
|
||||
// NULL when scope = 'user' (global to the user). Mirrors `memories`.
|
||||
projectId: uuid("project_id").references(() => projects.id, { onDelete: "set null" }),
|
||||
scope: memoryScope("scope").notNull().default("user"),
|
||||
name: varchar("name", { length: 200 }).notNull(),
|
||||
body: text("body").notNull(),
|
||||
description: text("description"),
|
||||
tags: textArray("tags").notNull().default([]),
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
deletedAt: timestamp("deleted_at", { withTimezone: true }),
|
||||
},
|
||||
(t) => ({
|
||||
uniqueUserName: uniqueIndex("snippets_user_name_uq").on(t.userId, t.name),
|
||||
userIdx: index("snippets_user_idx").on(t.userId),
|
||||
projectIdx: index("snippets_project_idx").on(t.projectId),
|
||||
// Partial unique indexes (one per scope, live rows only) are declared
|
||||
// in the SQL migration since drizzle-kit doesn't model partial indexes.
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user