用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/PaulJPhilp/EffectPatterns --skill effect-patterns-platform命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
正在显示 SKILL.md
基于 SOC 职业分类
| name | effect-patterns-platform |
| description | Effect-TS patterns for Platform. Use when working with platform in Effect-TS applications. |
This skill provides 6 curated Effect-TS patterns for platform. Use this skill when working on tasks related to:
Rule: Use Terminal for user input/output in CLI applications, providing proper buffering and cross-platform character encoding.
Good Example:
This example demonstrates building an interactive CLI application.
import { Terminal, Effect } from "@effect/platform";
interface UserInput {
readonly name: string;
readonly email: string;
readonly age: number;
}
const program = Effect.gen(function* () {
console.log(`\n[INTERACTIVE CLI] User Information Form\n`);
// Example 1: Simple prompts
yield* Terminal.writeLine(`=== User Setup ===`);
yield* Terminal.writeLine(``);
yield* Terminal.write(`What is your name? `);
const name = yield* Terminal.readLine();
yield* Terminal.write(`What is your email? `);
const email = yield* Terminal.readLine();
yield* Terminal.write(`What is your age? `);
const ageStr = yield* Terminal.readLine();
const age = parseInt(ageStr);
// Example 2: Display collected information
yield* Terminal.writeLine(``);
yield* Terminal.writeLine(`=== Summary ===`);
yield* Terminal.writeLine(`Name: ${name}`);
yield* Terminal.writeLine(`Email: ${email}`);
yield* Terminal.writeLine(`Age: ${age}`);
// Example 3: Confirmation
yield* Terminal.writeLine(``);
yield* Terminal.write(`Confirm information? (yes/no) `);
const confirm = yield* Terminal.readLine();
if (confirm.toLowerCase() === "yes") {
yield* Terminal.writeLine(`✓ Information saved`);
} else {
yield* Terminal.writeLine(`✗ Cancelled`);
}
});
Effect.runPromise(program);
Rationale:
Terminal operations:
Pattern: Terminal.readLine().pipe(...)
Direct stdin/stdout causes issues:
Terminal enables:
Real-world example: CLI setup wizard
Rule: Use FileSystem module for safe, resource-managed file operations with proper error handling and cleanup.
Good Example:
This example demonstrates reading, writing, and manipulating files.
import { FileSystem, Effect, Stream } from "@effect/platform";
import * as fs from "fs/promises";
const program = Effect.gen(function* () {
console.log(`\n[FILESYSTEM] Demonstrating file operations\n`);
// Example 1: Write a file
console.log(`[1] Writing file:\n`);
const content = `Hello, Effect-TS!\nThis is a test file.\nCreated at ${new Date().toISOString()}`;
yield* FileSystem.writeFileUtf8("test.txt", content);
yield* Effect.log(`✓ File written: test.txt`);
// Example 2: Read the file
console.log(`\n[2] Reading file:\n`);
const readContent = yield* FileSystem.readFileUtf8("test.txt");
console.log(readContent);
// Example 3: Get file stats
console.log();
stats = * .().(
.(
.({
: stat.,
: stat.(),
: stat.,
})
)
);
.();
.();
.();
.();
* .();
* .(
.({ : },
.(
,
)
)
);
* .();
.();
entries = * .();
entries.( {
.();
});
.();
appendContent = ;
* .(, appendContent);
finalContent = * .();
.();
.();
* .(
.({ : },
.()
)
);
* .();
* .();
* .();
});
.(program);
Rationale:
FileSystem operations:
Pattern: FileSystem.read(path).pipe(...)
Direct file operations without FileSystem create issues:
FileSystem enables:
Real-world example: Process log files
FileSystem.read(path).pipe(...)Rule: Use KeyValueStore for simple persistent storage of key-value pairs, enabling lightweight caching and session management.
Good Example:
This example demonstrates storing and retrieving persistent data.
import { KeyValueStore, Effect } from "@effect/platform";
interface UserSession {
readonly userId: string;
readonly token: string;
readonly expiresAt: number;
}
const program = Effect.gen(function* () {
console.log(`\n[KEYVALUESTORE] Persistent storage example\n`);
const store = yield* KeyValueStore.KeyValueStore;
// Example 1: Store session data
console.log(`[1] Storing session:\n`);
const session: UserSession = {
userId: "user-123",
token: "token-abc-def",
expiresAt: Date.now() + 3600000, // 1 hour
};
yield* store.set("session:user-123", JSON.stringify(session));
yield* Effect.log();
.();
stored = * store.();
(stored. === ) {
retrievedSession = .(stored.) ;
.();
.();
.(
);
}
.();
hasSession = * store.();
hasOther = * store.();
.();
.();
.();
apiResponses = [
{ : , : [{ : , : }] },
{ : , : [{ : , : }] },
{ : , : [] },
];
* .(
apiResponses.(
store.(
,
.(item.)
)
)
);
* .();
.();
( item apiResponses) {
cached = * store.();
(cached. === ) {
data = .(cached.);
.(
);
}
}
.();
* store.();
removed = * store.();
.();
.();
allKeys = * store..(
.( entries.)
);
.();
});
.(program);
Rationale:
KeyValueStore operations:
Pattern: KeyValueStore.set(key, value).pipe(...)
Without persistent storage, transient data is lost:
KeyValueStore enables:
Real-world example: Caching API responses
Rule: Use Command to spawn and manage external processes, capturing output and handling exit codes reliably with proper error handling.
Good Example:
This example demonstrates executing commands and handling their output.
import { Command, Effect, Chunk } from "@effect/platform";
// Simple command execution
const program = Effect.gen(function* () {
console.log(`\n[COMMAND] Executing shell commands\n`);
// Example 1: List files
console.log(`[1] List files in current directory:\n`);
const lsResult = yield* Command.make("ls", ["-la"]).pipe(
Command.string
);
console.log(lsResult);
// Example 2: Get current date
console.log(`\n[2] Get current date:\n`);
const dateResult = yield* Command.make("date", ["+%Y-%m-%d %H:%M:%S"]).pipe(
Command.string
);
console.log(`Current date: ${dateResult.trim()}`);
// Example 3: Capture exit code
console.log();
fileCheckCmd = * .(, [
,
,
]).(
.,
.
);
(fileCheckCmd. === ) {
.();
} {
.();
}
.();
findResult = * .(, [
,
,
,
,
,
]).(
.
);
tsFiles = .(findResult, );
.(tsFiles, {
.();
});
(.(findResult) > ) {
.();
}
.();
failResult = * .().(
.,
.(
.(-)
)
);
.();
});
.(program);
Rationale:
Execute shell commands with Command:
Pattern: Command.exec("command args").pipe(...)
Shell integration without proper handling causes issues:
Command enables:
Real-world example: Build pipeline
Rule: Use Effect's platform-aware path utilities to handle separators, absolute/relative paths, and environment variables consistently.
Good Example:
This example demonstrates cross-platform path manipulation.
import { Effect, FileSystem } from "@effect/platform";
import * as Path from "node:path";
import * as OS from "node:os";
interface PathOperation {
readonly input: string;
readonly description: string;
}
// Platform info
const getPlatformInfo = () =>
Effect.gen(function* () {
const platform = process.platform;
const separator = Path.sep;
const delimiter = Path.delimiter;
const homeDir = OS.homedir();
yield* Effect.log(
`[PLATFORM] OS: ${platform}, Separator: "${separator}", Home: ${homeDir}`
);
return { platform, separator, delimiter, homeDir };
});
const program = Effect.(* () {
.();
platformInfo = * ();
.();
segments = [, , ];
joinedPath = .(...segments);
* .();
* .();
.();
relativePath = ;
absolutePath = .(relativePath);
* .();
* .();
.();
filePath = ;
parsed = .(filePath);
* .();
* .();
* .();
* .();
* .();
* .();
.();
expandPath = (: ): {
result = pathStr;
result = result.(, .());
result = result.(, .());
result = result.(, process.. || );
result = result.(, process.());
result = result.(, .());
result = result.(, process.. || );
result = result.(, .());
result;
};
envPaths = [
,
,
,
,
];
( envPath envPaths) {
expanded = (envPath);
* .(
);
}
.();
messyPaths = [
,
,
,
,
];
( messy messyPaths) {
normalized = .(messy);
* .(
);
* .(
);
}
.();
baseDir = ;
safeJoin = (: , : ): <> => {
(.(userPath)) {
{ : , : };
}
(userPath.()) {
{ : , : };
}
fullPath = .(base, userPath);
(!fullPath.(base)) {
{ : , : };
}
{ : , : fullPath };
};
<T> {
: ;
?: ;
?: T;
}
testPaths = [
,
,
,
,
];
( test testPaths) {
result = (baseDir, test);
(result.) {
* .();
} {
* .();
}
}
.();
fromDir = ;
toPath = ;
relativePath2 = .(fromDir, toPath);
* .();
* .();
* .();
.();
fileName = ;
ext = .(fileName);
baseName = .(fileName);
dirName = .();
* .();
* .();
* .();
* .();
.();
segmentPath = ;
segments2 = segmentPath.(.).( s);
* .();
* .();
});
.(program);
Rationale:
Path manipulation requires platform awareness:
\, Unix uses //root vs ./file$HOME, %APPDATA%Pattern: Avoid string concatenation, use path.join(), path.resolve()
String-based path handling causes problems:
Problem 1: Platform inconsistency
"C:\data\file.txt" (Windows)Problem 2: Path traversal attacks
"../../../../etc/passwd"Problem 3: Environment variable expansion
"$HOME/myapp/data"$HOME in pathProblem 4: Symlink resolution
/etc/ssl/certs/ca-bundle.crt (symlink)/usr/share/ca-certificates/ca-bundle.crtSolutions:
Platform-aware API:
path.join() handles separatorspath.resolve() creates absolute pathspath.parse() componentsVariable expansion:
$HOME, ~ → user home$USER → username$PWD → current directoryValidation:
..Rule: Use advanced file system patterns to implement efficient, reliable file operations with proper error handling and resource cleanup.
Good Example:
This example demonstrates advanced file system patterns.
import { Effect, Stream, Ref, FileSystem } from "@effect/platform";
import * as Path from "node:path";
import * as FS from "node:fs";
import * as PromiseFS from "node:fs/promises";
const program = Effect.gen(function* () {
console.log(`\n[ADVANCED FILESYSTEM] Complex file operations\n`);
// Example 1: Atomic file write with temporary file
console.log(`[1] Atomic write (crash-safe):\n`);
const atomicWrite = (
filePath: string,
content: string
): Effect.Effect<void> =>
Effect.gen(function* () {
const tempPath = `${filePath}.tmp`;
try {
// Step 1: Write to temporary file
yield* Effect.(
.(tempPath, content, )
);
* .();
* .(
.(tempPath, content, )
);
* .();
* .(
.(tempPath, filePath)
);
* .();
} (error) {
{
* .( .(tempPath));
} {
}
* .(error);
}
});
testFile = ;
* (testFile, );
content = * .(
.(testFile, )
);
* .();
.();
= () =>
.(* () {
byteCount = ;
lineCount = ;
readStream = .(filePath, {
: ,
: * ,
});
* .();
= () =>
.(* () {
byteCount += line.;
lineCount++;
(lineCount <= || lineCount % === ) {
* .(
);
}
});
* ();
* ();
* .(
);
});
* (testFile);
.();
recursiveList = (
: ,
: =
): .<<{ : ; : | }>> =>
.(* () {
: <{ : ; : | }> = [];
= () =>
.(* () {
(depth > maxDepth) {
;
}
entries = * .(
.(currentDir, { : })
);
( entry entries) {
fullPath = .(currentDir, entry.);
(entry.()) {
results.({ : fullPath, : });
* (fullPath, depth + );
} {
results.({ : fullPath, : });
}
}
});
* (dir, );
results;
});
entries = * (, );
* .(
);
( entry entries.(, )) {
= entry. === ? : ;
* .();
}
.();
= () =>
.(* () {
* .();
( file files) {
* (, file.);
}
* .();
});
testFiles = [
{ : , : },
{ : , : },
{ : , : },
];
* (testFiles);
.();
= () =>
.(* () {
* .();
changeCount = ;
= () =>
.(* () {
( i = ; i < ; i++) {
* .();
stat = * .(
.(filePath)
);
(i === ) {
changeCount++;
* .(
);
}
}
});
* ();
* .();
});
* (testFile);
.();
= () =>
.(* () {
lockPath = ;
* (lockPath, );
* .();
{
* .();
* .();
} {
* .(
.(lockPath)
);
* .();
}
});
* (testFile);
.();
efficientCopy = (
: ,
:
): .<> =>
.(* () {
stat = * .(
.(source)
);
* .(
);
content = * .(
.(source)
);
* (destination, content.());
* .();
});
* (testFile, );
* .();
( name [testFile, , ...testFiles.( )]) {
{
* .(
.(name)
);
* .();
} {
}
}
});
.(program);
Rationale:
Advanced file system operations require careful handling:
Pattern: Combine FileSystem API with Ref for state, Stream for data
Simple file operations cause problems at scale:
Problem 1: Corrupted files
Problem 2: Large file handling
Problem 3: Directory synchronization
Problem 4: Inefficient updates
Problem 5: File locking
Solutions:
Atomic writes:
Streaming:
Bulk operations:
File watching: