Metadata-Version: 2.5
Name: phpkg
Version: 0.1.0
Summary: Code knowledge graph for large legacy PHP codebases (CakePHP 2/3 + Laravel), with an MCP server for coding agents
Project-URL: Homepage, https://github.com/ArcXzost/phpkg
Project-URL: Repository, https://github.com/ArcXzost/phpkg
Project-URL: Issues, https://github.com/ArcXzost/phpkg/issues
License-Expression: MIT
License-File: LICENSE
Keywords: cakephp,claude,code-search,knowledge-graph,laravel,legacy-code,mcp,model-context-protocol,php,static-analysis
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: PHP
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Documentation
Classifier: Topic :: Software Development :: Quality Assurance
Requires-Python: <3.14,>=3.10
Requires-Dist: click>=8.1.8
Requires-Dist: fastmcp>=2.3
Requires-Dist: graspologic>=0.3.4
Requires-Dist: kuzu==0.11.3
Requires-Dist: networkx>=3.4
Requires-Dist: orjson>=3.10
Requires-Dist: platformdirs>=4.3
Requires-Dist: pydantic>=2.11
Requires-Dist: pyyaml>=6.0
Requires-Dist: rank-bm25>=0.2.2
Requires-Dist: rich>=13.9
Requires-Dist: setuptools<81
Requires-Dist: tree-sitter-php>=0.23.4
Requires-Dist: tree-sitter==0.25.2
Requires-Dist: watchdog>=6.0.0
Requires-Dist: xxhash>=3.5
Provides-Extra: embeddings
Requires-Dist: numpy>=1.26; extra == 'embeddings'
Requires-Dist: openai>=1.30; extra == 'embeddings'
Description-Content-Type: text/markdown

# 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…)

```bash
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

```bash
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:

```bash
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.

```yaml
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:

```bash
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
