@rankcli/agent-runtime 0.0.14 → 0.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +77 -393
- package/dist/index.d.ts +77 -393
- package/dist/index.js +417 -6443
- package/dist/index.mjs +296 -6313
- package/package.json +6 -1
- package/scripts/build-deno.ts +0 -134
- package/src/analyzers/core-web-vitals-analyzer.test.ts +0 -236
- package/src/analyzers/core-web-vitals-analyzer.ts +0 -557
- package/src/analyzers/geo-analyzer.test.ts +0 -310
- package/src/analyzers/geo-analyzer.ts +0 -814
- package/src/analyzers/image-optimization-analyzer.test.ts +0 -145
- package/src/analyzers/image-optimization-analyzer.ts +0 -348
- package/src/analyzers/index.ts +0 -233
- package/src/analyzers/internal-linking-analyzer.test.ts +0 -141
- package/src/analyzers/internal-linking-analyzer.ts +0 -419
- package/src/analyzers/mobile-seo-analyzer.test.ts +0 -140
- package/src/analyzers/mobile-seo-analyzer.ts +0 -455
- package/src/analyzers/security-headers-analyzer.test.ts +0 -115
- package/src/analyzers/security-headers-analyzer.ts +0 -318
- package/src/analyzers/structured-data-analyzer.test.ts +0 -210
- package/src/analyzers/structured-data-analyzer.ts +0 -590
- package/src/audit/ai/analyzer.ts +0 -347
- package/src/audit/ai/index.ts +0 -29
- package/src/audit/ai/prompts/content-analysis.ts +0 -271
- package/src/audit/ai/types.ts +0 -179
- package/src/audit/checks/additional-checks.ts +0 -439
- package/src/audit/checks/ai-citation-worthiness.ts +0 -399
- package/src/audit/checks/ai-content-structure.ts +0 -325
- package/src/audit/checks/ai-readiness.ts +0 -339
- package/src/audit/checks/anchor-text.ts +0 -179
- package/src/audit/checks/answer-conciseness.ts +0 -322
- package/src/audit/checks/asset-minification.ts +0 -270
- package/src/audit/checks/bing-optimization.ts +0 -206
- package/src/audit/checks/brand-mention-optimization.ts +0 -349
- package/src/audit/checks/caching-headers.ts +0 -305
- package/src/audit/checks/canonical-advanced.ts +0 -150
- package/src/audit/checks/canonical-domain.ts +0 -196
- package/src/audit/checks/citation-quality.ts +0 -358
- package/src/audit/checks/client-rendering.ts +0 -542
- package/src/audit/checks/color-contrast.ts +0 -342
- package/src/audit/checks/content-freshness.ts +0 -170
- package/src/audit/checks/content-science.ts +0 -589
- package/src/audit/checks/conversion-elements.ts +0 -526
- package/src/audit/checks/crawlability.ts +0 -220
- package/src/audit/checks/directory-listing.ts +0 -172
- package/src/audit/checks/dom-analysis.ts +0 -191
- package/src/audit/checks/dom-size.ts +0 -246
- package/src/audit/checks/duplicate-content.ts +0 -194
- package/src/audit/checks/eeat-signals.ts +0 -990
- package/src/audit/checks/entity-seo.ts +0 -396
- package/src/audit/checks/featured-snippet.ts +0 -473
- package/src/audit/checks/freshness-signals.ts +0 -443
- package/src/audit/checks/funnel-intent.ts +0 -463
- package/src/audit/checks/hreflang.ts +0 -174
- package/src/audit/checks/html-compliance.ts +0 -302
- package/src/audit/checks/image-dimensions.ts +0 -167
- package/src/audit/checks/images.ts +0 -160
- package/src/audit/checks/indexnow.ts +0 -275
- package/src/audit/checks/interactive-tools.ts +0 -475
- package/src/audit/checks/internal-link-graph.ts +0 -436
- package/src/audit/checks/keyword-analysis.ts +0 -239
- package/src/audit/checks/keyword-cannibalization.ts +0 -385
- package/src/audit/checks/keyword-placement.ts +0 -471
- package/src/audit/checks/links.ts +0 -273
- package/src/audit/checks/llms-txt.ts +0 -224
- package/src/audit/checks/local-seo.ts +0 -296
- package/src/audit/checks/mobile.ts +0 -167
- package/src/audit/checks/modern-images.ts +0 -226
- package/src/audit/checks/navboost-signals.ts +0 -395
- package/src/audit/checks/on-page.ts +0 -209
- package/src/audit/checks/page-resources.ts +0 -285
- package/src/audit/checks/pagination.ts +0 -180
- package/src/audit/checks/performance.ts +0 -153
- package/src/audit/checks/platform-presence.ts +0 -580
- package/src/audit/checks/redirect-analysis.ts +0 -153
- package/src/audit/checks/redirect-chain.ts +0 -389
- package/src/audit/checks/resource-hints.ts +0 -420
- package/src/audit/checks/responsive-css.ts +0 -247
- package/src/audit/checks/responsive-images.ts +0 -396
- package/src/audit/checks/review-ecosystem.ts +0 -415
- package/src/audit/checks/robots-validation.ts +0 -373
- package/src/audit/checks/security-headers.ts +0 -188
- package/src/audit/checks/security.ts +0 -144
- package/src/audit/checks/serp-preview.ts +0 -251
- package/src/audit/checks/site-maturity.ts +0 -453
- package/src/audit/checks/social-meta.test.ts +0 -275
- package/src/audit/checks/social-meta.ts +0 -134
- package/src/audit/checks/soft-404.ts +0 -151
- package/src/audit/checks/structured-data.ts +0 -238
- package/src/audit/checks/tech-detection.ts +0 -496
- package/src/audit/checks/topical-clusters.ts +0 -435
- package/src/audit/checks/tracker-bloat.ts +0 -462
- package/src/audit/checks/tracking-verification.test.ts +0 -371
- package/src/audit/checks/tracking-verification.ts +0 -636
- package/src/audit/checks/url-safety.ts +0 -682
- package/src/audit/deno-entry.ts +0 -66
- package/src/audit/discovery/index.ts +0 -15
- package/src/audit/discovery/link-crawler.ts +0 -233
- package/src/audit/discovery/repo-routes.ts +0 -347
- package/src/audit/engine.ts +0 -627
- package/src/audit/fixes/index.ts +0 -209
- package/src/audit/fixes/social-meta-fixes.test.ts +0 -329
- package/src/audit/fixes/social-meta-fixes.ts +0 -463
- package/src/audit/index.ts +0 -74
- package/src/audit/runner.test.ts +0 -299
- package/src/audit/runner.ts +0 -130
- package/src/audit/types.ts +0 -1963
- package/src/content/featured-snippet.ts +0 -367
- package/src/content/generator.test.ts +0 -534
- package/src/content/generator.ts +0 -501
- package/src/content/headline.ts +0 -317
- package/src/content/index.ts +0 -62
- package/src/content/intent.ts +0 -258
- package/src/content/keyword-density.ts +0 -349
- package/src/content/readability.ts +0 -262
- package/src/executor.ts +0 -336
- package/src/fixer/framework-fixes.test.ts +0 -489
- package/src/fixer/framework-fixes.ts +0 -5287
- package/src/fixer/index.ts +0 -6
- package/src/fixer/schemas.ts +0 -971
- package/src/fixer.ts +0 -642
- package/src/frameworks/detector.test.ts +0 -248
- package/src/frameworks/detector.ts +0 -899
- package/src/frameworks/index.ts +0 -68
- package/src/frameworks/recipes/angular.yaml +0 -171
- package/src/frameworks/recipes/astro.yaml +0 -206
- package/src/frameworks/recipes/django.yaml +0 -180
- package/src/frameworks/recipes/laravel.yaml +0 -137
- package/src/frameworks/recipes/nextjs.yaml +0 -268
- package/src/frameworks/recipes/nuxt.yaml +0 -175
- package/src/frameworks/recipes/rails.yaml +0 -188
- package/src/frameworks/recipes/react.yaml +0 -202
- package/src/frameworks/recipes/sveltekit.yaml +0 -154
- package/src/frameworks/recipes/vue.yaml +0 -137
- package/src/frameworks/recipes/wordpress.yaml +0 -209
- package/src/frameworks/suggestion-engine.ts +0 -357
- package/src/geo/geo-content.test.ts +0 -305
- package/src/geo/geo-content.ts +0 -266
- package/src/geo/geo-history.test.ts +0 -473
- package/src/geo/geo-history.ts +0 -433
- package/src/geo/geo-tracker.test.ts +0 -359
- package/src/geo/geo-tracker.ts +0 -411
- package/src/geo/index.ts +0 -11
- package/src/geo/llm-citation-checker.ts +0 -188
- package/src/git/commit-helper.test.ts +0 -261
- package/src/git/commit-helper.ts +0 -329
- package/src/git/index.ts +0 -12
- package/src/git/pr-helper.test.ts +0 -284
- package/src/git/pr-helper.ts +0 -307
- package/src/index.ts +0 -76
- package/src/keywords/ai-keyword-engine.ts +0 -1062
- package/src/keywords/ai-summarizer.ts +0 -387
- package/src/keywords/ci-mode.ts +0 -555
- package/src/keywords/engine.ts +0 -359
- package/src/keywords/index.ts +0 -151
- package/src/keywords/llm-judge.ts +0 -357
- package/src/keywords/nlp-analysis.ts +0 -706
- package/src/keywords/prioritizer.ts +0 -295
- package/src/keywords/site-crawler.ts +0 -342
- package/src/keywords/sources/autocomplete.ts +0 -139
- package/src/keywords/sources/competitive-search.ts +0 -450
- package/src/keywords/sources/competitor-analysis.ts +0 -374
- package/src/keywords/sources/dataforseo.ts +0 -206
- package/src/keywords/sources/free-sources.ts +0 -294
- package/src/keywords/sources/gsc.ts +0 -123
- package/src/keywords/topic-grouping.ts +0 -327
- package/src/keywords/types.ts +0 -144
- package/src/keywords/wizard.ts +0 -457
- package/src/loader.ts +0 -40
- package/src/ranking/index.ts +0 -5
- package/src/ranking/serp-client.ts +0 -348
- package/src/ranking/tracker.ts +0 -380
- package/src/ranking/types.ts +0 -123
- package/src/reports/index.ts +0 -7
- package/src/reports/report-generator.test.ts +0 -293
- package/src/reports/report-generator.ts +0 -713
- package/src/scheduler/alerts.test.ts +0 -458
- package/src/scheduler/alerts.ts +0 -328
- package/src/scheduler/index.ts +0 -8
- package/src/scheduler/scheduled-audit.test.ts +0 -377
- package/src/scheduler/scheduled-audit.ts +0 -149
- package/src/test/integration-test.ts +0 -325
- package/src/tools/analyzer.ts +0 -373
- package/src/tools/crawl.ts +0 -293
- package/src/tools/files.ts +0 -301
- package/src/tools/h1-fixer.ts +0 -249
- package/src/tools/index.ts +0 -67
- package/src/tracking/github-action.ts +0 -326
- package/src/tracking/google-analytics.ts +0 -265
- package/src/tracking/index.ts +0 -45
- package/src/tracking/report-generator.ts +0 -386
- package/src/tracking/search-console.ts +0 -335
- package/src/types.ts +0 -148
- package/src/utils/http.ts +0 -302
- package/src/wasm-adapter.ts +0 -297
- package/src/wasm-entry.ts +0 -14
- package/tsconfig.json +0 -17
- package/tsup.wasm.config.ts +0 -26
- package/vitest.config.ts +0 -15
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
import {
|
|
3
|
-
formatConventionalCommit,
|
|
4
|
-
formatSEOCommitMessage,
|
|
5
|
-
isGitRepo,
|
|
6
|
-
detectGitHubPages,
|
|
7
|
-
generateCommitSummary,
|
|
8
|
-
COMMIT_TYPES,
|
|
9
|
-
SEO_SCOPES,
|
|
10
|
-
type ConventionalCommit,
|
|
11
|
-
type SEOFixCommit,
|
|
12
|
-
type CommitConfig,
|
|
13
|
-
} from './commit-helper.js';
|
|
14
|
-
|
|
15
|
-
describe('commit-helper', () => {
|
|
16
|
-
describe('formatConventionalCommit', () => {
|
|
17
|
-
it('formats a basic commit message', () => {
|
|
18
|
-
const commit: ConventionalCommit = {
|
|
19
|
-
type: 'fix',
|
|
20
|
-
description: 'resolve meta tag issue',
|
|
21
|
-
};
|
|
22
|
-
expect(formatConventionalCommit(commit)).toBe('fix: resolve meta tag issue');
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('includes scope when provided', () => {
|
|
26
|
-
const commit: ConventionalCommit = {
|
|
27
|
-
type: 'fix',
|
|
28
|
-
scope: 'seo',
|
|
29
|
-
description: 'add missing title tag',
|
|
30
|
-
};
|
|
31
|
-
expect(formatConventionalCommit(commit)).toBe('fix(seo): add missing title tag');
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('adds breaking change indicator', () => {
|
|
35
|
-
const commit: ConventionalCommit = {
|
|
36
|
-
type: 'feat',
|
|
37
|
-
scope: 'schema',
|
|
38
|
-
description: 'change JSON-LD format',
|
|
39
|
-
breaking: true,
|
|
40
|
-
};
|
|
41
|
-
expect(formatConventionalCommit(commit)).toBe('feat(schema)!: change JSON-LD format');
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('includes body when provided', () => {
|
|
45
|
-
const commit: ConventionalCommit = {
|
|
46
|
-
type: 'fix',
|
|
47
|
-
scope: 'meta',
|
|
48
|
-
description: 'add meta description',
|
|
49
|
-
body: 'Added meta descriptions to all pages.\nThis improves SEO score.',
|
|
50
|
-
};
|
|
51
|
-
const result = formatConventionalCommit(commit);
|
|
52
|
-
expect(result).toContain('fix(meta): add meta description');
|
|
53
|
-
expect(result).toContain('Added meta descriptions to all pages.');
|
|
54
|
-
expect(result).toContain('This improves SEO score.');
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('includes issue codes when provided', () => {
|
|
58
|
-
const commit: ConventionalCommit = {
|
|
59
|
-
type: 'fix',
|
|
60
|
-
scope: 'seo',
|
|
61
|
-
description: 'fix crawlability issues',
|
|
62
|
-
issues: ['ROBOTS_TXT_MISSING', 'SITEMAP_MISSING'],
|
|
63
|
-
};
|
|
64
|
-
const result = formatConventionalCommit(commit);
|
|
65
|
-
expect(result).toContain('Issue codes: ROBOTS_TXT_MISSING, SITEMAP_MISSING');
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
describe('formatSEOCommitMessage', () => {
|
|
70
|
-
it('formats SEO fix with co-author', () => {
|
|
71
|
-
const fix: SEOFixCommit = {
|
|
72
|
-
category: 'on-page',
|
|
73
|
-
issues: ['TITLE_MISSING', 'META_DESC_MISSING'],
|
|
74
|
-
filesChanged: ['index.html'],
|
|
75
|
-
description: 'add on page improvements',
|
|
76
|
-
};
|
|
77
|
-
const config: CommitConfig = {
|
|
78
|
-
systemName: 'SEO Autopilot',
|
|
79
|
-
systemEmail: 'bot@seo-autopilot.dev',
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
const result = formatSEOCommitMessage(fix, config);
|
|
83
|
-
|
|
84
|
-
expect(result).toContain('fix(meta): add on page improvements');
|
|
85
|
-
expect(result).toContain('Files changed:');
|
|
86
|
-
expect(result).toContain('- index.html');
|
|
87
|
-
expect(result).toContain('Issue codes: TITLE_MISSING, META_DESC_MISSING');
|
|
88
|
-
expect(result).toContain('Co-Authored-By: SEO Autopilot <bot@seo-autopilot.dev>');
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it('maps categories to correct commit types', () => {
|
|
92
|
-
const testCases = [
|
|
93
|
-
{ category: 'crawlability', expectedType: 'fix', expectedScope: 'seo' },
|
|
94
|
-
{ category: 'on-page', expectedType: 'fix', expectedScope: 'meta' },
|
|
95
|
-
{ category: 'structured-data', expectedType: 'feat', expectedScope: 'schema' },
|
|
96
|
-
{ category: 'performance', expectedType: 'perf', expectedScope: 'web' },
|
|
97
|
-
{ category: 'social-meta', expectedType: 'feat', expectedScope: 'og' },
|
|
98
|
-
{ category: 'images', expectedType: 'fix', expectedScope: 'images' },
|
|
99
|
-
{ category: 'links', expectedType: 'fix', expectedScope: 'links' },
|
|
100
|
-
{ category: 'security', expectedType: 'fix', expectedScope: 'security' },
|
|
101
|
-
];
|
|
102
|
-
|
|
103
|
-
testCases.forEach(({ category, expectedType, expectedScope }) => {
|
|
104
|
-
const fix: SEOFixCommit = {
|
|
105
|
-
category,
|
|
106
|
-
issues: ['TEST_ISSUE'],
|
|
107
|
-
filesChanged: ['test.html'],
|
|
108
|
-
description: 'test fix',
|
|
109
|
-
};
|
|
110
|
-
const result = formatSEOCommitMessage(fix);
|
|
111
|
-
expect(result).toContain(`${expectedType}(${expectedScope}): test fix`);
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it('uses default scope for unknown categories', () => {
|
|
116
|
-
const fix: SEOFixCommit = {
|
|
117
|
-
category: 'unknown-category',
|
|
118
|
-
issues: ['TEST_ISSUE'],
|
|
119
|
-
filesChanged: ['test.html'],
|
|
120
|
-
description: 'test fix',
|
|
121
|
-
};
|
|
122
|
-
const result = formatSEOCommitMessage(fix);
|
|
123
|
-
expect(result).toContain('fix(seo): test fix');
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it('handles multiple files', () => {
|
|
127
|
-
const fix: SEOFixCommit = {
|
|
128
|
-
category: 'on-page',
|
|
129
|
-
issues: ['TEST'],
|
|
130
|
-
filesChanged: ['index.html', 'about.html', 'contact.html'],
|
|
131
|
-
description: 'fix meta tags',
|
|
132
|
-
};
|
|
133
|
-
const result = formatSEOCommitMessage(fix);
|
|
134
|
-
expect(result).toContain('- index.html');
|
|
135
|
-
expect(result).toContain('- about.html');
|
|
136
|
-
expect(result).toContain('- contact.html');
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it('uses custom system name and email', () => {
|
|
140
|
-
const fix: SEOFixCommit = {
|
|
141
|
-
category: 'on-page',
|
|
142
|
-
issues: ['TEST'],
|
|
143
|
-
filesChanged: ['index.html'],
|
|
144
|
-
description: 'test',
|
|
145
|
-
};
|
|
146
|
-
const config: CommitConfig = {
|
|
147
|
-
systemName: 'Custom Bot',
|
|
148
|
-
systemEmail: 'custom@example.com',
|
|
149
|
-
};
|
|
150
|
-
const result = formatSEOCommitMessage(fix, config);
|
|
151
|
-
expect(result).toContain('Co-Authored-By: Custom Bot <custom@example.com>');
|
|
152
|
-
});
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
describe('generateCommitSummary', () => {
|
|
156
|
-
it('generates summary for successful commits', () => {
|
|
157
|
-
const commits = [
|
|
158
|
-
{
|
|
159
|
-
fix: {
|
|
160
|
-
category: 'on-page',
|
|
161
|
-
issues: ['TITLE_MISSING'],
|
|
162
|
-
filesChanged: ['index.html'],
|
|
163
|
-
description: 'add title tags',
|
|
164
|
-
},
|
|
165
|
-
result: { success: true, hash: 'abc1234' },
|
|
166
|
-
},
|
|
167
|
-
{
|
|
168
|
-
fix: {
|
|
169
|
-
category: 'social-meta',
|
|
170
|
-
issues: ['OG_TITLE_MISSING'],
|
|
171
|
-
filesChanged: ['index.html'],
|
|
172
|
-
description: 'add og tags',
|
|
173
|
-
},
|
|
174
|
-
result: { success: true, hash: 'def5678' },
|
|
175
|
-
},
|
|
176
|
-
];
|
|
177
|
-
|
|
178
|
-
const summary = generateCommitSummary(commits);
|
|
179
|
-
|
|
180
|
-
expect(summary).toContain('## SEO Fix Commits Summary');
|
|
181
|
-
expect(summary).toContain('2 commits created');
|
|
182
|
-
expect(summary).toContain('abc1234');
|
|
183
|
-
expect(summary).toContain('def5678');
|
|
184
|
-
expect(summary).toContain('add title tags');
|
|
185
|
-
expect(summary).toContain('add og tags');
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
it('includes failed commits in summary', () => {
|
|
189
|
-
const commits = [
|
|
190
|
-
{
|
|
191
|
-
fix: {
|
|
192
|
-
category: 'on-page',
|
|
193
|
-
issues: ['TITLE_MISSING'],
|
|
194
|
-
filesChanged: ['index.html'],
|
|
195
|
-
description: 'add title tags',
|
|
196
|
-
},
|
|
197
|
-
result: { success: true, hash: 'abc1234' },
|
|
198
|
-
},
|
|
199
|
-
{
|
|
200
|
-
fix: {
|
|
201
|
-
category: 'crawlability',
|
|
202
|
-
issues: ['ROBOTS_TXT_MISSING'],
|
|
203
|
-
filesChanged: ['robots.txt'],
|
|
204
|
-
description: 'add robots.txt',
|
|
205
|
-
},
|
|
206
|
-
result: { success: false, error: 'Permission denied' },
|
|
207
|
-
},
|
|
208
|
-
];
|
|
209
|
-
|
|
210
|
-
const summary = generateCommitSummary(commits);
|
|
211
|
-
|
|
212
|
-
expect(summary).toContain('1 commits created');
|
|
213
|
-
expect(summary).toContain('1 commits failed');
|
|
214
|
-
expect(summary).toContain('### Failed Commits');
|
|
215
|
-
expect(summary).toContain('Permission denied');
|
|
216
|
-
});
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
describe('COMMIT_TYPES and SEO_SCOPES', () => {
|
|
220
|
-
it('has all expected commit types', () => {
|
|
221
|
-
const types = COMMIT_TYPES.map((t) => t.type);
|
|
222
|
-
expect(types).toContain('fix');
|
|
223
|
-
expect(types).toContain('feat');
|
|
224
|
-
expect(types).toContain('docs');
|
|
225
|
-
expect(types).toContain('perf');
|
|
226
|
-
expect(types).toContain('chore');
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
it('has all expected SEO scopes', () => {
|
|
230
|
-
expect(SEO_SCOPES).toContain('seo');
|
|
231
|
-
expect(SEO_SCOPES).toContain('meta');
|
|
232
|
-
expect(SEO_SCOPES).toContain('og');
|
|
233
|
-
expect(SEO_SCOPES).toContain('schema');
|
|
234
|
-
expect(SEO_SCOPES).toContain('images');
|
|
235
|
-
expect(SEO_SCOPES).toContain('links');
|
|
236
|
-
expect(SEO_SCOPES).toContain('security');
|
|
237
|
-
});
|
|
238
|
-
});
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
describe('detectGitHubPages', () => {
|
|
242
|
-
// These tests would need filesystem mocking
|
|
243
|
-
// For now, we test the function exists and returns expected shape
|
|
244
|
-
it('returns expected structure', () => {
|
|
245
|
-
// Test with a non-existent path returns false
|
|
246
|
-
const result = detectGitHubPages('/non/existent/path');
|
|
247
|
-
expect(result).toHaveProperty('isGitHubPages');
|
|
248
|
-
expect(typeof result.isGitHubPages).toBe('boolean');
|
|
249
|
-
});
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
describe('isGitRepo', () => {
|
|
253
|
-
it('returns false for non-git directory', () => {
|
|
254
|
-
expect(isGitRepo('/tmp')).toBe(false);
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
it('returns boolean', () => {
|
|
258
|
-
const result = isGitRepo();
|
|
259
|
-
expect(typeof result).toBe('boolean');
|
|
260
|
-
});
|
|
261
|
-
});
|
package/src/git/commit-helper.ts
DELETED
|
@@ -1,329 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Git Commit Helper
|
|
3
|
-
*
|
|
4
|
-
* Creates conventional commits for SEO fixes with proper co-authoring.
|
|
5
|
-
* Supports GitHub Pages and various static site generators.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { execSync, exec } from 'child_process';
|
|
9
|
-
import { promisify } from 'util';
|
|
10
|
-
|
|
11
|
-
const execAsync = promisify(exec);
|
|
12
|
-
|
|
13
|
-
// System name is configurable (TBD - SEO Autopilot is temporary)
|
|
14
|
-
const SYSTEM_NAME = process.env.SEO_SYSTEM_NAME || 'SEO Autopilot';
|
|
15
|
-
const SYSTEM_EMAIL = process.env.SEO_SYSTEM_EMAIL || 'bot@seo-autopilot.dev';
|
|
16
|
-
|
|
17
|
-
export interface CommitConfig {
|
|
18
|
-
systemName?: string;
|
|
19
|
-
systemEmail?: string;
|
|
20
|
-
dryRun?: boolean;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface ConventionalCommit {
|
|
24
|
-
type: 'fix' | 'feat' | 'docs' | 'style' | 'refactor' | 'perf' | 'chore';
|
|
25
|
-
scope?: string;
|
|
26
|
-
description: string;
|
|
27
|
-
body?: string;
|
|
28
|
-
breaking?: boolean;
|
|
29
|
-
issues?: string[]; // Issue codes like TITLE_MISSING
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface SEOFixCommit {
|
|
33
|
-
category: string;
|
|
34
|
-
issues: string[];
|
|
35
|
-
filesChanged: string[];
|
|
36
|
-
description: string;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Maps SEO issue categories to conventional commit types and scopes
|
|
41
|
-
*/
|
|
42
|
-
const CATEGORY_MAPPING: Record<string, { type: ConventionalCommit['type']; scope: string }> = {
|
|
43
|
-
'crawlability': { type: 'fix', scope: 'seo' },
|
|
44
|
-
'indexability': { type: 'fix', scope: 'seo' },
|
|
45
|
-
'on-page': { type: 'fix', scope: 'meta' },
|
|
46
|
-
'content': { type: 'fix', scope: 'content' },
|
|
47
|
-
'links': { type: 'fix', scope: 'links' },
|
|
48
|
-
'images': { type: 'fix', scope: 'images' },
|
|
49
|
-
'structured-data': { type: 'feat', scope: 'schema' },
|
|
50
|
-
'performance': { type: 'perf', scope: 'web' },
|
|
51
|
-
'security': { type: 'fix', scope: 'security' },
|
|
52
|
-
'mobile': { type: 'fix', scope: 'mobile' },
|
|
53
|
-
'international': { type: 'fix', scope: 'i18n' },
|
|
54
|
-
'social-meta': { type: 'feat', scope: 'og' },
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Formats a conventional commit message
|
|
59
|
-
*/
|
|
60
|
-
export function formatConventionalCommit(commit: ConventionalCommit): string {
|
|
61
|
-
let message = commit.type;
|
|
62
|
-
|
|
63
|
-
if (commit.scope) {
|
|
64
|
-
message += `(${commit.scope})`;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (commit.breaking) {
|
|
68
|
-
message += '!';
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
message += `: ${commit.description}`;
|
|
72
|
-
|
|
73
|
-
if (commit.body) {
|
|
74
|
-
message += `\n\n${commit.body}`;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (commit.issues && commit.issues.length > 0) {
|
|
78
|
-
message += `\n\nIssue codes: ${commit.issues.join(', ')}`;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return message;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Formats a commit message for SEO fixes
|
|
86
|
-
*/
|
|
87
|
-
export function formatSEOCommitMessage(fix: SEOFixCommit, config?: CommitConfig): string {
|
|
88
|
-
const mapping = CATEGORY_MAPPING[fix.category] || { type: 'fix', scope: 'seo' };
|
|
89
|
-
const systemName = config?.systemName || SYSTEM_NAME;
|
|
90
|
-
const systemEmail = config?.systemEmail || SYSTEM_EMAIL;
|
|
91
|
-
|
|
92
|
-
const commit: ConventionalCommit = {
|
|
93
|
-
type: mapping.type,
|
|
94
|
-
scope: mapping.scope,
|
|
95
|
-
description: fix.description,
|
|
96
|
-
body: `Files changed:\n${fix.filesChanged.map(f => ` - ${f}`).join('\n')}`,
|
|
97
|
-
issues: fix.issues,
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
let message = formatConventionalCommit(commit);
|
|
101
|
-
|
|
102
|
-
// Add co-author trailer
|
|
103
|
-
message += `\n\nCo-Authored-By: ${systemName} <${systemEmail}>`;
|
|
104
|
-
|
|
105
|
-
return message;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Creates a git commit with the SEO fix
|
|
110
|
-
*/
|
|
111
|
-
export async function createSEOCommit(
|
|
112
|
-
fix: SEOFixCommit,
|
|
113
|
-
config?: CommitConfig
|
|
114
|
-
): Promise<{ success: boolean; hash?: string; error?: string }> {
|
|
115
|
-
const message = formatSEOCommitMessage(fix, config);
|
|
116
|
-
|
|
117
|
-
if (config?.dryRun) {
|
|
118
|
-
console.log('DRY RUN - Would create commit:');
|
|
119
|
-
console.log('---');
|
|
120
|
-
console.log(message);
|
|
121
|
-
console.log('---');
|
|
122
|
-
return { success: true, hash: 'dry-run' };
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
try {
|
|
126
|
-
// Stage the files
|
|
127
|
-
for (const file of fix.filesChanged) {
|
|
128
|
-
await execAsync(`git add "${file}"`);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Create the commit using heredoc for proper message formatting
|
|
132
|
-
const { stdout } = await execAsync(`git commit -m "$(cat <<'EOF'
|
|
133
|
-
${message}
|
|
134
|
-
EOF
|
|
135
|
-
)"`);
|
|
136
|
-
|
|
137
|
-
// Extract commit hash
|
|
138
|
-
const hashMatch = stdout.match(/\[[\w-]+ ([a-f0-9]+)\]/);
|
|
139
|
-
const hash = hashMatch ? hashMatch[1] : undefined;
|
|
140
|
-
|
|
141
|
-
return { success: true, hash };
|
|
142
|
-
} catch (error) {
|
|
143
|
-
return { success: false, error: (error as Error).message };
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Creates multiple commits, one per category
|
|
149
|
-
*/
|
|
150
|
-
export async function createSEOCommits(
|
|
151
|
-
fixes: SEOFixCommit[],
|
|
152
|
-
config?: CommitConfig
|
|
153
|
-
): Promise<{ commits: Array<{ fix: SEOFixCommit; result: Awaited<ReturnType<typeof createSEOCommit>> }> }> {
|
|
154
|
-
const commits = [];
|
|
155
|
-
|
|
156
|
-
for (const fix of fixes) {
|
|
157
|
-
const result = await createSEOCommit(fix, config);
|
|
158
|
-
commits.push({ fix, result });
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return { commits };
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Checks if the current directory is a git repository
|
|
166
|
-
*/
|
|
167
|
-
export function isGitRepo(dir?: string): boolean {
|
|
168
|
-
try {
|
|
169
|
-
const cwd = dir ? { cwd: dir } : undefined;
|
|
170
|
-
execSync('git rev-parse --is-inside-work-tree', { ...cwd, stdio: 'ignore' });
|
|
171
|
-
return true;
|
|
172
|
-
} catch {
|
|
173
|
-
return false;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Initializes a git repository if not already initialized
|
|
179
|
-
*/
|
|
180
|
-
export async function ensureGitRepo(dir?: string): Promise<void> {
|
|
181
|
-
if (!isGitRepo(dir)) {
|
|
182
|
-
const cwd = dir ? { cwd: dir } : undefined;
|
|
183
|
-
await execAsync('git init', cwd);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Gets the current git user config
|
|
189
|
-
*/
|
|
190
|
-
export function getGitUser(): { name?: string; email?: string } {
|
|
191
|
-
try {
|
|
192
|
-
const name = execSync('git config user.name', { encoding: 'utf8' }).trim();
|
|
193
|
-
const email = execSync('git config user.email', { encoding: 'utf8' }).trim();
|
|
194
|
-
return { name, email };
|
|
195
|
-
} catch {
|
|
196
|
-
return {};
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Detects if this is a GitHub Pages repository
|
|
202
|
-
*/
|
|
203
|
-
export function detectGitHubPages(dir: string): {
|
|
204
|
-
isGitHubPages: boolean;
|
|
205
|
-
type?: 'jekyll' | 'static' | 'docs' | 'custom';
|
|
206
|
-
baseUrl?: string;
|
|
207
|
-
} {
|
|
208
|
-
const fs = require('fs');
|
|
209
|
-
const path = require('path');
|
|
210
|
-
|
|
211
|
-
// Check for common GitHub Pages indicators
|
|
212
|
-
const checks = [
|
|
213
|
-
{ file: '_config.yml', type: 'jekyll' as const },
|
|
214
|
-
{ file: 'docs/index.html', type: 'docs' as const },
|
|
215
|
-
{ file: 'CNAME', type: 'custom' as const },
|
|
216
|
-
{ file: '.nojekyll', type: 'static' as const },
|
|
217
|
-
{ file: 'index.html', type: 'static' as const },
|
|
218
|
-
];
|
|
219
|
-
|
|
220
|
-
for (const check of checks) {
|
|
221
|
-
const filePath = path.join(dir, check.file);
|
|
222
|
-
if (fs.existsSync(filePath)) {
|
|
223
|
-
// Try to extract base URL from _config.yml
|
|
224
|
-
if (check.file === '_config.yml') {
|
|
225
|
-
try {
|
|
226
|
-
const content = fs.readFileSync(filePath, 'utf8');
|
|
227
|
-
const baseUrlMatch = content.match(/baseurl:\s*["']?([^"'\n]+)/);
|
|
228
|
-
const urlMatch = content.match(/url:\s*["']?([^"'\n]+)/);
|
|
229
|
-
return {
|
|
230
|
-
isGitHubPages: true,
|
|
231
|
-
type: check.type,
|
|
232
|
-
baseUrl: urlMatch ? urlMatch[1] + (baseUrlMatch?.[1] || '') : undefined,
|
|
233
|
-
};
|
|
234
|
-
} catch {
|
|
235
|
-
// Continue
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Check for CNAME (custom domain)
|
|
240
|
-
if (check.file === 'CNAME') {
|
|
241
|
-
try {
|
|
242
|
-
const cname = fs.readFileSync(path.join(dir, 'CNAME'), 'utf8').trim();
|
|
243
|
-
return {
|
|
244
|
-
isGitHubPages: true,
|
|
245
|
-
type: check.type,
|
|
246
|
-
baseUrl: `https://${cname}`,
|
|
247
|
-
};
|
|
248
|
-
} catch {
|
|
249
|
-
// Continue
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
return { isGitHubPages: true, type: check.type };
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// Check if remote is github.io
|
|
258
|
-
try {
|
|
259
|
-
const remote = execSync('git remote get-url origin', { cwd: dir, encoding: 'utf8' }).trim();
|
|
260
|
-
if (remote.includes('github.io') || remote.includes('.github.')) {
|
|
261
|
-
return { isGitHubPages: true, type: 'static' };
|
|
262
|
-
}
|
|
263
|
-
} catch {
|
|
264
|
-
// Not a git repo or no remote
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
return { isGitHubPages: false };
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Generates a summary of commits made
|
|
272
|
-
*/
|
|
273
|
-
export function generateCommitSummary(
|
|
274
|
-
commits: Array<{ fix: SEOFixCommit; result: { success: boolean; hash?: string; error?: string } }>
|
|
275
|
-
): string {
|
|
276
|
-
const successful = commits.filter(c => c.result.success);
|
|
277
|
-
const failed = commits.filter(c => !c.result.success);
|
|
278
|
-
|
|
279
|
-
let summary = `## SEO Fix Commits Summary\n\n`;
|
|
280
|
-
summary += `✅ ${successful.length} commits created\n`;
|
|
281
|
-
if (failed.length > 0) {
|
|
282
|
-
summary += `❌ ${failed.length} commits failed\n`;
|
|
283
|
-
}
|
|
284
|
-
summary += '\n';
|
|
285
|
-
|
|
286
|
-
if (successful.length > 0) {
|
|
287
|
-
summary += `### Commits Created\n\n`;
|
|
288
|
-
for (const { fix, result } of successful) {
|
|
289
|
-
const mapping = CATEGORY_MAPPING[fix.category] || { type: 'fix', scope: 'seo' };
|
|
290
|
-
summary += `- \`${result.hash?.slice(0, 7) || 'N/A'}\` ${mapping.type}(${mapping.scope}): ${fix.description}\n`;
|
|
291
|
-
summary += ` - Files: ${fix.filesChanged.join(', ')}\n`;
|
|
292
|
-
summary += ` - Issues: ${fix.issues.join(', ')}\n`;
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
if (failed.length > 0) {
|
|
297
|
-
summary += `\n### Failed Commits\n\n`;
|
|
298
|
-
for (const { fix, result } of failed) {
|
|
299
|
-
summary += `- ❌ ${fix.description}: ${result.error}\n`;
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
return summary;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
// Export types for conventional commit categories
|
|
307
|
-
export const COMMIT_TYPES = [
|
|
308
|
-
{ type: 'fix', description: 'Bug fix (SEO issue correction)' },
|
|
309
|
-
{ type: 'feat', description: 'New feature (schema, OG tags, etc.)' },
|
|
310
|
-
{ type: 'docs', description: 'Documentation update' },
|
|
311
|
-
{ type: 'style', description: 'Code style changes (formatting)' },
|
|
312
|
-
{ type: 'refactor', description: 'Code restructuring' },
|
|
313
|
-
{ type: 'perf', description: 'Performance improvement' },
|
|
314
|
-
{ type: 'chore', description: 'Maintenance task' },
|
|
315
|
-
] as const;
|
|
316
|
-
|
|
317
|
-
export const SEO_SCOPES = [
|
|
318
|
-
'seo', // General SEO
|
|
319
|
-
'meta', // Meta tags (title, description)
|
|
320
|
-
'og', // Open Graph / Twitter Cards
|
|
321
|
-
'schema', // Structured data / JSON-LD
|
|
322
|
-
'images', // Image optimization
|
|
323
|
-
'links', // Link fixes
|
|
324
|
-
'content', // Content optimization
|
|
325
|
-
'security', // Security headers
|
|
326
|
-
'mobile', // Mobile optimization
|
|
327
|
-
'i18n', // Internationalization
|
|
328
|
-
'perf', // Performance
|
|
329
|
-
] as const;
|
package/src/git/index.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Git integration module
|
|
3
|
-
*
|
|
4
|
-
* Provides git operations for SEO fixes including:
|
|
5
|
-
* - Conventional commit formatting
|
|
6
|
-
* - Co-author support
|
|
7
|
-
* - GitHub Pages detection
|
|
8
|
-
* - Pull request generation
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
export * from './commit-helper.js';
|
|
12
|
-
export * from './pr-helper.js';
|