Skip to content

Commit 56ecaac

Browse files
ifahimrezaclaude
andauthored
chore: record the vendored adapter's one deviation as a script (#101)
CLAUDE.md says twice, in bold, that includes/lib/wp-mcp/ is vendored and never hand-edited. It has been hand-edited: 147 i18n text domains across 18 files were rewritten from `mcp-adapter` to `saddle`. The rewrite itself is defensible — the self-hosted build is the only one that ships the library (the .org zip excludes the whole directory), and showing translators two text domains for one plugin is worse than the deviation. What was not defensible is that it existed only as edits in the tree. Drop in a fresh upstream release and it is gone, silently, because nothing fails. So it is now written down and re-runnable. Re-vendoring is: replace the directory, run scripts/revendor-wp-mcp.php, run the suite. `--check` makes no changes and exits non-zero when work is pending, which is the pre-release guard against exactly the silent-revert case. Verified by reproduction rather than assertion: reverting the whole library to a pristine text domain and re-running the script restores all 147 occurrences and leaves includes/lib/wp-mcp byte-identical to what main carries. The script rewrites ONLY the text-domain argument, recognised by the closing parenthesis that follows it. `'mcp-adapter'` also appears five times as an ability category and as the adapter's own server id; those are identifiers that upstream code looks up by name, and renaming them would break lookups. That distinction is why a blanket find-and-replace would have been wrong, and it is the reason this needed to be a script rather than a sed one-liner in a comment. scripts/ is excluded from both build channels — the src list is `['**', …]` with exclusions, so a new top-level directory ships by default unless it is named. Verified against the built zip. Claude-Session: https://claude.ai/code/session_01GkZr73cqaSesHRDG89Yy8S Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 3c51141 commit 56ecaac

3 files changed

Lines changed: 126 additions & 0 deletions

File tree

CLAUDE.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,15 @@ change. `Tested up to:` moves only when Fahim has verified against a newer WP.
352352
- Don't wire up `Saddle_Ecosystem`.
353353
- Don't put licensing, upsell or builder-specific code in free.
354354
- Don't edit `includes/lib/wp-mcp/` — it is vendored. Fix upstream and re-vendor.
355+
**One deviation exists and is recorded, not hand-applied:** every i18n text
356+
domain in there is rewritten to `saddle`, so the self-hosted build (the only
357+
one that ships the library) shows translators one domain instead of two.
358+
That rewrite lives in `scripts/revendor-wp-mcp.php`. Re-vendoring is: drop in
359+
upstream, run the script, run the suite. `--check` reports pending work and
360+
exits non-zero — run it before a release, because a fresh upstream copy
361+
silently reverts the rewrite and nothing else would notice. The script
362+
touches only the text-domain *argument*; `'mcp-adapter'` also appears as an
363+
ability category and the adapter's server id, and those are identifiers.
355364

356365
---
357366

Gruntfile.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ module.exports = function ( grunt ) {
8181
// config, not plugin code. Excluded explicitly rather
8282
// than relying on how the globber treats dot-dirs.
8383
'!.agents/**',
84+
// Developer CLI tooling — re-vendoring helpers and
85+
// the like. Never plugin code.
86+
'!scripts/**',
8487
'!Gruntfile.js',
8588
'!package.json',
8689
'!package-lock.json',

scripts/revendor-wp-mcp.php

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
#!/usr/bin/env php
2+
<?php
3+
/**
4+
* Re-apply Saddle's one recorded change to the vendored MCP adapter.
5+
*
6+
* `includes/lib/wp-mcp/` is vendored and must not be hand-edited — CLAUDE.md
7+
* says so twice. Exactly one deviation is nevertheless carried, deliberately:
8+
* every i18n text domain inside it is rewritten from `mcp-adapter` to `saddle`,
9+
* so the self-hosted build (the only build that ships the library at all — the
10+
* WordPress.org zip excludes it) exposes one text domain to translators rather
11+
* than two.
12+
*
13+
* That deviation was applied by hand, and a hand-applied change to a vendored
14+
* tree is lost the moment someone drops in a fresh upstream copy — silently,
15+
* because nothing fails. This script is the change, written down and
16+
* re-runnable, so re-vendoring is:
17+
*
18+
* 1. replace includes/lib/wp-mcp/ with the upstream release
19+
* 2. php scripts/revendor-wp-mcp.php
20+
* 3. composer test && composer lint
21+
*
22+
* It is idempotent: running it on an already-patched tree reports zero
23+
* changes. `--check` makes no changes and exits non-zero if any are pending,
24+
* which is what to run before a release.
25+
*
26+
* WHAT IT DELIBERATELY DOES NOT TOUCH: `'mcp-adapter'` also appears as an
27+
* ability category and as the adapter's own server id. Those are identifiers,
28+
* not translatable strings, and rewriting them would rename things upstream
29+
* code looks up by name. Only the text-domain ARGUMENT is rewritten — the
30+
* last argument of an i18n call, recognised by the closing parenthesis that
31+
* follows it.
32+
*
33+
* Not shipped: `scripts/` is excluded from both build channels.
34+
*
35+
* @package Saddle
36+
*/
37+
38+
// This is a developer CLI tool, not plugin code — it runs outside WordPress.
39+
if ( 'cli' !== PHP_SAPI ) {
40+
fwrite( STDERR, "This script is CLI-only.\n" );
41+
exit( 1 );
42+
}
43+
44+
$saddle_lib = dirname( __DIR__ ) . '/includes/lib/wp-mcp';
45+
46+
if ( ! is_dir( $saddle_lib ) ) {
47+
// The WordPress.org build has no vendored library, and neither does a
48+
// checkout that never had one. Nothing to do is a success, not a failure.
49+
fwrite( STDOUT, "No vendored adapter at includes/lib/wp-mcp — nothing to patch.\n" );
50+
exit( 0 );
51+
}
52+
53+
$saddle_check_only = in_array( '--check', $argv, true );
54+
55+
/**
56+
* Only ever the text-domain argument: a quoted 'mcp-adapter' whose next
57+
* non-whitespace character closes the call. An ability category or a server
58+
* id is followed by a comma, so neither matches.
59+
*/
60+
$saddle_pattern = "/'mcp-adapter'(\s*\))/";
61+
62+
$saddle_files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $saddle_lib ) );
63+
$saddle_touched = array();
64+
$saddle_total = 0;
65+
66+
foreach ( $saddle_files as $saddle_file ) {
67+
if ( ! $saddle_file->isFile() || 'php' !== strtolower( $saddle_file->getExtension() ) ) {
68+
continue;
69+
}
70+
71+
$saddle_path = $saddle_file->getPathname();
72+
$saddle_contents = file_get_contents( $saddle_path );
73+
if ( false === $saddle_contents ) {
74+
fwrite( STDERR, sprintf( "Could not read %s\n", $saddle_path ) );
75+
exit( 1 );
76+
}
77+
78+
$saddle_count = 0;
79+
$saddle_patched = preg_replace( $saddle_pattern, "'saddle'\$1", $saddle_contents, -1, $saddle_count );
80+
81+
if ( ! $saddle_count ) {
82+
continue;
83+
}
84+
85+
$saddle_total += $saddle_count;
86+
$saddle_touched[] = sprintf( '%s (%d)', substr( $saddle_path, strlen( $saddle_lib ) + 1 ), $saddle_count );
87+
88+
if ( ! $saddle_check_only && false === file_put_contents( $saddle_path, $saddle_patched ) ) {
89+
fwrite( STDERR, sprintf( "Could not write %s\n", $saddle_path ) );
90+
exit( 1 );
91+
}
92+
}
93+
94+
sort( $saddle_touched );
95+
96+
if ( ! $saddle_total ) {
97+
fwrite( STDOUT, "Vendored adapter already carries Saddle's text domain — nothing to do.\n" );
98+
exit( 0 );
99+
}
100+
101+
fwrite(
102+
STDOUT,
103+
sprintf(
104+
"%s %d text-domain occurrence(s) across %d file(s):\n %s\n",
105+
$saddle_check_only ? 'PENDING:' : 'Rewrote',
106+
$saddle_total,
107+
count( $saddle_touched ),
108+
implode( "\n ", $saddle_touched )
109+
)
110+
);
111+
112+
// --check is for CI and pre-release: pending work is a failure there, because
113+
// it means the tree was re-vendored and this was never re-run.
114+
exit( $saddle_check_only ? 1 : 0 );

0 commit comments

Comments
 (0)