@edtools/cli 0.7.0 → 0.7.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.
package/dist/cli/index.js CHANGED
@@ -1,15 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ContentGenerator
4
- } from "../chunk-TROAGFSZ.js";
4
+ } from "../chunk-OVFZRN7O.js";
5
5
  import "../chunk-INVECVSW.js";
6
6
 
7
7
  // src/cli/index.ts
8
8
  import { Command } from "commander";
9
+ import fs11 from "fs-extra";
10
+ import path9 from "path";
11
+ import { fileURLToPath as fileURLToPath2 } from "url";
9
12
 
10
13
  // src/cli/commands/init.ts
11
- import fs2 from "fs-extra";
12
- import path2 from "path";
14
+ import fs3 from "fs-extra";
15
+ import path3 from "path";
13
16
  import chalk2 from "chalk";
14
17
  import ora from "ora";
15
18
  import inquirer from "inquirer";
@@ -20,6 +23,9 @@ import figlet from "figlet";
20
23
  import gradient from "gradient-string";
21
24
  import boxen from "boxen";
22
25
  import chalk from "chalk";
26
+ import fs from "fs-extra";
27
+ import path from "path";
28
+ import { fileURLToPath } from "url";
23
29
  function generateBanner() {
24
30
  const asciiArt = figlet.textSync("edtools", {
25
31
  font: "ANSI Shadow",
@@ -63,8 +69,24 @@ function showWelcome() {
63
69
  }
64
70
  function getVersion() {
65
71
  try {
66
- return "0.6.0";
67
- } catch {
72
+ const __filename = fileURLToPath(import.meta.url);
73
+ const __dirname = path.dirname(__filename);
74
+ const possiblePaths = [
75
+ path.join(__dirname, "../../package.json"),
76
+ // From dist/ui/
77
+ path.join(__dirname, "../../../package.json"),
78
+ // From src/ui/
79
+ path.join(__dirname, "../../../../package.json")
80
+ // From npm global install
81
+ ];
82
+ for (const pkgPath of possiblePaths) {
83
+ if (fs.existsSync(pkgPath)) {
84
+ const pkg = fs.readJsonSync(pkgPath);
85
+ return pkg.version || "unknown";
86
+ }
87
+ }
88
+ return "unknown";
89
+ } catch (error) {
68
90
  return "dev";
69
91
  }
70
92
  }
@@ -104,8 +126,8 @@ function warningBox(message) {
104
126
  }
105
127
 
106
128
  // src/utils/hosting-detection.ts
107
- import fs from "fs";
108
- import path from "path";
129
+ import fs2 from "fs";
130
+ import path2 from "path";
109
131
  import yaml from "yaml";
110
132
  var detectors = [
111
133
  {
@@ -181,9 +203,9 @@ var detectors = [
181
203
  file: "next.config.js",
182
204
  parse: (content, projectPath) => {
183
205
  if (content.includes("output:") && content.includes("export")) {
184
- const packageJsonPath = path.join(projectPath, "package.json");
185
- if (fs.existsSync(packageJsonPath)) {
186
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
206
+ const packageJsonPath = path2.join(projectPath, "package.json");
207
+ if (fs2.existsSync(packageJsonPath)) {
208
+ const packageJson = JSON.parse(fs2.readFileSync(packageJsonPath, "utf-8"));
187
209
  const buildScript = packageJson.scripts?.build || "";
188
210
  if (buildScript.includes("out")) {
189
211
  return {
@@ -205,10 +227,10 @@ var detectors = [
205
227
  ];
206
228
  function detectHostingConfig(projectPath) {
207
229
  for (const detector of detectors) {
208
- const configPath = path.join(projectPath, detector.file);
209
- if (fs.existsSync(configPath)) {
230
+ const configPath = path2.join(projectPath, detector.file);
231
+ if (fs2.existsSync(configPath)) {
210
232
  try {
211
- const content = fs.readFileSync(configPath, "utf-8");
233
+ const content = fs2.readFileSync(configPath, "utf-8");
212
234
  const result = detector.parse(content, projectPath);
213
235
  if (result) {
214
236
  return result;
@@ -254,9 +276,9 @@ function getSuggestedOutputDir(projectPath) {
254
276
  // src/cli/commands/init.ts
255
277
  async function initCommand(options) {
256
278
  console.log(chalk2.cyan.bold("\n\u{1F680} Initializing Edtools...\n"));
257
- const projectPath = path2.resolve(options.path);
258
- const configPath = path2.join(projectPath, "edtools.config.js");
259
- if (await fs2.pathExists(configPath)) {
279
+ const projectPath = path3.resolve(options.path);
280
+ const configPath = path3.join(projectPath, "edtools.config.js");
281
+ if (await fs3.pathExists(configPath)) {
260
282
  const { overwrite } = await inquirer.prompt([
261
283
  {
262
284
  type: "confirm",
@@ -273,9 +295,9 @@ async function initCommand(options) {
273
295
  const spinner = ora("Analyzing your landing page...").start();
274
296
  let productInfo = {};
275
297
  try {
276
- const indexPath = path2.join(projectPath, "index.html");
277
- if (await fs2.pathExists(indexPath)) {
278
- const html = await fs2.readFile(indexPath, "utf-8");
298
+ const indexPath = path3.join(projectPath, "index.html");
299
+ if (await fs3.pathExists(indexPath)) {
300
+ const html = await fs3.readFile(indexPath, "utf-8");
279
301
  const $ = loadCheerio(html);
280
302
  productInfo = {
281
303
  name: $("title").text() || $("h1").first().text() || "My Product",
@@ -543,17 +565,17 @@ async function initCommand(options) {
543
565
  },
544
566
  };
545
567
  `;
546
- await fs2.writeFile(configPath, config, "utf-8");
547
- const edtoolsDir = path2.join(projectPath, ".edtools");
548
- await fs2.ensureDir(edtoolsDir);
568
+ await fs3.writeFile(configPath, config, "utf-8");
569
+ const edtoolsDir = path3.join(projectPath, ".edtools");
570
+ await fs3.ensureDir(edtoolsDir);
549
571
  const edtoolsConfig = {
550
572
  apiKey: apiKeyAnswer.apiKey,
551
573
  provider: finalProductInfo.preferredProvider
552
574
  };
553
- const edtoolsConfigPath = path2.join(edtoolsDir, "config.json");
554
- await fs2.writeFile(edtoolsConfigPath, JSON.stringify(edtoolsConfig, null, 2), "utf-8");
555
- const gitignorePath = path2.join(edtoolsDir, ".gitignore");
556
- await fs2.writeFile(gitignorePath, "*\n!.gitignore\n", "utf-8");
575
+ const edtoolsConfigPath = path3.join(edtoolsDir, "config.json");
576
+ await fs3.writeFile(edtoolsConfigPath, JSON.stringify(edtoolsConfig, null, 2), "utf-8");
577
+ const gitignorePath = path3.join(edtoolsDir, ".gitignore");
578
+ await fs3.writeFile(gitignorePath, "*\n!.gitignore\n", "utf-8");
557
579
  console.log("");
558
580
  console.log(successBox("Configuration created successfully!"));
559
581
  const filesCreated = `${chalk2.cyan("Files created:")}
@@ -573,8 +595,8 @@ ${chalk2.gray("API key stored securely (gitignored)")}`;
573
595
  }
574
596
 
575
597
  // src/cli/commands/generate.ts
576
- import fs4 from "fs-extra";
577
- import path4 from "path";
598
+ import fs5 from "fs-extra";
599
+ import path5 from "path";
578
600
  import { pathToFileURL } from "url";
579
601
  import chalk4 from "chalk";
580
602
  import ora2 from "ora";
@@ -582,18 +604,18 @@ import Table from "cli-table3";
582
604
  import inquirer3 from "inquirer";
583
605
 
584
606
  // src/cli/commands/config.ts
585
- import fs3 from "fs-extra";
586
- import path3 from "path";
607
+ import fs4 from "fs-extra";
608
+ import path4 from "path";
587
609
  import chalk3 from "chalk";
588
610
  import inquirer2 from "inquirer";
589
611
  async function promptForApiKey(projectPath, providedKey, providedProvider) {
590
- const edtoolsDir = path3.join(projectPath, ".edtools");
591
- const configPath = path3.join(edtoolsDir, "config.json");
592
- await fs3.ensureDir(edtoolsDir);
612
+ const edtoolsDir = path4.join(projectPath, ".edtools");
613
+ const configPath = path4.join(edtoolsDir, "config.json");
614
+ await fs4.ensureDir(edtoolsDir);
593
615
  let existingConfig = {};
594
- if (await fs3.pathExists(configPath)) {
616
+ if (await fs4.pathExists(configPath)) {
595
617
  try {
596
- existingConfig = await fs3.readJson(configPath);
618
+ existingConfig = await fs4.readJson(configPath);
597
619
  } catch (error) {
598
620
  console.log(chalk3.yellow("\u26A0\uFE0F Existing config is corrupted, will create new one"));
599
621
  }
@@ -649,10 +671,10 @@ async function promptForApiKey(projectPath, providedKey, providedProvider) {
649
671
  apiKey,
650
672
  provider
651
673
  };
652
- await fs3.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
653
- const gitignorePath = path3.join(edtoolsDir, ".gitignore");
654
- if (!await fs3.pathExists(gitignorePath)) {
655
- await fs3.writeFile(gitignorePath, "*\n!.gitignore\n", "utf-8");
674
+ await fs4.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
675
+ const gitignorePath = path4.join(edtoolsDir, ".gitignore");
676
+ if (!await fs4.pathExists(gitignorePath)) {
677
+ await fs4.writeFile(gitignorePath, "*\n!.gitignore\n", "utf-8");
656
678
  }
657
679
  console.log("");
658
680
  console.log(successBox(`API Key configured successfully for ${provider}!`));
@@ -661,7 +683,7 @@ async function promptForApiKey(projectPath, providedKey, providedProvider) {
661
683
  return { apiKey, provider };
662
684
  }
663
685
  async function configSetApiKeyCommand(options) {
664
- const projectPath = path3.resolve(options.path);
686
+ const projectPath = path4.resolve(options.path);
665
687
  const result = await promptForApiKey(projectPath, options.key, options.provider);
666
688
  if (!result) {
667
689
  console.log(chalk3.red("\n\u2717 API key setup cancelled or failed\n"));
@@ -669,11 +691,11 @@ async function configSetApiKeyCommand(options) {
669
691
  }
670
692
  }
671
693
  async function configShowCommand(options) {
672
- const projectPath = path3.resolve(options.path);
673
- const edtoolsConfigPath = path3.join(projectPath, ".edtools", "config.json");
674
- const mainConfigPath = path3.join(projectPath, "edtools.config.js");
694
+ const projectPath = path4.resolve(options.path);
695
+ const edtoolsConfigPath = path4.join(projectPath, ".edtools", "config.json");
696
+ const mainConfigPath = path4.join(projectPath, "edtools.config.js");
675
697
  console.log(chalk3.cyan.bold("\n\u{1F4CB} Edtools Configuration:\n"));
676
- if (await fs3.pathExists(mainConfigPath)) {
698
+ if (await fs4.pathExists(mainConfigPath)) {
677
699
  console.log(chalk3.green("\u2713") + " edtools.config.js found");
678
700
  console.log(chalk3.gray(` Location: ${mainConfigPath}
679
701
  `));
@@ -682,9 +704,9 @@ async function configShowCommand(options) {
682
704
  console.log(chalk3.gray(` Run: ${chalk3.white("edtools init")} to create it
683
705
  `));
684
706
  }
685
- if (await fs3.pathExists(edtoolsConfigPath)) {
707
+ if (await fs4.pathExists(edtoolsConfigPath)) {
686
708
  try {
687
- const config = await fs3.readJson(edtoolsConfigPath);
709
+ const config = await fs4.readJson(edtoolsConfigPath);
688
710
  const hasApiKey = config.apiKey && config.apiKey.length > 0;
689
711
  const provider = config.provider || "anthropic";
690
712
  console.log(chalk3.green("\u2713") + " API key configured");
@@ -725,8 +747,8 @@ async function configShowCommand(options) {
725
747
  async function generateCommand(options) {
726
748
  console.log(chalk4.cyan.bold("\n\u{1F4DD} Generating content...\n"));
727
749
  const projectPath = process.cwd();
728
- const configPath = path4.join(projectPath, "edtools.config.js");
729
- if (!await fs4.pathExists(configPath)) {
750
+ const configPath = path5.join(projectPath, "edtools.config.js");
751
+ if (!await fs5.pathExists(configPath)) {
730
752
  console.log(chalk4.red("\u2717 No edtools.config.js found"));
731
753
  console.log(chalk4.yellow(' Run "edtools init" first\n'));
732
754
  process.exit(1);
@@ -740,10 +762,10 @@ async function generateCommand(options) {
740
762
  }
741
763
  const provider = productInfo.preferredProvider || "anthropic";
742
764
  let storedApiKey;
743
- const edtoolsConfigPath = path4.join(projectPath, ".edtools", "config.json");
744
- if (await fs4.pathExists(edtoolsConfigPath)) {
765
+ const edtoolsConfigPath = path5.join(projectPath, ".edtools", "config.json");
766
+ if (await fs5.pathExists(edtoolsConfigPath)) {
745
767
  try {
746
- const edtoolsConfig = await fs4.readJson(edtoolsConfigPath);
768
+ const edtoolsConfig = await fs5.readJson(edtoolsConfigPath);
747
769
  storedApiKey = edtoolsConfig.apiKey;
748
770
  } catch (error) {
749
771
  }
@@ -807,14 +829,14 @@ async function generateCommand(options) {
807
829
  }
808
830
  let topics = options.topics;
809
831
  if (options.fromCsv) {
810
- const opportunitiesPath = path4.join(projectPath, ".edtools", "opportunities.json");
811
- if (!await fs4.pathExists(opportunitiesPath)) {
832
+ const opportunitiesPath = path5.join(projectPath, ".edtools", "opportunities.json");
833
+ if (!await fs5.pathExists(opportunitiesPath)) {
812
834
  console.log(chalk4.red("\u2717 No CSV analysis found"));
813
835
  console.log(chalk4.yellow(' Run "edtools analyze" first to analyze your GSC data\n'));
814
836
  process.exit(1);
815
837
  }
816
838
  try {
817
- const oppData = await fs4.readJson(opportunitiesPath);
839
+ const oppData = await fs5.readJson(opportunitiesPath);
818
840
  topics = oppData.opportunities.slice(0, parseInt(options.posts, 10)).map((opp) => opp.suggestedTitle);
819
841
  console.log(chalk4.cyan("\u{1F4CA} Using topics from CSV analysis:\n"));
820
842
  topics.forEach((topic, i) => {
@@ -847,8 +869,8 @@ async function generateCommand(options) {
847
869
  outputDirConfig = "./blog";
848
870
  outputDirSource = "default";
849
871
  }
850
- const outputDir = path4.resolve(projectPath, outputDirConfig);
851
- await fs4.ensureDir(outputDir);
872
+ const outputDir = path5.resolve(projectPath, outputDirConfig);
873
+ await fs5.ensureDir(outputDir);
852
874
  const hostingConfig = detectHostingConfig(projectPath);
853
875
  if (hostingConfig) {
854
876
  const validation = validateOutputDir(outputDirConfig, hostingConfig);
@@ -1040,17 +1062,17 @@ function truncateTitle(title, maxLen) {
1040
1062
  }
1041
1063
 
1042
1064
  // src/cli/commands/analyze.ts
1043
- import fs6 from "fs-extra";
1044
- import path5 from "path";
1065
+ import fs7 from "fs-extra";
1066
+ import path6 from "path";
1045
1067
  import chalk5 from "chalk";
1046
1068
  import ora3 from "ora";
1047
1069
  import Table2 from "cli-table3";
1048
1070
 
1049
1071
  // src/integrations/gsc-csv-analyzer.ts
1050
- import fs5 from "fs-extra";
1072
+ import fs6 from "fs-extra";
1051
1073
  import { parse } from "csv-parse/sync";
1052
1074
  async function parseGSCCSV(filePath) {
1053
- const content = await fs5.readFile(filePath, "utf-8");
1075
+ const content = await fs6.readFile(filePath, "utf-8");
1054
1076
  const records = parse(content, {
1055
1077
  columns: true,
1056
1078
  skip_empty_lines: true,
@@ -1146,7 +1168,7 @@ async function saveOpportunities(opportunities, outputPath) {
1146
1168
  count: opportunities.length,
1147
1169
  opportunities
1148
1170
  };
1149
- await fs5.writeJson(outputPath, data, { spaces: 2 });
1171
+ await fs6.writeJson(outputPath, data, { spaces: 2 });
1150
1172
  }
1151
1173
 
1152
1174
  // src/cli/commands/analyze.ts
@@ -1155,9 +1177,9 @@ async function analyzeCommand(options) {
1155
1177
  const projectPath = process.cwd();
1156
1178
  let csvPath;
1157
1179
  if (options.file) {
1158
- csvPath = path5.resolve(options.file);
1180
+ csvPath = path6.resolve(options.file);
1159
1181
  } else {
1160
- const files = await fs6.readdir(projectPath);
1182
+ const files = await fs7.readdir(projectPath);
1161
1183
  const csvFiles = files.filter((f) => f.endsWith(".csv") && f.toLowerCase().includes("search"));
1162
1184
  if (csvFiles.length === 0) {
1163
1185
  console.log(chalk5.red("\u2717 No CSV file found"));
@@ -1170,9 +1192,9 @@ async function analyzeCommand(options) {
1170
1192
  csvFiles.forEach((f) => console.log(` - ${f}`));
1171
1193
  console.log(chalk5.yellow("\nUsing first file. Specify with --file to use a different one.\n"));
1172
1194
  }
1173
- csvPath = path5.join(projectPath, csvFiles[0]);
1195
+ csvPath = path6.join(projectPath, csvFiles[0]);
1174
1196
  }
1175
- if (!await fs6.pathExists(csvPath)) {
1197
+ if (!await fs7.pathExists(csvPath)) {
1176
1198
  console.log(chalk5.red(`\u2717 File not found: ${csvPath}
1177
1199
  `));
1178
1200
  process.exit(1);
@@ -1181,7 +1203,7 @@ async function analyzeCommand(options) {
1181
1203
  let data;
1182
1204
  try {
1183
1205
  data = await parseGSCCSV(csvPath);
1184
- spinner.succeed(`Parsed ${chalk5.white(data.length)} keywords from ${chalk5.white(path5.basename(csvPath))}`);
1206
+ spinner.succeed(`Parsed ${chalk5.white(data.length)} keywords from ${chalk5.white(path6.basename(csvPath))}`);
1185
1207
  } catch (error) {
1186
1208
  spinner.fail("Failed to parse CSV");
1187
1209
  console.log(chalk5.red(`
@@ -1286,9 +1308,9 @@ Error: ${error.message}
1286
1308
  console.log(topTable.toString());
1287
1309
  console.log("");
1288
1310
  }
1289
- const edtoolsDir = path5.join(projectPath, ".edtools");
1290
- await fs6.ensureDir(edtoolsDir);
1291
- const opportunitiesPath = path5.join(edtoolsDir, "opportunities.json");
1311
+ const edtoolsDir = path6.join(projectPath, ".edtools");
1312
+ await fs7.ensureDir(edtoolsDir);
1313
+ const opportunitiesPath = path6.join(edtoolsDir, "opportunities.json");
1292
1314
  await saveOpportunities(analysis.opportunities, opportunitiesPath);
1293
1315
  console.log(successBox(`Analysis saved to ${chalk5.white(".edtools/opportunities.json")}`));
1294
1316
  console.log(chalk5.cyan.bold("Next steps:"));
@@ -1299,16 +1321,16 @@ Error: ${error.message}
1299
1321
  }
1300
1322
 
1301
1323
  // src/cli/commands/validate.ts
1302
- import fs8 from "fs-extra";
1303
- import path6 from "path";
1324
+ import fs9 from "fs-extra";
1325
+ import path7 from "path";
1304
1326
  import chalk6 from "chalk";
1305
1327
  import Table3 from "cli-table3";
1306
1328
 
1307
1329
  // src/utils/seo-validator.ts
1308
1330
  import * as cheerio from "cheerio";
1309
- import fs7 from "fs-extra";
1331
+ import fs8 from "fs-extra";
1310
1332
  async function validatePost(htmlPath) {
1311
- const html = await fs7.readFile(htmlPath, "utf-8");
1333
+ const html = await fs8.readFile(htmlPath, "utf-8");
1312
1334
  const $ = cheerio.load(html);
1313
1335
  const issues = [];
1314
1336
  const passed = [];
@@ -1535,15 +1557,15 @@ async function validateCommand(options) {
1535
1557
  const projectPath = process.cwd();
1536
1558
  let htmlFiles = [];
1537
1559
  if (options.post) {
1538
- const postPath = path6.resolve(projectPath, options.post);
1539
- if (!await fs8.pathExists(postPath)) {
1560
+ const postPath = path7.resolve(projectPath, options.post);
1561
+ if (!await fs9.pathExists(postPath)) {
1540
1562
  console.log(errorBox(`Post not found: ${postPath}`));
1541
1563
  process.exit(1);
1542
1564
  }
1543
1565
  htmlFiles = [postPath];
1544
1566
  } else if (options.posts) {
1545
- const postsDir = path6.resolve(projectPath, options.posts);
1546
- if (!await fs8.pathExists(postsDir)) {
1567
+ const postsDir = path7.resolve(projectPath, options.posts);
1568
+ if (!await fs9.pathExists(postsDir)) {
1547
1569
  console.log(errorBox(`Directory not found: ${postsDir}`));
1548
1570
  process.exit(1);
1549
1571
  }
@@ -1553,8 +1575,8 @@ async function validateCommand(options) {
1553
1575
  process.exit(1);
1554
1576
  }
1555
1577
  } else {
1556
- const defaultBlogDir = path6.join(projectPath, "blog");
1557
- if (await fs8.pathExists(defaultBlogDir)) {
1578
+ const defaultBlogDir = path7.join(projectPath, "blog");
1579
+ if (await fs9.pathExists(defaultBlogDir)) {
1558
1580
  htmlFiles = await findHtmlFiles(defaultBlogDir);
1559
1581
  if (htmlFiles.length === 0) {
1560
1582
  console.log(errorBox("No HTML files found in blog/ directory"));
@@ -1599,8 +1621,8 @@ async function validateCommand(options) {
1599
1621
  })),
1600
1622
  stats: stats2
1601
1623
  };
1602
- const outputPath = path6.resolve(projectPath, options.output);
1603
- await fs8.writeJson(outputPath, report, { spaces: 2 });
1624
+ const outputPath = path7.resolve(projectPath, options.output);
1625
+ await fs9.writeJson(outputPath, report, { spaces: 2 });
1604
1626
  console.log(successBox(`Validation report saved to ${outputPath}`));
1605
1627
  }
1606
1628
  if (filteredResults.length > 0) {
@@ -1678,9 +1700,9 @@ async function validateCommand(options) {
1678
1700
  }
1679
1701
  async function findHtmlFiles(dir) {
1680
1702
  const files = [];
1681
- const entries = await fs8.readdir(dir, { withFileTypes: true });
1703
+ const entries = await fs9.readdir(dir, { withFileTypes: true });
1682
1704
  for (const entry of entries) {
1683
- const fullPath = path6.join(dir, entry.name);
1705
+ const fullPath = path7.join(dir, entry.name);
1684
1706
  if (entry.isDirectory()) {
1685
1707
  const subFiles = await findHtmlFiles(fullPath);
1686
1708
  files.push(...subFiles);
@@ -1705,18 +1727,18 @@ function capitalize(str) {
1705
1727
  }
1706
1728
 
1707
1729
  // src/cli/commands/doctor.ts
1708
- import fs9 from "fs-extra";
1709
- import path7 from "path";
1730
+ import fs10 from "fs-extra";
1731
+ import path8 from "path";
1710
1732
  import { pathToFileURL as pathToFileURL2 } from "url";
1711
1733
  import chalk7 from "chalk";
1712
1734
  async function doctorCommand(options) {
1713
1735
  console.log(chalk7.cyan.bold("\n\u{1F50D} Diagnosing project configuration...\n"));
1714
1736
  const projectPath = process.cwd();
1715
- const configPath = path7.join(projectPath, "edtools.config.js");
1737
+ const configPath = path8.join(projectPath, "edtools.config.js");
1716
1738
  const issues = [];
1717
1739
  const warnings = [];
1718
1740
  const suggestions = [];
1719
- if (await fs9.pathExists(configPath)) {
1741
+ if (await fs10.pathExists(configPath)) {
1720
1742
  console.log(chalk7.green("\u2713 Configuration file found: ") + chalk7.white("edtools.config.js"));
1721
1743
  } else {
1722
1744
  console.log(chalk7.red("\u2717 Configuration file not found"));
@@ -1740,19 +1762,19 @@ async function doctorCommand(options) {
1740
1762
  `));
1741
1763
  process.exit(1);
1742
1764
  }
1743
- const outputDirPath = path7.resolve(projectPath, outputDir);
1744
- if (await fs9.pathExists(outputDirPath)) {
1765
+ const outputDirPath = path8.resolve(projectPath, outputDir);
1766
+ if (await fs10.pathExists(outputDirPath)) {
1745
1767
  console.log(chalk7.green("\u2713 Output directory exists: ") + chalk7.white(outputDir));
1746
1768
  } else {
1747
1769
  warnings.push(`Output directory does not exist: ${outputDir}`);
1748
1770
  console.log(chalk7.yellow("\u26A0\uFE0F Output directory does not exist: ") + chalk7.white(outputDir));
1749
1771
  console.log(chalk7.yellow(' It will be created when you run "edtools generate"'));
1750
1772
  }
1751
- const edtoolsConfigPath = path7.join(projectPath, ".edtools", "config.json");
1773
+ const edtoolsConfigPath = path8.join(projectPath, ".edtools", "config.json");
1752
1774
  let hasApiKey = false;
1753
- if (await fs9.pathExists(edtoolsConfigPath)) {
1775
+ if (await fs10.pathExists(edtoolsConfigPath)) {
1754
1776
  try {
1755
- const edtoolsConfig = await fs9.readJson(edtoolsConfigPath);
1777
+ const edtoolsConfig = await fs10.readJson(edtoolsConfigPath);
1756
1778
  hasApiKey = !!edtoolsConfig.apiKey;
1757
1779
  } catch (error) {
1758
1780
  }
@@ -1791,12 +1813,12 @@ async function doctorCommand(options) {
1791
1813
  if (options.fix) {
1792
1814
  console.log(chalk7.yellow.bold("\u{1F527} Auto-fixing configuration...\n"));
1793
1815
  try {
1794
- const configContent = await fs9.readFile(configPath, "utf-8");
1816
+ const configContent = await fs10.readFile(configPath, "utf-8");
1795
1817
  const updatedConfig = configContent.replace(
1796
1818
  /outputDir:\s*['"]([^'"]+)['"]/,
1797
1819
  `outputDir: '${validation.suggestion}'`
1798
1820
  );
1799
- await fs9.writeFile(configPath, updatedConfig, "utf-8");
1821
+ await fs10.writeFile(configPath, updatedConfig, "utf-8");
1800
1822
  console.log(chalk7.green("\u2713 Updated edtools.config.js"));
1801
1823
  console.log(chalk7.green(` outputDir: '${validation.suggestion}'`));
1802
1824
  console.log("");
@@ -1849,8 +1871,31 @@ async function doctorCommand(options) {
1849
1871
  }
1850
1872
 
1851
1873
  // src/cli/index.ts
1874
+ function getVersion2() {
1875
+ try {
1876
+ const __filename = fileURLToPath2(import.meta.url);
1877
+ const __dirname = path9.dirname(__filename);
1878
+ const possiblePaths = [
1879
+ path9.join(__dirname, "../../package.json"),
1880
+ // From dist/cli/
1881
+ path9.join(__dirname, "../../../package.json"),
1882
+ // From src/cli/
1883
+ path9.join(__dirname, "../../../../package.json")
1884
+ // From npm global install
1885
+ ];
1886
+ for (const pkgPath of possiblePaths) {
1887
+ if (fs11.existsSync(pkgPath)) {
1888
+ const pkg = fs11.readJsonSync(pkgPath);
1889
+ return pkg.version || "unknown";
1890
+ }
1891
+ }
1892
+ return "unknown";
1893
+ } catch (error) {
1894
+ return "dev";
1895
+ }
1896
+ }
1852
1897
  var program = new Command();
1853
- program.name("edtools").description("AI-Powered Content Marketing CLI - Generate, validate, and optimize SEO content").version("0.6.2");
1898
+ program.name("edtools").description("AI-Powered Content Marketing CLI - Generate, validate, and optimize SEO content").version(getVersion2());
1854
1899
  program.command("init").description("Initialize edtools in your project").option("-p, --path <path>", "Project path", process.cwd()).action(initCommand);
1855
1900
  program.command("generate").description("Generate SEO-optimized blog posts").option("-n, --posts <number>", "Number of posts to generate (1-10)", "3").option("-t, --topics <topics...>", "Specific topics to write about").option("-o, --output <dir>", "Output directory (defaults to config or ./blog)").option("--api-key <key>", "API key (or use ANTHROPIC_API_KEY/OPENAI_API_KEY env var)").option("--from-csv", "Generate from CSV analysis opportunities").option("--dry-run", "Preview what would be generated without writing files").option("--report <format>", "Output format: json, table, pretty (default: table)", "table").action(generateCommand);
1856
1901
  program.command("analyze").description("Analyze Google Search Console CSV data").option("-f, --file <path>", "Path to CSV file (auto-detects if not provided)").option("--min-impressions <number>", "Minimum impressions for opportunities", "50").option("--min-position <number>", "Minimum position for opportunities", "20").option("--limit <number>", "Number of opportunities to show", "10").action(analyzeCommand);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/core/generator.ts"],"names":[],"mappings":"AAWA,OAAO,EACL,eAAe,EAIf,cAAc,EACd,cAAc,EAEd,UAAU,EAGV,OAAO,EACP,cAAc,EACd,WAAW,EAEZ,MAAM,mBAAmB,CAAC;AAW3B,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,SAAS,CAA0B;IAC3C,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,QAAQ,CAAkB;gBAEtB,MAAM,CAAC,EAAE,MAAM,EAAE,QAAQ,GAAE,UAAwB;IAuBzD,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;YAgJjD,eAAe;YAyEf,qBAAqB;YA+BrB,kBAAkB;IA8BhC,OAAO,CAAC,WAAW;IAkDnB,OAAO,CAAC,iBAAiB;YAwBX,iBAAiB;YAsCjB,cAAc;YAqEd,gBAAgB;YA4DhB,iBAAiB;YAgDjB,aAAa;IAuC3B,uBAAuB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,EAAE;IAyBtD,oBAAoB,CACxB,WAAW,EAAE,eAAe,EAC5B,SAAS,EAAE,MAAM,EACjB,KAAK,GAAE,MAAU,GAChB,OAAO,CAAC,WAAW,EAAE,CAAC;IAoDnB,sBAAsB,CAC1B,WAAW,EAAE,eAAe,EAC5B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,CAAC;CA6C3B"}
1
+ {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/core/generator.ts"],"names":[],"mappings":"AAWA,OAAO,EACL,eAAe,EAIf,cAAc,EACd,cAAc,EAEd,UAAU,EAGV,OAAO,EACP,cAAc,EACd,WAAW,EAEZ,MAAM,mBAAmB,CAAC;AAW3B,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,SAAS,CAA0B;IAC3C,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,QAAQ,CAAkB;gBAEtB,MAAM,CAAC,EAAE,MAAM,EAAE,QAAQ,GAAE,UAAwB;IAuBzD,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;YAgJjD,eAAe;YAyEf,qBAAqB;YA+BrB,kBAAkB;IA8BhC,OAAO,CAAC,WAAW;IAkDnB,OAAO,CAAC,iBAAiB;YAwBX,iBAAiB;YAsCjB,cAAc;YAqEd,gBAAgB;YA4DhB,iBAAiB;YAsDjB,aAAa;IAuC3B,uBAAuB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,EAAE;IAyBtD,oBAAoB,CACxB,WAAW,EAAE,eAAe,EAC5B,SAAS,EAAE,MAAM,EACjB,KAAK,GAAE,MAAU,GAChB,OAAO,CAAC,WAAW,EAAE,CAAC;IAoDnB,sBAAsB,CAC1B,WAAW,EAAE,eAAe,EAC5B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,CAAC;CA6C3B"}
@@ -441,12 +441,16 @@ Output ONLY a JSON array of topic strings, no markdown:
441
441
  console.warn(`Blog index template not found in any of the expected locations. Skipping blog index generation.`);
442
442
  return;
443
443
  }
444
+ const templateDir = path.dirname(templatePath);
444
445
  const template = await fs.readFile(templatePath, 'utf-8');
445
446
  const html = ejs.render(template, {
446
447
  siteName: `${productInfo.name} Blog`,
447
448
  siteDescription: productInfo.description || `Insights on ${productInfo.category}`,
448
449
  siteUrl: productInfo.websiteUrl + '/blog',
449
450
  blogConfig,
451
+ }, {
452
+ filename: templatePath,
453
+ root: templateDir,
450
454
  });
451
455
  const indexPath = path.join(outputDir, 'index.html');
452
456
  await fs.writeFile(indexPath, html, 'utf-8');