@edtools/cli 0.3.2 → 0.4.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/dist/index.d.ts CHANGED
@@ -141,6 +141,43 @@ declare class AdapterRegistry {
141
141
  list(): string[];
142
142
  }
143
143
 
144
+ interface GSCKeywordData {
145
+ query: string;
146
+ impressions: number;
147
+ clicks: number;
148
+ ctr: number;
149
+ position: number;
150
+ url?: string;
151
+ }
152
+ interface ContentOpportunity {
153
+ keyword: string;
154
+ impressions: number;
155
+ clicks: number;
156
+ ctr: number;
157
+ position: number;
158
+ potentialClicks: number;
159
+ priority: 'high' | 'medium' | 'low';
160
+ suggestedTitle: string;
161
+ reason: string;
162
+ }
163
+ interface GSCAnalysisResult {
164
+ totalQueries: number;
165
+ totalImpressions: number;
166
+ totalClicks: number;
167
+ avgCTR: number;
168
+ avgPosition: number;
169
+ opportunities: ContentOpportunity[];
170
+ topKeywords: GSCKeywordData[];
171
+ lowCTRPages: GSCKeywordData[];
172
+ }
173
+ interface OpportunityConfig {
174
+ minImpressions?: number;
175
+ minPosition?: number;
176
+ maxPosition?: number;
177
+ minPotentialClicks?: number;
178
+ limit?: number;
179
+ }
180
+
144
181
  declare class ContentGenerator {
145
182
  private anthropic;
146
183
  private openai;
@@ -173,4 +210,4 @@ declare class HTMLAdapter implements Adapter {
173
210
  private escapeHtml;
174
211
  }
175
212
 
176
- export { type AIProvider, type Adapter, type AdapterConfig, AdapterRegistry, type BlogPostContent, type BlogPostMetadata, type CTAData, type CodeData, type ComparisonData, ContentGenerator, type ContentSection, type GenerateConfig, type GenerateResult, HTMLAdapter, type ListData, type ProductInfo, type RelatedPost, type SEOData, type SchemaOrgData };
213
+ export { type AIProvider, type Adapter, type AdapterConfig, AdapterRegistry, type BlogPostContent, type BlogPostMetadata, type CTAData, type CodeData, type ComparisonData, ContentGenerator, type ContentOpportunity, type ContentSection, type GSCAnalysisResult, type GSCKeywordData, type GenerateConfig, type GenerateResult, HTMLAdapter, type ListData, type OpportunityConfig, type ProductInfo, type RelatedPost, type SEOData, type SchemaOrgData };
@@ -0,0 +1,5 @@
1
+ import { GSCKeywordData, ContentOpportunity, GSCAnalysisResult, OpportunityConfig } from '../types/analytics.js';
2
+ export declare function parseGSCCSV(filePath: string): Promise<GSCKeywordData[]>;
3
+ export declare function analyzeGSCData(data: GSCKeywordData[], config?: OpportunityConfig): GSCAnalysisResult;
4
+ export declare function saveOpportunities(opportunities: ContentOpportunity[], outputPath: string): Promise<void>;
5
+ //# sourceMappingURL=gsc-csv-analyzer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gsc-csv-analyzer.d.ts","sourceRoot":"","sources":["../../src/integrations/gsc-csv-analyzer.ts"],"names":[],"mappings":"AAOA,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,uBAAuB,CAAC;AAK/B,wBAAsB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAkC7E;AAKD,wBAAgB,cAAc,CAC5B,IAAI,EAAE,cAAc,EAAE,EACtB,MAAM,GAAE,iBAAsB,GAC7B,iBAAiB,CAkFnB;AAwBD,wBAAsB,iBAAiB,CACrC,aAAa,EAAE,kBAAkB,EAAE,EACnC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAQf"}
@@ -0,0 +1,110 @@
1
+ import fs from 'fs-extra';
2
+ import { parse } from 'csv-parse/sync';
3
+ export async function parseGSCCSV(filePath) {
4
+ const content = await fs.readFile(filePath, 'utf-8');
5
+ const records = parse(content, {
6
+ columns: true,
7
+ skip_empty_lines: true,
8
+ cast: (value, context) => {
9
+ if (context.column === 'Clicks' || context.column === 'Impressions') {
10
+ return parseInt(value.replace(/,/g, ''), 10);
11
+ }
12
+ if (context.column === 'CTR') {
13
+ return parseFloat(value.replace('%', '')) / 100;
14
+ }
15
+ if (context.column === 'Position') {
16
+ return parseFloat(value);
17
+ }
18
+ return value;
19
+ },
20
+ });
21
+ const data = records.map((record) => ({
22
+ query: record['Top queries'] || record['Query'] || record['query'] || '',
23
+ impressions: record['Impressions'] || record['impressions'] || 0,
24
+ clicks: record['Clicks'] || record['clicks'] || 0,
25
+ ctr: record['CTR'] || record['ctr'] || 0,
26
+ position: record['Position'] || record['position'] || 0,
27
+ url: record['Top pages'] || record['Page'] || record['url'] || undefined,
28
+ }));
29
+ return data.filter((d) => d.query && d.impressions > 0);
30
+ }
31
+ export function analyzeGSCData(data, config = {}) {
32
+ const { minImpressions = 50, minPosition = 20, maxPosition = 100, minPotentialClicks = 10, limit = 10, } = config;
33
+ const totalQueries = data.length;
34
+ const totalImpressions = data.reduce((sum, d) => sum + d.impressions, 0);
35
+ const totalClicks = data.reduce((sum, d) => sum + d.clicks, 0);
36
+ const avgCTR = totalClicks / totalImpressions || 0;
37
+ const avgPosition = data.reduce((sum, d) => sum + d.position, 0) / totalQueries || 0;
38
+ const opportunities = data
39
+ .filter((d) => d.impressions >= minImpressions && d.position >= minPosition && d.position <= maxPosition)
40
+ .map((d) => {
41
+ const estimatedCTRTop10 = 0.15;
42
+ const potentialClicks = Math.round(d.impressions * estimatedCTRTop10 - d.clicks);
43
+ let priority = 'low';
44
+ if (d.impressions > 100 && potentialClicks > 20) {
45
+ priority = 'high';
46
+ }
47
+ else if (d.impressions > 50 && potentialClicks > 10) {
48
+ priority = 'medium';
49
+ }
50
+ const suggestedTitle = generateSuggestedTitle(d.query);
51
+ const reason = `${d.impressions} impressions at position ${d.position.toFixed(1)} → potential +${potentialClicks} clicks`;
52
+ return {
53
+ keyword: d.query,
54
+ impressions: d.impressions,
55
+ clicks: d.clicks,
56
+ ctr: d.ctr,
57
+ position: d.position,
58
+ potentialClicks,
59
+ priority,
60
+ suggestedTitle,
61
+ reason,
62
+ };
63
+ })
64
+ .filter((opp) => opp.potentialClicks >= minPotentialClicks)
65
+ .sort((a, b) => {
66
+ const priorityOrder = { high: 3, medium: 2, low: 1 };
67
+ const priorityDiff = priorityOrder[b.priority] - priorityOrder[a.priority];
68
+ if (priorityDiff !== 0)
69
+ return priorityDiff;
70
+ return b.potentialClicks - a.potentialClicks;
71
+ })
72
+ .slice(0, limit);
73
+ const topKeywords = [...data]
74
+ .sort((a, b) => b.clicks - a.clicks)
75
+ .slice(0, 10);
76
+ const lowCTRPages = data
77
+ .filter((d) => d.position <= 20 && d.ctr < 0.02)
78
+ .sort((a, b) => a.ctr - b.ctr)
79
+ .slice(0, 10);
80
+ return {
81
+ totalQueries,
82
+ totalImpressions,
83
+ totalClicks,
84
+ avgCTR,
85
+ avgPosition,
86
+ opportunities,
87
+ topKeywords,
88
+ lowCTRPages,
89
+ };
90
+ }
91
+ function generateSuggestedTitle(keyword) {
92
+ const words = keyword.split(' ');
93
+ const capitalized = words
94
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
95
+ .join(' ');
96
+ const currentYear = new Date().getFullYear();
97
+ if (!capitalized.includes(currentYear.toString())) {
98
+ return `${capitalized}: Complete Guide ${currentYear}`;
99
+ }
100
+ return `${capitalized}: Complete Guide`;
101
+ }
102
+ export async function saveOpportunities(opportunities, outputPath) {
103
+ const data = {
104
+ generatedAt: new Date().toISOString(),
105
+ count: opportunities.length,
106
+ opportunities,
107
+ };
108
+ await fs.writeJson(outputPath, data, { spaces: 2 });
109
+ }
110
+ //# sourceMappingURL=gsc-csv-analyzer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gsc-csv-analyzer.js","sourceRoot":"","sources":["../../src/integrations/gsc-csv-analyzer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,305 @@
1
+ <!DOCTYPE html>
2
+ <html lang="es">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title><%= metadata.title %></title>
7
+ <meta name="description" content="<%= metadata.description %>">
8
+ <meta name="keywords" content="<%= metadata.keywords.join(', ') %>">
9
+ <meta name="author" content="<%= metadata.author %>">
10
+ <meta name="date" content="<%= metadata.datePublished %>">
11
+
12
+ <!-- Open Graph / Social Media -->
13
+ <meta property="og:type" content="article">
14
+ <meta property="og:title" content="<%= metadata.title %>">
15
+ <meta property="og:description" content="<%= metadata.description %>">
16
+ <meta property="article:published_time" content="<%= metadata.datePublished %>">
17
+ <meta property="article:author" content="<%= metadata.author %>">
18
+ <meta property="article:section" content="<%= metadata.category %>">
19
+ <% metadata.keywords.forEach(keyword => { %>
20
+ <meta property="article:tag" content="<%= keyword %>">
21
+ <% }) %>
22
+
23
+ <!-- Schema.org structured data -->
24
+ <script type="application/ld+json">
25
+ <%- schemaOrg %>
26
+ </script>
27
+
28
+ <style>
29
+ :root {
30
+ --primary-color: #2563eb;
31
+ --text-color: #1f2937;
32
+ --text-light: #6b7280;
33
+ --border-color: #e5e7eb;
34
+ --bg-light: #f9fafb;
35
+ }
36
+
37
+ * {
38
+ margin: 0;
39
+ padding: 0;
40
+ box-sizing: border-box;
41
+ }
42
+
43
+ body {
44
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
45
+ line-height: 1.6;
46
+ color: var(--text-color);
47
+ max-width: 800px;
48
+ margin: 0 auto;
49
+ padding: 2rem 1rem;
50
+ }
51
+
52
+ article {
53
+ background: white;
54
+ }
55
+
56
+ h1 {
57
+ font-size: 2.5rem;
58
+ font-weight: 700;
59
+ line-height: 1.2;
60
+ margin-bottom: 1rem;
61
+ color: var(--text-color);
62
+ }
63
+
64
+ h2 {
65
+ font-size: 1.875rem;
66
+ font-weight: 600;
67
+ margin-top: 2.5rem;
68
+ margin-bottom: 1rem;
69
+ color: var(--text-color);
70
+ }
71
+
72
+ h3 {
73
+ font-size: 1.5rem;
74
+ font-weight: 600;
75
+ margin-top: 2rem;
76
+ margin-bottom: 0.75rem;
77
+ color: var(--text-color);
78
+ }
79
+
80
+ h4 {
81
+ font-size: 1.25rem;
82
+ font-weight: 600;
83
+ margin-top: 1.5rem;
84
+ margin-bottom: 0.5rem;
85
+ color: var(--text-color);
86
+ }
87
+
88
+ p {
89
+ margin-bottom: 1rem;
90
+ color: var(--text-color);
91
+ }
92
+
93
+ a {
94
+ color: var(--primary-color);
95
+ text-decoration: none;
96
+ }
97
+
98
+ a:hover {
99
+ text-decoration: underline;
100
+ }
101
+
102
+ .intro {
103
+ font-size: 1.125rem;
104
+ color: var(--text-light);
105
+ margin-bottom: 2rem;
106
+ padding-bottom: 2rem;
107
+ border-bottom: 1px solid var(--border-color);
108
+ }
109
+
110
+ section {
111
+ margin-bottom: 2.5rem;
112
+ }
113
+
114
+ .comparison-table {
115
+ width: 100%;
116
+ border-collapse: collapse;
117
+ margin: 1.5rem 0;
118
+ font-size: 0.9375rem;
119
+ }
120
+
121
+ .comparison-table thead {
122
+ background: var(--bg-light);
123
+ }
124
+
125
+ .comparison-table th,
126
+ .comparison-table td {
127
+ padding: 0.75rem 1rem;
128
+ text-align: left;
129
+ border: 1px solid var(--border-color);
130
+ }
131
+
132
+ .comparison-table th {
133
+ font-weight: 600;
134
+ color: var(--text-color);
135
+ }
136
+
137
+ .comparison-table tbody tr:hover {
138
+ background: var(--bg-light);
139
+ }
140
+
141
+ .content-list {
142
+ margin: 1.5rem 0;
143
+ padding-left: 1.5rem;
144
+ }
145
+
146
+ .content-list li {
147
+ margin-bottom: 1rem;
148
+ }
149
+
150
+ .content-list strong {
151
+ color: var(--text-color);
152
+ display: block;
153
+ margin-bottom: 0.25rem;
154
+ }
155
+
156
+ .code-block {
157
+ margin: 1.5rem 0;
158
+ background: var(--bg-light);
159
+ border: 1px solid var(--border-color);
160
+ border-radius: 0.375rem;
161
+ overflow: hidden;
162
+ }
163
+
164
+ .code-caption {
165
+ padding: 0.5rem 1rem;
166
+ background: #e5e7eb;
167
+ font-size: 0.875rem;
168
+ font-weight: 600;
169
+ border-bottom: 1px solid var(--border-color);
170
+ }
171
+
172
+ .code-block pre {
173
+ padding: 1rem;
174
+ overflow-x: auto;
175
+ }
176
+
177
+ .code-block code {
178
+ font-family: 'Monaco', 'Courier New', monospace;
179
+ font-size: 0.875rem;
180
+ line-height: 1.5;
181
+ }
182
+
183
+ .conclusion {
184
+ margin-top: 3rem;
185
+ padding-top: 2rem;
186
+ border-top: 1px solid var(--border-color);
187
+ }
188
+
189
+ .cta {
190
+ margin-top: 3rem;
191
+ padding: 2rem;
192
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
193
+ border-radius: 0.5rem;
194
+ text-align: center;
195
+ }
196
+
197
+ .cta a {
198
+ display: inline-block;
199
+ padding: 1rem 2rem;
200
+ background: white;
201
+ color: var(--primary-color);
202
+ font-weight: 600;
203
+ font-size: 1.125rem;
204
+ border-radius: 0.375rem;
205
+ transition: transform 0.2s;
206
+ }
207
+
208
+ .cta a:hover {
209
+ transform: translateY(-2px);
210
+ text-decoration: none;
211
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
212
+ }
213
+
214
+ .related-posts {
215
+ margin-top: 4rem;
216
+ padding-top: 2rem;
217
+ border-top: 2px solid var(--border-color);
218
+ }
219
+
220
+ .related-posts h3 {
221
+ margin-top: 0;
222
+ margin-bottom: 1.5rem;
223
+ }
224
+
225
+ .related-posts ul {
226
+ list-style: none;
227
+ padding: 0;
228
+ }
229
+
230
+ .related-posts li {
231
+ margin-bottom: 0.75rem;
232
+ }
233
+
234
+ .related-posts a {
235
+ font-weight: 500;
236
+ }
237
+
238
+ .seo-badge {
239
+ display: inline-block;
240
+ padding: 0.25rem 0.75rem;
241
+ background: <%= seoScore >= 80 ? '#10b981' : seoScore >= 60 ? '#f59e0b' : '#ef4444' %>;
242
+ color: white;
243
+ border-radius: 9999px;
244
+ font-size: 0.875rem;
245
+ font-weight: 600;
246
+ margin-bottom: 1rem;
247
+ }
248
+ </style>
249
+ </head>
250
+ <body>
251
+ <article>
252
+ <!-- SEO Badge (hidden by default, can be shown in preview mode) -->
253
+ <!-- <div class="seo-badge">SEO Score: <%= seoScore %>/100</div> -->
254
+
255
+ <h1><%= metadata.title %></h1>
256
+
257
+ <div class="intro">
258
+ <%- intro %>
259
+ </div>
260
+
261
+ <% sections.forEach(section => { %>
262
+ <section>
263
+ <h<%= section.level %>><%= section.heading %></h<%= section.level %>>
264
+ <%- section.content %>
265
+ </section>
266
+ <% }) %>
267
+
268
+ <div class="conclusion">
269
+ <%- conclusion %>
270
+ </div>
271
+
272
+ <div class="cta">
273
+ <a href="<%= cta.url %>"><%= cta.text %></a>
274
+ </div>
275
+
276
+ <% if (relatedPosts && relatedPosts.length > 0) { %>
277
+ <div class="related-posts">
278
+ <h3>Artículos relacionados</h3>
279
+ <ul>
280
+ <% relatedPosts.forEach(post => { %>
281
+ <li>
282
+ <a href="<%= post.url %>"><%= post.title %></a>
283
+ <% if (post.excerpt) { %>
284
+ <p style="color: var(--text-light); font-size: 0.875rem; margin-top: 0.25rem;">
285
+ <%= post.excerpt %>
286
+ </p>
287
+ <% } %>
288
+ </li>
289
+ <% }) %>
290
+ </ul>
291
+ </div>
292
+ <% } %>
293
+ </article>
294
+
295
+ <!-- AI-friendly metadata in comments for LLM parsing -->
296
+ <!--
297
+ METADATA:
298
+ - Title: <%= metadata.title %>
299
+ - Category: <%= metadata.category %>
300
+ - Keywords: <%= metadata.keywords.join(', ') %>
301
+ - Published: <%= metadata.datePublished %>
302
+ - SEO Score: <%= seoScore %>/100
303
+ -->
304
+ </body>
305
+ </html>
@@ -0,0 +1,37 @@
1
+ export interface GSCKeywordData {
2
+ query: string;
3
+ impressions: number;
4
+ clicks: number;
5
+ ctr: number;
6
+ position: number;
7
+ url?: string;
8
+ }
9
+ export interface ContentOpportunity {
10
+ keyword: string;
11
+ impressions: number;
12
+ clicks: number;
13
+ ctr: number;
14
+ position: number;
15
+ potentialClicks: number;
16
+ priority: 'high' | 'medium' | 'low';
17
+ suggestedTitle: string;
18
+ reason: string;
19
+ }
20
+ export interface GSCAnalysisResult {
21
+ totalQueries: number;
22
+ totalImpressions: number;
23
+ totalClicks: number;
24
+ avgCTR: number;
25
+ avgPosition: number;
26
+ opportunities: ContentOpportunity[];
27
+ topKeywords: GSCKeywordData[];
28
+ lowCTRPages: GSCKeywordData[];
29
+ }
30
+ export interface OpportunityConfig {
31
+ minImpressions?: number;
32
+ minPosition?: number;
33
+ maxPosition?: number;
34
+ minPotentialClicks?: number;
35
+ limit?: number;
36
+ }
37
+ //# sourceMappingURL=analytics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../../src/types/analytics.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAKD,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB;AAKD,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,kBAAkB,EAAE,CAAC;IACpC,WAAW,EAAE,cAAc,EAAE,CAAC;IAC9B,WAAW,EAAE,cAAc,EAAE,CAAC;CAC/B;AAKD,MAAM,WAAW,iBAAiB;IAChC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=analytics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analytics.js","sourceRoot":"","sources":["../../src/types/analytics.ts"],"names":[],"mappings":""}
@@ -1,3 +1,4 @@
1
1
  export * from './content.js';
2
2
  export * from './adapter.js';
3
+ export * from './analytics.js';
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAIA,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAIA,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC"}
@@ -1,3 +1,4 @@
1
1
  export * from './content.js';
2
2
  export * from './adapter.js';
3
+ export * from './analytics.js';
3
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAIA,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAIA,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC"}
@@ -0,0 +1,7 @@
1
+ export declare function generateBanner(): string;
2
+ export declare function showWelcome(): void;
3
+ export declare function successBox(message: string): string;
4
+ export declare function errorBox(message: string): string;
5
+ export declare function infoBox(title: string, content: string): string;
6
+ export declare function warningBox(message: string): string;
7
+ //# sourceMappingURL=banner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"banner.d.ts","sourceRoot":"","sources":["../../src/ui/banner.ts"],"names":[],"mappings":"AAQA,wBAAgB,cAAc,IAAI,MAAM,CAsBvC;AAKD,wBAAgB,WAAW,IAAI,IAAI,CAYlC;AAiBD,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAOlD;AAKD,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAOhD;AAKD,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAO9D;AAKD,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAOlD"}
@@ -0,0 +1,76 @@
1
+ import figlet from 'figlet';
2
+ import gradient from 'gradient-string';
3
+ import boxen from 'boxen';
4
+ import chalk from 'chalk';
5
+ export function generateBanner() {
6
+ const asciiArt = figlet.textSync('edtools', {
7
+ font: 'ANSI Shadow',
8
+ horizontalLayout: 'default',
9
+ verticalLayout: 'default',
10
+ });
11
+ const coloredAscii = gradient(['#00D9FF', '#00FF9F'])(asciiArt);
12
+ const tagline = chalk.gray('AI-Powered Content Marketing CLI');
13
+ const version = chalk.dim(`v${getVersion()}`);
14
+ const content = `${coloredAscii}\n\n${tagline} ${version}`;
15
+ return boxen(content, {
16
+ padding: 1,
17
+ margin: 1,
18
+ borderStyle: 'round',
19
+ borderColor: 'cyan',
20
+ dimBorder: false,
21
+ });
22
+ }
23
+ export function showWelcome() {
24
+ console.log(generateBanner());
25
+ console.log('');
26
+ console.log(chalk.bold.cyan(' Quick Start:'));
27
+ console.log(chalk.gray(' ────────────'));
28
+ console.log('');
29
+ console.log(` ${chalk.cyan('edtools init')} ${chalk.gray('Initialize your project')}`);
30
+ console.log(` ${chalk.cyan('edtools generate')} ${chalk.gray('Generate SEO-optimized posts')}`);
31
+ console.log(` ${chalk.cyan('edtools analyze')} ${chalk.gray('Analyze Google Search Console CSV')}`);
32
+ console.log('');
33
+ console.log(chalk.gray(' Run ') + chalk.cyan('edtools --help') + chalk.gray(' for all commands'));
34
+ console.log('');
35
+ }
36
+ function getVersion() {
37
+ try {
38
+ return '0.4.0';
39
+ }
40
+ catch {
41
+ return 'dev';
42
+ }
43
+ }
44
+ export function successBox(message) {
45
+ return boxen(chalk.green('✓ ') + message, {
46
+ padding: 1,
47
+ margin: 1,
48
+ borderStyle: 'round',
49
+ borderColor: 'green',
50
+ });
51
+ }
52
+ export function errorBox(message) {
53
+ return boxen(chalk.red('✗ ') + message, {
54
+ padding: 1,
55
+ margin: 1,
56
+ borderStyle: 'round',
57
+ borderColor: 'red',
58
+ });
59
+ }
60
+ export function infoBox(title, content) {
61
+ return boxen(`${chalk.cyan.bold(title)}\n\n${content}`, {
62
+ padding: 1,
63
+ margin: 1,
64
+ borderStyle: 'round',
65
+ borderColor: 'cyan',
66
+ });
67
+ }
68
+ export function warningBox(message) {
69
+ return boxen(chalk.yellow('⚠ ') + message, {
70
+ padding: 1,
71
+ margin: 1,
72
+ borderStyle: 'round',
73
+ borderColor: 'yellow',
74
+ });
75
+ }
76
+ //# sourceMappingURL=banner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"banner.js","sourceRoot":"","sources":["../../src/ui/banner.ts"],"names":[],"mappings":"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"}