Token导航 LogoToken导航

Agent Skill · 前端设计

repository-pattern(存储库模式)

用于围绕 GitHub 仓库、Issue、Pull Request、分支、提交和代码协作流程提供辅助能力。它适合让 Agent 查询项目状态、整理变更、辅助创建或检查协作事项,并把仓库中的信息转成可执行的下一步。使用时需要区分只读查询和写入操作;涉及创建 PR、修改 Issue、推送分支或访问私有仓库时,应确认 token 权限、目标仓库范围和用户授权。

repository-pattern 是前端设计类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为需要联网;许可证为 MIT,第三方扫描已通过,按安装量在前端设计类的 10,216 个 Skill 里排第 8,294。

总安装 297周安装 12GitHub Stars 12下载 93核验 2026-05-01

安装说明

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

安装方式

通过对话安装

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

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

命令行安装

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

skills.shnpx skills
npx skills add https://github.com/alexanderop/workouttracker --skill repository-pattern

这个 Skill 是做什么的

前端设计类 Skill 关注页面结构、视觉规范和组件写法,输出通常是可运行的界面代码。

repository-pattern 用于围绕 GitHub 仓库、Issue、Pull Request、分支和提交提供辅助能力。它适合查询项目状态、整理变更并支持协作事项操作。可把仓库信息转化为可执行的下一步建议。

安装命令为 npx skills add https://github.com/alexanderop/workouttracker --skill repository-pattern。涉及写入操作时应确认 token 权限和仓库范围。

来源仓库内路径:skills/repository-pattern

上手步骤

4 步
  1. 1

    确认自己的宿主工具

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

  2. 2

    拿到安装命令

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

  3. 3

    安装后重启宿主

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

  4. 4

    按最小权限试用

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

常见问题

6 条

repository-pattern 怎么安装?

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

repository-pattern 支持哪些 AI 工具?

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

repository-pattern 需要什么权限?

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

repository-pattern 是免费的吗?

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

repository-pattern 用的人多吗?

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

repository-pattern 安全吗?

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

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

值得选的地方

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

先想清楚的地方

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

适不适合你

更适合

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

不太适合

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

前端设计类 Skill 对比

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

前端设计 · Codex、Claude

8,823,360126,072只读MIT通过
vercel-composition-patterns

Vercel 组合模式 · Codex、Claude

3,858,81625,893需要联网MIT通过
vercel-react-native-skills

Vercel React native skills 前端 · Codex、Claude

2,468,66425,878只读MIT通过
adapt

adapt 搜索 · Codex、Claude

2,027,04023,125操作浏览器未明确通过
bolder

bolder 分析 · Codex、Claude

2,004,28823,130只读未明确通过
repository-pattern

存储库模式 · 本页

29712需要联网MIT通过

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

SKILL.md 原文

Repository Pattern

Overview

This skill helps implement the repository pattern used in this workout tracker application. The pattern provides a clean abstraction over Dexie (IndexedDB) with type-safe interfaces, consistent error handling, and standardized CRUD operations.

Architecture Overview

Layered Approach:

Interfaces → Implementations → Provider → Public API
    ↓              ↓              ↓           ↓
db/interfaces  db/impl/dexie  db/provider  db/index

Flow:

  1. Define repository interface in db/interfaces.ts
  2. Implement with Dexie in db/implementations/dexie/[entity].ts
  3. Register in factory provider (db/implementations/dexie/index.ts)
  4. Export public getter (db/index.ts)
  5. Use in features via getEntityRepository()

Core Workflow

Follow these 6 steps when creating a new repository:

Step 1: Define Interface

Location: src/db/interfaces.ts

Define the repository interface with standard CRUD methods:

export type EntityRepository = {
  getAll(): Promise<ReadonlyArray<DbEntity>>
  getById(id: string): Promise<DbEntity | undefined>
  create(entity: Omit<DbEntity, 'id' | 'createdAt'>): Promise<DbEntity>
  update(id: string, updates: Partial<Omit<DbEntity, 'id' | 'createdAt'>>): Promise<void>
  delete(id: string): Promise<void>
  // ... entity-specific queries
}

Add to RepositoryProvider:

export type RepositoryProvider = {
  // ... existing
  entity: EntityRepository
}

Guidelines:

  • Use ReadonlyArray<T> and Readonly<T> for arguments
  • getById returns undefined (not throw) when not found
  • create returns the created entity with generated ID
  • update and delete return void
  • Use Omit<> to exclude auto-generated fields (id, createdAt)

Step 2: Add Schema Type

Location: src/db/schema.ts

Define database type with Db prefix:

/**
 * Entity stored in database.
 * Uses null instead of undefined for explicit "no value" semantics.
 */
export type DbEntity = {
  id: string
  name: string
  value: string | null        // Use null, not undefined
  createdAt: number
  updatedAt: number | null    // null until first update
}

Key Conventions:

  • Always use Db prefix for database types
  • Use null for "no value" (not undefined)
  • Store user input numbers as string (e.g., kg: string, reps: string)
  • Use discriminated unions with kind property for variants
  • Include type guards if needed: export function isDbEntity(x: unknown): x is DbEntity

Step 3: Update Database Class

Location: src/db/implementations/dexie/database.ts

Add table and indexes:

export class WorkoutTrackerDb extends Dexie {
  // ... existing tables
  entities!: Table<DbEntity, string>

  constructor() {
    super('WorkoutTracker')

    // Increment version number
    this.version(3).stores({
      // ... existing tables
      entities: 'id, name, createdAt',  // Index: primary + frequently queried fields
    })
  }
}

Indexing Guidelines:

  • Always index primary key (automatic)
  • Index fields used in where(), orderBy(), equals()
  • Index foreign keys for joins
  • Compound indexes for junction tables: '[field1+field2], field1, field2'

Step 4: Implement Repository

Location: src/db/implementations/dexie/[entity].ts

Create factory function returning repository implementation:

import type { EntityRepository } from '@/db/interfaces'
import type { DbEntity } from '@/db/schema'
import { createDatabaseError, tryCatch } from '@/lib/tryCatch'
import type { WorkoutTrackerDb } from './database'
import { generateId } from './database'

/**
 * Dexie implementation of EntityRepository.
 */
export function createDexieEntityRepository(db: WorkoutTrackerDb): EntityRepository {
  return {
    async getAll(): Promise<ReadonlyArray<DbEntity>> {
      const [error, entities] = await tryCatch(
        db.entities.orderBy('createdAt').reverse().toArray(),
      )
      if (error) {
        throw createDatabaseError('LOAD_FAILED', 'retrieve entities', error)
      }
      return entities
    },

    async getById(id: string): Promise<DbEntity | undefined> {
      const [error, entity] = await tryCatch(db.entities.get(id))
      if (error) {
        throw createDatabaseError('LOAD_FAILED', `retrieve entity with id ${id}`, error)
      }
      return entity
    },

    async create(
      entity: Omit<DbEntity, 'id' | 'createdAt'>,
    ): Promise<DbEntity> {
      const newEntity: DbEntity = {
        ...entity,
        id: generateId(),
        createdAt: Date.now(),
      }

      const [error] = await tryCatch(db.entities.add(newEntity))
      if (error) {
        throw createDatabaseError('SAVE_FAILED', 'create entity', error)
      }

      return newEntity
    },

    async update(
      id: string,
      updates: Partial<Omit<DbEntity, 'id' | 'createdAt'>>,
    ): Promise<void> {
      const [error, updatedCount] = await tryCatch(
        db.entities.update(id, {
          ...updates,
          updatedAt: Date.now(),  // Auto-inject timestamp
        }),
      )

      if (error) {
        throw createDatabaseError('SAVE_FAILED', `update entity with id ${id}`, error)
      }

      if (updatedCount === 0) {
        throw createDatabaseError('NOT_FOUND', `entity with id ${id} not found`)
      }
    },

    async delete(id: string): Promise<void> {
      const [error] = await tryCatch(db.entities.delete(id))
      if (error) {
        throw createDatabaseError('SAVE_FAILED', `delete entity with id ${id}`, error)
      }
      // Soft delete: no NOT_FOUND check
    },
  }
}

Key Patterns:

  • Use tryCatch() wrapper for all operations (preferred pattern)
  • Two-phase error checking: operation failure + not found
  • Auto-inject timestamps: createdAt, updatedAt
  • Use generateId() for new IDs
  • Soft delete: no error if entity doesn't exist

Step 5: Register in Factory Provider

Location: src/db/implementations/dexie/index.ts

Import and add to provider:

import { createDexieEntityRepository } from './entity'

export function createDexieRepositoryProvider(): RepositoryProvider {
  return {
    activeWorkout: createDexieActiveWorkoutRepository(db),
    workouts: createDexieWorkoutsRepository(db),
    // ... existing repositories
    entity: createDexieEntityRepository(db),  // ADD THIS
  }
}

Step 6: Export Public Getter

Location: src/db/index.ts

Add getter function:

export function getEntityRepository(): EntityRepository {
  return getRepositoryProvider().entity
}

Usage in Features

import { getEntityRepository } from '@/db'
import type { DbEntity } from '@/db/schema'

export function useEntities() {
  const entities = ref<ReadonlyArray<DbEntity>>([])
  const entityRepo = getEntityRepository()

  async function loadEntities() {
    entities.value = await entityRepo.getAll()
  }

  async function createEntity(name: string, value: string | null) {
    const newEntity = await entityRepo.create({ name, value, updatedAt: null })
    entities.value = [...entities.value, newEntity]
  }

  async function updateEntity(id: string, updates: Partial<DbEntity>) {
    await entityRepo.update(id, updates)
    await loadEntities()
  }

  async function deleteEntity(id: string) {
    await entityRepo.delete(id)
    entities.value = entities.value.filter(e => e.id !== id)
  }

  onMounted(() => loadEntities())

  return {
    entities: readonly(entities),
    createEntity,
    updateEntity,
    deleteEntity,
  }
}

Key Principles

1. Error Handling

Preferred: tryCatch wrapper

const [error, result] = await tryCatch(operation)
if (error) {
  throw createDatabaseError('ERROR_CODE', 'description', error)
}

Error codes:

  • SAVE_FAILED - Create, update, delete operations
  • LOAD_FAILED - Read operations
  • NOT_FOUND - Entity doesn't exist

2. Timestamp Management

Auto-inject timestamps in repository methods:

  • createdAt: Date.now() in create()
  • updatedAt: Date.now() in update()
  • lastUsedAt: Date.now() when accessing entity

3. ID Generation

Always use generateId() from database.ts:

import { generateId } from './database'

const newEntity = {
  ...entity,
  id: generateId(),  // crypto.randomUUID()
}

4. Soft Delete

Delete operations don't throw if entity doesn't exist:

async delete(id: string): Promise<void> {
  await tryCatch(db.entities.delete(id))
  // No NOT_FOUND check - silent success
}

5. Type Safety

  • Use Readonly<T> and ReadonlyArray<T> for function parameters
  • Use Omit<> to exclude auto-generated fields
  • Use discriminated unions with exhaustive checking
  • Define type guards for runtime type checking

File Reference

Critical files when creating repository:

  • src/db/interfaces.ts - Interface definition + RepositoryProvider
  • src/db/schema.ts - Db-prefixed type definitions
  • src/db/implementations/dexie/database.ts - Table + indexes
  • src/db/implementations/dexie/[entity].ts - Implementation
  • src/db/implementations/dexie/index.ts - Factory registration
  • src/db/index.ts - Public getter export

Utility imports:

  • @/lib/tryCatch - Error handling utilities
  • @/db/implementations/dexie/database - generateId()

Detailed References

For complete examples and advanced patterns, see:

- Example 1: Simple CRUD repository (Notes) - Example 2: Complex transformations (Tags with many-to-many) - Example 3: Extending Settings with function overloads

- Error handling patterns (direct throw vs tryCatch) - CRUD patterns (getAll, create, update, delete, timestamps) - Type transformation patterns (helper utilities, deep cloning) - Advanced patterns (function overloads, singleton, transactions, bulk ops) - Schema design patterns (discriminated unions, indexing, embedded vs referenced)

Common Scenarios

Scenario 1: Simple CRUD Repository

Need basic storage for an entity? See examples.md → Example 1 (Notes).

Quick checklist:

  1. Define interface with getAll/getById/create/update/delete
  2. Add DbEntity type with Db prefix
  3. Add table with indexes
  4. Implement using tryCatch pattern
  5. Register and export

Scenario 2: Complex Relationships

Need many-to-many relationships or complex queries? See examples.md → Example 2 (Tags).

Pattern: Junction table + transaction handling + usage tracking.

Scenario 3: Extending Settings

Adding new setting? See examples.md → Example 3.

Pattern: Add discriminated union member + function overload + default value.

Scenario 4: Conversions Between Types

Need to convert between templates and workouts? See patterns.md → Type Transformation Patterns.

Pattern: Helper utilities with exhaustive switch statements.

Scenario 5: Bulk Operations

Import/export or batch delete? See patterns.md → Advanced Patterns → Bulk Operations.

Pattern: Transactions + Promise.all() + bulkAdd().

Testing Support

Mock repositories for unit tests:

import { createMockRepositories } from '@/__tests__/helpers/mockRepositories'

const mockRepos = createMockRepositories()
mockRepos.entity.getAll.mockResolvedValue([...])

Integration tests with fake-indexeddb are automatically set up via test helpers.

Migration Strategy

When updating schema version:

  1. Increment version number in database.ts
  2. Add new table/indexes in .stores({})
  3. Dexie handles migrations automatically
  4. For data migrations, use .upgrade() callback
this.version(3)
  .stores({
    entities: 'id, name, createdAt',
  })
  .upgrade(tx => {
    // Optional data migration logic
    return tx.table('entities').toCollection().modify(entity => {
      entity.newField = 'default'
    })
  })

Project-Specific Repositories

Db* Types vs Domain Types

AspectDatabase (Db*)Domain
Filesrc/db/schema.tssrc/types/
PrefixDbWorkout, DbSetWorkout, Set
No valuenullundefined
Optimized forStorageApp logic

Available Repositories

SettingsRepository - Key-value store with defaults:

const repo = getSettingsRepository()
await repo.get('theme')           // 'light' | 'dark' | 'system'
await repo.get('defaultRestTimer') // number
await repo.set({ key: 'theme', value: 'dark' })
await repo.getAll()               // All settings merged with defaults
await repo.reset('theme')

CustomExercisesRepository - Exercise CRUD:

const repo = getCustomExercisesRepository()
await repo.getAll()
await repo.getById(id)
await repo.add({ id: generateId(), name: 'Squat', ... })
await repo.update(id, { name: 'Back Squat' })
await repo.delete(id)

WorkoutsRepository - Completed workouts:

const repo = getWorkoutsRepository()
await repo.getAll()
await repo.getById(id)
await repo.create(convertWorkoutToDb(workout))
await repo.delete(id)

ActiveWorkoutRepository - Singleton active workout:

const repo = getActiveWorkoutRepository()
await repo.load()
await repo.save(dbActiveWorkout)
await repo.delete()
await repo.exists()

BenchmarksRepository - Benchmark workouts:

const repo = getBenchmarksRepository()
await repo.getAll()
await repo.getById(id)
await repo.create({ id: generateId(), name: 'Fran', ... })
await repo.update(id, { name: 'Fran (Scaled)' })
await repo.delete(id)

TemplatesRepository - Workout templates:

const repo = getTemplatesRepository()
await repo.getAll()
await repo.getById(id)
await repo.create(template)
await repo.update(id, changes)
await repo.delete(id)

Using Converters

Always convert when crossing domain/database boundary:

import { convertWorkoutToDb, convertDbToWorkout } from '@/db/converters'

// Domain → Database
const dbWorkout = convertWorkoutToDb(workout)
await getWorkoutsRepository().create(dbWorkout)

// Database → Domain
const dbWorkout = await getWorkoutsRepository().getById(id)
const workout = convertDbToWorkout(dbWorkout)

Partial Updates with buildPartialUpdate

Dexie's update() overwrites all keys in the object. Use buildPartialUpdate to only modify provided fields:

import { buildPartialUpdate } from '@/db/partialUpdate'

const NULLABLE_FIELDS = ['equipment', 'muscle', 'image']

// Only includes keys present in updates
// Converts undefined → null for nullable fields
const dbUpdates = buildPartialUpdate(updates, NULLABLE_FIELDS)
await repo.update(id, dbUpdates)

Why: Without filtering, {name: 'Squat', equipment: undefined} would set equipment to null even if you only meant to update the name.

Project-Specific Gotchas

1. Use null in Database, undefined in Domain

IndexedDB doesn't support undefined:

// Database types
type DbExercise = {
  equipment: Equipment | null  // Use null
}

// Domain types
type Exercise = {
  equipment?: Equipment  // Use undefined
}

2. Always Reset Database in Tests

import { resetDatabase } from '@/__tests__/setup'

beforeEach(async () => {
  await resetDatabase()
})

3. Convert Types at Boundaries

// BAD - Type mismatch
await getWorkoutsRepository().create(workout)

// GOOD - Convert first
const dbWorkout = convertWorkoutToDb(workout)
await getWorkoutsRepository().create(dbWorkout)

平台分布

OpenCode

29.44%
按下载量换算27

Claude Code

24.09%
按下载量换算22

windsurf

19.29%
按下载量换算18

Codex

11.85%
按下载量换算11

Antigravity

7.07%
按下载量换算7

Gemini CLI

3.85%
按下载量换算4

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

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

安装前确认

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

继续浏览同类 Skills