seomd-cli 1.3.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/package.json +1 -1
- package/src/commands/analyze.js +5 -5
- package/src/commands/init.js +9 -22
- package/src/commands/status.js +19 -7
- package/src/commands/sync.js +4 -4
- package/src/generators/directory.js +26 -58
- package/src/templates/blog/SEO.md +0 -115
- package/src/templates/ecommerce/SEO.md +0 -115
- package/src/templates/local/SEO.md +0 -115
- package/src/templates/marketplace/SEO.md +0 -115
- package/src/templates/saas/SEO.md +0 -115
- package/src/utils/writeback.js +42 -50
package/package.json
CHANGED
package/src/commands/analyze.js
CHANGED
|
@@ -105,7 +105,7 @@ export async function analyzeCommand(options) {
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
// Extract engines
|
|
108
|
-
let engines =
|
|
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,10 +140,10 @@ export async function analyzeCommand(options) {
|
|
|
140
140
|
|
|
141
141
|
spinner.text = 'Writing analysis blocks back to repository files...';
|
|
142
142
|
|
|
143
|
-
// Writeback to
|
|
143
|
+
// Writeback to .seo/STATUS.yml
|
|
144
144
|
await writeAnalysisToSeoMd(doc, results, cwd);
|
|
145
145
|
|
|
146
|
-
// Writeback to
|
|
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
|
|
|
@@ -166,8 +166,8 @@ 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('✔
|
|
170
|
-
console.log(chalk.green('✔
|
|
169
|
+
console.log(chalk.green('✔ .seo/STATUS.yml updated.'));
|
|
170
|
+
console.log(chalk.green('✔ .seo/REVERSE.md updated.'));
|
|
171
171
|
console.log(chalk.green('✔ .seo/pages/ playbooks generated.'));
|
|
172
172
|
console.log('');
|
|
173
173
|
|
package/src/commands/init.js
CHANGED
|
@@ -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.
|
|
139
|
-
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'));
|
|
142
|
-
|
|
143
|
-
// 3. Create .seo/ directory structure
|
|
138
|
+
// 2. Create .seo/ directory structure
|
|
144
139
|
await createSeomdDir(workingDir, answers);
|
|
145
140
|
spinner.succeed(chalk.green('.seo/ directory created'));
|
|
146
141
|
|
|
147
|
-
//
|
|
148
|
-
|
|
142
|
+
// 3. Generate .seo/REVERSE.md
|
|
143
|
+
const reverseContent = generateReverseMd(answers);
|
|
144
|
+
await fs.writeFile(path.join(workingDir, '.seo', 'REVERSE.md'), reverseContent, 'utf8');
|
|
145
|
+
spinner.succeed(chalk.green('.seo/REVERSE.md initialized'));
|
|
146
|
+
|
|
147
|
+
|
|
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('
|
|
156
|
-
console.log(' ' + chalk.cyan('.seo/') + chalk.dim('
|
|
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.seo/reports/\n';
|
|
175
|
-
|
|
176
|
-
if (await fs.pathExists(gitignorePath)) {
|
|
177
|
-
const content = await fs.readFile(gitignorePath, 'utf8');
|
|
178
|
-
if (!content.includes('.seo')) {
|
|
179
|
-
await fs.appendFile(gitignorePath, entry);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
package/src/commands/status.js
CHANGED
|
@@ -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
|
|
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 =
|
|
9
|
-
const intentAnalysis =
|
|
20
|
+
const aeoAnalysis = statusData.aeo || {};
|
|
21
|
+
const intentAnalysis = statusData.intent || {};
|
|
10
22
|
const pagesRequired = data.pages?.required || [];
|
|
11
|
-
const pagesAnalysis =
|
|
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
|
|
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:
|
|
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: ${
|
|
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
|
package/src/commands/sync.js
CHANGED
|
@@ -83,10 +83,10 @@ export async function syncCommand(options) {
|
|
|
83
83
|
|
|
84
84
|
spinner.text = 'Updating repository files...';
|
|
85
85
|
|
|
86
|
-
// Writeback to
|
|
86
|
+
// Writeback to .seo/STATUS.yml
|
|
87
87
|
await writeAnalysisToSeoMd(doc, results, cwd);
|
|
88
88
|
|
|
89
|
-
// Writeback to
|
|
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
|
|
|
@@ -109,8 +109,8 @@ 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('✔
|
|
113
|
-
console.log(chalk.green('✔
|
|
112
|
+
console.log(chalk.green('✔ .seo/STATUS.yml updated.'));
|
|
113
|
+
console.log(chalk.green('✔ .seo/REVERSE.md updated.'));
|
|
114
114
|
console.log(chalk.green('✔ .seo/pages/ playbooks synchronized.'));
|
|
115
115
|
console.log('');
|
|
116
116
|
|
|
@@ -9,8 +9,6 @@ export async function createSeomdDir(cwd, answers) {
|
|
|
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
13
|
// Create README inside .seo/
|
|
16
14
|
await fs.writeFile(
|
|
@@ -27,14 +25,6 @@ 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) {
|
|
@@ -52,13 +42,8 @@ Generated by SEO.md CLI — https://seomd.dev
|
|
|
52
42
|
\`\`\`
|
|
53
43
|
.seo/
|
|
54
44
|
├── pages/ # per-page reverse-engineer analysis
|
|
55
|
-
|
|
56
|
-
|
|
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
|
|
@@ -66,10 +51,29 @@ Generated by SEO.md CLI — https://seomd.dev
|
|
|
66
51
|
| Path | Git | Why |
|
|
67
52
|
|------|-----|-----|
|
|
68
53
|
| \`.seo/pages/\` | tracked | actionable playbooks belong in code review |
|
|
69
|
-
| \`.seo/
|
|
70
|
-
| \`.seo/
|
|
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
|
-
|
|
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
|
-
|
|
|
114
|
+
| \`.seo/STATUS.yml\` | platform | no |
|
|
115
|
+
| \`.seo/REVERSE.md\` | platform | no |
|
|
111
116
|
| \`.seo/pages/*.md\` | platform | no |
|
|
112
|
-
| \`.seo/competitors/*.md\` | platform | no |
|
|
113
|
-
| \`.seo/reports/*.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
|