Token导航 LogoToken导航

Agent Skill · 开发规范

effect-best-practices(影响最佳实践)

effect-best-practices 用于记录任务执行中的错误、用户纠正、经验和能力缺口,适合在 Codex、Claude、Cursor、Gemini CLI 中希望让 Agent 持续沉淀问题、修正和最佳实践时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

effect-best-practices 是开发规范类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为接触敏感数据;许可证为 MIT,第三方扫描已通过,按安装量在开发规范类的 1,265 个 Skill 里排第 128。

总安装 14,737周安装 602GitHub Stars 67下载 4,720核验 2026-05-01

安装说明

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

安装方式

通过对话安装

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

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

命令行安装

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

skills.shnpx skills
npx skills add https://github.com/makisuo/skills --skill effect-best-practices

这个 Skill 是做什么的

开发规范类 Skill 用来统一代码风格、目录结构和评审口径,产出多为检查清单或改写建议。

Effect-TS 服务、错误、层和 React 状态管理的固定模式。

强化效果.服务 使用访问器: true 对于业务逻辑,Schema.TaggedError 对于错误,以及 Layer.mergeAll /Layer.provideMerge 用于平坦、高性能的构图 需要 Effect Language Server 进行编辑时诊断(30 多种特定于效果的检查)、快速信息、完成和重构 涵盖品牌实体 ID、选项 超过可空性,Effect.fn 用于跟踪以及使用 catchTag 进行显式错误处理 / catch标签 包括 React 状态的 Effect Atom 模式:Atom.make 外部组件,useAtomValue / 使用AtomSet 钩子,Result.builder 用于错误处理和 keepAlive 对于持久状态 禁止 runSync / 运行Promise 在服务中,抛出 在生成器中,console.log ,以及直接process.env 访问

来源仓库内路径:skills/effect-best-practices

上手步骤

4 步
  1. 1

    确认自己的宿主工具

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

  2. 2

    拿到安装命令

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

  3. 3

    安装后重启宿主

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

  4. 4

    按最小权限试用

    它需要接触敏感数据,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

effect-best-practices 怎么安装?

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

effect-best-practices 支持哪些 AI 工具?

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

effect-best-practices 需要什么权限?

权限级别是接触敏感数据。该 Skill 可能接触密钥、Token、环境变量或敏感配置,应进入高风险复核队列,默认不自动发布。

effect-best-practices 是免费的吗?

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

effect-best-practices 用的人多吗?

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

effect-best-practices 安全吗?

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

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

值得选的地方

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

先想清楚的地方

  • 需要接触敏感数据,装到常用环境前要先评估影响面。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

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

不太适合

  • 环境里有大量密钥、凭证,不方便给第三方 Skill 读取的人。
  • 需要厂商级技术支持和 SLA 的生产系统。

开发规范类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
vercel-react-best-practices

Vercel React 最佳实践 · Codex、Claude

8,490,72025,918操作浏览器MIT通过
web-design-guidelines

网页设计指南 · Codex、Claude

6,686,01625,885需要联网MIT有提醒
remotion-best-practices

Remotion 最佳实践 · Codex、Claude

6,505,6323,027操作浏览器MIT有提醒
supabase-postgres-best-practices

Supabase Postgres 最佳实践 · Codex、Claude

3,175,5232,030需要联网MIT通过
next-best-practices

Next.js 最佳实践 · Codex、Claude

1,848,240841操作浏览器MIT通过
effect-best-practices

影响最佳实践 · 本页

14,73767接触敏感数据MIT通过

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

SKILL.md 原文

Effect-TS Best Practices

This skill enforces opinionated, consistent patterns for Effect-TS codebases. These patterns optimize for type safety, testability, observability, and maintainability.

Effect Language Server (Required)

The Effect Language Server is essential for Effect development. It catches errors at edit-time that TypeScript alone cannot detect, provides Effect-specific refactors, and improves developer productivity.

Setup

  1. Install:
npm install @effect/language-service --save-dev
  1. Add to tsconfig.json:
{
  "compilerOptions": {
    "plugins": [{ "name": "@effect/language-service" }]
  }
}
  1. Configure your editor to use workspace TypeScript:

- VSCode: F1 → "TypeScript: Select TypeScript Version" → "Use Workspace Version" - JetBrains: Settings → Languages & Frameworks → TypeScript → Use workspace version

Features

  • Diagnostics: Detects 30+ Effect-specific issues (floating Effects, missing requirements, incorrect yield patterns)
  • Quick Info: Hover to see Effect type parameters (Success, Error, Requirements)
  • Completions: Auto-complete Self, Duration strings, Schema brands
  • Refactors: Convert async → Effect.gen, auto-compose Layers, transform to Schema

Build-Time Diagnostics

For CI enforcement:

npx effect-language-service patch

See references/language-server.md for configuration options and CLI tools.

Quick Reference: Critical Rules

CategoryDODON'T
ServicesEffect.Service with accessors: trueContext.Tag for business logic
Dependenciesdependencies: [Dep.Default] in serviceManual Layer.provide at usage sites
LayersLayer.mergeAll for flat compositionDeeply nested Layer.provide chains
Layer ChainingLayer.provideMerge for incremental compositionMultiple Layer.provide (creates nested types)
ErrorsSchema.TaggedError with message fieldPlain classes or generic Error
Error SpecificityUserNotFoundError, SessionExpiredErrorGeneric NotFoundError, BadRequestError
Error HandlingcatchTag/catchTagscatchAll or mapError
IDsSchema.UUID.pipe(Schema.brand("@App/EntityId"))Plain string for entity IDs
FunctionsEffect.fn("Service.method")Anonymous generators
LoggingEffect.log with structured dataconsole.log
ConfigConfig.* with validationprocess.env directly
OptionsOption.match with both casesOption.getOrThrow
NullabilityOption<T> in domain typesnull/undefined
AtomsAtom.make outside componentsCreating atoms inside render
Atom StateAtom.keepAlive for global stateForgetting keepAlive for persistent state
Atom UpdatesuseAtomSet in React componentsAtom.update imperatively from React
Atom Cleanupget.addFinalizer() for side effectsMissing cleanup for event listeners
Atom ResultsResult.builder with onErrorTagIgnoring loading/error states

Service Definition Pattern

Always use Effect.Service for business logic services. This provides automatic accessors, built-in Default layer, and proper dependency declaration.

import { Effect } from "effect"

export class UserService extends Effect.Service<UserService>()("UserService", {
    accessors: true,
    dependencies: [UserRepo.Default, CacheService.Default],
    effect: Effect.gen(function* () {
        const repo = yield* UserRepo
        const cache = yield* CacheService

        const findById = Effect.fn("UserService.findById")(function* (id: UserId) {
            const cached = yield* cache.get(id)
            if (Option.isSome(cached)) return cached.value

            const user = yield* repo.findById(id)
            yield* cache.set(id, user)
            return user
        })

        const create = Effect.fn("UserService.create")(function* (data: CreateUserInput) {
            const user = yield* repo.create(data)
            yield* Effect.log("User created", { userId: user.id })
            return user
        })

        return { findById, create }
    }),
}) {}

// Usage - dependencies are already wired
const program = Effect.gen(function* () {
    const user = yield* UserService.findById(userId)
    return user
})

// At app root
const MainLive = Layer.mergeAll(UserService.Default, OtherService.Default)

When Context.Tag is acceptable:

  • Infrastructure with runtime injection (Cloudflare KV, worker bindings)
  • Factory patterns where resources are provided externally

See references/service-patterns.md for detailed patterns.

Error Definition Pattern

Always use Schema.TaggedError for errors. This makes them serializable (required for RPC) and provides consistent structure.

import { Schema } from "effect"
import { HttpApiSchema } from "@effect/platform"

export class UserNotFoundError extends Schema.TaggedError<UserNotFoundError>()(
    "UserNotFoundError",
    {
        userId: UserId,
        message: Schema.String,
    },
    HttpApiSchema.annotations({ status: 404 }),
) {}

export class UserCreateError extends Schema.TaggedError<UserCreateError>()(
    "UserCreateError",
    {
        message: Schema.String,
        cause: Schema.optional(Schema.String),
    },
    HttpApiSchema.annotations({ status: 400 }),
) {}

Error handling - use catchTag/catchTags:

// CORRECT - preserves type information
yield* repo.findById(id).pipe(
    Effect.catchTag("DatabaseError", (err) =>
        Effect.fail(new UserNotFoundError({ userId: id, message: "Lookup failed" }))
    ),
    Effect.catchTag("ConnectionError", (err) =>
        Effect.fail(new ServiceUnavailableError({ message: "Database unreachable" }))
    ),
)

// CORRECT - multiple tags at once
yield* effect.pipe(
    Effect.catchTags({
        DatabaseError: (err) => Effect.fail(new UserNotFoundError({ userId: id, message: err.message })),
        ValidationError: (err) => Effect.fail(new InvalidEmailError({ email: input.email, message: err.message })),
    }),
)

Prefer Explicit Over Generic Errors

Every distinct failure reason deserves its own error type. Don't collapse multiple failure modes into generic HTTP errors.

// WRONG - Generic errors lose information
export class NotFoundError extends Schema.TaggedError<NotFoundError>()(
    "NotFoundError",
    { message: Schema.String },
    HttpApiSchema.annotations({ status: 404 }),
) {}

// Then mapping everything to it:
Effect.catchTags({
    UserNotFoundError: (err) => Effect.fail(new NotFoundError({ message: "Not found" })),
    ChannelNotFoundError: (err) => Effect.fail(new NotFoundError({ message: "Not found" })),
    MessageNotFoundError: (err) => Effect.fail(new NotFoundError({ message: "Not found" })),
})
// Frontend gets useless: { _tag: "NotFoundError", message: "Not found" }
// Which resource? User? Channel? Message? Can't tell!
// CORRECT - Explicit domain errors with rich context
export class UserNotFoundError extends Schema.TaggedError<UserNotFoundError>()(
    "UserNotFoundError",
    { userId: UserId, message: Schema.String },
    HttpApiSchema.annotations({ status: 404 }),
) {}

export class ChannelNotFoundError extends Schema.TaggedError<ChannelNotFoundError>()(
    "ChannelNotFoundError",
    { channelId: ChannelId, message: Schema.String },
    HttpApiSchema.annotations({ status: 404 }),
) {}

export class SessionExpiredError extends Schema.TaggedError<SessionExpiredError>()(
    "SessionExpiredError",
    { sessionId: SessionId, expiredAt: Schema.DateTimeUtc, message: Schema.String },
    HttpApiSchema.annotations({ status: 401 }),
) {}

// Frontend can now show specific UI:
// - UserNotFoundError → "User doesn't exist"
// - ChannelNotFoundError → "Channel was deleted"
// - SessionExpiredError → "Your session expired. Please log in again."

See references/error-patterns.md for error remapping and retry patterns.

Schema & Branded Types Pattern

Brand all entity IDs for type safety across service boundaries:

import { Schema } from "effect"

// Entity IDs - always branded
export const UserId = Schema.UUID.pipe(Schema.brand("@App/UserId"))
export type UserId = Schema.Schema.Type<typeof UserId>

export const OrganizationId = Schema.UUID.pipe(Schema.brand("@App/OrganizationId"))
export type OrganizationId = Schema.Schema.Type<typeof OrganizationId>

// Domain types - use Schema.Struct
export const User = Schema.Struct({
    id: UserId,
    email: Schema.String,
    name: Schema.String,
    organizationId: OrganizationId,
    createdAt: Schema.DateTimeUtc,
})
export type User = Schema.Schema.Type<typeof User>

// Input types for mutations
export const CreateUserInput = Schema.Struct({
    email: Schema.String.pipe(Schema.pattern(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)),
    name: Schema.String.pipe(Schema.minLength(1)),
    organizationId: OrganizationId,
})
export type CreateUserInput = Schema.Schema.Type<typeof CreateUserInput>

When NOT to brand:

  • Simple strings that don't cross service boundaries (URLs, file paths)
  • Primitive config values

See references/schema-patterns.md for transforms and advanced patterns.

Function Pattern with Effect.fn

Always use Effect.fn for service methods. This provides automatic tracing with proper span names:

// CORRECT - Effect.fn with descriptive name
const findById = Effect.fn("UserService.findById")(function* (id: UserId) {
    yield* Effect.annotateCurrentSpan("userId", id)
    const user = yield* repo.findById(id)
    return user
})

// CORRECT - Effect.fn with multiple parameters
const transfer = Effect.fn("AccountService.transfer")(
    function* (fromId: AccountId, toId: AccountId, amount: number) {
        yield* Effect.annotateCurrentSpan("fromId", fromId)
        yield* Effect.annotateCurrentSpan("toId", toId)
        yield* Effect.annotateCurrentSpan("amount", amount)
        // ...
    }
)

Layer Composition

Declare dependencies in the service, not at usage sites:

// CORRECT - dependencies in service definition
export class OrderService extends Effect.Service<OrderService>()("OrderService", {
    accessors: true,
    dependencies: [
        UserService.Default,
        ProductService.Default,
        PaymentService.Default,
    ],
    effect: Effect.gen(function* () {
        const users = yield* UserService
        const products = yield* ProductService
        const payments = yield* PaymentService
        // ...
    }),
}) {}

// At app root - simple merge
const AppLive = Layer.mergeAll(
    OrderService.Default,
    // Infrastructure layers (intentionally not in dependencies)
    DatabaseLive,
    RedisLive,
)

Layer composition patterns:

// Use Layer.mergeAll for flat composition of same-level layers
const RepoLive = Layer.mergeAll(
    UserRepo.Default,
    OrderRepo.Default,
    ProductRepo.Default,
)

// Use Layer.provideMerge for incremental chaining (flatter types than Layer.provide)
const MainLive = DatabaseLive.pipe(
    Layer.provideMerge(ConfigServiceLive),
    Layer.provideMerge(LoggerLive),
    Layer.provideMerge(CacheLive),
)

Why layers over Effect.provide:

  • Deduplication: Layers memoize construction - same service instantiated once. Effect.provide creates new instances each call.
  • TypeScript performance: Deep Layer.provide nesting creates complex recursive types that slow the LSP. Layer.mergeAll and Layer.provideMerge produce flatter types.
  • Resource management: Scoped layers properly share and clean up resources.

See references/layer-patterns.md for testing layers, config-dependent layers, and the layerConfig pattern.

Option Handling

Never use Option.getOrThrow. Always handle both cases explicitly:

// CORRECT - explicit handling
yield* Option.match(maybeUser, {
    onNone: () => Effect.fail(new UserNotFoundError({ userId, message: "Not found" })),
    onSome: (user) => Effect.succeed(user),
})

// CORRECT - with getOrElse for defaults
const name = Option.getOrElse(maybeName, () => "Anonymous")

// CORRECT - Option.map for transformations
const upperName = Option.map(maybeName, (n) => n.toUpperCase())

Effect Atom (Frontend State)

Effect Atom provides reactive state management for React with Effect integration.

Basic Atoms

import { Atom } from "@effect-atom/atom-react"

// Define atoms OUTSIDE components
const countAtom = Atom.make(0)

// Use keepAlive for global state that should persist
const userPrefsAtom = Atom.make({ theme: "dark" }).pipe(Atom.keepAlive)

// Atom families for per-entity state
const modalAtomFamily = Atom.family((type: string) =>
    Atom.make({ isOpen: false }).pipe(Atom.keepAlive)
)

React Integration

import { useAtomValue, useAtomSet, useAtom, useAtomMount } from "@effect-atom/atom-react"

function Counter() {
    const count = useAtomValue(countAtom)           // Read only
    const setCount = useAtomSet(countAtom)          // Write only
    const [value, setValue] = useAtom(countAtom)    // Read + write

    return <button onClick={() => setCount((c) => c + 1)}>{count}</button>
}

// Mount side-effect atoms without reading value
function App() {
    useAtomMount(keyboardShortcutsAtom)
    return <>{children}</>
}

Handling Results with Result.builder

Use Result.builder for rendering effectful atom results. It provides chainable error handling with onErrorTag:

import { Result } from "@effect-atom/atom-react"

function UserProfile() {
    const userResult = useAtomValue(userAtom) // Result<User, Error>

    return Result.builder(userResult)
        .onInitial(() => <div>Loading...</div>)
        .onErrorTag("NotFoundError", () => <div>User not found</div>)
        .onError((error) => <div>Error: {error.message}</div>)
        .onSuccess((user) => <div>Hello, {user.name}</div>)
        .render()
}

Atoms with Side Effects

const scrollYAtom = Atom.make((get) => {
    const onScroll = () => get.setSelf(window.scrollY)

    window.addEventListener("scroll", onScroll)
    get.addFinalizer(() => window.removeEventListener("scroll", onScroll)) // REQUIRED

    return window.scrollY
}).pipe(Atom.keepAlive)

React Mutations

For mutation atoms, derive loading state from result.waiting instead of useState:

const [result, mutate] = useAtom(deleteMutation, { mode: "promise" })
const isLoading = result.waiting // Updates automatically, no useState/finally needed

Dialog ownership: Move mutation logic into dialog components. Dialog owns the mutation hook, loading state, and toasts. Parent provides data props and an onSuccess callback.

Cache invalidation: Use reactivityKeys on both mutation and query atoms to auto-invalidate queries after mutations — replaces manual refresh() calls.

See references/effect-atom-patterns.md for complete patterns including families, localStorage, mutations, and anti-patterns.

RPC & Cluster Patterns

For RPC contracts and cluster workflows, see:

  • references/rpc-cluster-patterns.md - RpcGroup, Workflow.make, Activity patterns

Anti-Patterns (Forbidden)

These patterns are never acceptable:

// FORBIDDEN - runSync/runPromise inside services
const result = Effect.runSync(someEffect) // Never do this

// FORBIDDEN - throw inside Effect.gen
yield* Effect.gen(function* () {
    if (bad) throw new Error("No!") // Use Effect.fail instead
})

// FORBIDDEN - catchAll losing type info
yield* effect.pipe(Effect.catchAll(() => Effect.fail(new GenericError())))

// FORBIDDEN - console.log
console.log("debug") // Use Effect.log

// FORBIDDEN - process.env directly
const key = process.env.API_KEY // Use Config.string("API_KEY")

// FORBIDDEN - null/undefined in domain types
type User = { name: string | null } // Use Option<string>

See references/anti-patterns.md for the complete list with rationale.

Observability

// Structured logging
yield* Effect.log("Processing order", { orderId, userId, amount })

// Metrics
const orderCounter = Metric.counter("orders_processed")
yield* Metric.increment(orderCounter)

// Config with validation
const config = Config.all({
    port: Config.integer("PORT").pipe(Config.withDefault(3000)),
    apiKey: Config.redacted("API_KEY"),
    maxRetries: Config.integer("MAX_RETRIES").pipe(
        Config.validate({ message: "Must be positive", validation: (n) => n > 0 })
    ),
})

See references/observability-patterns.md for metrics and tracing patterns.

Reference Files

For detailed patterns, consult these reference files in the references/ directory:

  • language-server.md - Effect Language Service setup, diagnostics, refactors, CLI tools
  • service-patterns.md - Service definition, Effect.fn, Context.Tag exceptions
  • error-patterns.md - Schema.TaggedError, error remapping, retry patterns
  • schema-patterns.md - Branded types, transforms, Schema.Class
  • layer-patterns.md - Dependency composition, testing layers
  • rpc-cluster-patterns.md - RpcGroup, Workflow, Activity patterns
  • effect-atom-patterns.md - Atom, families, React hooks, Result handling
  • anti-patterns.md - Complete list of forbidden patterns
  • observability-patterns.md - Logging, metrics, config patterns

平台分布

Claude Code

31.71%
按下载量换算1,497

OpenCode

25.6%
按下载量换算1,208

Cursor

17%
按下载量换算802

Codex

12.98%
按下载量换算613

Gemini CLI

7.82%
按下载量换算369

Antigravity

3.32%
按下载量换算157

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

接触敏感数据

该 Skill 可能接触密钥、Token、环境变量或敏感配置,应进入高风险复核队列,默认不自动发布。

安装前确认

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

继续浏览同类 Skills