RepoPilotOpen in app →

carthage-software/mago

Mago is a toolchain for PHP that aims to provide a set of tools to help developers write better code.

Healthy

Healthy across all four use cases

weakest axis
Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 1d ago
  • 6 active contributors
  • Apache-2.0 licensed
Show all 6 evidence items →
  • CI configured
  • Tests present
  • Single-maintainer risk — top contributor 83% of recent commits

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Healthy" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/carthage-software/mago)](https://repopilot.app/r/carthage-software/mago)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/carthage-software/mago on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: carthage-software/mago

Generated by RepoPilot · 2026-05-09 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/carthage-software/mago shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

GO — Healthy across all four use cases

  • Last commit 1d ago
  • 6 active contributors
  • Apache-2.0 licensed
  • CI configured
  • Tests present
  • ⚠ Single-maintainer risk — top contributor 83% of recent commits

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live carthage-software/mago repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/carthage-software/mago.

What it runs against: a local clone of carthage-software/mago — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in carthage-software/mago | Confirms the artifact applies here, not a fork | | 2 | License is still Apache-2.0 | Catches relicense before you depend on it | | 3 | Default branch main exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 31 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>carthage-software/mago</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of carthage-software/mago. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/carthage-software/mago.git
#   cd mago
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of carthage-software/mago and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "carthage-software/mago(\\.git)?\\b" \\
  && ok "origin remote is carthage-software/mago" \\
  || miss "origin remote is not carthage-software/mago (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(Apache-2\\.0)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"Apache-2\\.0\"" package.json 2>/dev/null) \\
  && ok "license is Apache-2.0" \\
  || miss "license drift — was Apache-2.0 at generation time"

# 3. Default branch
git rev-parse --verify main >/dev/null 2>&1 \\
  && ok "default branch main exists" \\
  || miss "default branch main no longer exists"

# 4. Critical files exist
test -f "Cargo.toml" \\
  && ok "Cargo.toml" \\
  || miss "missing critical file: Cargo.toml"
test -f "crates/analyzer/src/lib.rs" \\
  && ok "crates/analyzer/src/lib.rs" \\
  || miss "missing critical file: crates/analyzer/src/lib.rs"
test -f "crates/analyzer/src/context/mod.rs" \\
  && ok "crates/analyzer/src/context/mod.rs" \\
  || miss "missing critical file: crates/analyzer/src/context/mod.rs"
test -f "crates/algebra/src/lib.rs" \\
  && ok "crates/algebra/src/lib.rs" \\
  || miss "missing critical file: crates/algebra/src/lib.rs"
test -f "build.rs" \\
  && ok "build.rs" \\
  || miss "missing critical file: build.rs"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 31 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~1d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/carthage-software/mago"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

Mago is an extremely fast PHP linter, formatter, and static analyzer written in Rust that brings toolchain capabilities inspired by the Rust ecosystem to PHP development. It provides real-time code quality checks, automated fixes, and deep static analysis to catch type errors and bugs without external dependencies, delivering results orders of magnitude faster than traditional PHP tools. Workspace monorepo under crates/ with specialized crates: crates/algebra for constraint solving, crates/analyzer for static analysis, crates/syntax for PHP parsing. Composer integration at composer/bin/mago bridges PHP package management. The project includes a corpus/ directory for regression testing against real PHP code. Rust edition 2024 with strict clippy lints enforcing code quality across all members.

👥Who it's for

PHP developers and development teams who need fast, reliable code quality tooling comparable to what Rust developers have with Clippy and rustfmt. DevOps engineers and CI/CD pipeline maintainers who want to replace slow PHP-based linters (like PHPStan, PHP-CS-Fixer, Psalm) with a single, performant binary.

🌱Maturity & risk

Production-ready and actively maintained: version 1.26.0 with multiple CI/CD workflows (ci.yml, cd.yml, ecosystem.yml), comprehensive issue templates, and security policy (SECURITY.md). The codebase shows strong engineering practices with CodSpeed benchmarking, CodeQL scanning, and dual licensing (MIT/Apache-2.0). Regular releases and active contributor community indicate sustained development momentum.

Low risk for adoption but with specific constraints: single primary author listed (Seifeddine Gmati), requires Rust 1.95.0+, and the PHP ecosystem integration is newer than competing tools. Monorepo with 11+ crate dependencies increases coordination complexity. Migration from established tools like PHPStan carries switching cost, though the speed advantage typically justifies it. Check ecosystem.yml workflows to see real-world project compatibility testing.

Active areas of work

Active development with automated workflows for nightly builds (update-nightly.yml), ecosystem compatibility testing (ecosystem.yml), and performance monitoring (codspeed.yml). The dual-license model and sponsor tracking (SPONSORS.md, sponsors.yml workflow) indicate community-driven growth. Multiple issue templates and triage automation suggest ongoing issue management and feature prioritization.

🚀Get running

Clone the repository: git clone https://github.com/carthage-software/mago.git && cd mago. Build with Cargo: cargo build --release. Install via the shell script: curl --proto '=https' --tlsv1.2 -sSf https://carthage.software/mago.sh | bash. Or install via Composer if targeting PHP projects: composer require carthage-software/mago.

Daily commands: Development: cargo build for debug or cargo build --release for optimized binary. Run tests: cargo test --all. Run benches: cargo bench. Use justfile for common tasks: just --list shows available commands. The Dockerfile and .envrc show containerized and Nix-shell development environments. Test against PHP corpus: cargo test --manifest-path corpus/mago.toml.

🗺️Map of the codebase

  • Cargo.toml — Root workspace manifest defining all crate dependencies, version (1.26.0), and Rust edition—essential for understanding build configuration and dependency tree.
  • crates/analyzer/src/lib.rs — Core analyzer library entry point; contains the primary AST analysis and type-checking logic that drives Mago's static analysis capabilities.
  • crates/analyzer/src/context/mod.rs — Central context management for scope tracking and assertion handling during analysis—fundamental to how symbols and types flow through the analyzer.
  • crates/algebra/src/lib.rs — Algebraic constraint system for assertion logic; used by analyzer to reason about type relationships and data flow.
  • build.rs — Build script orchestrating the compilation pipeline; critical for understanding how PHP code is parsed and bundled into the final binary.
  • .github/workflows/ci.yml — CI/CD pipeline definition; shows testing, linting, and build strategies that enforce code quality across the monorepo.

🧩Components & responsibilities

  • Parser (Rust, hand-written lexer/parser (inferred)) — Tokenize and parse PHP source into AST; handle PHP 8.0+ syntax including attributes, named arguments, union types, match expressions.
    • Failure mode: Syntax error → analysis skipped for that file; graceful error reporting with line/column.
  • Analyzer (Rust, custom context manager, algebraic constraint system) — Traverse AST and perform semantic analysis: type inference, control flow analysis, scope tracking, rule enforcement.
    • Failure mode: Type error or rule violation → diagnostic added; analysis continues to find multiple issues per run.
  • Algebra Engine (Rust, constraint SAT solver) — Manage type assertions and constraints; solve satisfiability of type relationships; support type narrowing (e.g., after instanceof checks).
    • Failure mode: Unsatisfiable constraint → type error reported; soundness violation → potential false negative or false positive.
  • Context Manager — Maintain scope hierarchy, variable bindings, and assertion state as code is analyzed; track control flow (return/break/continue)

🛠️How to make changes

Add a New Static Analysis Rule

  1. Create a new rule module under crates/analyzer/src/rule/ (inferred pattern) or extend existing analysis pass (crates/analyzer/src/lib.rs)
  2. Implement assertion logic using the algebraic constraint system to narrow types and detect violations (crates/algebra/src/assertion_set.rs)
  3. Register the rule in the analyzer's main analysis pipeline to run on all parsed files (crates/analyzer/src/lib.rs)
  4. Add test case in the corpus project to validate rule behavior on real PHP code (corpus/mago.toml)

Add Type Inference for New PHP Construct

  1. Add AST node representation in the parser crate (inferred) (crates/analyzer/src/expression)
  2. Implement type inference logic in the expression analyzer module (crates/analyzer/src/expression)
  3. Update context tracking to propagate type information through scope hierarchy (crates/analyzer/src/context/mod.rs)
  4. Add assertion rules if type narrowing applies to this construct (crates/analyzer/src/assertion.rs)

Extend Control Flow Analysis

  1. Add new scope type (e.g., match_scope) if needed for language feature (crates/analyzer/src/context/scope/mod.rs)
  2. Implement control action tracking for new branching logic (crates/analyzer/src/context/scope/control_action.rs)
  3. Update context builder to instantiate new scope during AST traversal (crates/analyzer/src/context/mod.rs)
  4. Test scope nesting and variable reachability with corpus examples (corpus/src/hello.php)

🔧Why these technologies

  • Rust — Provides memory safety, zero-cost abstractions, and extreme performance for parsing and analyzing large PHP codebases without garbage collection overhead.
  • Algebraic constraint system (custom) — Enables precise type narrowing and data flow reasoning through SAT-style assertion logic, supporting advanced features like type guards and union type discrimination.
  • Monorepo workspace structure (Cargo workspaces) — Organizes parser, analyzer, and algebra as separate composable crates, allowing independent optimization and reuse while maintaining coherent versioning.
  • PHP Composer integration — Bridges the Rust toolchain to PHP ecosystems, allowing seamless adoption via standard package managers without forcing Rust as a dependency.

⚖️Trade-offs already made

  • Implement type system in Rust rather than PHP

    • Why: Enables blazing-fast analysis without PHP runtime overhead; provides compile-time verification and memory safety guarantees.
    • Consequence: Requires maintaining PHP AST knowledge separately from PHP itself; any PHP spec changes require manual updates to analyzer.
  • Use algebraic constraint solving for type narrowing

    • Why: Provides mathematically sound type inference and eliminates unsound heuristics; supports complex type relationships and generics.
    • Consequence: Higher complexity and steeper learning curve; constraint solving can be expensive for deeply nested expressions.
  • Single-pass analysis with scope-based context

    • Why: Reduces memory usage and enables streaming analysis of large files; simpler mental model than multi-pass approaches.
    • Consequence: Some analyses (e.g., forward references) require two-pass logic or post-processing; limits use of global symbol tables.
  • No external PHP parser dependency; custom parser implementation (inferred)

    • Why: Avoids FFI overhead and licensing complications; guarantees parser performance matches toolchain ambitions.
    • Consequence: Significant implementation burden; requires ongoing maintenance as PHP syntax evolves (PHP 8.4+).

🚫Non-goals (don't propose these)

  • Does not execute PHP code; purely static analysis with no runtime semantics.
  • Does not provide IDE features like autocomplete or go-to-definition (use Intelephense or other LSP servers).
  • Does not aim to be 100% spec-compliant with PHP runtime behavior; focuses on common misuse patterns and type safety.
  • Does not support dynamic language features (eval, variable function names) beyond simple heuristics.

🪤Traps & gotchas

Requires Rust 1.95.0+ to build—check rustc --version. The monorepo members in Cargo.toml are interdependent; building individual crates may fail without workspace context. Fuzzing targets (crates/syntax/fuzz) require cargo-fuzz installed separately. The .envrc references Nix—non-Nix users can skip it but lose hermetic dev environment. Ecosystem testing (ecosystem.yml) runs on CI only and requires real internet access; local cargo test won't catch all ecosystem regressions. Composer integration at composer/bin/mago expects compiled Rust binary available—PHP-only installs will fail.

🏗️Architecture

💡Concepts to learn

  • Constraint Solving / Type Inference via CSP — crates/algebra implements constraint satisfaction to infer PHP types without runtime—critical to understanding how Mago performs deep static analysis that traditional tools miss.
  • Abstract Syntax Tree (AST) with Incremental Parsing — crates/syntax parses PHP into an AST that supports incremental updates for fast re-analysis on file changes—enables Mago's performance advantage.
  • Fuzzing-Driven Testing — crates/syntax/fuzz and related fuzz targets automatically discover parser edge cases and crash conditions—essential for a production parser's reliability.
  • Workspace Monorepo with Feature Flags — Cargo workspace structure lets Mago ship as single binary while maintaining modular analyzer rules and syntax components—reduces deployment complexity.
  • Cross-Language Tooling (Rust + PHP + Twig) — Mago analyzes not just PHP but also Twig templates (see crates/twig-syntax)—requires multiple parsers and constraint solvers for comprehensive project understanding.
  • Clippy-Inspired Lint System with Auto-Fix — Rules architecture in crates/analyzer mirrors Rust's Clippy lint levels (allow/warn/deny) with automated fix generation—enables gradual adoption and IDE integration.
  • Binary Distribution via Package Managers — Mago compiles to single Rust binary distributed via Homebrew, Cargo, and Composer—eliminates PHP dependency hell that traditional PHP tools suffer from.
  • phpstan/phpstan — Direct competitor static analyzer for PHP; Mago aims to replace it with superior performance via Rust implementation.
  • FriendsOfPHP/PHP-CS-Fixer — Established PHP formatter and fixer tool that Mago's formatting engine competes with while maintaining style consistency.
  • etsy/phan — Alternative PHP static analyzer with different architecture; understanding Phan's approach helps position Mago's constraint-solving strategy.
  • rust-lang/rust-clippy — Direct inspiration for Mago—Clippy's lint architecture and rule organization patterns are reflected in Mago's analyzer design.
  • tree-sitter/tree-sitter — Parser generator that Mago's syntax crate builds upon for robust incremental PHP parsing capabilities.

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add comprehensive benchmark suite for analyzer performance across PHP versions

The repo has crates/analyzer/benches/cases.rs but it lacks benchmarks covering different PHP language versions and edge cases. Given that Mago targets PHP tooling with performance as a key differentiator (mentioned in CI badges), expanding the benchmark suite to measure analyzer performance across PHP 7.4, 8.0, 8.1, 8.2, 8.3+ syntax would provide concrete performance regression detection and help validate optimization claims.

  • [ ] Expand crates/analyzer/benches/cases.rs with separate benchmark groups for each PHP version
  • [ ] Create test PHP files in corpus/src/ for PHP 8.1+ features (enums, readonly properties, attributes, fibers)
  • [ ] Add benchmarks measuring parse time, analysis time, and memory usage per PHP version
  • [ ] Integrate benchmarks into the existing codspeed.yml workflow to track performance over time

Create integration tests for the Composer package wrapper in composer/

The composer/ directory contains the PHP wrapper (composer/bin/mago, composer/functions.php, composer/internal.php) but there are no visible test files validating that the Rust CLI correctly interfaces with PHP tooling. This is critical for end-user experience since developers will primarily interact through Composer.

  • [ ] Create composer/tests/ directory with integration test PHP scripts
  • [ ] Test the composer/bin/mago executable against sample PHP files in corpus/src/
  • [ ] Validate output format consistency between direct CLI and Composer wrapper invocations
  • [ ] Add integration tests to CI workflow (.github/workflows/ci.yml) to run before release

Add missing documentation for analyzer rules and configuration in docs/

The repo has a docs.yml workflow and references mago.toml configuration in the corpus, but the docs structure is not visible in the file list. Given the extensive analyzer rules in crates/analyzer/src/, there should be detailed rule documentation. New contributors need clear docs explaining which rules exist, their severity levels, and configuration options.

  • [ ] Document each analyzer rule by creating rule guide pages in docs/ (reference crates/analyzer/src/ rule implementations)
  • [ ] Create a docs/configuration.md explaining all mago.toml options using the corpus/mago.toml as an example
  • [ ] Add rule metadata (code, name, severity, fixable) to a structured rule registry that can be auto-generated from rule code
  • [ ] Link rule documentation from error output by implementing rule code references in analyzer results

🌿Good first issues

  • Expand crates/algebra/src/assertion_set.rs test coverage for edge cases in constraint simplification—currently missing tests for complex type unions and intersection combinations used by the analyzer.
  • Add missing documentation examples in crates/analyzer/src for each built-in linting rule, following the pattern shown in existing rules—helps contributors understand rule implementation patterns.
  • Implement missing integration tests in .github/workflows/ that validate Mago against popular open-source PHP libraries (Laravel, Symfony, WordPress plugins)—current ecosystem.yml needs concrete project list to prevent regressions.

Top contributors

Click to expand

📝Recent commits

Click to expand
  • 9ae2f7a — release: 1.26.0 (azjezz)
  • 3f29da7 — docs: fix broken README links to docs site (azjezz)
  • 4b49d01 — fix(linter): drop trailing comma in no-redundant-yield-from auto-fix (azjezz)
  • 104f972 — fix(codex): make clippy happy (azjezz)
  • b3b11c5 — fix(analyzer): suppress list-destructure key warnings when known items cover the positions (azjezz)
  • 14269d6 — feat(codex): infer if-true/if-false assertions from function-like bodies (azjezz)
  • dccf465 — chore: update pinned nightly toolchain to nightly-2026-05-06 (#1793) (github-actions[bot])
  • 856ceca — fix(database): correct column_number for offsets at line start (azjezz)
  • e801af1 — ci: use signed API commits in update-nightly workflow (#1789) (dotdash)
  • 9e873b0 — fix(analyzer): mark interface property reads as possibly undefined (azjezz)

🔒Security observations

The Mago project has a solid security foundation with active CI/CD pipelines, CodeQL integration, and a security contact policy. However, several medium-to-low risk issues exist: the Dockerfile lacks hardening and non-root user configuration, dependency vulnerability scanning is not visible in the CI pipeline, and Clippy lints allow potentially unsafe patterns. The security policy documentation is minimal. Overall security posture is good but could be improved with container hardening, dependency auditing in CI, and more comprehensive security documentation.

  • Medium · Incomplete Dockerfile Security Configuration — Dockerfile. The Dockerfile uses Alpine 3 base image with minimal hardening. It installs git without pinning a specific version and runs the binary with ENTRYPOINT without specifying a non-root user. This could allow privilege escalation if the container is compromised. Fix: 1) Pin the Alpine version (e.g., 'FROM alpine:3.20'). 2) Add 'RUN addgroup -S mago && adduser -S mago -G mago' to create a non-root user. 3) Use 'USER mago' before ENTRYPOINT. 4) Pin git version or use multi-stage builds to reduce attack surface.
  • Low · Missing Security Headers and Hardening in CI/CD — .github/workflows/. While GitHub Actions workflows are present, there is no visible evidence of SAST (Static Application Security Testing) integration beyond CodeQL. The project lacks documented security scanning for Rust dependencies (cargo-audit, cargo-deny). Fix: 1) Add cargo-audit or cargo-deny to the CI pipeline to scan for known vulnerable dependencies. 2) Integrate additional SAST tools like Trivy or Snyk. 3) Document dependency scanning in SECURITY.md.
  • Low · Overly Permissive Clippy Lint Configuration — Cargo.toml - [workspace.lints.clippy]. The Cargo.toml allows 'as_conversions' without restrictions, which could hide unsafe type conversions. Multiple restriction-level lints are allowed ('indexing_slicing', 'cast_possible_truncation'), reducing security oversight for potential integer overflow or out-of-bounds access. Fix: 1) Enforce stricter linting for security-critical code paths. 2) Consider disallowing 'as_conversions' and 'cast_possible_truncation' or document exceptions with inline attributes. 3) Add clippy::cast_possible_wrap to catch integer wrap-around bugs.
  • Low · Incomplete Security Policy Documentation — SECURITY.md. The SECURITY.md file is minimal and provides only a basic security contact email. It lacks details on security processes, vulnerability disclosure timeline, and supported versions for security patches. Fix: Expand SECURITY.md to include: 1) Vulnerability disclosure timeline (e.g., 90-day coordinated disclosure). 2) Supported versions eligible for security patches. 3) Known security limitations. 4) Process for security advisories.
  • Info · Rust Edition 2024 Not Yet Released — Cargo.toml - edition field. The Cargo.toml specifies 'edition = "2024"', which is a future Rust edition not yet released. This may cause build failures or unpredictable behavior when the edition is finalized. Fix: Verify that Rust 1.95.0 officially supports edition 2024. If still in preview, consider reverting to edition 2021 until 2024 is officially released, or use the nightly toolchain with explicit opt-in.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · carthage-software/mago — RepoPilot