@xyleapp/cli 0.7.0 → 0.8.0
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.
- package/bin/xyle.mjs +2 -2
- package/package.json +2 -2
- package/src/commands.mjs +135 -13
package/bin/xyle.mjs
CHANGED
package/package.json
CHANGED
package/src/commands.mjs
CHANGED
|
@@ -136,24 +136,61 @@ export function registerCommands(program) {
|
|
|
136
136
|
if (opts.json) {
|
|
137
137
|
console.log(printJson(data));
|
|
138
138
|
} else {
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
139
|
+
const scoreBar = (label, score) => {
|
|
140
|
+
const pct = Math.round((score || 0) * 100);
|
|
141
|
+
const filled = Math.round(pct / 5);
|
|
142
|
+
const bar = "\u2588".repeat(filled) + "\u2591".repeat(20 - filled);
|
|
143
|
+
const color = pct >= 70 ? "\x1b[32m" : pct >= 40 ? "\x1b[33m" : "\x1b[31m";
|
|
144
|
+
return ` ${label.padEnd(22)} ${color}${bar} ${pct}%\x1b[0m`;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
// SEO Score Breakdown
|
|
148
|
+
const bd = data.seo_breakdown;
|
|
149
|
+
if (bd) {
|
|
150
|
+
console.log(`\n\x1b[1mSEO Score Breakdown\x1b[0m`);
|
|
151
|
+
console.log(scoreBar("Overall", bd.overall));
|
|
152
|
+
console.log(scoreBar("Technical (25%)", bd.technical));
|
|
153
|
+
console.log(scoreBar("On-Page (30%)", bd.on_page));
|
|
154
|
+
console.log(scoreBar("Content (25%)", bd.content));
|
|
155
|
+
console.log(scoreBar("Links (20%)", bd.links));
|
|
156
|
+
} else {
|
|
157
|
+
const score = data.score || 0;
|
|
158
|
+
const color = score >= 0.7 ? "\x1b[32m" : "\x1b[33m";
|
|
159
|
+
console.log(`${color}SEO Score: ${Math.round(score * 100)}%\x1b[0m`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
console.log(`\nSummary: ${data.summary || ""}`);
|
|
143
163
|
const missing = data.missing_topics || [];
|
|
144
164
|
if (missing.length) {
|
|
145
165
|
console.log(`Missing topics: ${missing.join(", ")}`);
|
|
146
166
|
}
|
|
147
|
-
|
|
167
|
+
|
|
168
|
+
// AEO Score
|
|
148
169
|
if (data.aeo_score != null) {
|
|
149
170
|
const aeoColor = data.aeo_score >= 0.7 ? "\x1b[32m" : "\x1b[33m";
|
|
150
171
|
console.log(`${aeoColor}AEO Score: ${Math.round(data.aeo_score * 100)}%\x1b[0m`);
|
|
151
172
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
173
|
+
|
|
174
|
+
// Structured Recommendations
|
|
175
|
+
const structured = data.recommendations || [];
|
|
176
|
+
if (structured.length) {
|
|
177
|
+
console.log(`\n\x1b[1mRecommendations\x1b[0m`);
|
|
178
|
+
const priorityColors = { critical: "\x1b[31m", important: "\x1b[33m", nice_to_have: "\x1b[2m" };
|
|
179
|
+
const priorityLabels = { critical: "CRITICAL", important: "IMPORTANT", nice_to_have: "NICE" };
|
|
180
|
+
for (const rec of structured) {
|
|
181
|
+
const pc = priorityColors[rec.priority] || "\x1b[0m";
|
|
182
|
+
const pl = priorityLabels[rec.priority] || rec.priority;
|
|
183
|
+
console.log(` ${pc}[${pl}]\x1b[0m \x1b[36m${rec.category}\x1b[0m ${rec.title}`);
|
|
184
|
+
console.log(` \x1b[2m${rec.description}\x1b[0m`);
|
|
185
|
+
}
|
|
186
|
+
} else {
|
|
187
|
+
// Fallback to old AEO recommendations
|
|
188
|
+
const recs = data.aeo_recommendations || [];
|
|
189
|
+
if (recs.length) {
|
|
190
|
+
console.log("\nAEO Recommendations:");
|
|
191
|
+
for (const rec of recs) {
|
|
192
|
+
console.log(` \x1b[36m\u2192\x1b[0m ${rec}`);
|
|
193
|
+
}
|
|
157
194
|
}
|
|
158
195
|
}
|
|
159
196
|
}
|
|
@@ -199,6 +236,16 @@ export function registerCommands(program) {
|
|
|
199
236
|
if (opts.json) {
|
|
200
237
|
console.log(printJson(data));
|
|
201
238
|
} else {
|
|
239
|
+
const check = (v) => (v ? "\x1b[32m\u2713\x1b[0m" : "\x1b[31m\u2717\x1b[0m");
|
|
240
|
+
const scoreBar = (label, score) => {
|
|
241
|
+
const pct = Math.round((score || 0) * 100);
|
|
242
|
+
const filled = Math.round(pct / 5);
|
|
243
|
+
const bar = "\u2588".repeat(filled) + "\u2591".repeat(20 - filled);
|
|
244
|
+
const color = pct >= 70 ? "\x1b[32m" : pct >= 40 ? "\x1b[33m" : "\x1b[31m";
|
|
245
|
+
return ` ${label.padEnd(22)} ${color}${bar} ${pct}%\x1b[0m`;
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
console.log(`\n\x1b[1mCrawl Results\x1b[0m`);
|
|
202
249
|
console.log(`Title: ${data.title || "-"}`);
|
|
203
250
|
console.log(`Meta desc: ${data.meta_desc || "-"}`);
|
|
204
251
|
console.log(`Word count: ${data.word_count || 0}`);
|
|
@@ -209,11 +256,79 @@ export function registerCommands(program) {
|
|
|
209
256
|
console.log(` ${h}`);
|
|
210
257
|
}
|
|
211
258
|
}
|
|
259
|
+
|
|
260
|
+
// Technical SEO
|
|
261
|
+
const tech = data.technical_seo;
|
|
262
|
+
if (tech) {
|
|
263
|
+
console.log(`\n\x1b[1mTechnical SEO\x1b[0m`);
|
|
264
|
+
console.log(
|
|
265
|
+
` ${check(tech.is_https)} HTTPS ${check(tech.has_canonical)} Canonical ${check(tech.has_viewport)} Viewport`
|
|
266
|
+
);
|
|
267
|
+
console.log(
|
|
268
|
+
` ${check(tech.has_charset)} Charset ${check(tech.has_lang)} Lang (${tech.lang_value || "-"}) ${check(tech.has_favicon)} Favicon`
|
|
269
|
+
);
|
|
270
|
+
console.log(
|
|
271
|
+
` ${check(tech.has_og_title)} OG Title ${check(tech.has_og_desc)} OG Description ${check(tech.has_og_image)} OG Image`
|
|
272
|
+
);
|
|
273
|
+
console.log(
|
|
274
|
+
` ${check(tech.has_twitter_card)} Twitter Card ${check(tech.has_robots_meta)} Robots Meta ${check(tech.h1_count === 1)} H1 Count: ${tech.h1_count}`
|
|
275
|
+
);
|
|
276
|
+
console.log(` Title: ${tech.title_length} chars Meta desc: ${tech.meta_desc_length} chars Images without alt: ${tech.images_without_alt}/${tech.image_count}`);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Rendering
|
|
280
|
+
const rendering = data.rendering;
|
|
281
|
+
if (rendering) {
|
|
282
|
+
console.log(`\n\x1b[1mRendering\x1b[0m`);
|
|
283
|
+
const fwLabel = rendering.detected_framework ? `\x1b[35m${rendering.detected_framework}\x1b[0m` : "none";
|
|
284
|
+
const rtColor = rendering.seo_impact === "good" ? "\x1b[32m" : rendering.seo_impact === "poor" ? "\x1b[31m" : "\x1b[33m";
|
|
285
|
+
console.log(` Framework: ${fwLabel} Type: ${rtColor}${rendering.rendering_type.toUpperCase()}\x1b[0m SEO Impact: ${rtColor}${rendering.seo_impact}\x1b[0m`);
|
|
286
|
+
console.log(` JS Bundles: ${rendering.js_bundle_count} DOM Content: ${rendering.initial_dom_has_content ? "rich" : "thin"} Client Router: ${rendering.uses_client_router ? "yes" : "no"}`);
|
|
287
|
+
if (rendering.rendering_notes) {
|
|
288
|
+
console.log(` \x1b[2m${rendering.rendering_notes}\x1b[0m`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Content Quality
|
|
293
|
+
const cq = data.content_quality;
|
|
294
|
+
if (cq) {
|
|
295
|
+
console.log(`\n\x1b[1mContent Quality\x1b[0m`);
|
|
296
|
+
console.log(` Words: ${cq.word_count} Sentences: ${cq.sentence_count} Paragraphs: ${cq.paragraph_count} Reading time: ${cq.reading_time_minutes} min`);
|
|
297
|
+
console.log(` Flesch Reading Ease: ${cq.flesch_reading_ease} Grade Level: ${cq.flesch_kincaid_grade} Vocabulary: ${Math.round(cq.vocabulary_richness * 100)}%`);
|
|
298
|
+
console.log(scoreBar("Humanness Score", cq.humanness_score));
|
|
299
|
+
console.log(scoreBar("Content Depth", cq.content_depth_score));
|
|
300
|
+
console.log(scoreBar("E-E-A-T Score", cq.eeat_score));
|
|
301
|
+
console.log(` ${check(cq.has_author)} Author ${check(cq.has_publish_date)} Publish Date ${check(cq.has_updated_date)} Updated Date ${check(cq.has_sources_section)} Sources`);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Link Analysis
|
|
305
|
+
const links = data.link_analysis;
|
|
306
|
+
if (links) {
|
|
307
|
+
console.log(`\n\x1b[1mLink Analysis\x1b[0m`);
|
|
308
|
+
console.log(` Internal: ${links.internal_link_count} External: ${links.external_link_count} Nofollow: ${links.nofollow_link_count} In content: ${links.links_in_content}`);
|
|
309
|
+
const aqColor = links.anchor_quality === "good" ? "\x1b[32m" : links.anchor_quality === "poor" ? "\x1b[31m" : "\x1b[33m";
|
|
310
|
+
console.log(` Anchor quality: ${aqColor}${links.anchor_quality}\x1b[0m Generic anchors: ${links.generic_anchor_count} External ratio: ${Math.round(links.external_link_ratio * 100)}%`);
|
|
311
|
+
if (links.broken_link_sample && links.broken_link_sample.length) {
|
|
312
|
+
console.log(` \x1b[31mBroken links:\x1b[0m`);
|
|
313
|
+
for (const bl of links.broken_link_sample) {
|
|
314
|
+
console.log(` \x1b[31m\u2717\x1b[0m ${bl}`);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Blog Signals
|
|
320
|
+
const blog = data.blog_signals;
|
|
321
|
+
if (blog && blog.is_blog_post) {
|
|
322
|
+
console.log(`\n\x1b[1mBlog Signals\x1b[0m`);
|
|
323
|
+
const freshColor = blog.content_freshness === "fresh" ? "\x1b[32m" : blog.content_freshness === "stale" ? "\x1b[31m" : "\x1b[33m";
|
|
324
|
+
console.log(` Author: ${blog.has_author_name || "-"} Published: ${blog.has_publish_date || "-"} Freshness: ${freshColor}${blog.content_freshness}\x1b[0m Reading time: ${blog.estimated_reading_time} min`);
|
|
325
|
+
console.log(` ${check(blog.has_social_sharing)} Social sharing`);
|
|
326
|
+
}
|
|
327
|
+
|
|
212
328
|
// AEO Signals
|
|
213
329
|
const aeo = data.aeo_signals;
|
|
214
330
|
if (aeo) {
|
|
215
|
-
console.log(
|
|
216
|
-
const check = (v) => (v ? "\x1b[32m\u2713\x1b[0m" : "\x1b[31m\u2717\x1b[0m");
|
|
331
|
+
console.log(`\n\x1b[1mAEO Signals\x1b[0m`);
|
|
217
332
|
console.log(
|
|
218
333
|
` ${check(aeo.has_article_schema)} Article Schema ${check(aeo.has_faq_schema)} FAQPage Schema ${check(aeo.has_howto_schema)} HowTo Schema`
|
|
219
334
|
);
|
|
@@ -221,15 +336,22 @@ export function registerCommands(program) {
|
|
|
221
336
|
` ${check(aeo.heading_hierarchy_valid)} Heading Hierarchy ${check(aeo.has_faq_content)} FAQ Content ${check(aeo.has_date_modified)} Date Modified`
|
|
222
337
|
);
|
|
223
338
|
console.log(
|
|
224
|
-
` ${check(aeo.has_speakable_schema)} Speakable Schema`
|
|
339
|
+
` ${check(aeo.has_speakable_schema)} Speakable Schema ${check(aeo.has_breadcrumb_schema)} Breadcrumb Schema ${check(aeo.has_org_schema)} Org Schema`
|
|
340
|
+
);
|
|
341
|
+
console.log(
|
|
342
|
+
` ${check(aeo.has_video_schema)} Video Schema`
|
|
225
343
|
);
|
|
226
344
|
console.log(
|
|
227
345
|
` Lists: ${aeo.list_count} Tables: ${aeo.table_count} Concise answers: ${aeo.concise_answer_count} Definitions: ${aeo.definition_count} Citations: ${aeo.citation_count}`
|
|
228
346
|
);
|
|
347
|
+
console.log(
|
|
348
|
+
` Questions: ${aeo.question_count} Direct answer ratio: ${(aeo.direct_answer_ratio * 100).toFixed(0)}%`
|
|
349
|
+
);
|
|
229
350
|
if (aeo.avg_sentence_length > 0) {
|
|
230
351
|
console.log(` Avg sentence length: ${aeo.avg_sentence_length} words`);
|
|
231
352
|
}
|
|
232
353
|
}
|
|
354
|
+
|
|
233
355
|
const wc = data.word_count || 0;
|
|
234
356
|
if (wc > 0 && wc < 50) {
|
|
235
357
|
console.log(
|