myaidev-method 0.2.24-1 → 0.2.24-2

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 (32) hide show
  1. package/.claude-plugin/plugin.json +251 -0
  2. package/PLUGIN_ARCHITECTURE.md +276 -0
  3. package/README.md +204 -0
  4. package/USER_GUIDE.md +436 -9
  5. package/bin/cli.js +152 -0
  6. package/extension.json +174 -0
  7. package/hooks/hooks.json +221 -0
  8. package/marketplace.json +179 -0
  9. package/package.json +15 -3
  10. package/skills/content-verifier/SKILL.md +178 -0
  11. package/skills/content-writer/SKILL.md +151 -0
  12. package/skills/coolify-deployer/SKILL.md +207 -0
  13. package/skills/openstack-manager/SKILL.md +213 -0
  14. package/skills/security-auditor/SKILL.md +180 -0
  15. package/skills/security-tester/SKILL.md +171 -0
  16. package/skills/sparc-architect/SKILL.md +146 -0
  17. package/skills/sparc-coder/SKILL.md +136 -0
  18. package/skills/sparc-documenter/SKILL.md +195 -0
  19. package/skills/sparc-reviewer/SKILL.md +179 -0
  20. package/skills/sparc-tester/SKILL.md +156 -0
  21. package/skills/visual-generator/SKILL.md +147 -0
  22. package/skills/wordpress-publisher/SKILL.md +150 -0
  23. package/src/lib/content-coordinator.js +2562 -0
  24. package/src/lib/installation-detector.js +266 -0
  25. package/src/lib/visual-config-utils.js +1 -1
  26. package/src/lib/visual-generation-utils.js +34 -14
  27. package/src/scripts/generate-visual-cli.js +39 -10
  28. package/src/scripts/ping.js +0 -1
  29. package/src/templates/claude/agents/content-production-coordinator.md +689 -15
  30. package/src/templates/claude/commands/myai-content-enrichment.md +227 -0
  31. package/src/templates/claude/commands/myai-content-writer.md +48 -37
  32. package/src/templates/claude/commands/myai-coordinate-content.md +347 -11
@@ -0,0 +1,266 @@
1
+ /**
2
+ * Installation Detection Utility
3
+ *
4
+ * Detects the current installation state of MyAIDev Method:
5
+ * - Legacy installation (npx-based file copying)
6
+ * - Plugin installation (Claude Code plugin system)
7
+ * - Both (dual installation)
8
+ * - None (not installed)
9
+ *
10
+ * This utility ensures backward compatibility during migration to plugin architecture.
11
+ */
12
+
13
+ import fs from 'fs-extra';
14
+ import path from 'path';
15
+
16
+ /**
17
+ * Installation types enum
18
+ */
19
+ export const InstallationType = {
20
+ NONE: 'none',
21
+ LEGACY: 'legacy',
22
+ PLUGIN: 'plugin',
23
+ BOTH: 'both'
24
+ };
25
+
26
+ /**
27
+ * Detect the current installation state of MyAIDev Method
28
+ *
29
+ * @param {string} projectRoot - The project root directory
30
+ * @returns {Promise<Object>} Installation detection result
31
+ */
32
+ export async function detectInstallation(projectRoot = process.cwd()) {
33
+ const checks = {
34
+ // Legacy installation indicators
35
+ hasClaudeDir: await fs.pathExists(path.join(projectRoot, '.claude')),
36
+ hasClaudeCommands: await fs.pathExists(path.join(projectRoot, '.claude', 'commands')),
37
+ hasClaudeAgents: await fs.pathExists(path.join(projectRoot, '.claude', 'agents')),
38
+ hasManifest: await fs.pathExists(path.join(projectRoot, '.myaidev-manifest.json')),
39
+
40
+ // Plugin installation indicators
41
+ hasPluginDir: await fs.pathExists(path.join(projectRoot, '.claude-plugin')),
42
+ hasPluginJson: await fs.pathExists(path.join(projectRoot, '.claude-plugin', 'plugin.json')),
43
+
44
+ // Skills-based installation
45
+ hasSkills: await fs.pathExists(path.join(projectRoot, 'skills')),
46
+
47
+ // Check for myaidev-specific files
48
+ hasMyAIDevCommands: false,
49
+ hasMyAIDevAgents: false
50
+ };
51
+
52
+ // Check for myaidev-specific commands
53
+ if (checks.hasClaudeCommands) {
54
+ const commandsDir = path.join(projectRoot, '.claude', 'commands');
55
+ try {
56
+ const files = await fs.readdir(commandsDir);
57
+ checks.hasMyAIDevCommands = files.some(f => f.startsWith('myai-'));
58
+ } catch (e) {
59
+ // Directory doesn't exist or not readable
60
+ }
61
+ }
62
+
63
+ // Check for myaidev-specific agents
64
+ if (checks.hasClaudeAgents) {
65
+ const agentsDir = path.join(projectRoot, '.claude', 'agents');
66
+ try {
67
+ const files = await fs.readdir(agentsDir);
68
+ checks.hasMyAIDevAgents = files.some(f =>
69
+ f.includes('content-writer') ||
70
+ f.includes('visual') ||
71
+ f.includes('wordpress')
72
+ );
73
+ } catch (e) {
74
+ // Directory doesn't exist or not readable
75
+ }
76
+ }
77
+
78
+ // Determine installation type
79
+ const hasLegacy = checks.hasManifest || (checks.hasMyAIDevCommands || checks.hasMyAIDevAgents);
80
+ const hasPlugin = checks.hasPluginJson;
81
+
82
+ let installationType;
83
+ if (hasLegacy && hasPlugin) {
84
+ installationType = InstallationType.BOTH;
85
+ } else if (hasLegacy) {
86
+ installationType = InstallationType.LEGACY;
87
+ } else if (hasPlugin) {
88
+ installationType = InstallationType.PLUGIN;
89
+ } else {
90
+ installationType = InstallationType.NONE;
91
+ }
92
+
93
+ // Get version info if available
94
+ let legacyVersion = null;
95
+ let pluginVersion = null;
96
+
97
+ if (checks.hasManifest) {
98
+ try {
99
+ const manifest = await fs.readJson(path.join(projectRoot, '.myaidev-manifest.json'));
100
+ legacyVersion = manifest.version || '1.0.0';
101
+ } catch (e) {
102
+ // Manifest unreadable
103
+ }
104
+ }
105
+
106
+ if (checks.hasPluginJson) {
107
+ try {
108
+ const pluginJson = await fs.readJson(path.join(projectRoot, '.claude-plugin', 'plugin.json'));
109
+ pluginVersion = pluginJson.version;
110
+ } catch (e) {
111
+ // Plugin json unreadable
112
+ }
113
+ }
114
+
115
+ // Get installed workflows from manifest
116
+ let installedWorkflows = [];
117
+ if (checks.hasManifest) {
118
+ try {
119
+ const manifest = await fs.readJson(path.join(projectRoot, '.myaidev-manifest.json'));
120
+ installedWorkflows = manifest.installedWorkflows || [];
121
+ } catch (e) {
122
+ // Manifest unreadable
123
+ }
124
+ }
125
+
126
+ return {
127
+ installationType,
128
+ hasLegacy,
129
+ hasPlugin,
130
+ canUpgrade: hasLegacy && !hasPlugin,
131
+ canCoexist: true, // Both installation methods can coexist
132
+ checks,
133
+ versions: {
134
+ legacy: legacyVersion,
135
+ plugin: pluginVersion
136
+ },
137
+ installedWorkflows,
138
+ recommendations: getRecommendations(installationType, checks)
139
+ };
140
+ }
141
+
142
+ /**
143
+ * Get recommendations based on installation state
144
+ *
145
+ * @param {string} installationType - Current installation type
146
+ * @param {Object} checks - Detection checks
147
+ * @returns {string[]} Array of recommendation strings
148
+ */
149
+ function getRecommendations(installationType, checks) {
150
+ const recommendations = [];
151
+
152
+ switch (installationType) {
153
+ case InstallationType.NONE:
154
+ recommendations.push('Run "npx myaidev-method init --claude" to install via npx');
155
+ recommendations.push('Or use "/plugin install myaidev-method" for plugin installation');
156
+ break;
157
+
158
+ case InstallationType.LEGACY:
159
+ recommendations.push('Legacy installation detected - all existing commands will continue to work');
160
+ recommendations.push('Consider upgrading to plugin installation for automatic updates');
161
+ recommendations.push('Use "/plugin install myaidev-method" to add plugin capabilities');
162
+ break;
163
+
164
+ case InstallationType.PLUGIN:
165
+ recommendations.push('Plugin installation detected - skills are auto-discovered');
166
+ recommendations.push('Use "/myaidev-method:*" commands or legacy "/myai-*" aliases');
167
+ break;
168
+
169
+ case InstallationType.BOTH:
170
+ recommendations.push('Both legacy and plugin installations detected - all features available');
171
+ recommendations.push('You can use both "/myai-*" and "/myaidev-method:*" command formats');
172
+ break;
173
+ }
174
+
175
+ return recommendations;
176
+ }
177
+
178
+ /**
179
+ * Get a human-readable status message for the installation
180
+ *
181
+ * @param {string} projectRoot - The project root directory
182
+ * @returns {Promise<string>} Status message
183
+ */
184
+ export async function getInstallationStatus(projectRoot = process.cwd()) {
185
+ const detection = await detectInstallation(projectRoot);
186
+
187
+ let status = '\n📦 MyAIDev Method Installation Status\n';
188
+ status += '════════════════════════════════════════\n\n';
189
+
190
+ switch (detection.installationType) {
191
+ case InstallationType.NONE:
192
+ status += '❌ Not Installed\n\n';
193
+ break;
194
+ case InstallationType.LEGACY:
195
+ status += '✅ Legacy Installation (npx)\n';
196
+ if (detection.versions.legacy) {
197
+ status += ` Version: ${detection.versions.legacy}\n`;
198
+ }
199
+ if (detection.installedWorkflows.length > 0) {
200
+ status += ` Workflows: ${detection.installedWorkflows.join(', ')}\n`;
201
+ }
202
+ status += '\n';
203
+ break;
204
+ case InstallationType.PLUGIN:
205
+ status += '✅ Plugin Installation\n';
206
+ if (detection.versions.plugin) {
207
+ status += ` Version: ${detection.versions.plugin}\n`;
208
+ }
209
+ status += '\n';
210
+ break;
211
+ case InstallationType.BOTH:
212
+ status += '✅ Dual Installation (Legacy + Plugin)\n';
213
+ if (detection.versions.legacy) {
214
+ status += ` Legacy Version: ${detection.versions.legacy}\n`;
215
+ }
216
+ if (detection.versions.plugin) {
217
+ status += ` Plugin Version: ${detection.versions.plugin}\n`;
218
+ }
219
+ if (detection.installedWorkflows.length > 0) {
220
+ status += ` Workflows: ${detection.installedWorkflows.join(', ')}\n`;
221
+ }
222
+ status += '\n';
223
+ break;
224
+ }
225
+
226
+ status += '📋 Recommendations:\n';
227
+ for (const rec of detection.recommendations) {
228
+ status += ` • ${rec}\n`;
229
+ }
230
+
231
+ return status;
232
+ }
233
+
234
+ /**
235
+ * Check if upgrade from legacy to plugin is available
236
+ *
237
+ * @param {string} projectRoot - The project root directory
238
+ * @returns {Promise<Object>} Upgrade availability info
239
+ */
240
+ export async function checkUpgradeAvailability(projectRoot = process.cwd()) {
241
+ const detection = await detectInstallation(projectRoot);
242
+
243
+ return {
244
+ canUpgrade: detection.canUpgrade,
245
+ currentType: detection.installationType,
246
+ upgradeBenefits: [
247
+ 'Automatic skill discovery',
248
+ 'Namespaced commands (/myaidev-method:*)',
249
+ 'Plugin marketplace updates',
250
+ 'Cross-platform support preparation'
251
+ ],
252
+ preservedFeatures: [
253
+ 'All existing /myai-* commands',
254
+ 'Installed workflows and agents',
255
+ 'Configuration and environment variables',
256
+ '.myaidev-manifest.json tracking'
257
+ ]
258
+ };
259
+ }
260
+
261
+ export default {
262
+ detectInstallation,
263
+ getInstallationStatus,
264
+ checkUpgradeAvailability,
265
+ InstallationType
266
+ };
@@ -74,7 +74,7 @@ export function validateVisualConfig() {
74
74
  validation.errors.push("FAL_KEY appears to be invalid (too short)");
75
75
  }
76
76
 
77
- validation.hasAnyAPIKeys = validation.hasGoogle || validation.hasOpenAI;
77
+ validation.hasAnyAPIKeys = validation.hasGoogle || validation.hasOpenAI || validation.hasFal;
78
78
 
79
79
  // Validate default service
80
80
  if (
@@ -42,13 +42,16 @@ const OPENAI_IMAGE_MODELS = {
42
42
  };
43
43
 
44
44
  // FLUX 2 Models (via Fal.ai or BFL API)
45
- const FLUX2_MODELS = {
46
- "flux2-pro": "fal-ai/flux-2/pro", // State-of-the-art quality, fastest, lowest cost
47
- "flux2-flex": "fal-ai/flux-2/flex", // Developer-controlled parameters
48
- "flux2-dev": "fal-ai/flux-2/dev", // 32B open-weight model
45
+ export const FLUX2_MODELS = {
46
+ "flux2-pro": "fal-ai/flux-2-pro", // State-of-the-art quality, fastest, lowest cost
47
+ "flux2-flex": "fal-ai/flux-2-flex", // Developer-controlled parameters
48
+ "flux2-dev": "fal-ai/flux-2-dev", // 32B open-weight model
49
49
  // Legacy FLUX 1.x models (still available)
50
50
  "flux-pro": "fal-ai/flux-pro/v1.1-ultra",
51
51
  "flux-dev": "fal-ai/flux/dev",
52
+
53
+ // nano banana models
54
+ "nano-banana-pro": "fal-ai/nano-banana-pro",
52
55
  };
53
56
 
54
57
  // Pricing (USD per image/video)
@@ -100,6 +103,7 @@ export function validateAPIKeys() {
100
103
  }
101
104
  if (hasFal || hasBFL) {
102
105
  availableServices.push(
106
+ "nano-banana-pro",
103
107
  "flux2-pro",
104
108
  "flux2-flex",
105
109
  "flux2-dev",
@@ -165,6 +169,9 @@ export function estimateCost(service, options = {}) {
165
169
  case "flux-dev":
166
170
  return PRICING.flux_dev;
167
171
 
172
+ case "nano-banana-pro":
173
+ return PRICING.nano_banana_pro;
174
+
168
175
  case "veo3":
169
176
  case "veo3-fast":
170
177
  return PRICING.veo3; // per second, will multiply by duration
@@ -642,7 +649,7 @@ export async function generateImageFlux2(prompt, options = {}) {
642
649
  mimeType: contentType,
643
650
  service: "flux2",
644
651
  model: model,
645
- cost: PRICING[model.replace("-", "_")] || PRICING.flux2_pro,
652
+ cost: PRICING[model.replaceAll("-", "_")] || PRICING.flux2_pro,
646
653
  };
647
654
  }
648
655
 
@@ -669,13 +676,17 @@ export async function generateImageFlux2(prompt, options = {}) {
669
676
  *
670
677
  * @param {string} prompt - Image description
671
678
  * @param {Object} options - Generation options
672
- * @param {string} options.model - FLUX model (flux-pro, flux-dev)
679
+ * @param {string} options.model - FLUX model (nano-banana-pro, flux-pro, flux-dev)
673
680
  * @param {string} options.size - Image size
674
681
  * @param {number} options.maxRetries - Maximum retry attempts
675
682
  * @returns {Promise<Object>} Generated image data
676
683
  */
677
684
  export async function generateImageFal(prompt, options = {}) {
678
- const { model = "flux-pro", size = "1024x1024", maxRetries = 3 } = options;
685
+ const {
686
+ model = "nano-banana-pro",
687
+ size = "1024x1024",
688
+ maxRetries = 3,
689
+ } = options;
679
690
 
680
691
  const apiKey = process.env.FAL_KEY;
681
692
  if (!apiKey) {
@@ -724,7 +735,7 @@ export async function generateImageFal(prompt, options = {}) {
724
735
  mimeType: contentType,
725
736
  service: "fal",
726
737
  model: model,
727
- cost: PRICING[model.replace("-", "_")] || PRICING.flux_pro,
738
+ cost: PRICING[model.replaceAll("-", "_")] || PRICING.flux_pro,
728
739
  };
729
740
  }
730
741
 
@@ -852,12 +863,21 @@ export async function downloadImage(url) {
852
863
  export async function generateImage(prompt, options = {}) {
853
864
  const { preferredService, type = "general", ...serviceOptions } = options;
854
865
 
855
- // Select service
856
- const defaultService = process.env.VISUAL_DEFAULT_SERVICE || "gemini";
857
- const service = selectBestService(preferredService || defaultService);
866
+ // If a model is explicitly specified, use it as the service
867
+ // This allows --service=fal --model=nano-banana-pro to work correctly
868
+ let service;
869
+ if (serviceOptions.model && FLUX2_MODELS[serviceOptions.model]) {
870
+ service = serviceOptions.model;
871
+ } else {
872
+ // Select service normally
873
+ const defaultService = process.env.VISUAL_DEFAULT_SERVICE || "gemini";
874
+ service = selectBestService(preferredService || defaultService);
875
+ }
858
876
 
859
- const modelInfo = serviceOptions.model ? ` (${serviceOptions.model})` : "";
860
- console.log(`🎨 Generating ${type} image using ${modelInfo}...`);
877
+ const modelInfo = serviceOptions.model
878
+ ? ` ${serviceOptions.model}`
879
+ : ` ${service}`;
880
+ console.log(`🎨 Generating ${type} image using${modelInfo}...`);
861
881
 
862
882
  // Enhance prompt based on image type
863
883
  const enhancedPrompt = enhancePrompt(prompt, type);
@@ -898,7 +918,7 @@ export async function generateImage(prompt, options = {}) {
898
918
  case "nano-banana-pro":
899
919
  result = await generateImageFal(enhancedPrompt, {
900
920
  ...serviceOptions,
901
- model: serviceOptions.model || service,,
921
+ model: serviceOptions.model || service,
902
922
  });
903
923
  break;
904
924
 
@@ -14,9 +14,10 @@
14
14
 
15
15
  import {
16
16
  generateImage,
17
- generateVideoVeo,
17
+ generateVideo,
18
18
  estimateCost,
19
19
  validateAPIKeys,
20
+ FLUX2_MODELS,
20
21
  } from "../lib/visual-generation-utils.js";
21
22
  import {
22
23
  saveImage,
@@ -57,6 +58,7 @@ if (!prompt) {
57
58
  const options = {
58
59
  type: getOption("type", "hero"),
59
60
  service: getOption("service", null),
61
+ model: getOption("model", null),
60
62
  size: getOption("size", "1024x1024"),
61
63
  quality: getOption("quality", "standard"),
62
64
  description: getOption("description", null),
@@ -96,11 +98,24 @@ const options = {
96
98
 
97
99
  // Estimate cost
98
100
  let service = options.service;
99
- if (!service) {
100
- // Auto-select based on available services
101
- const validation = validateAPIKeys();
102
- service = validation.availableServices[0];
103
- console.log(chalk.gray(`Auto-selected service: ${service}`));
101
+
102
+ // Video generation always uses veo3, ignore model/service for cost estimation
103
+ if (options.type === "video") {
104
+ service = "veo3";
105
+ console.log(chalk.gray(`Video generation uses: veo3`));
106
+ } else {
107
+ // For images: if model is specified, it takes precedence over service
108
+ if (options.model && FLUX2_MODELS[options.model]) {
109
+ service = options.model;
110
+ console.log(chalk.gray(`Using model: ${options.model}`));
111
+ }
112
+
113
+ if (!service) {
114
+ // Auto-select based on available services
115
+ const validation = validateAPIKeys();
116
+ service = validation.availableServices[0];
117
+ console.log(chalk.gray(`Auto-selected service: ${service}`));
118
+ }
104
119
  }
105
120
 
106
121
  const cost = estimateCost(service, {
@@ -108,13 +123,20 @@ const options = {
108
123
  size: options.size,
109
124
  });
110
125
 
126
+ // For video, multiply by duration
127
+ const videoDuration = 5; // Default duration
128
+ const finalCost = options.type === "video" ? cost * videoDuration : cost;
129
+
111
130
  // Check budget
112
- const budget = await checkBudgetLimit(cost);
131
+ const budget = await checkBudgetLimit(finalCost);
113
132
  const todayCost = await getTodaysCost();
114
133
  const monthCost = await getMonthCost();
115
134
 
116
135
  console.log(chalk.cyan("💰 Cost Estimate:"));
117
- console.log(chalk.gray(` This generation: $${cost.toFixed(2)}`));
136
+ console.log(chalk.gray(` This generation: $${finalCost.toFixed(2)}`));
137
+ if (options.type === "video") {
138
+ console.log(chalk.gray(` (${videoDuration}s × $${cost.toFixed(2)}/s)`));
139
+ }
118
140
  console.log(
119
141
  chalk.gray(
120
142
  ` Today: $${todayCost.toFixed(2)} / $${budget.dailyBudget.toFixed(2)} (${Math.round((todayCost / budget.dailyBudget) * 100)}%)`,
@@ -177,7 +199,7 @@ const options = {
177
199
  if (options.type === "video") {
178
200
  // Generate video
179
201
  spinner.text = `Generating video using ${service}...`;
180
- result = await generateVideoVeo(prompt, {
202
+ result = await generateVideo(prompt, {
181
203
  duration: 5,
182
204
  aspectRatio: "16:9",
183
205
  });
@@ -198,6 +220,7 @@ const options = {
198
220
  spinner.text = `Generating ${options.type} using ${service}...`;
199
221
  result = await generateImage(prompt, {
200
222
  preferredService: service,
223
+ model: options.model,
201
224
  type: options.type,
202
225
  quality: options.quality,
203
226
  size: options.size,
@@ -330,9 +353,15 @@ function showHelp() {
330
353
  console.log(
331
354
  chalk.gray(" --service=<service> AI service (default: auto-select)"),
332
355
  );
356
+ console.log(
357
+ chalk.gray(" Values: gemini, imagen, dalle, veo"),
358
+ );
359
+ console.log(
360
+ chalk.gray(" --model=<model> Specific model (for fal service)"),
361
+ );
333
362
  console.log(
334
363
  chalk.gray(
335
- " Values: gemini, imagen, dalle, veo, nano-banana-pro, flux-pro, flux-dev",
364
+ " Values: nano-banana-pro, flux-pro, flux-dev",
336
365
  ),
337
366
  );
338
367
  console.log(
@@ -15,7 +15,6 @@ export const LOCATIONS = {
15
15
  chicago: "8.8.4.4", // Google DNS secondary
16
16
  europe: "1.0.0.1", // Cloudflare secondary
17
17
  asia: "208.67.222.222", // OpenDNS
18
- amsterdam: "213.165.253.121", // aditya's ramnode vm
19
18
  };
20
19
 
21
20
  /**