Skip to content

Independent-AI-Labs/AMI-CI

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

32 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AMI-CI

Universal code quality enforcement for monorepos and standalone projects.

Two layers:

  1. Shell layer (lib/): bash + grep + awk + git. Zero runtime dependencies. Runs everywhere.
  2. Python layer (ami/ci/): HTTP API queries (PyPI, npm, Docker Hub) and AST analysis. Requires Python 3.11+ via uv run.

The shell layer does 90% of the work. Python exists only where shell can't: HTTP APIs and AST parsing.

The Problem

AI coding agents generate code fast, but they also generate garbage fast. They suppress linters, sneak in type-ignore comments, create stub implementations, commit .env files, attribute themselves as co-authors, produce 800-line god-files, stuff logic into __init__.py, and leave behind default-path wrappers that nobody asked for.

AMI-CI catches all of that before it hits the repository.

Architecture

AMI-CI/
├── lib/                                 # Shell layer (zero dependencies)
│   ├── ci.sh                            # Core: colors, file listing, YAML reader
│   ├── checks.sh                        # All check functions (sources ci.sh)
│   ├── parse_banned_words.awk           # YAML → records parser
│   ├── parse_exceptions.awk             # Exception YAML parser (universal + project)
│   └── parse_precommit_config.awk       # .pre-commit-config.yaml → hook records
├── ami/ci/                              # Python layer (HTTP + AST)
│   ├── types.py                         # NamedTuple definitions
│   ├── check_dependency_versions.py     # PyPI/npm/Docker version checker
│   ├── check_dead_code.py              # Dead code detection CLI
│   ├── dead_code_analyzer.py           # AST analysis engine
│   └── _docker_versions.py             # Docker Hub API queries
├── scripts/                             # Executable tools
│   ├── generate-hooks                   # .pre-commit-config.yaml → native git hooks
│   ├── audit-workspace                  # Discover repos, audit AMI-CI integration
│   ├── cleanup-precommit               # Remove pre-commit framework traces
│   └── rewrite-history                  # Strip blocked patterns from git history
├── config/                              # YAML rule files (see below)
├── hooks/                               # Generated git hook templates (output of generate-hooks)
├── tests/                               # Shell + Python tests
│   ├── run_tests.sh                     # Shell test runner
│   ├── test_helpers.sh                  # Shell test framework
│   ├── test_core.sh                     # ci.sh function tests
│   ├── test_checks.sh                   # checks.sh function tests
│   ├── test_blocked_patterns.sh         # Blocked commit pattern tests
│   ├── test_rewrite_history.sh          # History rewriting tests
│   ├── test_check_dependency_versions.py    # PyPI version checker tests
│   ├── test_check_npm_dependency_versions.py # npm version checker tests
│   ├── test_docker_versions.py          # Docker version checker tests
│   ├── test_dead_code_analyzer.py       # AST analysis engine tests
│   ├── test_dead_code_analyzer_detection.py # Detection edge case tests
│   ├── test_check_dead_code.py          # Dead code CLI tests
│   ├── test_check_dead_code_cli.py      # CLI output formatting tests
│   └── test_dependency_checker_integration.py # End-to-end dep checker tests
├── docs/
│   ├── SPEC.md                          # Full technical specification
│   └── HOOKS.md                         # Hook generation and integration guide
├── pyproject.toml                       # Python package definition
└── .pre-commit-config.yaml              # Hook config (consumed by generate-hooks)

What It Enforces

Shell Checks (pre-commit stage)

Function What Config
ci_check_unstaged Blocks commits with unstaged changes. Auto-stages and fails. None
ci_check_banned_words 50+ patterns: linter suppression, loose typing, default-path code, reflection abuse, bad data models, hardcoded paths, container hygiene, UUID correctness banned_words.yaml
ci_block_sensitive_files Blocks files likely containing secrets (14 extensions, 14 keywords, 22 safe exceptions) sensitive_files.yaml
ci_check_file_length Enforces max 512 lines per source file file_length_limits.yaml
ci_check_init_files Enforces completely empty __init__.py files None

Shell Checks (commit-msg stage)

Function What Config
ci_check_commit_message Enforces conventional commit format (type: description) None
ci_block_coauthored Blocks Co-authored-by and noreply@anthropic.com in commit messages blocked_commit_patterns.yaml

Shell Checks (pre-push stage)

Function What Config
ci_block_coauthored_history Scans full commit history for blocked patterns (catches rebased/amended commits) blocked_commit_patterns.yaml
ci_verify_coverage Runs test suites with minimum coverage thresholds coverage_thresholds.yaml

Python Checks

Module What Why not shell
check_dependency_versions.py Strict pinning + latest version enforcement across PyPI, npm, Docker Hub HTTP API queries, TOML parsing, auto-upgrade
check_dead_code.py + dead_code_analyzer.py AST-based dead code detection with cross-module reference graph Python AST requires Python runtime
_docker_versions.py Docker Hub registry queries, Dockerfile/compose image tag parsing HTTP API queries

Hook Generation

The pre-commit Python framework is not used. Instead, scripts/generate-hooks reads .pre-commit-config.yaml and generates native bash scripts in .git/hooks/.

# Install hooks (reads .pre-commit-config.yaml, writes .git/hooks/*)
scripts/generate-hooks

# Or via Makefile
make install-hooks

Key differences from pre-commit:

  • No stashing. If unstaged changes exist, the hook auto-stages and fails. Files never disappear from disk.
  • No Python runtime for the hook framework itself. Just bash.
  • No remote code execution. All hooks are local language: system commands.
  • Formatters are idempotent. After ruff format/lint, if files changed, the hook auto-stages and fails. You re-run commit.

Scripts

Script Purpose
generate-hooks Parse .pre-commit-config.yaml → generate native .git/hooks/{pre-commit,pre-push,commit-msg}
audit-workspace Discover all git repos in workspace, report AMI-CI integration level
cleanup-precommit Remove pre-commit Python package, cache, and generated hooks
rewrite-history Strip blocked patterns (Co-authored-by, etc.) from commit history with backup

Configuration

All rules live in config/. Nothing is hardcoded in shell.

File Purpose
banned_words.yaml 50+ content patterns, filename rules, directory rules, universal exceptions
banned_words_exceptions.yaml Universal path-based exceptions (tests/, .toml)
blocked_commit_patterns.yaml Patterns blocked in commit messages and push history
sensitive_files.yaml Sensitive extensions, keywords, safe exceptions, safe prefixes
file_length_limits.yaml Max lines (default 512), enforced extensions, ignore list
coverage_thresholds.yaml Per-suite test paths, minimum coverage, runner config
dead_code.yaml AST scan paths, entry points, ignore patterns
dependency_excludes.yaml Packages excluded from version checking

Per-project overrides

Projects can add exception files in their own config/ directory or repo root:

File Purpose
banned_words_exceptions.yaml Exempt specific patterns in specific paths
sensitive_files_exceptions.yaml Additional safe files
dependency_excludes_exceptions.yaml Additional excluded packages
coverage_thresholds.yaml Project-specific test suites and coverage minimums

Tests

# Shell tests (core functions, checks, blocked patterns, history rewriting)
./tests/run_tests.sh

# Python tests (dependency checker, dead code analyzer)
uv run pytest tests/ -v
File Tests What
test_core.sh Shell ci.sh functions: YAML reader, file listing, filter
test_checks.sh Shell checks.sh: banned words, commit message, dead code, deps
test_blocked_patterns.sh Shell Blocked commit pattern detection
test_rewrite_history.sh Shell History rewriting with backup and verification
test_check_dependency_versions.py Python PyPI version queries, pinning validation
test_check_npm_dependency_versions.py Python npm exact semver checking
test_docker_versions.py Python Docker image tag scanning
test_dead_code_analyzer.py Python AST visitor, cross-reference graph
test_dead_code_analyzer_detection.py Python Detection edge cases
test_check_dead_code.py Python CLI interface, config loading
test_check_dead_code_cli.py Python CLI output formatting, dry-run
test_dependency_checker_integration.py Python End-to-end dependency checking

Known discrepancy

The Python test files test Python code (ami/ci/*.py), not shell code. This is correct -- the Python layer requires Python to test. The shell layer is tested by shell tests (test_core.sh, test_checks.sh, test_blocked_patterns.sh, test_rewrite_history.sh). There is no cross-layer testing (Python testing shell or vice versa).

Design Decisions

  • Shell first: if it can be done with grep/awk/git, it stays in shell. Python exists only for HTTP APIs and AST parsing.
  • Source-and-call: no subprocesses. source checks.sh, call functions. Fast.
  • \034 delimiter: AWK parsers use ASCII File Separator instead of tab because IFS=$'\t' with read collapses consecutive tabs (losing empty fields in parsed YAML).
  • No pre-commit framework: generate-hooks reads the same .pre-commit-config.yaml format but produces native git hooks. The pre-commit Python package is never installed or executed.
  • Conservative dead code detection: the AST analyzer avoids false positives by skipping private names, dunder methods, and dynamically referenced names. It reports only high-confidence dead code.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors