Install
$ agentstack add skill-bensheridanedwards-architectplaybook-typescript-audit ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
/typescript-audit
Audit a TypeScript project's type discipline against an opinionated baseline organised in four layers — compiler configuration, type quality in source, type system usage, type safety at boundaries — preceded by a diagnostic snapshot. Then offer to generate an implementation plan for the gaps.
The default mental model is TypeScript and React. Layers 1, 2, 3 apply to any TypeScript codebase; layer 4's IO-boundary checks lean toward frontends and full-stack applications, but most checks apply equally to backend TypeScript.
How this differs from neighbouring audits
| Concern | Owner | | --- | --- | | Whether tsc --noEmit runs at every lifecycle stage | /quality-gates-audit | | @typescript-eslint plugin coverage and rule selection | /linting-audit | | incremental and project references for build performance | /bundle-build-audit | | useUnknownInCatchVariables as it affects catch typing | /error-handling-audit | | Whether the compiler is configured strictly | /typescript-audit | | Quality of types written in source (any, assertions, @ts-ignore) | /typescript-audit | | Type system usage (discriminated unions, branded types, utility types) | /typescript-audit | | Type safety at IO boundaries (runtime validation of network/form/storage data) | /typescript-audit |
When a single fix passes multiple audits — for example, enabling useUnknownInCatchVariables satisfies both /error-handling-audit (which checks the resulting catch typing) and /typescript-audit (which checks the compiler flag) — every relevant audit surfaces the same gap so the user sees it once and resolves it once.
Static-first design with optional run enrichment
This skill is read-only and never modifies anything. Two modes:
- Static (default). Read
tsconfig.json(and anyextends-chained configs),package.json, and source files. Pattern-detect type-quality signals across.tsand.tsxfiles. - Static plus opt-in
--with-run. Invokenpx tsc --noEmit(no emit, no side effects) and parse the diagnostics. Provides a real error count and the actual locations of every type error, which sharpens layer 2 substantially.
tsc --noEmit is genuinely read-only — it does not write to dist/, .next/, or anywhere else. The skill never invokes the compiler with any other flag, never installs anything, and never edits configuration.
Usage
/typescript-audit # default: concise Top 5 + full report saved + ask about plan
/typescript-audit --worktree # create an isolated Git worktree, then run the audit there
/typescript-audit --learn # mid-level engineer teaching mode (detailed explanations + file/line examples)
/typescript-audit --teach # alias for --learn
/typescript-audit --with-run # static plus enrichment from tsc --noEmit
/typescript-audit --threshold-any-per-file=5 # override default 3
/typescript-audit --threshold-as-per-file=10 # override default 5 (excluding `as const`)
/typescript-audit --threshold-non-null-assertion-per-file=5 # override default 3
/typescript-audit --threshold-conditional-type-depth=4 # override default 3
💡 Pro tip: Add --worktree to run this audit in an isolated Git worktree.
The skill never accepts --apply. The implementation plan is descriptive Markdown.
💡 Pro tip: Run /preflight --audit=typescript first to detect — and optionally install — the development dependency that makes --with-run useful (typescript, which provides tsc --noEmit). Skip if you already know the tooling is wired up.
The opinionated baseline
A check resolves to one of four statuses:
- present — the invariant holds.
- partial — most signals resolve, with a small number of exceptions, or the codebase shows mixed adherence to a soft check.
- missing — a structural prerequisite is absent (no
tsconfig.json, for example — the skill stops earlier in that case, but the status is reserved). - violation — the audit identified concrete configuration or source that breaks the invariant.
Layer 0 is informational only and has no status.
Layer 0 — Diagnostic snapshot (always written, no pass/fail)
- TypeScript version (resolved from the lockfile).
tsconfig.jsonpath(s); whenextendsis used, the resolved final configuration is recorded.- Detected runtime validation library: zod, valibot, arktype, io-ts, yup, joi, superstruct, runtypes — or none.
- Strict-flag adoption: which of
strict,noImplicitAny,strictNullChecks,strictFunctionTypes,strictBindCallApply,strictPropertyInitialization,noImplicitThis,alwaysStrict,useUnknownInCatchVariables,noUncheckedIndexedAccess,exactOptionalPropertyTypes,noImplicitReturns,noFallthroughCasesInSwitch,noImplicitOverrideare enabled. - Type-quality counts across source:
anyannotations,unknownannotations,asassertions (excludingas const),as constassertions, non-null assertions (!),@ts-ignore,@ts-expect-error,@ts-nocheck. - Top 10 files by
anycount, top 10 byascount, top 10 by!count. - Project references count (composite projects in monorepos).
tsc --noEmittotal error count when--with-run.
Layer 1 — Compiler configuration
| Check | Expectation | Violation signal | | --- | --- | --- | | strict: true | The strict family is enabled wholesale via the strict flag. | strict: false, or strict absent and individual strict-family flags missing. | | noUncheckedIndexedAccess enabled | Distinguishes array[i]: T from array[i]: T \| undefined, catching a major source of runtime errors that the compiler otherwise ignores. | Flag missing or false. | | exactOptionalPropertyTypes enabled | Distinguishes prop?: T from prop: T \| undefined. Soft check — reported as partial because some codebases legitimately can't enable it without significant refactoring. | Flag missing or false. | | noImplicitReturns enabled | Catches functions that fall off the end without returning when their type says they should. | Flag missing or false. | | noFallthroughCasesInSwitch enabled | Catches missing break or return in switch cases. | Flag missing or false. | | useUnknownInCatchVariables enabled | Catch variables default to unknown, not any. (Overlap with /error-handling-audit; both surface so a single fix passes both.) | Flag missing or false. | | noImplicitOverride enabled | Class methods that override a parent must use the override keyword. Soft check — reported as partial for codebases with no classes. | Flag missing in a class-using codebase. | | isolatedModules enabled | Required by every modern bundler (Vite, esbuild, swc, Bun) for safe per-file transpilation. | Flag missing or false in a project using one of those bundlers. | | skipLibCheck is a deliberate choice | The flag is either explicitly true (with the awareness that third-party .d.ts files won't be type-checked) or explicitly false. The check verifies the choice was made; it doesn't pick a side. Soft check. | The flag is absent and the default behaviour applies (which differs across TypeScript versions). | | target and module appropriate | target matches the runtime (ES2022 or newer for modern Node and modern browsers). module is ESNext, NodeNext, or Preserve — not CommonJS for browser code. | target: 'ES5' or older in a project that ships modern environments only; or module mismatched against the resolution model. | | moduleResolution set explicitly | moduleResolution is Bundler (Vite, esbuild) or NodeNext (Node), set explicitly rather than defaulting. | Flag absent. | | Composite or project references for monorepos | Monorepos use TypeScript project references (composite projects) so type-checks scale across packages. Skipped silently outside monorepos. Soft check. | Multi-package project without project references. |
Layer 2 — Type quality in source
| Check | Expectation | Violation signal | | --- | --- | --- | | any usage is rare | The total any annotation count per file is at or below the threshold (default 3; tunable via --threshold-any-per-file). The audit counts both scalar any and any[]. | Files exceeding the per-file threshold. | | unknown preferred over any for opaque values | unknown appears in the codebase as the safe alternative to any when typing genuinely opaque shapes. Soft check — reported as partial when no unknown appears at all in a non-trivial codebase. | A codebase with substantial any usage and zero unknown. | | @ts-ignore replaced by @ts-expect-error with description | No @ts-ignore directives remain; all suppressions are @ts-expect-error with a free-text justification on the same or adjacent line. | Any @ts-ignore in source. | | No @ts-nocheck at file level | No file disables type checking entirely. | Any @ts-nocheck at the top of a source file. | | Type assertions (as) are rare | The total as assertion count per file is at or below the threshold (default 5; tunable via --threshold-as-per-file). The audit recognises that as const is not a type assertion in the dangerous sense and excludes it from the count. | Files exceeding the per-file threshold. | | Non-null assertions (!) are rare | The total non-null assertion count per file is at or below the threshold (default 3; tunable via --threshold-non-null-assertion-per-file). | Files exceeding the per-file threshold. | | No Function type | Use specific function signatures, not the unsafe Function type (which permits any callable). | Any Function annotation in source. | | No Object type | Use Record, a specific shape, or object (lowercase). | Any Object annotation in source. | | No primitive wrapper types | Use string, number, boolean — not String, Number, Boolean (the wrapper-object types). | Any wrapper-type annotation in source. | | No empty interfaces | Interfaces declare at least one member, or extend another type. An empty interface either does nothing or is a same-shape alias for what it extends. | interface Foo {} declarations. |
Layer 3 — Type system usage
| Check | Expectation | Violation signal | | --- | --- | --- | | Discriminated unions for state machines | Multi-stage state is modelled as a discriminated union ({ status: 'idle' } \| { status: 'loading' } \| { status: 'success'; data: T } \| { status: 'error'; error: E }), not as multiple booleans or wide objects with optional fields. (Overlap with /react-audit's "status enums over multiple booleans"; both surface.) Soft check — reported as partial. | Multi-state shapes hand-rolled with several optional fields and no discriminant. | | Branded or nominal types for IDs | Identifier types (UserId, OrderId, WorkspaceId) are branded so the compiler distinguishes UserId from OrderId even though both carry string at runtime. Soft check — reported as partial. | Identifier types declared as bare string aliases. | | as const for literal preservation | Configuration objects, route lists, and similar literal values use as const so the compiler infers the narrow literal type rather than widening to string or number. Soft check — reported as partial. | Patterns where as const would tighten inference and is missing. | | Utility types preferred over hand-rolled | Use Pick, Omit, Partial, Required, Readonly, Record, NonNullable, Awaited, ReturnType, Parameters rather than hand-rolling equivalents. | Hand-rolled mapped types that duplicate a built-in utility. | | Generic constraints used | Generic parameters that are operated on with .length, indexing, or property access have extends constraints, not bare T. | Bare T generics whose body operates on the parameter assuming a shape. | | Conditional types used judiciously | Conditional types in source are bounded — no nested-deeper-than-threshold conditional chains in application code. The threshold is tunable via --threshold-conditional-type-depth=N (default 3). The audit isn't strict about this; library code can warrant complexity. Soft check — reported as partial when the depth threshold is exceeded. | Nested conditional types deeper than the threshold. |
Layer 4 — Type safety at boundaries
| Check | Expectation | Violation signal | | --- | --- | --- | | Runtime validation library installed | One of zod, valibot, arktype, io-ts, yup, joi, superstruct, runtypes is in dependencies. Soft check — reported as partial when absent (a project may legitimately have no untyped IO boundaries beyond a fully-typed framework like tRPC end-to-end). | None present in a project that performs network requests, parses JSON, or reads form/URL/storage data. | | Network response data validated | Calls to fetch, axios, framework data primitives have their JSON parsed through a validator (zod schema, etc.) before being typed. The audit recognises framework-level type-safe layers (tRPC, GraphQL Code Generator outputs) as equivalent. | Network responses cast directly with as or implicitly typed without runtime validation. | | JSON.parse results validated | JSON.parse(...) outputs are validated, not typed by direct assertion. | JSON.parse(...) as MyType patterns. | | Form data validated | Forms validate input with the form library's schema integration (react-hook-form + resolver, formik + Yup, equivalent) or a standalone validator. (Overlap with /react-audit's form-library check; both surface.) | Form data passed straight to a typed handler with no validator. | | URL parameters validated | Router-extracted params (useSearchParams, useParams, router.query) are validated before being typed as anything stricter than string or string[]. | URL parameters cast to specific types or assumed narrower than their actual runtime type. | | localStorage/sessionStorage reads validated | Storage reads are parsed and validated before being typed. | JSON.parse(localStorage.getItem('key')!) patterns with no validator. | | Environment variables validated | Environment-variable access (process.env, import.meta.env) goes through a validated configuration module — not direct reads scattered across the codebase. Soft check — reported as partial. | Direct process.env.X reads in feature code rather than a validated config module. |
What this skill does
- Reads the knowledge graph when present. Soft dependency: when
graphify-out/graph.jsonexists, the audit cross-references type-quality offenders (high-any, high-as, high-!files) against graph centrality and prioritises god nodes in the implementation plan. The audit still runs in full when the graph is absent. - Confirms a TypeScript project. Detects
package.json,tsconfig.json, andtypescriptin dependencies. If any are absent, the skill stops and tells the user it currently supports TypeScript projects only. - Resolves the effective
tsconfig.jsonby following anyextendschain. Records the final resolved configuration in metadata. - Detects the runtime validation library (zod, valibot, arktype, io-ts, yup, joi, superstruct, runtypes) for the diagnostic snapshot and for the layer 4 checks.
- When
--with-runis set, invokesnpx tsc --noEmitand parses the diagnostics. Iftscfails to start (not installed in the project), records the failure, prints to the chat and prepends tofindings.md: "--with-runwas requested buttypescriptis not installed. Run/preflight --audit=typescript --installto install it, then re-run this audit. The static analysis has been completed; only the run-dependent enrichment degraded topartial." RecordsrecoveryHint: "/preflight --audit=typescript --install"on each run-dependent check that degraded infindings.json. Continues w
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: BenSheridanEdwards
- Source: BenSheridanEdwards/ArchitectPlaybook
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.