plugins/dev/skills/fix-typescript/SKILL.md
Fix TypeScript errors with strict anti-reward-hacking rules. **ALWAYS use when** the user says 'fix type errors', 'fix typescript', 'type-check is failing', or when TypeScript compilation errors need to be resolved. Ensures runtime type safety — fixes root causes instead of silencing errors with casts.
npx skillsauth add coalesce-labs/catalyst fix-typescriptInstall 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.
You are fixing TypeScript type errors. This command embeds strict rules to prevent "reward hacking" - patterns that make linters pass without actually fixing type safety issues.
Your goal is RUNTIME TYPE SAFETY, not just passing linters.
If your fix would make the linter happy but could still crash at runtime, it's wrong.
These patterns are EXPLICITLY FORBIDDEN. Using them will require rework:
| Pattern | Why Forbidden | What To Do Instead |
| ----------------------------------------- | ---------------------------------------- | ------------------------------------- |
| as unknown as Type (undocumented) | Erases all type info, defeats TypeScript | Fix the source to return correct type |
| as any | Disables type checking entirely | Use proper typing or Zod validation |
| void (0 as unknown as Type) | Tricks linter into thinking type is used | Delete the unused type |
| const _var = ... (local variable) | Suppresses unused warning | Delete the unused variable |
| export type Foo (when unused elsewhere) | Suppresses unused warning | Remove export or delete the type |
| // @ts-ignore or // @ts-expect-error | Hides real type problems | Fix the actual type error |
| Commenting out code | Dead code clutters codebase | Delete it (git has history) |
| Excluding files from tsconfig | Hides errors in those files | Include files, fix errors |
Fix the SOURCE to return the correct type:
// WRONG - Casting a query result
const users = (await db.from("users").select("*")) as unknown as User[];
// CORRECT - Use the query builder's generic typing
const { data: users } = await db.from("users").select("*").returns<User[]>();
// WRONG - Casting a service result
const account = (await accountService.findById(id)) as Account;
// CORRECT - Fix the service to return the right type
const account = await accountService.findById(id); // Returns Account | null
if (account === null) throw new NotFoundError();
Validate with Zod at the boundary:
// WRONG - Trust external data
const webhook = req.body as WebhookPayload;
// CORRECT - Validate at boundary
const webhook = WebhookPayloadSchema.parse(req.body);
Type assertions are ONLY acceptable for third-party library limitations with full documentation:
// ACCEPTABLE - Third-party library type gap with documentation
// LIBRARY TYPE LIMITATION: The thirdPartyWrapper() function returns a type
// that TypeScript can't verify implements the expected interface.
// Verified at runtime that the object has the required methods.
// TODO: Remove when library updates types (tracked in TICKET-XXX)
const wrapped = thirdPartyResult as unknown as ExpectedInterface;
Requirements for acceptable assertions:
/scan-reward-hacking before marking completeFirst, check package.json for a type-check script. Then detect the package manager by looking for lock files:
bun.lockb -> bun run type-checkpnpm-lock.yaml -> pnpm type-checkyarn.lock -> yarn type-checkpackage-lock.json -> npm run type-checkIf no lock file is found, fall back to npx tsc --noEmit.
You MUST run these checks:
# Type check must pass (use the detected package manager)
# e.g., npm run type-check / yarn type-check / bun run type-check / pnpm type-check
# Scan for forbidden patterns (run /scan-reward-hacking command)
grep -r "as unknown as" [files-you-changed]
grep -r "void (0" [files-you-changed]
grep -r "as any" [files-you-changed]
If ANY forbidden pattern exists in your changes without proper documentation, your work is not complete.
If you need as, ask "Why doesn't the type already match?" and fix THAT.
Type assertions are a code smell. They mean either:
Never use assertions to silence errors - fix the underlying type mismatch.
development
Migrate a single-harness repo to the dual-harness layout so both Claude Code and Codex load the same instructions and skills — AGENTS.md as the portable canonical doc, a thin CLAUDE.md `@AGENTS.md` bridge, and a `.agents/skills` dir with a `.claude/skills` symlink onto it. Use when asked to migrate to dual-harness, make this repo work in both Claude and Codex, or for agent metadata cleanup.
tools
Goal-driven senior-engineer pipeline-unstick sweep (CTL-1176 rung 3). Given the stuck/failed/needs-human set (or ONE ticket handed by the recovery router), its GOAL is to get the pipeline MOVING again — not to fix one ticket's review findings (that is phase-remediate). It runs AFTER the eyes (diagnostician evidence) and the hands (deterministic unstuck-sweep seams) have already tried, and it CONSUMES their output from a recovery-pass.json brief rather than re-diagnosing or redoing their narrow work. It acts like a senior engineer with full tool access — it resolves merge conflicts, rebases, force-pushes, merges green PRs, and re-dispatches stalled phases AUTONOMOUSLY — and escalates to the operator ONLY for a genuine value judgment / something that degrades other functionality / a real cost-benefit trade-off / a serious architecture change / an ADR conflict. On escalation it AUTHORS the operator inbox row + the push notification (executive-voiced). Dispatched as a `claude --bg` job by phase-agent-dispatch via slash command, AND invocable bare by the operator as a sweep — hence `user-invocable: true`. Ships behind CATALYST_RECOVERY_PASS (off by default — no live behavior change until shadow/enforce).
tools
Diagnose and fix Catalyst setup issues. Validates tools, database, config, OTel, direnv, and thoughts. Automatically fixes what it can — creates directories, initializes the database, sets WAL mode, runs migrations. Use for new installs, upgrades, or when something isn't working.
tools
--- name: phase-triage description: Phase agent that triages a Linear ticket — expands acronyms, classifies (feature/bug/docs/refactor/chore), identifies genuine blockers (a semantic second-pass over the backlog — NOT a prose scrape; CTL-838), estimates scope, writes triage.json, and posts a triage analysis comment to Linear. Triage completion is signaled by that comment plus the local triage.json — there is no `triaged` label. Emits phase.triage.complete.<TICKET> on success and phase.triage.fai