{"id":50147982,"url":"https://github.com/wyolet/symbol","last_synced_at":"2026-05-24T06:05:21.885Z","repository":{"id":350871199,"uuid":"1208558448","full_name":"wyolet/symbol","owner":"wyolet","description":"AST-native code intelligence for Python — MCP server for Claude Code (12 agent tools) + standalone audit CLI (dead code, import graph, swallowed exceptions, 500+ language LOC via Linguist port).","archived":false,"fork":false,"pushed_at":"2026-05-21T01:59:18.000Z","size":817,"stargazers_count":0,"open_issues_count":6,"forks_count":2,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-21T03:14:49.117Z","etag":null,"topics":["ast","claude-code","cli","code-intelligence","developer-tools","linguist","mcp","python","static-analysis"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wyolet.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-12T12:59:21.000Z","updated_at":"2026-05-21T01:59:22.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/wyolet/symbol","commit_stats":null,"previous_names":["aaliboyev/ca-tools","wyolet/symbol"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/wyolet/symbol","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wyolet%2Fsymbol","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wyolet%2Fsymbol/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wyolet%2Fsymbol/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wyolet%2Fsymbol/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wyolet","download_url":"https://codeload.github.com/wyolet/symbol/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wyolet%2Fsymbol/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33423286,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-23T22:14:44.296Z","status":"online","status_checked_at":"2026-05-24T02:00:06.296Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ast","claude-code","cli","code-intelligence","developer-tools","linguist","mcp","python","static-analysis"],"created_at":"2026-05-24T06:05:11.940Z","updated_at":"2026-05-24T06:05:21.872Z","avatar_url":"https://github.com/wyolet.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# symbol\n\n**AST-native code intelligence for Python.** A CLI for humans, an MCP server for agents.\n\nPoint `symbol` at a directory and ask it questions a human or a coding agent actually has:\n\n- What frameworks does this project use, where are the entry points, which files are dead, what runs on import, where the TODOs and swallowed exceptions hide?\n- Where is `UserService` defined, who calls `db.commit`, what's the body of `process_payment` — without re-reading 800 lines of file?\n\nStatic analysis only — `symbol` never imports or executes the target code.\n\n\u003e **Status:** [`wyolet-symbol`](https://pypi.org/project/wyolet-symbol/) on PyPI (CLI command is `symbol` — the bare name is reserved by PyPI policy). Python is the proving ground; **Go** and **TypeScript** are next on the roadmap.\n\n![symbol audit on a FastAPI app](docs/demos/audit.svg)\n\n## Install\n\n### CLI only\n\n```bash\nuv tool install wyolet-symbol      # or: pipx install wyolet-symbol\n```\n\nThen `symbol audit /path/to/project`, `symbol loc`, `symbol map` work from any directory.\n\n### Claude Code plugin (CLI + MCP server + skill + hooks)\n\nFor full agent integration — MCP tools (`SearchSymbol`, `SymbolBody`, `MultiPatch`, …), the `symbol` skill, and soft-nudge PreToolUse / PostToolUse hooks that steer Claude away from native Grep/Read/Edit on indexed Python files:\n\n```bash\n# 1. Install the CLI from PyPI\nuv tool install wyolet-symbol\n\n# 2. Install the plugin in Claude Code (bundles MCP server registration, skill, hooks)\nclaude plugin install git+https://github.com/wyolet/symbol@main\n```\n\n### Other agent coding tools\n\nThe MCP server is plain stdio MCP — it works anywhere MCP works. **opencode** setup is documented in [`docs/integrations/opencode.md`](docs/integrations/opencode.md). Cursor, Continue, and Zed integrations are tracked in [#8](https://github.com/wyolet/symbol/issues/8); help wanted. The Claude-Code-specific glue (skill + hooks) does not port automatically.\n\n### Updating / uninstall\n\n```bash\nuv tool upgrade wyolet-symbol      # or: uv tool uninstall wyolet-symbol\nclaude plugin update symbol        # or: claude plugin uninstall symbol\n```\n\n## Commands\n\n### `symbol audit \u003cpath\u003e` — Full codebase audit\n\nRuns all registered checkers: stack detection, entry points, orphan files, side effects, swallowed exceptions, TODOs, unused deps, code structure metrics.\n\n```bash\nsymbol audit /path/to/project\nsymbol -v audit /path/to/project   # verbose — full detail\nsymbol /path/to/project            # shortcut — defaults to audit\n```\n\n### `symbol loc \u003cpath\u003e` — Lines of code\n\nGitHub Linguist port: 500+ languages, real GitHub colors, multi-strategy detection (modeline, shebang, filename, extension, XML, manpage). Colored bar chart by default.\n\n```bash\nsymbol loc /path/to/project\n```\n\n### `symbol map \u003cpath\u003e` — Import graph analysis\n\nCircular imports, hotspots, fragile modules, deep chains, leaf modules, blast radius.\n\n```bash\nsymbol map /path/to/project\nsymbol map /path/to/project --blast src/models.py      # blast radius\nsymbol map /path/to/project --min-chain 3              # show shorter chains\nsymbol map /path/to/project --min-fan-in 3             # lower hotspot threshold\n```\n\n### Symbol-level inspection\n\n```bash\nsymbol search UserService              # exact / suffix match on qualified path\nsymbol search user service --fixed     # all patterns must appear as substrings\nsymbol search '^get_' --regex          # Python regex\nsymbol search save --kind method\n\nsymbol code services.user.UserService         # body by qualified path\nsymbol code services.user.UserService.save    # method\nsymbol code src/services/user.py:120-145      # by explicit line range\n\nsymbol outline src/services/user.py    # parent-child tree of one file\nsymbol callers UserService             # textual tier-1 reference scan\n```\n\n### `symbol patch \u003cfile\u003e` — Byte-range edit\n\nEdit by line range without sending an `old_string` payload. Replace (with content), delete (empty content), or insert (zero-width range).\n\n```bash\nsymbol patch src/foo.py --range 10-20 --content 'new body'    # replace\nsymbol patch src/foo.py --range 10-20 --content ''            # delete\nsymbol patch src/foo.py --range 10-10 --content 'import os'   # insert before line 10\n\nsymbol patch src/foo.py --range 10-20 --content '...' --dry-run   # preview diff\nsymbol patch src/foo.py --range 10-20 --content '...' --force     # skip read-cache check\nsymbol patch src/foo.py --range 10-20 --content '...' --agent     # plain text for LLMs\n```\n\nExit codes: `0` applied/dry-run, `1` error, `2` needs_read_confirmation.\n\n### Plus\n\n`symbol analyze \u003cfile\u003e`, `symbol dump \u003cpath\u003e`, `symbol init \u003cpath\u003e`, `symbol update-linguist`, `symbol undo`, `symbol refresh [--full]`.\n\n## MCP surface (12 agent tools)\n\nWhen run as `symbol mcp` (or installed via the plugin), `symbol` exposes:\n\n| Read | Write | Safety |\n| --- | --- | --- |\n| `SearchSymbol` | `Patch` | `Undo` |\n| `SymbolBody` | `MultiPatch` | `Refresh` |\n| `SymbolOutline` | `InsertSymbol` | |\n| `SymbolCallers` | `DeleteSymbol` | |\n| | `RenameSymbol` | |\n| | `ReplaceSymbol` | |\n\n`Undo` is transactional and operates on `.symbol/transactions/` — no git involvement, no staged changes touched. `Refresh` is the escape hatch when the index drifts.\n\n## Configuration\n\nUse `symbol.toml` at the project root, or add the same tables under\n`[tool.symbol]` in `pyproject.toml`.\n\n```toml\n# symbol.toml\n[checker]\nexclude = [\"alembic/*\", \"scripts/*\"]\n\n[checkers.orphans]\nseverity = \"warning\"        # default: error\nignore = [\"alembic/*\", \"src/main.py\"]\n\n[checkers.side_effects]\nseverity = \"info\"           # default: warning\nignore = [\"*.include_router()\", \"*.add_middleware()\"]\n\n[checkers.unused_deps]\nseverity = \"error\"          # default: error\nignore = [\"greenlet\", \"psycopg\"]\n```\n\nIn `pyproject.toml`, prefix those tables with `tool.symbol`:\n\n```toml\n[tool.symbol.checker]\nexclude = [\"alembic/*\", \"scripts/*\"]\n\n[tool.symbol.checkers.orphans]\nseverity = \"warning\"\nignore = [\"alembic/*\", \"src/main.py\"]\n```\n\nThe config schema lives at\n[`schemas/symbol.config.schema.json`](schemas/symbol.config.schema.json). With\nTaplo, you can wire it up for both forms:\n\n```toml\n[[rule]]\ninclude = [\"symbol.toml\"]\nschema = { path = \"schemas/symbol.config.schema.json\" }\n\n[[rule]]\ninclude = [\"pyproject.toml\"]\nkeys = [\"tool\", \"symbol\"]\nschema = { path = \"schemas/symbol.config.schema.json\" }\n```\n\nSee [`docs/spec-schema.md`](docs/spec-schema.md) for the package/spec schema.\n\n## Global options\n\n```\n-v, --verbose          Show full detail instead of compact output\n--format json          Output as JSON (for CI/CD pipelines)\n-i, --include PATTERN  Only analyze files matching glob pattern\n-e, --exclude PATTERN  Skip files matching glob pattern\n```\n\n## Why\n\nMost Python tools find problems *inside* files (lint, types, dead code). `symbol` finds problems *between* files — and exposes the result to agents in tokens, not line ranges:\n\n- **knip** does this for JavaScript/TypeScript. Python didn't have an equivalent. `symbol` fills that gap.\n- **GitHub Linguist ported to Python** — accurate detection for 500+ languages with real GitHub colors.\n- **scc-style LOC** with language breakdown and colored bar chart.\n- **Import-graph analysis** — circular imports, hotspots, blast radius. Things no other Python tool surfaces.\n- **Agent-friendly write surface** — symbol-level patch / rename / replace with byte-range edits and a transactional undo log, so coding agents don't have to re-read entire files to make safe changes.\n\nFirst thing you run on an unfamiliar codebase, before reading a single line of code.\n\n## Architecture\n\n```\nsrc/wyolet/symbol/\n├── cli.py                  Typer root CLI\n├── commands/               Thin command views (audit, loc, map, analyze,\n│                           search, code, outline, callers, patch, refresh,\n│                           undo, init, + symbol-level ops for MCP)\n├── checkers/               @register'd checkers\n│   ├── stack.py            tech stack from deps\n│   ├── entrypoints.py      __main__ guards, framework hooks\n│   ├── orphans.py          unreachable files\n│   ├── side_effects.py     bare module-level calls\n│   ├── swallowed.py        silenced exceptions\n│   ├── todos.py            TODO/FIXME/HACK/XXX\n│   ├── unused_deps.py      declared but unimported\n│   └── code_structure.py   functions, classes, type coverage\n├── shared/                 Core infrastructure\n│   ├── context.py          AnalysisContext (root, spec, cache, config)\n│   ├── ast_cache.py        parse once, share across checkers\n│   ├── registry.py         @register + views()\n│   ├── runner.py           dispatches file/project checkers\n│   ├── spec.py             spec loader\n│   ├── config_resolver.py  spec → packages → project-config layering\n│   ├── framework_detector.py\n│   ├── pipeline.py         @hook(pipeline, priority)\n│   ├── graph.py            import graph primitives\n│   ├── symbol_index.py     qualified-path index for MCP read/write tools\n│   └── linguist/           GitHub Linguist port (500+ langs)\n└── data/\n    ├── spec.toml           Global baseline spec\n    └── specs/NAME/         Per-package specs (237 packages: django, fastapi,\n                            celery, sqlalchemy, langchain, pydantic, ...)\n```\n\n## Contributing\n\nThe fastest ways to help:\n\n- **Add a package spec** for a library we don't cover yet — no Python required, just TOML. See [#3](https://github.com/wyolet/symbol/issues/3) and [`CONTRIBUTING.md`](CONTRIBUTING.md).\n- **Run `symbol` on your real Python project** and file false positives. See [#6](https://github.com/wyolet/symbol/issues/6).\n- **Benchmark the MCP surface** against native Read/Grep/Edit on representative agent tasks. See [#7](https://github.com/wyolet/symbol/issues/7).\n- **Wire `symbol mcp` into opencode / Cursor / Continue / Zed**. See [#8](https://github.com/wyolet/symbol/issues/8).\n\nPinned issues on the repo show what's most useful right now.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwyolet%2Fsymbol","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwyolet%2Fsymbol","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwyolet%2Fsymbol/lists"}