seomd-cli 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -91,12 +91,14 @@ seomd status --json
91
91
  Copy `.env.example` from your platform provider docs, or create one with the vars you need:
92
92
 
93
93
  Required for live audits:
94
+
94
95
  ```bash
95
96
  SEOMD_API_URL=
96
97
  SEOMD_API_KEY=your_key_here
97
98
  ```
98
99
 
99
100
  Optional:
101
+
100
102
  ```bash
101
103
  SEOMD_PAYMENT_TOKEN= # x402 pay-per-scan token
102
104
  SEOMD_DOMAIN= # override domain header
@@ -108,9 +110,10 @@ SEOMD_DOMAIN= # override domain header
108
110
 
109
111
  ### `seomd init`
110
112
 
111
- Scaffolds `SEO.md`, `SEO.REVERSE.md`, and the `.seomd/` intelligence directory.
113
+ Scaffolds `SEO.md`, `SEO.REVERSE.md`, and the `.seo/` intelligence directory.
112
114
 
113
115
  **Usage:**
116
+
114
117
  ```bash
115
118
  seomd init # interactive 5-question flow
116
119
  seomd init -y --type local # skip prompts, use defaults
@@ -119,6 +122,7 @@ seomd init --type saas --brand "MyApp" --domain myapp.com --primary-keyword "bil
119
122
  ```
120
123
 
121
124
  **Options:**
125
+
122
126
  | Flag | Description |
123
127
  |------|-------------|
124
128
  | `-y, --yes` | skip prompts, use defaults |
@@ -130,6 +134,7 @@ seomd init --type saas --brand "MyApp" --domain myapp.com --primary-keyword "bil
130
134
  | `--output <dir>` | scaffold into a new (empty) directory instead of cwd |
131
135
 
132
136
  **Behavior:**
137
+
133
138
  - Interactive flow by default (5 questions)
134
139
  - Non-interactive when `-y` is set **OR** any config flag (`--brand`, `--domain`, `--primary-keyword`, `--competitors`) is provided
135
140
  - `--type` alone pre-selects site type in the interactive flow
@@ -151,7 +156,7 @@ Runs an AI search audit via your connected platform and writes results back into
151
156
 
152
157
  - `SEO.md` (`_analysis` blocks)
153
158
  - `SEO.REVERSE.md` (generated reverse view)
154
- - `.seomd/pages/*.md` (per-page playbooks when available)
159
+ - `.seo/pages/*.md` (per-page playbooks when available)
155
160
 
156
161
  ### `seomd sync`
157
162
 
@@ -223,9 +228,11 @@ To enable live intelligence writebacks (using automated platforms like [Foxcite]
223
228
 
224
229
  1. Obtain a developer API key from your platform provider.
225
230
  2. Export the key as an environment variable:
231
+
226
232
  ```bash
227
233
  export SEOMD_API_KEY="your_api_key_here"
228
234
  ```
235
+
229
236
  3. Run `seomd sync` or `seomd analyze`.
230
237
 
231
238
  _Note: Never commit your API keys or `.env` files containing keys to version control._
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "seomd-cli",
3
- "version": "1.2.0",
3
+ "version": "1.4.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": {
@@ -35,7 +35,6 @@
35
35
  "yaml": "^2.3.4"
36
36
  },
37
37
  "devDependencies": {
38
- "@eslint/js": "^10.0.1",
39
38
  "eslint": "^8.50.0",
40
39
  "eslint-plugin-n": "^18.1.0",
41
40
  "jest": "^29.7.0"
@@ -11,16 +11,16 @@ dotenv.config();
11
11
  function matchRoute(pattern, url) {
12
12
  const cleanPattern = pattern.replace(/\/$/, '');
13
13
  const cleanUrl = url.replace(/\/$/, '');
14
-
14
+
15
15
  if (cleanPattern.toLowerCase() === cleanUrl.toLowerCase()) {
16
16
  return true;
17
17
  }
18
-
18
+
19
19
  // Replace "/[param]" with optional group "(?:/([^/]+))?"
20
20
  const regexPattern = cleanPattern
21
21
  .replace(/\/\[[^\]]+\]/g, '(?:\\/([^/]+))?')
22
22
  .replace(/\//g, '\\/');
23
-
23
+
24
24
  const regex = new RegExp('^' + regexPattern + '\\/?$', 'i');
25
25
  return regex.test(url);
26
26
  }
@@ -105,7 +105,7 @@ export async function analyzeCommand(options) {
105
105
  }
106
106
 
107
107
  // Extract engines
108
- let engines = data.aeo?._analysis?.engines_tracked || ['ChatGPT'];
108
+ let engines = ['ChatGPT', 'Perplexity', 'Claude']; // Default since we removed _analysis
109
109
  if (options.engines) {
110
110
  engines = options.engines.split(',').map(e => e.trim());
111
111
  }
@@ -140,14 +140,14 @@ export async function analyzeCommand(options) {
140
140
 
141
141
  spinner.text = 'Writing analysis blocks back to repository files...';
142
142
 
143
- // Writeback to SEO.md
143
+ // Writeback to .seo/STATUS.yml
144
144
  await writeAnalysisToSeoMd(doc, results, cwd);
145
145
 
146
- // Writeback to SEO.REVERSE.md
146
+ // Writeback to .seo/REVERSE.md
147
147
  const brandName = data.identity?.brand || 'My Brand';
148
148
  await writeReverseMd(cwd, results, domain, brandName);
149
149
 
150
- // Writeback to .seomd/pages/*.md
150
+ // Writeback to .seo/pages/*.md
151
151
  await writePageAnalysis(cwd, results);
152
152
 
153
153
  spinner.succeed(chalk.green('Analysis completed successfully!'));
@@ -166,9 +166,9 @@ export async function analyzeCommand(options) {
166
166
  console.log(`Next Analysis Target : ${chalk.dim(aeo.next_analysis)}`);
167
167
  console.log('-----------------------');
168
168
  console.log('');
169
- console.log(chalk.green('✔ SEO.md updated.'));
170
- console.log(chalk.green('✔ SEO.REVERSE.md updated.'));
171
- console.log(chalk.green('✔ .seomd/pages/ playbooks generated.'));
169
+ console.log(chalk.green('✔ .seo/STATUS.yml updated.'));
170
+ console.log(chalk.green('✔ .seo/REVERSE.md updated.'));
171
+ console.log(chalk.green('✔ .seo/pages/ playbooks generated.'));
172
172
  console.log('');
173
173
 
174
174
  } catch (err) {
@@ -60,10 +60,10 @@ export async function initCommand(options) {
60
60
  primary_keyword: options.primaryKeyword || '',
61
61
  competitors: options.competitors
62
62
  ? options.competitors
63
- .split(',')
64
- .map((c) => c.trim())
65
- .filter(Boolean)
66
- .slice(0, 3)
63
+ .split(',')
64
+ .map((c) => c.trim())
65
+ .filter(Boolean)
66
+ .slice(0, 3)
67
67
  : [],
68
68
  };
69
69
  } else {
@@ -135,25 +135,24 @@ export async function initCommand(options) {
135
135
  await fs.writeFile(path.join(workingDir, 'SEO.md'), seomdContent, 'utf8');
136
136
  spinner.succeed(chalk.green('SEO.md created'));
137
137
 
138
- // 2. Generate SEO.REVERSE.md
138
+ // 2. Create .seo/ directory structure
139
+ await createSeomdDir(workingDir, answers);
140
+ spinner.succeed(chalk.green('.seo/ directory created'));
141
+
142
+ // 3. Generate .seo/REVERSE.md
139
143
  const reverseContent = generateReverseMd(answers);
140
- await fs.writeFile(path.join(workingDir, 'SEO.REVERSE.md'), reverseContent, 'utf8');
141
- spinner.succeed(chalk.green('SEO.REVERSE.md initialized'));
144
+ await fs.writeFile(path.join(workingDir, '.seo', 'REVERSE.md'), reverseContent, 'utf8');
145
+ spinner.succeed(chalk.green('.seo/REVERSE.md initialized'));
142
146
 
143
- // 3. Create .seomd/ directory structure
144
- await createSeomdDir(workingDir, answers);
145
- spinner.succeed(chalk.green('.seomd/ directory created'));
146
147
 
147
- // 4. Add .seomd/ to .gitignore if it exists
148
- await updateGitignore(workingDir);
149
148
 
150
149
  console.log('');
151
150
  console.log(chalk.bold.green('✓ SEO.md initialized successfully'));
152
151
  console.log('');
153
152
  console.log(chalk.dim('Files created:'));
154
153
  console.log(' ' + chalk.cyan('SEO.md') + chalk.dim(' — your living SEO config'));
155
- console.log(' ' + chalk.cyan('SEO.REVERSE.md') + chalk.dim(' — reverse engineer output (platform generated)'));
156
- console.log(' ' + chalk.cyan('.seomd/') + chalk.dim(' — intelligence directory'));
154
+ console.log(' ' + chalk.cyan('.seo/REVERSE.md') + chalk.dim(' — reverse engineer output (platform generated)'));
155
+ console.log(' ' + chalk.cyan('.seo/') + chalk.dim(' — intelligence directory'));
157
156
  console.log('');
158
157
  console.log(chalk.dim('Next steps:'));
159
158
  console.log(' ' + chalk.white('npx seomd analyze') + chalk.dim(' — run your first citation analysis'));
@@ -168,15 +167,3 @@ export async function initCommand(options) {
168
167
  process.exit(1);
169
168
  }
170
169
  }
171
-
172
- async function updateGitignore(cwd) {
173
- const gitignorePath = path.join(cwd, '.gitignore');
174
- const entry = '\n# seomd intelligence directory\n.seomd/reports/\n';
175
-
176
- if (await fs.pathExists(gitignorePath)) {
177
- const content = await fs.readFile(gitignorePath, 'utf8');
178
- if (!content.includes('.seomd')) {
179
- await fs.appendFile(gitignorePath, entry);
180
- }
181
- }
182
- }
@@ -1,14 +1,26 @@
1
1
  import chalk from 'chalk';
2
+ import fs from 'fs-extra';
3
+ import path from 'path';
4
+ import YAML from 'yaml';
2
5
  import { parseSeoMd } from '../utils/parser.js';
3
6
 
4
7
  export async function statusCommand(options) {
5
8
  try {
6
- const { data } = await parseSeoMd(process.cwd());
9
+ const cwd = process.cwd();
10
+ const { data } = await parseSeoMd(cwd);
11
+
12
+ let statusData = {};
13
+ const statusPath = path.join(cwd, '.seo', 'STATUS.yml');
14
+
15
+ if (await fs.pathExists(statusPath)) {
16
+ const content = await fs.readFile(statusPath, 'utf8');
17
+ statusData = YAML.parse(content) || {};
18
+ }
7
19
 
8
- const aeoAnalysis = data.aeo?._analysis || {};
9
- const intentAnalysis = data.intent?._analysis || {};
20
+ const aeoAnalysis = statusData.aeo || {};
21
+ const intentAnalysis = statusData.intent || {};
10
22
  const pagesRequired = data.pages?.required || [];
11
- const pagesAnalysis = data.pages?._analysis?.pages || [];
23
+ const pagesAnalysis = statusData.pages?.pages || [];
12
24
 
13
25
  // Check if there is any analysis data
14
26
  const hasOverallData = aeoAnalysis.overall_citation_rate !== null && aeoAnalysis.overall_citation_rate !== undefined;
@@ -18,7 +30,7 @@ export async function statusCommand(options) {
18
30
  console.log(JSON.stringify({ status: "no_data", message: "No analysis data found" }, null, 2));
19
31
  } else {
20
32
  console.log('');
21
- console.log(chalk.yellow('⚠ No analysis data found in SEO.md.'));
33
+ console.log(chalk.yellow('⚠ No analysis data found in .seo/STATUS.yml.'));
22
34
  console.log('');
23
35
  console.log('To populate analysis data:');
24
36
  console.log(` 1. Get an API key at ${chalk.cyan('https://seomd.dev/connect')}`);
@@ -47,7 +59,7 @@ export async function statusCommand(options) {
47
59
  overall: {
48
60
  citation_rate: aeoAnalysis.overall_citation_rate,
49
61
  gap_score: aeoAnalysis.overall_gap_score,
50
- last_analyzed: aeoAnalysis.last_analyzed || null,
62
+ last_analyzed: statusData.last_analyzed || null,
51
63
  },
52
64
  intent: {},
53
65
  pages: []
@@ -85,7 +97,7 @@ export async function statusCommand(options) {
85
97
  // Output beautiful terminal dashboard
86
98
  console.log('');
87
99
  console.log(chalk.bold('SEO.md Status Dashboard') + chalk.dim(` — ${data.identity?.brand || 'Brand'} (${data.site?.domain || 'domain'})`));
88
- console.log(chalk.dim(`Last analyzed: ${aeoAnalysis.last_analyzed || 'N/A'}`));
100
+ console.log(chalk.dim(`Last analyzed: ${statusData.last_analyzed || 'N/A'}`));
89
101
  console.log(chalk.dim('─'.repeat(60)));
90
102
 
91
103
  // Overall block
@@ -83,14 +83,14 @@ export async function syncCommand(options) {
83
83
 
84
84
  spinner.text = 'Updating repository files...';
85
85
 
86
- // Writeback to SEO.md
86
+ // Writeback to .seo/STATUS.yml
87
87
  await writeAnalysisToSeoMd(doc, results, cwd);
88
88
 
89
- // Writeback to SEO.REVERSE.md
89
+ // Writeback to .seo/REVERSE.md
90
90
  const brandName = data.identity?.brand || 'My Brand';
91
91
  await writeReverseMd(cwd, results, domain, brandName);
92
92
 
93
- // Writeback to .seomd/pages/*.md
93
+ // Writeback to .seo/pages/*.md
94
94
  await writePageAnalysis(cwd, results);
95
95
 
96
96
  spinner.succeed(chalk.green('Sync completed successfully!'));
@@ -109,9 +109,9 @@ export async function syncCommand(options) {
109
109
  console.log(`Next Analysis Target : ${chalk.dim(aeo.next_analysis)}`);
110
110
  console.log('----------------------------');
111
111
  console.log('');
112
- console.log(chalk.green('✔ SEO.md updated.'));
113
- console.log(chalk.green('✔ SEO.REVERSE.md updated.'));
114
- console.log(chalk.green('✔ .seomd/pages/ playbooks synchronized.'));
112
+ console.log(chalk.green('✔ .seo/STATUS.yml updated.'));
113
+ console.log(chalk.green('✔ .seo/REVERSE.md updated.'));
114
+ console.log(chalk.green('✔ .seo/pages/ playbooks synchronized.'));
115
115
  console.log('');
116
116
 
117
117
  } catch (err) {
@@ -5,14 +5,12 @@ 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
- await fs.ensureDir(path.join(seomdDir, 'reports'));
13
- await fs.ensureDir(path.join(seomdDir, 'competitors'));
14
12
 
15
- // Create README inside .seomd/
13
+ // Create README inside .seo/
16
14
  await fs.writeFile(
17
15
  path.join(seomdDir, 'README.md'),
18
16
  generateSeomdReadme(brand, domain),
@@ -27,49 +25,55 @@ export async function createSeomdDir(cwd, answers) {
27
25
  'utf8'
28
26
  );
29
27
  }
30
-
31
- // Create initial empty report
32
- const date = new Date().toISOString().split('T')[0];
33
- await fs.writeFile(
34
- path.join(seomdDir, 'reports', `${date}.md`),
35
- generateInitialReport(brand, domain, date),
36
- 'utf8'
37
- );
38
28
  }
39
29
 
40
30
  function generateSeomdReadme(brand, domain) {
41
- return `# .seomd/
31
+ return `# .seo/
42
32
 
43
33
  AI search optimization workspace for **${brand}** (${domain})
44
34
  Generated by SEO.md CLI — https://seomd.dev
45
35
 
46
36
  ## What is this?
47
37
 
48
- \`.seomd/\` 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.
38
+ \`.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
39
 
50
40
  ## Directory Structure
51
41
 
52
42
  \`\`\`
53
- .seomd/
43
+ .seo/
54
44
  ├── pages/ # per-page reverse-engineer analysis
55
- ├── homepage.md
56
- │ ├── services.md
57
- │ └── ...
58
- ├── reports/ # dated snapshot reports (gitignored by default)
59
- │ └── 2026-06-10.md
60
- └── competitors/ # competitor citation profiles
61
- └── comp-1.md
45
+ ├── STATUS.yml # gap scores and citation metrics (platform generated)
46
+ └── REVERSE.md # competitor playbooks (platform generated)
62
47
  \`\`\`
63
48
 
64
49
  ## Git behavior
65
50
 
66
51
  | Path | Git | Why |
67
52
  |------|-----|-----|
68
- | \`.seomd/pages/\` | tracked | actionable playbooks belong in code review |
69
- | \`.seomd/competitors/\` | tracked | competitor intel is project state |
70
- | \`.seomd/reports/\` | ignored | dated snapshots are noise in git history |
53
+ | \`.seo/pages/\` | tracked | actionable playbooks belong in code review |
54
+ | \`.seo/STATUS.yml\` | tracked | gap scores represent current project state |
55
+ | \`.seo/REVERSE.md\` | tracked | competitor intel is project state |
56
+
57
+ ## How to configure SEO.md
58
+
59
+ \`SEO.md\` is your declarative configuration file. It tells the AI optimization platform exactly what you care about. Here is a guide on how to fill it out:
60
+
61
+ ### 1. Intent (Queries)
62
+ Add queries your buyers actually type into AI engines. Think about what someone asks ChatGPT or Perplexity when they are looking for a solution like yours.
63
+
64
+ ### 2. Pages
65
+ Declare the key pages on your site. For each page, set its priority and status (\`live | draft | planned\`). The platform will scan these and generate playbooks for each one.
66
+
67
+ ### 3. AI Engine Optimization (AEO) Rules
68
+ Set the strict guidelines you want your writers or AI to follow (e.g. \`answer_first_format: true\`).
69
+
70
+ ### 4. Platform Connection
71
+ To enable live writebacks, connect your platform:
72
+ 1. Connect at https://seomd.dev/connect
73
+ 2. Add \`SEOMD_API_KEY\` to your \`.env\`
74
+ 3. Run \`npx seomd analyze\`
71
75
 
72
- \`.seomd/reports/\` is added to \`.gitignore\` automatically by \`seomd init\`.
76
+ **Note: Never commit your API key to version control.**
73
77
 
74
78
  ## When to run what
75
79
 
@@ -107,22 +111,11 @@ Each \`pages/<id>.md\` contains:
107
111
  | File/dir | Owner | Editable? |
108
112
  |-----------|-------|----------|
109
113
  | \`SEO.md\` | you | yes |
110
- | \`SEO.REVERSE.md\` | platform | no |
111
- | \`.seomd/pages/*.md\` | platform | no |
112
- | \`.seomd/competitors/*.md\` | platform | no |
113
- | \`.seomd/reports/*.md\` | platform | no |
114
+ | \`.seo/STATUS.yml\` | platform | no |
115
+ | \`.seo/REVERSE.md\` | platform | no |
116
+ | \`.seo/pages/*.md\` | platform | no |
114
117
 
115
118
  Platform-generated files are overwritten on each \`analyze\` or \`sync\`.
116
-
117
- ## Platform connection
118
-
119
- To enable live writebacks:
120
-
121
- 1. Connect at https://seomd.dev/connect
122
- 2. Add \`SEOMD_API_KEY\` to your \`.env\`
123
- 3. Run \`npx seomd analyze\`
124
-
125
- _Note: Never commit API keys or \`.env\` files to version control._
126
119
  `;
127
120
  }
128
121
 
@@ -160,28 +153,3 @@ fastest_win:
160
153
  suggested_content_outline: []
161
154
  `;
162
155
  }
163
-
164
- function generateInitialReport(brand, domain, date) {
165
- return `# SEO.md Report — ${date}
166
-
167
- ## ${brand} (${domain})
168
-
169
- ### Initial report — run \`npx seomd analyze\` to populate
170
-
171
- date: ${date}
172
- brand: "${brand}"
173
- domain: ${domain}
174
- overall_citation_rate: null
175
- overall_gap_score: null
176
-
177
- intent_summary:
178
- informational: null
179
- comparison: null
180
- transactional: null
181
- reputational: null
182
- category: null
183
-
184
- top_opportunities: []
185
- completed_wins: []
186
- `;
187
- }
@@ -4,14 +4,6 @@
4
4
 
5
5
  ### spec v1.0 | <https://seomd.dev>
6
6
 
7
- #### generated: {{date}}
8
-
9
- ## FIELD OWNERSHIP
10
-
11
- ### no prefix = founder declares (you own this)
12
-
13
- ### _analysis: = platform writes back (do not edit manually)
14
-
15
7
  ## Site
16
8
 
17
9
  site:
@@ -36,8 +28,6 @@ identity:
36
28
 
37
29
  keywords:
38
30
 
39
- ### FOUNDER DECLARES
40
-
41
31
  primary: "{{primary_keyword}}"
42
32
  secondary: [] # add your secondary keywords
43
33
  negative: # terms that dilute your intent signal
@@ -52,30 +42,10 @@ keywords:
52
42
  long_tail: [] # add long-tail variations
53
43
  seasonal: null # add seasonal terms if applicable
54
44
 
55
- ### PLATFORM WRITES BACK
56
-
57
- _analysis:
58
- source: null
59
- primary_search_volume: null
60
- primary_intent_type: null
61
- primary_trend: null
62
- recommended_secondary: []
63
- negative_additions_suggested: []
64
- last_analyzed: null
65
- next_analysis: null
66
-
67
45
  ## Intent
68
46
 
69
47
  intent:
70
48
 
71
- ### FOUNDER DECLARES
72
-
73
- #### Add queries your buyers actually type into AI engines
74
-
75
- #### Tip: think about what someone asks ChatGPT or Perplexity
76
-
77
- #### when they are looking for a solution like yours
78
-
79
49
  informational:
80
50
  priority: critical
81
51
  queries:
@@ -108,47 +78,11 @@ intent:
108
78
  - "best {{primary_keyword}}"
109
79
  - "top {{primary_keyword}} 2026"
110
80
 
111
- ### PLATFORM WRITES BACK
112
-
113
- _analysis:
114
- source: null
115
- last_analyzed: null
116
- next_analysis: null
117
- informational:
118
- citation_rate: null
119
- top_cited_competitor: null
120
- gap_score: null
121
- trend: null
122
- comparison:
123
- citation_rate: null
124
- top_cited_competitor: null
125
- gap_score: null
126
- trend: null
127
- transactional:
128
- citation_rate: null
129
- top_cited_competitor: null
130
- gap_score: null
131
- trend: null
132
- reputational:
133
- citation_rate: null
134
- top_cited_competitor: null
135
- gap_score: null
136
- trend: null
137
- category:
138
- citation_rate: null
139
- top_cited_competitor: null
140
- gap_score: null
141
- trend: null
142
-
143
81
  ## Pages
144
82
 
145
83
  pages:
146
84
  site_type: blog
147
85
 
148
- ### FOUNDER DECLARES
149
-
150
- #### status: live | draft | planned
151
-
152
86
  required:
153
87
  - id: homepage
154
88
  url: /
@@ -186,21 +120,10 @@ pages:
186
120
  status: planned
187
121
  priority: 6
188
122
 
189
- ### PLATFORM WRITES BACK
190
-
191
- _analysis:
192
- source: null
193
- last_analyzed: null
194
- pages: []
195
- missing_pages: []
196
- build_order_recommendation: []
197
-
198
123
  ## Copy
199
124
 
200
125
  copy:
201
126
 
202
- ### FOUNDER DECLARES
203
-
204
127
  h1_contains_primary_keyword: true
205
128
  meta_description_length: 150-160
206
129
  meta_description_includes_cta: true
@@ -215,8 +138,6 @@ copy:
215
138
 
216
139
  structure:
217
140
 
218
- ### FOUNDER DECLARES
219
-
220
141
  answer_first: true # direct answer in first 50 words
221
142
  faq_section_required: true # on all key pages
222
143
  faq_minimum_questions: 6
@@ -229,8 +150,6 @@ structure:
229
150
 
230
151
  authority:
231
152
 
232
- ### FOUNDER DECLARES
233
-
234
153
  cite_sources: true
235
154
  expert_quotes: false # set true when you have quotes
236
155
  eeat_signals:
@@ -243,8 +162,6 @@ authority:
243
162
 
244
163
  schema:
245
164
 
246
- ### FOUNDER DECLARES
247
-
248
165
  types:
249
166
  - Article
250
167
  - Person
@@ -258,8 +175,6 @@ schema:
258
175
 
259
176
  crawl:
260
177
 
261
- ### FOUNDER DECLARES
262
-
263
178
  sitemap: /sitemap.xml
264
179
  robots_txt: /robots.txt
265
180
  allow_ai_bots: true
@@ -282,8 +197,6 @@ crawl:
282
197
 
283
198
  performance:
284
199
 
285
- ### FOUNDER DECLARES
286
-
287
200
  lcp: 2.5s
288
201
  cls: 0.1
289
202
  fid: 100ms
@@ -294,10 +207,6 @@ performance:
294
207
 
295
208
  aeo:
296
209
 
297
- ### FOUNDER DECLARES
298
-
299
- ### AI Engine Optimization rules
300
-
301
210
  answer_first_format: true
302
211
  faq_on_all_key_pages: true
303
212
  structured_data_priority: high
@@ -305,27 +214,10 @@ aeo:
305
214
  competitors_to_monitor:
306
215
  {{competitors_to_monitor}}
307
216
 
308
- ### PLATFORM WRITES BACK
309
-
310
- _analysis:
311
- source: null
312
- overall_citation_rate: null
313
- overall_gap_score: null
314
- engines_tracked:
315
- - chatgpt
316
- - perplexity
317
- - claude
318
- - gemini
319
- - grok
320
- last_analyzed: null
321
- next_analysis: null
322
-
323
217
  ## Monitoring
324
218
 
325
219
  monitoring:
326
220
 
327
- ### FOUNDER DECLARES
328
-
329
221
  sync_schedule: monthly # monthly | weekly | on_demand
330
222
  auto_commit: false # platform commits directly to repo
331
223
  pr_mode: true # open PR instead of direct commit
@@ -335,14 +227,7 @@ monitoring:
335
227
 
336
228
  ## Platform Connection
337
229
 
338
- ### Connect at <https://seomd.dev/connect>
339
-
340
- ### Add SEOMD_API_KEY to your .env file
341
-
342
- ### Never commit your API key to version control
343
-
344
230
  platform:
345
231
  provider: null # foxcite | manual | ahrefs | semrush
346
232
  project_id: null
347
233
 
348
- ### api_key: loaded from SEOMD_API_KEY environment variable