Install
$ agentstack add skill-raintree-technology-agent-starter-cleanup-weak-types ✓ 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 No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ 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.
About
Replace weak escape-hatch types with strong types inferred from actual usage. Per-occurrence verification — each replacement is typechecked individually, reverted if it breaks. Conservative on public APIs.
Preflight
- Language detect: TS/JS (
any,unknown,as unknown as,Function,Object), Python (Any, missing type hints), Go (interface{},anysince 1.18), Rust (Boxis rare; mostly look forBoxwhere a concrete type would do). - Git state: refuse on dirty tree.
- Report dir: ensure exists.
- Read project conventions: check for a
check:weak-types(or similar) script in package.json. Checktsconfig.jsonforstrict/noImplicitAnyflags. Checkmypy.ini/pyproject.toml [tool.mypy]for strictness. - Read allow-list: many projects allow weak types in specific files (e.g.,
*.test.ts, generated code, third-party shim files). Find and respect them.
Detect
TypeScript / JavaScript
# Explicit `any`
grep -rn --include="*.ts" --include="*.tsx" -E "\b(: any\b||as any\b|as unknown as)" \
--exclude-dir=node_modules --exclude-dir=dist --exclude-dir=.next . > /tmp/ts-weak.txt
# Compiler-derived implicit-any (more accurate than grep)
npx tsc --noImplicitAny --noEmit 2>&1 | grep "implicitly has an 'any' type" > /tmp/ts-implicit-any.txt
For each occurrence, capture the surrounding context (function signature, callers).
Python
# Explicit Any imports + usage
grep -rn --include="*.py" -E "(from typing import.*Any|: Any\b|-> Any\b)" . > /tmp/py-any.txt
# Mypy strict mode finds untyped functions
mypy --disallow-untyped-defs --no-incremental . > /tmp/py-untyped.txt 2>&1 || true
Go
grep -rn --include="*.go" -E "\binterface\{\}|\bany\b" . > /tmp/go-any.txt
Rust
grep -rn --include="*.rs" -E "(Box /tmp/rust-dyn.txt
Assess
Write .claude/cleanup-reports/cleanup-weak-types-{YYYY-MM-DD}.md:
# Weak Types Assessment — YYYY-MM-DD
## Summary
- Total weak-type sites: N
- HIGH (safe to auto-fix): X
- MEDIUM (public API or cross-package): Y
- LOW (justified — e.g., genuine unknown JSON, third-party): Z
## Findings
### HIGH — `apps/app/lib/parse.ts:45` `function process(data: any)`
- Inferable type: `data` is always called with `{ id: string; events: Event[] }` (3 callers checked).
- Replacement: `function process(data: { id: string; events: Event[] })`.
- Even better: lift to a named type `ProcessInput`.
### MEDIUM — `packages/sdk/src/client.ts:12` `function send(payload: any): Promise`
- Public API of an SDK package — changing the type is a breaking change.
- Recommendation: introduce a generic `` and have callers specify, OR use `unknown` and require validation.
### LOW — `lib/json.ts:8` `function parseJson(s: string): unknown`
- Genuinely unknown — JSON.parse output. Keep as `unknown`, ensure callers narrow.
## Critical Assessment
[2-3 paragraphs: where are weak types concentrated? Boundary code (HTTP handlers, JSON parsing) often justifies them. Internal logic almost never does.]
Apply
Auto-fix HIGH only, ONE AT A TIME with typecheck between each. This is essential — bulk type changes can cascade in hard-to-predict ways.
Confidence rubric
HIGH (auto-apply, individually):
- The weak type is in a private/internal function.
- All callers are in the same repo and pass the same type (or a small finite set easily expressed as a union).
- Replacement is mechanically derivable from usage.
- No re-export of the symbol from a package boundary.
MEDIUM (report only):
- Public API surface (exported from a package, used by a
.d.ts, part of an SDK). - Generic-amenable signatures (suggest the generic but don't apply).
- Discriminated union opportunities — the human picks the discriminator field.
as unknown ascasts — these usually indicate a deeper type design problem.
LOW (note, no action):
- Boundary code receiving genuine unknown input (HTTP body before validation,
JSON.parse, dynamic config). - Third-party shim files where the actual library is untyped.
- Test files (allowed in most weak-type allow-lists).
Execution (HIGH, individually)
For EACH HIGH finding:
- Capture the exact
git diffof the proposed change. - Apply the change (Edit).
- Run scoped typecheck:
bun run typecheckortsc --noEmit. For Python:mypy. - If typecheck fails OR introduces new errors elsewhere:
git checkout --, downgrade this finding to MEDIUM in the report, continue. - If typecheck passes, move on.
After all HIGH findings processed, single commit: chore(cleanup): cleanup-weak-types — strengthened N type signatures.
Verify
# Full typecheck across the repo (not just changed files)
bun run typecheck 2>&1
mypy --strict . 2>&1 || mypy . 2>&1
go build ./... 2>&1
cargo check 2>&1
# Tests — important here, since type changes can affect runtime via narrowing
bun test 2>&1
pytest 2>&1
go test ./... 2>&1
cargo test 2>&1
# Project-specific weak-types gate
bun run check:weak-types 2>/dev/null || true # project-specific script, if defined
If anything fails after the per-file pass somehow (rare but possible across-file inference): revert all and downgrade. Should rarely happen because we typecheck after each.
Output
- "Strengthened N weak types. M deferred for review."
- Report path with breakdown of HIGH/MEDIUM/LOW.
- Verify status.
NEVER
- Bulk replace
anywithunknown— that's a different defect, not a fix. Both are weak;unknownjust forces narrowing. - Replace
anywith an over-narrow type that breaks one of N callers — verify ALL callers fit. - Touch generated types (Drizzle
$inferSelect, OpenAPI codegen, Prisma) — fix the codegen config instead. - Add
// @ts-ignoreor# type: ignoreto make the change pass — that's hiding the problem. - Modify ambient
.d.tsdeclarations for third-party libraries. - Remove an
as unknown ascast without understanding why it was added — it's often masking a type incompatibility worth investigating, not silently fixing. - Auto-add generics to public APIs — that's a contract change requiring human design.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: raintree-technology
- Source: raintree-technology/agent-starter
- License: MIT
- Homepage: https://claude.raintree.technology
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.