fimo 2.1.0 → 2.2.0-staging.10

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 (77) hide show
  1. package/README.md +3 -2
  2. package/assets/agent-templates/_default/config.yaml +1 -0
  3. package/assets/agent-templates/broken-link-sweeper/config.yaml +1 -0
  4. package/assets/agent-templates/content-translator/config.yaml +1 -0
  5. package/assets/agent-templates/content-translator/scripts/translate-entries.ts +1 -1
  6. package/assets/agent-templates/post-deploy-check/config.yaml +1 -0
  7. package/assets/agent-templates/post-deploy-check/scripts/check-logs.ts +1 -1
  8. package/assets/agent-templates/seo-audit/config.yaml +1 -0
  9. package/assets/agent-templates/seo-audit/scripts/list-routes.ts +3 -3
  10. package/assets/skills/fimo-cli/SKILL.md +5 -5
  11. package/assets/skills/fimo-cli/references/agents.md +2 -1
  12. package/assets/skills/fimo-cli/references/assets.md +4 -1
  13. package/assets/skills/fimo-cli/references/branches.md +1 -1
  14. package/assets/skills/fimo-cli/references/content.md +3 -3
  15. package/assets/skills/fimo-cli/references/deploy.md +2 -2
  16. package/assets/skills/fimo-cli/references/forms.md +3 -1
  17. package/assets/skills/fimo-cli/references/history.md +1 -0
  18. package/assets/skills/fimo-cli/references/init.md +30 -39
  19. package/assets/skills/fimo-cli/references/preview.md +5 -3
  20. package/assets/skills/fimo-cli/references/schedules.md +4 -2
  21. package/assets/skills/fimo-cli/references/webhooks.md +2 -0
  22. package/assets/skills/fimo-code/references/frameworks/next.md +4 -1
  23. package/assets/skills/fimo-code/references/frameworks/react-router.md +4 -3
  24. package/assets/skills/fimo-migration/SKILL.md +3 -3
  25. package/assets/skills/fimo-studio/references/preview-and-responsive.md +4 -1
  26. package/assets/skills/fimo-studio/references/visual-editing.md +9 -1
  27. package/dist/build/astro/config.js +2 -1
  28. package/dist/build/vite/plugins/preview-health.d.ts +4 -0
  29. package/dist/build/vite/plugins/preview-health.js +40 -0
  30. package/dist/cli/bundle.json +1 -1
  31. package/dist/cli/index.js +136 -137
  32. package/dist/runtime/astro/server-runtime.js +7 -3
  33. package/dist/runtime/content/client/collection-core.js +3 -2
  34. package/dist/runtime/content/client/core-types.d.ts +4 -3
  35. package/dist/runtime/content/client/internal.d.ts +2 -0
  36. package/dist/runtime/content/client/internal.js +54 -25
  37. package/dist/runtime/content/client/singleton-core.js +2 -1
  38. package/dist/runtime/content/index.d.ts +1 -1
  39. package/dist/runtime/content/index.js +1 -1
  40. package/dist/runtime/paths/get-fimo-paths.js +7 -3
  41. package/dist/runtime/primitives/lib/index.d.ts +1 -1
  42. package/dist/runtime/primitives/lib/index.js +1 -1
  43. package/dist/runtime/primitives/lib/primitives.d.ts +24 -5
  44. package/dist/runtime/primitives/lib/primitives.js +69 -16
  45. package/dist/runtime/primitives/lib/template.js +2 -2
  46. package/dist/runtime/shared/branch-identity.d.ts +56 -0
  47. package/dist/runtime/shared/{env-identity.js → branch-identity.js} +40 -27
  48. package/dist/runtime/shared/local-runtime-env.d.ts +2 -2
  49. package/dist/runtime/shared/local-runtime-env.js +12 -10
  50. package/dist/runtime/templates.d.ts +8 -2
  51. package/dist/runtime/templates.js +34 -8
  52. package/package.json +13 -9
  53. package/release.json +2 -2
  54. package/templates/astro/package.json +2 -2
  55. package/templates/nextjs/AGENTS.md +1 -1
  56. package/templates/nextjs/package.json +4 -4
  57. package/templates/react-router/.fimo/config.json +1 -2
  58. package/templates/react-router/AGENTS.md +1 -1
  59. package/templates/react-router/package.json +4 -4
  60. package/templates/sveltekit/package.json +2 -2
  61. package/templates/vite/package.json +4 -4
  62. package/assets/starters/fimo-site/manifest.json +0 -64
  63. package/assets/starters/fimo-site/template/.fimo/agents/content-guide/GOAL.md +0 -8
  64. package/assets/starters/fimo-site/template/.fimo/agents/content-guide/capabilities.yaml +0 -1
  65. package/assets/starters/fimo-site/template/.fimo/agents/content-guide/config.yaml +0 -9
  66. package/assets/starters/fimo-site/template/.fimo/agents/site-review/GOAL.md +0 -14
  67. package/assets/starters/fimo-site/template/.fimo/agents/site-review/capabilities.yaml +0 -3
  68. package/assets/starters/fimo-site/template/.fimo/agents/site-review/config.yaml +0 -9
  69. package/assets/starters/fimo-site/template/src/forms/contact.json +0 -14
  70. package/assets/starters/fimo-site/template/src/forms/contact.ts +0 -46
  71. package/assets/starters/fimo-site/template/src/pages/Contact.tsx +0 -193
  72. package/assets/starters/fimo-site/template/src/pages/Index.tsx +0 -27
  73. package/assets/starters/fimo-site/template/src/pages/SiteContent.tsx +0 -191
  74. package/assets/starters/fimo-site/template/src/schemas/HomePage.json +0 -28
  75. package/assets/starters/fimo-site/template/src/schemas/HomePage.ts +0 -29
  76. package/assets/starters/fimo-site/template/src/schemas/HomePage.types.ts +0 -64
  77. package/dist/runtime/shared/env-identity.d.ts +0 -43
@@ -0,0 +1,56 @@
1
+ /**
2
+ * How a local checkout names and addresses a Fimo Branch — runtime-safe copy.
3
+ *
4
+ * TRAP: this looks like a duplicate of `@fimo/api-contracts` and it is, on
5
+ * purpose. `dist/runtime/` and `dist/build/` ship UNBUNDLED inside the `fimo`
6
+ * npm package and can only import published dependencies; `@fimo/api-contracts`
7
+ * is a private workspace package, so a runtime import of it breaks every user
8
+ * project (`ERR_MODULE_NOT_FOUND` from `vite.config.ts`). The CLI binary is
9
+ * bundled and may import contracts freely — the runtime may not.
10
+ *
11
+ * The two copies must agree byte-for-byte or a checkout computes a different
12
+ * tenant host than the API hands out. `branch-identity.test.ts` pins them
13
+ * together against a corpus; change one, and that test tells you to change the
14
+ * other.
15
+ */
16
+ export declare const DEFAULT_BRANCH = "main";
17
+ export declare const PROD_BRANCH_PREFIX = "release/";
18
+ /** Mirror of `isValidBranch` in `@fimo/api-contracts`. */
19
+ export declare function isValidBranch(branch: string): boolean;
20
+ /**
21
+ * Mirror of `envResourceKey` in `@fimo/api-contracts`. Keeps that name on both
22
+ * sides: its OUTPUT is a live tenant hostname and R2 prefix, and the symbol
23
+ * rename is a later slice (branch-cutover ledger, F10).
24
+ */
25
+ export declare function envResourceKey(branch: string): string;
26
+ /** Mirror of `buildTenantApiUrl` in `@fimo/api-contracts`. */
27
+ export declare function buildTenantApiUrl(projectId: string, branch: string, tenantApiBaseUrl: string): string;
28
+ /**
29
+ * Content-API aliases a local checkout exposes — one per framework family
30
+ * that has a Fimo startup hook (Vite/React Router, Next, Astro, SvelteKit).
31
+ * Add a key when its framework's hook lands, not before: an alias no runtime
32
+ * reads is one more line to reason about when a site talks to the wrong Branch.
33
+ *
34
+ * These are process-env variable names in a CUSTOMER's app; they keep their
35
+ * `ENV` spelling (branch-cutover ledger, F3 "Preserve").
36
+ */
37
+ export declare const LOCAL_CONTENT_API_ENV_KEYS: readonly ['FIMO_CONTENT_API_URL', 'VITE_API_URL', 'NEXT_PUBLIC_FIMO_API_URL', 'PUBLIC_FIMO_API_URL'];
38
+ /**
39
+ * Branch selectors a runtime turns into the Branch request header.
40
+ *
41
+ * `FIMO_ENV` / `NEXT_PUBLIC_FIMO_ENV` are the variable names a customer's
42
+ * deployed build reads; renaming either breaks it (branch-cutover ledger,
43
+ * "Names that must not be mechanically renamed").
44
+ */
45
+ export declare const LOCAL_ENV_SELECTOR_KEYS: readonly ['FIMO_ENV', 'NEXT_PUBLIC_FIMO_ENV'];
46
+ /**
47
+ * Both halves move together. The tenant URL selects the Branch for content
48
+ * reads (each Branch has its own host); the selectors select it for every
49
+ * service that reads the Branch header. Setting one without the other points a
50
+ * checkout at two different Branches.
51
+ */
52
+ export declare function buildLocalRuntimeEnv(options: {
53
+ readonly tenantApiUrl: string;
54
+ readonly branch: string;
55
+ }): Record<string, string>;
56
+ //# sourceMappingURL=branch-identity.d.ts.map
@@ -1,5 +1,5 @@
1
1
  /**
2
- * How a local checkout names and addresses a Fimo env — runtime-safe copy.
2
+ * How a local checkout names and addresses a Fimo Branch — runtime-safe copy.
3
3
  *
4
4
  * TRAP: this looks like a duplicate of `@fimo/api-contracts` and it is, on
5
5
  * purpose. `dist/runtime/` and `dist/build/` ship UNBUNDLED inside the `fimo`
@@ -9,32 +9,32 @@
9
9
  * bundled and may import contracts freely — the runtime may not.
10
10
  *
11
11
  * The two copies must agree byte-for-byte or a checkout computes a different
12
- * tenant host than the API hands out. `env-identity.test.ts` pins them
12
+ * tenant host than the API hands out. `branch-identity.test.ts` pins them
13
13
  * together against a corpus; change one, and that test tells you to change the
14
14
  * other.
15
15
  */
16
- export const DEFAULT_ENV = 'main';
17
- export const PROD_ENV_PREFIX = 'release/';
18
- const ENV_ID_REGEX = /^[a-zA-Z0-9._/-]+$/;
19
- const MAX_ENV_LENGTH = 255;
20
- /** Mirror of `isValidEnv` in `@fimo/api-contracts`. */
21
- export function isValidEnv(env) {
22
- if (typeof env !== 'string') {
16
+ export const DEFAULT_BRANCH = 'main';
17
+ export const PROD_BRANCH_PREFIX = 'release/';
18
+ const BRANCH_ID_REGEX = /^[a-zA-Z0-9._/-]+$/;
19
+ const MAX_BRANCH_LENGTH = 255;
20
+ /** Mirror of `isValidBranch` in `@fimo/api-contracts`. */
21
+ export function isValidBranch(branch) {
22
+ if (typeof branch !== 'string') {
23
23
  return false;
24
24
  }
25
- if (env.length === 0 || env.length > MAX_ENV_LENGTH) {
25
+ if (branch.length === 0 || branch.length > MAX_BRANCH_LENGTH) {
26
26
  return false;
27
27
  }
28
- if (env.startsWith('-') || env.startsWith('.')) {
28
+ if (branch.startsWith('-') || branch.startsWith('.')) {
29
29
  return false;
30
30
  }
31
- if (env.includes('//') || env.includes('--')) {
31
+ if (branch.includes('//') || branch.includes('--')) {
32
32
  return false;
33
33
  }
34
- if (env.split('/').some((segment) => segment === '..')) {
34
+ if (branch.split('/').some((segment) => segment === '..')) {
35
35
  return false;
36
36
  }
37
- return ENV_ID_REGEX.test(env);
37
+ return BRANCH_ID_REGEX.test(branch);
38
38
  }
39
39
  function fnv1a8(s) {
40
40
  let h = 0x811c9dc5;
@@ -44,27 +44,34 @@ function fnv1a8(s) {
44
44
  }
45
45
  return (h >>> 0).toString(16).padStart(8, '0');
46
46
  }
47
- /** Mirror of `envResourceKey` in `@fimo/api-contracts`. */
48
- export function envResourceKey(env) {
49
- const base = env
47
+ /**
48
+ * Mirror of `envResourceKey` in `@fimo/api-contracts`. Keeps that name on both
49
+ * sides: its OUTPUT is a live tenant hostname and R2 prefix, and the symbol
50
+ * rename is a later slice (branch-cutover ledger, F10).
51
+ */
52
+ export function envResourceKey(branch) {
53
+ const base = branch
50
54
  .toLowerCase()
51
55
  .replace(/[^a-z0-9]+/g, '-')
52
56
  .replace(/^-+|-+$/g, '')
53
57
  .slice(0, 15);
54
- const hash = fnv1a8(env);
58
+ const hash = fnv1a8(branch);
55
59
  return base ? `${base}-${hash}` : hash;
56
60
  }
57
61
  /** Mirror of `buildTenantApiUrl` in `@fimo/api-contracts`. */
58
- export function buildTenantApiUrl(projectId, env, tenantApiBaseUrl) {
62
+ export function buildTenantApiUrl(projectId, branch, tenantApiBaseUrl) {
59
63
  const url = new URL(tenantApiBaseUrl);
60
- url.hostname = `${projectId}-${envResourceKey(env)}.${url.hostname}`;
64
+ url.hostname = `${projectId}-${envResourceKey(branch)}.${url.hostname}`;
61
65
  return url.toString().replace(/\/$/, '');
62
66
  }
63
67
  /**
64
68
  * Content-API aliases a local checkout exposes — one per framework family
65
69
  * that has a Fimo startup hook (Vite/React Router, Next, Astro, SvelteKit).
66
70
  * Add a key when its framework's hook lands, not before: an alias no runtime
67
- * reads is one more line to reason about when a site talks to the wrong env.
71
+ * reads is one more line to reason about when a site talks to the wrong Branch.
72
+ *
73
+ * These are process-env variable names in a CUSTOMER's app; they keep their
74
+ * `ENV` spelling (branch-cutover ledger, F3 "Preserve").
68
75
  */
69
76
  export const LOCAL_CONTENT_API_ENV_KEYS = [
70
77
  'FIMO_CONTENT_API_URL',
@@ -72,17 +79,23 @@ export const LOCAL_CONTENT_API_ENV_KEYS = [
72
79
  'NEXT_PUBLIC_FIMO_API_URL',
73
80
  'PUBLIC_FIMO_API_URL',
74
81
  ];
75
- /** Env selectors a runtime turns into the `X-Fimo-Env` request header. */
82
+ /**
83
+ * Branch selectors a runtime turns into the Branch request header.
84
+ *
85
+ * `FIMO_ENV` / `NEXT_PUBLIC_FIMO_ENV` are the variable names a customer's
86
+ * deployed build reads; renaming either breaks it (branch-cutover ledger,
87
+ * "Names that must not be mechanically renamed").
88
+ */
76
89
  export const LOCAL_ENV_SELECTOR_KEYS = ['FIMO_ENV', 'NEXT_PUBLIC_FIMO_ENV'];
77
90
  /**
78
- * Both halves move together. The tenant URL selects the env for content reads
79
- * (each env has its own host); the selectors select it for every service that
80
- * reads `X-Fimo-Env`. Setting one without the other points a checkout at two
81
- * different envs.
91
+ * Both halves move together. The tenant URL selects the Branch for content
92
+ * reads (each Branch has its own host); the selectors select it for every
93
+ * service that reads the Branch header. Setting one without the other points a
94
+ * checkout at two different Branches.
82
95
  */
83
96
  export function buildLocalRuntimeEnv(options) {
84
97
  return {
85
98
  ...Object.fromEntries(LOCAL_CONTENT_API_ENV_KEYS.map((key) => [key, options.tenantApiUrl])),
86
- ...Object.fromEntries(LOCAL_ENV_SELECTOR_KEYS.map((key) => [key, options.env])),
99
+ ...Object.fromEntries(LOCAL_ENV_SELECTOR_KEYS.map((key) => [key, options.branch])),
87
100
  };
88
101
  }
@@ -38,9 +38,9 @@ export declare function resolveLocalRuntimeEnv(options: {
38
38
  readonly cwd: string;
39
39
  }): LocalRuntimeEnv | null;
40
40
  /**
41
- * The one case where "explicit wins" pins the app to the WRONG env: an
41
+ * The one case where "explicit wins" pins the app to the WRONG Branch: an
42
42
  * explicit tenant URL that Fimo itself minted for this project (old-CLI
43
- * `fimo clone` seeded the clone-time env's URL into `.env`) and that disagrees
43
+ * `fimo clone` seeded the clone-time Branch's URL into `.env`) and that disagrees
44
44
  * with the branch. A URL the user typed — `http://localhost:4000` — is never
45
45
  * reported; that is the override the design exists to protect.
46
46
  */
@@ -19,7 +19,7 @@ import { execFileSync } from 'node:child_process';
19
19
  import fs from 'node:fs';
20
20
  import path from 'node:path';
21
21
  import { CURRENT_PROJECT_LAYOUT, LEGACY_PROJECT_LAYOUT } from '../project-layout.js';
22
- import { DEFAULT_ENV, LOCAL_CONTENT_API_ENV_KEYS, buildLocalRuntimeEnv, buildTenantApiUrl, isValidEnv, } from './env-identity.js';
22
+ import { DEFAULT_BRANCH, LOCAL_CONTENT_API_ENV_KEYS, buildLocalRuntimeEnv, buildTenantApiUrl, isValidBranch, } from './branch-identity.js';
23
23
  /**
24
24
  * Derive the local runtime env, or `null` when this directory is not a linked
25
25
  * Fimo project (or the link predates `tenantApiBaseUrl` — nothing safe to
@@ -31,21 +31,23 @@ export function resolveLocalRuntimeEnv(options) {
31
31
  if (!root || !link) {
32
32
  return null;
33
33
  }
34
- const branch = currentBranch(root);
35
- const env = branch ?? DEFAULT_ENV;
36
- const tenantApiUrl = buildTenantApiUrl(link.projectId, env, link.tenantApiBaseUrl);
34
+ const gitBranch = currentBranch(root);
35
+ const branch = gitBranch ?? DEFAULT_BRANCH;
36
+ const tenantApiUrl = buildTenantApiUrl(link.projectId, branch, link.tenantApiBaseUrl);
37
37
  return {
38
- env,
38
+ // `env` stays the field name: `resolveLocalRuntimeEnv` is a published
39
+ // `fimo/config` export that framework adapters destructure.
40
+ env: branch,
39
41
  tenantApiUrl,
40
- source: branch ? 'branch' : 'fallback',
41
- values: buildLocalRuntimeEnv({ tenantApiUrl, env }),
42
+ source: gitBranch ? 'branch' : 'fallback',
43
+ values: buildLocalRuntimeEnv({ tenantApiUrl, branch }),
42
44
  link,
43
45
  };
44
46
  }
45
47
  /**
46
- * The one case where "explicit wins" pins the app to the WRONG env: an
48
+ * The one case where "explicit wins" pins the app to the WRONG Branch: an
47
49
  * explicit tenant URL that Fimo itself minted for this project (old-CLI
48
- * `fimo clone` seeded the clone-time env's URL into `.env`) and that disagrees
50
+ * `fimo clone` seeded the clone-time Branch's URL into `.env`) and that disagrees
49
51
  * with the branch. A URL the user typed — `http://localhost:4000` — is never
50
52
  * reported; that is the override the design exists to protect.
51
53
  */
@@ -128,7 +130,7 @@ function currentBranch(root) {
128
130
  stdio: ['ignore', 'pipe', 'ignore'],
129
131
  encoding: 'utf-8',
130
132
  }).trim();
131
- return branch && branch !== 'HEAD' && isValidEnv(branch) ? branch : null;
133
+ return branch && branch !== 'HEAD' && isValidBranch(branch) ? branch : null;
132
134
  }
133
135
  catch {
134
136
  return null;
@@ -30,7 +30,9 @@ export declare function getTemplatePath(framework?: TemplateFramework): string;
30
30
  */
31
31
  export declare function getLegacyTemplateDir(): string;
32
32
  export interface ReleaseMeta {
33
- source: 'npm' | 'dev' | null;
33
+ source: 'npm' | 'url' | 'dev' | null;
34
+ /** Immutable artifact URL, baked into `source: 'url'` development tarballs. */
35
+ url?: string | null;
34
36
  }
35
37
  export interface DevReleaseVersion {
36
38
  devJsonPath: string | null;
@@ -40,7 +42,11 @@ export declare function readReleaseMeta(): ReleaseMeta;
40
42
  export declare function readCliSemver(): string | null;
41
43
  /**
42
44
  * Pure version: given a release manifest + CLI semver, return the value
43
- * to pin `fimo` to (or null for dev). Exported for unit tests.
45
+ * to pin `fimo` to (or null when nothing is known). Exported for unit tests.
46
+ *
47
+ * Throws rather than returning an unreproducible pin: a project that records a
48
+ * package-manager cache path (or a `fimo` its companions cannot follow) is
49
+ * broken on every other machine, and the error names the missing publish step.
44
50
  */
45
51
  export declare function computeFimoPin(meta: ReleaseMeta, semver: string | null, options?: {
46
52
  devPackageRoot?: string | null;
@@ -28,7 +28,7 @@ const PACKAGE_ROOT = path.resolve(MODULE_DIR, '..', '..');
28
28
  const RELEASE_FILE = path.join(PACKAGE_ROOT, 'release.json');
29
29
  const PACKAGE_JSON_FILE = path.join(PACKAGE_ROOT, 'package.json');
30
30
  export const TEMPLATE_FRAMEWORKS = ['react-router', 'vite', 'astro', 'nextjs', 'sveltekit'];
31
- export const DEFAULT_TEMPLATE = 'react-router';
31
+ export const DEFAULT_TEMPLATE = 'nextjs';
32
32
  /**
33
33
  * Absolute path to the requested framework's template directory.
34
34
  *
@@ -69,8 +69,8 @@ export function readReleaseMeta() {
69
69
  try {
70
70
  const raw = fs.readFileSync(RELEASE_FILE, 'utf8');
71
71
  const parsed = JSON.parse(raw);
72
- const source = parsed.source === 'npm' || parsed.source === 'dev' ? parsed.source : null;
73
- return { source };
72
+ const source = parsed.source === 'npm' || parsed.source === 'dev' || parsed.source === 'url' ? parsed.source : null;
73
+ return { source, url: typeof parsed.url === 'string' && parsed.url.length > 0 ? parsed.url : null };
74
74
  }
75
75
  catch {
76
76
  return { source: null };
@@ -91,16 +91,34 @@ export function readCliSemver() {
91
91
  }
92
92
  /**
93
93
  * Pure version: given a release manifest + CLI semver, return the value
94
- * to pin `fimo` to (or null for dev). Exported for unit tests.
94
+ * to pin `fimo` to (or null when nothing is known). Exported for unit tests.
95
+ *
96
+ * Throws rather than returning an unreproducible pin: a project that records a
97
+ * package-manager cache path (or a `fimo` its companions cannot follow) is
98
+ * broken on every other machine, and the error names the missing publish step.
95
99
  */
96
100
  export function computeFimoPin(meta, semver, options = {}) {
97
101
  if (meta.source === 'npm') {
98
- if (semver) {
99
- return semver;
102
+ return semver;
103
+ }
104
+ // A checkout wins over a baked artifact URL: `publish-cli --id` keeps its
105
+ // release.json in the workspace so the next local run still iterates on
106
+ // source, not on the tarball it just uploaded.
107
+ const localPin = options.devPackageRoot ? `file:${options.devPackageRoot}` : null;
108
+ if (localPin && pinsWholeGroup(localPin)) {
109
+ return localPin;
110
+ }
111
+ if (meta.source === 'url') {
112
+ if (!meta.url) {
113
+ throw new Error('Refusing to create a project from a development CLI that was published without a public artifact URL. ' +
114
+ 'Republish it remotely with `pnpm -F fimo publish-cli -- --id <task-name>` and install that artifact.');
100
115
  }
116
+ return meta.url;
101
117
  }
102
- if ((meta.source === 'dev' || meta.source === null) && options.devPackageRoot) {
103
- return `file:${options.devPackageRoot}`;
118
+ if (localPin) {
119
+ throw new Error(`Refusing to create a project pinned to ${localPin}: that path is not a Fimo checkout, so it is neither ` +
120
+ 'reproducible on another machine nor able to pin the matching companion packages. Install `fimo` from npm, ' +
121
+ 'or publish this CLI with `pnpm -F fimo publish-cli -- --id <task-name>` and install that artifact.');
104
122
  }
105
123
  return null;
106
124
  }
@@ -233,6 +251,14 @@ export function deriveFimoPackagePin(name, fimoPin) {
233
251
  }
234
252
  return fimoPin;
235
253
  }
254
+ /**
255
+ * Whether every Fimo package can be pinned from this `fimo` pin. A pin that
256
+ * only the CLI can follow leaves the companions on the template's `latest`,
257
+ * which silently mixes channels inside one project (FIMO-2070).
258
+ */
259
+ function pinsWholeGroup(fimoPin) {
260
+ return FIMO_PACKAGES.every((name) => deriveFimoPackagePin(name, fimoPin) !== null);
261
+ }
236
262
  /**
237
263
  * Rewrite `fimo` and any companion packages present (in dependencies +
238
264
  * devDependencies) to the current pin. Only touches entries that look like a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fimo",
3
- "version": "2.1.0",
3
+ "version": "2.2.0-staging.10",
4
4
  "description": "Fimo CLI - create, deploy, and manage Fimo projects",
5
5
  "keywords": [
6
6
  "astro-component"
@@ -77,6 +77,10 @@
77
77
  "types": "./dist/runtime/astro/internal.d.ts",
78
78
  "import": "./dist/runtime/astro/internal.js"
79
79
  },
80
+ "./vite/preview-health": {
81
+ "types": "./dist/build/vite/plugins/preview-health.d.ts",
82
+ "import": "./dist/build/vite/plugins/preview-health.js"
83
+ },
80
84
  "./next": {
81
85
  "types": "./dist/build/next/moved.d.ts",
82
86
  "import": "./dist/build/next/moved.js"
@@ -184,12 +188,12 @@
184
188
  "devDependencies": {
185
189
  "@clack/core": "^1.2.0",
186
190
  "@clack/prompts": "^1.2.0",
187
- "@fimo/agents-bundle": "2.1.0",
188
- "@fimo/api-contracts": "2.1.0",
189
- "@fimo/cli-kit": "2.1.0",
190
- "@fimo/framework-detect": "2.1.0",
191
- "@fimo/routes": "2.1.0",
192
- "@fimo/tsconfig": "2.1.0",
191
+ "@fimo/agents-bundle": "2.2.0-staging.10",
192
+ "@fimo/api-contracts": "2.2.0-staging.10",
193
+ "@fimo/cli-kit": "2.2.0-staging.10",
194
+ "@fimo/framework-detect": "2.2.0-staging.10",
195
+ "@fimo/routes": "2.2.0-staging.10",
196
+ "@fimo/tsconfig": "2.2.0-staging.10",
193
197
  "@types/node": "22.18.6",
194
198
  "agent-install": "^0.0.5",
195
199
  "astro": "^7.1.6",
@@ -201,8 +205,8 @@
201
205
  "tsx": "4.22.4",
202
206
  "typescript": "7.0.2",
203
207
  "vite": "8.2.2",
204
- "vitest": "4.1.10",
205
- "wrangler": "4.124.0",
208
+ "vitest": "4.1.11",
209
+ "wrangler": "4.126.0",
206
210
  "yaml": "2.8.2"
207
211
  },
208
212
  "engines": {
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
  }
@@ -9,8 +9,8 @@
9
9
  "check": "astro sync && astro check"
10
10
  },
11
11
  "dependencies": {
12
- "fimo": "2.1.0",
13
- "fimo-astro": "2.1.0"
12
+ "fimo": "2.2.0-staging.10",
13
+ "fimo-astro": "2.2.0-staging.10"
14
14
  },
15
15
  "devDependencies": {
16
16
  "@astrojs/check": "^0.9.10",
@@ -37,7 +37,7 @@ The scaffolded homepage is disposable. Replace it on the first real generation.
37
37
  - **Deploy is on-demand**, not a save step. Batch related edits into one deploy.
38
38
  - Use the selected package manager's committed build-script policy. pnpm projects pin `pnpm@10.26.0` and approve `esbuild` and `fimo` through `pnpm-workspace.yaml#allowBuilds`; Bun projects trust the same packages through `package.json#trustedDependencies`. Run a plain install with the selected manager so local installation matches Preview and production publishing. Review new dependencies before extending the policy; all three paths stop on unreviewed scripts.
39
39
  - Treat the user's language request as part of setup. The first explicitly requested locale is the primary locale; put it first and set it as `i18n.defaultLocale` in `.fimo/config.json`, with every requested locale in `i18n.locales`. If no language is requested, keep `en` and state that choice. That file is the only place to change: `src/locales.ts` reads it, so the served language and `<html lang>` follow. It does not create locale URLs; implement a URL strategy only when the product asks for one. Never introduce a default-locale environment variable.
40
- - Next's `app` tree is the router and stays authoritative: the starter is one ordinary root layout plus `/` as the homepage, and unknown paths get Next's native 404. When a product requires locale URLs, implement the framework-native strategy (`[locale]` segments, middleware, cookies, domains, or an i18n library), pass the resolved locale into Fimo server reads and `FimoProvider`, and load `fimo skills get fimo-code --ref frameworks/next/locales` first. Do not impose locale-prefixed URLs on a default-only project.
40
+ - Next's `app` tree is the router and stays authoritative: the default shell is one ordinary root layout plus `/` as the homepage, and unknown paths get Next's native 404. When a product requires locale URLs, implement the framework-native strategy (`[locale]` segments, middleware, cookies, domains, or an i18n library), pass the resolved locale into Fimo server reads and `FimoProvider`, and load `fimo skills get fimo-code --ref frameworks/next/locales` first. Do not impose locale-prefixed URLs on a default-only project.
41
41
  - `FimoProvider` owns the optional Studio preview runtime. Do not add a preview bridge component, read Fimo preview environment variables, or inject Fimo scripts in application code.
42
42
  - Keep `.fimo/config.json#routes` as Studio metadata, not as the Next.js router. Describe only URLs the application actually serves; add a `:locale` segment to a route's metadata only after the application implements that locale routing. There, `params.locale.optional: true` means omit the configured default locale from the URL; use `optional: false` only when every locale, including the default, must be prefixed.
43
43
  - The application owns routes, locale resolution, links, HTML language/direction, and SEO. Fimo connects to those decisions.
@@ -12,10 +12,10 @@
12
12
  },
13
13
  "dependencies": {
14
14
  "@tanstack/react-query": "^5.80.6",
15
- "fimo": "2.1.0",
16
- "fimo-next": "2.1.0",
17
- "fimo-react": "2.1.0",
18
- "next": "^16.3.0",
15
+ "fimo": "2.2.0-staging.10",
16
+ "fimo-next": "2.2.0-staging.10",
17
+ "fimo-react": "2.2.0-staging.10",
18
+ "next": "^16.3.3",
19
19
  "react": "^19.2.7",
20
20
  "react-dom": "^19.2.7"
21
21
  },
@@ -2,8 +2,7 @@
2
2
  "runtime": { "framework": "react-router" },
3
3
  "seo": {
4
4
  "title": "Welcome to your Fimo website",
5
- "description": "A default homepage starter for Fimo projects.",
6
- "siteName": "Fimo Starter Site",
5
+ "description": "A default homepage for Fimo projects.",
7
6
  "url": "https://fimo.ai/",
8
7
  "image": "https://fimo.ai/socials/og_cover.png",
9
8
  "favicon": "https://fimo.ai/favicon-orange.svg",
@@ -47,6 +47,6 @@ breaks that. Load `fimo-code` for the exact primitives, but the rule is:
47
47
  - Do not add a second deploy tool (Vercel, Netlify, etc.).
48
48
  - **Never edit `.fimo/project.json`** — it links this folder to the remote project.
49
49
  - **Keep `.fimo/config.json#runtime` true to the code** — framework, `dev`/`build`/`install` commands, output directory. Update it when you rename a package script, move the build output, or change framework; the reference is `fimo skills get fimo-code --ref frameworks/hosting`.
50
- - The scaffolded homepage is disposable starter content. Replace it on the first real generation.
50
+ - The scaffolded homepage is disposable template content. Replace it on the first real generation.
51
51
  - **Secrets live in the Fimo dashboard**, not in a committed `.env`.
52
52
  - When scripting, pass flags to `fimo` commands (`fimo deploy -m "…"`, `fimo delete -y`) — bare interactive forms hang in non-TTY shells.
@@ -21,10 +21,10 @@
21
21
  "cmdk": "^1.1.1",
22
22
  "date-fns": "^4.1.0",
23
23
  "embla-carousel-react": "^8.6.0",
24
- "fimo": "2.1.0",
25
- "fimo-react": "2.1.0",
26
- "fimo-react-router": "2.1.0",
27
- "fimo-vite": "2.1.0",
24
+ "fimo": "2.2.0-staging.10",
25
+ "fimo-react": "2.2.0-staging.10",
26
+ "fimo-react-router": "2.2.0-staging.10",
27
+ "fimo-vite": "2.2.0-staging.10",
28
28
  "input-otp": "^1.4.2",
29
29
  "isbot": "^5",
30
30
  "lucide-react": "^0.577.0",
@@ -11,8 +11,8 @@
11
11
  "prepare": "svelte-kit sync"
12
12
  },
13
13
  "dependencies": {
14
- "fimo": "2.1.0",
15
- "fimo-sveltekit": "2.1.0"
14
+ "fimo": "2.2.0-staging.10",
15
+ "fimo-sveltekit": "2.2.0-staging.10"
16
16
  },
17
17
  "devDependencies": {
18
18
  "@sveltejs/kit": "^2.70.2",
@@ -11,10 +11,10 @@
11
11
  },
12
12
  "dependencies": {
13
13
  "@tanstack/react-query": "^5.80.6",
14
- "fimo": "2.1.0",
15
- "fimo-react": "2.1.0",
16
- "fimo-react-router": "2.1.0",
17
- "fimo-vite": "2.1.0",
14
+ "fimo": "2.2.0-staging.10",
15
+ "fimo-react": "2.2.0-staging.10",
16
+ "fimo-react-router": "2.2.0-staging.10",
17
+ "fimo-vite": "2.2.0-staging.10",
18
18
  "react": "^19.2.7",
19
19
  "react-dom": "^19.2.7",
20
20
  "react-router": "^8.3.0"
@@ -1,64 +0,0 @@
1
- {
2
- "name": "fimo-site",
3
- "version": 1,
4
- "description": "A content-led site with a homepage, contact form, labels, and manual review agents.",
5
- "singletons": [
6
- {
7
- "type": "HomePage",
8
- "data": {
9
- "eyebrow": "Field notes",
10
- "title": "Your website, ready to edit.",
11
- "intro": {
12
- "type": "doc",
13
- "content": [
14
- {
15
- "type": "paragraph",
16
- "content": [
17
- {
18
- "type": "text",
19
- "text": "This starter is real website content. Edit the copy in Studio, change the code with your favorite agent, and keep both surfaces in sync."
20
- }
21
- ]
22
- }
23
- ]
24
- },
25
- "primaryCtaLabel": "Read the field notes",
26
- "primaryCtaHref": "#stories",
27
- "featured": [
28
- {
29
- "label": "Start here",
30
- "title": "A calmer content workflow",
31
- "summary": "Keep content, code, previews, and the team in one editable project.",
32
- "href": "#stories"
33
- },
34
- {
35
- "label": "For agents",
36
- "title": "Build once, keep iterating",
37
- "summary": "Give your coding agent a real CMS surface to work with after the first deploy.",
38
- "href": "#stories"
39
- },
40
- {
41
- "label": "For teams",
42
- "title": "Make every edit reviewable",
43
- "summary": "Let teammates shape the site without asking them to open a code editor.",
44
- "href": "#stories"
45
- }
46
- ],
47
- "contactHeading": "Make something useful",
48
- "contactIntro": "Tell us what you are building and we will help you find the shortest path from first draft to a site your team can own."
49
- }
50
- }
51
- ],
52
- "labels": [
53
- { "key": "nav.stories", "value": "Field notes", "locale": "en" },
54
- { "key": "nav.contact", "value": "Start a conversation", "locale": "en" },
55
- { "key": "hero.scroll", "value": "Explore the starter", "locale": "en" },
56
- { "key": "contact.name", "value": "Your name", "locale": "en" },
57
- { "key": "contact.email", "value": "Email address", "locale": "en" },
58
- { "key": "contact.message", "value": "What are you building?", "locale": "en" },
59
- { "key": "contact.submit", "value": "Send a note", "locale": "en" },
60
- { "key": "contact.sent", "value": "Thanks. Your note is on its way.", "locale": "en" },
61
- { "key": "contact.failed", "value": "That did not send. Please try again.", "locale": "en" }
62
- ],
63
- "forms": ["contact"]
64
- }
@@ -1,8 +0,0 @@
1
- # Content guide
2
-
3
- Inspect the site's content model and homepage copy. Suggest a small editorial
4
- backlog that makes the site more useful to its intended audience.
5
-
6
- Return three suggestions, each with the content type or field it touches and
7
- why it matters. Use the existing content as evidence. Do not write files,
8
- change entries, or create labels.
@@ -1,9 +0,0 @@
1
- # Keep starter agents manual-only. Adding a schedule or webhook and pushing to
2
- # main registers an automation for the project.
3
- version: 1
4
- description: 'Suggest useful editorial improvements from the existing CMS content.'
5
- data_sources:
6
- - main
7
-
8
- triggers:
9
- - type: manual
@@ -1,14 +0,0 @@
1
- # Site review
2
-
3
- Review the starter project as a teammate would before the first publish.
4
-
5
- Read the declared routes, the CMS content, the labels, and the public site.
6
- Return a short report with:
7
-
8
- - one thing that already feels clear;
9
- - the three highest-value improvements;
10
- - any missing content or broken route you can prove;
11
- - one next action that a human or coding agent can review.
12
-
13
- This is a read-only review. Do not edit files, change CMS content, create
14
- branches, or publish anything.
@@ -1,3 +0,0 @@
1
- - cms:read
2
- - i18n:read
3
- - net:fetch
@@ -1,9 +0,0 @@
1
- # Keep starter agents manual-only. Adding a schedule or webhook and pushing to
2
- # main registers an automation for the project.
3
- version: 1
4
- description: 'Review the starter site and return a concise, read-only launch report.'
5
- data_sources:
6
- - main
7
-
8
- triggers:
9
- - type: manual