-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathPromptWire.module.php
More file actions
154 lines (137 loc) · 5.71 KB
/
Copy pathPromptWire.module.php
File metadata and controls
154 lines (137 loc) · 5.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
<?php namespace ProcessWire;
/**
* PromptWire: ProcessWire ↔ Cursor MCP Bridge
*
* This module serves as the ProcessWire component of the PromptWire bridge,
* exposing ProcessWire's structure and content to Cursor IDE via the
* Model Context Protocol (MCP).
*
* Provides full read/write access: site inspection, content sync (pull/push),
* file sync, schema sync, page management, and cross-environment deployment.
*
* The module itself is minimal — most functionality is provided via
* the CLI interface (bin/promptwire.php) which can be invoked by the
* MCP server running in Node.js.
*
* @package PromptWire
* @author Peter Knight <https://www.peterknight.digital>
* @license MIT
* @version 1.12.6
* @link https://github.com/PeterKnightDigital/PromptWire-MCP
*
* @see /bin/promptwire.php CLI entrypoint
* @see /src/Cli/CommandRouter Command routing and execution
* @see /src/Schema/* Schema export classes
* @see /src/Query/* Page query classes
*/
class PromptWire extends WireData implements Module {
public static function getModuleInfo(): array {
return [
'title' => 'PromptWire',
'summary' => 'ProcessWire ↔ Cursor MCP Bridge for AI-assisted development',
'version' => '1.12.6',
'author' => 'Peter Knight',
'href' => 'https://github.com/PeterKnightDigital/PromptWire-MCP',
'singular' => true,
'autoload' => true,
'icon' => 'plug',
'requires' => 'ProcessWire>=3.0.0',
'installs' => ['ProcessPromptWireAdmin'],
];
}
public function ___install() {
$this->cleanupLegacyStructure();
$this->installAdminModule();
}
public function ___upgrade($fromVersion, $toVersion) {
$this->cleanupLegacyStructure();
$this->installAdminModule();
}
private function installAdminModule(): void {
$modules = $this->wire('modules');
if ($modules->isInstalled('ProcessPromptWireAdmin')) return;
try {
$modules->resetCache();
$modules->install('ProcessPromptWireAdmin');
$this->message('Installed PromptWire Admin dashboard — find it under Setup → PromptWire Admin.');
} catch (\Exception $e) {
$this->warning('Could not auto-install ProcessPromptWireAdmin: ' . $e->getMessage());
}
}
/**
* Detect and remove legacy module directories from pre-1.5 installs.
*/
private function cleanupLegacyStructure(): void {
$modulesPath = $this->wire('config')->paths->siteModules;
$modules = $this->wire('modules');
// Remove old PwMcpAdmin standalone directory
$oldAdminPath = $modulesPath . 'PwMcpAdmin/';
if (is_dir($oldAdminPath) && file_exists($oldAdminPath . 'ProcessPwMcpAdmin.module.php')) {
if ($modules->isInstalled('ProcessPwMcpAdmin')) {
try { $modules->uninstall('ProcessPwMcpAdmin'); } catch (\Exception $e) {}
}
$this->removeDirectoryRecursive($oldAdminPath);
$this->message('Removed legacy site/modules/PwMcpAdmin/ directory.');
}
// Remove old PwMcp directory if PromptWire is installed alongside it
$oldMcpPath = $modulesPath . 'PwMcp/';
if (is_dir($oldMcpPath) && file_exists($oldMcpPath . 'PwMcp.module.php')) {
if ($modules->isInstalled('PwMcp')) {
try { $modules->uninstall('PwMcp'); } catch (\Exception $e) {}
}
$this->removeDirectoryRecursive($oldMcpPath);
$this->message('Removed legacy site/modules/PwMcp/ directory.');
}
}
private function removeDirectoryRecursive(string $dir): void {
if (!is_dir($dir)) return;
$items = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS),
\RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($items as $item) {
$item->isDir() ? rmdir($item->getRealPath()) : unlink($item->getRealPath());
}
rmdir($dir);
}
public function init() {
$flagFile = $this->wire('config')->paths->assets . 'cache/maintenance.flag';
if (!file_exists($flagFile)) return;
if ($this->wire('user')->isSuperuser()) return;
if (php_sapi_name() === 'cli') return;
$uri = $_SERVER['REQUEST_URI'] ?? '';
$apiPatterns = ['promptwire', 'pw-mcp', 'pw-bridge'];
foreach ($apiPatterns as $pattern) {
if (stripos($uri, $pattern) !== false) return;
}
$customPage = __DIR__ . '/maintenance.html';
header('HTTP/1.1 503 Service Temporarily Unavailable');
header('Retry-After: 300');
header('Content-Type: text/html; charset=utf-8');
if (file_exists($customPage)) {
readfile($customPage);
} else {
echo '<html><head><title>Maintenance</title></head><body>';
echo '<h1>Site is undergoing maintenance</h1>';
echo '<p>We\'ll be back shortly.</p>';
echo '</body></html>';
}
exit;
}
public function getPwVersion(): string {
return $this->wire('config')->version;
}
public function getSiteName(): string {
return $this->wire('config')->httpHost ?: 'ProcessWire Site';
}
public function isLoaded(): bool {
return true;
}
public function getCounts(): array {
return [
'templates' => $this->wire('templates')->getAll()->count(),
'fields' => $this->wire('fields')->getAll()->count(),
'pages' => $this->wire('pages')->count('include=all'),
];
}
}