@andygo.dev/cakephp-harness
v1.0.2
Published
Local AI development harness for CakePHP projects: version-aware manual sync, SQLite/FTS5 search, a CakePHP Claude Code Skill, and an MCP server.
Maintainers
Readme
@andygo.dev/cakephp-harness
A local AI development harness for CakePHP projects.
It gives AI coding agents version-aware, local, authoritative CakePHP knowledge, while keeping how to write CakePHP code separate from what the framework actually does:
- Guidance — development conventions, architecture and testing practice
- Roles —
cakephp-expert,cakephp-code-reviewer,cakephp-test-writerandcakephp-planner, which verify APIs against the docs instead of recalling them - Manuals + MCP — the official CakePHP documentation for your version
- Project specs (optional) — your own docs and specs, in a separate corpus
- Config — how this specific project should be developed
The problem it solves: an agent confidently inventing a CakePHP API, or answering a CakePHP 5.4 question from CakePHP 3.x memory.
It works with Claude Code, Cursor, OpenAI Codex CLI, Gemini CLI and OpenCode — one source of guidance, rendered into whatever each tool reads, so a team on mixed tooling cannot end up with two versions of "how we write CakePHP here".
Developer
│ npx @andygo.dev/cakephp-harness setup
▼
CakePHP Harness ── detects version ── installs guidance ── syncs manuals ── indexes ── serves MCP
│
▼
Claude Code · Cursor · Codex · Gemini CLI · OpenCodeRequirements
- Node.js >= 22.13 (uses the built-in
node:sqlite) - A CakePHP project with a
composer.jsonrequiringcakephp/cakephp
No native modules, no compilation, no database server.
Quick start
cd my-cakephp-project
npx @andygo.dev/cakephp-harness setupOr install the CLI once and run the binary:
npm install -g @andygo.dev/cakephp-harness
cd my-cakephp-project
cakephp-harness setupThat detects your CakePHP version, detects which coding agents the project already uses, installs the guidance and roles for each of them, downloads and indexes the matching manuals, and offers to register the MCP server with each agent.
To choose the agents yourself:
cakephp-harness setup --target claude-code --target cursorThen:
cakephp-harness manuals search "middleware queue"1. Middleware › Creating Middleware
Section: Controllers
Version: 5.x (project: 5.4)
URL: https://book.cakephp.org/5/en/controllers/middleware.html#creating-middleware
Id: 5.x:en:controllers/middleware.md#creating-middleware
…MiddlewareQueue { // Add your simple middleware onto the queue …Checking your setup
cakephp-harness doctor✓ CakePHP project detected
✓ CakePHP version: 5.4
✓ Manuals synchronized
✓ Documentation index available
✓ CakePHP Skill installed
✓ Cursor guidance installed
✓ MCP server available
✓ search_cakephp_manual available
✓ get_cakephp_manual available
✓ search_cakephp_api available
Available commands:
cakephp-harness setup Run the full idempotent setup flow
cakephp-harness doctor Check setup health and list available commands
cakephp-harness init Detect the project and create .cakephp-harness/
cakephp-harness manuals sync Download the official CakePHP manuals
cakephp-harness manuals index Build the manual search index
cakephp-harness manuals update Sync manuals and update the index
cakephp-harness manuals search <query> Search the CakePHP manuals
cakephp-harness manuals status Show synchronized and indexed manual status
cakephp-harness manuals versions List documentation lines and local status
cakephp-harness specs index Index this project's own specs
cakephp-harness specs search <query> Search this project's own specs
cakephp-harness specs status Show project spec search status
cakephp-harness targets list List coding agents and their setup state
cakephp-harness targets add <agent> Set this project up for another coding agent
cakephp-harness targets remove <agent> Stop maintaining files for a coding agent
cakephp-harness targets install Reinstall files for every configured agent
cakephp-harness skill install Install the CakePHP guidance
cakephp-harness skill update Update the CakePHP guidance
cakephp-harness agent install Install the CakePHP roles
cakephp-harness agent update Update the CakePHP roles
cakephp-harness mcp start Run the MCP server on stdio
cakephp-harness mcp status Show MCP registration and readinessThe MCP checks are not assertions — doctor stands the server up over an
in-memory transport, lists its tools and calls them, so a tool that is
registered but broken (stale index, version drift) is reported as broken.
Failures print the command that fixes them, and the exit code is non-zero,
which makes it usable as a CI gate. doctor also prints the complete command
catalog so it doubles as command discovery. --json emits the full report,
including the command catalog.
Commands
| Command | What it does |
|---|---|
| setup | Everything below, in one idempotent command |
| doctor | Check setup health and list available commands (--json) |
| init | Detect the project and create .cakephp-harness/ (no downloads) |
| manuals sync | Download the official manuals for your version |
| manuals index | Build the SQLite/FTS5 search index |
| manuals update | Sync, then incrementally reindex — the everyday command |
| manuals search <query> | Search the manuals (--limit, --full, --version) |
| manuals status | What is synced and indexed (--json) |
| manuals versions | Documentation lines and their local status |
| specs index | Index this project's own specs (opt-in; enables spec search) |
| specs search <query> | Search this project's own specs (--limit, --full) |
| specs status | Whether project spec search is enabled and current (--json) |
| targets list | Coding agents, and whether each is set up (--json) |
| targets add <agent> | Set the project up for another agent and install its files |
| targets remove <agent> | Stop maintaining an agent's files (deletes nothing) |
| targets install | Reinstall guidance, roles and MCP for every configured agent |
| skill install / skill update | Install or refresh the guidance, for every agent |
| agent install / agent update | Install or refresh the roles, for every agent |
| mcp start | Run the MCP server on stdio (your coding agent launches this) |
| mcp status | Registration per agent and documentation readiness (--json) |
Add --verbose to any command for diagnostics and stack traces. setup,
targets install, skill install and agent install accept --target <agent>
(repeatable) to work on one agent at a time.
Every command is safe to run repeatedly. manuals update detects that nothing
changed and does no work; nothing will clobber your local edits.
What gets created
Always:
.cakephp-harness/
├── config.json project configuration
├── manuals/cakephp-5.x/ synced Markdown + .meta.json
├── index/docs.sqlite FTS5 search index (framework manual)
├── index/specs.sqlite FTS5 search index (project specs, optional)
├── targets/<agent>.json what the harness installed, per agent
└── cache/ download cacheThen, per coding agent — only for the ones your project is set up for:
Claude Code .claude/skills/cakephp/ SKILL.md + references/
.claude/agents/*.md four subagents
.mcp.json
Cursor .cursor/rules/cakephp.mdc rule, auto-attached to **/*.php
.cursor/commands/*.md four role playbooks
.cursor/mcp.json
OpenAI Codex CLI AGENTS.md a marked-off block, merged in
.codex/config.toml
Gemini CLI GEMINI.md a marked-off block, merged in
.gemini/commands/cakephp/*.toml /cakephp:expert, …
.gemini/settings.json
OpenCode AGENTS.md a marked-off block, merged in
.opencode/agent/*.md four subagents
opencode.jsonAgents that do not keep guidance in a self-contained directory share one copy of
the reference documents at .cakephp-harness/instructions/references/, which
their guidance file links to.
MCP registration files are only written if you approve the prompt.
The npm package contains the tooling. Documentation is downloaded locally by
manuals sync, never bundled.
Package name:
@andygo.dev/cakephp-harness.
The installed CLI binary is still cakephp-harness.
Coding agents
Which agents a project is set up for is recorded in config.json as targets,
and init proposes what it can detect (.cursor/, .codex/, GEMINI.md,
opencode.json, …). Detection only ever informs a new config — once the list
is recorded it is your decision, and adding .cursor/ to a repository will not
silently start writing Cursor files.
cakephp-harness targets listAgent Set up Detected MCP
claude-code yes yes yes
cursor yes yes yes
codex — yes —
gemini — — —
opencode — — —cakephp-harness targets add codex
cakephp-harness targets remove cursortargets remove stops maintaining an agent's files; it never deletes them. They
are in your repository, possibly committed and possibly edited, and quietly
deleting them because a config list changed is not a trade this tool makes. It
prints exactly what was left behind.
One source of guidance
The conventions are written once, in this package's Skill templates, and rendered per agent. The four roles are written once as subagent definitions and re-expressed as whatever the tool actually supports:
| Agent | Roles become | Permissions |
|---|---|---|
| Claude Code | subagents in .claude/agents/ | native tools: list |
| OpenCode | subagents in .opencode/agent/ | translated to tools: {write: false, …} |
| Gemini CLI | commands — /cakephp:expert, … | not expressible |
| Cursor | commands in .cursor/commands/ | not expressible |
| OpenAI Codex CLI | playbook documents it is pointed at | not expressible |
Two details there are load-bearing. Claude Code namespaces MCP tools as
mcp__<server>__<tool> and other clients do not, so the prefix is stripped for
them — a role telling Gemini CLI to call mcp__cakephp-docs__search_cakephp_manual
would simply never look anything up. And the reviewer's read-only restriction is
translated rather than dropped where the syntax differs; where a tool cannot
express it at all, that is stated rather than assumed.
AGENTS.md is shared by Codex and OpenCode, so a project set up for both gets
one block describing both, rather than each overwriting the other's on every
run.
MCP tools
Once registered, your coding agent gains these tools:
| Tool | Purpose |
|---|---|
| search_cakephp_manual | Ranked search; returns compact excerpts + documentId |
| get_cakephp_manual | Full document text for a documentId |
| search_cakephp_api | Look up a class or method |
| search_project_specs | Search this project's own specs (optional, see below) |
| get_project_spec | Full text of one of this project's spec documents |
Results are deliberately small — title, section, version, URL, excerpt,
documentId — so a search never floods the context window. The agent fetches
full documents only when it needs them.
Project specs (optional)
Beyond the framework manual, the harness can index your project's own specs, design notes and ADRs — the knowledge that explains how this application is meant to behave.
It is opt-in. Nothing scans your repository until you run:
cakephp-harness specs index
cakephp-harness specs search "invoice numbering"1. Billing Rules › Invoice Numbering
Section: docs
File: docs/billing.md#invoice-numbering
Id: spec:docs/billing.md#invoice-numbering
Invoice numbers use the prefix ACME- followed by a zero-padded sequence…Which files count is configurable:
"specs": {
"enabled": true,
"include": ["docs/**/*.md", "specs/**/*.md", "*.md"],
"exclude": ["vendor/**", "node_modules/**", ".cakephp-harness/**", ".claude/**",
".cursor/**", ".codex/**", ".gemini/**", ".opencode/**",
"AGENTS.md", "CLAUDE.md", "GEMINI.md"]
}Discovery prunes excluded directories rather than walking them, skips symlinks so it cannot escape the project, and indexes incrementally by content hash like the manual does.
The excludes cover every file the harness installs for a coding agent. Those
hold framework guidance, and indexing them here would let CakePHP conventions
come back out of search_project_specs dressed as this project's own
requirements.
The two corpora never mix. Project specs live in their own SQLite database
(index/specs.sqlite) with their own tools, so a project design note cannot be
returned by search_cakephp_manual — that separation is structural, not a
filter that could be got wrong. The tool descriptions and the server
instructions both state which corpus is which, so an agent does not present
your internal ADR as CakePHP framework behaviour.
The CakePHP roles
setup installs four roles for every coding agent the project is set up for:
| Role | Does | Tools |
|---|---|---|
| cakephp-expert | Implements and refactors CakePHP code | full (reads, edits, runs) |
| cakephp-code-reviewer | Reviews CakePHP code for defects | read-only + MCP lookups |
| cakephp-test-writer | Writes and repairs tests | read/write + Bash + MCP lookups |
| cakephp-planner | Plans features, refactors and migrations before implementation | read-only + MCP lookups |
In Claude Code and OpenCode they are subagents:
> use the cakephp-expert agent to add soft deletes to the Articles table
> use the cakephp-planner agent to plan the billing refactor
> use the cakephp-code-reviewer agent on my changes
> use the cakephp-test-writer agent to cover ArticlesTableIn Gemini CLI they are commands (/cakephp:expert, /cakephp:code-reviewer,
/cakephp:test-writer, /cakephp:planner); in Cursor, commands in
.cursor/commands/; in Codex, playbook documents its AGENTS.md block points
at.
All four share one defining rule: verify framework APIs against the documentation before asserting them. For the expert that means searching before writing; for the planner it means grounding implementation steps in this project's actual CakePHP version; for the reviewer it means confirming an API really is wrong before flagging it — a review that confidently flags correct code is worse than no review; for the test writer it means checking that an assertion actually exists in this version before relying on it.
The reviewer is restricted to Read, Grep, Glob, Bash plus the three MCP
tools, so it cannot rewrite the code it is reviewing — translated to
write: false, edit: false for OpenCode, and stated in the prose for tools that
cannot enforce it. It reports findings as
Critical / Warning / Suggestion with file:line and a concrete fix, covering
CakePHP-specific defects: mass assignment via $_accessible, interpolated SQL
conditions, N+1 from missing contain(), uniqueness checks misplaced in
validation instead of buildRules(), unchecked save() returns, fat
controllers, and missing CSRF tokens in integration tests.
The test writer carries two hard rules that tool permissions cannot express:
it never edits production code to make a test pass (a failing test it wrote is a
bug found, and it reports it instead), and it never claims a suite passes
without actually running it. It knows the CakePHP 5 testing surface —
IntegrationTestTrait, typed protected array $fixtures, enableCsrfToken(),
getTableLocator() — and is told to cover failure paths, not just happy paths.
In Claude Code, tool names are namespaced by your MCP server name
(mcp__cakephp-docs__search_cakephp_manual), so all four roles are rendered with
the mcp.serverName from your config at install time — rename the server and
agent update rewires them.
Unlike the ambient guidance, a subagent runs in a separate context with its own tool budget. Use the guidance for everyday CakePHP work; reach for a role on larger, self-contained tasks.
Manual registration
setup asks before touching any MCP configuration file. To do it yourself, in
.mcp.json (Claude Code), .cursor/mcp.json (Cursor) or .gemini/settings.json
(Gemini CLI):
{
"mcpServers": {
"cakephp-docs": {
"command": "npx",
"args": ["-y", "@andygo.dev/cakephp-harness", "mcp", "start"]
}
}
}In opencode.json (OpenCode):
{
"mcp": {
"cakephp-docs": {
"type": "local",
"command": ["npx", "-y", "@andygo.dev/cakephp-harness", "mcp", "start"],
"enabled": true
}
}
}In .codex/config.toml (Codex CLI — it also reads ~/.codex/config.toml):
[mcp_servers.cakephp-docs]
command = "npx"
args = ["-y", "@andygo.dev/cakephp-harness", "mcp", "start"]Existing servers in these files are never modified, and an entry for our own server that you have customised is left alone.
Version safety
This is the point of the tool, so it is strict.
Claude → MCP → project config → CakePHP version → version-specific index → searchCakePHP publishes documentation per major line — there is no 5.4 branch;
the 5.x branch is the documentation for 5.4. So the harness records your
project version (5.4) and maps it to a corpus (5.x), and never crosses a
major-version boundary.
If the right documentation is not available, you get an error, not a guess:
✖ CakePHP 4.5 documentation has not been synchronized (corpus: cakephp-4.x, language: en).
Run:
cakephp-harness manuals sync
cakephp-harness manuals index
Indexed documentation for other versions is present but will not be used:
cakephp-5.x (en, 1066 documents)Version detection prefers composer.lock (exact, 5.4.2) and falls back to the
composer.json constraint (^5.4).
How it works
Sync asks GitHub for the head commit of the cakephp/docs branch for your
major line. If it matches what you have, nothing is downloaded. Otherwise it
pulls the branch tarball once and extracts only docs/<lang>/**/*.md.
Indexing splits each page into one document per ## section — a section is
the unit a developer actually wants back, and whole pages rank badly and blow up
context. Each chunk is content-hashed, so re-indexing only touches what changed.
Search is BM25 via SQLite FTS5 by default, with title and heading weighted
above body text. Queries are tokenised and re-quoted before they reach FTS5, so
Cake\ORM\Table, find() and $this->Articles work rather than throwing
syntax errors. The search widens in stages: all terms → any term → prefix.
Hybrid search (optional) blends that BM25 ranking with semantic
similarity from local embeddings, combined by reciprocal rank fusion — a query
phrased nothing like the manual's own wording ("how do I avoid mass
assignment vulnerabilities") can still surface the right section. See
Hybrid search below.
Storage is behind a repository interface so another backend can be added later.
Hybrid search (optional)
By default, search is BM25 only — lexical, offline, no extra dependency. Set
index.searchStrategy to "hybrid" in .cakephp-harness/config.json to also
rank by semantic similarity from a local embedding model, blended with BM25 by
reciprocal rank fusion:
"index": {
"searchStrategy": "hybrid",
"embeddingModel": "Xenova/all-MiniLM-L6-v2"
}Then reindex — hybrid search needs embeddings to search against, not just the FTS5 index:
cakephp-harness manuals update
cakephp-harness specs indexThis works on an index you already have: nothing needs to change on disk for the embeddings to be filled in, and neither command re-downloads or re-parses anything it does not have to. It is also resumable — if the run is interrupted, everything embedded so far is kept and the next run picks up exactly what is still missing.
manuals status reports readiness (1058/1058 documents embedded), and
manuals search / search_cakephp_manual refuse to run hybrid search with a
message telling you to reindex, rather than silently falling back to bm25,
unless every document in the corpus has an embedding for the configured
model. Reading a document by id (get_cakephp_manual) and doctor never
require embeddings, so neither is affected while a corpus is still filling in.
Why bother: BM25 only ever matches vocabulary that is actually in the query.
A query phrased in the developer's own words — "how do I avoid mass
assignment vulnerabilities" — has almost no token overlap with the manual's
own heading, Mass Assignment, but hybrid search still ranks it first,
because the embeddings capture that they mean the same thing.
What's actually running: a small sentence-embedding model
(Xenova/all-MiniLM-L6-v2 by default) via
@huggingface/transformers —
transformers.js, a local WASM/ONNX runtime. No API key, no server, no
outbound calls per query. The model downloads once on first use
(a few tens of MB), reporting progress as it goes, and is cached after that.
Trade-offs worth knowing before you opt in:
- It is the one path in this package that is not "no native modules": in
Node, transformers.js runs its ONNX graph through
onnxruntime-node, a small prebuilt (not compiled) native addon. The defaultbm25strategy is entirely unaffected — this only loads ifsearchStrategyishybrid. @huggingface/transformersis listed as anoptionalDependenciesentry specifically so abm25-only install never has to carry it. It pulls inonnxruntime-nodeandsharp(image handling the text-embedding path here never uses), both of which currently have open, unpatched high-severity advisories in their dependency chains at the time of writing — checknpm auditbefore deciding whether that is acceptable for your project.- Indexing a full manual corpus (~1000 chunks) takes tens of seconds longer than bm25-only, since every added or changed chunk needs an embedding. A chunk that is unchanged and already has a vector for the configured model is never re-embedded.
- Similarity is a brute-force cosine scan over stored vectors at query time — fine at the corpus sizes this tool deals with (low thousands of documents), deliberately not a dedicated ANN index for a problem this size does not have.
Security
Downloaded documentation is untrusted input:
- extracted entries must be regular Markdown files under
docs/<lang>/ - absolute paths,
..segments, symlinks and hardlinks are rejected - every destination is verified to resolve inside the manuals directory
- content is only ever stored and displayed — never executed, never interpolated into a shell command, never able to influence control flow
Programmatic use
import { detectCakePhpVersion, openCorpus, searchManuals } from '@andygo.dev/cakephp-harness';The version model, config, sync, index, search, MCP server and the target installers are all exported. To set a project up for an agent from your own tooling:
import { installTargets, registerTargetServer, TARGETS } from '@andygo.dev/cakephp-harness';
await installTargets(['claude-code', 'cursor'], { root, serverName: 'cakephp-docs' });
await registerTargetServer(root, TARGETS.cursor, 'cakephp-docs');Development
npm install
npm run build
npm test # fully offline (hybrid search is tested against a fake embedding provider)
npm run typecheckLicense
MIT
