fimo 0.8.0 → 0.9.0-staging.1

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 (44) hide show
  1. package/assets/content-templates/hooks-template.eta +4 -2
  2. package/assets/content-templates/types-template.eta +66 -1
  3. package/assets/skills/fimo/SKILL.md +17 -54
  4. package/assets/skills/fimo-cli/SKILL.md +21 -17
  5. package/assets/skills/fimo-cli/references/agent-setup.md +36 -53
  6. package/assets/skills/fimo-cli/references/agents.md +4 -4
  7. package/assets/skills/fimo-cli/references/content.md +62 -3
  8. package/assets/skills/fimo-cli/references/deploy.md +1 -1
  9. package/assets/skills/fimo-cli/references/forms.md +1 -1
  10. package/assets/skills/fimo-cli/references/init.md +16 -24
  11. package/assets/skills/fimo-cli/references/integrations/firecrawl.md +20 -4
  12. package/assets/skills/fimo-cli/references/integrations.md +5 -1
  13. package/assets/skills/fimo-cli/references/project-vars-and-secrets.md +14 -11
  14. package/assets/skills/fimo-cli/references/projects.md +1 -1
  15. package/assets/skills/fimo-cli/references/support.md +11 -9
  16. package/assets/skills/fimo-cli/references/webhooks.md +164 -0
  17. package/assets/skills/fimo-code/SKILL.md +67 -0
  18. package/assets/skills/{fimo → fimo-code}/references/content.md +86 -15
  19. package/assets/skills/fimo-studio/SKILL.md +5 -3
  20. package/assets/skills/fimo-studio/references/cms.md +9 -1
  21. package/assets/skills/fimo-studio/references/webhooks.md +42 -0
  22. package/dist/cli/bundle.json +1 -1
  23. package/dist/cli/index.js +306 -297
  24. package/dist/runtime/content/client/createContentClient.js +2 -1
  25. package/dist/runtime/content/client/internal.js +1 -0
  26. package/dist/runtime/content/client/types.d.ts +16 -0
  27. package/dist/scripts/validate-schemas.js +42 -1
  28. package/package.json +1 -1
  29. package/release.json +2 -2
  30. package/scripts/postinstall.mjs +11 -65
  31. package/templates/react-router/AGENTS.md +44 -0
  32. package/templates/react-router/CLAUDE.md +5 -0
  33. package/templates/react-router/package.json +1 -1
  34. package/assets/agents-starter/AGENTS.md +0 -66
  35. /package/assets/skills/{fimo → fimo-code}/assets/react-router-locales/navigation.tsx +0 -0
  36. /package/assets/skills/{fimo → fimo-code}/assets/react-router-locales/routes.ts +0 -0
  37. /package/assets/skills/{fimo → fimo-code}/references/assets.md +0 -0
  38. /package/assets/skills/{fimo → fimo-code}/references/design.md +0 -0
  39. /package/assets/skills/{fimo → fimo-code}/references/forms.md +0 -0
  40. /package/assets/skills/{fimo → fimo-code}/references/labels.md +0 -0
  41. /package/assets/skills/{fimo → fimo-code}/references/locales.md +0 -0
  42. /package/assets/skills/{fimo → fimo-code}/references/setup-plain-vite.md +0 -0
  43. /package/assets/skills/{fimo → fimo-code}/references/setup-react-router.md +0 -0
  44. /package/assets/skills/{fimo → fimo-code}/references/ui.md +0 -0
@@ -100,10 +100,11 @@ export function createContentClient(config) {
100
100
  return (await list(params)).items;
101
101
  }
102
102
  async function create(payload) {
103
+ const localizedPayload = info.localized ? withMutationLocale(payload, readCollectionLocale({})) : payload;
103
104
  const res = await fetch(`${apiBase()}/entries/${info.uid}`, {
104
105
  method: 'POST',
105
106
  headers: fimoHeaders({ 'Content-Type': 'application/json' }),
106
- body: JSON.stringify(splitWritePayload(payload)),
107
+ body: JSON.stringify(splitWritePayload(localizedPayload)),
107
108
  });
108
109
  if (!res.ok) {
109
110
  throw new Error(await res.text());
@@ -179,6 +179,7 @@ function wrapReference(value, uid, resolve) {
179
179
  function wrapField(field, value, source, resolve) {
180
180
  switch (field.type) {
181
181
  case 'string':
182
+ case 'text':
182
183
  return value != null ? new FimoString(value, source) : value;
183
184
  case 'richtext':
184
185
  return value != null ? new FimoRichText(value, source) : value;
@@ -18,6 +18,7 @@ export type ContentResult<T extends {
18
18
  export interface ContentSchemaField {
19
19
  type?: string;
20
20
  required?: boolean;
21
+ default?: unknown;
21
22
  /** Media fields: multiple assets. */
22
23
  multiple?: boolean;
23
24
  /** Reference fields: to-many. */
@@ -26,6 +27,21 @@ export interface ContentSchemaField {
26
27
  target?: string;
27
28
  /** Polymorphic reference fields: allowed target schema uids. */
28
29
  targets?: readonly string[];
30
+ min?: number;
31
+ max?: number;
32
+ minLength?: number;
33
+ maxLength?: number;
34
+ regex?: string;
35
+ schema?: {
36
+ fields: Readonly<Record<string, ContentSchemaField>>;
37
+ } | {
38
+ oneOf: readonly {
39
+ uid: string;
40
+ label: string;
41
+ description?: string;
42
+ fields: Readonly<Record<string, ContentSchemaField>>;
43
+ }[];
44
+ };
29
45
  }
30
46
  export interface ContentSchema {
31
47
  uid: string;
@@ -1,6 +1,6 @@
1
1
  import { existsSync, readFileSync, readdirSync } from 'node:fs';
2
2
  import { basename, join, resolve } from 'node:path';
3
- import { CONTENT_FIELD_NAME_PATTERN, CONTENT_FIELD_NAME_PATTERN_DESCRIPTION, CONTENT_TYPE_NAME_PATTERN, CONTENT_TYPE_NAME_PATTERN_DESCRIPTION, FieldTypeSchema, RESERVED_CONTENT_FIELD_NAMES, } from '@fimo/api-contracts';
3
+ import { CONTENT_FIELD_NAME_PATTERN, CONTENT_FIELD_NAME_PATTERN_DESCRIPTION, CONTENT_TYPE_NAME_PATTERN, CONTENT_TYPE_NAME_PATTERN_DESCRIPTION, ContentFieldDefinitionSchema, FieldTypeSchema, MAX_STRUCTURED_CONTENT_DEPTH, RESERVED_CONTENT_FIELD_NAMES, walkContentFields, } from '@fimo/api-contracts';
4
4
  /**
5
5
  * Validate `src/schemas/*.json` locally against the same rules the API's
6
6
  * schema validator enforces (field types, reserved field names, required
@@ -169,6 +169,47 @@ export function validateSchemaFile(absolutePath, file, schemaUids = new Set()) {
169
169
  file,
170
170
  });
171
171
  }
172
+ if (type === 'blocks') {
173
+ const parsed = ContentFieldDefinitionSchema.safeParse(definition);
174
+ if (!parsed.success) {
175
+ for (const issue of parsed.error.issues) {
176
+ errors.push({
177
+ rule: 'schemas/structured-content-shape',
178
+ message: `Field "${fieldName}"${issue.path.length > 0 ? ` at ${issue.path.join('.')}` : ''}: ${issue.message}`,
179
+ file,
180
+ });
181
+ }
182
+ continue;
183
+ }
184
+ walkContentFields({ [fieldName]: parsed.data }, ({ field, path, structuredDepth, insideStructuredContent }) => {
185
+ if (field.type === 'blocks' && structuredDepth >= MAX_STRUCTURED_CONTENT_DEPTH) {
186
+ errors.push({
187
+ rule: 'schemas/structured-content-depth',
188
+ message: `Field "${path.join('.')}" exceeds the ${MAX_STRUCTURED_CONTENT_DEPTH}-level blocks limit.`,
189
+ file,
190
+ });
191
+ }
192
+ if (!insideStructuredContent || field.type !== 'reference') {
193
+ return;
194
+ }
195
+ if (field.targets) {
196
+ errors.push({
197
+ rule: 'schemas/structured-content-reference',
198
+ message: `Reference field "${path.join('.')}" must use one fixed "target" inside blocks.`,
199
+ file,
200
+ });
201
+ return;
202
+ }
203
+ if (field.target && schemaUids.size > 0 && !schemaUids.has(field.target)) {
204
+ errors.push({
205
+ rule: 'schemas/reference-target',
206
+ message: `Reference field "${path.join('.')}" targets unknown schema "${field.target}".`,
207
+ file,
208
+ });
209
+ }
210
+ });
211
+ continue;
212
+ }
172
213
  if (type === 'reference') {
173
214
  const { target, targets, many } = definition;
174
215
  if (targets !== undefined && target !== undefined) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fimo",
3
- "version": "0.8.0",
3
+ "version": "0.9.0-staging.1",
4
4
  "description": "Fimo CLI - create, deploy, and manage Fimo projects",
5
5
  "bin": {
6
6
  "fimo": "dist/cli/index.js"
package/release.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "source": "npm",
3
- "apiUrl": "https://api.fimo.ai",
4
- "webUrl": "https://fimo.ai"
3
+ "apiUrl": "https://api.staging.fimo.team",
4
+ "webUrl": "https://staging.fimo.team"
5
5
  }
@@ -1,39 +1,17 @@
1
1
  #!/usr/bin/env node
2
- // Two paths:
2
+ // GLOBAL install (`npm i -g fimo`, `pnpm add -g fimo`, etc.)
3
+ // → print a short, non-blocking hint. We steer users toward `npx fimo@latest`
4
+ // (always current) and `fimo init`, and don't run any interactive prompt
5
+ // from a package lifecycle script.
3
6
  //
4
- // 1) GLOBAL install (`npm i -g fimo`, `pnpm add -g fimo`, etc.)
5
- // → print a short, non-blocking hint. We steer users toward
6
- // `npx fimo@latest` (always current) and `fimo init`, and don't run any
7
- // interactive prompt from a package lifecycle script.
8
- //
9
- // 2) LOCAL / project install (`pnpm install` in a project that has `fimo`
10
- // in its dependencies)
11
- // → if the install was inside a Fimo project (`.fimo.settings.json`
12
- // found by walking up from `INIT_CWD`), re-sync the project-scope
13
- // `fimo` skill into every per-agent dir the project is set up for.
14
- // The skill source is `node_modules/fimo/assets/skills/fimo/`, version-
15
- // locked to the installed package.
16
- //
17
- // Silent on no-op; non-fatal on any failure (a skill copy must
18
- // never break the user's install).
19
- //
20
- // pnpm caveat: by default, pnpm strips build scripts from non-approved deps.
21
- // Fimo-controlled installs (`fimo init --install`, sandbox `fimopm install`,
22
- // and E2E helper installs) pass a scoped allow-all-builds flag for pnpm so the
23
- // fimo postinstall hook can refresh project skills. The template intentionally
24
- // does not persist allowlists in package.json / pnpm-workspace.yaml because
25
- // those conflict with sandbox install flags and drift when deps change. npm /
26
- // yarn run lifecycle scripts by default and need no extra config.
27
-
28
- import fs from 'node:fs';
29
- import path from 'node:path';
30
- import { fileURLToPath } from 'node:url';
7
+ // LOCAL / project install (`pnpm install` in a project that depends on `fimo`)
8
+ // → nothing to do. Fimo no longer writes skills into projects: the only skill
9
+ // is a tiny global `fimo` bootstrap installed by `fimo skills install`, and
10
+ // the rich skills are fetched on demand via `fimo skills get`. Keeping this
11
+ // path a no-op means `pnpm install` never touches the working tree.
31
12
 
32
13
  import { isGlobalInstall } from './lib/postinstall-onboarding.mjs';
33
14
 
34
- const THIS_FILE = fileURLToPath(import.meta.url);
35
- const CLI_ROOT = path.resolve(path.dirname(THIS_FILE), '..');
36
-
37
15
  const ESC = String.fromCharCode(27);
38
16
  const green = (s) => `${ESC}[32m${s}${ESC}[0m`;
39
17
  const bold = (s) => `${ESC}[1m${s}${ESC}[0m`;
@@ -49,40 +27,8 @@ if (isGlobalInstall(process.env)) {
49
27
  console.log();
50
28
  console.log(` Get started:`);
51
29
  console.log(` ${cyan('fimo init')} ${dim('# sign in, create or link a project')}`);
52
- console.log(` ${cyan('fimo skills install')} ${dim('# set up skills for your AI coding tools')}`);
30
+ console.log(` ${cyan('fimo skills install')} ${dim('# set up Fimo skills for your AI coding tools')}`);
53
31
  console.log();
54
- process.exit(0);
55
32
  }
56
33
 
57
- // Project-install path: dynamic-import the built helper and re-sync the
58
- // project-scope `fimo` skill if we resolve to a Fimo project root.
59
- // Skipped silently when:
60
- // - The CLI hasn't been built yet (workspace dev install before `pnpm build`)
61
- // - `INIT_CWD` isn't inside a Fimo project (helper returns 'not-a-fimo-project')
62
- // - The project has no per-agent rule files yet (helper returns 'no-agents')
63
- const helperPath = path.join(CLI_ROOT, 'dist', 'cli', 'project-skill.js');
64
- if (!fs.existsSync(helperPath)) {
65
- process.exit(0);
66
- }
67
-
68
- try {
69
- const { syncProjectFimoSkill } = await import(helperPath);
70
- const initCwd = process.env.INIT_CWD ?? process.cwd();
71
- const result = await syncProjectFimoSkill({ cwd: initCwd });
72
-
73
- if (result.status === 'installed' && !result.skillSourceMissing) {
74
- const skillDest =
75
- result.agents.length === 1 ? result.agents[0] : `${result.agents.length} agents (${result.agents.join(', ')})`;
76
- console.log(`${green('✓')} fimo skill synced for ${skillDest}`);
77
- for (const failure of result.failures) {
78
- console.error(` ! fimo skill failed for ${failure.agent}: ${failure.error}`);
79
- }
80
- }
81
- // Other statuses (not-a-fimo-project, no-agents, skill-source-missing)
82
- // are silent — they're expected for non-Fimo deps and pre-create installs.
83
- } catch (err) {
84
- // Never break the user's install over a skill-sync failure.
85
- if (process.env.FIMO_DEBUG_POSTINSTALL === '1') {
86
- console.error('[fimo postinstall] sync failed:', err);
87
- }
88
- }
34
+ process.exit(0);
@@ -0,0 +1,44 @@
1
+ # Fimo project
2
+
3
+ This is a Fimo project — a content + media + forms + analytics platform for React websites with a CMS
4
+ dashboard. Fimo owns everything after the build: content, editing, environments, and hosting stay
5
+ editable by the whole team.
6
+
7
+ ## Load the Fimo skills before doing Fimo work
8
+
9
+ Fimo ships its skills through its CLI so they always match this project's installed version. Before
10
+ writing schema-driven code, running CLI/deploy workflows, or migrating content, load the relevant skill:
11
+
12
+ ```bash
13
+ npx fimo@latest skills list # what's available
14
+ npx fimo@latest skills get fimo-code # writing code: schemas, forms, labels, fimo/ui
15
+ npx fimo@latest skills get fimo-cli # CLI + workflows: push, deploy, sync, labels, domains
16
+ ```
17
+
18
+ Add `--ref <doc>` for a skill's reference docs. Always fetch fresh from the CLI rather than trusting a
19
+ cached copy.
20
+
21
+ ## Critical: never hardcode user-facing things
22
+
23
+ Fimo's whole point is that editors change content in the dashboard, not in code. Anything hardcoded
24
+ breaks that. Load `fimo-code` for the exact primitives, but the rule is:
25
+
26
+ - **Content** → declare a schema, render with the generated `useList()` / `useGet()` hooks + `fimo/ui`. Never `const posts = [...]` in JSX.
27
+ - **Strings** → `t('key')` + `fimo labels set`. Never `<h1>Welcome</h1>`.
28
+ - **Media** → `<Image>` / `<Video>` from `fimo/ui`. Never `<img src>`.
29
+ - **Forms** → the generated Zod schema + `submitX` helper. Never hand-roll fetch/validation.
30
+
31
+ ## Development loop
32
+
33
+ 1. Start the dev server with the project's package manager (`pnpm dev`, `npm run dev`, …) — live reload on localhost.
34
+ 2. Make edits; the running server shows them instantly. No deploy needed to see local changes.
35
+ 3. When a unit of work is done, **offer** to deploy: `fimo deploy` for a hosted preview / shareable link, or `fimo deploy --publish` to go live. Only after the developer confirms — never deploy on your own.
36
+
37
+ ## Project rules
38
+
39
+ - **Deploy is on-demand**, not a save step. Batch related edits into one deploy.
40
+ - Do not add a second deploy tool (Vercel, Netlify, etc.).
41
+ - **Never edit `.fimo.settings.json`** — it links this folder to the remote project.
42
+ - The scaffolded homepage is disposable starter content. Replace it on the first real generation.
43
+ - **Secrets live in the Fimo dashboard**, not in a committed `.env`.
44
+ - When scripting, pass flags to `fimo` commands (`fimo deploy -m "…"`, `fimo delete -y`) — bare interactive forms hang in non-TTY shells.
@@ -0,0 +1,5 @@
1
+ # Fimo project
2
+
3
+ Instructions for this project live in `AGENTS.md` (the cross-tool standard). Read it:
4
+
5
+ @AGENTS.md
@@ -21,7 +21,7 @@
21
21
  "cmdk": "^1.1.1",
22
22
  "date-fns": "^4.1.0",
23
23
  "embla-carousel-react": "^8.6.0",
24
- "fimo": "0.8.0",
24
+ "fimo": "0.9.0-staging.1",
25
25
  "input-otp": "^1.4.2",
26
26
  "isbot": "^5",
27
27
  "lucide-react": "^0.577.0",
@@ -1,66 +0,0 @@
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
- ## Development loop
35
-
36
- Work with the developer locally, not through deploys:
37
-
38
- 1. Start the app with the project's package-manager `dev` script — `pnpm dev`, `npm run dev`, `yarn dev`, or `bun dev` (match the lockfile). Run it in the background; it serves the site on localhost with live reload.
39
- 2. Ask the developer what they want to build or change.
40
- 3. Make the edits — the running dev server shows them instantly. You do not need to deploy to see local changes.
41
- 4. Iterate until they are happy with it locally.
42
- 5. When a unit of work looks done, **offer** to deploy: `fimo deploy` for a hosted preview / shareable link, or `fimo deploy --publish` to go live. Run it only after they confirm (or when they ask). Never deploy on your own.
43
-
44
- The local dev server is local (localhost). The Fimo dashboard preview and the shareable preview URL come from `fimo deploy` — so if the developer wants to see it in the dashboard or share a link, that needs a deploy.
45
-
46
- ## Critical project rules
47
-
48
- - **Deploy is on-demand — not a save step and not a mandatory final step.** `fimo deploy` commits everything, pushes to the current env's branch, and round-trips the sandbox. Iterate locally with the dev server; deploy only when the developer wants a hosted preview, a shareable link, or to go live — and only after they confirm. Batch related edits into one deploy; never deploy after every change.
49
- - **Run `fimo deploy --publish` only when the developer signals "ship it"** — an explicit "publish" / "go live" request. Plain `fimo deploy` (preview) is the default; `--publish` is the visible, externally-facing step.
50
- - Do not add a second deploy tool (Vercel, Netlify, etc.).
51
- - **Never edit `.fimo.settings.json`.** It is the link between this folder and the remote project — it is written once by `fimo create`.
52
- - 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.
53
- - **Secrets live in the Fimo dashboard**, not in `.env` committed to git.
54
- - **Read before writing backend code.** Check the `fimo` skill's `references/<topic>.md` before calling the tenant API; the shape may have changed.
55
- - **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.
56
-
57
- ## Design system (optional)
58
-
59
- If a `design.md` file exists at the project root, treat it as the source of truth for all visual decisions:
60
-
61
- - Use only the colors, fonts, spacing, and radius tokens defined there.
62
- - Do not invent new values or fall back to framework defaults (Tailwind defaults, shadcn defaults, etc.).
63
- - Match component states (hover, focus, active, disabled) to the patterns specified.
64
- - Follow the typographic scale and weight assignments verbatim.
65
-
66
- If `design.md` is absent, fall back to the `frontend-design` skill's guidance.