@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 CHANGED
@@ -7,8 +7,8 @@ const program = new Command();
7
7
 
8
8
  program
9
9
  .name("xyle")
10
- .description("SEO Intelligence Engine CLI")
11
- .version("0.7.0");
10
+ .description("SEO & AEO Intelligence Engine CLI")
11
+ .version("0.8.0");
12
12
 
13
13
  registerCommands(program);
14
14
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@xyleapp/cli",
3
- "version": "0.7.0",
4
- "description": "CLI for the Xyle SEO Intelligence Engine",
3
+ "version": "0.8.0",
4
+ "description": "CLI for the Xyle SEO & AEO Intelligence Engine",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "xyle": "bin/xyle.mjs"
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 score = data.score || 0;
140
- const color = score >= 0.7 ? "\x1b[32m" : "\x1b[33m";
141
- console.log(`${color}SEO Score: ${Math.round(score * 100)}%\x1b[0m`);
142
- console.log(`Summary: ${data.summary || ""}`);
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
- // AEO Score + Recommendations
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
- const recs = data.aeo_recommendations || [];
153
- if (recs.length) {
154
- console.log("AEO Recommendations:");
155
- for (const rec of recs) {
156
- console.log(` \x1b[36m\u2192\x1b[0m ${rec}`);
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("AEO Signals:");
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(