From effect-ts
This skill should be used when the user asks about "Effect resources", "acquireRelease", "Scope", "finalizers", "resource cleanup", "Effect.addFinalizer", "Effect.ensuring", "scoped effects", "resource lifecycle", "bracket pattern", "safe resource handling", "database connections", "file handles", or needs to understand how Effect guarantees resource cleanup.
npx claudepluginhub andrueandersoncs/claude-skill-effect-ts --plugin effect-tsThis skill uses the workspace's default tool permissions.
Effect provides structured resource management that **guarantees cleanup** even when errors occur or the effect is interrupted. This is essential for:
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Checks Next.js compilation errors using a running Turbopack dev server after code edits. Fixes actionable issues before reporting complete. Replaces `next build`.
Effect provides structured resource management that guarantees cleanup even when errors occur or the effect is interrupted. This is essential for:
A Scope is a context that tracks resources and ensures their cleanup:
Effect<A, E, R | Scope>;
// ^^^^^ Indicates resource needs cleanup
The fundamental pattern for safe resource management:
import { Effect } from "effect";
const managedFile = Effect.acquireRelease(
Effect.sync(() => fs.openSync("file.txt", "r")),
(fd) => Effect.sync(() => fs.closeSync(fd)),
);
const program = Effect.gen(function* () {
const fd = yield* managedFile;
const content = yield* Effect.sync(() => fs.readFileSync(fd, "utf-8"));
return content;
});
// Run with automatic scope management
const result = yield * Effect.scoped(program);
Converts a scoped effect into a regular effect by managing the scope:
const runnable = Effect.scoped(program);
The scope closes when the scoped block completes, triggering all finalizers.
For simpler cases, combine acquire/use/release in one call:
const readFile = (path: string) =>
Effect.acquireUseRelease(
Effect.sync(() => fs.openSync(path, "r")),
(fd) => Effect.sync(() => fs.readFileSync(fd, "utf-8")),
(fd) => Effect.sync(() => fs.closeSync(fd)),
);
Add cleanup logic to the current scope:
const program = Effect.gen(function* () {
yield* Effect.addFinalizer(() => Effect.log("Cleanup running!"));
// ... do work ...
return result;
});
Run cleanup after effect completes (success or failure):
const withCleanup = someEffect.pipe(Effect.ensuring(Effect.log("Always runs after effect")));
Run different cleanup based on exit status:
const withExitHandler = someEffect.pipe(
Effect.onExit((exit) => (Exit.isSuccess(exit) ? Effect.log("Succeeded!") : Effect.log("Failed or interrupted"))),
);
const program = Effect.gen(function* () {
const db = yield* acquireDbConnection;
const cache = yield* acquireRedisConnection;
});
const result = yield * Effect.scoped(program);
const program = Effect.gen(function* () {
const [db, cache] = yield* Effect.all([acquireDbConnection, acquireRedisConnection]);
});
const DbPool = Effect.acquireRelease(
Effect.promise(() =>
createPool({
host: "localhost",
database: "mydb",
max: 10,
}),
),
(pool) => Effect.promise(() => pool.end()),
);
const query = (sql: string) =>
Effect.gen(function* () {
const pool = yield* DbPool;
return yield* Effect.tryPromise(() => pool.query(sql));
});
const withFile = <A>(path: string, use: (handle: FileHandle) => Effect.Effect<A>) =>
Effect.acquireUseRelease(
Effect.promise(() => fs.promises.open(path)),
use,
(handle) => Effect.promise(() => handle.close()),
);
const withLock = <A>(lock: Lock, effect: Effect.Effect<A>) =>
Effect.acquireUseRelease(
lock.acquire,
() => effect,
() => lock.release,
);
Use Layer.scoped for service-level resources:
const DatabaseLive = Layer.scoped(
Database,
Effect.gen(function* () {
const pool = yield* Effect.acquireRelease(createPool(), (pool) => Effect.promise(() => pool.end()));
return {
query: (sql) => Effect.tryPromise(() => pool.query(sql)),
};
}),
);
Finalizers should not fail, but if they do:
const safeRelease = (resource: Resource) =>
Effect.sync(() => resource.close()).pipe(Effect.catchAll((error) => Effect.logError("Cleanup failed", error)));
const managed = Effect.acquireRelease(acquire, safeRelease);
Resources are cleaned up even on interruption:
const program = Effect.gen(function* () {
const resource = yield* acquireResource;
yield* Effect.sleep("1 hour");
});
const result = yield * program.pipe(Effect.scoped, Effect.timeout("1 second"));
For comprehensive resource management documentation, consult ${CLAUDE_PLUGIN_ROOT}/references/llms-full.txt.
Search for these sections: