@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/adapters/html/index.d.ts.map +1 -1
- package/dist/adapters/html/index.js +21 -4
- package/dist/adapters/html/index.js.map +1 -1
- package/dist/chunk-EPM74QNH.js +844 -0
- package/dist/chunk-OVFZRN7O.js +850 -0
- package/dist/cli/index.js +140 -95
- package/dist/cli/index.js.map +1 -1
- package/dist/core/generator.d.ts.map +1 -1
- package/dist/core/generator.js +4 -0
- package/dist/core/generator.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/ui/banner.d.ts.map +1 -1
- package/dist/ui/banner.js +18 -2
- package/dist/ui/banner.js.map +1 -1
- package/package.json +1 -1
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-
|
|
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
|
|
12
|
-
import
|
|
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
|
-
|
|
67
|
-
|
|
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
|
|
108
|
-
import
|
|
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 =
|
|
185
|
-
if (
|
|
186
|
-
const packageJson = JSON.parse(
|
|
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 =
|
|
209
|
-
if (
|
|
230
|
+
const configPath = path2.join(projectPath, detector.file);
|
|
231
|
+
if (fs2.existsSync(configPath)) {
|
|
210
232
|
try {
|
|
211
|
-
const content =
|
|
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 =
|
|
258
|
-
const configPath =
|
|
259
|
-
if (await
|
|
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 =
|
|
277
|
-
if (await
|
|
278
|
-
const html = await
|
|
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
|
|
547
|
-
const edtoolsDir =
|
|
548
|
-
await
|
|
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 =
|
|
554
|
-
await
|
|
555
|
-
const gitignorePath =
|
|
556
|
-
await
|
|
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
|
|
577
|
-
import
|
|
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
|
|
586
|
-
import
|
|
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 =
|
|
591
|
-
const configPath =
|
|
592
|
-
await
|
|
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
|
|
616
|
+
if (await fs4.pathExists(configPath)) {
|
|
595
617
|
try {
|
|
596
|
-
existingConfig = await
|
|
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
|
|
653
|
-
const gitignorePath =
|
|
654
|
-
if (!await
|
|
655
|
-
await
|
|
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 =
|
|
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 =
|
|
673
|
-
const edtoolsConfigPath =
|
|
674
|
-
const mainConfigPath =
|
|
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
|
|
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
|
|
707
|
+
if (await fs4.pathExists(edtoolsConfigPath)) {
|
|
686
708
|
try {
|
|
687
|
-
const config = await
|
|
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 =
|
|
729
|
-
if (!await
|
|
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 =
|
|
744
|
-
if (await
|
|
765
|
+
const edtoolsConfigPath = path5.join(projectPath, ".edtools", "config.json");
|
|
766
|
+
if (await fs5.pathExists(edtoolsConfigPath)) {
|
|
745
767
|
try {
|
|
746
|
-
const edtoolsConfig = await
|
|
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 =
|
|
811
|
-
if (!await
|
|
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
|
|
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 =
|
|
851
|
-
await
|
|
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
|
|
1044
|
-
import
|
|
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
|
|
1072
|
+
import fs6 from "fs-extra";
|
|
1051
1073
|
import { parse } from "csv-parse/sync";
|
|
1052
1074
|
async function parseGSCCSV(filePath) {
|
|
1053
|
-
const content = await
|
|
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
|
|
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 =
|
|
1180
|
+
csvPath = path6.resolve(options.file);
|
|
1159
1181
|
} else {
|
|
1160
|
-
const files = await
|
|
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 =
|
|
1195
|
+
csvPath = path6.join(projectPath, csvFiles[0]);
|
|
1174
1196
|
}
|
|
1175
|
-
if (!await
|
|
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(
|
|
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 =
|
|
1290
|
-
await
|
|
1291
|
-
const opportunitiesPath =
|
|
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
|
|
1303
|
-
import
|
|
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
|
|
1331
|
+
import fs8 from "fs-extra";
|
|
1310
1332
|
async function validatePost(htmlPath) {
|
|
1311
|
-
const html = await
|
|
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 =
|
|
1539
|
-
if (!await
|
|
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 =
|
|
1546
|
-
if (!await
|
|
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 =
|
|
1557
|
-
if (await
|
|
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 =
|
|
1603
|
-
await
|
|
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
|
|
1703
|
+
const entries = await fs9.readdir(dir, { withFileTypes: true });
|
|
1682
1704
|
for (const entry of entries) {
|
|
1683
|
-
const fullPath =
|
|
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
|
|
1709
|
-
import
|
|
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 =
|
|
1737
|
+
const configPath = path8.join(projectPath, "edtools.config.js");
|
|
1716
1738
|
const issues = [];
|
|
1717
1739
|
const warnings = [];
|
|
1718
1740
|
const suggestions = [];
|
|
1719
|
-
if (await
|
|
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 =
|
|
1744
|
-
if (await
|
|
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 =
|
|
1773
|
+
const edtoolsConfigPath = path8.join(projectPath, ".edtools", "config.json");
|
|
1752
1774
|
let hasApiKey = false;
|
|
1753
|
-
if (await
|
|
1775
|
+
if (await fs10.pathExists(edtoolsConfigPath)) {
|
|
1754
1776
|
try {
|
|
1755
|
-
const edtoolsConfig = await
|
|
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
|
|
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
|
|
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(
|
|
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);
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAMA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACjF,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;
|
|
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;
|
|
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"}
|
package/dist/core/generator.js
CHANGED
|
@@ -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');
|