@koda-sl/baker-cli 0.5.1 → 0.11.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.
Files changed (135) hide show
  1. package/README.md +718 -162
  2. package/dist/cli.js +7 -3
  3. package/dist/cli.js.map +1 -1
  4. package/dist/client.d.ts.map +1 -1
  5. package/dist/client.js +7 -1
  6. package/dist/client.js.map +1 -1
  7. package/dist/commands/ads/cache.d.ts +7 -0
  8. package/dist/commands/ads/cache.d.ts.map +1 -0
  9. package/dist/commands/ads/cache.js +73 -0
  10. package/dist/commands/ads/cache.js.map +1 -0
  11. package/dist/commands/ads/cache.test.d.ts +2 -0
  12. package/dist/commands/ads/cache.test.d.ts.map +1 -0
  13. package/dist/commands/ads/cache.test.js +44 -0
  14. package/dist/commands/ads/cache.test.js.map +1 -0
  15. package/dist/commands/ads/field-descriptions.d.ts +2 -0
  16. package/dist/commands/ads/field-descriptions.d.ts.map +1 -0
  17. package/dist/commands/ads/field-descriptions.js +91 -0
  18. package/dist/commands/ads/field-descriptions.js.map +1 -0
  19. package/dist/commands/ads/google/accounts.d.ts +14 -0
  20. package/dist/commands/ads/google/accounts.d.ts.map +1 -0
  21. package/dist/commands/ads/google/accounts.js +73 -0
  22. package/dist/commands/ads/google/accounts.js.map +1 -0
  23. package/dist/commands/ads/google/changes.d.ts +34 -0
  24. package/dist/commands/ads/google/changes.d.ts.map +1 -0
  25. package/dist/commands/ads/google/changes.js +80 -0
  26. package/dist/commands/ads/google/changes.js.map +1 -0
  27. package/dist/commands/ads/google/correction-table.d.ts +3 -0
  28. package/dist/commands/ads/google/correction-table.d.ts.map +1 -0
  29. package/dist/commands/ads/google/correction-table.js +340 -0
  30. package/dist/commands/ads/google/correction-table.js.map +1 -0
  31. package/dist/commands/ads/google/currency.d.ts +13 -0
  32. package/dist/commands/ads/google/currency.d.ts.map +1 -0
  33. package/dist/commands/ads/google/currency.js +68 -0
  34. package/dist/commands/ads/google/currency.js.map +1 -0
  35. package/dist/commands/ads/google/error-parser.d.ts +3 -0
  36. package/dist/commands/ads/google/error-parser.d.ts.map +1 -0
  37. package/dist/commands/ads/google/error-parser.js +91 -0
  38. package/dist/commands/ads/google/error-parser.js.map +1 -0
  39. package/dist/commands/ads/google/error-parser.test.d.ts +2 -0
  40. package/dist/commands/ads/google/error-parser.test.d.ts.map +1 -0
  41. package/dist/commands/ads/google/error-parser.test.js +48 -0
  42. package/dist/commands/ads/google/error-parser.test.js.map +1 -0
  43. package/dist/commands/ads/google/index.d.ts +2 -0
  44. package/dist/commands/ads/google/index.d.ts.map +1 -0
  45. package/dist/commands/ads/google/index.js +30 -0
  46. package/dist/commands/ads/google/index.js.map +1 -0
  47. package/dist/commands/ads/google/keywords/discover.d.ts +49 -0
  48. package/dist/commands/ads/google/keywords/discover.d.ts.map +1 -0
  49. package/dist/commands/ads/google/keywords/discover.js +139 -0
  50. package/dist/commands/ads/google/keywords/discover.js.map +1 -0
  51. package/dist/commands/ads/google/keywords/index.d.ts +2 -0
  52. package/dist/commands/ads/google/keywords/index.d.ts.map +1 -0
  53. package/dist/commands/ads/google/keywords/index.js +18 -0
  54. package/dist/commands/ads/google/keywords/index.js.map +1 -0
  55. package/dist/commands/ads/google/keywords/metrics.d.ts +34 -0
  56. package/dist/commands/ads/google/keywords/metrics.d.ts.map +1 -0
  57. package/dist/commands/ads/google/keywords/metrics.js +111 -0
  58. package/dist/commands/ads/google/keywords/metrics.js.map +1 -0
  59. package/dist/commands/ads/google/preflight.d.ts +3 -0
  60. package/dist/commands/ads/google/preflight.d.ts.map +1 -0
  61. package/dist/commands/ads/google/preflight.js +115 -0
  62. package/dist/commands/ads/google/preflight.js.map +1 -0
  63. package/dist/commands/ads/google/preflight.test.d.ts +2 -0
  64. package/dist/commands/ads/google/preflight.test.d.ts.map +1 -0
  65. package/dist/commands/ads/google/preflight.test.js +50 -0
  66. package/dist/commands/ads/google/preflight.test.js.map +1 -0
  67. package/dist/commands/ads/google/presets.d.ts +12 -0
  68. package/dist/commands/ads/google/presets.d.ts.map +1 -0
  69. package/dist/commands/ads/google/presets.js +71 -0
  70. package/dist/commands/ads/google/presets.js.map +1 -0
  71. package/dist/commands/ads/google/presets.test.d.ts +2 -0
  72. package/dist/commands/ads/google/presets.test.d.ts.map +1 -0
  73. package/dist/commands/ads/google/presets.test.js +79 -0
  74. package/dist/commands/ads/google/presets.test.js.map +1 -0
  75. package/dist/commands/ads/google/query.d.ts +64 -0
  76. package/dist/commands/ads/google/query.d.ts.map +1 -0
  77. package/dist/commands/ads/google/query.js +304 -0
  78. package/dist/commands/ads/google/query.js.map +1 -0
  79. package/dist/commands/ads/index.d.ts +2 -0
  80. package/dist/commands/ads/index.d.ts.map +1 -0
  81. package/dist/commands/ads/index.js +19 -0
  82. package/dist/commands/ads/index.js.map +1 -0
  83. package/dist/commands/ads/output.d.ts +17 -0
  84. package/dist/commands/ads/output.d.ts.map +1 -0
  85. package/dist/commands/ads/output.js +78 -0
  86. package/dist/commands/ads/output.js.map +1 -0
  87. package/dist/commands/ads/output.test.d.ts +2 -0
  88. package/dist/commands/ads/output.test.d.ts.map +1 -0
  89. package/dist/commands/ads/output.test.js +100 -0
  90. package/dist/commands/ads/output.test.js.map +1 -0
  91. package/dist/commands/ads/types.d.ts +69 -0
  92. package/dist/commands/ads/types.d.ts.map +1 -0
  93. package/dist/commands/ads/types.js +2 -0
  94. package/dist/commands/ads/types.js.map +1 -0
  95. package/dist/commands/research/advertisers.d.ts +34 -0
  96. package/dist/commands/research/advertisers.d.ts.map +1 -0
  97. package/dist/commands/research/advertisers.js +75 -0
  98. package/dist/commands/research/advertisers.js.map +1 -0
  99. package/dist/commands/research/countries.d.ts +2 -0
  100. package/dist/commands/research/countries.d.ts.map +1 -0
  101. package/dist/commands/research/countries.js +69 -0
  102. package/dist/commands/research/countries.js.map +1 -0
  103. package/dist/commands/research/index.d.ts +2 -0
  104. package/dist/commands/research/index.d.ts.map +1 -0
  105. package/dist/commands/research/index.js +40 -0
  106. package/dist/commands/research/index.js.map +1 -0
  107. package/dist/commands/research/intent.d.ts +24 -0
  108. package/dist/commands/research/intent.d.ts.map +1 -0
  109. package/dist/commands/research/intent.js +71 -0
  110. package/dist/commands/research/intent.js.map +1 -0
  111. package/dist/commands/research/keyword-gap.d.ts +49 -0
  112. package/dist/commands/research/keyword-gap.d.ts.map +1 -0
  113. package/dist/commands/research/keyword-gap.js +99 -0
  114. package/dist/commands/research/keyword-gap.js.map +1 -0
  115. package/dist/commands/research/keywords-for-site.d.ts +44 -0
  116. package/dist/commands/research/keywords-for-site.d.ts.map +1 -0
  117. package/dist/commands/research/keywords-for-site.js +83 -0
  118. package/dist/commands/research/keywords-for-site.js.map +1 -0
  119. package/dist/commands/research/languages.d.ts +2 -0
  120. package/dist/commands/research/languages.d.ts.map +1 -0
  121. package/dist/commands/research/languages.js +42 -0
  122. package/dist/commands/research/languages.js.map +1 -0
  123. package/dist/commands/research/lighthouse.d.ts +24 -0
  124. package/dist/commands/research/lighthouse.d.ts.map +1 -0
  125. package/dist/commands/research/lighthouse.js +60 -0
  126. package/dist/commands/research/lighthouse.js.map +1 -0
  127. package/dist/commands/research/output.d.ts +29 -0
  128. package/dist/commands/research/output.d.ts.map +1 -0
  129. package/dist/commands/research/output.js +81 -0
  130. package/dist/commands/research/output.js.map +1 -0
  131. package/dist/env.d.ts +1 -0
  132. package/dist/env.d.ts.map +1 -1
  133. package/dist/env.js +5 -1
  134. package/dist/env.js.map +1 -1
  135. package/package.json +1 -1
@@ -0,0 +1,71 @@
1
+ import { defineCommand } from "citty";
2
+ import { apiPost } from "../../client.js";
3
+ import { registerSchema } from "../../schemas.js";
4
+ import { handleResearchError, writeResearchJson, writeResearchOutput } from "./output.js";
5
+ registerSchema({
6
+ command: "research.intent",
7
+ description: "Classify Google Search intent for keywords. Determines if someone searching is looking to buy, research, or navigate.",
8
+ args: {
9
+ keywords: {
10
+ type: "string",
11
+ description: "Comma-separated keywords (min 3 chars each, max 1000 keywords)",
12
+ required: true,
13
+ },
14
+ language: { type: "string", description: "Language code or name (default: en)", required: false },
15
+ "no-cache": { type: "boolean", description: "Skip server cache, hit API directly", required: false },
16
+ },
17
+ });
18
+ const FIELDS = {
19
+ keyword: "The keyword analyzed",
20
+ intent: "Primary Google Search intent: informational, navigational, commercial, transactional",
21
+ probability: "Confidence score 0.0-1.0",
22
+ };
23
+ export const intentCommand = defineCommand({
24
+ meta: {
25
+ name: "intent",
26
+ description: `Classify Google Search intent for keywords. Returns intent type and confidence.
27
+
28
+ Examples:
29
+ baker research intent "buy running shoes,best running shoes 2026,how to tie running shoes"
30
+ baker research intent "crm software" --language spanish`,
31
+ },
32
+ args: {
33
+ keywords: { type: "positional", description: "Comma-separated keywords", required: true },
34
+ language: { type: "string", description: "Language code or name (default: en)", required: false },
35
+ output: { type: "string", description: "Format: json|csv|md|jsonl", required: false, default: "json" },
36
+ "no-cache": { type: "boolean", description: "Skip server cache, hit API directly", required: false },
37
+ },
38
+ run: async ({ args }) => {
39
+ const rawKeywords = args.keywords;
40
+ const keywords = rawKeywords
41
+ .split(",")
42
+ .map((k) => k.trim())
43
+ .filter((k) => k.length >= 3);
44
+ if (keywords.length === 0) {
45
+ writeResearchJson({
46
+ ok: false,
47
+ error: { code: "VALIDATION_ERROR", message: "Provide at least one keyword (min 3 chars each)" },
48
+ });
49
+ process.exit(1);
50
+ }
51
+ const language = args.language || undefined;
52
+ const skipCache = args["no-cache"] ? true : undefined;
53
+ try {
54
+ const data = await apiPost("/api/research/intent", {
55
+ keywords,
56
+ language,
57
+ skipCache,
58
+ });
59
+ const format = args.output || "json";
60
+ if (format !== "json") {
61
+ writeResearchOutput(data, format);
62
+ return;
63
+ }
64
+ writeResearchJson({ ok: true, data, fields: FIELDS });
65
+ }
66
+ catch (err) {
67
+ handleResearchError(err);
68
+ }
69
+ },
70
+ });
71
+ //# sourceMappingURL=intent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"intent.js","sourceRoot":"","sources":["../../../src/commands/research/intent.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ export declare const keywordGapCommand: import("citty").CommandDef<{
2
+ readonly competitor: {
3
+ readonly type: "positional";
4
+ readonly description: "Competitor domain";
5
+ readonly required: true;
6
+ };
7
+ readonly ours: {
8
+ readonly type: "positional";
9
+ readonly description: "Your domain";
10
+ readonly required: true;
11
+ };
12
+ readonly location: {
13
+ readonly type: "string";
14
+ readonly description: "Country code or location code (default: us)";
15
+ readonly required: false;
16
+ };
17
+ readonly language: {
18
+ readonly type: "string";
19
+ readonly description: "Language code or name (default: en)";
20
+ readonly required: false;
21
+ };
22
+ readonly type: {
23
+ readonly type: "string";
24
+ readonly description: "Filter: paid, organic, all (default: all)";
25
+ readonly required: false;
26
+ };
27
+ readonly limit: {
28
+ readonly type: "string";
29
+ readonly description: "Max results per page (default: 50, max: 1000)";
30
+ readonly required: false;
31
+ };
32
+ readonly offset: {
33
+ readonly type: "string";
34
+ readonly description: "Pagination offset (default: 0)";
35
+ readonly required: false;
36
+ };
37
+ readonly output: {
38
+ readonly type: "string";
39
+ readonly description: "Format: json|csv|md|jsonl";
40
+ readonly required: false;
41
+ readonly default: "json";
42
+ };
43
+ readonly "no-cache": {
44
+ readonly type: "boolean";
45
+ readonly description: "Skip server cache, hit API directly";
46
+ readonly required: false;
47
+ };
48
+ }>;
49
+ //# sourceMappingURL=keyword-gap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyword-gap.d.ts","sourceRoot":"","sources":["../../../src/commands/research/keyword-gap.ts"],"names":[],"mappings":"AAuCA,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuE5B,CAAC"}
@@ -0,0 +1,99 @@
1
+ import { defineCommand } from "citty";
2
+ import { apiPost } from "../../client.js";
3
+ import { registerSchema } from "../../schemas.js";
4
+ import { handleResearchError, RESEARCH_DATA_NOTE, writeResearchJson, writeResearchOutput } from "./output.js";
5
+ registerSchema({
6
+ command: "research.keyword-gap",
7
+ description: "Find keywords a competitor ranks for (organic or paid) that you don't. Discovers expansion opportunities.",
8
+ args: {
9
+ competitor: { type: "string", description: "Competitor domain (positional 1)", required: true },
10
+ ours: { type: "string", description: "Your domain (positional 2)", required: true },
11
+ location: {
12
+ type: "string",
13
+ description: "Country code (us, uk, es, de...) or DataForSEO location code. Default: us",
14
+ required: false,
15
+ },
16
+ language: { type: "string", description: "Language code or name (default: en)", required: false },
17
+ type: { type: "string", description: "Filter: paid, organic, all (default: all)", required: false },
18
+ limit: { type: "string", description: "Max results per page (default: 50, max: 1000)", required: false },
19
+ offset: { type: "string", description: "Pagination offset (default: 0)", required: false },
20
+ "no-cache": { type: "boolean", description: "Skip server cache, hit API directly", required: false },
21
+ },
22
+ });
23
+ const FIELDS = {
24
+ keyword: "Keyword in the gap (they rank, you don't)",
25
+ search_volume: "Monthly search volume",
26
+ cpc: "Cost per click USD",
27
+ their_position: "Competitor's ranking position",
28
+ };
29
+ export const keywordGapCommand = defineCommand({
30
+ meta: {
31
+ name: "keyword-gap",
32
+ description: `Find keywords a competitor has that you don't. Supports pagination via --offset.
33
+
34
+ Examples:
35
+ baker research keyword-gap "competitor.com" "mysite.com"
36
+ baker research keyword-gap "competitor.com" "mysite.com" --type paid --limit 100
37
+ baker research keyword-gap "competitor.com" "mysite.com" --offset 50 --limit 50`,
38
+ },
39
+ args: {
40
+ competitor: { type: "positional", description: "Competitor domain", required: true },
41
+ ours: { type: "positional", description: "Your domain", required: true },
42
+ location: { type: "string", description: "Country code or location code (default: us)", required: false },
43
+ language: { type: "string", description: "Language code or name (default: en)", required: false },
44
+ type: { type: "string", description: "Filter: paid, organic, all (default: all)", required: false },
45
+ limit: { type: "string", description: "Max results per page (default: 50, max: 1000)", required: false },
46
+ offset: { type: "string", description: "Pagination offset (default: 0)", required: false },
47
+ output: { type: "string", description: "Format: json|csv|md|jsonl", required: false, default: "json" },
48
+ "no-cache": { type: "boolean", description: "Skip server cache, hit API directly", required: false },
49
+ },
50
+ run: async ({ args }) => {
51
+ const competitor = args.competitor;
52
+ const ours = args.ours;
53
+ const location = args.location || undefined;
54
+ const language = args.language || undefined;
55
+ const type = args.type || undefined;
56
+ const limit = args.limit ? Number(args.limit) : undefined;
57
+ const offset = args.offset ? Number(args.offset) : undefined;
58
+ const skipCache = args["no-cache"] ? true : undefined;
59
+ try {
60
+ const result = await apiPost("/api/research/keyword-gap", {
61
+ competitor,
62
+ ours,
63
+ location,
64
+ language,
65
+ type,
66
+ limit,
67
+ offset,
68
+ skipCache,
69
+ });
70
+ const currentOffset = offset ?? 0;
71
+ const currentLimit = limit ?? 50;
72
+ const hasMore = currentOffset + result.data.length < result.total_count;
73
+ const format = args.output || "json";
74
+ if (format !== "json") {
75
+ writeResearchOutput(result.data, format);
76
+ return;
77
+ }
78
+ writeResearchJson({
79
+ ok: true,
80
+ your_domain: ours,
81
+ competitor_domain: competitor,
82
+ data: result.data,
83
+ fields: FIELDS,
84
+ note: RESEARCH_DATA_NOTE,
85
+ total_count: result.total_count,
86
+ pagination: {
87
+ offset: currentOffset,
88
+ limit: currentLimit,
89
+ has_more: hasMore,
90
+ next_offset: hasMore ? currentOffset + currentLimit : undefined,
91
+ },
92
+ });
93
+ }
94
+ catch (err) {
95
+ handleResearchError(err);
96
+ }
97
+ },
98
+ });
99
+ //# sourceMappingURL=keyword-gap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyword-gap.js","sourceRoot":"","sources":["../../../src/commands/research/keyword-gap.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ export declare const keywordsForSiteCommand: import("citty").CommandDef<{
2
+ readonly target: {
3
+ readonly type: "positional";
4
+ readonly description: "Domain or URL to analyze";
5
+ readonly required: true;
6
+ };
7
+ readonly location: {
8
+ readonly type: "string";
9
+ readonly description: "Country code or location code (default: us)";
10
+ readonly required: false;
11
+ };
12
+ readonly language: {
13
+ readonly type: "string";
14
+ readonly description: "Language code or name (default: en)";
15
+ readonly required: false;
16
+ };
17
+ readonly sort: {
18
+ readonly type: "string";
19
+ readonly description: "Sort: relevance, search_volume, competition, cpc";
20
+ readonly required: false;
21
+ };
22
+ readonly type: {
23
+ readonly type: "string";
24
+ readonly description: "Filter: paid, organic, all (default: all)";
25
+ readonly required: false;
26
+ };
27
+ readonly limit: {
28
+ readonly type: "string";
29
+ readonly description: "Max results (default: 50, max: 1000)";
30
+ readonly required: false;
31
+ };
32
+ readonly output: {
33
+ readonly type: "string";
34
+ readonly description: "Format: json|csv|md|jsonl";
35
+ readonly required: false;
36
+ readonly default: "json";
37
+ };
38
+ readonly "no-cache": {
39
+ readonly type: "boolean";
40
+ readonly description: "Skip server cache, hit API directly";
41
+ readonly required: false;
42
+ };
43
+ }>;
44
+ //# sourceMappingURL=keywords-for-site.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keywords-for-site.d.ts","sourceRoot":"","sources":["../../../src/commands/research/keywords-for-site.ts"],"names":[],"mappings":"AA4CA,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkDjC,CAAC"}
@@ -0,0 +1,83 @@
1
+ import { defineCommand } from "citty";
2
+ import { apiPost } from "../../client.js";
3
+ import { registerSchema } from "../../schemas.js";
4
+ import { handleResearchError, RESEARCH_DATA_NOTE, writeResearchJson, writeResearchOutput } from "./output.js";
5
+ registerSchema({
6
+ command: "research.keywords-for-site",
7
+ description: "Get keywords a competitor targets in Google. Use --type paid to see only paid keywords, --type organic for organic only.",
8
+ args: {
9
+ target: { type: "string", description: "Domain or URL to analyze (positional)", required: true },
10
+ location: {
11
+ type: "string",
12
+ description: "Country code (us, uk, es, de...) or DataForSEO location code. Default: us",
13
+ required: false,
14
+ },
15
+ language: { type: "string", description: "Language code or name (default: en)", required: false },
16
+ sort: {
17
+ type: "string",
18
+ description: "Sort: relevance, search_volume, competition, cpc (default: search_volume)",
19
+ required: false,
20
+ },
21
+ type: { type: "string", description: "Filter: paid, organic, all (default: all)", required: false },
22
+ limit: { type: "string", description: "Max results (default: 50, max: 1000)", required: false },
23
+ "no-cache": { type: "boolean", description: "Skip server cache, hit API directly", required: false },
24
+ },
25
+ });
26
+ const FIELDS = {
27
+ keyword: "Keyword the site targets",
28
+ search_volume: "Monthly search volume",
29
+ cpc: "Cost per click in USD",
30
+ competition: "LOW, MEDIUM, or HIGH",
31
+ competition_index: "Competition score 0-100",
32
+ };
33
+ export const keywordsForSiteCommand = defineCommand({
34
+ meta: {
35
+ name: "keywords-for-site",
36
+ description: `Get keywords a competitor targets in Google. Use --type to filter paid/organic.
37
+
38
+ Examples:
39
+ baker research keywords-for-site "competitor.com"
40
+ baker research keywords-for-site "competitor.com" --type paid --limit 20
41
+ baker research keywords-for-site "competitor.com" --type organic --location uk`,
42
+ },
43
+ args: {
44
+ target: { type: "positional", description: "Domain or URL to analyze", required: true },
45
+ location: { type: "string", description: "Country code or location code (default: us)", required: false },
46
+ language: { type: "string", description: "Language code or name (default: en)", required: false },
47
+ sort: { type: "string", description: "Sort: relevance, search_volume, competition, cpc", required: false },
48
+ type: { type: "string", description: "Filter: paid, organic, all (default: all)", required: false },
49
+ limit: { type: "string", description: "Max results (default: 50, max: 1000)", required: false },
50
+ output: { type: "string", description: "Format: json|csv|md|jsonl", required: false, default: "json" },
51
+ "no-cache": { type: "boolean", description: "Skip server cache, hit API directly", required: false },
52
+ },
53
+ run: async ({ args }) => {
54
+ const target = args.target;
55
+ const location = args.location || undefined;
56
+ const language = args.language || undefined;
57
+ const sort = args.sort || undefined;
58
+ const type = args.type || undefined;
59
+ const limit = args.limit ? Number(args.limit) : undefined;
60
+ const skipCache = args["no-cache"] ? true : undefined;
61
+ try {
62
+ const data = await apiPost("/api/research/keywords-for-site", {
63
+ target,
64
+ location,
65
+ language,
66
+ sort,
67
+ type,
68
+ limit,
69
+ skipCache,
70
+ });
71
+ const format = args.output || "json";
72
+ if (format !== "json") {
73
+ writeResearchOutput(data, format);
74
+ return;
75
+ }
76
+ writeResearchJson({ ok: true, data, fields: FIELDS, note: RESEARCH_DATA_NOTE });
77
+ }
78
+ catch (err) {
79
+ handleResearchError(err);
80
+ }
81
+ },
82
+ });
83
+ //# sourceMappingURL=keywords-for-site.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keywords-for-site.js","sourceRoot":"","sources":["../../../src/commands/research/keywords-for-site.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare const languagesCommand: import("citty").CommandDef<import("citty").ArgsDef>;
2
+ //# sourceMappingURL=languages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"languages.d.ts","sourceRoot":"","sources":["../../../src/commands/research/languages.ts"],"names":[],"mappings":"AAoCA,eAAO,MAAM,gBAAgB,qDAQ3B,CAAC"}
@@ -0,0 +1,42 @@
1
+ import { defineCommand } from "citty";
2
+ import { registerSchema } from "../../schemas.js";
3
+ import { writeResearchJson } from "./output.js";
4
+ registerSchema({
5
+ command: "research.languages",
6
+ description: "List all supported language codes for --language flag in research commands.",
7
+ args: {},
8
+ });
9
+ const LANGUAGES = [
10
+ { code: "en", name: "english" },
11
+ { code: "es", name: "spanish" },
12
+ { code: "fr", name: "french" },
13
+ { code: "de", name: "german" },
14
+ { code: "it", name: "italian" },
15
+ { code: "pt", name: "portuguese" },
16
+ { code: "nl", name: "dutch" },
17
+ { code: "ja", name: "japanese" },
18
+ { code: "ko", name: "korean" },
19
+ { code: "zh", name: "chinese" },
20
+ { code: "ar", name: "arabic" },
21
+ { code: "ru", name: "russian" },
22
+ { code: "pl", name: "polish" },
23
+ { code: "tr", name: "turkish" },
24
+ { code: "sv", name: "swedish" },
25
+ { code: "no", name: "norwegian" },
26
+ { code: "da", name: "danish" },
27
+ { code: "fi", name: "finnish" },
28
+ ];
29
+ const FIELDS = {
30
+ code: "Language code to pass as --language",
31
+ name: "Language name (also accepted by --language)",
32
+ };
33
+ export const languagesCommand = defineCommand({
34
+ meta: {
35
+ name: "languages",
36
+ description: "List all supported language codes for --language flag.",
37
+ },
38
+ run: () => {
39
+ writeResearchJson({ ok: true, data: LANGUAGES, fields: FIELDS });
40
+ },
41
+ });
42
+ //# sourceMappingURL=languages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"languages.js","sourceRoot":"","sources":["../../../src/commands/research/languages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,cAAc,CAAC;IACb,OAAO,EAAE,oBAAoB;IAC7B,WAAW,EAAE,6EAA6E;IAC1F,IAAI,EAAE,EAAE;CACT,CAAC,CAAC;AAEH,MAAM,SAAS,GAA0C;IACvD,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE;IAC/B,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE;IAC/B,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE;IAC9B,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE;IAC9B,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE;IAC/B,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE;IAClC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE;IAC7B,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE;IAChC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE;IAC9B,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE;IAC/B,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE;IAC9B,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE;IAC/B,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE;IAC9B,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE;IAC/B,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE;IAC/B,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE;IACjC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE;IAC9B,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE;CAChC,CAAC;AAEF,MAAM,MAAM,GAA2B;IACrC,IAAI,EAAE,qCAAqC;IAC3C,IAAI,EAAE,6CAA6C;CACpD,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAG,aAAa,CAAC;IAC5C,IAAI,EAAE;QACJ,IAAI,EAAE,WAAW;QACjB,WAAW,EAAE,wDAAwD;KACtE;IACD,GAAG,EAAE,GAAG,EAAE;QACR,iBAAiB,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACnE,CAAC;CACF,CAAC,CAAC"}
@@ -0,0 +1,24 @@
1
+ export declare const lighthouseCommand: import("citty").CommandDef<{
2
+ readonly url: {
3
+ readonly type: "positional";
4
+ readonly description: "Full URL to audit (with https://)";
5
+ readonly required: true;
6
+ };
7
+ readonly mobile: {
8
+ readonly type: "string";
9
+ readonly description: "Test as mobile: true|false (default: true)";
10
+ readonly required: false;
11
+ };
12
+ readonly output: {
13
+ readonly type: "string";
14
+ readonly description: "Format: json|csv|md|jsonl";
15
+ readonly required: false;
16
+ readonly default: "json";
17
+ };
18
+ readonly "no-cache": {
19
+ readonly type: "boolean";
20
+ readonly description: "Skip server cache, hit API directly";
21
+ readonly required: false;
22
+ };
23
+ }>;
24
+ //# sourceMappingURL=lighthouse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lighthouse.d.ts","sourceRoot":"","sources":["../../../src/commands/research/lighthouse.ts"],"names":[],"mappings":"AAmCA,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;EAqC5B,CAAC"}
@@ -0,0 +1,60 @@
1
+ import { defineCommand } from "citty";
2
+ import { apiPost } from "../../client.js";
3
+ import { registerSchema } from "../../schemas.js";
4
+ import { handleResearchError, writeResearchJson, writeResearchOutput } from "./output.js";
5
+ registerSchema({
6
+ command: "research.lighthouse",
7
+ description: "Landing page performance audit. Returns metrics that affect Google Ads Quality Score and CPC.",
8
+ args: {
9
+ url: { type: "string", description: "Full URL to audit (with https://)", required: true },
10
+ mobile: { type: "boolean", description: "Test as mobile (default: true)", required: false },
11
+ "no-cache": { type: "boolean", description: "Skip server cache, hit API directly", required: false },
12
+ },
13
+ });
14
+ const FIELDS = {
15
+ url: "URL that was audited",
16
+ performance_score: "Lighthouse performance score 0-100 (aim for 90+)",
17
+ fcp_ms: "First Contentful Paint in ms (good: < 1800)",
18
+ lcp_ms: "Largest Contentful Paint in ms (good: < 2500)",
19
+ cls: "Cumulative Layout Shift (good: < 0.1)",
20
+ speed_index_ms: "Speed Index in ms (good: < 3400)",
21
+ interactive_ms: "Time to Interactive in ms (good: < 3800)",
22
+ };
23
+ export const lighthouseCommand = defineCommand({
24
+ meta: {
25
+ name: "lighthouse",
26
+ description: `Landing page performance audit. Metrics affecting Google Ads Quality Score.
27
+
28
+ Examples:
29
+ baker research lighthouse "https://example.com/landing"
30
+ baker research lighthouse "https://example.com" --mobile false`,
31
+ },
32
+ args: {
33
+ url: { type: "positional", description: "Full URL to audit (with https://)", required: true },
34
+ mobile: { type: "string", description: "Test as mobile: true|false (default: true)", required: false },
35
+ output: { type: "string", description: "Format: json|csv|md|jsonl", required: false, default: "json" },
36
+ "no-cache": { type: "boolean", description: "Skip server cache, hit API directly", required: false },
37
+ },
38
+ run: async ({ args }) => {
39
+ const url = args.url;
40
+ const mobile = args.mobile === "false" ? false : undefined;
41
+ const skipCache = args["no-cache"] ? true : undefined;
42
+ try {
43
+ const data = await apiPost("/api/research/lighthouse", {
44
+ url,
45
+ mobile,
46
+ skipCache,
47
+ });
48
+ const format = args.output || "json";
49
+ if (format !== "json") {
50
+ writeResearchOutput([data], format);
51
+ return;
52
+ }
53
+ writeResearchJson({ ok: true, data, fields: FIELDS });
54
+ }
55
+ catch (err) {
56
+ handleResearchError(err);
57
+ }
58
+ },
59
+ });
60
+ //# sourceMappingURL=lighthouse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lighthouse.js","sourceRoot":"","sources":["../../../src/commands/research/lighthouse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAE1F,cAAc,CAAC;IACb,OAAO,EAAE,qBAAqB;IAC9B,WAAW,EAAE,+FAA+F;IAC5G,IAAI,EAAE;QACJ,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,mCAAmC,EAAE,QAAQ,EAAE,IAAI,EAAE;QACzF,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,gCAAgC,EAAE,QAAQ,EAAE,KAAK,EAAE;QAC3F,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,qCAAqC,EAAE,QAAQ,EAAE,KAAK,EAAE;KACrG;CACF,CAAC,CAAC;AAYH,MAAM,MAAM,GAA2B;IACrC,GAAG,EAAE,sBAAsB;IAC3B,iBAAiB,EAAE,kDAAkD;IACrE,MAAM,EAAE,6CAA6C;IACrD,MAAM,EAAE,+CAA+C;IACvD,GAAG,EAAE,uCAAuC;IAC5C,cAAc,EAAE,kCAAkC;IAClD,cAAc,EAAE,0CAA0C;CAC3D,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG,aAAa,CAAC;IAC7C,IAAI,EAAE;QACJ,IAAI,EAAE,YAAY;QAClB,WAAW,EAAE;;;;iEAIgD;KAC9D;IACD,IAAI,EAAE;QACJ,GAAG,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,WAAW,EAAE,mCAAmC,EAAE,QAAQ,EAAE,IAAI,EAAE;QAC7F,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,4CAA4C,EAAE,QAAQ,EAAE,KAAK,EAAE;QACtG,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,2BAA2B,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE;QACtG,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,qCAAqC,EAAE,QAAQ,EAAE,KAAK,EAAE;KACrG;IACD,GAAG,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QACtB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAa,CAAC;QAC/B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;QAC3D,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;QAEtD,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,OAAO,CAAmB,0BAA0B,EAAE;gBACvE,GAAG;gBACH,MAAM;gBACN,SAAS;aACV,CAAC,CAAC;YAEH,MAAM,MAAM,GAAI,IAAI,CAAC,MAAiB,IAAI,MAAM,CAAC;YACjD,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;gBACtB,mBAAmB,CAAC,CAAC,IAA0C,CAAC,EAAE,MAAM,CAAC,CAAC;gBAC1E,OAAO;YACT,CAAC;YACD,iBAAiB,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACxD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,mBAAmB,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;CACF,CAAC,CAAC"}
@@ -0,0 +1,29 @@
1
+ export declare const RESEARCH_DATA_NOTE = "Estimates based on third-party SERP data \u2014 not exact figures. Use for directional insights, not precise measurement.";
2
+ interface PaginationInfo {
3
+ offset: number;
4
+ limit: number;
5
+ has_more: boolean;
6
+ next_offset?: number;
7
+ }
8
+ interface ResearchSuccessEnvelope<T> {
9
+ ok: true;
10
+ data: T;
11
+ fields?: Record<string, string>;
12
+ note?: string;
13
+ total_count?: number;
14
+ pagination?: PaginationInfo;
15
+ [key: string]: unknown;
16
+ }
17
+ interface ResearchErrorEnvelope {
18
+ ok: false;
19
+ error: {
20
+ code: string;
21
+ message: string;
22
+ };
23
+ }
24
+ type ResearchOutput = ResearchSuccessEnvelope<unknown> | ResearchErrorEnvelope;
25
+ export declare function writeResearchJson(envelope: ResearchOutput): void;
26
+ export declare function writeResearchOutput(data: Array<Record<string, unknown>>, format: string, fields?: string[]): void;
27
+ export declare function handleResearchError(err: unknown): never;
28
+ export {};
29
+ //# sourceMappingURL=output.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../../src/commands/research/output.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,kBAAkB,8HACyF,CAAC;AAEzH,UAAU,cAAc;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,UAAU,uBAAuB,CAAC,CAAC;IACjC,EAAE,EAAE,IAAI,CAAC;IACT,IAAI,EAAE,CAAC,CAAC;IACR,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,UAAU,qBAAqB;IAC7B,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED,KAAK,cAAc,GAAG,uBAAuB,CAAC,OAAO,CAAC,GAAG,qBAAqB,CAAC;AAE/E,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAEhE;AAgDD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAcjH;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG,KAAK,CAgBvD"}
@@ -0,0 +1,81 @@
1
+ import { ApiError } from "../../client.js";
2
+ export const RESEARCH_DATA_NOTE = "Estimates based on third-party SERP data — not exact figures. Use for directional insights, not precise measurement.";
3
+ export function writeResearchJson(envelope) {
4
+ process.stdout.write(`${JSON.stringify(envelope, null, 2)}\n`);
5
+ }
6
+ function toCsvRow(values) {
7
+ return values
8
+ .map((v) => {
9
+ if (v.includes(",") || v.includes('"') || v.includes("\n")) {
10
+ return `"${v.replace(/"/g, '""')}"`;
11
+ }
12
+ return v;
13
+ })
14
+ .join(",");
15
+ }
16
+ function flattenRow(row) {
17
+ const flat = {};
18
+ for (const [key, val] of Object.entries(row)) {
19
+ flat[key] = typeof val === "object" && val !== null ? JSON.stringify(val) : String(val ?? "");
20
+ }
21
+ return flat;
22
+ }
23
+ function writeCsv(data, fields) {
24
+ if (data.length === 0)
25
+ return;
26
+ const cols = fields ?? Object.keys(data[0] ?? {});
27
+ process.stdout.write(`${toCsvRow(cols)}\n`);
28
+ for (const row of data) {
29
+ const flat = flattenRow(row);
30
+ process.stdout.write(`${toCsvRow(cols.map((f) => flat[f] ?? ""))}\n`);
31
+ }
32
+ }
33
+ function writeJsonl(data) {
34
+ for (const row of data) {
35
+ process.stdout.write(`${JSON.stringify(row)}\n`);
36
+ }
37
+ }
38
+ function writeMd(data, fields) {
39
+ if (data.length === 0)
40
+ return;
41
+ const cols = fields ?? Object.keys(data[0] ?? {});
42
+ process.stdout.write(`| ${cols.join(" | ")} |\n`);
43
+ process.stdout.write(`| ${cols.map(() => "---").join(" | ")} |\n`);
44
+ for (const row of data) {
45
+ const flat = flattenRow(row);
46
+ process.stdout.write(`| ${cols.map((f) => flat[f] ?? "").join(" | ")} |\n`);
47
+ }
48
+ }
49
+ export function writeResearchOutput(data, format, fields) {
50
+ switch (format) {
51
+ case "csv":
52
+ writeCsv(data, fields);
53
+ break;
54
+ case "jsonl":
55
+ writeJsonl(data);
56
+ break;
57
+ case "md":
58
+ writeMd(data, fields);
59
+ break;
60
+ default:
61
+ writeResearchJson({ ok: true, data });
62
+ }
63
+ }
64
+ export function handleResearchError(err) {
65
+ if (err instanceof ApiError) {
66
+ writeResearchJson({
67
+ ok: false,
68
+ error: {
69
+ code: err.code,
70
+ message: err.message,
71
+ },
72
+ });
73
+ process.exit(1);
74
+ }
75
+ writeResearchJson({
76
+ ok: false,
77
+ error: { code: "NETWORK_ERROR", message: err instanceof Error ? err.message : "Unexpected error" },
78
+ });
79
+ process.exit(1);
80
+ }
81
+ //# sourceMappingURL=output.js.map