Token导航 LogoToken导航

Agent Skill · 研究检索

epic-database(史诗数据库)

用于辅助数据库表结构、查询语句、迁移脚本和数据维护任务。它适合让 Agent 分析 schema、编写 SQL、排查查询问题、整理索引或生成迁移建议。使用时需要明确数据库类型、连接环境和目标表,区分只读分析与写入变更;涉及删除、更新、迁移和批量导入时,应优先 dry-run、备份或事务保护,避免误操作。

epic-database 是研究检索类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为需要联网;许可证为 MIT,第三方扫描已通过,按安装量在研究检索类的 73,675 个 Skill 里排第 62,819。

总安装 326周安装 14GitHub Stars 5,470下载 114核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
MIT

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:epic-database(史诗数据库)
来源仓库:https://github.com/epicweb-dev/epic-stack
仓库路径:skills/epic-database
安装命令:
npx skills add https://github.com/epicweb-dev/epic-stack --skill epic-database
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。不同来源提供的安装方式可能略有差异;本站展示可直接复制的安装命令,安装前请核对来源页面。

skills.shnpx skills
npx skills add https://github.com/epicweb-dev/epic-stack --skill epic-database

这个 Skill 是做什么的

研究检索类 Skill 主要解决「找资料、核对事实、把来源整理清楚」的问题,通常会让助手先检索再总结。

Epic 数据库技能基于 Prisma 提供 schema 设计、迁移脚本编写和多区域部署支持,倡导按需获取数据原则。适合 SQLite/LiteFS 环境中的表结构变更、查询性能优化和种子数据生成任务。写入类操作(如删除、批量更新)前应优先执行 dry-run 或事务回滚测试,防止误操作影响生产数据。

安装命令为 npx skills add https://github.com/epicweb-dev/epic-stack --skill epic-database。连接生产数据库时需确认连接字符串安全性,避免硬编码凭据或暴露内网地址。

来源仓库内路径:skills/epic-database

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/epicweb-dev/epic-stack --skill epic-database;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要需要联网,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

epic-database 怎么安装?

在终端执行 npx skills add https://github.com/epicweb-dev/epic-stack --skill epic-database。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

epic-database 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

epic-database 需要什么权限?

权限级别是需要联网。该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

epic-database 是免费的吗?

来源标注的许可证是 MIT。本站不代收任何费用,也不托管安装包;能否商用、要不要署名,以许可证原文为准。

epic-database 用的人多吗?

公开数据是累计安装 326 次,近一周安装 14 次,下载 114 次,GitHub 5,470 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

epic-database 安全吗?

第三方扫描结果为通过,但这只覆盖已知的静态检查项,不能替代自己读一遍源码和安装命令。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 3 个目录同时收录,说明这个 Skill 流传比较广。
  • 许可证明确为 MIT,商用和分发边界好判断。
  • 第三方扫描已通过,常见的高风险行为没有命中。

先想清楚的地方

  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理研究检索类任务、又不想每次都手写提示词的人。
  • 需要许可证清楚、并且有第三方扫描结果的团队项目。

不太适合

  • 需要厂商级技术支持和 SLA 的生产系统。

研究检索类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
find-skills

寻找技能 · Codex、Claude

31,199,81316,486操作浏览器MIT有提醒
self-improving-agent

自我改进 Agent · OpenClaw

16,464,0023,396接触敏感数据MIT-0通过
soultrace

灵魂追踪 · Codex、Claude

7,588,94412需要联网未明确有提醒
self-improving

自我完善 · OpenClaw

6,829,2841,046需要联网MIT-0通过
microsoft-foundry

微软代工厂 · Codex、Claude

6,614,144764调用外部服务未明确有提醒
epic-database

史诗数据库 · 本页

3265,470需要联网MIT通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

Epic Stack: Database

When to use this skill

Use this skill when you need to:

  • Design database schema with Prisma
  • Create migrations
  • Work with SQLite and LiteFS
  • Optimize queries and performance
  • Create seed scripts
  • Work with multi-region deployments
  • Manage backups and restores

Patterns and conventions

Database Philosophy

Following Epic Web principles:

Do as little as possible - Only fetch the data you actually need. Use select to fetch specific fields instead of entire models. Avoid over-fetching data "just in case" - fetch what you need, when you need it.

Pragmatism over purity - Optimize queries when there's a measurable benefit, but don't over-optimize prematurely. Simple, readable queries are often better than complex optimized ones. Add indexes when queries are slow, not before.

Example - Fetch only what you need:

// ✅ Good - Fetch only needed fields
const user = await prisma.user.findUnique({
	where: { id: userId },
	select: {
		id: true,
		username: true,
		name: true,
		// Only fetch what you actually use
	},
})

// ❌ Avoid - Fetching everything
const user = await prisma.user.findUnique({
	where: { id: userId },
	// Fetches all fields including password hash, email, etc.
})

Example - Pragmatic optimization:

// ✅ Good - Simple query first, optimize if needed
const notes = await prisma.note.findMany({
	where: { ownerId: userId },
	select: { id: true, title: true, updatedAt: true },
	orderBy: { updatedAt: 'desc' },
	take: 20,
})

// Only add indexes if this query is actually slow
// Don't pre-optimize

// ❌ Avoid - Over-optimizing before measuring
// Adding complex indexes, joins, etc. before knowing if it's needed

Prisma Schema

Epic Stack uses Prisma with SQLite as the database.

Basic configuration:

// prisma/schema.prisma
generator client {
  provider        = "prisma-client-js"
  previewFeatures = ["typedSql"]
}

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

Basic model:

model User {
  id        String   @id @default(cuid())
  email     String   @unique
  username  String   @unique
  name      String?

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  notes     Note[]
  roles     Role[]
}

model Note {
  id      String @id @default(cuid())
  title   String
  content String

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  owner   User   @relation(fields: [ownerId], references: [id])
  ownerId String

  @@index([ownerId])
  @@index([ownerId, updatedAt])
}

CUID2 for IDs

Epic Stack uses CUID2 to generate unique IDs.

Advantages:

  • Globally unique
  • Sortable
  • Secure (no exposed information)
  • URL-friendly

Example:

model User {
  id String @id @default(cuid()) // Automatically generates CUID2
}

Timestamps

Standard fields:

model User {
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt // Automatically updated
}

Relationships

One-to-Many:

model User {
  id    String @id @default(cuid())
  notes Note[]
}

model Note {
  id      String @id @default(cuid())
  owner   User   @relation(fields: [ownerId], references: [id])
  ownerId String

  @@index([ownerId])
}

One-to-One:

model User {
  id      String  @id @default(cuid())
  image   UserImage?
}

model UserImage {
  id        String @id @default(cuid())
  user      User   @relation(fields: [userId], references: [id])
  userId    String @unique
}

Many-to-Many:

model User {
  id    String @id @default(cuid())
  roles Role[]
}

model Role {
  id    String @id @default(cuid())
  users User[]
}

Indexes

Create indexes:

model Note {
  id      String @id @default(cuid())
  ownerId String
  updatedAt DateTime

  @@index([ownerId])              // Simple index
  @@index([ownerId, updatedAt])   // Composite index
}

Best practices:

  • Index foreign keys
  • Index fields used in where frequently
  • Index fields used in orderBy
  • Use composite indexes for complex queries

Cascade Delete

Configure cascade:

model User {
  id    String @id @default(cuid())
  notes Note[]
}

model Note {
  id      String @id @default(cuid())
  owner   User   @relation(fields: [ownerId], references: [id], onDelete: Cascade)
  ownerId String
}

Options:

  • onDelete: Cascade - Deletes children when parent is deleted
  • onDelete: SetNull - Sets to null when parent is deleted
  • onDelete: Restrict - Prevents deletion if there are children

Migrations

Create migration:

npx prisma migrate dev --name add_user_field

Apply migrations in production:

npx prisma migrate deploy

Automatic migrations: Migrations are automatically applied on deploy via litefs.yml.

"Widen then Narrow" strategy for zero-downtime:

  1. Widen app - App accepts A or B
  2. Widen db - DB provides A and B, app writes to both
  3. Narrow app - App only uses B
  4. Narrow db - DB only provides B

Example: Rename field name to firstName and lastName:

// Step 1: Widen app (accepts both)
model User {
  id        String @id @default(cuid())
  name      String?  // Deprecated
  firstName String?  // New
  lastName  String?  // New
}

// Step 2: Widen db (migration copies data)
// In SQL migration:
ALTER TABLE User ADD COLUMN firstName TEXT;
ALTER TABLE User ADD COLUMN lastName TEXT;
UPDATE User SET firstName = name;

// Step 3: Narrow app (only uses new fields)
// Code only uses firstName and lastName

// Step 4: Narrow db (removes old field)
ALTER TABLE User DROP COLUMN name;

Prisma Client

Import Prisma Client:

import { prisma } from '#app/utils/db.server.ts'

Basic query:

const user = await prisma.user.findUnique({
	where: { id: userId },
})

Specific select:

const user = await prisma.user.findUnique({
	where: { id: userId },
	select: {
		id: true,
		email: true,
		username: true,
		// Don't include password or sensitive data
	},
})

Include relations:

const user = await prisma.user.findUnique({
	where: { id: userId },
	include: {
		notes: {
			select: {
				id: true,
				title: true,
			},
			orderBy: { updatedAt: 'desc' },
		},
		roles: true,
	},
})

Complex queries:

const notes = await prisma.note.findMany({
	where: {
		ownerId: userId,
		title: { contains: searchTerm },
	},
	select: {
		id: true,
		title: true,
		updatedAt: true,
	},
	orderBy: { updatedAt: 'desc' },
	take: 20,
	skip: (page - 1) * 20,
})

Transactions

Use transactions:

await prisma.$transaction(async (tx) => {
	const user = await tx.user.create({
		data: {
			email,
			username,
			roles: { connect: { name: 'user' } },
		},
	})

	await tx.note.create({
		data: {
			title: 'Welcome',
			content: 'Welcome to the app!',
			ownerId: user.id,
		},
	})

	return user
})

SQLite con LiteFS

Multi-region with LiteFS:

  • Only the primary instance can write
  • Replicas can only read
  • Writes are automatically replicated

Check primary instance:

import { ensurePrimary, getInstanceInfo } from '#app/utils/litefs.server.ts'

export async function action({ request }: Route.ActionArgs) {
	// Ensure we're on primary instance for writes
	await ensurePrimary()

	// Now we can write safely
	await prisma.user.create({
		data: {
			/* ... */
		},
	})
}

Get instance information:

import { getInstanceInfo } from '#app/utils/litefs.server.ts'

const { currentIsPrimary, primaryInstance } = await getInstanceInfo()

if (currentIsPrimary) {
	// Can write
} else {
	// Read-only, redirect to primary if necessary
}

Seed Scripts

Create seed:

// prisma/seed.ts
import { prisma } from '#app/utils/db.server.ts'

async function seed() {
	// Create roles
	await prisma.role.createMany({
		data: [
			{ name: 'user', description: 'Standard user' },
			{ name: 'admin', description: 'Administrator' },
		],
	})

	// Create users
	const user = await prisma.user.create({
		data: {
			email: 'user@example.com',
			username: 'testuser',
			roles: { connect: { name: 'user' } },
		},
	})

	console.log('Seed complete!')
}

seed()
	.catch((e) => {
		console.error(e)
		process.exit(1)
	})
	.finally(async () => {
		await prisma.$disconnect()
	})

Run seed:

npx prisma db seed
# Or directly:
npx tsx prisma/seed.ts

Query Optimization

Guidelines (pragmatic approach):

  • Use select to fetch only needed fields - do as little as possible
  • Use selective include - only include relations you actually use
  • Index fields used in where and orderBy - but only if queries are slow
  • Use composite indexes for complex queries - when you have a real performance problem
  • Avoid select: true (fetches everything) - be explicit about what you need
  • Measure first, optimize second - don't pre-optimize

Optimized example (do as little as possible):

// ❌ Avoid: Fetches everything unnecessarily
const user = await prisma.user.findUnique({
	where: { id: userId },
	// Fetches password hash, email, all relations, etc.
})

// ✅ Good: Only needed fields - do as little as possible
const user = await prisma.user.findUnique({
	where: { id: userId },
	select: {
		id: true,
		username: true,
		name: true,
		// Only what you actually use
	},
})

// ✅ Better: With selective relations (only if you need them)
const user = await prisma.user.findUnique({
	where: { id: userId },
	select: {
		id: true,
		username: true,
		notes: {
			select: {
				id: true,
				title: true,
			},
			take: 10, // Only fetch what you need
		},
	},
})

Prisma Query Logging

Configure logging:

// app/utils/db.server.ts
const client = new PrismaClient({
	log: [
		{ level: 'query', emit: 'event' },
		{ level: 'error', emit: 'stdout' },
		{ level: 'warn', emit: 'stdout' },
	],
})

client.$on('query', async (e) => {
	if (e.duration < 20) return // Only log slow queries

	console.info(`prisma:query - ${e.duration}ms - ${e.query}`)
})

Database URL

Development:

DATABASE_URL=file:./data/db.sqlite

Production (Fly.io):

DATABASE_URL=file:/litefs/data/sqlite.db

Connecting to DB in Production

SSH to Fly instance:

fly ssh console --app [YOUR_APP_NAME]

Connect to DB CLI:

fly ssh console -C database-cli --app [YOUR_APP_NAME]

Prisma Studio:

# Terminal 1: Start Prisma Studio
fly ssh console -C "npx prisma studio" -s --app [YOUR_APP_NAME]

# Terminal 2: Local proxy
fly proxy 5556:5555 --app [YOUR_APP_NAME]

# Open in browser
# http://localhost:5556

Common examples

Example 1: Create model with relations

model Post {
  id        String   @id @default(cuid())
  title     String
  content   String
  published Boolean  @default(false)

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  author   User   @relation(fields: [authorId], references: [id], onDelete: Cascade)
  authorId String

  comments Comment[]
  tags     Tag[]

  @@index([authorId])
  @@index([authorId, published])
  @@index([published, updatedAt])
}

model Comment {
  id      String @id @default(cuid())
  content String

  createdAt DateTime @default(now())

  post   Post   @relation(fields: [postId], references: [id], onDelete: Cascade)
  postId String

  author   User   @relation(fields: [authorId], references: [id])
  authorId String

  @@index([postId])
  @@index([authorId])
}

Example 2: Complex query with pagination

export async function getPosts({
	userId,
	page = 1,
	perPage = 20,
	published,
}: {
	userId?: string
	page?: number
	perPage?: number
	published?: boolean
}) {
	const where: Prisma.PostWhereInput = {}

	if (userId) {
		where.authorId = userId
	}
	if (published !== undefined) {
		where.published = published
	}

	const [posts, total] = await Promise.all([
		prisma.post.findMany({
			where,
			select: {
				id: true,
				title: true,
				updatedAt: true,
				author: {
					select: {
						id: true,
						username: true,
					},
				},
			},
			orderBy: { updatedAt: 'desc' },
			take: perPage,
			skip: (page - 1) * perPage,
		}),
		prisma.post.count({ where }),
	])

	return {
		posts,
		total,
		pages: Math.ceil(total / perPage),
	}
}

Example 3: Transaction with multiple operations

export async function createPostWithTags({
	authorId,
	title,
	content,
	tagNames,
}: {
	authorId: string
	title: string
	content: string
	tagNames: string[]
}) {
	return await prisma.$transaction(async (tx) => {
		// Create tags if they don't exist
		await Promise.all(
			tagNames.map((name) =>
				tx.tag.upsert({
					where: { name },
					update: {},
					create: { name },
				}),
			),
		)

		// Create post
		const post = await tx.post.create({
			data: {
				title,
				content,
				authorId,
				tags: {
					connect: tagNames.map((name) => ({ name })),
				},
			},
		})

		return post
	})
}

Example 4: Seed with related data

async function seed() {
	// Create permissions
	const permissions = await Promise.all([
		prisma.permission.create({
			data: {
				action: 'create',
				entity: 'note',
				access: 'own',
				description: 'Can create own notes',
			},
		}),
		prisma.permission.create({
			data: {
				action: 'read',
				entity: 'note',
				access: 'own',
				description: 'Can read own notes',
			},
		}),
	])

	// Create roles with permissions
	const userRole = await prisma.role.create({
		data: {
			name: 'user',
			description: 'Standard user',
			permissions: {
				connect: permissions.map((p) => ({ id: p.id })),
			},
		},
	})

	// Create user with role
	const user = await prisma.user.create({
		data: {
			email: 'user@example.com',
			username: 'testuser',
			roles: {
				connect: { id: userRole.id },
			},
		},
	})

	console.log('Seed complete!')
}

Common mistakes to avoid

  • ❌ Fetching unnecessary data: Use select to fetch only what you need - do as little as possible
  • ❌ Over-optimizing prematurely: Measure first, then optimize. Don't add indexes "just in case"
  • ❌ Not using indexes when needed: Index foreign keys and fields used in frequent queries, but only if they're actually slow
  • ❌ N+1 queries: Use include to fetch relations in a single query when you need them
  • ❌ Not using transactions for related operations: Always use transactions when multiple operations must be atomic
  • ❌ Writing from replicas: Verify ensurePrimary() before writes in production
  • ❌ Breaking migrations without strategy: Use "widen then narrow" for zero-downtime
  • ❌ Not validating data before inserting: Always validate with Zod before create/update
  • ❌ Forgetting onDelete in relations: Explicitly decide what to do when parent is deleted
  • ❌ Not using CUID2: Epic Stack uses CUID2 by default, don't use UUID or others
  • ❌ Not closing Prisma Client: Prisma handles this automatically, but ensure in scripts
  • ❌ Complex queries when simple ones work: Prefer simple, readable queries over complex optimized ones unless there's a real problem

References

平台分布

Gemini CLI

29.48%
按下载量换算34

Antigravity

20.71%
按下载量换算24

Claude Code

18.1%
按下载量换算21

OpenCode

10.84%
按下载量换算12

windsurf

8.5%
按下载量换算10

github-copilot

3.13%
按下载量换算4

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。

继续浏览同类 Skills