From typescript-services
Enforces true-myth Result/Task/Maybe patterns for functional error handling and async operations. Use when working with error handling, async operations, optional values, or functional programming patterns.
npx claudepluginhub andercore-labs/claudes-kitchen --plugin typescript-servicesThis skill uses the workspace's default tool permissions.
```typescript
Implements structured self-debugging workflow for AI agent failures: capture errors, diagnose patterns like loops or context overflow, apply contained recoveries, and generate introspection reports.
Monitors deployed URLs for regressions in HTTP status, console errors, performance metrics, content, network, and APIs after deploys, merges, or upgrades.
Provides React and Next.js patterns for component composition, compound components, state management, data fetching, performance optimization, forms, routing, and accessible UIs.
import { Result, Maybe, Unit } from 'true-myth'
import { fromPromise, fromResult, Task } from 'true-myth/task'
Abbreviations: R = Result, T = Task, M = Maybe
Domain (sync):
validate(x: Input): Result<Valid, Error> {
return x.valid
? Result.ok(x)
: Result.err(new ValidationError())
}
Service (async):
save(data: Domain): Task<Domain, DbError> {
return fromPromise(
this.db.save(data),
err => new DbError(err)
)
}
Inbound (unwrap):
async handle(dto: Dto): Promise<Response> {
return fromResult(this.toDomain(dto))
.andThen(this.service.execute)
.map(this.toDto)
.toPromise()
.then(r => r.unwrapOrElse(e => Promise.reject(e)))
}
Error handling | async operations | optional values | promise wrapping | result chaining
| Operation | Use | Example |
|---|---|---|
| Result.ok(value) | Success | Result.ok(user) |
| Result.err(error) | Failure | Result.err(new NotFoundError()) |
| .andThen(fn) | Chain fallible | .andThen(validate) |
| .map(fn) | Transform success | .map(toDto) |
| .mapErr(fn) | Transform error | .mapErr(e => new DomainError(e)) |
| .unwrapOr(default) | Extract with default | .unwrapOr([]) |
| .unwrapOrElse(fn) | Extract with handler | .unwrapOrElse(e => Promise.reject(e)) |
| Operation | Use | Example |
|---|---|---|
| fromPromise(p, mapper) | Wrap promise | fromPromise(api.call(), mapError) |
| fromResult(result) | Result → Task | fromResult(validate(x)) |
| .andThen(fn) | Chain async ops | .andThen(save) |
| .map(fn) | Transform success | .map(toDto) |
| .mapErr(fn) | Transform error | .mapErr(e => new ServiceError(e)) |
| .orElse(fn) | Provide fallback/recovery | .orElse(handleError) |
| await task | Execute Task | await myTask() (NO .toPromise()) |
| .toPromise() | Task → Promise | ONLY at inbound edge |
| Operation | Use | Example |
|---|---|---|
| Maybe.of(value) | Non-null value | Maybe.of(user) |
| Maybe.none() | Absence | Maybe.none() |
| Maybe.fromNullable(x) | Convert nullable | Maybe.fromNullable(apiResponse) |
| .map(fn) | Transform if present | .map(u => u.name) |
| .andThen(fn) | Chain Maybe ops | .andThen(getProfile) |
| .unwrapOr(default) | Extract with default | .unwrapOr('guest') |
// Await Tasks directly (PromiseLike)
async function process(): Promise<void> {
await myTask()
}
// Wrap Promises with fromPromise
const task = fromPromise(
httpClient.get(url),
error => new NetworkError(error)
)
// Chain functionally
fromPromise(fetchUser(id), mapError)
.andThen(user => validateUser(user))
.map(user => transformUser(user))
.orElse(error => {
logger.error('Failed', { error })
return Task.resolve(defaultUser)
})
// Convert error to success with orElse
const safeTask = (): Task<Unit, never> =>
dangerousTask()
.map(() => {
logger.info('Success')
return Unit
})
.orElse(error => {
logger.error('Failed', { error })
return Task.resolve(Unit)
})
// Task ↔ Promise round-trips
safelyTry(() => myTask().toPromise()) // WRONG
myTask().orElse(handleError) // CORRECT
// Unnecessary toPromise before await
await task.toPromise() // WRONG
await task // CORRECT
// Using safelyTry with Tasks
safelyTry(() => myTask().toPromise()) // WRONG - Task already handles errors
myTask() // CORRECT
// Unnecessary allSettled for single Task
allSettled([singleTask()]).map(() => Unit) // WRONG
singleTask().map(() => Unit) // CORRECT
// Using try/catch pattern
safelyTry(() => dangerousTask().toPromise()).mapRejected(...) // WRONG
dangerousTask().orElse(error => ...) // CORRECT
| Never Use | Reason | Use Instead |
|---|---|---|
| .unwrap() | Throws on error | .unwrapOrElse(handler) |
| .unwrapErr() | Throws on ok | .mapErr().unwrapOrElse() |
| .isOk, .isErr | Conditional checking | .map() or .andThen() |
| result.value | Direct access | .map() or .unwrapOr() |
| result.error | Direct access | .mapErr() or .unwrapOrElse() |
| .match() | Pattern matching | .unwrapOrElse() |
| if (result.isOk) | Imperative check | result.map() |
| Task.resolve() | Direct construction | fromResult() |
| Task.reject() | Direct construction | fromPromise() with rejection |
| await task.toPromise() | Unnecessary conversion | await task |
| safelyTry(() => task.toPromise()) | Double wrapping | task.orElse() |
| Never Nest | Use Instead |
|---|---|
| R<M, E> | R<T, NotFoundError | E> |
| M<R<T, E>> | R<T, E> |
| T<M, E> | T<T, NotFoundError | E> |
| M<T<T, E>> | T<T, E> |
| T<R<T, E>, F> | T<T, E | F> |
ONE monadic wrapper per type.
| Layer | Returns | Pattern |
|---|---|---|
| Domain | R<T, E> or M | Synchronous only, NO T/Promise |
| Service | T<T, E> | fromResult() or fromPromise() |
| Inbound | Promise | .toPromise().then(r => r.unwrapOrElse(...)) |
| Outbound | T<T, E> | fromPromise() for external calls |
| Layer | Rule | Pattern |
|---|---|---|
| Domain | NO null/undefined | M for optional, [] for collections |
| Service | NO null/undefined | M for optional, [] for collections |
| Inbound | Convert at edge | M.fromNullable(dto.field) |
| Outbound | Convert at edge | M.fromNullable(external.data) |
Collections:
users: User[] not User[] | null
return [] not null
config: M<Config> not Config | undefined
function(x: M<string>) not function(x?: string)
// External promise
fromPromise(
this.api.call(params),
err => this.mapError(err)
)
// Result → Task
fromResult(this.validate(data))
// Chained
fromResult(validate(x))
.andThen(data => fromPromise(this.save(data), mapDbError))
return fromResult(this.validateDto(dto))
.andThen(this.toDomain)
.andThen(domain => this.service.execute(domain))
.map(this.toResponseDto)
.toPromise()
.then(result => result.unwrapOrElse(error => Promise.reject(error)))
// External data
Maybe.fromNullable(apiResponse.optionalField)
.map(field => this.process(field))
.unwrapOr(defaultValue)
// Chained access
Maybe.fromNullable(user)
.andThen(u => Maybe.fromNullable(u.profile))
.andThen(p => Maybe.fromNullable(p.settings))
.map(s => s.theme)
.unwrapOr('default')
type ServiceError = DomainError | DbError | ApiError
domain: R<T, DomainError>
service: T<T, DomainError | DbError>
inbound: Promise<T>
See examples.md for complete layer-specific implementations:
import { Unit } from 'true-myth'
deleteUser(id: UserId): Result<Unit, DeleteError> {
return this.canDelete(id)
.andThen(() => this.performDelete(id))
.map(() => Unit)
}
// Result<Unit, E> NOT Result<void, E>
// Result.ok(Unit) NOT Result.ok(undefined)
function mockFindUser(id: string): Task<User, NotFoundError> {
return id === 'not-found'
? Task.reject(new NotFoundError())
: Task.resolve({ id, name: 'Test User' })
}
it('should handle user lookup', async () => {
mockRepo.findById.mockReturnValue(Task.resolve(testUser))
const result = await service.getUser('123')
expect(result).toEqual(Result.ok(testUser))
})
// ✓ CORRECT - compare monadic type directly
expect(result).toEqual(Result.ok(expectedValue))
expect(maybe).toEqual(Maybe.of(expectedValue))
expect(maybe).toEqual(Maybe.none())
// ✗ WRONG - don't unwrap or check internal state
expect(result.isOk).toBe(true)
expect(result.value).toEqual(expectedValue)
| Violation | Fix |
|---|---|
| try/catch | fromPromise(promise, errorMapper) |
| throw new Error() | Promise.reject() in unwrapOrElse ONLY |
| .unwrap() | .unwrapOrElse(handler) |
| if (result.isOk) | result.map() or unwrapOrElse() |
| value ?? default | M.fromNullable(value).unwrapOr(default) |
| value?.prop | M.fromNullable(value).map(v => v.prop) |
| function(x?: T) | function(x: M) |
| return null | return M.none() |
| R<M> | R<T, NotFoundError> |
| T<M> | T<T, NotFoundError> |
| .match({ Ok, Err }) | .unwrapOrElse(err => handle(err)) |
| async without T | fromPromise(asyncFn(), mapper) |
| await task.toPromise() | await task |
| safelyTry(() => task.toPromise()) | task.orElse(handleError) |
CHECK:
1. fromPromise → ALL promises wrapped
2. fromResult → R → T conversions correct
3. .andThen() → Chaining fallible operations
4. .map() → Pure transformations
5. .unwrapOrElse() → ONLY unwrap method in inbound
6. NO .unwrap()/.match()/.isOk usage
7. NO nested monads (R<M<T>>)
8. M<T> → Optional values in domain/service
9. Unit → No-value operations, not void
10. Collections → T[] never T[] | null
11. await task → NO .toPromise() before await
12. .orElse() → Error recovery, not safelyTry
13. NO Task ↔ Promise round-trips
ALL pass → Code compliant
ANY fail → REJECT with violation
MANDATORY: Run after code generation or review.
| Phase | Action |
|---|---|
| 1. Execute | Generate/review code using true-myth |
| 2. Validate | Gather evidence from execution, confirm output validity |
| 3. Report | ✓ Pass → Done | ✗ Fail → List violations with evidence |
| 4. Fix | Violations found → Fix → Re-validate |
| 5. Store Metrics | After ALL validation passes, call mcp__agent-orchestrator__store-skill-metrics |
Validation principle:
Validation = Evidence gathering + Output confirmation
NOT re-running operations
Validation method:
Review generated code (NOT re-execute)
→ Gather evidence from Write/Edit tool outputs
→ Check verification protocol items
→ Cite specific files/lines for violations
→ Confirm monadic pattern validity
Validation checks:
| Check | Evidence Source |
|---|---|
| Promise wrapping | Generated code shows fromPromise for all promises |
| R → T | Code shows fromResult, not Task.resolve |
| Chaining | Code shows .andThen() for fallible, .map() for pure |
| Unwrapping | ONLY .unwrapOrElse() in inbound files |
| Nesting | No R<M> or T<M> in code |
| Null handling | Code shows M, no null/undefined |
| Unit usage | Code shows R<Unit, E> for no-value ops |
| Task await | Code shows await task, not await task.toPromise() |
| Error recovery | Code shows .orElse(), not safelyTry |
Output format (with evidence):
VALIDATION REPORT:
✓ Promise wrapping: [Evidence] All 3 promises in user.service.ts use fromPromise
✓ Result → Task: [Evidence] Line 45 shows fromResult(validate(x))
✓ Chaining: [Evidence] Lines 50-53 show .andThen() and .map() usage
✓ Task await: [Evidence] Line 78 shows await task, not await task.toPromise()
✗ FAIL: .unwrap() found in service layer
✗ Evidence: user.service.ts:92 contains result.unwrap()
VIOLATIONS (1):
1. user.service.ts:92 - Replace .unwrap() with .unwrapOrElse() or continue chain
Evidence: Line shows "result.unwrap()" instead of monadic operation
ACTION: Fix violations and re-validate
Re-validation required after fixes. Repeat until ALL checks pass.
Metrics storage (Phase 5):
ALL validation checks pass → Store effectiveness metrics
mcp__agent-orchestrator__store-skill-metrics({
sessionId: "[from conversation context]",
skill: "true-myth-recipe",
initialViolations: 1,
iterations: 1,
fixesApplied: 1,
finalViolations: 0,
validationPassed: true,
durationSeconds: 30,
metadata: { files: ["user.service.ts"], patterns: ["fromPromise", "await task"] }
})
✓ Metrics stored → Skill effectiveness tracked