sample/harness/tanstack-start/skills/better-result-adopt/SKILL.md
Adopt better-result in an existing TypeScript codebase. Use when replacing try/catch, Promise rejection handling, null sentinels, or thrown domain exceptions with typed Result workflows.
npx skillsauth add sc30gsw/claude-code-customes better-result-adoptInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Adopt better-result incrementally in existing codebases without rewriting everything at once.
Use this skill when the user wants to:
Result.try or Result.tryPromiseResult<T, E>TaggedError typesandThen chains or Result.gen| Task | Files to Read |
| -------------------------------------- | ----------------------------- |
| Adopt better-result in a module | This file |
| Define or review error types | references/tagged-errors.md |
| Inspect library implementation details | opensrc/ if present |
Before editing code:
better-result is already installed in the target project.opensrc/ directory. If present, read the package source there for current patterns.Begin with I/O boundaries and exception-heavy code:
Do not convert the whole codebase at once.
Use the official better-result Best Practices as the source of truth:
Result for expected failures that are part of normal flow.Result.try / Result.tryPromise.TaggedError for discriminated domain and infrastructure error unions.message, cause, ids, status, operation, and reason.Result.gen; in async generators, use yield* Result.await(...).Result.isError / Result.isOk type guards or .match(...); both are valid. Choose the clearer option for the call site.matchError when handling a tagged error union exhaustively.Result<T, any>..unwrap() without a preceding type guard or an intentional, documented throw.| Category | Examples | Target shape |
| --------------------- | --------------------------- | -------------------------------------------------------------- |
| Domain errors | not found, validation, auth | TaggedError + Result.err |
| Infrastructure errors | network, DB, file I/O | Result.tryPromise + mapped error |
| Programmer defects | bad assumptions, null deref | leave throwing; defects become Panic inside Result callbacks |
Result.try / Result.tryPromise.Result.Result values.andThen, mapError, or Result.gen.When a function is called through RPC, server functions, route loaders, or any serialization boundary:
Result internally to model and compose failures.Ok, Err, TaggedError, Error, Response, or other class instances directly.unknown/any or the function is an explicit public API boundary.const result = await Result.gen(async function* () {
const user = yield* Result.await(fetchUser(input));
const session = yield* createSession(user);
return Result.ok(session);
});
if (Result.isError(result)) {
return { error: true as const, message: result.error.message };
}
return { error: false as const, session: result.value };
Result.tryfunction parseConfig(json: string): Result<Config, ParseError> {
return Result.try({
try: () => JSON.parse(json) as Config,
catch: (cause) => new ParseError({ cause, message: `Parse failed: ${cause}` }),
});
}
Result.tryPromiseasync function fetchUser(id: string): Promise<Result<User, ApiError | UnhandledException>> {
return Result.tryPromise({
try: async () => {
const res = await fetch(`/api/users/${id}`);
if (!res.ok) throw new ApiError({ status: res.status, message: `API ${res.status}` });
return res.json() as Promise<User>;
},
catch: (cause) => (cause instanceof ApiError ? cause : new UnhandledException({ cause })),
});
}
Resultfunction findUser(id: string): Result<User, NotFoundError> {
const user = users.find((candidate) => candidate.id === id);
return user
? Result.ok(user)
: Result.err(new NotFoundError({ id, message: `User ${id} not found` }));
}
Result.genasync function processOrder(orderId: string): Promise<Result<OrderResult, OrderError>> {
return Result.gen(async function* () {
const order = yield* Result.await(fetchOrder(orderId));
const validated = yield* validateOrder(order);
const result = yield* Result.await(submitOrder(validated));
return Result.ok(result);
});
}
try, catch, .catch(...), throw, null, undefined, and status-flag error handling.TaggedError classes before changing control flow.Result<T, E> or Promise<Result<T, E>>.Result.Result.gen or andThen.cause, IDs, messages, and other structured fields.A migration is complete when:
Result valuesTaggedError classesResult or explicitly unwrap/match itthrow-based and Result-based APIs deep in the same flowPanic instead of fixing the underlying defect.match(...) as mandatory when a Result.isError / Result.isOk type guard is clearer| File | Purpose |
| ----------------------------- | --------------------------------------------------------- |
| references/tagged-errors.md | TaggedError patterns, matching, type guards, and examples |
If opensrc/ exists, treat it as the source of truth for implementation details and current API behavior.
tools
Guide for creating effective skills. This skill should be used when users want to create a new skill (or update an existing skill) that extends Claude's capabilities with specialized knowledge, workflows, or tool integrations.
testing
# sdd-workflow — Workflow Status Dashboard ## Slash Command ``` /sdd-workflow [slug] ``` ## Purpose Read-only meta skill. Displays the current state of the SDD workflow — which phases are complete, which is next, and any blockers. Does NOT modify any files. --- ## This Skill is Read-Only `sdd-workflow` never writes to or modifies any file. It only reads spec files and git history to report status. There is no approval gate for this skill. --- ## Usage: Specific Feature ``` /sdd-workflo
content-media
# sdd-tasks **Slash command**: `/sdd-tasks <slug>` **Purpose**: Generate `tasks.md` (TASK-001..N) and `progress.md` from `requirements.md` and `design.md`. --- ## Prerequisites - `.claude/specs/<slug>/requirements.md` must exist - `.claude/specs/<slug>/design.md` must exist (run `/sdd-design` first) --- ## Steps ### 1. Read spec inputs ``` .claude/specs/<slug>/requirements.md .claude/specs/<slug>/design.md ``` Extract: - Every REQ-XXX ID with its acceptance criteria - Every design sect
development
# sdd-review — Post-Implementation Code Review ## Slash Command ``` /sdd-review <slug> ``` ## Purpose Run code review and security review on all changes introduced by the feature branch. Append structured findings to `review.md`. Does NOT auto-apply fixes — only proposes them. --- ## Prerequisites - `sdd-impl` has completed: all tasks in `progress.md` are `done` (or at least one is `done`; partial reviews are allowed). - The feature branch must have at least one commit ahead of `main`. -