@edtools/cli 0.5.0 → 0.6.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 (46) hide show
  1. package/dist/chunk-BI3UJPWA.js +690 -0
  2. package/dist/chunk-JCUQ7D56.js +688 -0
  3. package/dist/cli/commands/doctor.d.ts +6 -0
  4. package/dist/cli/commands/doctor.d.ts.map +1 -0
  5. package/dist/cli/commands/doctor.js +150 -0
  6. package/dist/cli/commands/doctor.js.map +1 -0
  7. package/dist/cli/commands/generate.d.ts +2 -0
  8. package/dist/cli/commands/generate.d.ts.map +1 -1
  9. package/dist/cli/commands/generate.js +148 -38
  10. package/dist/cli/commands/generate.js.map +1 -1
  11. package/dist/cli/commands/init.d.ts.map +1 -1
  12. package/dist/cli/commands/init.js +11 -1
  13. package/dist/cli/commands/init.js.map +1 -1
  14. package/dist/cli/commands/validate.d.ts +9 -0
  15. package/dist/cli/commands/validate.d.ts.map +1 -0
  16. package/dist/cli/commands/validate.js +196 -0
  17. package/dist/cli/commands/validate.js.map +1 -0
  18. package/dist/cli/index.js +910 -87
  19. package/dist/cli/index.js.map +1 -1
  20. package/dist/core/generator.d.ts.map +1 -1
  21. package/dist/core/generator.js +36 -8
  22. package/dist/core/generator.js.map +1 -1
  23. package/dist/index.d.ts +64 -1
  24. package/dist/index.js +1 -1
  25. package/dist/types/content.d.ts +44 -0
  26. package/dist/types/content.d.ts.map +1 -1
  27. package/dist/types/hosting.d.ts +19 -0
  28. package/dist/types/hosting.d.ts.map +1 -0
  29. package/dist/types/hosting.js +2 -0
  30. package/dist/types/hosting.js.map +1 -0
  31. package/dist/types/index.d.ts +1 -0
  32. package/dist/types/index.d.ts.map +1 -1
  33. package/dist/types/index.js +1 -0
  34. package/dist/types/index.js.map +1 -1
  35. package/dist/ui/banner.d.ts.map +1 -1
  36. package/dist/ui/banner.js +9 -1
  37. package/dist/ui/banner.js.map +1 -1
  38. package/dist/utils/hosting-detection.d.ts +6 -0
  39. package/dist/utils/hosting-detection.d.ts.map +1 -0
  40. package/dist/utils/hosting-detection.js +173 -0
  41. package/dist/utils/hosting-detection.js.map +1 -0
  42. package/dist/utils/seo-validator.d.ts +35 -0
  43. package/dist/utils/seo-validator.d.ts.map +1 -0
  44. package/dist/utils/seo-validator.js +244 -0
  45. package/dist/utils/seo-validator.js.map +1 -0
  46. package/package.json +4 -3
@@ -0,0 +1,173 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import yaml from 'yaml';
4
+ const detectors = [
5
+ {
6
+ name: 'Firebase',
7
+ file: 'firebase.json',
8
+ parse: (content, projectPath) => {
9
+ try {
10
+ const config = JSON.parse(content);
11
+ const publicDir = config.hosting?.public || 'public';
12
+ return {
13
+ platform: 'Firebase',
14
+ publicDir,
15
+ configFile: 'firebase.json',
16
+ };
17
+ }
18
+ catch (error) {
19
+ return null;
20
+ }
21
+ },
22
+ },
23
+ {
24
+ name: 'AWS Amplify',
25
+ file: 'amplify.yml',
26
+ parse: (content, projectPath) => {
27
+ try {
28
+ const config = yaml.parse(content);
29
+ const baseDirectory = config?.frontend?.artifacts?.baseDirectory || 'build';
30
+ return {
31
+ platform: 'AWS Amplify',
32
+ publicDir: baseDirectory,
33
+ configFile: 'amplify.yml',
34
+ };
35
+ }
36
+ catch (error) {
37
+ return null;
38
+ }
39
+ },
40
+ },
41
+ {
42
+ name: 'Vercel',
43
+ file: 'vercel.json',
44
+ parse: (content, projectPath) => {
45
+ try {
46
+ const config = JSON.parse(content);
47
+ const outputDirectory = config.outputDirectory || config.buildCommand?.match(/out|dist|build/)?.[0] || 'public';
48
+ return {
49
+ platform: 'Vercel',
50
+ publicDir: outputDirectory,
51
+ configFile: 'vercel.json',
52
+ };
53
+ }
54
+ catch (error) {
55
+ return null;
56
+ }
57
+ },
58
+ },
59
+ {
60
+ name: 'Netlify',
61
+ file: 'netlify.toml',
62
+ parse: (content, projectPath) => {
63
+ try {
64
+ const match = content.match(/publish\s*=\s*"([^"]+)"/);
65
+ const publicDir = match?.[1] || 'public';
66
+ return {
67
+ platform: 'Netlify',
68
+ publicDir,
69
+ configFile: 'netlify.toml',
70
+ };
71
+ }
72
+ catch (error) {
73
+ return null;
74
+ }
75
+ },
76
+ },
77
+ {
78
+ name: 'Next.js',
79
+ file: 'next.config.js',
80
+ parse: (content, projectPath) => {
81
+ if (content.includes('output:') && content.includes('export')) {
82
+ const packageJsonPath = path.join(projectPath, 'package.json');
83
+ if (fs.existsSync(packageJsonPath)) {
84
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
85
+ const buildScript = packageJson.scripts?.build || '';
86
+ if (buildScript.includes('out')) {
87
+ return {
88
+ platform: 'Next.js (Static Export)',
89
+ publicDir: 'out',
90
+ configFile: 'next.config.js',
91
+ };
92
+ }
93
+ }
94
+ return {
95
+ platform: 'Next.js (Static Export)',
96
+ publicDir: 'out',
97
+ configFile: 'next.config.js',
98
+ };
99
+ }
100
+ return null;
101
+ },
102
+ },
103
+ ];
104
+ export function detectHostingConfig(projectPath) {
105
+ for (const detector of detectors) {
106
+ const configPath = path.join(projectPath, detector.file);
107
+ if (fs.existsSync(configPath)) {
108
+ try {
109
+ const content = fs.readFileSync(configPath, 'utf-8');
110
+ const result = detector.parse(content, projectPath);
111
+ if (result) {
112
+ return result;
113
+ }
114
+ }
115
+ catch (error) {
116
+ console.error(`Error parsing ${detector.file}:`, error);
117
+ }
118
+ }
119
+ }
120
+ return null;
121
+ }
122
+ export function validateOutputDir(outputDir, hostingConfig) {
123
+ if (!hostingConfig) {
124
+ return { valid: true };
125
+ }
126
+ const normalizedOutputDir = outputDir.replace(/^\.\//, '');
127
+ const normalizedPublicDir = hostingConfig.publicDir.replace(/^\.\//, '');
128
+ const isInPublicDir = normalizedOutputDir.startsWith(`${normalizedPublicDir}/`) ||
129
+ normalizedOutputDir === normalizedPublicDir;
130
+ const issues = [];
131
+ if (!isInPublicDir) {
132
+ issues.push(`Output directory "${outputDir}" is outside the public directory "${hostingConfig.publicDir}"`);
133
+ issues.push('Generated files will not be accessible in production');
134
+ }
135
+ return {
136
+ valid: isInPublicDir,
137
+ platform: hostingConfig.platform,
138
+ publicDir: hostingConfig.publicDir,
139
+ currentOutputDir: outputDir,
140
+ suggestion: `./${normalizedPublicDir}/blog`,
141
+ issues: issues.length > 0 ? issues : undefined,
142
+ };
143
+ }
144
+ export function getSuggestedOutputDir(projectPath) {
145
+ const hostingConfig = detectHostingConfig(projectPath);
146
+ if (hostingConfig) {
147
+ return `./${hostingConfig.publicDir}/blog`;
148
+ }
149
+ return './blog';
150
+ }
151
+ export function formatValidationMessage(validation) {
152
+ if (validation.valid) {
153
+ return '✓ Output directory is correctly configured';
154
+ }
155
+ let message = `⚠️ HOSTING PLATFORM DETECTED: ${validation.platform}\n`;
156
+ message += ` - Public directory: ${validation.publicDir}\n`;
157
+ message += ` - Your outputDir: ${validation.currentOutputDir}\n`;
158
+ message += ` - Problem: Files outside ${validation.publicDir}/ won't be accessible\n\n`;
159
+ if (validation.issues) {
160
+ message += `❌ ISSUES:\n`;
161
+ validation.issues.forEach((issue) => {
162
+ message += ` - ${issue}\n`;
163
+ });
164
+ message += `\n`;
165
+ }
166
+ message += `📝 Suggested fix:\n`;
167
+ message += ` Update edtools.config.js:\n\n`;
168
+ message += ` content: {\n`;
169
+ message += ` outputDir: '${validation.suggestion}' // ✅ Correct path\n`;
170
+ message += ` }\n`;
171
+ return message;
172
+ }
173
+ //# sourceMappingURL=hosting-detection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hosting-detection.js","sourceRoot":"","sources":["../../src/utils/hosting-detection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ export interface ValidationIssue {
2
+ severity: 'error' | 'warning' | 'info';
3
+ category: 'metadata' | 'headings' | 'content' | 'images' | 'links' | 'structure';
4
+ message: string;
5
+ suggestion?: string;
6
+ }
7
+ export interface ValidationResult {
8
+ path: string;
9
+ title: string;
10
+ seoScore: number;
11
+ issues: ValidationIssue[];
12
+ passed: string[];
13
+ metadata?: {
14
+ titleLength?: number;
15
+ descriptionLength?: number;
16
+ h1Count?: number;
17
+ h2Count?: number;
18
+ imageCount?: number;
19
+ wordCount?: number;
20
+ };
21
+ }
22
+ export declare function validatePost(htmlPath: string): Promise<ValidationResult>;
23
+ export declare function calculateValidationStats(results: ValidationResult[]): {
24
+ totalPosts: number;
25
+ avgSeoScore: number;
26
+ totalIssues: number;
27
+ postsWithIssues: number;
28
+ issuesBySeverity: {
29
+ error: number;
30
+ warning: number;
31
+ info: number;
32
+ };
33
+ issuesByCategory: Record<string, number>;
34
+ };
35
+ //# sourceMappingURL=seo-validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seo-validator.d.ts","sourceRoot":"","sources":["../../src/utils/seo-validator.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IACvC,QAAQ,EAAE,UAAU,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,WAAW,CAAC;IACjF,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE;QACT,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAKD,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA2O9E;AAKD,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,gBAAgB,EAAE;;;;;;;;;;;EA2BnE"}
@@ -0,0 +1,244 @@
1
+ import * as cheerio from 'cheerio';
2
+ import fs from 'fs-extra';
3
+ export async function validatePost(htmlPath) {
4
+ const html = await fs.readFile(htmlPath, 'utf-8');
5
+ const $ = cheerio.load(html);
6
+ const issues = [];
7
+ const passed = [];
8
+ const metadata = {};
9
+ const title = $('title').text().trim();
10
+ metadata.titleLength = title.length;
11
+ if (!title) {
12
+ issues.push({
13
+ severity: 'error',
14
+ category: 'metadata',
15
+ message: 'Missing <title> tag',
16
+ suggestion: 'Add a descriptive title (50-60 characters)'
17
+ });
18
+ }
19
+ else if (title.length < 30) {
20
+ issues.push({
21
+ severity: 'warning',
22
+ category: 'metadata',
23
+ message: `Title too short (${title.length} chars)`,
24
+ suggestion: 'Optimal length is 50-60 characters for better CTR'
25
+ });
26
+ }
27
+ else if (title.length > 60) {
28
+ issues.push({
29
+ severity: 'warning',
30
+ category: 'metadata',
31
+ message: `Title too long (${title.length} chars)`,
32
+ suggestion: 'Shorten to 50-60 characters to avoid truncation in SERPs'
33
+ });
34
+ }
35
+ else {
36
+ passed.push(`Title length optimal (${title.length} chars)`);
37
+ }
38
+ const metaDesc = $('meta[name="description"]').attr('content')?.trim() || '';
39
+ metadata.descriptionLength = metaDesc.length;
40
+ if (!metaDesc) {
41
+ issues.push({
42
+ severity: 'error',
43
+ category: 'metadata',
44
+ message: 'Missing meta description',
45
+ suggestion: 'Add a compelling description (150-160 characters)'
46
+ });
47
+ }
48
+ else if (metaDesc.length < 120) {
49
+ issues.push({
50
+ severity: 'warning',
51
+ category: 'metadata',
52
+ message: `Meta description too short (${metaDesc.length}/160 chars)`,
53
+ suggestion: `Add ${160 - metaDesc.length} more characters to optimize CTR`
54
+ });
55
+ }
56
+ else if (metaDesc.length > 160) {
57
+ issues.push({
58
+ severity: 'warning',
59
+ category: 'metadata',
60
+ message: `Meta description too long (${metaDesc.length}/160 chars)`,
61
+ suggestion: `Remove ${metaDesc.length - 160} characters to avoid truncation`
62
+ });
63
+ }
64
+ else {
65
+ passed.push(`Meta description optimal (${metaDesc.length} chars)`);
66
+ }
67
+ const h1Elements = $('h1');
68
+ const h1Count = h1Elements.length;
69
+ metadata.h1Count = h1Count;
70
+ if (h1Count === 0) {
71
+ issues.push({
72
+ severity: 'error',
73
+ category: 'headings',
74
+ message: 'Missing H1 heading',
75
+ suggestion: 'Add a single H1 with primary keyword'
76
+ });
77
+ }
78
+ else if (h1Count > 1) {
79
+ issues.push({
80
+ severity: 'warning',
81
+ category: 'headings',
82
+ message: `Multiple H1 tags found (${h1Count})`,
83
+ suggestion: 'Use only one H1 per page for proper structure'
84
+ });
85
+ }
86
+ else {
87
+ passed.push('Single H1 heading');
88
+ }
89
+ const h2Count = $('h2').length;
90
+ metadata.h2Count = h2Count;
91
+ if (h2Count < 2) {
92
+ issues.push({
93
+ severity: 'warning',
94
+ category: 'headings',
95
+ message: `Few H2 headings for readability (${h2Count} found)`,
96
+ suggestion: 'Add 3-5 H2 headings for better structure and scannability'
97
+ });
98
+ }
99
+ else if (h2Count > 10) {
100
+ issues.push({
101
+ severity: 'info',
102
+ category: 'headings',
103
+ message: `Many H2 headings (${h2Count})`,
104
+ suggestion: 'Consider if all sections need H2 or some should be H3'
105
+ });
106
+ }
107
+ else {
108
+ passed.push(`Good heading structure (${h2Count} H2s)`);
109
+ }
110
+ const images = $('img');
111
+ metadata.imageCount = images.length;
112
+ const imagesWithoutAlt = images.filter((i, el) => !$(el).attr('alt')).length;
113
+ if (imagesWithoutAlt > 0) {
114
+ issues.push({
115
+ severity: 'warning',
116
+ category: 'images',
117
+ message: `${imagesWithoutAlt} image${imagesWithoutAlt > 1 ? 's' : ''} missing alt text`,
118
+ suggestion: 'Add descriptive alt text to all images for accessibility and SEO'
119
+ });
120
+ }
121
+ else if (images.length > 0) {
122
+ passed.push(`All images have alt text (${images.length} images)`);
123
+ }
124
+ const schemaScript = $('script[type="application/ld+json"]');
125
+ if (schemaScript.length === 0) {
126
+ issues.push({
127
+ severity: 'info',
128
+ category: 'structure',
129
+ message: 'Missing Schema.org markup',
130
+ suggestion: 'Add structured data for rich snippets in search results'
131
+ });
132
+ }
133
+ else {
134
+ passed.push('Has Schema.org markup');
135
+ }
136
+ const metaKeywords = $('meta[name="keywords"]');
137
+ if (metaKeywords.length > 0) {
138
+ issues.push({
139
+ severity: 'info',
140
+ category: 'metadata',
141
+ message: 'Using deprecated meta keywords tag',
142
+ suggestion: 'Remove meta keywords tag (not used by modern search engines)'
143
+ });
144
+ }
145
+ const ogTitle = $('meta[property="og:title"]').attr('content');
146
+ const ogDescription = $('meta[property="og:description"]').attr('content');
147
+ const ogImage = $('meta[property="og:image"]').attr('content');
148
+ if (!ogTitle || !ogDescription) {
149
+ issues.push({
150
+ severity: 'info',
151
+ category: 'metadata',
152
+ message: 'Missing Open Graph tags',
153
+ suggestion: 'Add og:title, og:description, og:image for better social sharing'
154
+ });
155
+ }
156
+ else {
157
+ passed.push('Has Open Graph tags');
158
+ }
159
+ const bodyText = $('article, main, body').text();
160
+ const wordCount = bodyText
161
+ .replace(/\s+/g, ' ')
162
+ .trim()
163
+ .split(/\s+/)
164
+ .filter(word => word.length > 0).length;
165
+ metadata.wordCount = wordCount;
166
+ if (wordCount < 300) {
167
+ issues.push({
168
+ severity: 'warning',
169
+ category: 'content',
170
+ message: `Content too short (${wordCount} words)`,
171
+ suggestion: 'Aim for at least 1000 words for better SEO performance'
172
+ });
173
+ }
174
+ else if (wordCount < 1000) {
175
+ issues.push({
176
+ severity: 'info',
177
+ category: 'content',
178
+ message: `Content could be longer (${wordCount} words)`,
179
+ suggestion: 'Consider expanding to 1500+ words for comprehensive coverage'
180
+ });
181
+ }
182
+ else {
183
+ passed.push(`Good content length (${wordCount} words)`);
184
+ }
185
+ const links = $('a[href]');
186
+ const internalLinks = links.filter((i, el) => {
187
+ const href = $(el).attr('href') || '';
188
+ return href.startsWith('/') || href.startsWith('#');
189
+ }).length;
190
+ const externalLinks = links.length - internalLinks;
191
+ if (internalLinks === 0 && links.length > 0) {
192
+ issues.push({
193
+ severity: 'info',
194
+ category: 'links',
195
+ message: 'No internal links found',
196
+ suggestion: 'Add 2-3 internal links to related content'
197
+ });
198
+ }
199
+ else if (internalLinks > 0) {
200
+ passed.push(`Has internal links (${internalLinks} links)`);
201
+ }
202
+ const errorCount = issues.filter(i => i.severity === 'error').length;
203
+ const warningCount = issues.filter(i => i.severity === 'warning').length;
204
+ const infoCount = issues.filter(i => i.severity === 'info').length;
205
+ let score = 100;
206
+ score -= errorCount * 20;
207
+ score -= warningCount * 7;
208
+ score -= infoCount * 2;
209
+ score = Math.max(0, Math.min(100, score));
210
+ return {
211
+ path: htmlPath,
212
+ title: title || 'Untitled',
213
+ seoScore: score,
214
+ issues,
215
+ passed,
216
+ metadata
217
+ };
218
+ }
219
+ export function calculateValidationStats(results) {
220
+ const totalPosts = results.length;
221
+ const avgSeoScore = results.reduce((sum, r) => sum + r.seoScore, 0) / totalPosts;
222
+ const totalIssues = results.reduce((sum, r) => sum + r.issues.length, 0);
223
+ const postsWithIssues = results.filter(r => r.issues.length > 0).length;
224
+ const issuesBySeverity = {
225
+ error: results.reduce((sum, r) => sum + r.issues.filter(i => i.severity === 'error').length, 0),
226
+ warning: results.reduce((sum, r) => sum + r.issues.filter(i => i.severity === 'warning').length, 0),
227
+ info: results.reduce((sum, r) => sum + r.issues.filter(i => i.severity === 'info').length, 0),
228
+ };
229
+ const issuesByCategory = results.reduce((acc, r) => {
230
+ r.issues.forEach(issue => {
231
+ acc[issue.category] = (acc[issue.category] || 0) + 1;
232
+ });
233
+ return acc;
234
+ }, {});
235
+ return {
236
+ totalPosts,
237
+ avgSeoScore: Math.round(avgSeoScore * 10) / 10,
238
+ totalIssues,
239
+ postsWithIssues,
240
+ issuesBySeverity,
241
+ issuesByCategory
242
+ };
243
+ }
244
+ //# sourceMappingURL=seo-validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seo-validator.js","sourceRoot":"","sources":["../../src/utils/seo-validator.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@edtools/cli",
3
- "version": "0.5.0",
4
- "description": "AI-Powered Content Marketing CLI - Generate SEO-optimized content with Google Search Console analysis",
3
+ "version": "0.6.1",
4
+ "description": "AI-Powered Content Marketing CLI - Generate, validate, and optimize SEO content with data-driven insights",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -47,7 +47,8 @@
47
47
  "marked": "^11.1.1",
48
48
  "openai": "^6.15.0",
49
49
  "ora": "^8.0.1",
50
- "slugify": "^1.6.6"
50
+ "slugify": "^1.6.6",
51
+ "yaml": "^2.3.4"
51
52
  },
52
53
  "devDependencies": {
53
54
  "@types/ejs": "^3.1.5",