fimo 2.8.0-staging.8 → 2.8.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.
Files changed (129) hide show
  1. package/assets/skills/fimo/SKILL.md +0 -1
  2. package/assets/skills/fimo-cli/SKILL.md +6 -7
  3. package/assets/skills/fimo-cli/references/assets.md +2 -0
  4. package/assets/skills/fimo-cli/references/deploy.md +1 -1
  5. package/assets/skills/fimo-cli/references/init.md +1 -2
  6. package/assets/skills/fimo-cli/references/integrations/firecrawl.md +1 -41
  7. package/assets/skills/fimo-cli/references/integrations.md +2 -38
  8. package/assets/skills/fimo-cli/references/knowledge.md +1 -1
  9. package/assets/skills/fimo-cli/references/mcp.md +5 -6
  10. package/assets/skills/fimo-cli/references/preview.md +1 -1
  11. package/assets/skills/fimo-cli/references/project-vars-and-secrets.md +0 -16
  12. package/assets/skills/fimo-cli/references/webhooks.md +0 -2
  13. package/assets/skills/fimo-code/SKILL.md +1 -0
  14. package/assets/skills/fimo-code/references/assets.md +5 -5
  15. package/assets/skills/fimo-code/references/design.md +1 -1
  16. package/assets/skills/fimo-code/references/ui.md +1 -1
  17. package/assets/skills/fimo-studio/SKILL.md +13 -7
  18. package/assets/skills/fimo-studio/references/ai-chat.md +15 -2
  19. package/assets/skills/fimo-studio/references/billing-and-credits.md +4 -6
  20. package/assets/skills/fimo-studio/references/cms.md +1 -1
  21. package/assets/skills/fimo-studio/references/command-palette.md +1 -2
  22. package/assets/skills/fimo-studio/references/connections.md +3 -4
  23. package/assets/skills/fimo-studio/references/custom-domains.md +2 -2
  24. package/assets/skills/fimo-studio/references/media-library.md +1 -2
  25. package/assets/skills/fimo-studio/references/preview-and-responsive.md +4 -9
  26. package/assets/skills/fimo-studio/references/project-creation.md +24 -6
  27. package/assets/skills/fimo-studio/references/publish-and-deploy.md +2 -2
  28. package/assets/skills/fimo-studio/references/schedules.md +2 -3
  29. package/assets/skills/fimo-studio/references/seo-settings.md +2 -2
  30. package/assets/skills/fimo-studio/references/sharing-and-collaboration.md +2 -3
  31. package/assets/skills/fimo-studio/references/visual-editing.md +1 -1
  32. package/assets/skills/fimo-studio/references/webhooks.md +7 -8
  33. package/dist/cli/bundle.json +2 -2
  34. package/dist/cli/index.js +153 -162
  35. package/dist/runtime/paths/get-fimo-paths.js +2 -24
  36. package/dist/runtime/project-layout.d.ts +0 -1
  37. package/dist/runtime/project-layout.js +1 -5
  38. package/dist/runtime/shared/local-runtime-env.js +2 -14
  39. package/dist/runtime/templates.d.ts +0 -1
  40. package/dist/runtime/templates.js +0 -3
  41. package/package.json +10 -10
  42. package/release.json +2 -2
  43. package/templates/astro/.fimo/config.json +0 -14
  44. package/templates/astro/AGENTS.md +2 -1
  45. package/templates/astro/_gitignore +3 -0
  46. package/templates/astro/package.json +3 -3
  47. package/templates/astro/src/layouts/Base.astro +0 -48
  48. package/templates/nextjs/AGENTS.md +1 -1
  49. package/templates/nextjs/_gitignore +3 -0
  50. package/templates/nextjs/package.json +5 -5
  51. package/templates/react-router/AGENTS.md +1 -1
  52. package/templates/react-router/_gitignore +3 -0
  53. package/templates/react-router/package.json +5 -5
  54. package/templates/sveltekit/AGENTS.md +1 -0
  55. package/templates/sveltekit/_gitignore +3 -0
  56. package/templates/sveltekit/package.json +3 -3
  57. package/templates/vite/AGENTS.md +2 -0
  58. package/templates/vite/_gitignore +3 -0
  59. package/templates/vite/package.json +5 -5
  60. package/assets/agent-templates/_default/GOAL.md +0 -21
  61. package/assets/agent-templates/_default/capabilities.yaml +0 -11
  62. package/assets/agent-templates/_default/config.yaml +0 -13
  63. package/assets/agent-templates/broken-link-sweeper/GOAL.md +0 -51
  64. package/assets/agent-templates/broken-link-sweeper/capabilities.yaml +0 -1
  65. package/assets/agent-templates/broken-link-sweeper/config.yaml +0 -13
  66. package/assets/agent-templates/content-translator/GOAL.md +0 -33
  67. package/assets/agent-templates/content-translator/capabilities.yaml +0 -5
  68. package/assets/agent-templates/content-translator/config.yaml +0 -12
  69. package/assets/agent-templates/content-translator/scripts/translate-entries.ts +0 -69
  70. package/assets/agent-templates/post-deploy-check/GOAL.md +0 -23
  71. package/assets/agent-templates/post-deploy-check/capabilities.yaml +0 -1
  72. package/assets/agent-templates/post-deploy-check/config.yaml +0 -10
  73. package/assets/agent-templates/post-deploy-check/scripts/check-logs.ts +0 -39
  74. package/assets/agent-templates/post-deploy-check/scripts/probe-routes.ts +0 -53
  75. package/assets/agent-templates/seo-audit/GOAL.md +0 -43
  76. package/assets/agent-templates/seo-audit/capabilities.yaml +0 -3
  77. package/assets/agent-templates/seo-audit/config.yaml +0 -14
  78. package/assets/agent-templates/seo-audit/references/report-template.md +0 -28
  79. package/assets/agent-templates/seo-audit/references/scoring-methodology.md +0 -30
  80. package/assets/agent-templates/seo-audit/scripts/analyze-html.ts +0 -82
  81. package/assets/agent-templates/seo-audit/scripts/fetch-page.ts +0 -39
  82. package/assets/agent-templates/seo-audit/scripts/list-routes.ts +0 -46
  83. package/assets/agent-templates/seo-audit/scripts/report.ts +0 -82
  84. package/assets/agent-templates/seo-audit/scripts/score.ts +0 -131
  85. package/assets/skills/fimo-cli/references/agents.md +0 -298
  86. package/assets/skills/fimo-studio/references/agents.md +0 -33
  87. package/assets/skills/fimo-studio/references/drawing-and-wireframing.md +0 -61
  88. package/templates/astro/src/components/ProjectOverview.astro +0 -45
  89. package/templates/astro/src/pages/[locale]/projects/[slug].astro +0 -38
  90. package/templates/astro/src/pages/[locale]/projects/index.astro +0 -19
  91. package/templates/astro/src/pages/projects/[slug].astro +0 -31
  92. package/templates/astro/src/pages/projects/index.astro +0 -13
  93. package/templates/astro/src/schemas/Project.json +0 -9
  94. package/templates/astro/src/schemas/Project.ts +0 -29
  95. package/templates/astro/src/schemas/Project.types.ts +0 -70
  96. package/templates/astro/src/schemas/SiteSettings.json +0 -10
  97. package/templates/astro/src/schemas/SiteSettings.ts +0 -31
  98. package/templates/astro/src/schemas/SiteSettings.types.ts +0 -69
  99. package/templates/react-router/src/pages/Privacy.tsx +0 -79
  100. package/templates/react-router/src/pages/Terms.tsx +0 -48
  101. package/templates/react-router/src/pages/legal/LegalPage.tsx +0 -42
  102. package/templates/welcome/manifest.json +0 -655
  103. package/templates/welcome/media/analytics-overview.avif +0 -0
  104. package/templates/welcome/media/autonomous-loop.avif +0 -0
  105. package/templates/welcome/media/editable-preview.avif +0 -0
  106. package/templates/welcome/media/media-library.avif +0 -0
  107. package/templates/welcome/media/slack-teams.avif +0 -0
  108. package/templates/welcome/template/.fimo/agents/welcome-guide/GOAL.md +0 -12
  109. package/templates/welcome/template/.fimo/agents/welcome-guide/capabilities.yaml +0 -3
  110. package/templates/welcome/template/.fimo/agents/welcome-guide/config.yaml +0 -8
  111. package/templates/welcome/template/.fimo/config.json +0 -52
  112. package/templates/welcome/template/src/font.css +0 -60
  113. package/templates/welcome/template/src/pages/ArticleDetail.tsx +0 -104
  114. package/templates/welcome/template/src/pages/Index.tsx +0 -36
  115. package/templates/welcome/template/src/pages/SiteContent.tsx +0 -674
  116. package/templates/welcome/template/src/pages/fallback-content.ts +0 -152
  117. package/templates/welcome/template/src/routes.ts +0 -9
  118. package/templates/welcome/template/src/schemas/Article.json +0 -34
  119. package/templates/welcome/template/src/schemas/Article.ts +0 -54
  120. package/templates/welcome/template/src/schemas/Article.types.ts +0 -93
  121. package/templates/welcome/template/src/schemas/Author.json +0 -17
  122. package/templates/welcome/template/src/schemas/Author.ts +0 -51
  123. package/templates/welcome/template/src/schemas/Author.types.ts +0 -54
  124. package/templates/welcome/template/src/schemas/Category.json +0 -17
  125. package/templates/welcome/template/src/schemas/Category.ts +0 -51
  126. package/templates/welcome/template/src/schemas/Category.types.ts +0 -54
  127. package/templates/welcome/template/src/schemas/HomePage.json +0 -110
  128. package/templates/welcome/template/src/schemas/HomePage.ts +0 -22
  129. package/templates/welcome/template/src/schemas/HomePage.types.ts +0 -116
@@ -1,69 +0,0 @@
1
- /**
2
- * Tool: translate-entries
3
- *
4
- * Walk every default-locale CMS entry, find missing non-default-locale
5
- * document rows, call the configured translation provider, and write back
6
- * through the Fimo API. Skips fields marked `translate: false` in the schema.
7
- *
8
- * Provider is configured via env (FIMO_AGENT_TRANSLATE_PROVIDER) — the
9
- * runtime threads project secrets through when the v1.x secrets API
10
- * lands. For now this template uses the OpenAI fallback.
11
- */
12
-
13
- interface Entry {
14
- id: string;
15
- documentId: string;
16
- locale: string;
17
- type: string;
18
- data: Record<string, unknown>;
19
- }
20
-
21
- interface TranslateOptions {
22
- fromLocale: string;
23
- toLocales: string[];
24
- api: { base: string; projectId: string; env: string };
25
- }
26
-
27
- async function listMissing(_opts: TranslateOptions): Promise<Entry[]> {
28
- // Stub — wired to the Fimo API by the runtime. Returns entries with
29
- // at least one missing target-locale document row while the default-locale
30
- // row is populated.
31
- return [];
32
- }
33
-
34
- async function translate(text: string, _to: string): Promise<string> {
35
- // Stub — call the configured provider. Returns the translated string.
36
- return text;
37
- }
38
-
39
- async function writeBack(_entry: Entry, _locale: string, _value: string): Promise<void> {
40
- // Stub — create/update the target locale entry via
41
- // `/api/tenant/:projectId/branches/:branch/entries/:type`.
42
- }
43
-
44
- async function main(): Promise<void> {
45
- const fromLocale = process.env.FIMO_DEFAULT_LOCALE ?? 'en';
46
- const toLocales = (process.env.FIMO_TARGET_LOCALES ?? '').split(',').filter(Boolean);
47
- const api = {
48
- base: process.env.FIMO_API_URL ?? 'http://localhost:3000',
49
- projectId: process.env.FIMO_PROJECT_ID ?? '',
50
- env: process.env.FIMO_ENV ?? 'main',
51
- };
52
-
53
- const entries = await listMissing({ fromLocale, toLocales, api });
54
- for (const entry of entries) {
55
- for (const locale of toLocales) {
56
- const sourceValue = typeof entry.data.title === 'string' ? entry.data.title : undefined;
57
- if (!sourceValue) {
58
- continue;
59
- }
60
- const translated = await translate(sourceValue, locale);
61
- await writeBack(entry, locale, translated);
62
- }
63
- }
64
- }
65
-
66
- main().catch((err) => {
67
- console.error(err);
68
- process.exit(1);
69
- });
@@ -1,23 +0,0 @@
1
- # Post-deploy check
2
-
3
- Run a quick health probe every time the project deploys, and post a
4
- summary back to the deploy log. Triggered automatically by the
5
- `deploy.succeeded` event.
6
-
7
- ## What to do
8
-
9
- 1. Read the routes the deploy declares as critical (`probe-routes.ts`).
10
- 2. For each one, fetch and assert that the response is 2xx and renders
11
- the expected key element (a known DOM selector).
12
- 3. Read the last 5 minutes of production logs (`check-logs.ts`) and
13
- surface any errors emitted during the deploy window.
14
- 4. Write a single-line summary to stdout that the deploy notification
15
- includes verbatim. Examples:
16
- - `✅ 12 routes pass · 0 errors`
17
- - `❌ /pricing failed (500) · 3 errors in last 5min`
18
-
19
- ## What NOT to do
20
-
21
- - Don't roll back the deploy — that's a separate human-in-the-loop
22
- decision; this agent only reports.
23
- - Don't write to CMS or files. Read-only probes plus log reads.
@@ -1 +0,0 @@
1
- - fimo:logs:read
@@ -1,10 +0,0 @@
1
- version: 1
2
- description: 'Probe routes and inspect logs after a deploy; report any issues to email recipients.'
3
- model: openai/gpt-5.6-luna
4
- data_sources:
5
- - prod
6
-
7
- triggers:
8
- - type: manual
9
- - type: webhook
10
- path: deploy-done
@@ -1,39 +0,0 @@
1
- /**
2
- * Tool: check-logs
3
- *
4
- * Query the project's production log stream for the last N minutes and
5
- * count errors. Used by the post-deploy-check summary line.
6
- *
7
- * Inputs (argv):
8
- * 1. Minutes to look back (default 5)
9
- *
10
- * Outputs (stdout JSON): { errors: number; first?: { ts: string; message: string } }
11
- */
12
-
13
- async function main(): Promise<void> {
14
- const minutes = Number(process.argv[2] ?? '5');
15
- const projectId = process.env.FIMO_PROJECT_ID ?? '';
16
- const env = process.env.FIMO_ENV ?? 'main';
17
- const base = process.env.FIMO_API_URL ?? 'http://localhost:3000';
18
-
19
- const since = new Date(Date.now() - minutes * 60_000).toISOString();
20
- const url = `${base}/api/management/projects/${projectId}/branches/${encodeURIComponent(env)}/logs?since=${encodeURIComponent(since)}&level=error`;
21
-
22
- try {
23
- const res = await fetch(url, { credentials: 'include' });
24
- if (!res.ok) {
25
- process.stdout.write(JSON.stringify({ errors: 0, note: `log endpoint returned ${res.status}` }));
26
- return;
27
- }
28
- const payload = (await res.json()) as { data?: { entries?: Array<{ ts: string; message: string }> } };
29
- const entries = payload.data?.entries ?? [];
30
- process.stdout.write(JSON.stringify({ errors: entries.length, first: entries[0] }));
31
- } catch (err) {
32
- process.stdout.write(JSON.stringify({ errors: 0, note: `log fetch failed: ${(err as Error).message}` }));
33
- }
34
- }
35
-
36
- main().catch((err) => {
37
- console.error(err);
38
- process.exit(1);
39
- });
@@ -1,53 +0,0 @@
1
- /**
2
- * Tool: probe-routes
3
- *
4
- * Fetch a list of critical routes and assert that each one responds
5
- * with a 2xx. Optionally checks that a known selector is present.
6
- *
7
- * Inputs (argv): JSON array of `{ url, selector? }`
8
- * Outputs (stdout JSON): { ok: number; failed: { url: string; status: number; reason: string }[] }
9
- */
10
-
11
- interface Probe {
12
- url: string;
13
- selector?: string;
14
- }
15
-
16
- interface Result {
17
- ok: number;
18
- failed: { url: string; status: number; reason: string }[];
19
- }
20
-
21
- async function probe(p: Probe): Promise<{ url: string; status: number; reason: string } | null> {
22
- const res = await fetch(p.url, { redirect: 'manual' });
23
- if (res.status < 200 || res.status >= 300) {
24
- return { url: p.url, status: res.status, reason: `non-2xx status ${res.status}` };
25
- }
26
- if (p.selector) {
27
- const body = await res.text();
28
- if (!body.includes(p.selector)) {
29
- return { url: p.url, status: res.status, reason: `expected selector "${p.selector}" not found` };
30
- }
31
- }
32
- return null;
33
- }
34
-
35
- async function main(): Promise<void> {
36
- const raw = process.argv[2] ?? '[]';
37
- const probes = JSON.parse(raw) as Probe[];
38
- const result: Result = { ok: 0, failed: [] };
39
- for (const p of probes) {
40
- const failure = await probe(p);
41
- if (failure) {
42
- result.failed.push(failure);
43
- } else {
44
- result.ok += 1;
45
- }
46
- }
47
- process.stdout.write(JSON.stringify(result));
48
- }
49
-
50
- main().catch((err) => {
51
- console.error(err);
52
- process.exit(1);
53
- });
@@ -1,43 +0,0 @@
1
- # SEO audit
2
-
3
- Audit the project's pages for SEO health and deliver the findings as your session report.
4
-
5
- ## Inputs
6
-
7
- - The project's published routes (via `scripts/list-routes.ts`)
8
- - Each page's HTML + response headers (via `scripts/fetch-page.ts`)
9
- - The current analytics data (read-only, via `fimo:analytics:read`)
10
-
11
- ## What to check (per page)
12
-
13
- 1. **Title tag** — present, 30–60 chars, unique across routes
14
- 2. **Meta description** — present, 50–160 chars, unique
15
- 3. **Open Graph** — `og:title`, `og:description`, `og:image`, `og:url`
16
- 4. **Schema.org** — at minimum `WebPage`; `Article` on blog posts;
17
- `BreadcrumbList` on nested routes
18
- 5. **Headings** — exactly one `h1`; `h2`/`h3` nest correctly
19
- 6. **Canonical URL** — `<link rel="canonical">` present and self-referential
20
- 7. **Robots / X-Robots-Tag** — not `noindex` on routes that should be indexed
21
- 8. **Sitemap** — every route is present in `/sitemap.xml`
22
-
23
- ## What to produce
24
-
25
- Deliver the audit as your final session report — Fimo renders your last
26
- message as the report, so you do not have (and do not need) write access.
27
- Structure it as:
28
-
29
- - An executive summary (score / 100, top 3 issues)
30
- - A per-page table with status chips (✅ / ⚠️ / ❌)
31
- - A "fix recipes" appendix linking each failure to the smallest change
32
- that would resolve it
33
- - A diff-friendly machine-readable footer (`<!-- agent-summary: ... -->`)
34
- so subsequent runs can show the trend
35
-
36
- ## What NOT to do
37
-
38
- - This audit is strictly read-only. Don't attempt to write files, create a
39
- report file, or commit — and don't mention capabilities or write access in
40
- the report itself. The report is your final message, nothing more.
41
- - Don't fetch external URLs other than the project's own domain.
42
- - Don't include traffic / conversion data in the report — it lives
43
- elsewhere in the Fimo dashboard.
@@ -1,3 +0,0 @@
1
- # Read-only audit — no writes to project files or CMS.
2
- - cms:read
3
- - fimo:analytics:read
@@ -1,14 +0,0 @@
1
- version: 1
2
- description: "Audit the project's pages for SEO health and write a markdown report."
3
- model: openai/gpt-5.6-luna
4
- # Read from main env. Switch to prod when running the audit against the
5
- # live site (M13 keeps data_sources here so M16's sandbox adapter can
6
- # bind the right Fimo API base URL).
7
- data_sources:
8
- - main
9
-
10
- # Manual-only in v1. Add a schedule trigger when you're ready to run
11
- # this nightly — make sure .fimo/config.json has
12
- # `agents.notifications.email_recipients` set before activating.
13
- triggers:
14
- - type: manual
@@ -1,28 +0,0 @@
1
- # Report template
2
-
3
- The agent renders `reports/seo-audit-<YYYY-MM-DD>.md` using the shape:
4
-
5
- ```markdown
6
- # SEO audit · 2026-05-26
7
-
8
- **Overall score:** 78 / 100
9
-
10
- ## Per-page summary
11
-
12
- | Path | Score | Status |
13
- | -------- | ----- | ------ |
14
- | `/` | 95 | OK |
15
- | `/about` | 70 | WARN |
16
-
17
- ## Findings
18
-
19
- ### `/about`
20
-
21
- - ⚠️ **title-length** — Title length 22 chars — recommend 30–60
22
- - ❌ **h1-present** — No <h1> on the page
23
-
24
- <!-- agent-summary: { "score": 78, "pages": 12 } -->
25
- ```
26
-
27
- The `<!-- agent-summary -->` comment is parsed by subsequent runs to
28
- plot a trend; keep its shape stable.
@@ -1,30 +0,0 @@
1
- # Scoring methodology
2
-
3
- A page starts at 100 points and loses:
4
-
5
- - **-15** per `fail` finding (missing title, missing `<h1>`, `noindex` on a public page, etc.)
6
- - **-5** per `warn` finding (length out of range, missing OG tag, no canonical, etc.)
7
-
8
- The overall project score is the arithmetic mean of per-page scores.
9
-
10
- ## Severity guide
11
-
12
- | Rule | Severity | Why |
13
- | ---------------- | --------- | -------------------------------------------------------------------- |
14
- | title-present | fail | Without a title, search snippets fall back to the URL — costly |
15
- | title-length | warn | Outside 30–60 chars, Google truncates or pads — still ranks |
16
- | description-\* | warn/fail | Less impact on rank but huge impact on CTR |
17
- | og-{title,image} | warn | Affects social shares only; no SEO rank impact |
18
- | h1-present | fail | Page has no semantic root — both screen-readers and search hurt |
19
- | h1-single | warn | Multiple h1s — accessibility lint, not a ranking penalty |
20
- | canonical | warn | Self-referential canonical is best practice; rarely required |
21
- | robots-noindex | fail | Page explicitly excluded from index — review and remove if a mistake |
22
- | schema-org | warn | Missing rich-result eligibility — won't hurt rank but loses CTR |
23
-
24
- ## How the agent should act on findings
25
-
26
- Don't write to project files in v1. Instead, the report's "fix recipes"
27
- appendix should link to the file the agent suspects is responsible (e.g.
28
- "fix this in `src/routes/[slug].tsx`'s `meta` export"). A follow-up
29
- agent — the one that resolves recipes into actual PRs — is the
30
- appropriate writer.
@@ -1,82 +0,0 @@
1
- /**
2
- * Tool: analyze-html
3
- *
4
- * Extract SEO signals from HTML — title, meta description, OG tags,
5
- * schema.org JSON-LD, heading structure, canonical, robots directive.
6
- * Returns a structured shape `score.ts` consumes.
7
- *
8
- * Inputs (stdin): HTML body
9
- * Outputs (stdout JSON): SEOSignals
10
- */
11
-
12
- interface SEOSignals {
13
- title: string | null;
14
- description: string | null;
15
- canonical: string | null;
16
- robots: string | null;
17
- openGraph: Record<string, string>;
18
- schemaOrg: unknown[];
19
- headings: { h1: string[]; h2: string[]; h3: string[] };
20
- }
21
-
22
- function getMatch(html: string, regex: RegExp): string | null {
23
- const m = html.match(regex);
24
- return m && m[1] ? m[1].trim() : null;
25
- }
26
-
27
- function getAllMatches(html: string, regex: RegExp): string[] {
28
- const out: string[] = [];
29
- for (const m of html.matchAll(regex)) {
30
- if (m[1]) {
31
- out.push(m[1].trim());
32
- }
33
- }
34
- return out;
35
- }
36
-
37
- async function readStdin(): Promise<string> {
38
- const chunks: Buffer[] = [];
39
- for await (const chunk of process.stdin) {
40
- chunks.push(Buffer.from(chunk));
41
- }
42
- return Buffer.concat(chunks).toString('utf8');
43
- }
44
-
45
- async function main(): Promise<void> {
46
- const html = await readStdin();
47
-
48
- const openGraph: Record<string, string> = {};
49
- for (const m of html.matchAll(/<meta\s+property=["']og:([^"']+)["']\s+content=["']([^"']*)["']/gi)) {
50
- openGraph[m[1]!] = m[2] ?? '';
51
- }
52
-
53
- const schemaOrg: unknown[] = [];
54
- for (const m of html.matchAll(/<script\s+type=["']application\/ld\+json["'][^>]*>([\s\S]*?)<\/script>/gi)) {
55
- try {
56
- schemaOrg.push(JSON.parse(m[1]!.trim()));
57
- } catch {
58
- // skip invalid JSON-LD blocks
59
- }
60
- }
61
-
62
- const signals: SEOSignals = {
63
- title: getMatch(html, /<title>([\s\S]*?)<\/title>/i),
64
- description: getMatch(html, /<meta\s+name=["']description["']\s+content=["']([^"']*)["']/i),
65
- canonical: getMatch(html, /<link\s+rel=["']canonical["']\s+href=["']([^"']*)["']/i),
66
- robots: getMatch(html, /<meta\s+name=["']robots["']\s+content=["']([^"']*)["']/i),
67
- openGraph,
68
- schemaOrg,
69
- headings: {
70
- h1: getAllMatches(html, /<h1[^>]*>([\s\S]*?)<\/h1>/gi).map((s) => s.replace(/<[^>]+>/g, '').trim()),
71
- h2: getAllMatches(html, /<h2[^>]*>([\s\S]*?)<\/h2>/gi).map((s) => s.replace(/<[^>]+>/g, '').trim()),
72
- h3: getAllMatches(html, /<h3[^>]*>([\s\S]*?)<\/h3>/gi).map((s) => s.replace(/<[^>]+>/g, '').trim()),
73
- },
74
- };
75
-
76
- process.stdout.write(JSON.stringify(signals, null, 2));
77
- }
78
-
79
- main().catch((err) => {
80
- console.error(err);
81
- process.exit(1);
82
- });
@@ -1,39 +0,0 @@
1
- /**
2
- * Tool: fetch-page
3
- *
4
- * Fetch HTML + headers for a given URL. Used by the audit to walk every
5
- * route returned by `list-routes` and feed the bytes to `analyze-html`.
6
- *
7
- * Inputs (argv):
8
- * 1. The URL to fetch (must be the project's own domain)
9
- *
10
- * Outputs (stdout JSON):
11
- * {
12
- * "url": "...",
13
- * "status": 200,
14
- * "headers": { "content-type": "text/html; charset=utf-8", ... },
15
- * "body": "<!doctype html>..."
16
- * }
17
- */
18
-
19
- async function main(): Promise<void> {
20
- const url = process.argv[2];
21
- if (!url) {
22
- console.error('Usage: fetch-page <url>');
23
- process.exit(1);
24
- }
25
-
26
- const res = await fetch(url, { redirect: 'manual' });
27
- const headers: Record<string, string> = {};
28
- res.headers.forEach((value, key) => {
29
- headers[key] = value;
30
- });
31
-
32
- const body = await res.text();
33
- process.stdout.write(JSON.stringify({ url, status: res.status, headers, body }, null, 2));
34
- }
35
-
36
- main().catch((err) => {
37
- console.error(err);
38
- process.exit(1);
39
- });
@@ -1,46 +0,0 @@
1
- /**
2
- * Tool: list-routes
3
- *
4
- * Enumerate every public route in the project via the Fimo management
5
- * API. Returns paths the audit walks one-by-one through `fetch-page`.
6
- *
7
- * Inputs (branch):
8
- * - FIMO_PROJECT_ID — provided by the runtime (M16 sandbox adapter)
9
- * - FIMO_ENV — provided by the runtime (legacy runtime selector)
10
- * - FIMO_API_URL — provided by the runtime
11
- *
12
- * Outputs (stdout JSON):
13
- * { "routes": [{ "path": "/", "label": "Home" }, ...] }
14
- */
15
-
16
- interface Route {
17
- path: string;
18
- label: string;
19
- }
20
-
21
- async function main(): Promise<void> {
22
- const projectId = process.env.FIMO_PROJECT_ID;
23
- const env = process.env.FIMO_ENV ?? 'main';
24
- const base = process.env.FIMO_API_URL ?? 'http://localhost:3000';
25
-
26
- if (!projectId) {
27
- console.error('FIMO_PROJECT_ID is required');
28
- process.exit(1);
29
- }
30
-
31
- const url = `${base}/api/management/projects/${projectId}/branches/${encodeURIComponent(env)}/routes`;
32
- const res = await fetch(url, { credentials: 'include' });
33
- if (!res.ok) {
34
- console.error(`Failed to list routes: ${res.status} ${res.statusText}`);
35
- process.exit(1);
36
- }
37
-
38
- const payload = (await res.json()) as { data?: { routes?: Record<string, Route> } };
39
- const routes = Object.values(payload.data?.routes ?? {});
40
- process.stdout.write(JSON.stringify({ routes }, null, 2));
41
- }
42
-
43
- main().catch((err) => {
44
- console.error(err);
45
- process.exit(1);
46
- });
@@ -1,82 +0,0 @@
1
- /**
2
- * Tool: report
3
- *
4
- * Render a markdown report from the per-page audit results.
5
- *
6
- * Inputs (stdin JSON): { score: number, pages: PageReport[] }
7
- * Outputs (stdout): markdown
8
- */
9
-
10
- interface Finding {
11
- rule: string;
12
- severity: 'ok' | 'warn' | 'fail';
13
- message: string;
14
- }
15
-
16
- interface PageReport {
17
- path: string;
18
- score: number;
19
- findings: Finding[];
20
- }
21
-
22
- function statusChip(score: number): string {
23
- if (score >= 80) {
24
- return 'OK';
25
- }
26
- if (score >= 60) {
27
- return 'WARN';
28
- }
29
- return 'FAIL';
30
- }
31
-
32
- async function readStdin(): Promise<string> {
33
- const chunks: Buffer[] = [];
34
- for await (const chunk of process.stdin) {
35
- chunks.push(Buffer.from(chunk));
36
- }
37
- return Buffer.concat(chunks).toString('utf8');
38
- }
39
-
40
- async function main(): Promise<void> {
41
- const text = await readStdin();
42
- const data = JSON.parse(text) as { score: number; pages: PageReport[] };
43
-
44
- const date = new Date().toISOString().slice(0, 10);
45
- const lines: string[] = [];
46
- lines.push(`# SEO audit · ${date}`);
47
- lines.push('');
48
- lines.push(`**Overall score:** ${data.score} / 100`);
49
- lines.push('');
50
- lines.push('## Per-page summary');
51
- lines.push('');
52
- lines.push('| Path | Score | Status |');
53
- lines.push('|------|-------|--------|');
54
- for (const page of data.pages) {
55
- lines.push(`| \`${page.path}\` | ${page.score} | ${statusChip(page.score)} |`);
56
- }
57
- lines.push('');
58
- lines.push('## Findings');
59
- lines.push('');
60
- for (const page of data.pages) {
61
- const failures = page.findings.filter((f) => f.severity !== 'ok');
62
- if (failures.length === 0) {
63
- continue;
64
- }
65
- lines.push(`### \`${page.path}\``);
66
- lines.push('');
67
- for (const finding of failures) {
68
- const sev = finding.severity === 'fail' ? '❌' : '⚠️';
69
- lines.push(`- ${sev} **${finding.rule}** — ${finding.message}`);
70
- }
71
- lines.push('');
72
- }
73
- lines.push('');
74
- lines.push(`<!-- agent-summary: { "score": ${data.score}, "pages": ${data.pages.length} } -->`);
75
-
76
- process.stdout.write(lines.join('\n'));
77
- }
78
-
79
- main().catch((err) => {
80
- console.error(err);
81
- process.exit(1);
82
- });