
Static Code Analysis
- 559 installs
- 305 repo stars
- Updated March 4, 2026
- aj-geddes/useful-ai-prompts
static-code-analysis is a Claude Code skill that implements linters, formatters, and security scanners to catch bugs and style violations for developers who need automated analysis before merging or deploying code.
About
static-code-analysis is a skill in aj-geddes/useful-ai-prompts for setting up automated code analysis without execution. It integrates linters, formatters, and security scanners to detect bugs, vulnerabilities, and style violations early in the development cycle. Developers invoke it when enforcing team coding standards, hardening CI pipelines, or augmenting manual code review with repeatable checks. The skill guides agent sessions through tool selection, configuration, and pipeline wiring so analysis runs on every pull request or pre-deploy step. Reach for static-code-analysis when prompts mention ESLint, Prettier, Semgrep, SonarQube, or automated review gates.
- Catches bugs and security vulnerabilities without running code
- Enforces consistent coding standards across projects
- Automates code review in CI/CD pipelines and pre-commit hooks
- Supports ESLint, TypeScript, security plugins and import ordering
- Provides actionable warnings and errors for refactoring
Static Code Analysis by the numbers
- 559 all-time installs (skills.sh)
- Ranked #226 of 1,352 Code Review & Quality skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/aj-geddes/useful-ai-prompts --skill static-code-analysisAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 559 |
|---|---|
| repo stars | ★ 305 |
| Last updated | March 4, 2026 |
| Repository | aj-geddes/useful-ai-prompts ↗ |
How do you set up static analysis in CI pipelines?
Automatically catch bugs, security issues, and style violations before merging or deploying code.
Who is it for?
Developers adding automated linting, formatting, and security scanning gates to CI before merging or deploying application code.
Skip if: Teams needing runtime profiling, load testing, or dynamic penetration testing that requires executing the application in production-like environments.
When should I use this skill?
User asks to set up linters, formatters, security scanners, ESLint, Prettier, or automated code review checks in CI.
What you get
Linter and formatter configs, security scanner integration, and CI pipeline checks that block bugs and style violations pre-merge.
- Linter and formatter configuration files
- CI pipeline static analysis job
By the numbers
- Covers three static analysis tool classes: linters, formatters, and security scanners
Files
Static Code Analysis
Table of Contents
Overview
Use automated tools to analyze code without executing it, catching bugs, security issues, and style violations early.
When to Use
- Enforcing coding standards
- Security vulnerability detection
- Bug prevention
- Code review automation
- CI/CD pipelines
- Pre-commit hooks
- Refactoring assistance
Quick Start
Minimal working example:
// .eslintrc.js
module.exports = {
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:security/recommended",
],
plugins: ["@typescript-eslint", "security", "import"],
rules: {
"no-console": ["warn", { allow: ["error", "warn"] }],
"no-unused-vars": "error",
"prefer-const": "error",
eqeqeq: ["error", "always"],
"no-eval": "error",
"security/detect-object-injection": "warn",
"security/detect-non-literal-regexp": "warn",
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/explicit-function-return-type": "error",
"import/order": [
"error",
{
groups: [
"builtin",
"external",
"internal",
// ... (see reference guides for full implementation)Reference Guides
Detailed implementations in the references/ directory:
| Guide | Contents |
|---|---|
| ESLint Configuration | ESLint Configuration |
| Python Linting (pylint + mypy) | Python Linting (pylint + mypy) |
| Pre-commit Hooks | Pre-commit Hooks |
| SonarQube Integration | SonarQube Integration |
| Custom AST Analysis | Custom AST Analysis |
| Security Scanning | Security Scanning |
Best Practices
✅ DO
- Run linters in CI/CD
- Use pre-commit hooks
- Configure IDE integration
- Fix issues incrementally
- Document custom rules
- Share configuration across team
- Automate security scanning
❌ DON'T
- Ignore all warnings
- Skip linter setup
- Commit lint violations
- Use overly strict rules initially
- Skip security scans
- Disable rules without reason
Custom AST Analysis
Custom AST Analysis
import * as ts from "typescript";
import * as fs from "fs";
interface Issue {
file: string;
line: number;
column: number;
message: string;
severity: "error" | "warning" | "info";
rule: string;
}
class CustomLinter {
private issues: Issue[] = [];
lintFile(filePath: string): Issue[] {
this.issues = [];
const sourceCode = fs.readFileSync(filePath, "utf-8");
const sourceFile = ts.createSourceFile(
filePath,
sourceCode,
ts.ScriptTarget.Latest,
true,
);
this.visit(sourceFile, filePath);
return this.issues;
}
private visit(node: ts.Node, filePath: string): void {
// Check for console.log
if (
ts.isCallExpression(node) &&
ts.isPropertyAccessExpression(node.expression) &&
node.expression.expression.getText() === "console" &&
node.expression.name.getText() === "log"
) {
const { line, character } = ts.getLineAndCharacterOfPosition(
node.getSourceFile(),
node.getStart(),
);
this.issues.push({
file: filePath,
line: line + 1,
column: character + 1,
message: "Unexpected console.log statement",
severity: "warning",
rule: "no-console",
});
}
// Check for any type
if (ts.isTypeReferenceNode(node) && node.typeName.getText() === "any") {
const { line, character } = ts.getLineAndCharacterOfPosition(
node.getSourceFile(),
node.getStart(),
);
this.issues.push({
file: filePath,
line: line + 1,
column: character + 1,
message: "Avoid using any type",
severity: "warning",
rule: "no-any",
});
}
// Check for long functions
if (ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node)) {
const body = node.body;
if (body && body.getFullText().split("\n").length > 50) {
const { line, character } = ts.getLineAndCharacterOfPosition(
node.getSourceFile(),
node.getStart(),
);
this.issues.push({
file: filePath,
line: line + 1,
column: character + 1,
message: "Function is too long (>50 lines)",
severity: "warning",
rule: "max-lines-per-function",
});
}
}
ts.forEachChild(node, (child) => this.visit(child, filePath));
}
formatIssues(issues: Issue[]): string {
if (issues.length === 0) {
return "No issues found.";
}
return issues
.map(
(issue) =>
`${issue.file}:${issue.line}:${issue.column} - ${issue.severity}: ${issue.message} (${issue.rule})`,
)
.join("\n");
}
}
// Usage
const linter = new CustomLinter();
const issues = linter.lintFile("./src/example.ts");
console.log(linter.formatIssues(issues));ESLint Configuration
ESLint Configuration
// .eslintrc.js
module.exports = {
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:security/recommended",
],
plugins: ["@typescript-eslint", "security", "import"],
rules: {
"no-console": ["warn", { allow: ["error", "warn"] }],
"no-unused-vars": "error",
"prefer-const": "error",
eqeqeq: ["error", "always"],
"no-eval": "error",
"security/detect-object-injection": "warn",
"security/detect-non-literal-regexp": "warn",
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/explicit-function-return-type": "error",
"import/order": [
"error",
{
groups: [
"builtin",
"external",
"internal",
"parent",
"sibling",
"index",
],
"newlines-between": "always",
},
],
},
};Pre-commit Hooks
Pre-commit Hooks
# .pre-commit-config.yaml
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-json
- id: check-merge-conflict
- id: detect-private-key
- repo: https://github.com/pre-commit/mirrors-eslint
rev: v8.50.0
hooks:
- id: eslint
files: \.[jt]sx?$
types: [file]
- repo: https://github.com/psf/black
rev: 23.9.1
hooks:
- id: black
- repo: https://github.com/PyCQA/pylint
rev: v3.0.0
hooks:
- id: pylint
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.5.1
hooks:
- id: mypy
additional_dependencies: [types-requests]
- repo: https://github.com/trufflesecurity/trufflehog
rev: v3.58.0
hooks:
- id: trufflehog
entry: trufflehog filesystem --directory .Python Linting (pylint + mypy)
Python Linting (pylint + mypy)
# .pylintrc
[MASTER]
ignore=venv,.git,__pycache__
jobs=4
[MESSAGES CONTROL]
disable=
missing-docstring,
too-few-public-methods
[FORMAT]
max-line-length=100
max-module-lines=1000
[DESIGN]
max-args=5
max-locals=15
max-returns=6
max-branches=12
max-statements=50# mypy.ini
[mypy]
python_version = 3.10
warn_return_any = True
warn_unused_configs = True
disallow_untyped_defs = True
disallow_incomplete_defs = True
check_untyped_defs = True
disallow_untyped_calls = True
warn_redundant_casts = True
warn_unused_ignores = True
strict_equality = TrueSecurity Scanning
Security Scanning
import { exec } from "child_process";
import { promisify } from "util";
const execAsync = promisify(exec);
interface SecurityIssue {
severity: "critical" | "high" | "medium" | "low";
title: string;
description: string;
file?: string;
line?: number;
remediation?: string;
}
class SecurityScanner {
async scanDependencies(): Promise<SecurityIssue[]> {
try {
const { stdout } = await execAsync("npm audit --json");
const auditResult = JSON.parse(stdout);
const issues: SecurityIssue[] = [];
for (const [name, advisory] of Object.entries(
auditResult.vulnerabilities || {},
)) {
const vuln = advisory as any;
issues.push({
severity: vuln.severity,
title: vuln.via[0]?.title || name,
description: vuln.via[0]?.url || "",
remediation: `Update ${name} to ${vuln.fixAvailable || "latest"}`,
});
}
return issues;
} catch (error) {
console.error("Dependency scan failed:", error);
return [];
}
}
async scanSecrets(directory: string): Promise<SecurityIssue[]> {
const issues: SecurityIssue[] = [];
// Simple regex-based secret detection
const patterns = [
{
name: "API Key",
pattern: /api[_-]?key['"]?\s*[:=]\s*['"]([a-zA-Z0-9]{32,})['"]/,
},
{ name: "AWS Key", pattern: /(AKIA[0-9A-Z]{16})/ },
{
name: "Private Key",
pattern: /-----BEGIN (RSA |EC )?PRIVATE KEY-----/,
},
{
name: "Password",
pattern: /password['"]?\s*[:=]\s*['"]((?!<%= ).{8,})['"]/,
},
];
// Scan files
const files = this.getFiles(directory);
for (const file of files) {
const content = fs.readFileSync(file, "utf-8");
const lines = content.split("\n");
for (let i = 0; i < lines.length; i++) {
for (const { name, pattern } of patterns) {
if (pattern.test(lines[i])) {
issues.push({
severity: "critical",
title: `Potential ${name} detected`,
description: `Found in ${file}:${i + 1}`,
file,
line: i + 1,
remediation: "Remove secret and use environment variables",
});
}
}
}
}
return issues;
}
private getFiles(dir: string): string[] {
// Implementation to recursively get files
return [];
}
generateReport(issues: SecurityIssue[]): string {
let report = "# Security Scan Report\n\n";
const grouped = issues.reduce(
(acc, issue) => {
acc[issue.severity] = acc[issue.severity] || [];
acc[issue.severity].push(issue);
return acc;
},
{} as Record<string, SecurityIssue[]>,
);
for (const [severity, items] of Object.entries(grouped)) {
report += `## ${severity.toUpperCase()} (${items.length})\n\n`;
for (const issue of items) {
report += `### ${issue.title}\n`;
report += `${issue.description}\n`;
if (issue.remediation) {
report += `**Remediation:** ${issue.remediation}\n`;
}
report += "\n";
}
}
return report;
}
}
// Usage
const scanner = new SecurityScanner();
const depIssues = await scanner.scanDependencies();
const secretIssues = await scanner.scanSecrets("./src");
const allIssues = [...depIssues, ...secretIssues];
console.log(scanner.generateReport(allIssues));SonarQube Integration
SonarQube Integration
# sonar-project.properties
sonar.projectKey=my-project
sonar.projectName=My Project
sonar.projectVersion=1.0
sonar.sources=src
sonar.tests=tests
sonar.exclusions=**/node_modules/**,**/*.test.ts
sonar.typescript.lcov.reportPaths=coverage/lcov.info
sonar.qualitygate.wait=true
# Quality gates
sonar.coverage.exclusions=**/*.test.ts# .github/workflows/sonar.yml
name: SonarQube Analysis
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
sonar:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: SonarQube Scan
uses: sonarsource/sonarqube-scan-action@master
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
- name: Quality Gate Check
uses: sonarsource/sonarqube-quality-gate-action@master
timeout-minutes: 5
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}#!/bin/bash
# security-checklist.sh - Generate a security review checklist
# Usage: ./security-checklist.sh [--output checklist.md]
set -euo pipefail
OUTPUT="${{1:-/dev/stdout}}"
cat > "$OUTPUT" << 'CHECKLIST'
# Security Review Checklist
## Authentication & Authorization
- [ ] All endpoints require authentication
- [ ] Role-based access control implemented
- [ ] Session management is secure
## Input Validation
- [ ] All user inputs are validated
- [ ] SQL injection prevention
- [ ] XSS prevention
## Data Protection
- [ ] Sensitive data encrypted at rest
- [ ] Sensitive data encrypted in transit
- [ ] PII handling compliant
## TODO: Add domain-specific security checks
CHECKLIST
echo "Checklist generated: $OUTPUT" >&2
Related skills
How it compares
Choose static-code-analysis over runtime debugging skills when the goal is pre-merge linting, formatting, and vulnerability scanning without executing code.
FAQ
What does static-code-analysis check without running code?
static-code-analysis configures linters, formatters, and security scanners that analyze source statically. The skill catches bugs, vulnerabilities, and style violations before merge or deployment without executing the application.
When should static-code-analysis run in a workflow?
static-code-analysis fits pre-merge CI gates and pre-deploy checks. Invoke it when enforcing coding standards, automating review, or detecting security issues before shipping changes to production.
Can static-code-analysis replace manual code review?
static-code-analysis augments manual review with repeatable linter, formatter, and scanner checks. It standardizes enforcement across pull requests but does not replace human judgment on architecture or business logic.