phpkg
A code knowledge graph for large legacy PHP codebases — CakePHP 2/3 and Laravel — served to coding agents over MCP.
Legacy PHP defeats both of an AI agent's usual moves. The files are too big to read, and the symbol names are too repetitive to grep. In the codebase this was built against:
| File | Lines | Tokens to read it | get_file method index |
Saving |
|---|---|---|---|---|
OrderRepository.php |
34,053 | ~447,000 | 4,249 | 99.0% |
PaymentsController.php |
30,267 | ~399,500 | — | — |
UsersController.php |
18,395 | ~212,700 | 10,427 | 95.1% |
Reading one of those files can consume an entire context window. Meanwhile
279 methods in that codebase are named index, 230 add, 211 edit — so
grep "function index" returns 279 hits with no way to rank them.
phpkg parses the codebase once with tree-sitter, resolves it into a graph (symbols, callers, routes, model associations, DB-table access), and serves 36 tools over MCP so your agent asks precise questions instead of reading files.
get_symbol("applyScheme") → file + exact start_line/end_line (~200 tokens)
get_callers("applyScheme") → real CALLS_METHOD edges, not text matches
table_consumers("orders") → every reader/writer, across every stack
blast_radius("applyScheme") → what breaks if you change it
Install
Claude Code (plugin — MCP + skill + hooks in one)
/plugin marketplace add ArcXzost/phpkg
/plugin install phpkg@phpkg
Then, in your PHP project:
/phpkg-setup
That detects your stacks, builds the graph, and verifies it. No Python setup —
the plugin bootstraps its own environment via uv.
Any other agent (opencode, Cursor, Zed, Codex…)
uv tool install phpkg # or: pipx install phpkg
phpkg init && phpkg index # in your PHP project
phpkg mcp-config opencode --write
mcp-config supports claude-code, opencode, cursor, zed, codex and
generic. It merges into an existing config rather than overwriting it.
What it indexes
Both stacks, one graph. Many legacy PHP estates run a monolith and a newer framework over the same database. phpkg joins them on physical table nodes, so you can ask what no single-stack tool can answer: if I change this repository, what on the other side of the codebase reads the tables it writes?
| CakePHP 2/3 | Controllers, actions, models, behaviors, components, helpers, templates, routes, shells |
| Laravel | Controllers, Eloquent models, repositories, services, providers, routes, form requests, observers, container bindings |
| Shared | DbTable nodes — the join between stacks |
Convention-driven CakePHP and declaration-driven Laravel get separate resolvers, because they establish structure in opposite directions.
Commands
phpkg init # detect stacks, write phpkg.yaml
phpkg index # build the graph
phpkg watch # incremental reindex on file changes
phpkg serve # start the MCP server
phpkg status # generation, index age, node/edge counts
phpkg summarize # plain-English summaries (see below)
phpkg mcp-config # emit MCP config for a given agent
Reindexing while the server is live
Kuzu takes an exclusive cross-process lock even read-only, so "server running"
and "indexer running" cannot share one database file. phpkg writes each index
run to a new generation and atomically flips a marker; the live server
reopens on its next tool call. phpkg index is safe to run at any time, and
phpkg watch is safe to leave running.
Semantic summaries (optional, no API key)
The graph knows where a method is and what calls it — never what it does.
phpkg summarize fills that in, and your agent writes the prose, so there's
no API key and no per-run cost:
phpkg summarize next <module> -o batch.json # methods + source out
# agent writes {id: sentence} into answers.json
phpkg summarize apply answers.json # summaries in
Summaries survive reindexing for unchanged methods, so a re-run only costs
effort for code that actually changed. Read them back with describe_symbol /
describe_module. The module-level links (cross-module calls, shared tables)
are derived mechanically from existing edges — those cost nothing and work
immediately.
Configuration
phpkg init writes phpkg.yaml. Detection reads composer.json constraints and
layout markers; it never infers a CakePHP major version from a directory name,
because the wrong extractor produces a confidently empty graph rather than an
error. If detection reports ambiguous, set stack: yourself.
repos:
- name: legacy-monolith
root: .
stack: cake2 # cake2 | cake3 | laravel
app_path: app
exclude: [app/Vendor, vendor, app/tmp]
- name: api
root: app/laravel # nested apps are normal
stack: laravel
app_path: app
psr4: { "App\\": "app/" }
When not to use it
Grep is still the right tool for string literals, config values, comments, migrations and template bodies — anything that isn't a PHP symbol. phpkg replaces grep for structure, not for text.
Requirements
Python 3.10–3.13 (Kuzu and tree-sitter have no 3.14 wheels yet). No PHP runtime needed — parsing is static.
Releasing
Publishing runs on PyPI Trusted Publishing (OIDC) — no API token is stored anywhere. Tag and push:
git tag v0.1.0 && git push origin v0.1.0
CI runs the test matrix plus a clean-install job that builds the wheel,
installs it with freshly resolved dependencies, and indexes a real project.
That job exists because the dev tree pins dependencies via uv.lock and
therefore cannot catch a break introduced by a newer release of one — which
is exactly how xxhash 4.0 once produced a silently empty graph.
Status & contributing
Beta. Built against a ~60k-node, ~100k-edge production codebase; broader framework-version coverage is exactly where outside testing helps most. Issues and PRs welcome — especially CakePHP 3/4 and non-standard Laravel layouts.
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file phpkg-0.1.0.tar.gz.
File metadata
- Download URL: phpkg-0.1.0.tar.gz
- Upload date:
- Size: 326.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5df77bafe8831062a64c2d211ef094e0297db826cd4914bdeda6e6fee99acb13
|
|
| MD5 |
15111cf312f9608b4f2a72355ae861b8
|
|
| BLAKE2b-256 |
cd50934d0b45b743a6e8dc1999bdab9545aed147b6765ef951e4904d256308d6
|
Provenance
The following attestation bundles were made for phpkg-0.1.0.tar.gz:
Publisher:
publish.yml on ArcXzost/phpkg
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
phpkg-0.1.0.tar.gz -
Subject digest:
5df77bafe8831062a64c2d211ef094e0297db826cd4914bdeda6e6fee99acb13 - Sigstore transparency entry: 2562808582
- Sigstore integration time:
-
Permalink:
ArcXzost/phpkg@eef4bf6e84c09f91573312a988ca20598611c360 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/ArcXzost
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@eef4bf6e84c09f91573312a988ca20598611c360 -
Trigger Event:
push
-
Statement type:
File details
Details for the file phpkg-0.1.0-py3-none-any.whl.
File metadata
- Download URL: phpkg-0.1.0-py3-none-any.whl
- Upload date:
- Size: 159.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
69fc7f50f142a34874785a823c979d7c8cc86b8c66cda406f4d5f6000acd753b
|
|
| MD5 |
9c51b9a07d797d7b707f7cc44cca8423
|
|
| BLAKE2b-256 |
0e5f89f3f60318a61fe879cb1145913af1bd1ef59f84dab4483d62c24555fc67
|
Provenance
The following attestation bundles were made for phpkg-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on ArcXzost/phpkg
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
phpkg-0.1.0-py3-none-any.whl -
Subject digest:
69fc7f50f142a34874785a823c979d7c8cc86b8c66cda406f4d5f6000acd753b - Sigstore transparency entry: 2562808862
- Sigstore integration time:
-
Permalink:
ArcXzost/phpkg@eef4bf6e84c09f91573312a988ca20598611c360 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/ArcXzost
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@eef4bf6e84c09f91573312a988ca20598611c360 -
Trigger Event:
push
-
Statement type: