diff --git a/.github/workflows/deploy-website.yml b/.github/workflows/deploy-website.yml
index 79ceb5c2..b333ef03 100644
--- a/.github/workflows/deploy-website.yml
+++ b/.github/workflows/deploy-website.yml
@@ -6,6 +6,10 @@
# requires a token other than GITHUB_TOKEN to enable Pages via API.
#
# Live site: https://samplexbro.github.io/agentsmesh/ (see website/astro.config.mjs `site` + `base`).
+#
+# SEO: set repository variable DEPLOY_SITE_URL to the exact origin you want indexed
+# (HTTPS, no trailing slash), e.g. https://samplexbro.github.io or your custom domain.
+# Configure DNS or CDN to 301 the non-canonical hostname (www ↔ apex) to that URL.
name: Deploy Website
on:
@@ -53,6 +57,8 @@ jobs:
- name: Build website
working-directory: website
+ env:
+ DEPLOY_SITE_URL: ${{ vars.DEPLOY_SITE_URL }}
run: pnpm run build
- name: Upload Pages artifact
diff --git a/website/astro.config.mjs b/website/astro.config.mjs
index 8503d8ea..a865c3ca 100644
--- a/website/astro.config.mjs
+++ b/website/astro.config.mjs
@@ -1,9 +1,15 @@
// @ts-check
import { defineConfig } from 'astro/config';
import starlight from '@astrojs/starlight';
+import seoRobotsIntegration from './integrations/seo-robots.mjs';
+import { absoluteFromBase, getSiteOrigin } from './site-url.mjs';
+
+const site = getSiteOrigin();
+const ogImage = absoluteFromBase('/og-image.png');
export default defineConfig({
- site: 'https://samplexbro.github.io',
+ site,
+ trailingSlash: 'always',
base: '/agentsmesh',
integrations: [
starlight({
@@ -30,7 +36,7 @@ export default defineConfig({
head: [
{
tag: 'meta',
- attrs: { property: 'og:image', content: 'https://samplexbro.github.io/agentsmesh/og-image.png' },
+ attrs: { property: 'og:image', content: ogImage },
},
{
tag: 'meta',
@@ -38,7 +44,7 @@ export default defineConfig({
},
{
tag: 'meta',
- attrs: { name: 'twitter:image', content: 'https://samplexbro.github.io/agentsmesh/og-image.png' },
+ attrs: { name: 'twitter:image', content: ogImage },
},
{
tag: 'meta',
@@ -117,5 +123,6 @@ export default defineConfig({
},
],
}),
+ seoRobotsIntegration(() => getSiteOrigin()),
],
});
diff --git a/website/integrations/seo-robots.mjs b/website/integrations/seo-robots.mjs
new file mode 100644
index 00000000..33d7cf22
--- /dev/null
+++ b/website/integrations/seo-robots.mjs
@@ -0,0 +1,21 @@
+import { writeFileSync } from 'node:fs';
+
+/**
+ * @param {() => string} getOrigin Host-only HTTPS URL, no trailing slash
+ */
+export default function seoRobotsIntegration(getOrigin) {
+ return {
+ name: 'seo-robots',
+ hooks: {
+ 'astro:build:done': ({ dir }) => {
+ const origin = getOrigin().replace(/\/$/, '');
+ const body = `User-agent: *
+Allow: /
+
+Sitemap: ${origin}/agentsmesh/sitemap-index.xml
+`;
+ writeFileSync(new URL('robots.txt', dir), body, 'utf8');
+ },
+ },
+ };
+}
diff --git a/website/public/robots.txt b/website/public/robots.txt
deleted file mode 100644
index 25dab986..00000000
--- a/website/public/robots.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-User-agent: *
-Allow: /
-
-Sitemap: https://samplexbro.github.io/agentsmesh/sitemap-index.xml
diff --git a/website/site-url.mjs b/website/site-url.mjs
new file mode 100644
index 00000000..15cf0bbd
--- /dev/null
+++ b/website/site-url.mjs
@@ -0,0 +1,23 @@
+/**
+ * Single source of truth for the docs site origin. Set DEPLOY_SITE_URL in CI
+ * (GitHub repository variable) to your indexed hostname — e.g. apex HTTPS URL
+ * with no trailing slash. Configure DNS/CDN to 301 the non-canonical host (www vs apex).
+ */
+
+/** @returns {string} e.g. https://samplexbro.github.io */
+export function getSiteOrigin() {
+ const raw =
+ process.env.DEPLOY_SITE_URL?.trim() ||
+ process.env.SITE_URL?.trim() ||
+ 'https://samplexbro.github.io';
+ return raw.replace(/\/$/, '');
+}
+
+/** @param {string} pathWithLeadingSlash path after base, e.g. /og-image.png */
+export function absoluteFromBase(pathWithLeadingSlash) {
+ const origin = getSiteOrigin();
+ const suffix = pathWithLeadingSlash.startsWith('/')
+ ? pathWithLeadingSlash
+ : `/${pathWithLeadingSlash}`;
+ return `${origin}/agentsmesh${suffix}`;
+}
diff --git a/website/src/components/SoftwareApplicationJsonLd.astro b/website/src/components/SoftwareApplicationJsonLd.astro
new file mode 100644
index 00000000..2509b67d
--- /dev/null
+++ b/website/src/components/SoftwareApplicationJsonLd.astro
@@ -0,0 +1,23 @@
+---
+const origin = Astro.site?.origin ?? 'https://samplexbro.github.io';
+const base = import.meta.env.BASE_URL;
+const docsRoot = `${origin.replace(/\/$/, '')}${base}`.replace(/\/?$/, '/');
+
+const jsonLd = {
+ '@context': 'https://schema.org',
+ '@type': 'SoftwareApplication',
+ name: 'AgentsMesh',
+ description:
+ 'Open-source CLI that syncs AI coding assistant configuration across every major tool — Claude Code, Cursor, Copilot, Gemini CLI, Windsurf, and more. One canonical .agentsmesh/ directory with bidirectional import, generate, community skill registry, and CI drift detection.',
+ applicationCategory: 'DeveloperApplication',
+ operatingSystem: 'Node.js 20+',
+ offers: { '@type': 'Offer', price: '0', priceCurrency: 'USD' },
+ license: 'https://opensource.org/licenses/MIT',
+ url: docsRoot,
+ downloadUrl: 'https://www.npmjs.com/package/agentsmesh',
+ codeRepository: 'https://github.com/sampleXbro/agentsmesh',
+ author: { '@type': 'Person', name: 'sampleXbro' },
+};
+---
+
+
diff --git a/website/src/content/docs/index.mdx b/website/src/content/docs/index.mdx
index 7e7ba9b1..962a382e 100644
--- a/website/src/content/docs/index.mdx
+++ b/website/src/content/docs/index.mdx
@@ -25,6 +25,7 @@ hero:
import { Card, CardGrid, Badge, Aside } from '@astrojs/starlight/components';
import CatalogExplorer from '../../components/catalog-explorer/CatalogExplorer.astro';
+import SoftwareApplicationJsonLd from '../../components/SoftwareApplicationJsonLd.astro';
@@ -142,24 +143,4 @@ npx agentsmesh generate
The CLI is available as both `agentsmesh` and the shorter alias `amsh`.
-
+