seo-intel 1.4.5 → 1.4.7

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 (3) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/package.json +1 -1
  3. package/server.js +43 -10
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.4.7 (2026-04-09)
4
+
5
+ ### Export: profiles are actions only
6
+ - Removed schemas from all export profiles — pure inventory, not actionable
7
+ - "No schema" issues already surfaced in technical section
8
+ - Raw Full Export (ZIP) still includes everything for data access
9
+
10
+ ## 1.4.6 (2026-04-09)
11
+
12
+ ### Export: rich actionable content
13
+ - Insights export now renders type-specific tables (quick wins show issue + fix + impact, keyword gaps show coverage, etc.)
14
+ - Schema markup export scoped to own site only — no competitor schema dumps
15
+ - SKILL.md updated with export profiles documentation
16
+
3
17
  ## 1.4.5 (2026-04-09)
4
18
 
5
19
  ### Export: actionable summaries only
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "seo-intel",
3
- "version": "1.4.5",
3
+ "version": "1.4.7",
4
4
  "description": "Local Ahrefs-style SEO competitor intelligence. Crawl → SQLite → cloud analysis.",
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN LICENSE",
package/server.js CHANGED
@@ -611,7 +611,7 @@ async function handleRequest(req, res) {
611
611
  // ── Profile definitions: which sections + which insight types matter ──
612
612
  const PROFILES = {
613
613
  dev: {
614
- sections: ['technical', 'schemas', 'links', 'headings', 'watch', 'insights'],
614
+ sections: ['technical', 'links', 'headings', 'watch', 'insights'],
615
615
  insightTypes: ['technical_gap', 'quick_win', 'site_watch'],
616
616
  label: 'Developer',
617
617
  },
@@ -827,12 +827,7 @@ async function handleRequest(req, res) {
827
827
  }
828
828
  return issues;
829
829
  }
830
- case 'schemas': {
831
- if (!Array.isArray(data) || prof !== 'dev') return data;
832
- // Dev: pages missing schema are more useful — but we only have pages WITH schema here
833
- // So return all (schema gaps come from technical section's has_schema=false)
834
- return data;
835
- }
830
+ case 'schemas': return data; // raw only — not in any profile
836
831
  case 'aeo': {
837
832
  if (!Array.isArray(data)) return data;
838
833
  if (prof === 'content') {
@@ -905,9 +900,47 @@ async function handleRequest(req, res) {
905
900
  for (const r of data) { (grouped[r._type] ||= []).push(r); }
906
901
  for (const [type, items] of Object.entries(grouped)) {
907
902
  md += `### ${type.replace(/_/g, ' ')} (${items.length})\n\n`;
908
- for (const item of items) {
909
- const desc = item.phrase || item.keyword || item.title || item.page || item.message || JSON.stringify(item).slice(0, 120);
910
- md += `- ${desc}\n`;
903
+ switch (type) {
904
+ case 'quick_win':
905
+ md += '| Page | Issue | Fix | Impact |\n|------|-------|-----|--------|\n';
906
+ for (const i of items) md += `| ${i.page || ''} | ${i.issue || ''} | ${i.fix || ''} | ${i.impact || ''} |\n`;
907
+ break;
908
+ case 'keyword_gap':
909
+ md += '| Keyword | Your Coverage | Competitor Coverage |\n|---------|--------------|--------------------|\n';
910
+ for (const i of items) md += `| ${i.keyword || ''} | ${i.your_coverage || i.target_count || 'none'} | ${i.competitor_coverage || i.competitor_count || ''} |\n`;
911
+ break;
912
+ case 'long_tail':
913
+ md += '| Phrase | Parent Keyword | Opportunity |\n|-------|----------------|-------------|\n';
914
+ for (const i of items) md += `| ${i.phrase || ''} | ${i.parent || i.keyword || ''} | ${i.opportunity || i.rationale || ''} |\n`;
915
+ break;
916
+ case 'new_page':
917
+ md += '| Title | Target Keyword | Rationale |\n|-------|----------------|----------|\n';
918
+ for (const i of items) md += `| ${i.title || ''} | ${i.target_keyword || ''} | ${i.rationale || ''} |\n`;
919
+ break;
920
+ case 'content_gap':
921
+ md += '| Topic | Gap | Suggestion |\n|-------|-----|------------|\n';
922
+ for (const i of items) md += `| ${i.topic || ''} | ${i.gap || ''} | ${i.suggestion || ''} |\n`;
923
+ break;
924
+ case 'technical_gap':
925
+ md += '| Issue | Affected | Recommendation |\n|-------|----------|----------------|\n';
926
+ for (const i of items) md += `| ${i.gap || i.issue || ''} | ${i.affected || i.pages || ''} | ${i.recommendation || i.fix || ''} |\n`;
927
+ break;
928
+ case 'citability_gap':
929
+ md += '| URL | Score | Weakest Signals |\n|-----|-------|----------------|\n';
930
+ for (const i of items) md += `| ${i.url || ''} | ${i.score ?? ''} | ${i.weak_signals || ''} |\n`;
931
+ break;
932
+ case 'keyword_inventor':
933
+ md += '| Phrase | Cluster | Search Potential |\n|-------|---------|------------------|\n';
934
+ for (const i of items) md += `| ${i.phrase || ''} | ${i.cluster || ''} | ${i.potential || i.volume || ''} |\n`;
935
+ break;
936
+ case 'site_watch':
937
+ md += '| URL | Event | Details |\n|-----|-------|--------|\n';
938
+ for (const i of items) md += `| ${i.url || ''} | ${i.event_type || ''} | ${i.details || ''} |\n`;
939
+ break;
940
+ default:
941
+ for (const i of items) {
942
+ md += `- ${i.phrase || i.keyword || i.title || i.page || i.message || JSON.stringify(i).slice(0, 120)}\n`;
943
+ }
911
944
  }
912
945
  md += '\n';
913
946
  }