myaidev-method 0.2.22 → 0.2.24-1

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 (59) hide show
  1. package/USER_GUIDE.md +453 -48
  2. package/bin/cli.js +236 -38
  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 +22 -17
  11. package/src/config/workflows.js +28 -44
  12. package/src/index.js +21 -8
  13. package/src/lib/ascii-banner.js +214 -0
  14. package/src/lib/config-manager.js +470 -0
  15. package/src/lib/content-generator.js +427 -0
  16. package/src/lib/html-conversion-utils.js +843 -0
  17. package/src/lib/seo-optimizer.js +515 -0
  18. package/src/lib/update-manager.js +2 -1
  19. package/src/lib/visual-config-utils.js +321 -295
  20. package/src/lib/visual-generation-utils.js +1000 -811
  21. package/src/lib/wordpress-client.js +633 -0
  22. package/src/lib/workflow-installer.js +3 -3
  23. package/src/scripts/configure-wordpress-mcp.js +8 -3
  24. package/src/scripts/generate-visual-cli.js +365 -235
  25. package/src/scripts/html-conversion-cli.js +526 -0
  26. package/src/scripts/init/configure.js +436 -0
  27. package/src/scripts/init/install.js +460 -0
  28. package/src/scripts/ping.js +250 -0
  29. package/src/scripts/utils/file-utils.js +404 -0
  30. package/src/scripts/utils/logger.js +300 -0
  31. package/src/scripts/utils/write-content.js +293 -0
  32. package/src/scripts/wordpress/publish-to-wordpress.js +165 -0
  33. package/src/server/auth/services/TokenService.ts +1 -1
  34. package/src/templates/claude/agents/content-rules-setup.md +657 -0
  35. package/src/templates/claude/agents/content-writer.md +328 -1
  36. package/src/templates/claude/agents/visual-content-generator.md +311 -8
  37. package/src/templates/claude/commands/myai-configure.md +1 -1
  38. package/src/templates/claude/commands/myai-content-rules-setup.md +204 -0
  39. package/src/templates/claude/commands/myai-convert-html.md +186 -0
  40. package/src/templates/codex/commands/myai-content-rules-setup.md +85 -0
  41. package/src/templates/diagrams/architecture.d2 +52 -0
  42. package/src/templates/diagrams/flowchart.d2 +42 -0
  43. package/src/templates/diagrams/sequence.d2 +47 -0
  44. package/src/templates/docs/content-creation-guide.md +164 -0
  45. package/src/templates/docs/deployment-guide.md +336 -0
  46. package/src/templates/docs/visual-generation-guide.md +248 -0
  47. package/src/templates/docs/wordpress-publishing-guide.md +208 -0
  48. package/src/templates/gemini/commands/myai-content-rules-setup.toml +57 -0
  49. package/src/templates/infographics/comparison-table.html +347 -0
  50. package/src/templates/infographics/data-chart.html +268 -0
  51. package/src/templates/infographics/process-flow.html +365 -0
  52. package/.claude/mcp/sparc-orchestrator-server.js +0 -607
  53. package/.claude/mcp/wordpress-server.js +0 -1277
  54. package/src/agents/content-writer-prompt.md +0 -164
  55. package/src/agents/content-writer.json +0 -70
  56. package/src/templates/claude/mcp_config.json +0 -74
  57. package/src/templates/claude/slash_commands.json +0 -166
  58. package/src/templates/scripts/configure-wordpress-mcp.js +0 -181
  59. /package/src/scripts/{wordpress-health-check.js → wordpress/wordpress-health-check.js} +0 -0
@@ -0,0 +1,293 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Write Content CLI
5
+ * Command-line wrapper for content generation
6
+ * Usage: node write-content.js "Topic" [options]
7
+ */
8
+
9
+ import { ContentGenerator } from '../../lib/content-generator.js';
10
+ import { SEOOptimizer } from '../../lib/seo-optimizer.js';
11
+ import { logger, formatTable } from './logger.js';
12
+ import fs from 'fs-extra';
13
+ import path from 'path';
14
+
15
+ /**
16
+ * Parse command line arguments
17
+ * @param {string[]} args - Command line arguments
18
+ * @returns {Object} Parsed options
19
+ */
20
+ function parseArgs(args) {
21
+ const options = {
22
+ topic: '',
23
+ contentType: 'blog-post',
24
+ wordCount: 1500,
25
+ tone: 'professional',
26
+ keywords: [],
27
+ outputDir: './content',
28
+ outputFile: '',
29
+ analyze: false,
30
+ help: false
31
+ };
32
+
33
+ let i = 0;
34
+ while (i < args.length) {
35
+ const arg = args[i];
36
+
37
+ if (arg === '--help' || arg === '-h') {
38
+ options.help = true;
39
+ } else if (arg === '--type' || arg === '-t') {
40
+ options.contentType = args[++i];
41
+ } else if (arg === '--word-count' || arg === '-w') {
42
+ options.wordCount = parseInt(args[++i], 10);
43
+ } else if (arg === '--tone') {
44
+ options.tone = args[++i];
45
+ } else if (arg === '--keywords' || arg === '-k') {
46
+ options.keywords = args[++i].split(',').map(k => k.trim());
47
+ } else if (arg === '--output' || arg === '-o') {
48
+ options.outputFile = args[++i];
49
+ } else if (arg === '--output-dir') {
50
+ options.outputDir = args[++i];
51
+ } else if (arg === '--analyze' || arg === '-a') {
52
+ options.analyze = true;
53
+ } else if (!arg.startsWith('-')) {
54
+ options.topic = arg;
55
+ }
56
+
57
+ i++;
58
+ }
59
+
60
+ return options;
61
+ }
62
+
63
+ /**
64
+ * Show help message
65
+ */
66
+ function showHelp() {
67
+ console.log(`
68
+ Write Content - Content Generation CLI
69
+
70
+ Usage: node write-content.js "Topic" [options]
71
+
72
+ Options:
73
+ -h, --help Show this help message
74
+ -t, --type <type> Content type (blog-post, tutorial, how-to, listicle, product-review, comparison)
75
+ -w, --word-count <num> Target word count (default: 1500)
76
+ --tone <tone> Writing tone (professional, conversational, technical, casual, authoritative)
77
+ -k, --keywords <list> Comma-separated keywords for SEO
78
+ -o, --output <file> Output filename (auto-generated if not specified)
79
+ --output-dir <dir> Output directory (default: ./content)
80
+ -a, --analyze Analyze content for SEO after generation
81
+
82
+ Examples:
83
+ node write-content.js "How to Train a Puppy" --type tutorial --tone conversational
84
+ node write-content.js "Best Laptops 2024" --type listicle --keywords "laptops,tech,reviews"
85
+ node write-content.js "React vs Vue" --type comparison --word-count 2500 --analyze
86
+ `);
87
+ }
88
+
89
+ /**
90
+ * Generate content specification for the content-writer agent
91
+ * @param {Object} options - Generation options
92
+ */
93
+ async function generateContentSpec(options) {
94
+ const generator = new ContentGenerator({
95
+ outputDir: options.outputDir,
96
+ tone: options.tone,
97
+ wordCount: options.wordCount
98
+ });
99
+
100
+ await generator.initialize();
101
+
102
+ // Validate options
103
+ const validation = generator.validateOptions(options);
104
+ if (!validation.valid) {
105
+ for (const error of validation.errors) {
106
+ logger.error(error);
107
+ }
108
+ process.exit(1);
109
+ }
110
+
111
+ // Generate content specification
112
+ const spec = generator.generateContentSpec(options.topic, {
113
+ contentType: options.contentType,
114
+ wordCount: options.wordCount,
115
+ tone: options.tone,
116
+ keywords: options.keywords
117
+ });
118
+
119
+ logger.header('Content Specification');
120
+ logger.log(`Topic: ${spec.topic}`);
121
+ logger.log(`Type: ${spec.contentType}`);
122
+ logger.log(`Word Count Target: ${spec.wordCount}`);
123
+ logger.log(`Tone: ${spec.tone}`);
124
+ logger.log(`Keywords: ${spec.keywords.length > 0 ? spec.keywords.join(', ') : 'None specified'}`);
125
+ logger.blank();
126
+
127
+ logger.section('Required Sections');
128
+ for (const section of spec.sections) {
129
+ logger.log(` • ${section}`);
130
+ }
131
+
132
+ logger.section('Frontmatter Fields');
133
+ for (const field of spec.requiredFrontmatter) {
134
+ logger.log(` • ${field}`);
135
+ }
136
+
137
+ // Generate filename
138
+ const filename = options.outputFile || generator.generateFilename(options.topic);
139
+ const outputPath = path.join(options.outputDir, filename);
140
+
141
+ logger.section('Output');
142
+ logger.log(`Filename: ${filename}`);
143
+ logger.log(`Path: ${outputPath}`);
144
+ logger.blank();
145
+
146
+ // Generate frontmatter template
147
+ const frontmatterData = {
148
+ title: options.topic,
149
+ description: '',
150
+ date: new Date().toISOString().split('T')[0],
151
+ author: '',
152
+ categories: [],
153
+ tags: options.keywords,
154
+ status: 'draft'
155
+ };
156
+
157
+ const frontmatter = generator.generateFrontmatter(frontmatterData);
158
+
159
+ // Create template file
160
+ const templateContent = `${frontmatter}
161
+ # ${options.topic}
162
+
163
+ <!-- Introduction -->
164
+
165
+ ## Overview
166
+
167
+ [Write introduction here]
168
+
169
+ ${spec.sections.slice(1).map(section => `
170
+ ## ${section.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ')}
171
+
172
+ [Write ${section} content here]
173
+ `).join('\n')}
174
+
175
+ <!-- Call to Action -->
176
+
177
+ ## Conclusion
178
+
179
+ [Write conclusion here]
180
+ `;
181
+
182
+ await fs.ensureDir(options.outputDir);
183
+ await fs.writeFile(outputPath, templateContent, 'utf-8');
184
+
185
+ logger.success(`Template created: ${outputPath}`);
186
+ logger.blank();
187
+ logger.info('Use the content-writer agent to generate full content:');
188
+ logger.log(` /myai-content-writer "${options.topic}" --type ${options.contentType}`);
189
+
190
+ return { spec, outputPath, templateContent };
191
+ }
192
+
193
+ /**
194
+ * Analyze existing content file
195
+ * @param {string} filePath - Path to content file
196
+ * @param {string[]} keywords - Keywords to check
197
+ */
198
+ async function analyzeContent(filePath, keywords) {
199
+ const optimizer = new SEOOptimizer();
200
+
201
+ const content = await fs.readFile(filePath, 'utf-8');
202
+
203
+ // Extract title from frontmatter or first heading
204
+ const titleMatch = content.match(/^title:\s*(.+)$/m) || content.match(/^#\s+(.+)$/m);
205
+ const title = titleMatch ? titleMatch[1].trim() : '';
206
+
207
+ logger.header('SEO Analysis');
208
+ logger.log(`File: ${filePath}`);
209
+ logger.log(`Title: ${title || 'Not found'}`);
210
+ logger.blank();
211
+
212
+ const analysis = optimizer.analyzeSEO(content, keywords, title);
213
+
214
+ // Score display
215
+ const scoreColor = analysis.score >= 80 ? 'green' : analysis.score >= 60 ? 'yellow' : 'red';
216
+ logger.log(`SEO Score: ${analysis.score}/100 (Grade: ${analysis.grade})`);
217
+ logger.log(`Word Count: ${analysis.wordCount}`);
218
+ if (analysis.keywordDensity !== null) {
219
+ logger.log(`Keyword Density: ${analysis.keywordDensity}%`);
220
+ }
221
+ logger.blank();
222
+
223
+ // Issues
224
+ if (analysis.issues.length > 0) {
225
+ logger.section('Issues');
226
+ for (const issue of analysis.issues) {
227
+ logger.error(issue);
228
+ }
229
+ }
230
+
231
+ // Recommendations
232
+ if (analysis.recommendations.length > 0) {
233
+ logger.section('Recommendations');
234
+ for (const rec of analysis.recommendations) {
235
+ logger.warn(rec);
236
+ }
237
+ }
238
+
239
+ // Meta description
240
+ logger.section('Generated Meta Description');
241
+ const metaDesc = optimizer.generateMetaDescription(content);
242
+ logger.log(metaDesc);
243
+ logger.log(`Length: ${metaDesc.length} characters`);
244
+
245
+ // Slug
246
+ if (title) {
247
+ logger.section('Generated Slug');
248
+ logger.log(optimizer.generateSlug(title));
249
+ }
250
+
251
+ return analysis;
252
+ }
253
+
254
+ /**
255
+ * Main function
256
+ */
257
+ async function main() {
258
+ const args = process.argv.slice(2);
259
+ const options = parseArgs(args);
260
+
261
+ if (options.help) {
262
+ showHelp();
263
+ process.exit(0);
264
+ }
265
+
266
+ if (!options.topic && !options.analyze) {
267
+ logger.error('Topic is required. Use --help for usage information.');
268
+ process.exit(1);
269
+ }
270
+
271
+ try {
272
+ if (options.analyze && options.topic) {
273
+ // If topic looks like a file path, analyze it
274
+ if (options.topic.endsWith('.md') || options.topic.includes('/')) {
275
+ await analyzeContent(options.topic, options.keywords);
276
+ } else {
277
+ // Generate spec and analyze
278
+ const { outputPath } = await generateContentSpec(options);
279
+ await analyzeContent(outputPath, options.keywords);
280
+ }
281
+ } else if (options.analyze && !options.topic) {
282
+ logger.error('Please provide a file path to analyze.');
283
+ process.exit(1);
284
+ } else {
285
+ await generateContentSpec(options);
286
+ }
287
+ } catch (error) {
288
+ logger.error(`Error: ${error.message}`);
289
+ process.exit(1);
290
+ }
291
+ }
292
+
293
+ main();
@@ -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,