seomd-cli 1.1.2 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -36,6 +36,12 @@ SEO.md is a structured, version-controlled specification for describing your sit
36
36
  npm install -g seomd-cli
37
37
  ```
38
38
 
39
+ Or run without installing (zero-install):
40
+
41
+ ```bash
42
+ npx seomd-cli init
43
+ ```
44
+
39
45
  Verify:
40
46
 
41
47
  ```bash
@@ -52,6 +58,14 @@ Run in the root of your project:
52
58
  seomd init
53
59
  ```
54
60
 
61
+ Or non-interactively with flags:
62
+
63
+ ```bash
64
+ seomd init -y --type local
65
+ # or
66
+ seomd init --brand "Acme" --domain acme.com --primary-keyword "local seo"
67
+ ```
68
+
55
69
  ### 2) Validate
56
70
 
57
71
  ```bash
@@ -74,24 +88,57 @@ seomd status --json
74
88
 
75
89
  ## Configuration
76
90
 
77
- Copy the example env file:
91
+ Copy `.env.example` from your platform provider docs, or create one with the vars you need:
92
+
93
+ Required for live audits:
78
94
 
79
95
  ```bash
80
- cp .env.example .env
96
+ SEOMD_API_URL=
97
+ SEOMD_API_KEY=your_key_here
81
98
  ```
82
99
 
83
- Environment variables:
100
+ Optional:
84
101
 
85
- - `SEOMD_API_URL` (optional) API base URL (defaults to `https://api.foxcite.com`)
86
- - `SEOMD_API_KEY` (optional) platform API key (human dashboard auth)
87
- - `SEOMD_PAYMENT_TOKEN` (optional) agent-native payment token (x402 pay-per-scan)
88
- - `SEOMD_DOMAIN` (optional) override domain header
102
+ ```bash
103
+ SEOMD_PAYMENT_TOKEN= # x402 pay-per-scan token
104
+ SEOMD_DOMAIN= # override domain header
105
+ ```
106
+
107
+ > If you don't have a platform key yet, `seomd init` still works without `.env`.
89
108
 
90
109
  ## Commands
91
110
 
92
111
  ### `seomd init`
93
112
 
94
- Scaffolds `SEO.md`, `SEO.REVERSE.md`, and the `.seomd/` intelligence directory.
113
+ Scaffolds `SEO.md`, `SEO.REVERSE.md`, and the `.seo/` intelligence directory.
114
+
115
+ **Usage:**
116
+
117
+ ```bash
118
+ seomd init # interactive 5-question flow
119
+ seomd init -y --type local # skip prompts, use defaults
120
+ seomd init --brand "Acme" --domain acme.com # non-interactive with partial flags
121
+ seomd init --type saas --brand "MyApp" --domain myapp.com --primary-keyword "billing automation" --output ./new-project
122
+ ```
123
+
124
+ **Options:**
125
+
126
+ | Flag | Description |
127
+ |------|-------------|
128
+ | `-y, --yes` | skip prompts, use defaults |
129
+ | `--type <type>` | site type: saas, ecommerce, local, blog, marketplace |
130
+ | `--brand <name>` | brand name |
131
+ | `--domain <domain>` | primary domain |
132
+ | `--primary-keyword <keyword>` | primary keyword |
133
+ | `--competitors <list>` | comma-separated competitor list |
134
+ | `--output <dir>` | scaffold into a new (empty) directory instead of cwd |
135
+
136
+ **Behavior:**
137
+
138
+ - Interactive flow by default (5 questions)
139
+ - Non-interactive when `-y` is set **OR** any config flag (`--brand`, `--domain`, `--primary-keyword`, `--competitors`) is provided
140
+ - `--type` alone pre-selects site type in the interactive flow
141
+ - `--output` writes all files to the target directory (must be empty or non-existent)
95
142
 
96
143
  ### `seomd validate`
97
144
 
@@ -109,7 +156,7 @@ Runs an AI search audit via your connected platform and writes results back into
109
156
 
110
157
  - `SEO.md` (`_analysis` blocks)
111
158
  - `SEO.REVERSE.md` (generated reverse view)
112
- - `.seomd/pages/*.md` (per-page playbooks when available)
159
+ - `.seo/pages/*.md` (per-page playbooks when available)
113
160
 
114
161
  ### `seomd sync`
115
162
 
@@ -117,6 +164,25 @@ Pulls cached/latest platform intelligence and writes it back to the same files a
117
164
 
118
165
  - `--dry-run` prints a preview and does not modify files
119
166
 
167
+ ## Built-in Templates
168
+
169
+ `seomd-cli` ships with type-specific templates under `src/templates/`. `seomd init --type <type>` uses the matching template automatically.
170
+
171
+ | Type | Template dir | Best for |
172
+ |------|-------------|----------|
173
+ | `saas` | `src/templates/saas/` | Software products, B2B tools, web apps |
174
+ | `blog` | `src/templates/blog/` | Content sites, newsletters, personal brands |
175
+ | `ecommerce` | `src/templates/ecommerce/` | Online stores, DTC brands, product catalogs |
176
+ | `local` | `src/templates/local/` | Service-area businesses, locations pages |
177
+ | `marketplace` | `src/templates/marketplace/` | Two-sided platforms, directories |
178
+
179
+ Each template contains:
180
+
181
+ - `SEO.md` — pre-filled with type-specific intent queries, page structures, and negative keywords
182
+ - `SEO.REVERSE.md` — reverse-engineer output scaffold with placeholders for competitor analysis
183
+
184
+ Want to customize a template? Copy the relevant folder, edit the placeholders (`{{brand}}`, `{{domain}}`, etc.), and use `--type` with your custom scaffold.
185
+
120
186
  ## Local Development
121
187
 
122
188
  Prefer the local entrypoint while developing:
@@ -162,9 +228,11 @@ To enable live intelligence writebacks (using automated platforms like [Foxcite]
162
228
 
163
229
  1. Obtain a developer API key from your platform provider.
164
230
  2. Export the key as an environment variable:
231
+
165
232
  ```bash
166
233
  export SEOMD_API_KEY="your_api_key_here"
167
234
  ```
235
+
168
236
  3. Run `seomd sync` or `seomd analyze`.
169
237
 
170
238
  _Note: Never commit your API keys or `.env` files containing keys to version control._
package/bin/seomd.js CHANGED
@@ -25,6 +25,19 @@ program
25
25
  .description('Scaffold SEO.md for your project')
26
26
  .option('-y, --yes', 'skip prompts and use defaults')
27
27
  .option('--type <type>', 'site type: saas, ecommerce, local, blog, marketplace')
28
+ .option('--brand <brand>', 'brand name')
29
+ .option('--domain <domain>', 'primary domain')
30
+ .option('--primary-keyword <keyword>', 'primary keyword')
31
+ .option('--competitors <list>', 'comma-separated competitor list')
32
+ .option('--output <dir>', 'scaffold into a new directory instead of cwd')
33
+ .addHelpText('after', `
34
+
35
+ Examples:
36
+ seomd init # interactive 5-question flow
37
+ seomd init -y --type local # skip prompts, use defaults
38
+ seomd init --brand "Acme" --domain acme.com --primary-keyword "local seo"
39
+ seomd init --type saas --brand "MyApp" --domain myapp.com --output ./new-project
40
+ `)
28
41
  .action(initCommand);
29
42
 
30
43
  program
@@ -33,23 +46,47 @@ program
33
46
  .option('--page <url>', 'analyze a specific page only')
34
47
  .option('--intent <category>', 'analyze a specific intent category only')
35
48
  .option('--engines <list>', 'comma-separated list of engines to scan (e.g. chatgpt,claude)')
49
+ .addHelpText('after', `
50
+
51
+ Examples:
52
+ seomd analyze # full audit
53
+ seomd analyze --page /pricing # single page audit
54
+ seomd analyze --intent transactional --engines chatgpt,claude
55
+ `)
36
56
  .action(analyzeCommand);
37
57
 
38
58
  program
39
59
  .command('sync')
40
60
  .description('Sync latest platform intelligence to your SEO.md files')
41
61
  .option('--dry-run', 'preview changes without writing')
62
+ .addHelpText('after', `
63
+
64
+ Examples:
65
+ seomd sync # sync latest data
66
+ seomd sync --dry-run # preview changes only
67
+ `)
42
68
  .action(syncCommand);
43
69
 
44
70
  program
45
71
  .command('status')
46
72
  .description('Show current citation rates and gap scores')
47
73
  .option('--json', 'output as JSON')
74
+ .addHelpText('after', `
75
+
76
+ Examples:
77
+ seomd status # human-readable summary
78
+ seomd status --json # machine-readable JSON
79
+ `)
48
80
  .action(statusCommand);
49
81
 
50
82
  program
51
83
  .command('validate')
52
84
  .description('Validate your SEO.md against the spec')
85
+ .addHelpText('after', `
86
+
87
+ Example:
88
+ seomd validate
89
+ `)
53
90
  .action(validateCommand);
54
91
 
55
92
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "seomd-cli",
3
- "version": "1.1.2",
3
+ "version": "1.3.0",
4
4
  "description": "The official CLI for the SEO.md open standard — AEO infrastructure for technical founders",
5
5
  "homepage": "https://seomd.dev",
6
6
  "repository": {
@@ -25,18 +25,19 @@
25
25
  "release:notes": "node scripts/release-notes.js"
26
26
  },
27
27
  "dependencies": {
28
+ "axios": "^1.6.0",
28
29
  "chalk": "^5.3.0",
29
30
  "commander": "^11.0.0",
31
+ "dotenv": "^16.3.1",
30
32
  "enquirer": "^2.4.1",
31
- "ora": "^7.0.1",
32
- "yaml": "^2.3.4",
33
33
  "fs-extra": "^11.1.1",
34
- "axios": "^1.6.0",
35
- "dotenv": "^16.3.1"
34
+ "ora": "^7.0.1",
35
+ "yaml": "^2.3.4"
36
36
  },
37
37
  "devDependencies": {
38
- "jest": "^29.7.0",
39
- "eslint": "^8.50.0"
38
+ "eslint": "^8.50.0",
39
+ "eslint-plugin-n": "^18.1.0",
40
+ "jest": "^29.7.0"
40
41
  },
41
42
  "engines": {
42
43
  "node": ">=18.0.0"
@@ -1,7 +1,6 @@
1
1
  import chalk from 'chalk';
2
2
  import ora from 'ora';
3
3
  import fs from 'fs-extra';
4
- import path from 'path';
5
4
  import dotenv from 'dotenv';
6
5
  import { parseSeoMd } from '../utils/parser.js';
7
6
  import { client } from '../utils/api-client.js';
@@ -12,16 +11,16 @@ dotenv.config();
12
11
  function matchRoute(pattern, url) {
13
12
  const cleanPattern = pattern.replace(/\/$/, '');
14
13
  const cleanUrl = url.replace(/\/$/, '');
15
-
14
+
16
15
  if (cleanPattern.toLowerCase() === cleanUrl.toLowerCase()) {
17
16
  return true;
18
17
  }
19
-
18
+
20
19
  // Replace "/[param]" with optional group "(?:/([^/]+))?"
21
20
  const regexPattern = cleanPattern
22
21
  .replace(/\/\[[^\]]+\]/g, '(?:\\/([^/]+))?')
23
22
  .replace(/\//g, '\\/');
24
-
23
+
25
24
  const regex = new RegExp('^' + regexPattern + '\\/?$', 'i');
26
25
  return regex.test(url);
27
26
  }
@@ -148,7 +147,7 @@ export async function analyzeCommand(options) {
148
147
  const brandName = data.identity?.brand || 'My Brand';
149
148
  await writeReverseMd(cwd, results, domain, brandName);
150
149
 
151
- // Writeback to .seomd/pages/*.md
150
+ // Writeback to .seo/pages/*.md
152
151
  await writePageAnalysis(cwd, results);
153
152
 
154
153
  spinner.succeed(chalk.green('Analysis completed successfully!'));
@@ -169,7 +168,7 @@ export async function analyzeCommand(options) {
169
168
  console.log('');
170
169
  console.log(chalk.green('✔ SEO.md updated.'));
171
170
  console.log(chalk.green('✔ SEO.REVERSE.md updated.'));
172
- console.log(chalk.green('✔ .seomd/pages/ playbooks generated.'));
171
+ console.log(chalk.green('✔ .seo/pages/ playbooks generated.'));
173
172
  console.log('');
174
173
 
175
174
  } catch (err) {
@@ -16,8 +16,19 @@ export async function initCommand(options) {
16
16
  console.log(chalk.dim('The open standard for AI-era SEO configuration.'));
17
17
  console.log('');
18
18
 
19
- // Check if SEO.md already exists
20
- const seomdPath = path.join(process.cwd(), 'SEO.md');
19
+ // Determine target directory
20
+ let workingDir = process.cwd();
21
+ if (options.output) {
22
+ const resolved = path.resolve(options.output);
23
+ if (await fs.pathExists(resolved) && (await fs.readdir(resolved)).length > 0) {
24
+ console.log(chalk.red(`Output directory must be empty or non-existent: ${resolved}`));
25
+ process.exit(1);
26
+ }
27
+ await fs.ensureDir(resolved);
28
+ workingDir = resolved;
29
+ }
30
+
31
+ const seomdPath = path.join(workingDir, 'SEO.md');
21
32
  if (await fs.pathExists(seomdPath)) {
22
33
  console.log(chalk.yellow('⚠ SEO.md already exists in this directory.'));
23
34
  const { overwrite } = await prompt({
@@ -34,14 +45,26 @@ export async function initCommand(options) {
34
45
 
35
46
  let answers;
36
47
 
37
- if (options.yes) {
38
- // Default values for --yes flag
48
+ // Mode 5: non-interactive if -y OR any config field provided
49
+ const hasConfigFlags =
50
+ options.brand !== undefined ||
51
+ options.domain !== undefined ||
52
+ options.primaryKeyword !== undefined ||
53
+ options.competitors !== undefined;
54
+
55
+ if (options.yes || hasConfigFlags) {
39
56
  answers = {
40
57
  site_type: options.type || 'saas',
41
- domain: 'example.com',
42
- brand: 'My Brand',
43
- primary_keyword: '',
44
- competitors: '',
58
+ domain: options.domain || 'example.com',
59
+ brand: options.brand || 'My Brand',
60
+ primary_keyword: options.primaryKeyword || '',
61
+ competitors: options.competitors
62
+ ? options.competitors
63
+ .split(',')
64
+ .map((c) => c.trim())
65
+ .filter(Boolean)
66
+ .slice(0, 3)
67
+ : [],
45
68
  };
46
69
  } else {
47
70
  // The 5-question init flow
@@ -109,21 +132,20 @@ export async function initCommand(options) {
109
132
  try {
110
133
  // 1. Generate SEO.md
111
134
  const seomdContent = generateSeoMd(answers);
112
- await fs.writeFile(seomdPath, seomdContent, 'utf8');
135
+ await fs.writeFile(path.join(workingDir, 'SEO.md'), seomdContent, 'utf8');
113
136
  spinner.succeed(chalk.green('SEO.md created'));
114
137
 
115
138
  // 2. Generate SEO.REVERSE.md
116
- const reversePath = path.join(process.cwd(), 'SEO.REVERSE.md');
117
139
  const reverseContent = generateReverseMd(answers);
118
- await fs.writeFile(reversePath, reverseContent, 'utf8');
140
+ await fs.writeFile(path.join(workingDir, 'SEO.REVERSE.md'), reverseContent, 'utf8');
119
141
  spinner.succeed(chalk.green('SEO.REVERSE.md initialized'));
120
142
 
121
- // 3. Create .seomd/ directory structure
122
- await createSeomdDir(process.cwd(), answers);
123
- spinner.succeed(chalk.green('.seomd/ directory created'));
143
+ // 3. Create .seo/ directory structure
144
+ await createSeomdDir(workingDir, answers);
145
+ spinner.succeed(chalk.green('.seo/ directory created'));
124
146
 
125
- // 4. Add .seomd/ to .gitignore if it exists
126
- await updateGitignore(process.cwd());
147
+ // 4. Add .seo/ to .gitignore if it exists
148
+ await updateGitignore(workingDir);
127
149
 
128
150
  console.log('');
129
151
  console.log(chalk.bold.green('✓ SEO.md initialized successfully'));
@@ -131,7 +153,7 @@ export async function initCommand(options) {
131
153
  console.log(chalk.dim('Files created:'));
132
154
  console.log(' ' + chalk.cyan('SEO.md') + chalk.dim(' — your living SEO config'));
133
155
  console.log(' ' + chalk.cyan('SEO.REVERSE.md') + chalk.dim(' — reverse engineer output (platform generated)'));
134
- console.log(' ' + chalk.cyan('.seomd/') + chalk.dim(' — intelligence directory'));
156
+ console.log(' ' + chalk.cyan('.seo/') + chalk.dim(' — intelligence directory'));
135
157
  console.log('');
136
158
  console.log(chalk.dim('Next steps:'));
137
159
  console.log(' ' + chalk.white('npx seomd analyze') + chalk.dim(' — run your first citation analysis'));
@@ -149,11 +171,11 @@ export async function initCommand(options) {
149
171
 
150
172
  async function updateGitignore(cwd) {
151
173
  const gitignorePath = path.join(cwd, '.gitignore');
152
- const entry = '\n# seomd intelligence directory\n.seomd/reports/\n';
174
+ const entry = '\n# seomd intelligence directory\n.seo/reports/\n';
153
175
 
154
176
  if (await fs.pathExists(gitignorePath)) {
155
177
  const content = await fs.readFile(gitignorePath, 'utf8');
156
- if (!content.includes('.seomd')) {
178
+ if (!content.includes('.seo')) {
157
179
  await fs.appendFile(gitignorePath, entry);
158
180
  }
159
181
  }
@@ -1,7 +1,6 @@
1
1
  import chalk from 'chalk';
2
2
  import ora from 'ora';
3
3
  import fs from 'fs-extra';
4
- import path from 'path';
5
4
  import dotenv from 'dotenv';
6
5
  import { parseSeoMd } from '../utils/parser.js';
7
6
  import { client } from '../utils/api-client.js';
@@ -91,7 +90,7 @@ export async function syncCommand(options) {
91
90
  const brandName = data.identity?.brand || 'My Brand';
92
91
  await writeReverseMd(cwd, results, domain, brandName);
93
92
 
94
- // Writeback to .seomd/pages/*.md
93
+ // Writeback to .seo/pages/*.md
95
94
  await writePageAnalysis(cwd, results);
96
95
 
97
96
  spinner.succeed(chalk.green('Sync completed successfully!'));
@@ -112,7 +111,7 @@ export async function syncCommand(options) {
112
111
  console.log('');
113
112
  console.log(chalk.green('✔ SEO.md updated.'));
114
113
  console.log(chalk.green('✔ SEO.REVERSE.md updated.'));
115
- console.log(chalk.green('✔ .seomd/pages/ playbooks synchronized.'));
114
+ console.log(chalk.green('✔ .seo/pages/ playbooks synchronized.'));
116
115
  console.log('');
117
116
 
118
117
  } catch (err) {
@@ -5,14 +5,14 @@ import { REQUIRED_PAGES } from '../utils/constants.js';
5
5
  export async function createSeomdDir(cwd, answers) {
6
6
  const { site_type, brand, domain } = answers;
7
7
  const pages = REQUIRED_PAGES[site_type] || REQUIRED_PAGES.saas;
8
- const seomdDir = path.join(cwd, '.seomd');
8
+ const seomdDir = path.join(cwd, '.seo');
9
9
 
10
10
  // Create directory structure
11
11
  await fs.ensureDir(path.join(seomdDir, 'pages'));
12
12
  await fs.ensureDir(path.join(seomdDir, 'reports'));
13
13
  await fs.ensureDir(path.join(seomdDir, 'competitors'));
14
14
 
15
- // Create README inside .seomd/
15
+ // Create README inside .seo/
16
16
  await fs.writeFile(
17
17
  path.join(seomdDir, 'README.md'),
18
18
  generateSeomdReadme(brand, domain),
@@ -38,40 +38,91 @@ export async function createSeomdDir(cwd, answers) {
38
38
  }
39
39
 
40
40
  function generateSeomdReadme(brand, domain) {
41
- return `# .seomd/
41
+ return `# .seo/
42
42
 
43
- Intelligence directory for ${brand} (${domain})
43
+ AI search optimization workspace for **${brand}** (${domain})
44
44
  Generated by SEO.md CLI — https://seomd.dev
45
45
 
46
+ ## What is this?
47
+
48
+ \`.seo/\` stores platform-generated intelligence from citation audits, competitor analysis, and reverse-engineered playbooks. It lives alongside your source code so SEO strategy is version-controlled and reviewable in PRs.
49
+
46
50
  ## Directory Structure
47
51
 
48
52
  \`\`\`
49
- .seomd/
50
- ├── pages/ # per-page reverse engineer analysis
53
+ .seo/
54
+ ├── pages/ # per-page reverse-engineer analysis
55
+ │ ├── homepage.md
56
+ │ ├── services.md
57
+ │ └── ...
51
58
  ├── reports/ # dated snapshot reports (gitignored by default)
59
+ │ └── 2026-06-10.md
52
60
  └── competitors/ # competitor citation profiles
61
+ └── comp-1.md
53
62
  \`\`\`
54
63
 
55
- ## Usage
64
+ ## Git behavior
65
+
66
+ | Path | Git | Why |
67
+ |------|-----|-----|
68
+ | \`.seo/pages/\` | tracked | actionable playbooks belong in code review |
69
+ | \`.seo/competitors/\` | tracked | competitor intel is project state |
70
+ | \`.seo/reports/\` | ignored | dated snapshots are noise in git history |
71
+
72
+ \`.seo/reports/\` is added to \`.gitignore\` automatically by \`seomd init\`.
73
+
74
+ ## When to run what
56
75
 
57
76
  \`\`\`bash
58
- # Run citation analysis and update all files
77
+ # First full audit scans all pages + intents
59
78
  npx seomd analyze
60
79
 
61
- # Sync latest platform intelligence
80
+ # Refresh from cached platform data (no new scan)
62
81
  npx seomd sync
63
82
 
64
- # View current status
83
+ # Check current gap scores without modifying files
65
84
  npx seomd status
85
+ npx seomd status --json
66
86
  \`\`\`
67
87
 
68
- ## File Ownership
88
+ | Command | Use when |
89
+ |---------|----------|
90
+ | \`analyze\` | You want a fresh audit, new competitor data, or updated playbooks |
91
+ | \`sync\` | You just want the latest cached data from your platform |
92
+ | \`status\` | You want a quick health check without writebacks |
93
+
94
+ ## Reading a page playbook
95
+
96
+ Each \`pages/<id>.md\` contains:
97
+
98
+ - **why_page_won** — what top-cited competitors do right
99
+ - **citation_hooks** — patterns to replicate
100
+ - **gaps_for_brand** — where your page falls short
101
+ - **remediation_playbook** — step-by-step fixes with effort/impact
102
+ - **fastest_win** — highest ROI action to take now
103
+ - **suggested_content_outline** — ready-to-use outline for your writer or AI
104
+
105
+ ## File ownership
106
+
107
+ | File/dir | Owner | Editable? |
108
+ |-----------|-------|----------|
109
+ | \`SEO.md\` | you | yes |
110
+ | \`SEO.REVERSE.md\` | platform | no |
111
+ | \`.seo/pages/*.md\` | platform | no |
112
+ | \`.seo/competitors/*.md\` | platform | no |
113
+ | \`.seo/reports/*.md\` | platform | no |
114
+
115
+ Platform-generated files are overwritten on each \`analyze\` or \`sync\`.
116
+
117
+ ## Platform connection
118
+
119
+ To enable live writebacks:
69
120
 
70
- - \`pages/\` platform generated, do not edit manually
71
- - \`reports/\` platform generated, gitignored by default
72
- - \`competitors/\` platform generated, do not edit manually
121
+ 1. Connect at https://seomd.dev/connect
122
+ 2. Add \`SEOMD_API_KEY\` to your \`.env\`
123
+ 3. Run \`npx seomd analyze\`
73
124
 
74
- Connect your platform at https://seomd.dev/connect
125
+ _Note: Never commit API keys or \`.env\` files to version control._
75
126
  `;
76
127
  }
77
128
 
@@ -110,13 +110,13 @@ export async function writeReverseMd(cwd, response, defaultDomain = 'example.com
110
110
  }
111
111
 
112
112
  /**
113
- * Writes individual page playbooks into .seomd/pages/{id}.md files.
113
+ * Writes individual page playbooks into .seo/pages/{id}.md files.
114
114
  *
115
115
  * @param {string} cwd - Current working directory
116
116
  * @param {any} response - The API response from analyze/sync
117
117
  */
118
118
  export async function writePageAnalysis(cwd, response) {
119
- const seomdDir = path.join(cwd, '.seomd');
119
+ const seomdDir = path.join(cwd, '.seo');
120
120
  const pagesDir = path.join(seomdDir, 'pages');
121
121
 
122
122
  await fs.ensureDir(pagesDir);