myaidev-method 0.2.22 → 0.2.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/USER_GUIDE.md +453 -48
  2. package/bin/cli.js +18 -0
  3. package/content-rules.example.md +80 -0
  4. package/dist/mcp/mcp-launcher.js +237 -0
  5. package/dist/server/.tsbuildinfo +1 -1
  6. package/dist/server/auth/layers.d.ts +1 -1
  7. package/dist/server/auth/services/AuthService.d.ts +1 -1
  8. package/dist/server/auth/services/TokenService.js.map +1 -1
  9. package/dist/server/auth/services/example.d.ts +5 -5
  10. package/package.json +16 -16
  11. package/src/index.js +21 -8
  12. package/src/lib/update-manager.js +2 -1
  13. package/src/lib/visual-config-utils.js +321 -295
  14. package/src/lib/visual-generation-utils.js +1000 -811
  15. package/src/scripts/configure-wordpress-mcp.js +8 -3
  16. package/src/scripts/generate-visual-cli.js +365 -235
  17. package/src/scripts/ping.js +250 -0
  18. package/src/scripts/wordpress/publish-to-wordpress.js +165 -0
  19. package/src/server/auth/services/TokenService.ts +1 -1
  20. package/src/templates/claude/agents/content-rules-setup.md +657 -0
  21. package/src/templates/claude/agents/content-writer.md +328 -1
  22. package/src/templates/claude/agents/visual-content-generator.md +182 -4
  23. package/src/templates/claude/commands/myai-configure.md +1 -1
  24. package/src/templates/claude/commands/myai-content-rules-setup.md +204 -0
  25. package/src/templates/codex/commands/myai-content-rules-setup.md +85 -0
  26. package/src/templates/gemini/commands/myai-content-rules-setup.toml +57 -0
  27. package/.claude/mcp/sparc-orchestrator-server.js +0 -607
  28. package/.claude/mcp/wordpress-server.js +0 -1277
  29. package/src/agents/content-writer-prompt.md +0 -164
  30. package/src/agents/content-writer.json +0 -70
  31. package/src/templates/claude/mcp_config.json +0 -74
  32. package/src/templates/claude/slash_commands.json +0 -166
  33. package/src/templates/scripts/configure-wordpress-mcp.js +0 -181
@@ -0,0 +1,250 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { exec } from "child_process";
4
+ import { promisify } from "util";
5
+ import os from "os";
6
+
7
+ const execAsync = promisify(exec);
8
+
9
+ /**
10
+ * Location to IP mapping
11
+ */
12
+ export const LOCATIONS = {
13
+ "ashburn-va": "8.8.8.8", // Google DNS (East Coast reference)
14
+ "los-angeles": "1.1.1.1", // Cloudflare DNS (West Coast reference)
15
+ chicago: "8.8.4.4", // Google DNS secondary
16
+ europe: "1.0.0.1", // Cloudflare secondary
17
+ asia: "208.67.222.222", // OpenDNS
18
+ amsterdam: "213.165.253.121", // aditya's ramnode vm
19
+ };
20
+
21
+ /**
22
+ * Get device's local IP
23
+ */
24
+ function getLocalIP() {
25
+ const interfaces = os.networkInterfaces();
26
+
27
+ for (const name of Object.keys(interfaces)) {
28
+ for (const iface of interfaces[name]) {
29
+ // Skip internal and non-IPv4
30
+ if (iface.family === "IPv4" && !iface.internal) {
31
+ return iface.address;
32
+ }
33
+ }
34
+ }
35
+
36
+ return "127.0.0.1";
37
+ }
38
+
39
+ /**
40
+ * Get public IP and location info
41
+ */
42
+ async function getPublicIPInfo() {
43
+ try {
44
+ // Try ip-api.com first (free, no key needed)
45
+ const response = await fetch("http://ip-api.com/json/");
46
+ const data = await response.json();
47
+
48
+ // Check if we got valid data
49
+ if (data.status === "success" && data.query) {
50
+ return {
51
+ ip: data.query,
52
+ city: data.city || "Unknown City",
53
+ region: data.regionName || data.region,
54
+ country: data.country || "Unknown Country",
55
+ location: `${data.city}, ${data.country}`,
56
+ };
57
+ }
58
+
59
+ // Fallback if API returns error
60
+ throw new Error("Invalid response from IP API");
61
+ } catch (error) {
62
+ // Use local IP as fallback
63
+ const localIP = getLocalIP();
64
+ return {
65
+ ip: localIP,
66
+ location: `Local Network (${localIP})`,
67
+ };
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Simple ping function
73
+ * @param {string} destination - IP address or hostname to ping
74
+ * @param {string} source - Source IP (defaults to device IP)
75
+ * @returns {Promise<object>} Ping results
76
+ */
77
+ export async function ping(destination, source = null) {
78
+ const sourceIP = source || getLocalIP();
79
+
80
+ try {
81
+ const platform = process.platform;
82
+ const countFlag = platform === "win32" ? "-n" : "-c";
83
+ const command = `ping ${countFlag} 4 ${destination}`;
84
+
85
+ const { stdout } = await execAsync(command);
86
+
87
+ // Parse average latency
88
+ let avgLatency = null;
89
+
90
+ if (platform === "win32") {
91
+ const match = stdout.match(/Average = (\d+)ms/);
92
+ if (match) avgLatency = parseFloat(match[1]);
93
+ } else {
94
+ const match = stdout.match(/min\/avg\/max\/[^\s]+ = [\d.]+\/([\d.]+)\//);
95
+ if (match) avgLatency = parseFloat(match[1]);
96
+ }
97
+
98
+ return {
99
+ source: sourceIP,
100
+ destination,
101
+ latency: avgLatency,
102
+ unit: "ms",
103
+ success: true,
104
+ };
105
+ } catch (error) {
106
+ return {
107
+ source: sourceIP,
108
+ destination,
109
+ latency: null,
110
+ error: error.message,
111
+ success: false,
112
+ };
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Ping multiple locations
118
+ * @param {Array<string>} locations - Array of location keys from LOCATIONS
119
+ * @param {string} source - Source IP (optional)
120
+ * @returns {Promise<Array>} Array of ping results
121
+ */
122
+ export async function pingLocations(locations, source = null) {
123
+ const results = [];
124
+
125
+ for (const loc of locations) {
126
+ const ip = LOCATIONS[loc];
127
+ if (!ip) {
128
+ results.push({
129
+ location: loc,
130
+ error: "Unknown location",
131
+ success: false,
132
+ });
133
+ continue;
134
+ }
135
+
136
+ const result = await ping(ip, source);
137
+ results.push({
138
+ location: loc,
139
+ ...result,
140
+ });
141
+ }
142
+
143
+ return results;
144
+ }
145
+
146
+ /**
147
+ * Format ping results as markdown
148
+ * @param {Array} results - Array of ping results from pingLocations
149
+ * @param {object} options - Formatting options
150
+ * @returns {string} Markdown formatted output
151
+ */
152
+ export async function formatAsMarkdown(results, options = {}) {
153
+ const { includeMetadata = true, title = "Network Latency Test Results" } =
154
+ options;
155
+
156
+ let md = `## ${title}\n\n`;
157
+
158
+ if (includeMetadata && results.length > 0) {
159
+ const timestamp = new Date().toISOString();
160
+ const ipInfo = await getPublicIPInfo();
161
+
162
+ md += `**Test Date**: ${timestamp}\n`;
163
+ md += `**Source**: ${ipInfo.location} (${ipInfo.ip})\n\n`;
164
+ }
165
+
166
+ // Build table
167
+ md += "| Location | Destination | Latency | Status |\n";
168
+ md += "|----------|-------------|---------|--------|\n";
169
+
170
+ for (const result of results) {
171
+ const location = result.location || "N/A";
172
+ const destination = result.destination || "N/A";
173
+ const latency = result.success ? `${result.latency}ms` : "-";
174
+ const status = result.success
175
+ ? "āœ… Success"
176
+ : `āŒ ${result.error || "Failed"}`;
177
+
178
+ md += `| ${location} | ${destination} | ${latency} | ${status} |\n`;
179
+ }
180
+
181
+ // Add summary statistics
182
+ const successful = results.filter((r) => r.success);
183
+ if (successful.length > 0) {
184
+ md += "\n### Summary\n\n";
185
+
186
+ const latencies = successful.map((r) => r.latency);
187
+ const avgLatency = (
188
+ latencies.reduce((a, b) => a + b, 0) / latencies.length
189
+ ).toFixed(2);
190
+ const minLatency = Math.min(...latencies).toFixed(2);
191
+ const maxLatency = Math.max(...latencies).toFixed(2);
192
+
193
+ md += `- **Average Latency**: ${avgLatency}ms\n`;
194
+ md += `- **Best Latency**: ${minLatency}ms\n`;
195
+ md += `- **Worst Latency**: ${maxLatency}ms\n`;
196
+ md += `- **Success Rate**: ${successful.length}/${results.length} (${((successful.length / results.length) * 100).toFixed(0)}%)\n`;
197
+ }
198
+
199
+ return md;
200
+ }
201
+
202
+ // CLI interface
203
+ if (import.meta.url === `file://${process.argv[1]}`) {
204
+ const args = process.argv.slice(2);
205
+
206
+ // Check for --all flag
207
+ if (args.includes("--all")) {
208
+ console.log("Testing all locations...\n");
209
+ const allLocations = Object.keys(LOCATIONS);
210
+ const results = await pingLocations(allLocations);
211
+ const markdown = await formatAsMarkdown(results);
212
+ console.log(markdown);
213
+ process.exit(0);
214
+ }
215
+
216
+ // Check for multiple locations
217
+ if (args.length > 1) {
218
+ console.log(`Testing ${args.length} locations...\n`);
219
+ const results = await pingLocations(args);
220
+ const markdown = await formatAsMarkdown(results);
221
+ console.log(markdown);
222
+ process.exit(0);
223
+ }
224
+
225
+ const destination = args[0];
226
+
227
+ if (!destination) {
228
+ console.log("Usage:");
229
+ console.log(
230
+ " node ping.js <destination> # Single location (markdown output)",
231
+ );
232
+ console.log(
233
+ " node ping.js <loc1> <loc2> <loc3> # Multiple locations (markdown output)",
234
+ );
235
+ console.log(
236
+ " node ping.js --all # Test all locations (markdown output)",
237
+ );
238
+ console.log("\nAvailable locations:");
239
+ Object.keys(LOCATIONS).forEach((loc) => {
240
+ console.log(` ${loc} -> ${LOCATIONS[loc]}`);
241
+ });
242
+ process.exit(1);
243
+ }
244
+
245
+ // Single location - markdown output (consistent with multiple locations)
246
+ console.log(`Testing ${destination}...\n`);
247
+ const results = await pingLocations([destination]);
248
+ const markdown = await formatAsMarkdown(results);
249
+ console.log(markdown);
250
+ }
@@ -0,0 +1,165 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Publish markdown content to WordPress
4
+ *
5
+ * Usage: node publish-to-wordpress.js <file-path> [--status draft|publish]
6
+ */
7
+
8
+ import { readFileSync } from "fs";
9
+ import { resolve } from "path";
10
+ import matter from "gray-matter";
11
+ import dotenv from "dotenv";
12
+ import { marked } from "marked";
13
+ import { GutenbergConverter } from "../../mcp/gutenberg-converter.js";
14
+ import { WordPressMCP } from "../../mcp/wordpress-integration.js";
15
+
16
+ // Load environment variables
17
+ dotenv.config();
18
+
19
+ // Parse command line arguments
20
+ const args = process.argv.slice(2);
21
+ const filePath = args[0];
22
+ const statusIndex = args.indexOf("--status");
23
+ const requestedStatus =
24
+ statusIndex !== -1 && args[statusIndex + 1] ? args[statusIndex + 1] : "draft";
25
+
26
+ if (!filePath) {
27
+ console.error("Error: File path is required");
28
+ console.error(
29
+ "Usage: node publish-to-wordpress.js <file-path> [--status draft|publish]",
30
+ );
31
+ process.exit(1);
32
+ }
33
+
34
+ // Validate WordPress configuration
35
+ if (
36
+ !process.env.WORDPRESS_URL ||
37
+ !process.env.WORDPRESS_USERNAME ||
38
+ !process.env.WORDPRESS_APP_PASSWORD
39
+ ) {
40
+ console.error("Error: WordPress configuration is incomplete");
41
+ console.error("Required environment variables:");
42
+ console.error(" - WORDPRESS_URL");
43
+ console.error(" - WORDPRESS_USERNAME");
44
+ console.error(" - WORDPRESS_APP_PASSWORD");
45
+ console.error(
46
+ "\nRun /myai-configure wordpress to set up your WordPress connection",
47
+ );
48
+ process.exit(1);
49
+ }
50
+
51
+ async function publishToWordPress() {
52
+ try {
53
+ // Read and parse markdown file
54
+ const absolutePath = resolve(filePath);
55
+ const fileContent = readFileSync(absolutePath, "utf-8");
56
+ const { data: frontmatter, content } = matter(fileContent);
57
+
58
+ console.log("šŸ“„ Processing file:", filePath);
59
+ console.log("šŸ“‹ Title:", frontmatter.title || "No title");
60
+
61
+ // Extract interactive blocks before markdown conversion
62
+ // const { markdown: cleanMarkdown, interactiveBlocks } =
63
+ // GutenbergConverter.extractInteractiveBlocks(content);
64
+
65
+ // if (interactiveBlocks.length > 0) {
66
+ // console.log(
67
+ // ` Interactive blocks: ${interactiveBlocks.length} found`,
68
+ // );
69
+ // }
70
+
71
+ // Convert markdown to HTML using marked (preserves placeholders)
72
+ const html = marked.parse(content);
73
+
74
+ // Convert HTML to Gutenberg blocks
75
+ let htmlContent = GutenbergConverter.htmlToGutenberg(html);
76
+
77
+ // Restore interactive blocks as raw HTML blocks
78
+ // if (interactiveBlocks.length > 0) {
79
+ // htmlContent = GutenbergConverter.restoreInteractiveBlocks(
80
+ // htmlContent,
81
+ // interactiveBlocks,
82
+ // );
83
+ // }
84
+
85
+ // Prepare WordPress configuration
86
+ const config = {
87
+ endpoint: {
88
+ url: process.env.WORDPRESS_URL,
89
+ api_version: "wp/v2",
90
+ authentication: {
91
+ username: process.env.WORDPRESS_USERNAME,
92
+ password: process.env.WORDPRESS_APP_PASSWORD,
93
+ },
94
+ },
95
+ defaults: {
96
+ post_status: requestedStatus,
97
+ format: "standard",
98
+ comment_status: "open",
99
+ ping_status: "open",
100
+ },
101
+ };
102
+
103
+ // Initialize WordPress client
104
+ const wp = new WordPressMCP(config);
105
+
106
+ // Prepare post data
107
+ const postParams = {
108
+ title: frontmatter.title || "Untitled",
109
+ content: htmlContent,
110
+ status: requestedStatus,
111
+ excerpt: frontmatter.meta_description || "",
112
+ slug: frontmatter.slug || "",
113
+ use_gutenberg: process.env.USE_GUTENBERG === "true",
114
+ // Note: Tags and categories require ID mapping which will be added in future versions
115
+ };
116
+
117
+ console.log("šŸš€ Publishing to WordPress...");
118
+ console.log(" URL:", process.env.WORDPRESS_URL);
119
+ console.log(" Status:", requestedStatus);
120
+ console.log(
121
+ " Format:",
122
+ postParams.use_gutenberg ? "Gutenberg" : "Classic",
123
+ );
124
+
125
+ // Create the post
126
+ const result = await wp.createPost(postParams);
127
+
128
+ // Success!
129
+ console.log("\nāœ… Successfully published to WordPress!");
130
+ console.log("\nšŸ“„ Post Details:");
131
+ console.log(" Post ID:", result.id);
132
+ console.log(" Title:", result.title.rendered);
133
+ console.log(" Status:", result.status);
134
+ console.log(" Slug:", result.slug);
135
+
136
+ console.log("\nšŸ”— URLs:");
137
+ console.log(" View:", result.link);
138
+ console.log(
139
+ " Edit:",
140
+ `${process.env.WORDPRESS_URL}/wp-admin/post.php?post=${result.id}&action=edit`,
141
+ );
142
+
143
+ return result;
144
+ } catch (error) {
145
+ console.error("\nāŒ Error publishing to WordPress:");
146
+ console.error(error.message);
147
+
148
+ if (error.message.includes("401")) {
149
+ console.error("\nšŸ”‘ Authentication failed. Please check:");
150
+ console.error(" 1. Your WordPress username is correct");
151
+ console.error(" 2. Your Application Password is valid");
152
+ console.error(" 3. Application Passwords are enabled on your site");
153
+ } else if (error.message.includes("404")) {
154
+ console.error("\n🌐 WordPress site not found. Please check:");
155
+ console.error(" 1. Your WORDPRESS_URL is correct");
156
+ console.error(" 2. The site is accessible");
157
+ console.error(" 3. The REST API is enabled");
158
+ }
159
+
160
+ process.exit(1);
161
+ }
162
+ }
163
+
164
+ // Run the publisher
165
+ publishToWordPress();
@@ -6,7 +6,7 @@ import { AuthError, JWTPayload } from "../../../shared/types.js";
6
6
  const TOKEN_EXPIRY_DAYS = 7;
7
7
  const ALGORITHM = "RS256";
8
8
 
9
- let keyPair: { publicKey: jose.KeyLike; privateKey: jose.KeyLike } | null = null;
9
+ let keyPair: { publicKey: jose.CryptoKey; privateKey: jose.CryptoKey } | null = null;
10
10
 
11
11
  export class TokenService extends Context.Tag("TokenService")<
12
12
  TokenService,