fimo 0.0.0 → 0.2.1-experimental.1781864090656

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 (376) hide show
  1. package/README.md +344 -0
  2. package/assets/agent-templates/_default/GOAL.md +11 -0
  3. package/assets/agent-templates/_default/config.yaml +4 -0
  4. package/assets/agent-templates/broken-link-sweeper/GOAL.md +51 -0
  5. package/assets/agent-templates/broken-link-sweeper/capabilities.yaml +6 -0
  6. package/assets/agent-templates/broken-link-sweeper/config.yaml +12 -0
  7. package/assets/agent-templates/content-translator/GOAL.md +37 -0
  8. package/assets/agent-templates/content-translator/capabilities.yaml +10 -0
  9. package/assets/agent-templates/content-translator/config.yaml +11 -0
  10. package/assets/agent-templates/content-translator/scripts/translate-entries.ts +66 -0
  11. package/assets/agent-templates/content-translator/scripts/write-locale-files.ts +66 -0
  12. package/assets/agent-templates/post-deploy-check/GOAL.md +23 -0
  13. package/assets/agent-templates/post-deploy-check/capabilities.yaml +2 -0
  14. package/assets/agent-templates/post-deploy-check/config.yaml +9 -0
  15. package/assets/agent-templates/post-deploy-check/scripts/check-logs.ts +39 -0
  16. package/assets/agent-templates/post-deploy-check/scripts/probe-routes.ts +53 -0
  17. package/assets/agent-templates/seo-audit/GOAL.md +39 -0
  18. package/assets/agent-templates/seo-audit/capabilities.yaml +5 -0
  19. package/assets/agent-templates/seo-audit/config.yaml +13 -0
  20. package/assets/agent-templates/seo-audit/references/report-template.md +28 -0
  21. package/assets/agent-templates/seo-audit/references/scoring-methodology.md +30 -0
  22. package/assets/agent-templates/seo-audit/scripts/analyze-html.ts +82 -0
  23. package/assets/agent-templates/seo-audit/scripts/fetch-page.ts +39 -0
  24. package/assets/agent-templates/seo-audit/scripts/list-routes.ts +46 -0
  25. package/assets/agent-templates/seo-audit/scripts/report.ts +82 -0
  26. package/assets/agent-templates/seo-audit/scripts/score.ts +131 -0
  27. package/assets/agents-starter/AGENTS.md +54 -0
  28. package/assets/agents-starter/claude/settings.json +19 -0
  29. package/assets/content-templates/form-template.eta +188 -0
  30. package/assets/content-templates/hooks-template.eta +315 -0
  31. package/assets/legal-templates/privacy.eta +64 -0
  32. package/assets/legal-templates/terms.eta +41 -0
  33. package/assets/skills/fimo/SKILL.md +65 -0
  34. package/assets/skills/fimo/references/assets.md +68 -0
  35. package/assets/skills/fimo/references/content.md +261 -0
  36. package/assets/skills/fimo/references/design.md +142 -0
  37. package/assets/skills/fimo/references/forms.md +106 -0
  38. package/assets/skills/fimo/references/setup-plain-vite.md +218 -0
  39. package/assets/skills/fimo/references/setup-react-router.md +257 -0
  40. package/assets/skills/fimo/references/translations.md +75 -0
  41. package/assets/skills/fimo/references/ui.md +86 -0
  42. package/assets/skills/fimo-cli/SKILL.md +115 -0
  43. package/assets/skills/fimo-cli/references/account.md +49 -0
  44. package/assets/skills/fimo-cli/references/agent-setup.md +120 -0
  45. package/assets/skills/fimo-cli/references/agents.md +84 -0
  46. package/assets/skills/fimo-cli/references/analytics.md +62 -0
  47. package/assets/skills/fimo-cli/references/assets.md +147 -0
  48. package/assets/skills/fimo-cli/references/branches.md +106 -0
  49. package/assets/skills/fimo-cli/references/content.md +82 -0
  50. package/assets/skills/fimo-cli/references/create.md +90 -0
  51. package/assets/skills/fimo-cli/references/credits.md +84 -0
  52. package/assets/skills/fimo-cli/references/deploy.md +101 -0
  53. package/assets/skills/fimo-cli/references/describe.md +79 -0
  54. package/assets/skills/fimo-cli/references/domains.md +139 -0
  55. package/assets/skills/fimo-cli/references/forms.md +67 -0
  56. package/assets/skills/fimo-cli/references/integrations/firecrawl.md +224 -0
  57. package/assets/skills/fimo-cli/references/integrations.md +106 -0
  58. package/assets/skills/fimo-cli/references/invite.md +94 -0
  59. package/assets/skills/fimo-cli/references/media.md +179 -0
  60. package/assets/skills/fimo-cli/references/preview.md +53 -0
  61. package/assets/skills/fimo-cli/references/project-vars-and-secrets.md +68 -0
  62. package/assets/skills/fimo-cli/references/projects.md +42 -0
  63. package/assets/skills/fimo-cli/references/translations.md +43 -0
  64. package/assets/skills/fimo-migration/SKILL.md +138 -0
  65. package/assets/skills/fimo-migration/agents/openai.yaml +4 -0
  66. package/assets/skills/fimo-migration/references/content.md +61 -0
  67. package/assets/skills/fimo-migration/references/forms.md +54 -0
  68. package/assets/skills/fimo-migration/references/import-contract.md +102 -0
  69. package/assets/skills/fimo-migration/references/manifest.md +160 -0
  70. package/assets/skills/fimo-migration/references/media.md +65 -0
  71. package/assets/skills/fimo-migration/references/platforms.md +54 -0
  72. package/assets/skills/fimo-migration/references/qa.md +91 -0
  73. package/assets/skills/fimo-migration/references/workflow.md +173 -0
  74. package/assets/skills/fimo-migration/scripts/build-canonical-manifest.mjs +560 -0
  75. package/assets/skills/fimo-migration/scripts/write-migration-report.mjs +462 -0
  76. package/assets/skills/fimo-studio/SKILL.md +59 -0
  77. package/assets/skills/fimo-studio/references/ai-chat.md +54 -0
  78. package/assets/skills/fimo-studio/references/billing-and-credits.md +43 -0
  79. package/assets/skills/fimo-studio/references/cms.md +66 -0
  80. package/assets/skills/fimo-studio/references/code-editor.md +65 -0
  81. package/assets/skills/fimo-studio/references/custom-domains.md +52 -0
  82. package/assets/skills/fimo-studio/references/drawing-and-wireframing.md +63 -0
  83. package/assets/skills/fimo-studio/references/media-library.md +65 -0
  84. package/assets/skills/fimo-studio/references/preview-and-responsive.md +50 -0
  85. package/assets/skills/fimo-studio/references/project-creation.md +24 -0
  86. package/assets/skills/fimo-studio/references/publish-and-deploy.md +38 -0
  87. package/assets/skills/fimo-studio/references/seo-settings.md +31 -0
  88. package/assets/skills/fimo-studio/references/sharing-and-collaboration.md +36 -0
  89. package/assets/skills/fimo-studio/references/version-history.md +42 -0
  90. package/assets/skills/fimo-studio/references/visual-editing.md +35 -0
  91. package/dist/build/vite/index.d.ts +32 -0
  92. package/dist/build/vite/index.d.ts.map +1 -0
  93. package/dist/build/vite/index.js +90 -0
  94. package/dist/build/vite/plugins/content-sync.d.ts +13 -0
  95. package/dist/build/vite/plugins/content-sync.d.ts.map +1 -0
  96. package/dist/build/vite/plugins/content-sync.js +50 -0
  97. package/dist/build/vite/plugins/data-id.d.ts +3 -0
  98. package/dist/build/vite/plugins/data-id.d.ts.map +1 -0
  99. package/dist/build/vite/plugins/data-id.js +104 -0
  100. package/dist/build/vite/plugins/fimo-config.d.ts +12 -0
  101. package/dist/build/vite/plugins/fimo-config.d.ts.map +1 -0
  102. package/dist/build/vite/plugins/fimo-config.js +52 -0
  103. package/dist/build/vite/plugins/overlay.d.ts +3 -0
  104. package/dist/build/vite/plugins/overlay.d.ts.map +1 -0
  105. package/dist/build/vite/plugins/overlay.js +154 -0
  106. package/dist/build/vite/plugins/robots.d.ts +10 -0
  107. package/dist/build/vite/plugins/robots.d.ts.map +1 -0
  108. package/dist/build/vite/plugins/robots.js +56 -0
  109. package/dist/build/vite/plugins/seo.d.ts +3 -0
  110. package/dist/build/vite/plugins/seo.d.ts.map +1 -0
  111. package/dist/build/vite/plugins/seo.js +112 -0
  112. package/dist/build/vite/plugins/server-config.d.ts +3 -0
  113. package/dist/build/vite/plugins/server-config.d.ts.map +1 -0
  114. package/dist/build/vite/plugins/server-config.js +23 -0
  115. package/dist/build/vite/plugins/sitemap.d.ts +3 -0
  116. package/dist/build/vite/plugins/sitemap.d.ts.map +1 -0
  117. package/dist/build/vite/plugins/sitemap.js +73 -0
  118. package/dist/build/vite/plugins/translations.d.ts +9 -0
  119. package/dist/build/vite/plugins/translations.d.ts.map +1 -0
  120. package/dist/build/vite/plugins/translations.js +61 -0
  121. package/dist/cli/bundle.json +10 -0
  122. package/dist/cli/index.js +106724 -0
  123. package/dist/runtime/app/FimoProviders.d.ts +16 -0
  124. package/dist/runtime/app/FimoProviders.d.ts.map +1 -0
  125. package/dist/runtime/app/FimoProviders.js +75 -0
  126. package/dist/runtime/app/FimoScripts.d.ts +14 -0
  127. package/dist/runtime/app/FimoScripts.d.ts.map +1 -0
  128. package/dist/runtime/app/FimoScripts.js +48 -0
  129. package/dist/runtime/app/index.d.ts +4 -0
  130. package/dist/runtime/app/index.d.ts.map +1 -0
  131. package/dist/runtime/app/index.js +2 -0
  132. package/dist/runtime/app/prefetch.d.ts +6 -0
  133. package/dist/runtime/app/prefetch.d.ts.map +1 -0
  134. package/dist/runtime/app/prefetch.js +74 -0
  135. package/dist/runtime/content/DataSourcesProvider.d.ts +4 -0
  136. package/dist/runtime/content/DataSourcesProvider.d.ts.map +1 -0
  137. package/dist/runtime/content/DataSourcesProvider.js +63 -0
  138. package/dist/runtime/content/RefreshContentProvider.d.ts +3 -0
  139. package/dist/runtime/content/RefreshContentProvider.d.ts.map +1 -0
  140. package/dist/runtime/content/RefreshContentProvider.js +22 -0
  141. package/dist/runtime/content/utils/retrieve-data-sources.d.ts +15 -0
  142. package/dist/runtime/content/utils/retrieve-data-sources.d.ts.map +1 -0
  143. package/dist/runtime/content/utils/retrieve-data-sources.js +70 -0
  144. package/dist/runtime/core/AppErrorBoundary.d.ts +10 -0
  145. package/dist/runtime/core/AppErrorBoundary.d.ts.map +1 -0
  146. package/dist/runtime/core/AppErrorBoundary.js +27 -0
  147. package/dist/runtime/core/ViteLifecycles.d.ts +5 -0
  148. package/dist/runtime/core/ViteLifecycles.d.ts.map +1 -0
  149. package/dist/runtime/core/ViteLifecycles.js +31 -0
  150. package/dist/runtime/iframe/ConsoleErrorProvider.d.ts +3 -0
  151. package/dist/runtime/iframe/ConsoleErrorProvider.d.ts.map +1 -0
  152. package/dist/runtime/iframe/ConsoleErrorProvider.js +103 -0
  153. package/dist/runtime/iframe/ScreenshotProvider.d.ts +3 -0
  154. package/dist/runtime/iframe/ScreenshotProvider.d.ts.map +1 -0
  155. package/dist/runtime/iframe/ScreenshotProvider.js +60 -0
  156. package/dist/runtime/iframe/runtime/overlay-hmr.d.ts +2 -0
  157. package/dist/runtime/iframe/runtime/overlay-hmr.d.ts.map +1 -0
  158. package/dist/runtime/iframe/runtime/overlay-hmr.js +24 -0
  159. package/dist/runtime/iframe/runtime/overlay-runtime.d.ts +2 -0
  160. package/dist/runtime/iframe/runtime/overlay-runtime.d.ts.map +1 -0
  161. package/dist/runtime/iframe/runtime/overlay-runtime.js +103 -0
  162. package/dist/runtime/index.d.ts +11 -0
  163. package/dist/runtime/index.d.ts.map +1 -0
  164. package/dist/runtime/index.js +14 -0
  165. package/dist/runtime/paths/get-fimo-paths.d.ts +31 -0
  166. package/dist/runtime/paths/get-fimo-paths.d.ts.map +1 -0
  167. package/dist/runtime/paths/get-fimo-paths.js +164 -0
  168. package/dist/runtime/paths/index.d.ts +3 -0
  169. package/dist/runtime/paths/index.d.ts.map +1 -0
  170. package/dist/runtime/paths/index.js +1 -0
  171. package/dist/runtime/paths/types.d.ts +28 -0
  172. package/dist/runtime/paths/types.d.ts.map +1 -0
  173. package/dist/runtime/paths/types.js +1 -0
  174. package/dist/runtime/primitives/components/Boolean.d.ts +29 -0
  175. package/dist/runtime/primitives/components/Boolean.d.ts.map +1 -0
  176. package/dist/runtime/primitives/components/Boolean.js +33 -0
  177. package/dist/runtime/primitives/components/Date.d.ts +30 -0
  178. package/dist/runtime/primitives/components/Date.d.ts.map +1 -0
  179. package/dist/runtime/primitives/components/Date.js +43 -0
  180. package/dist/runtime/primitives/components/Image.d.ts +72 -0
  181. package/dist/runtime/primitives/components/Image.d.ts.map +1 -0
  182. package/dist/runtime/primitives/components/Image.js +108 -0
  183. package/dist/runtime/primitives/components/RichText.d.ts +9 -0
  184. package/dist/runtime/primitives/components/RichText.d.ts.map +1 -0
  185. package/dist/runtime/primitives/components/RichText.js +135 -0
  186. package/dist/runtime/primitives/components/StaticImage.d.ts +44 -0
  187. package/dist/runtime/primitives/components/StaticImage.d.ts.map +1 -0
  188. package/dist/runtime/primitives/components/StaticImage.js +73 -0
  189. package/dist/runtime/primitives/components/Text.d.ts +24 -0
  190. package/dist/runtime/primitives/components/Text.d.ts.map +1 -0
  191. package/dist/runtime/primitives/components/Text.js +35 -0
  192. package/dist/runtime/primitives/components/Video.d.ts +31 -0
  193. package/dist/runtime/primitives/components/Video.d.ts.map +1 -0
  194. package/dist/runtime/primitives/components/Video.js +34 -0
  195. package/dist/runtime/primitives/components/index.d.ts +6 -0
  196. package/dist/runtime/primitives/components/index.d.ts.map +1 -0
  197. package/dist/runtime/primitives/components/index.js +5 -0
  198. package/dist/runtime/primitives/index.d.ts +11 -0
  199. package/dist/runtime/primitives/index.d.ts.map +1 -0
  200. package/dist/runtime/primitives/index.js +14 -0
  201. package/dist/runtime/primitives/lib/image-optimization.d.ts +50 -0
  202. package/dist/runtime/primitives/lib/image-optimization.d.ts.map +1 -0
  203. package/dist/runtime/primitives/lib/image-optimization.js +183 -0
  204. package/dist/runtime/primitives/lib/index.d.ts +5 -0
  205. package/dist/runtime/primitives/lib/index.d.ts.map +1 -0
  206. package/dist/runtime/primitives/lib/index.js +4 -0
  207. package/dist/runtime/primitives/lib/primitives.d.ts +179 -0
  208. package/dist/runtime/primitives/lib/primitives.d.ts.map +1 -0
  209. package/dist/runtime/primitives/lib/primitives.js +224 -0
  210. package/dist/runtime/primitives/lib/query.d.ts +46 -0
  211. package/dist/runtime/primitives/lib/query.d.ts.map +1 -0
  212. package/dist/runtime/primitives/lib/query.js +1 -0
  213. package/dist/runtime/primitives/lib/template.d.ts +13 -0
  214. package/dist/runtime/primitives/lib/template.d.ts.map +1 -0
  215. package/dist/runtime/primitives/lib/template.js +26 -0
  216. package/dist/runtime/primitives/lib/useFimoLiveValue.d.ts +22 -0
  217. package/dist/runtime/primitives/lib/useFimoLiveValue.d.ts.map +1 -0
  218. package/dist/runtime/primitives/lib/useFimoLiveValue.js +49 -0
  219. package/dist/runtime/primitives/translations.d.ts +20 -0
  220. package/dist/runtime/primitives/translations.d.ts.map +1 -0
  221. package/dist/runtime/primitives/translations.js +25 -0
  222. package/dist/runtime/react-router/index.d.ts +8 -0
  223. package/dist/runtime/react-router/index.d.ts.map +1 -0
  224. package/dist/runtime/react-router/index.js +15 -0
  225. package/dist/runtime/routing/AppResetScrollOnNavigate.d.ts +2 -0
  226. package/dist/runtime/routing/AppResetScrollOnNavigate.d.ts.map +1 -0
  227. package/dist/runtime/routing/AppResetScrollOnNavigate.js +12 -0
  228. package/dist/runtime/routing/AppRoutesProvider.d.ts +25 -0
  229. package/dist/runtime/routing/AppRoutesProvider.d.ts.map +1 -0
  230. package/dist/runtime/routing/AppRoutesProvider.js +92 -0
  231. package/dist/runtime/seo/buildSeoForPath.d.ts +76 -0
  232. package/dist/runtime/seo/buildSeoForPath.d.ts.map +1 -0
  233. package/dist/runtime/seo/buildSeoForPath.js +52 -0
  234. package/dist/runtime/seo/htmlProps.d.ts +10 -0
  235. package/dist/runtime/seo/htmlProps.d.ts.map +1 -0
  236. package/dist/runtime/seo/htmlProps.js +14 -0
  237. package/dist/runtime/seo/index.d.ts +9 -0
  238. package/dist/runtime/seo/index.d.ts.map +1 -0
  239. package/dist/runtime/seo/index.js +8 -0
  240. package/dist/runtime/seo/resolver.d.ts +39 -0
  241. package/dist/runtime/seo/resolver.d.ts.map +1 -0
  242. package/dist/runtime/seo/resolver.js +144 -0
  243. package/dist/runtime/shared/fimo-config.server.d.ts +8 -0
  244. package/dist/runtime/shared/fimo-config.server.d.ts.map +1 -0
  245. package/dist/runtime/shared/fimo-config.server.js +39 -0
  246. package/dist/runtime/shared/fimo-config.types.d.ts +40 -0
  247. package/dist/runtime/shared/fimo-config.types.d.ts.map +1 -0
  248. package/dist/runtime/shared/fimo-config.types.js +1 -0
  249. package/dist/runtime/t/OptionalTracking.d.ts +13 -0
  250. package/dist/runtime/t/OptionalTracking.d.ts.map +1 -0
  251. package/dist/runtime/t/OptionalTracking.js +28 -0
  252. package/dist/runtime/t/OptionalTracking.test.d.ts +2 -0
  253. package/dist/runtime/t/OptionalTracking.test.d.ts.map +1 -0
  254. package/dist/runtime/t/OptionalTracking.test.js +11 -0
  255. package/dist/runtime/t/Pageview.d.ts +9 -0
  256. package/dist/runtime/t/Pageview.d.ts.map +1 -0
  257. package/dist/runtime/t/Pageview.js +118 -0
  258. package/dist/runtime/templates.d.ts +62 -0
  259. package/dist/runtime/templates.d.ts.map +1 -0
  260. package/dist/runtime/templates.js +145 -0
  261. package/dist/scripts/export-static.d.ts +12 -0
  262. package/dist/scripts/export-static.js +47 -0
  263. package/dist/scripts/extract-translations.d.ts +13 -0
  264. package/dist/scripts/extract-translations.js +124 -0
  265. package/dist/scripts/inject-translations.d.ts +6 -0
  266. package/dist/scripts/inject-translations.js +168 -0
  267. package/dist/scripts/lib/parse-routes-file.d.ts +14 -0
  268. package/dist/scripts/lib/parse-routes-file.js +43 -0
  269. package/dist/scripts/lib/validation-report.d.ts +44 -0
  270. package/dist/scripts/lib/validation-report.js +84 -0
  271. package/dist/scripts/lint-translation-keys.d.ts +13 -0
  272. package/dist/scripts/lint-translation-keys.js +137 -0
  273. package/dist/scripts/resolve-routes.d.ts +9 -0
  274. package/dist/scripts/resolve-routes.js +222 -0
  275. package/dist/scripts/sync-forms.d.ts +10 -0
  276. package/dist/scripts/sync-forms.js +125 -0
  277. package/dist/scripts/sync-schemas.d.ts +10 -0
  278. package/dist/scripts/sync-schemas.js +107 -0
  279. package/dist/scripts/utils/assets.d.ts +1 -0
  280. package/dist/scripts/utils/assets.js +14 -0
  281. package/dist/scripts/validate-route-metadata.d.ts +8 -0
  282. package/dist/scripts/validate-route-metadata.js +139 -0
  283. package/package.json +99 -6
  284. package/release.json +5 -0
  285. package/scripts/add-ext.mjs +47 -0
  286. package/scripts/bump-version.mjs +77 -0
  287. package/scripts/bundle-cli.mjs +123 -0
  288. package/scripts/install-cli.mjs +36 -0
  289. package/scripts/lib/channels.mjs +69 -0
  290. package/scripts/lib/cleanup-release.mjs +64 -0
  291. package/scripts/lib/cleanup-release.test.ts +142 -0
  292. package/scripts/lib/postinstall-onboarding.mjs +40 -0
  293. package/scripts/lib/postinstall-onboarding.test.ts +82 -0
  294. package/scripts/lib/release-core.mjs +122 -0
  295. package/scripts/postinstall.mjs +132 -0
  296. package/scripts/publish-npm.mjs +136 -0
  297. package/scripts/publish-tarball.mjs +245 -0
  298. package/templates/react-router/_gitignore +14 -0
  299. package/templates/react-router/components.json +21 -0
  300. package/templates/react-router/fimo-config.json +23 -0
  301. package/templates/react-router/index.html +15 -0
  302. package/templates/react-router/package.json +63 -0
  303. package/templates/react-router/pnpm-workspace.yaml +3 -0
  304. package/templates/react-router/public/favicon.ico +0 -0
  305. package/templates/react-router/public/favicon.svg +4 -0
  306. package/templates/react-router/public/robots.txt +4 -0
  307. package/templates/react-router/react-router.config.ts +17 -0
  308. package/templates/react-router/src/components/ui/accordion.tsx +51 -0
  309. package/templates/react-router/src/components/ui/alert-dialog.tsx +163 -0
  310. package/templates/react-router/src/components/ui/alert.tsx +49 -0
  311. package/templates/react-router/src/components/ui/aspect-ratio.tsx +9 -0
  312. package/templates/react-router/src/components/ui/avatar.tsx +87 -0
  313. package/templates/react-router/src/components/ui/badge.tsx +40 -0
  314. package/templates/react-router/src/components/ui/breadcrumb.tsx +92 -0
  315. package/templates/react-router/src/components/ui/button-group.tsx +75 -0
  316. package/templates/react-router/src/components/ui/button.tsx +62 -0
  317. package/templates/react-router/src/components/ui/calendar.tsx +159 -0
  318. package/templates/react-router/src/components/ui/card.tsx +56 -0
  319. package/templates/react-router/src/components/ui/carousel.tsx +216 -0
  320. package/templates/react-router/src/components/ui/chart.tsx +306 -0
  321. package/templates/react-router/src/components/ui/checkbox.tsx +29 -0
  322. package/templates/react-router/src/components/ui/collapsible.tsx +15 -0
  323. package/templates/react-router/src/components/ui/combobox.tsx +267 -0
  324. package/templates/react-router/src/components/ui/command.tsx +137 -0
  325. package/templates/react-router/src/components/ui/context-menu.tsx +211 -0
  326. package/templates/react-router/src/components/ui/dialog.tsx +136 -0
  327. package/templates/react-router/src/components/ui/direction.tsx +18 -0
  328. package/templates/react-router/src/components/ui/drawer.tsx +106 -0
  329. package/templates/react-router/src/components/ui/dropdown-menu.tsx +219 -0
  330. package/templates/react-router/src/components/ui/empty.tsx +85 -0
  331. package/templates/react-router/src/components/ui/field.tsx +226 -0
  332. package/templates/react-router/src/components/ui/form.tsx +136 -0
  333. package/templates/react-router/src/components/ui/hover-card.tsx +36 -0
  334. package/templates/react-router/src/components/ui/input-group.tsx +147 -0
  335. package/templates/react-router/src/components/ui/input-otp.tsx +68 -0
  336. package/templates/react-router/src/components/ui/input.tsx +21 -0
  337. package/templates/react-router/src/components/ui/item.tsx +158 -0
  338. package/templates/react-router/src/components/ui/kbd.tsx +22 -0
  339. package/templates/react-router/src/components/ui/label.tsx +19 -0
  340. package/templates/react-router/src/components/ui/menubar.tsx +236 -0
  341. package/templates/react-router/src/components/ui/native-select.tsx +52 -0
  342. package/templates/react-router/src/components/ui/navigation-menu.tsx +142 -0
  343. package/templates/react-router/src/components/ui/pagination.tsx +100 -0
  344. package/templates/react-router/src/components/ui/popover.tsx +52 -0
  345. package/templates/react-router/src/components/ui/progress.tsx +24 -0
  346. package/templates/react-router/src/components/ui/radio-group.tsx +31 -0
  347. package/templates/react-router/src/components/ui/resizable.tsx +47 -0
  348. package/templates/react-router/src/components/ui/scroll-area.tsx +46 -0
  349. package/templates/react-router/src/components/ui/select.tsx +162 -0
  350. package/templates/react-router/src/components/ui/separator.tsx +26 -0
  351. package/templates/react-router/src/components/ui/sheet.tsx +107 -0
  352. package/templates/react-router/src/components/ui/sidebar.tsx +675 -0
  353. package/templates/react-router/src/components/ui/skeleton.tsx +7 -0
  354. package/templates/react-router/src/components/ui/slider.tsx +54 -0
  355. package/templates/react-router/src/components/ui/sonner.tsx +34 -0
  356. package/templates/react-router/src/components/ui/spinner.tsx +9 -0
  357. package/templates/react-router/src/components/ui/switch.tsx +33 -0
  358. package/templates/react-router/src/components/ui/table.tsx +78 -0
  359. package/templates/react-router/src/components/ui/tabs.tsx +69 -0
  360. package/templates/react-router/src/components/ui/textarea.tsx +18 -0
  361. package/templates/react-router/src/components/ui/toggle-group.tsx +80 -0
  362. package/templates/react-router/src/components/ui/toggle.tsx +41 -0
  363. package/templates/react-router/src/components/ui/tooltip.tsx +42 -0
  364. package/templates/react-router/src/hooks/use-mobile.ts +19 -0
  365. package/templates/react-router/src/index.css +147 -0
  366. package/templates/react-router/src/lib/utils.ts +6 -0
  367. package/templates/react-router/src/pages/Index.tsx +108 -0
  368. package/templates/react-router/src/pages/Privacy.tsx +79 -0
  369. package/templates/react-router/src/pages/Terms.tsx +48 -0
  370. package/templates/react-router/src/pages/legal/LegalPage.tsx +42 -0
  371. package/templates/react-router/src/root.tsx +53 -0
  372. package/templates/react-router/src/routes.ts +13 -0
  373. package/templates/react-router/src/vite-env.d.ts +11 -0
  374. package/templates/react-router/translations/en.json +1 -0
  375. package/templates/react-router/tsconfig.json +35 -0
  376. package/templates/react-router/vite.config.ts +63 -0
@@ -0,0 +1,39 @@
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}/envs/${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
+ });
@@ -0,0 +1,53 @@
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
+ });
@@ -0,0 +1,39 @@
1
+ # SEO audit
2
+
3
+ Audit the project's pages for SEO health and write a markdown 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
+ Write `reports/seo-audit-<YYYY-MM-DD>.md` with:
26
+
27
+ - An executive summary (score / 100, top 3 issues)
28
+ - A per-page table with status chips (✅ / ⚠️ / ❌)
29
+ - A "fix recipes" appendix linking each failure to the smallest change
30
+ that would resolve it
31
+ - A diff-friendly machine-readable footer (`<!-- agent-summary: ... -->`)
32
+ so subsequent runs can show the trend
33
+
34
+ ## What NOT to do
35
+
36
+ - Don't modify any project files. This audit is read-only.
37
+ - Don't fetch external URLs other than the project's own domain.
38
+ - Don't include traffic / conversion data in the report — it lives
39
+ elsewhere in the Fimo dashboard.
@@ -0,0 +1,5 @@
1
+ # Read-only audit — no writes to project files or CMS.
2
+ - files:read
3
+ - cms:read
4
+ - net:fetch
5
+ - fimo:analytics:read
@@ -0,0 +1,13 @@
1
+ version: 1
2
+ description: "Audit the project's pages for SEO health and write a markdown report."
3
+ # Read from main env. Switch to prod when running the audit against the
4
+ # live site (M13 keeps data_sources here so M16's sandbox adapter can
5
+ # bind the right Fimo API base URL).
6
+ data_sources:
7
+ - main
8
+
9
+ # Manual-only in v1. Add a schedule trigger when you're ready to run
10
+ # this nightly — make sure fimo.config.json has
11
+ # `agents.notifications.email_recipients` set before activating.
12
+ triggers:
13
+ - type: manual
@@ -0,0 +1,28 @@
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.
@@ -0,0 +1,30 @@
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.
@@ -0,0 +1,82 @@
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
+ });
@@ -0,0 +1,39 @@
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
+ });
@@ -0,0 +1,46 @@
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 (env):
8
+ * - FIMO_PROJECT_ID — provided by the runtime (M16 sandbox adapter)
9
+ * - FIMO_ENV — provided by the runtime
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}/envs/${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
+ });
@@ -0,0 +1,82 @@
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
+ });
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Tool: score
3
+ *
4
+ * Apply the SEO scoring methodology (see references/scoring-methodology.md)
5
+ * to a list of `(path, SEOSignals)` tuples and produce a per-page report.
6
+ *
7
+ * Inputs (stdin JSON): { signals: Record<path, SEOSignals> }
8
+ * Outputs (stdout JSON): { score: 0..100, pages: PageReport[] }
9
+ */
10
+
11
+ interface SEOSignals {
12
+ title: string | null;
13
+ description: string | null;
14
+ canonical: string | null;
15
+ robots: string | null;
16
+ openGraph: Record<string, string>;
17
+ schemaOrg: unknown[];
18
+ headings: { h1: string[]; h2: string[]; h3: string[] };
19
+ }
20
+
21
+ interface Finding {
22
+ rule: string;
23
+ severity: 'ok' | 'warn' | 'fail';
24
+ message: string;
25
+ }
26
+
27
+ interface PageReport {
28
+ path: string;
29
+ score: number;
30
+ findings: Finding[];
31
+ }
32
+
33
+ function audit(signals: SEOSignals): Finding[] {
34
+ const out: Finding[] = [];
35
+
36
+ // Title
37
+ if (!signals.title) {
38
+ out.push({ rule: 'title-present', severity: 'fail', message: 'Missing <title>' });
39
+ } else if (signals.title.length < 30 || signals.title.length > 60) {
40
+ out.push({
41
+ rule: 'title-length',
42
+ severity: 'warn',
43
+ message: `Title length ${signals.title.length} chars — recommend 30–60`,
44
+ });
45
+ } else {
46
+ out.push({ rule: 'title', severity: 'ok', message: 'Title looks good' });
47
+ }
48
+
49
+ // Description
50
+ if (!signals.description) {
51
+ out.push({ rule: 'description-present', severity: 'fail', message: 'Missing meta description' });
52
+ } else if (signals.description.length < 50 || signals.description.length > 160) {
53
+ out.push({
54
+ rule: 'description-length',
55
+ severity: 'warn',
56
+ message: `Description length ${signals.description.length} chars — recommend 50–160`,
57
+ });
58
+ }
59
+
60
+ // OG tags
61
+ for (const key of ['title', 'description', 'image', 'url']) {
62
+ if (!signals.openGraph[key]) {
63
+ out.push({ rule: `og-${key}`, severity: 'warn', message: `Missing og:${key}` });
64
+ }
65
+ }
66
+
67
+ // Headings
68
+ if (signals.headings.h1.length === 0) {
69
+ out.push({ rule: 'h1-present', severity: 'fail', message: 'No <h1> on the page' });
70
+ } else if (signals.headings.h1.length > 1) {
71
+ out.push({ rule: 'h1-single', severity: 'warn', message: `Multiple <h1> tags (${signals.headings.h1.length})` });
72
+ }
73
+
74
+ // Canonical
75
+ if (!signals.canonical) {
76
+ out.push({ rule: 'canonical', severity: 'warn', message: 'Missing canonical link' });
77
+ }
78
+
79
+ // Robots
80
+ if (signals.robots && /noindex/i.test(signals.robots)) {
81
+ out.push({ rule: 'robots-noindex', severity: 'fail', message: 'Page is marked noindex' });
82
+ }
83
+
84
+ // Schema.org
85
+ if (signals.schemaOrg.length === 0) {
86
+ out.push({ rule: 'schema-org', severity: 'warn', message: 'No JSON-LD schema.org block' });
87
+ }
88
+
89
+ return out;
90
+ }
91
+
92
+ function scoreOf(findings: Finding[]): number {
93
+ // Each fail = -15, each warn = -5. Floor at 0.
94
+ let pts = 100;
95
+ for (const f of findings) {
96
+ if (f.severity === 'fail') {
97
+ pts -= 15;
98
+ } else if (f.severity === 'warn') {
99
+ pts -= 5;
100
+ }
101
+ }
102
+ return Math.max(0, pts);
103
+ }
104
+
105
+ async function readStdin(): Promise<string> {
106
+ const chunks: Buffer[] = [];
107
+ for await (const chunk of process.stdin) {
108
+ chunks.push(Buffer.from(chunk));
109
+ }
110
+ return Buffer.concat(chunks).toString('utf8');
111
+ }
112
+
113
+ async function main(): Promise<void> {
114
+ const text = await readStdin();
115
+ const payload = JSON.parse(text) as { signals: Record<string, SEOSignals> };
116
+
117
+ const pages: PageReport[] = [];
118
+ for (const [path, signals] of Object.entries(payload.signals)) {
119
+ const findings = audit(signals);
120
+ pages.push({ path, score: scoreOf(findings), findings });
121
+ }
122
+
123
+ const overall = pages.length === 0 ? 0 : Math.round(pages.reduce((a, p) => a + p.score, 0) / pages.length);
124
+
125
+ process.stdout.write(JSON.stringify({ score: overall, pages }, null, 2));
126
+ }
127
+
128
+ main().catch((err) => {
129
+ console.error(err);
130
+ process.exit(1);
131
+ });
@@ -0,0 +1,54 @@
1
+ # Fimo project rules
2
+
3
+ This is a Fimo project. Fimo is a content + media + forms + analytics platform for React websites with a CMS dashboard.
4
+
5
+ ## Working with this project
6
+
7
+ When generating or modifying code in this Fimo project, your AI tool's `fimo` skill should load these references up front (they define the load-bearing primitives):
8
+
9
+ - `content.md` — schemas + entries
10
+ - `forms.md` — forms
11
+ - `assets.md` — media + AI image generation
12
+ - `translations.md` — `t()` and i18n
13
+ - `ui.md` — `fimo/ui` components + JSX conventions
14
+
15
+ The `fimo` skill describes how to build inside this project; the `fimo-cli` skill covers the `fimo` CLI itself (create, account, agent setup).
16
+
17
+ **If your AI tool doesn't seem to know about Fimo, install the skills:**
18
+
19
+ ```bash
20
+ fimo skills install
21
+ # or, without a global fimo install, copy skills out of the npx cache:
22
+ npx fimo@latest skills install --mode=copy
23
+ ```
24
+
25
+ To add another AI tool to this project (e.g., a teammate joins on Cursor or Windsurf) — global skill + project skill + rule file in one shot:
26
+
27
+ ```bash
28
+ fimo skills install --agents=cursor # explicit
29
+ fimo skills install # interactive: pick agents to add
30
+ ```
31
+
32
+ If you later edit this `AGENTS.md` and want the per-agent variants (`CLAUDE.md`, `.cursor/rules/fimo.mdc`, `.windsurfrules`, `GEMINI.md`) regenerated from it, delete the stale variant file and run `fimo rules sync`. Sync is purely additive — it never overwrites existing files, so project-specific edits are preserved unless you remove the file first.
33
+
34
+ ## Critical project rules
35
+
36
+ - **Deploy at meaningful milestones, not after every edit.** `fimo deploy` commits everything, pushes to `main`, and round-trips the sandbox — it's a finishing step, not a save step. For initial site generation, run it once at the end when the site is complete. For follow-up work, batch related edits and deploy when the user-visible unit of work is done.
37
+ - **Run `fimo deploy --publish` only when the user signals "ship it"** — first-time site completion or an explicit "publish" / "go live" request. Plain `fimo deploy` (preview) is the right default; `--publish` is the visible, externally-facing step.
38
+ - Do not add a second deploy tool (Vercel, Netlify, etc.).
39
+ - **Never edit `.fimo.settings.json`.** It is the link between this folder and the remote project — it is written once by `fimo create`.
40
+ - The scaffolded homepage is disposable starter content. On the first real generation, replace its copy, layout, and visual style instead of extending the Fimo-branded design. Keep only the technical shell and required legal pages unless the user explicitly asks to preserve the starter.
41
+ - **Secrets live in the Fimo dashboard**, not in `.env` committed to git.
42
+ - **Read before writing backend code.** Check the `fimo` skill's `references/<topic>.md` before calling the tenant API; the shape may have changed.
43
+ - **Always pass flags to `fimo` commands when scripting** — `fimo deploy -m "<msg>"`, `fimo create <dir> --org <id> --no-install --agents=<list>`, `fimo switch --org <id>`, `fimo delete -y`. Bare interactive forms hang in non-TTY shells.
44
+
45
+ ## Design system (optional)
46
+
47
+ If a `design.md` file exists at the project root, treat it as the source of truth for all visual decisions:
48
+
49
+ - Use only the colors, fonts, spacing, and radius tokens defined there.
50
+ - Do not invent new values or fall back to framework defaults (Tailwind defaults, shadcn defaults, etc.).
51
+ - Match component states (hover, focus, active, disabled) to the patterns specified.
52
+ - Follow the typographic scale and weight assignments verbatim.
53
+
54
+ If `design.md` is absent, fall back to the `frontend-design` skill's guidance.
@@ -0,0 +1,19 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(fimo:*)",
5
+ "Bash(npx fimo:*)",
6
+ "Bash(pnpm:*)",
7
+ "Bash(pnpm dlx:*)",
8
+ "Bash(npx:*)",
9
+ "Bash(git status)",
10
+ "Bash(git status:*)",
11
+ "Bash(git diff)",
12
+ "Bash(git diff:*)",
13
+ "Bash(git log:*)",
14
+ "Bash(git add:*)",
15
+ "Bash(git restore:*)",
16
+ "Bash(mkdir:*)"
17
+ ]
18
+ }
19
+ }