fimo 0.3.4-staging.26 → 0.3.4

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.
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "bundled": true,
3
3
  "bundler": "esbuild",
4
- "bundledAt": "2026-07-07T09:17:55.884Z",
5
- "cliVersion": "0.3.3",
4
+ "bundledAt": "2026-07-07T11:27:20.207Z",
5
+ "cliVersion": "0.3.4",
6
6
  "external": [
7
7
  "oxc-parser",
8
8
  "fsevents"
package/dist/cli/index.js CHANGED
@@ -45923,13 +45923,22 @@ var init_local_project_files = __esm({
45923
45923
  }
45924
45924
  });
45925
45925
 
45926
+ // src/cli/utils/slug.ts
45927
+ function slugifyName(input) {
45928
+ return input.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
45929
+ }
45930
+ var init_slug = __esm({
45931
+ "src/cli/utils/slug.ts"() {
45932
+ __name(slugifyName, "slugifyName");
45933
+ }
45934
+ });
45935
+
45926
45936
  // src/cli/commands/clone.ts
45927
45937
  import fs18 from "node:fs";
45928
45938
  import path22 from "node:path";
45929
45939
  function defaultDirectoryName(project) {
45930
45940
  const source = project.displayName?.trim() || project.name || project.id;
45931
- const cleaned = source.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
45932
- return cleaned || project.id;
45941
+ return slugifyName(source) || project.id;
45933
45942
  }
45934
45943
  function explainCloneRemoteError(error45) {
45935
45944
  if (error45 instanceof ApiRequestError && error45.status === 403) {
@@ -46079,7 +46088,12 @@ async function cloneProject(args) {
46079
46088
  }
46080
46089
  const targetDir = path22.resolve(dir ?? defaultDirectoryName(project));
46081
46090
  if (fs18.existsSync(targetDir) && fs18.readdirSync(targetDir).length > 0) {
46082
- throw new CloneError("DIRECTORY_NOT_EMPTY", `Refusing to clone into non-empty directory: ${targetDir}`);
46091
+ const name = path22.basename(targetDir);
46092
+ throw new CloneError(
46093
+ "DIRECTORY_NOT_EMPTY",
46094
+ `The folder "${name}" already exists and isn't empty.`,
46095
+ `Pick a different directory (e.g. \`fimo clone ${projectId} <dir>\`), or empty "${name}" first.`
46096
+ );
46083
46097
  }
46084
46098
  const relativeTarget = path22.relative(process.cwd(), targetDir) || ".";
46085
46099
  if (!await remoteHasBranch(remote.url, remote.branch)) {
@@ -46174,6 +46188,7 @@ var init_clone = __esm({
46174
46188
  init_git();
46175
46189
  init_interactive();
46176
46190
  init_local_project_files();
46191
+ init_slug();
46177
46192
  CloneError = class extends Error {
46178
46193
  static {
46179
46194
  __name(this, "CloneError");
@@ -48440,12 +48455,15 @@ function explainCreateProjectError(error45) {
48440
48455
  return error45 instanceof Error ? error45.message : String(error45);
48441
48456
  }
48442
48457
  async function scaffoldNewProject(args) {
48443
- const { api, cfg, targetDir, options } = args;
48458
+ const { api, cfg, targetDir, displayName, options } = args;
48444
48459
  const organizationId = await resolveOrganization(api, options.org);
48445
48460
  const createSpinner = spinner("Creating Fimo project");
48446
48461
  let project;
48447
48462
  try {
48448
- project = await api.createProject({ organizationId, displayName: path34.basename(targetDir) });
48463
+ project = await api.createProject({
48464
+ organizationId,
48465
+ displayName: displayName ?? path34.basename(targetDir)
48466
+ });
48449
48467
  } catch (error45) {
48450
48468
  createSpinner.fail("Project creation failed");
48451
48469
  throw new ScaffoldError("PROJECT_CREATE_FAILED", explainCreateProjectError(error45));
@@ -48687,7 +48705,17 @@ async function initCommand(dirArg, options) {
48687
48705
  const api = new FimoApiClient(cfg);
48688
48706
  const creds = readCredentials(cfg);
48689
48707
  const useExisting = await resolveUseExisting(options);
48690
- const result = useExisting ? await runExisting({ api, cfg, dirArg, options, activeOrgId: creds?.activeOrganization?.id ?? null }) : await runNew({ api, cfg, dirArg, options });
48708
+ const plan = await planInit({ api, creds, useExisting, dirArg, options });
48709
+ if (!plan) {
48710
+ return;
48711
+ }
48712
+ const result = useExisting ? await runExisting({ api, cfg, projectId: plan.projectId, dirArg, options: plan.options }) : await runNew({
48713
+ api,
48714
+ cfg,
48715
+ targetDir: plan.targetDir,
48716
+ displayName: plan.displayName,
48717
+ options: plan.options
48718
+ });
48691
48719
  if (!result) {
48692
48720
  return;
48693
48721
  }
@@ -48695,8 +48723,8 @@ async function initCommand(dirArg, options) {
48695
48723
  const scaffoldSummary = result.summary;
48696
48724
  const scaffoldedAgents = result.selectedAgents;
48697
48725
  const pushedInitial = result.pushedInitial;
48698
- const depResult = await maybeInstallDependencies(checkout.targetDir, options);
48699
- const skills2 = await maybeInstallSkills(checkout.targetDir, options, scaffoldedAgents);
48726
+ const depResult = await maybeInstallDependencies(checkout.targetDir, plan.packageManager);
48727
+ const skills2 = await maybeInstallSkills(checkout.targetDir, plan.options, scaffoldedAgents);
48700
48728
  const warnings = [];
48701
48729
  if (!pushedInitial) {
48702
48730
  warnings.push(
@@ -48835,19 +48863,73 @@ async function resolveUseExisting(options) {
48835
48863
  }
48836
48864
  return choice === "existing";
48837
48865
  }
48838
- async function runExisting(args) {
48839
- const { api, cfg, dirArg, options, activeOrgId } = args;
48840
- const projectId = options.projectId ?? await selectProjectId({
48841
- api,
48842
- activeOrganizationId: activeOrgId,
48843
- command: "init",
48844
- message: "Select a project to link"
48845
- });
48846
- if (!projectId) {
48847
- return null;
48866
+ async function planInit(args) {
48867
+ const { api, creds, useExisting, dirArg, options } = args;
48868
+ let projectId;
48869
+ let targetDir;
48870
+ let displayName;
48871
+ if (useExisting) {
48872
+ projectId = options.projectId ?? await selectProjectId({
48873
+ api,
48874
+ activeOrganizationId: creds?.activeOrganization?.id ?? null,
48875
+ command: "init",
48876
+ message: "Select a project to link"
48877
+ }) ?? void 0;
48878
+ if (!projectId) {
48879
+ return null;
48880
+ }
48881
+ } else {
48882
+ const resolved = await resolveTargetDir(dirArg, nonInteractive(options));
48883
+ if (!resolved) {
48884
+ return null;
48885
+ }
48886
+ targetDir = resolved.targetDir;
48887
+ displayName = resolved.displayName;
48888
+ if (fs33.existsSync(targetDir) && fs33.readdirSync(targetDir).length > 0) {
48889
+ const name = path35.basename(targetDir);
48890
+ ui.error({
48891
+ command: "init",
48892
+ code: "DIRECTORY_NOT_EMPTY",
48893
+ message: `The folder "${name}" already exists and isn't empty.`,
48894
+ hint: `Pick a different directory (e.g. \`fimo init <dir>\`), or empty "${name}" first.`
48895
+ });
48896
+ return null;
48897
+ }
48848
48898
  }
48899
+ const derived = { ...options };
48900
+ const gatherAgents = !nonInteractive(options) && (!useExisting || options.skills !== false);
48901
+ if (gatherAgents) {
48902
+ let agents;
48903
+ try {
48904
+ agents = await selectAgents(options, {
48905
+ promptMessage: "Which agents do you want to set up?",
48906
+ detectFn: detectAgentsWithFimoSkills
48907
+ });
48908
+ } catch (err) {
48909
+ ui.error({
48910
+ command: "init",
48911
+ code: "AGENT_SELECTION_FAILED",
48912
+ message: err instanceof Error ? err.message : String(err)
48913
+ });
48914
+ return null;
48915
+ }
48916
+ derived.agents = agents.length > 0 ? agents.join(",") : "none";
48917
+ } else if (useExisting && options.skills === false && !derived.agents) {
48918
+ derived.agents = "none";
48919
+ }
48920
+ const packageManager = options.install === false ? null : await resolvePackageManager(derived);
48921
+ return { projectId, targetDir, displayName, packageManager, options: derived };
48922
+ }
48923
+ async function runExisting(args) {
48924
+ const { api, cfg, projectId, dirArg, options } = args;
48849
48925
  try {
48850
- const result = await cloneProject({ api, cfg, projectId, dir: dirArg, env: options.env });
48926
+ const result = await cloneProject({
48927
+ api,
48928
+ cfg,
48929
+ projectId,
48930
+ dir: dirArg,
48931
+ env: options.env
48932
+ });
48851
48933
  return {
48852
48934
  checkout: {
48853
48935
  projectId: result.project.id,
@@ -48864,7 +48946,12 @@ async function runExisting(args) {
48864
48946
  return adoptEmptyRemote({ api, cfg, ctx: error45.emptyRemote, options });
48865
48947
  }
48866
48948
  if (error45 instanceof CloneError) {
48867
- ui.error({ command: "init", code: error45.code, message: error45.message, hint: error45.hint });
48949
+ ui.error({
48950
+ command: "init",
48951
+ code: error45.code,
48952
+ message: error45.message,
48953
+ hint: error45.hint
48954
+ });
48868
48955
  return null;
48869
48956
  }
48870
48957
  throw error45;
@@ -48876,7 +48963,13 @@ async function adoptEmptyRemote(args) {
48876
48963
  O2.step("This project has no content yet \u2014 scaffolding a starter site and pushing the first commit.");
48877
48964
  }
48878
48965
  try {
48879
- const result = await scaffoldIntoProject({ project: ctx.project, api, cfg, targetDir: ctx.targetDir, options });
48966
+ const result = await scaffoldIntoProject({
48967
+ project: ctx.project,
48968
+ api,
48969
+ cfg,
48970
+ targetDir: ctx.targetDir,
48971
+ options
48972
+ });
48880
48973
  return {
48881
48974
  checkout: {
48882
48975
  projectId: result.project.id,
@@ -48890,28 +48983,21 @@ async function adoptEmptyRemote(args) {
48890
48983
  };
48891
48984
  } catch (error45) {
48892
48985
  if (error45 instanceof ScaffoldError) {
48893
- ui.error({ command: "init", code: error45.code, message: error45.message, hint: error45.hint });
48986
+ ui.error({
48987
+ command: "init",
48988
+ code: error45.code,
48989
+ message: error45.message,
48990
+ hint: error45.hint
48991
+ });
48894
48992
  return null;
48895
48993
  }
48896
48994
  throw error45;
48897
48995
  }
48898
48996
  }
48899
48997
  async function runNew(args) {
48900
- const { api, cfg, dirArg, options } = args;
48901
- const targetDir = await resolveTargetDir(dirArg, nonInteractive(options));
48902
- if (!targetDir) {
48903
- return null;
48904
- }
48905
- if (fs33.existsSync(targetDir) && fs33.readdirSync(targetDir).length > 0) {
48906
- ui.error({
48907
- command: "init",
48908
- code: "DIRECTORY_NOT_EMPTY",
48909
- message: `Refusing to scaffold into non-empty directory: ${targetDir}`
48910
- });
48911
- return null;
48912
- }
48998
+ const { api, cfg, targetDir, displayName, options } = args;
48913
48999
  try {
48914
- const result = await scaffoldNewProject({ api, cfg, targetDir, options });
49000
+ const result = await scaffoldNewProject({ api, cfg, targetDir, displayName, options });
48915
49001
  return {
48916
49002
  checkout: {
48917
49003
  projectId: result.project.id,
@@ -48925,7 +49011,12 @@ async function runNew(args) {
48925
49011
  };
48926
49012
  } catch (error45) {
48927
49013
  if (error45 instanceof ScaffoldError) {
48928
- ui.error({ command: "init", code: error45.code, message: error45.message, hint: error45.hint });
49014
+ ui.error({
49015
+ command: "init",
49016
+ code: error45.code,
49017
+ message: error45.message,
49018
+ hint: error45.hint
49019
+ });
48929
49020
  return null;
48930
49021
  }
48931
49022
  throw error45;
@@ -48933,7 +49024,7 @@ async function runNew(args) {
48933
49024
  }
48934
49025
  async function resolveTargetDir(dirArg, noPrompt) {
48935
49026
  if (dirArg) {
48936
- return path35.resolve(dirArg);
49027
+ return { targetDir: path35.resolve(dirArg) };
48937
49028
  }
48938
49029
  if (noPrompt) {
48939
49030
  ui.error({
@@ -48945,21 +49036,19 @@ async function resolveTargetDir(dirArg, noPrompt) {
48945
49036
  return null;
48946
49037
  }
48947
49038
  const input = await Ot({
48948
- message: "Project directory",
48949
- placeholder: "my-fimo-site",
48950
- validate: /* @__PURE__ */ __name((value) => value && value.trim().length > 0 ? void 0 : "directory required", "validate")
49039
+ message: "Project name",
49040
+ placeholder: "My Fimo Site",
49041
+ validate: /* @__PURE__ */ __name((value) => value && value.trim().length > 0 ? void 0 : "project name required", "validate")
48951
49042
  });
48952
49043
  if (q(input)) {
48953
49044
  ui.info({ command: "init", message: "Cancelled." });
48954
49045
  return null;
48955
49046
  }
48956
- return path35.resolve(String(input));
49047
+ const displayName = String(input).trim();
49048
+ const folder = slugifyName(displayName) || "fimo-project";
49049
+ return { targetDir: path35.resolve(folder), displayName };
48957
49050
  }
48958
- async function maybeInstallDependencies(targetDir, options) {
48959
- if (options.install === false) {
48960
- return { installed: false, packageManager: null, error: null };
48961
- }
48962
- const packageManager = await resolvePackageManager(options);
49051
+ async function maybeInstallDependencies(targetDir, packageManager) {
48963
49052
  if (!packageManager) {
48964
49053
  return { installed: false, packageManager: null, error: null };
48965
49054
  }
@@ -48977,9 +49066,12 @@ async function resolvePackageManager(options) {
48977
49066
  const selected = await _t({
48978
49067
  message: "Install dependencies with which package manager?",
48979
49068
  initialValue: detected,
48980
- options: PACKAGE_MANAGERS.map((pm) => ({ value: pm, label: pm }))
49069
+ options: [
49070
+ ...PACKAGE_MANAGERS.map((pm) => ({ value: pm, label: pm })),
49071
+ { value: MANUAL_INSTALL, label: "Manual \u2014 I'll install dependencies myself" }
49072
+ ]
48981
49073
  });
48982
- if (q(selected)) {
49074
+ if (q(selected) || selected === MANUAL_INSTALL) {
48983
49075
  return null;
48984
49076
  }
48985
49077
  return selected;
@@ -49000,7 +49092,6 @@ async function maybeInstallSkills(targetDir, options, scaffoldedAgents) {
49000
49092
  } else if (nonInteractive(options) && !agentsFlag) {
49001
49093
  return { setUp: false, error: null };
49002
49094
  }
49003
- const noPrompt = nonInteractive(options);
49004
49095
  const prevCwd = process.cwd();
49005
49096
  let outcome;
49006
49097
  try {
@@ -49008,21 +49099,27 @@ async function maybeInstallSkills(targetDir, options, scaffoldedAgents) {
49008
49099
  outcome = await runSkillsInstall({
49009
49100
  agents: agentsFlag,
49010
49101
  agent: scaffoldedAgents !== null ? void 0 : options.agent,
49011
- mode: noPrompt ? options.mode ?? "symlink" : options.mode,
49012
- yes: noPrompt
49102
+ mode: options.mode ?? "symlink",
49103
+ yes: true
49013
49104
  });
49014
49105
  } finally {
49015
49106
  process.chdir(prevCwd);
49016
49107
  }
49108
+ if (outcome.status === "error" && outcome.code === "NO_AGENTS_SELECTED") {
49109
+ return { setUp: false, error: null };
49110
+ }
49017
49111
  if (outcome.status === "ok") {
49018
- return { setUp: true, error: outcome.data.ok ? null : "Some skills failed to install." };
49112
+ return {
49113
+ setUp: true,
49114
+ error: outcome.data.ok ? null : "Some skills failed to install."
49115
+ };
49019
49116
  }
49020
49117
  if (outcome.status === "cancelled") {
49021
49118
  return { setUp: false, error: null };
49022
49119
  }
49023
49120
  return { setUp: false, error: outcome.message };
49024
49121
  }
49025
- var PACKAGE_MANAGERS;
49122
+ var PACKAGE_MANAGERS, MANUAL_INSTALL;
49026
49123
  var init_init = __esm({
49027
49124
  "src/cli/commands/init.ts"() {
49028
49125
  init_dist6();
@@ -49030,13 +49127,16 @@ var init_init = __esm({
49030
49127
  init_api();
49031
49128
  init_auth();
49032
49129
  init_config2();
49130
+ init_agents();
49033
49131
  init_color();
49034
49132
  init_interactive();
49133
+ init_slug();
49035
49134
  init_clone();
49036
49135
  init_login();
49037
49136
  init_scaffold_project();
49038
49137
  init_skills();
49039
49138
  PACKAGE_MANAGERS = ["npm", "pnpm", "yarn", "bun"];
49139
+ MANUAL_INSTALL = "manual";
49040
49140
  __name(isPretty, "isPretty");
49041
49141
  __name(nonInteractive, "nonInteractive");
49042
49142
  __name(initCommand, "initCommand");
@@ -49044,6 +49144,7 @@ var init_init = __esm({
49044
49144
  __name(renderInitPushFailed, "renderInitPushFailed");
49045
49145
  __name(ensureAuthenticated, "ensureAuthenticated");
49046
49146
  __name(resolveUseExisting, "resolveUseExisting");
49147
+ __name(planInit, "planInit");
49047
49148
  __name(runExisting, "runExisting");
49048
49149
  __name(adoptEmptyRemote, "adoptEmptyRemote");
49049
49150
  __name(runNew, "runNew");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fimo",
3
- "version": "0.3.4-staging.26",
3
+ "version": "0.3.4",
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.staging.fimo.team",
4
- "webUrl": "https://staging.fimo.team"
3
+ "apiUrl": "https://api.fimo.ai",
4
+ "webUrl": "https://fimo.ai"
5
5
  }
@@ -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.3.4-staging.26",
24
+ "fimo": "0.3.4",
25
25
  "input-otp": "^1.4.2",
26
26
  "isbot": "^5",
27
27
  "lucide-react": "^0.577.0",