{
  "markdown": "<p align=\"center\">\n  <img src=\"docs/vulture.png\" alt=\"agent4s\" width=\"200\">\n  <br>\n  <strong>agent4s</strong>\n  <br><br>\n  <em>Scala superpowers for AI coding agents.</em>\n</p>\n\n<p align=\"center\">\n  <a href=\"LICENSE\"><img src=\"https://img.shields.io/badge/license-MIT-blue.svg\" alt=\"MIT License\"></a>\n  <a href=\"#all-35-commands\"><img src=\"https://img.shields.io/badge/commands-35-brightgreen.svg\" alt=\"35 Commands\"></a>\n</p>\n\n---\n\nYour AI agent treats Scala like plain text. `grep` finds 50 things named `Config`. agent4s finds the one you mean.\n\n**35 commands** for code navigation, refactoring, dead code detection, and bug hunting. No build server. No compilation. From `git clone` to first answer in 349ms.\n\n```bash\n# Claude Code\nclaude plugins install agent4s\n\n# Homebrew (macOS/Linux)\nbrew install scala-digest/tap/agent4s\n```\n\n---\n\n## Why\n\nAI coding agents have three bad options for Scala:\n\n| Option | Problem |\n|---|---|\n| **grep** | Returns raw text. `class Config` matches `ConfigStore`, `ConfigParser`. Two packages with `Config`? Hits both. |\n| **Metals LSP** | Requires build server, full compilation, minutes of startup. Designed for humans in IDEs, not agents making 50 tool calls per task. |\n| **The AI model itself** | Reads source files well, but can't trace inheritance trees, find dead code, or scan 18K files for bug patterns in 3 seconds. |\n\nagent4s is the fourth option: **instant structured code intelligence from parsed ASTs**. Works without a build. Gets smarter when you compile (`--semantic`).\n\n---\n\n## 5 things only agent4s can do\n\n**Find bugs without compiling.** 45 AST patterns with cross-file taint analysis. SQL injection, XSS, `.get` on Option, `null`, weak crypto, ZIO anti-patterns. No other tool does this for Scala without a build server.\n```bash\nagent4s bug-hunt --severity critical --no-tests\nagent4s bug-hunt --hotspots                        # rank by findings x git churn\n```\n\n**Rename the right `Config`.** Two classes named `Config` in different packages? `--semantic` renames only the one you mean.\n```bash\nagent4s rename Config AppConfig --semantic\n```\n\n**Find dead code in seconds.** Bloom filter pre-screening across every file. If no file's bloom filter contains the symbol name — it has zero external references.\n```bash\nagent4s unused com.legacy --kind class\n```\n\n**Trace call chains.** What does `processPayment` call? Who calls it? Bidirectional call graph from parsed method bodies.\n```bash\nagent4s call-graph processPayment --in PaymentService\n```\n\n**Understand a codebase in one command.** Packages, hub types, dependency graph, architecture — all in 60 lines.\n```bash\nagent4s overview --concise\n```\n\n---\n\n## Quick Start\n\n### Claude Code\n\n```bash\nclaude plugins install agent4s\n```\n\nThen in your project:\n```\n/agent4s:setup\n```\n\n14 skills available: `/agent4s:bug-hunt`, `/agent4s:audit`, `/agent4s:critique`, `/agent4s:harden`, `/agent4s:simplify`, `/agent4s:normalize`, `/agent4s:extract`, `/agent4s:polish`, `/agent4s:setup`, `/agent4s:semanticdb`, `/agent4s:doctor`, `/agent4s:upgrade`, `/agent4s:submit`. Plus `scala-expert` agent for multi-step tasks.\n\n### MCP Server\n\n35 commands exposed as MCP tools. In-memory index caching between calls. Copy-paste the config for your editor:\n\n<details>\n<summary><strong>Cursor</strong> — <code>.cursor/mcp.json</code></summary>\n\n```json\n{\n  \"mcpServers\": {\n    \"agent4s\": {\n      \"command\": \"agent4s\",\n      \"args\": [\"mcp\"]\n    }\n  }\n}\n```\n</details>\n\n<details>\n<summary><strong>Windsurf</strong> — <code>~/.codeium/windsurf/mcp_config.json</code></summary>\n\n```json\n{\n  \"mcpServers\": {\n    \"agent4s\": {\n      \"command\": \"agent4s\",\n      \"args\": [\"mcp\"]\n    }\n  }\n}\n```\n</details>\n\n<details>\n<summary><strong>Cline</strong> — <code>~/.cline/mcp_settings.json</code></summary>\n\n```json\n{\n  \"mcpServers\": {\n    \"agent4s\": {\n      \"command\": \"agent4s\",\n      \"args\": [\"mcp\"],\n      \"disabled\": false\n    }\n  }\n}\n```\n</details>\n\n<details>\n<summary><strong>Generic MCP client</strong></summary>\n\n```json\n{\n  \"mcpServers\": {\n    \"agent4s\": {\n      \"type\": \"stdio\",\n      \"command\": \"agent4s\",\n      \"args\": [\"mcp\"]\n    }\n  }\n}\n```\n</details>\n\nIf you installed via Homebrew, `agent4s` is already on your PATH. Otherwise replace `agent4s` with the full binary path.\n\n### GitHub Action (CI)\n\n```yaml\n- uses: scala-digest/agent4s-action@v1\n  with:\n    command: bug-hunt\n    args: '--severity high --no-tests'\n```\n\nSee [action/README.md](action/README.md) for full options (unused code checks, fail gates, multi-command setups).\n\n### CLI\n\n```bash\ngit clone https://github.com/scala-digest/agent4s.git\ncd agent4s && ./build-native.sh\n\n# Or run without building\nscala-cli run src/ -- search /path/to/project MyClass\n```\n\n---\n\n## bug-hunt\n\nStatic analysis for Scala that doesn't need your build to compile. Self-improving: learns from every triage.\n\n```\n                            bug-hunt pipeline\n                            ─────────────────\n\n  ┌──────────────────────────────────────────────────────────────────┐\n  │                                                                  │\n  │   ┌─────────────┐   ┌─────────────┐   ┌────────────────────┐   │\n  │   │  AST Scan   │──▶│ Reachability │──▶│    LLM Triage      │   │\n  │   │             │   │   Filter     │   │                    │   │\n  │   │ 45 patterns │   │             │   │ Read context code  │   │\n  │   │ taint trace │   │ entrypoints │   │ Classify each:     │   │\n  │   │ credential  │   │ call-graph  │   │  ✓ Confirmed       │   │\n  │   │ detection   │   │ depth limit │   │  ? Likely           │   │\n  │   └─────────────┘   └─────────────┘   │  ✗ False positive  │   │\n  │         ▲                              └──────┬─────────────┘   │\n  │         │                                     │                 │\n  │         │   ┌──────────────────┐              │                 │\n  │         │   │   Suppression    │◀─── ✗ FP ───┘                 │\n  │         │   │    Memories      │         ┌────────────────┐     │\n  │         │   │                  │    ✓ ──▶│    Report      │     │\n  │         │   │ .scalex/        │    ? ──▶│               │     │\n  │         │   │ memories.json   │         │ + GitHub issue │     │\n  │         │   └────────┬─────────┘         │   cross-ref   │     │\n  │         │            │                   │ + repro script │     │\n  │         │            │ auto-suppress     └────────────────┘     │\n  │         └────────────┘ on next scan                             │\n  │                                                                  │\n  └──────── self-improving loop: fewer false positives each run ────┘\n```\n\n### How the loop works\n\n1. **Scan** — 45 AST patterns + cross-file taint analysis + credential regex. Bloom filter pre-screens files. Parallel scan with 20s timeout.\n2. **Reachability** — `--reachable` filters to findings reachable from entrypoints (routes, `@main`, `extends App`). Dead code excluded.\n3. **LLM Triage** — `/agent4s:bug-hunt` skill reads surrounding code, classifies each finding. Known patterns: ZIO `Ref.get` vs `Option.get`, test-only secrets, safe casts after match.\n4. **Memory** — false positives auto-recorded: `memory add <pattern> --source llm-triage`. Stored in `.scalex/memories.json` (version-controllable, shareable).\n5. **Next scan** — memories loaded at scan start, matching findings suppressed. Fewer false positives with each run.\n\n**45 patterns** across 7 categories:\n\n| Category | Patterns |\n|---|---|\n| **Security** | SQL injection, XSS, SSRF, XXE, command injection, path traversal, weak crypto, hardcoded secrets, open redirect, regex DoS, LDAP injection, insecure deserialization, log injection |\n| **Type safety** | `.get` on Option, `.head`/`.last` on collection, `asInstanceOf`, `null`, `return` in lambda |\n| **Concurrency** | `Await.result(Duration.Inf)`, `Thread.sleep`, nested `synchronized`, `sender()` in Future, `var` + Future |\n| **Effects** | `throw` in `ZIO.succeed`, `ZIO.die`, `unsafeRun`, blocking in effect |\n| **Resources** | Unclosed `Source`, `Stream`, `Connection` |\n| **Crypto** | Weak hash (MD5/SHA1), weak cipher (DES/RC4), weak random, hardcoded IV, ECB mode |\n| **Credentials** | 16 regex patterns: AWS, GitHub, Anthropic, OpenAI, Slack, Stripe, private keys |\n\n**Taint analysis** (on by default): traces variable assignments backward from sinks to sources. HTTP parameter flows to SQL query? Flagged with the full flow chain. Literal-derived sinks? Suppressed. Cross-file tracing up to 3 hops.\n\n```bash\nagent4s bug-hunt -w /path/to/project              # all patterns\nagent4s bug-hunt --severity critical --no-tests    # critical only, production code\nagent4s bug-hunt --reachable                       # only findings reachable from entrypoints\nagent4s bug-hunt --hotspots                        # files ranked by findings x git churn\nagent4s bug-hunt --json                            # structured output for CI\nagent4s memory list                                # show suppression memories\nagent4s pattern validate spec.json                 # validate a CVE-to-pattern spec\n```\n\n---\n\n## Benchmarks\n\nNative GraalVM binary, Apple Silicon M3 Max. Full methodology: [docs/BENCHMARK.md](docs/BENCHMARK.md).\n\n| Project | Files | Symbols | Cold Index | Warm Index |\n|---|---|---|---|---|\n| Scala 3 compiler | 18,703 | 148,179 | 2.7s | 349ms |\n\n### agent4s vs grep — real numbers on scala3 compiler\n\n| Task | agent4s | grep |\n|---|---|---|\n| Who imports `Compiler`? | **1,213 files** (resolves `import dotty.tools.*`) | 86 files (literal match only) |\n| Inheritance tree | 7 subclasses, 3 levels deep | Not possible |\n| Dead code in `dotty.tools.dotc` | **113 classes** with zero external refs | Not possible |\n| Bug patterns | **30 findings** in 16 files (MD5 hash, deadlocks, ReDoS) | Regex hacks, high false positives |\n| Project overview | Packages, hub types, dep graph in 60 lines | Not possible |\n| Rename `Config` (2 packages) | `--semantic` renames only the right one | Renames both |\n\nUse grep for: string literals, config values, non-Scala files.\n\n---\n\n## Commands\n\n### Search & Navigate\n\n```bash\nagent4s search Service --kind trait         # fuzzy camelCase search\nagent4s def UserService --verbose           # find definition with signature\nagent4s explain UserService --related       # definition + doc + members + impls in one call\nagent4s hierarchy Compiler --depth 3        # inheritance tree from parsed extends clauses\nagent4s members Signal --inherited          # members including parents\n```\n\n### Refactor\n\n```bash\nagent4s rename OldName NewName              # text-based, word-boundary safe\nagent4s rename OldName NewName --semantic   # type-aware via SemanticDB\nagent4s scaffold impl MyServiceLive         # generate override stubs with type param substitution\nagent4s scaffold test MyService             # test skeleton (munit/scalatest/zio-test)\n```\n\n### Analyze\n\n```bash\nagent4s refs UserService --count            # how many files reference this symbol\nagent4s call-graph processPayment --in Svc  # what it calls + who calls it\nagent4s unused com.legacy                   # symbols with zero external refs\nagent4s coverage UserService                # references in test files only\nagent4s deps Phase --depth 2               # what this symbol depends on\n```\n\n### Explore\n\n```bash\nagent4s overview --concise                  # project summary: packages, key types, stats\nagent4s api com.example --used-by com.web   # coupling between packages\nagent4s diff HEAD~5                         # which symbols changed vs a git ref\nagent4s ast-pattern --extends Phase --has-method run  # structural search by shape\nagent4s grep \"pattern\" --in ClassName --each-method   # regex scoped to a type's methods\n```\n\n<details>\n<summary><strong>All 35 commands</strong></summary>\n\n```\nsearch          Fuzzy camelCase symbol search\ndef             Find where a symbol is defined\nimpl            Find classes/objects extending a trait\nrefs            Find references (text matching + bloom filters)\nimports         Find import statements for a symbol\nmembers         List members of a class/trait/object\ndoc             Extract scaladoc comment\nexplain         Definition + doc + members + impls in one call\nbody            Extract method/class source text\nhierarchy       Inheritance tree from extends clauses\noverrides       Find override implementations across types\ndeps            Import + body dependencies of a symbol\ncontext         Enclosing scopes at a file:line\ndiff            Symbol-level diff vs a git ref\ncoverage        References in test files only\ntests           List test cases structurally\nast-pattern     Search by structural shape (extends + has-method + body-contains)\noverview        Project summary (symbols by kind, top packages)\napi             Externally-imported symbols of a package\nsummary         Sub-packages with symbol counts\npackages        List all packages\npackage         All symbols in a package\nfile            Find files by name (fuzzy)\nsymbols         What's defined in a file\nannotated       Find symbols with a specific annotation\nentrypoints     Find @main, def main, extends App, test suites\ngrep            Regex search scoped to Scala/Java files\nindex           Force reindex\nbatch           Multiple queries, one index load (stdin)\nrename          Word-boundary rename (text or semantic)\nunused          Symbols with zero external references\ncall-graph      Callees (from body) + callers (from refs) of a method\nbug-hunt        45 AST patterns + taint analysis + hotspot ranking\nmemory          Suppression memory management (list/add/remove/export/import)\npattern         CVE-to-pattern validation pipeline\nscaffold impl   Generate override stubs for unimplemented members\nscaffold test   Generate test suite skeleton\ngraph           ASCII/Unicode directed graph rendering\nmcp             Start MCP server (JSON-RPC over stdio)\n```\n\n</details>\n\nAll commands support `--json`, `--path`, `--no-tests`, `--in-package`, `--limit`.\n\n---\n\n## How It Works\n\n```\n1. git ls-files --stage       → tracked .scala/.java files + content hashes\n2. Compare OIDs vs cache      → skip unchanged files\n3. Scalameta parse (parallel) → AST → symbols, bloom filters, imports\n4. .scalex/index.bin          → binary cache with string interning\n5. Answer the query            → lazy indexes built on demand\n6. [Optional] SemanticDB       → .semanticdb files from compiler for type-aware mode\n```\n\nNo build server. No daemon. Run, answer, exit. Works on any Scala project from `git clone`.\n\n---\n\n## Limitations\n\nagent4s parses source text into ASTs — it does not compile. This makes it fast and dependency-free, but:\n\n- **No type inference.** We don't know what type `x` has unless it's annotated.\n- **No implicit resolution.** Can't find which given instance the compiler would select.\n- **No macro expansion.** Macro-generated code is invisible.\n- **refs is text-based.** `refs Config` finds all things named Config. Use `--semantic` for disambiguation.\n- **Taint analysis is heuristic.** Traces variable names, not types. The LLM triage in `/agent4s:bug-hunt` helps filter false positives.\n\nFor full semantic precision: compile with `-Xsemanticdb` and use `--semantic` flag.\n\n---\n\n## Credits\n\nBuilt on [scalex](https://github.com/nguyenyou/scalex) by Tu Nguyen. MIT licensed.\n\n- [Scalameta](https://scalameta.org/) — AST parsing and SemanticDB format\n- [Metals](https://scalameta.org/metals/) — inspiration for git OID caching, bloom filter search\n- [ascii-graphs](https://github.com/scalameta/ascii-graphs) — Sugiyama-style graph layout (ported to Scala 3.8)\n\n---\n\n## License\n\nMIT\n",
  "bytes": 15598,
  "sha": "1885157313c39c9ab1fa883366c38a749f5559aaca49a24fc8f2f95b500b969f",
  "repo_slug": "scala-digest/agent4s",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/plg_scala_digest_agent4s_agent4s_97656cdd/readme"
}