@hubspot/cli 7.6.0-beta.12 → 7.6.0-beta.14

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 (93) hide show
  1. package/commands/__tests__/getStarted.test.js +2 -2
  2. package/commands/__tests__/mcp.test.js +1 -1
  3. package/commands/__tests__/project.test.js +0 -3
  4. package/commands/app/__tests__/migrate.test.js +0 -1
  5. package/commands/app/migrate.js +4 -5
  6. package/commands/app/secret/add.js +2 -1
  7. package/commands/app/secret/delete.js +2 -1
  8. package/commands/app/secret/list.js +2 -1
  9. package/commands/app/secret/update.js +2 -1
  10. package/commands/app/secret.js +2 -1
  11. package/commands/app.js +2 -2
  12. package/commands/config/set.js +0 -1
  13. package/commands/getStarted.d.ts +0 -2
  14. package/commands/getStarted.js +2 -2
  15. package/commands/mcp/__tests__/setup.test.js +2 -2
  16. package/commands/mcp/setup.js +10 -2
  17. package/commands/mcp.js +2 -2
  18. package/commands/project/__tests__/create.test.js +6 -6
  19. package/commands/project/__tests__/deploy.test.js +0 -3
  20. package/commands/project/__tests__/devUnifiedFlow.test.js +2 -4
  21. package/commands/project/__tests__/logs.test.js +0 -3
  22. package/commands/project/__tests__/migrate.test.js +1 -2
  23. package/commands/project/__tests__/migrateApp.test.js +1 -2
  24. package/commands/project/__tests__/profile.test.js +1 -1
  25. package/commands/project/add.js +1 -5
  26. package/commands/project/create.js +3 -9
  27. package/commands/project/deploy.js +2 -2
  28. package/commands/project/dev/index.js +4 -3
  29. package/commands/project/dev/unifiedFlow.js +1 -2
  30. package/commands/project/download.js +1 -2
  31. package/commands/project/installDeps.js +1 -2
  32. package/commands/project/listBuilds.js +2 -2
  33. package/commands/project/logs.js +2 -2
  34. package/commands/project/migrate.js +41 -13
  35. package/commands/project/migrateApp.js +1 -2
  36. package/commands/project/open.js +1 -2
  37. package/commands/project/profile/add.js +3 -3
  38. package/commands/project/profile/delete.js +1 -2
  39. package/commands/project/profile.js +2 -3
  40. package/commands/project/upload.js +2 -2
  41. package/commands/project/validate.js +1 -1
  42. package/commands/project/watch.js +2 -2
  43. package/commands/project.js +1 -2
  44. package/lang/en.d.ts +14 -3
  45. package/lang/en.js +16 -4
  46. package/lib/app/__tests__/migrate.test.js +14 -51
  47. package/lib/app/migrate.d.ts +2 -8
  48. package/lib/app/migrate.js +5 -80
  49. package/lib/constants.d.ts +6 -0
  50. package/lib/constants.js +6 -0
  51. package/lib/links.d.ts +1 -0
  52. package/lib/links.js +10 -3
  53. package/lib/mcp/setup.js +1 -1
  54. package/lib/middleware/fireAlarmMiddleware.js +15 -5
  55. package/lib/projects/__tests__/LocalDevProcess.test.js +227 -16
  56. package/lib/projects/__tests__/LocalDevWebsocketServer.test.js +16 -21
  57. package/lib/projects/__tests__/deploy.test.js +71 -6
  58. package/lib/projects/create/__tests__/v3.test.js +4 -4
  59. package/lib/projects/create/v3.js +2 -2
  60. package/lib/projects/localDev/LocalDevLogger.d.ts +4 -0
  61. package/lib/projects/localDev/LocalDevLogger.js +22 -0
  62. package/lib/projects/localDev/LocalDevProcess.d.ts +7 -5
  63. package/lib/projects/localDev/LocalDevProcess.js +90 -19
  64. package/lib/projects/localDev/LocalDevState.d.ts +9 -8
  65. package/lib/projects/localDev/LocalDevState.js +18 -17
  66. package/lib/projects/localDev/LocalDevWebsocketServer.d.ts +2 -0
  67. package/lib/projects/localDev/LocalDevWebsocketServer.js +55 -23
  68. package/lib/projects/localDev/localDevWebsocketServerUtils.d.ts +4 -0
  69. package/lib/projects/localDev/localDevWebsocketServerUtils.js +10 -0
  70. package/lib/projects/pollProjectBuildAndDeploy.js +4 -4
  71. package/lib/prompts/projectAddPrompt.js +3 -1
  72. package/lib/prompts/promptUtils.js +3 -0
  73. package/lib/prompts/selectProjectTemplatePrompt.js +2 -0
  74. package/lib/theme/__tests__/migrate.test.d.ts +1 -0
  75. package/lib/theme/__tests__/migrate.test.js +233 -0
  76. package/lib/theme/migrate.d.ts +13 -0
  77. package/lib/theme/migrate.js +90 -0
  78. package/lib/usageTracking.js +2 -2
  79. package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +2 -2
  80. package/mcp-server/tools/project/CreateProjectTool.d.ts +2 -2
  81. package/mcp-server/tools/project/DocsSearchTool.d.ts +4 -1
  82. package/mcp-server/tools/project/DocsSearchTool.js +5 -5
  83. package/mcp-server/tools/project/GetConfigValuesTool.d.ts +4 -1
  84. package/mcp-server/tools/project/GetConfigValuesTool.js +10 -4
  85. package/mcp-server/tools/project/__tests__/DocsSearchTool.test.js +12 -10
  86. package/mcp-server/tools/project/__tests__/GetConfigValuesTool.test.js +8 -7
  87. package/mcp-server/utils/__tests__/cliConfig.test.d.ts +1 -0
  88. package/mcp-server/utils/__tests__/cliConfig.test.js +110 -0
  89. package/mcp-server/utils/cliConfig.d.ts +1 -0
  90. package/mcp-server/utils/cliConfig.js +12 -0
  91. package/package.json +4 -4
  92. package/types/LocalDev.d.ts +19 -3
  93. package/ui/index.js +1 -1
@@ -8,9 +8,14 @@ import { uiCommandReference } from '../../lib/ui/index.js';
8
8
  import { commands, lib } from '../../lang/en.js';
9
9
  import { uiLogger } from '../../lib/ui/logger.js';
10
10
  import { logInBox } from '../../lib/ui/boxen.js';
11
+ import { renderInline } from '../../ui/index.js';
12
+ import { getWarningBox } from '../../ui/components/StatusMessageBoxes.js';
13
+ import { getHasMigratableThemes, migrateThemes2025_2, } from '../../lib/theme/migrate.js';
14
+ import { hasFeature } from '../../lib/hasFeature.js';
15
+ import { FEATURES } from '../../lib/constants.js';
11
16
  const { v2025_2 } = PLATFORM_VERSIONS;
12
17
  const command = 'migrate';
13
- const describe = undefined; // commands.project.migrate.describe
18
+ const describe = commands.project.migrate.describe;
14
19
  async function handler(args) {
15
20
  const { platformVersion, unstable } = args;
16
21
  const projectConfig = await getProjectConfig();
@@ -19,20 +24,44 @@ async function handler(args) {
19
24
  return process.exit(EXIT_CODES.ERROR);
20
25
  }
21
26
  if (projectConfig?.projectConfig) {
22
- await logInBox({
23
- contents: lib.migrate.projectMigrationWarning,
24
- options: { title: lib.migrate.projectMigrationWarningTitle },
25
- });
27
+ if (!process.env.HUBSPOT_ENABLE_INK) {
28
+ await logInBox({
29
+ contents: lib.migrate.projectMigrationWarning,
30
+ options: { title: lib.migrate.projectMigrationWarningTitle },
31
+ });
32
+ }
33
+ else {
34
+ await renderInline(getWarningBox({
35
+ title: lib.migrate.projectMigrationWarningTitle,
36
+ message: lib.migrate.projectMigrationWarning,
37
+ }));
38
+ }
26
39
  }
27
40
  const { derivedAccountId } = args;
28
41
  try {
29
- await migrateApp2025_2(derivedAccountId, {
30
- ...args,
31
- name: projectConfig?.projectConfig?.name,
32
- platformVersion: unstable
33
- ? PLATFORM_VERSIONS.unstable
34
- : platformVersion,
35
- }, projectConfig);
42
+ const { hasMigratableThemes, migratableThemesCount } = await getHasMigratableThemes(projectConfig);
43
+ if (hasMigratableThemes) {
44
+ const hasThemeMigrationAccess = await hasFeature(derivedAccountId, FEATURES.THEME_MIGRATION_2025_2);
45
+ if (!hasThemeMigrationAccess) {
46
+ uiLogger.error(commands.project.migrate.errors.noThemeMigrationAccess(derivedAccountId));
47
+ return process.exit(EXIT_CODES.ERROR);
48
+ }
49
+ await migrateThemes2025_2(derivedAccountId, {
50
+ ...args,
51
+ platformVersion: unstable
52
+ ? PLATFORM_VERSIONS.unstable
53
+ : platformVersion,
54
+ }, migratableThemesCount, projectConfig);
55
+ }
56
+ else {
57
+ await migrateApp2025_2(derivedAccountId, {
58
+ ...args,
59
+ name: projectConfig?.projectConfig?.name,
60
+ platformVersion: unstable
61
+ ? PLATFORM_VERSIONS.unstable
62
+ : platformVersion,
63
+ }, projectConfig);
64
+ }
36
65
  }
37
66
  catch (error) {
38
67
  logError(error);
@@ -46,7 +75,6 @@ function projectMigrateBuilder(yargs) {
46
75
  type: 'string',
47
76
  choices: [v2025_2],
48
77
  default: v2025_2,
49
- hidden: true,
50
78
  })
51
79
  .option('unstable', {
52
80
  type: 'boolean',
@@ -34,8 +34,7 @@ function projectMigrateAppBuilder(yargs) {
34
34
  'platform-version': {
35
35
  type: 'string',
36
36
  choices: [v2023_2, v2025_2],
37
- hidden: true,
38
- default: v2023_2,
37
+ default: v2025_2,
39
38
  },
40
39
  });
41
40
  yargs.example([
@@ -6,11 +6,10 @@ import { getProjectConfig } from '../../lib/projects/config.js';
6
6
  import { ensureProjectExists } from '../../lib/projects/ensureProjectExists.js';
7
7
  import { getProjectDetailUrl } from '../../lib/projects/urls.js';
8
8
  import { projectNamePrompt } from '../../lib/prompts/projectNamePrompt.js';
9
- import { uiBetaTag } from '../../lib/ui/index.js';
10
9
  import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
11
10
  import { makeYargsBuilder } from '../../lib/yargsUtils.js';
12
11
  const command = 'open';
13
- const describe = uiBetaTag(i18n(`commands.project.subcommands.open.describe`), false);
12
+ const describe = i18n(`commands.project.subcommands.open.describe`);
14
13
  async function handler(args) {
15
14
  const { project, derivedAccountId } = args;
16
15
  trackCommandUsage('project-open', undefined, derivedAccountId);
@@ -5,7 +5,7 @@ import { getAccountIdentifier } from '@hubspot/local-dev-lib/config/getAccountId
5
5
  import { getAllHsProfiles, getHsProfileFilename, loadHsProfileFile, } from '@hubspot/project-parsing-lib';
6
6
  import { trackCommandUsage } from '../../../lib/usageTracking.js';
7
7
  import { getProjectConfig } from '../../../lib/projects/config.js';
8
- import { uiBetaTag, uiAccountDescription } from '../../../lib/ui/index.js';
8
+ import { uiAccountDescription } from '../../../lib/ui/index.js';
9
9
  import { uiLogger } from '../../../lib/ui/logger.js';
10
10
  import { EXIT_CODES } from '../../../lib/enums/exitCodes.js';
11
11
  import { makeYargsBuilder } from '../../../lib/yargsUtils.js';
@@ -14,8 +14,8 @@ import { promptUser, listPrompt, confirmPrompt, } from '../../../lib/prompts/pro
14
14
  import { fileExists } from '../../../lib/validation.js';
15
15
  import { debugError } from '../../../lib/errorHandlers/index.js';
16
16
  const command = 'add [name]';
17
- const describe = uiBetaTag(commands.project.profile.add.describe, false);
18
- const verboseDescribe = uiBetaTag(commands.project.profile.add.verboseDescribe, false);
17
+ const describe = commands.project.profile.add.describe;
18
+ const verboseDescribe = commands.project.profile.add.verboseDescribe;
19
19
  async function selectProfileToCopyVariablesFrom(existingProfiles) {
20
20
  let profileToCopyVariablesFrom;
21
21
  if (existingProfiles.length == 1) {
@@ -5,7 +5,6 @@ import { fetchProject, deleteProject, } from '@hubspot/local-dev-lib/api/project
5
5
  import { getAccountConfig } from '@hubspot/local-dev-lib/config';
6
6
  import { trackCommandUsage } from '../../../lib/usageTracking.js';
7
7
  import { getProjectConfig } from '../../../lib/projects/config.js';
8
- import { uiBetaTag } from '../../../lib/ui/index.js';
9
8
  import { uiLogger } from '../../../lib/ui/logger.js';
10
9
  import { EXIT_CODES } from '../../../lib/enums/exitCodes.js';
11
10
  import { makeYargsBuilder } from '../../../lib/yargsUtils.js';
@@ -15,7 +14,7 @@ import { fileExists } from '../../../lib/validation.js';
15
14
  import { debugError } from '../../../lib/errorHandlers/index.js';
16
15
  import { isDeveloperTestAccount, isSandbox, } from '../../../lib/accountTypes.js';
17
16
  const command = 'delete [name]';
18
- const describe = uiBetaTag(commands.project.profile.delete.describe, false);
17
+ const describe = commands.project.profile.delete.describe;
19
18
  async function handler(args) {
20
19
  const { derivedAccountId } = args;
21
20
  trackCommandUsage('project-profile-delete', undefined, derivedAccountId);
@@ -1,11 +1,10 @@
1
- import { uiBetaTag } from '../../lib/ui/index.js';
2
1
  import add from './profile/add.js';
3
2
  import deleteProfile from './profile/delete.js';
4
3
  import { makeYargsBuilder } from '../../lib/yargsUtils.js';
5
4
  import { commands } from '../../lang/en.js';
6
5
  const command = ['profile', 'profiles'];
7
- const describe = undefined; // uiBetaTag(commands.project.profile.describe, false);
8
- const verboseDescribe = uiBetaTag(commands.project.profile.verboseDescribe, false);
6
+ const describe = commands.project.profile.describe;
7
+ const verboseDescribe = commands.project.profile.verboseDescribe;
9
8
  function projectProfileBuilder(yargs) {
10
9
  yargs.command(add).command(deleteProfile).demandCommand(1, '');
11
10
  return yargs;
@@ -3,7 +3,7 @@ import { logger } from '@hubspot/local-dev-lib/logger';
3
3
  import { getAccountConfig } from '@hubspot/local-dev-lib/config';
4
4
  import { isSpecifiedError } from '@hubspot/local-dev-lib/errors/index';
5
5
  import { useV3Api } from '../../lib/projects/platformVersion.js';
6
- import { uiBetaTag, uiCommandReference } from '../../lib/ui/index.js';
6
+ import { uiCommandReference } from '../../lib/ui/index.js';
7
7
  import { trackCommandUsage } from '../../lib/usageTracking.js';
8
8
  import { getProjectConfig, validateProjectConfig, } from '../../lib/projects/config.js';
9
9
  import { logFeedbackMessage } from '../../lib/projects/ui.js';
@@ -17,7 +17,7 @@ import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
17
17
  import { makeYargsBuilder } from '../../lib/yargsUtils.js';
18
18
  import { uiLogger } from '../../lib/ui/logger.js';
19
19
  const command = 'upload';
20
- const describe = uiBetaTag(i18n(`commands.project.subcommands.upload.describe`), false);
20
+ const describe = i18n(`commands.project.subcommands.upload.describe`);
21
21
  async function handler(args) {
22
22
  const { forceCreate, message, derivedAccountId, skipValidation, formatOutputAsJson, profile, } = args;
23
23
  const jsonOutput = {};
@@ -11,7 +11,7 @@ import { commands } from '../../lang/en.js';
11
11
  import { loadAndValidateProfile } from '../../lib/projectProfiles.js';
12
12
  import { logError } from '../../lib/errorHandlers/index.js';
13
13
  const command = 'validate';
14
- const describe = undefined;
14
+ const describe = commands.project.validate.describe;
15
15
  async function handler(args) {
16
16
  const { derivedAccountId, profile } = args;
17
17
  const { projectConfig, projectDir } = await getProjectConfig();
@@ -1,7 +1,7 @@
1
1
  import { cancelStagedBuild, fetchProjectBuilds, } from '@hubspot/local-dev-lib/api/projects';
2
2
  import { isSpecifiedError } from '@hubspot/local-dev-lib/errors/index';
3
3
  import { useV3Api } from '../../lib/projects/platformVersion.js';
4
- import { uiCommandReference, uiLink, uiBetaTag } from '../../lib/ui/index.js';
4
+ import { uiCommandReference, uiLink } from '../../lib/ui/index.js';
5
5
  import { i18n } from '../../lib/lang.js';
6
6
  import { createWatcher } from '../../lib/projects/watch.js';
7
7
  import { logError, ApiErrorContext } from '../../lib/errorHandlers/index.js';
@@ -16,7 +16,7 @@ import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
16
16
  import { handleKeypress, handleExit } from '../../lib/process.js';
17
17
  import { makeYargsBuilder } from '../../lib/yargsUtils.js';
18
18
  const command = 'watch';
19
- const describe = uiBetaTag(i18n(`commands.project.subcommands.watch.describe`), false);
19
+ const describe = i18n(`commands.project.subcommands.watch.describe`);
20
20
  async function handleBuildStatus(accountId, projectName, buildId) {
21
21
  const { isAutoDeployEnabled, deployStatusTaskLocator } = await pollBuildStatus(accountId, projectName, buildId, null);
22
22
  if (isAutoDeployEnabled && deployStatusTaskLocator) {
@@ -1,5 +1,4 @@
1
1
  import { i18n } from '../lib/lang.js';
2
- import { uiBetaTag } from '../lib/ui/index.js';
3
2
  import deploy from './project/deploy.js';
4
3
  import create from './project/create.js';
5
4
  import upload from './project/upload.js';
@@ -18,7 +17,7 @@ import profile from './project/profile.js';
18
17
  import projectValidate from './project/validate.js';
19
18
  import { makeYargsBuilder } from '../lib/yargsUtils.js';
20
19
  const command = ['project', 'projects'];
21
- const describe = uiBetaTag(i18n(`commands.project.describe`), false);
20
+ const describe = i18n(`commands.project.describe`);
22
21
  function projectBuilder(yargs) {
23
22
  yargs
24
23
  .command(create)
package/lang/en.d.ts CHANGED
@@ -10,6 +10,7 @@ export declare const commands: {
10
10
  };
11
11
  };
12
12
  readonly getStarted: {
13
+ readonly describe: "A step-by-step command to get you started with a HubSpot project.";
13
14
  readonly options: {
14
15
  readonly dest: {
15
16
  readonly describe: "Directory where the project should be created";
@@ -859,8 +860,9 @@ Global configuration replaces hubspot.config.yml, and you will be prompted to mi
859
860
  readonly tailLogs: (functionPath: string, accountId: string) => string;
860
861
  };
861
862
  readonly mcp: {
862
- readonly describe: "Commands for managing HubSpot MCP servers";
863
+ readonly describe: "Commands for managing HubSpot MCP servers.";
863
864
  readonly setup: {
865
+ readonly describe: "Setup the HubSpot development MCP servers.";
864
866
  readonly installingDocSearch: "Adding the docs-search mcp server";
865
867
  readonly claudeCode: "Claude Code";
866
868
  readonly cursor: "Cursor";
@@ -872,6 +874,7 @@ Global configuration replaces hubspot.config.yml, and you will be prompted to mi
872
874
  };
873
875
  readonly success: (derivedTargets: string[]) => string;
874
876
  readonly errors: {
877
+ readonly needsMcpAccess: (accountId?: number) => string;
875
878
  readonly needsNode20: "This feature requires node >=20";
876
879
  readonly errorParsingJsonFIle: (filename: string, errorMessage: string) => string;
877
880
  };
@@ -1008,7 +1011,7 @@ Profiles enable you to reference variables in your component configuration files
1008
1011
  readonly dev: {
1009
1012
  readonly describe: "Start local dev for the current project.";
1010
1013
  readonly logs: {
1011
- readonly betaMessage: "HubSpot projects local development";
1014
+ readonly header: "HubSpot projects local development";
1012
1015
  readonly placeholderAccountSelection: "Using default account as target account (for now)";
1013
1016
  readonly accountTypeInformation: "Testing in a developer test account is strongly recommended, but you can use a sandbox account if your plan allows you to create one.";
1014
1017
  readonly learnMoreMessageV3: `Learn more about ${string} | ${string}`;
@@ -1070,7 +1073,7 @@ ${string}`;
1070
1073
  readonly describe: "Project name (cannot be changed)";
1071
1074
  };
1072
1075
  readonly template: {
1073
- readonly describe: "The starting template";
1076
+ readonly describe: "The starting template. Only applies when platform version is less than 2025.2.";
1074
1077
  };
1075
1078
  readonly templateSource: {
1076
1079
  readonly describe: "Path to custom GitHub repository from which to create project template";
@@ -1136,6 +1139,7 @@ ${string}`;
1136
1139
  readonly describe: "Migrate an existing project to the new version of the projects framework.";
1137
1140
  readonly errors: {
1138
1141
  readonly noProjectConfig: (command: string) => string;
1142
+ readonly noThemeMigrationAccess: (accountId?: number) => string;
1139
1143
  };
1140
1144
  readonly examples: {
1141
1145
  readonly default: "Migrate an existing project to the new version of the projects framework.";
@@ -2591,8 +2595,13 @@ export declare const lib: {
2591
2595
  readonly LocalDevProcess: {
2592
2596
  readonly projectConfigMismatch: `Unable to upload project. The project config has been modified since starting ${string}.`;
2593
2597
  readonly uploadInitiated: "Project upload initiated from Local Dev UI.";
2598
+ readonly deployInitiated: "Project deploy initiated from Local Dev UI.";
2594
2599
  readonly uploadFailed: "Project upload failed. To proceed with local development, fix any necessary errors, then re-upload your project.";
2600
+ readonly deployFailed: "Project deploy failed. To proceed with local development, fix any necessary errors, then re-deploy your project.";
2595
2601
  readonly uploadSuccess: "Project upload completed successfully. Resuming local dev...";
2602
+ readonly uploadSuccessAutoDeployDisabled: "Project upload completed successfully, but auto-deploy is disabled for this project. Deploy your latest build to proceed with local development.";
2603
+ readonly deploySuccess: "Project deploy completed successfully. Resuming local dev...";
2604
+ readonly noBuildToDeploy: "Error deploying project. No build was found to deploy.";
2596
2605
  };
2597
2606
  readonly localDevHelpers: {
2598
2607
  readonly project: {
@@ -3111,6 +3120,7 @@ Run ${string} to upgrade to version ${string}`;
3111
3120
  };
3112
3121
  readonly projectAddPrompt: {
3113
3122
  readonly selectType: "[--type] Select an app feature to add: ";
3123
+ readonly selectFeatures: "[--features] Select an app feature to add: ";
3114
3124
  readonly enterName: "[--name] Give your component a name: ";
3115
3125
  readonly errors: {
3116
3126
  readonly nameRequired: "A component name is required";
@@ -3388,6 +3398,7 @@ Run ${string} to upgrade to version ${string}`;
3388
3398
  readonly sourceContentsMoved: (newLocation: string) => string;
3389
3399
  readonly projectMigrationWarningTitle: "Important: Migrating to platformVersion 2025.2 is irreversible";
3390
3400
  readonly projectMigrationWarning: string;
3401
+ readonly exitWithoutMigrating: "Exiting without migrating";
3391
3402
  readonly success: {
3392
3403
  readonly downloadedProject: (projectName: string, projectDest: string) => string;
3393
3404
  readonly themesMigrationSuccess: (platformVersion: string) => string;
package/lang/en.js CHANGED
@@ -5,6 +5,7 @@ import { PERSONAL_ACCESS_KEY_AUTH_METHOD } from '@hubspot/local-dev-lib/constant
5
5
  import { ARCHIVED_HUBSPOT_CONFIG_YAML_FILE_NAME, GLOBAL_CONFIG_PATH, DEFAULT_HUBSPOT_CONFIG_YAML_FILE_NAME, } from '@hubspot/local-dev-lib/constants/config';
6
6
  import { uiAccountDescription, uiBetaTag, uiCommandReference, uiLink, UI_COLORS, } from '../lib/ui/index.js';
7
7
  import { getProjectDetailUrl, getProjectSettingsUrl, getLocalDevUiUrl, getAppAllowlistUrl, } from '../lib/projects/urls.js';
8
+ import { getProductUpdatesUrl } from '../lib/links.js';
8
9
  import { APP_DISTRIBUTION_TYPES, APP_AUTH_TYPES, PROJECT_CONFIG_FILE, PROJECT_WITH_APP, } from '../lib/constants.js';
9
10
  import { getAccountIdentifier } from '@hubspot/local-dev-lib/config/getAccountIdentifier';
10
11
  export const commands = {
@@ -18,6 +19,7 @@ export const commands = {
18
19
  },
19
20
  },
20
21
  getStarted: {
22
+ describe: 'A step-by-step command to get you started with a HubSpot project.',
21
23
  options: {
22
24
  dest: {
23
25
  describe: 'Directory where the project should be created',
@@ -862,8 +864,9 @@ export const commands = {
862
864
  tailLogs: (functionPath, accountId) => `Waiting for log entries for "${functionPath}" on account "${accountId}".\n`,
863
865
  },
864
866
  mcp: {
865
- describe: 'Commands for managing HubSpot MCP servers',
867
+ describe: 'Commands for managing HubSpot MCP servers.',
866
868
  setup: {
869
+ describe: 'Setup the HubSpot development MCP servers.',
867
870
  installingDocSearch: 'Adding the docs-search mcp server',
868
871
  claudeCode: 'Claude Code',
869
872
  cursor: 'Cursor',
@@ -875,6 +878,7 @@ export const commands = {
875
878
  },
876
879
  success: (derivedTargets) => `You can now use the HubSpot CLI MCP Server in ${derivedTargets.join(', ')}. ${chalk.bold('You may need to restart these tools to apply the changes')}.`,
877
880
  errors: {
881
+ needsMcpAccess: (accountId) => `You must opt in to the developer MCP beta to use this feature on ${uiAccountDescription(accountId)}. Try again with a different account or ${uiLink('join the beta now', getProductUpdatesUrl('239890', accountId))}`,
878
882
  needsNode20: `This feature requires node >=20`,
879
883
  errorParsingJsonFIle: (filename, errorMessage) => `Unable to update ${chalk.bold(filename)} due to invalid JSON: ${errorMessage}`,
880
884
  },
@@ -1009,7 +1013,7 @@ export const commands = {
1009
1013
  dev: {
1010
1014
  describe: 'Start local dev for the current project.',
1011
1015
  logs: {
1012
- betaMessage: 'HubSpot projects local development',
1016
+ header: 'HubSpot projects local development',
1013
1017
  placeholderAccountSelection: 'Using default account as target account (for now)',
1014
1018
  accountTypeInformation: 'Testing in a developer test account is strongly recommended, but you can use a sandbox account if your plan allows you to create one.',
1015
1019
  learnMoreMessageV3: `Learn more about ${uiLink('HubSpot projects local dev', 'https://hubspot.mintlify.io/en-us/developer-tooling/local-development/hubspot-cli/project-commands#start-a-local-development-server')} | ${uiLink('HubSpot account types', 'https://developers.hubspot.com/docs/getting-started/account-types')}`,
@@ -1069,7 +1073,7 @@ export const commands = {
1069
1073
  describe: 'Project name (cannot be changed)',
1070
1074
  },
1071
1075
  template: {
1072
- describe: 'The starting template',
1076
+ describe: 'The starting template. Only applies when platform version is less than 2025.2.',
1073
1077
  },
1074
1078
  templateSource: {
1075
1079
  describe: 'Path to custom GitHub repository from which to create project template',
@@ -1135,6 +1139,7 @@ export const commands = {
1135
1139
  describe: 'Migrate an existing project to the new version of the projects framework.',
1136
1140
  errors: {
1137
1141
  noProjectConfig: (command) => `No project detected. Please run this command again from a project directory. If you are trying to migrate an app, run ${command}`,
1142
+ noThemeMigrationAccess: (accountId) => `This project contains a CMS theme. You must opt in to theme migration beta to continue updating it on ${uiAccountDescription(accountId)}. Try again with a different account or ${uiLink('join the beta now', getProductUpdatesUrl('253920', accountId))}`,
1138
1143
  },
1139
1144
  examples: {
1140
1145
  default: 'Migrate an existing project to the new version of the projects framework.',
@@ -1192,7 +1197,7 @@ export const commands = {
1192
1197
  maxExceeded: (maxCount) => `This project has the maximum allowed(${maxCount})`,
1193
1198
  authTypeNotAllowed: (authType) => `Auth type '${authType}' not allowed.`,
1194
1199
  distributionNotAllowed: (dist) => `Distribution '${dist}' not allowed.`,
1195
- portalDoesNotHaveAccessToThisFeature: (accountId) => `The account ${uiAccountDescription(accountId)} does not have access to this feature`,
1200
+ portalDoesNotHaveAccessToThisFeature: (accountId) => `The account ${uiAccountDescription(accountId)} does not have access to this feature.`,
1196
1201
  locationInProject: 'This command must be run from within a project directory.',
1197
1202
  failedToFetchComponentList: 'Failed to fetch the list of available features. Please try again later.',
1198
1203
  projectContainsPublicApp: 'This project contains a public app. This command is currently only compatible with projects that contain private apps.',
@@ -2588,8 +2593,13 @@ export const lib = {
2588
2593
  LocalDevProcess: {
2589
2594
  projectConfigMismatch: `Unable to upload project. The project config has been modified since starting ${uiCommandReference('hs project dev')}.`,
2590
2595
  uploadInitiated: 'Project upload initiated from Local Dev UI.',
2596
+ deployInitiated: 'Project deploy initiated from Local Dev UI.',
2591
2597
  uploadFailed: 'Project upload failed. To proceed with local development, fix any necessary errors, then re-upload your project.',
2598
+ deployFailed: 'Project deploy failed. To proceed with local development, fix any necessary errors, then re-deploy your project.',
2592
2599
  uploadSuccess: 'Project upload completed successfully. Resuming local dev...',
2600
+ uploadSuccessAutoDeployDisabled: 'Project upload completed successfully, but auto-deploy is disabled for this project. Deploy your latest build to proceed with local development.',
2601
+ deploySuccess: 'Project deploy completed successfully. Resuming local dev...',
2602
+ noBuildToDeploy: 'Error deploying project. No build was found to deploy.',
2593
2603
  },
2594
2604
  localDevHelpers: {
2595
2605
  project: {
@@ -3105,6 +3115,7 @@ export const lib = {
3105
3115
  },
3106
3116
  projectAddPrompt: {
3107
3117
  selectType: '[--type] Select an app feature to add: ',
3118
+ selectFeatures: '[--features] Select an app feature to add: ',
3108
3119
  enterName: '[--name] Give your component a name: ',
3109
3120
  errors: {
3110
3121
  nameRequired: 'A component name is required',
@@ -3382,6 +3393,7 @@ export const lib = {
3382
3393
  sourceContentsMoved: (newLocation) => `The contents of your old source directory have been moved to ${newLocation}, move any required files to the new source directory.`,
3383
3394
  projectMigrationWarningTitle: 'Important: Migrating to platformVersion 2025.2 is irreversible',
3384
3395
  projectMigrationWarning: uiBetaTag(`Running the ${uiCommandReference('hs project migrate')} command will permanently upgrade your project to platformVersion 2025.2. This action cannot be undone. To ensure you have access to your original files, they will be copied to a new directory (archive) for safekeeping.\n\nThis command will guide you through the process, prompting you to enter the required fields and will download the new project source code into your project source directory.`, false),
3396
+ exitWithoutMigrating: 'Exiting without migrating',
3385
3397
  success: {
3386
3398
  downloadedProject: (projectName, projectDest) => `Saved ${projectName} to ${projectDest}`,
3387
3399
  themesMigrationSuccess: (platformVersion) => `Successfully migrated project to platformVersion ${chalk.bold(platformVersion)}. Upload your project using ${uiCommandReference('hs project upload')}`,
@@ -1,7 +1,7 @@
1
1
  import { logger } from '@hubspot/local-dev-lib/logger';
2
2
  import { getCwd, sanitizeFileName } from '@hubspot/local-dev-lib/path';
3
3
  import { extractZipArchive } from '@hubspot/local-dev-lib/archive';
4
- import { validateUid, getProjectThemeDetails, } from '@hubspot/project-parsing-lib';
4
+ import { validateUid } from '@hubspot/project-parsing-lib';
5
5
  import { UNMIGRATABLE_REASONS } from '@hubspot/local-dev-lib/constants/projects';
6
6
  import { MIGRATION_STATUS } from '@hubspot/local-dev-lib/types/Migration';
7
7
  import { downloadProject } from '@hubspot/local-dev-lib/api/projects';
@@ -12,7 +12,7 @@ import { poll } from '../../polling.js';
12
12
  import { CLI_UNMIGRATABLE_REASONS, continueAppMigration, initializeAppMigration, listAppsForMigration, } from '../../../api/migrate.js';
13
13
  import { lib } from '../../../lang/en.js';
14
14
  import { hasUnfiedAppsAccess } from '../../hasFeature.js';
15
- import { getUnmigratableReason, generateFilterAppsByProjectNameFunction, buildErrorMessageFromMigrationStatus, fetchMigrationApps, promptForAppToMigrate, selectAppToMigrate, handleMigrationSetup, getHasMigratableThemes, beginAppMigration, pollMigrationStatus, finalizeAppMigration, downloadProjectFiles, migrateApp2025_2, logInvalidAccountError, validateMigrationAppsAndThemes, } from '../migrate.js';
15
+ import { getUnmigratableReason, generateFilterAppsByProjectNameFunction, buildErrorMessageFromMigrationStatus, fetchMigrationApps, promptForAppToMigrate, selectAppToMigrate, handleMigrationSetup, beginAppMigration, pollMigrationStatus, finalizeAppMigration, downloadProjectFiles, migrateApp2025_2, logInvalidAccountError, validateMigrationApps, } from '../migrate.js';
16
16
  vi.mock('@hubspot/local-dev-lib/logger');
17
17
  vi.mock('@hubspot/local-dev-lib/path');
18
18
  vi.mock('@hubspot/local-dev-lib/archive');
@@ -40,7 +40,6 @@ const mockedListPrompt = listPrompt;
40
40
  const mockedEnsureProjectExists = ensureProjectExists;
41
41
  const mockedPoll = poll;
42
42
  const mockedListAppsForMigration = listAppsForMigration;
43
- const mockedGetProjectThemeDetails = getProjectThemeDetails;
44
43
  const mockedInitializeAppMigration = initializeAppMigration;
45
44
  const mockedContinueAppMigration = continueAppMigration;
46
45
  const mockedHasUnfiedAppsAccess = hasUnfiedAppsAccess;
@@ -205,58 +204,31 @@ describe('lib/app/migrate', () => {
205
204
  expect(result.migratableApps[0].projectName).toBe(PROJECT_NAME);
206
205
  });
207
206
  });
208
- describe('getHasMigratableThemes', () => {
209
- it('should return false when no projectConfig is provided', async () => {
210
- const result = await getHasMigratableThemes();
211
- expect(result).toEqual({
212
- hasMigratableThemes: false,
213
- migratableThemesCount: 0,
214
- });
215
- });
216
- it('should return true when there are migratable themes', async () => {
217
- mockedGetProjectThemeDetails.mockResolvedValue({
218
- legacyThemeDetails: [
219
- {
220
- configFilepath: 'src/theme.json',
221
- themePath: 'src/theme',
222
- themeConfig: {
223
- secret_names: ['my-secret'],
224
- },
225
- },
226
- ],
227
- legacyReactThemeDetails: [],
228
- });
229
- const projectConfig = createLoadedProjectConfig(PROJECT_NAME);
230
- const result = await getHasMigratableThemes(projectConfig);
231
- expect(result).toEqual({
232
- hasMigratableThemes: true,
233
- migratableThemesCount: 1,
234
- });
235
- });
236
- });
237
- describe('validateMigrationAppsAndThemes', () => {
207
+ describe('validateMigrationApps', () => {
238
208
  const mockMigratableApp1 = createMockMigratableApp(1, 'App 1', PROJECT_NAME);
239
209
  const mockMigratableApp2 = createMockMigratableApp(2, 'App 2', PROJECT_NAME);
240
210
  it('should throw an error when multiple apps are found for a project', async () => {
241
211
  const projectConfig = createLoadedProjectConfig(PROJECT_NAME);
242
- await expect(validateMigrationAppsAndThemes(APP_ID, ACCOUNT_ID, {
212
+ await expect(validateMigrationApps(APP_ID, ACCOUNT_ID, {
243
213
  migratableApps: [mockMigratableApp1, mockMigratableApp2],
244
214
  unmigratableApps: [],
245
- }, false, projectConfig)).rejects.toThrow(lib.migrate.errors.project.multipleApps);
246
- });
247
- it('should throw an error when cms themes are found for a project', async () => {
248
- const projectConfig = createLoadedProjectConfig(PROJECT_NAME);
249
- await expect(validateMigrationAppsAndThemes(APP_ID, ACCOUNT_ID, { migratableApps: [mockMigratableApp1], unmigratableApps: [] }, true, projectConfig)).rejects.toThrow(lib.migrate.errors.project.themesAndAppsNotAllowed);
215
+ }, projectConfig)).rejects.toThrow(lib.migrate.errors.project.multipleApps);
250
216
  });
251
217
  it('should throw an error when no apps are found for a project', async () => {
252
218
  const projectConfig = createLoadedProjectConfig(PROJECT_NAME);
253
- await expect(validateMigrationAppsAndThemes(APP_ID, ACCOUNT_ID, { migratableApps: [], unmigratableApps: [] }, false, projectConfig)).rejects.toThrow(lib.migrate.errors.noAppsForProject(PROJECT_NAME));
219
+ await expect(validateMigrationApps(APP_ID, ACCOUNT_ID, { migratableApps: [], unmigratableApps: [] }, projectConfig)).rejects.toThrow(lib.migrate.errors.noAppsForProject(PROJECT_NAME));
254
220
  });
255
221
  it('should throw an error when no migratable apps are found', async () => {
256
- await expect(validateMigrationAppsAndThemes(APP_ID, ACCOUNT_ID, { migratableApps: [], unmigratableApps: mockUnmigratableApps }, false)).rejects.toThrow(/No apps in account/);
222
+ await expect(validateMigrationApps(APP_ID, ACCOUNT_ID, {
223
+ migratableApps: [],
224
+ unmigratableApps: mockUnmigratableApps,
225
+ })).rejects.toThrow(/No apps in account/);
257
226
  });
258
227
  it('should throw an error when appId is provided but not found', async () => {
259
- await expect(validateMigrationAppsAndThemes(APP_ID, ACCOUNT_ID, { migratableApps: [], unmigratableApps: [] }, false)).rejects.toThrow(/No apps in account/);
228
+ await expect(validateMigrationApps(APP_ID, ACCOUNT_ID, {
229
+ migratableApps: [],
230
+ unmigratableApps: [],
231
+ })).rejects.toThrow(/No apps in account/);
260
232
  });
261
233
  });
262
234
  describe('promptForAppToMigrate', () => {
@@ -330,10 +302,6 @@ describe('lib/app/migrate', () => {
330
302
  unmigratableApps: [],
331
303
  },
332
304
  });
333
- mockedGetProjectThemeDetails.mockResolvedValue({
334
- legacyThemeDetails: [],
335
- legacyReactThemeDetails: [],
336
- });
337
305
  mockedListPrompt.mockResolvedValue({ appId: 1 });
338
306
  mockedConfirmPrompt.mockResolvedValue(true);
339
307
  mockedEnsureProjectExists.mockResolvedValue({ projectExists: false });
@@ -352,16 +320,11 @@ describe('lib/app/migrate', () => {
352
320
  unmigratableApps: [],
353
321
  },
354
322
  });
355
- mockedGetProjectThemeDetails.mockResolvedValueOnce({
356
- legacyThemeDetails: [],
357
- legacyReactThemeDetails: [],
358
- });
359
323
  const result = await handleMigrationSetup(ACCOUNT_ID, defaultOptions, projectConfig);
360
324
  expect(result).toEqual({
361
325
  appIdToMigrate: 1,
362
326
  projectName: PROJECT_NAME,
363
327
  projectDest: MOCK_PROJECT_DIR,
364
- isThemesMigration: false,
365
328
  });
366
329
  });
367
330
  it('should prompt for project name when not provided', async () => {
@@ -12,18 +12,14 @@ export type MigrateAppArgs = CommonArgs & AccountArgs & EnvironmentArgs & Config
12
12
  export declare function getUnmigratableReason(reasonCode: string, projectName: string | undefined, accountId: number): string;
13
13
  export declare function generateFilterAppsByProjectNameFunction(projectConfig?: LoadedProjectConfig): (app: MigrationApp) => boolean;
14
14
  export declare function buildErrorMessageFromMigrationStatus(error: MigrationFailed): string;
15
- export declare function getHasMigratableThemes(projectConfig?: LoadedProjectConfig): Promise<{
16
- hasMigratableThemes: boolean;
17
- migratableThemesCount: number;
18
- }>;
19
15
  export declare function fetchMigrationApps(derivedAccountId: number, platformVersion: string, projectConfig?: LoadedProjectConfig): Promise<{
20
16
  migratableApps: MigratableApp[];
21
17
  unmigratableApps: UnmigratableApp[];
22
18
  }>;
23
- export declare function validateMigrationAppsAndThemes(appId: MigrateAppArgs['appId'], derivedAccountId: number, { migratableApps, unmigratableApps, }: {
19
+ export declare function validateMigrationApps(appId: MigrateAppArgs['appId'], derivedAccountId: number, { migratableApps, unmigratableApps, }: {
24
20
  migratableApps: MigratableApp[];
25
21
  unmigratableApps: UnmigratableApp[];
26
- }, hasMigratableThemes: boolean, projectConfig?: LoadedProjectConfig): Promise<void>;
22
+ }, projectConfig?: LoadedProjectConfig): Promise<void>;
27
23
  export declare function promptForAppToMigrate(allApps: MigrationApp[], derivedAccountId: number): Promise<number>;
28
24
  export declare function selectAppToMigrate(allApps: MigrationApp[], derivedAccountId: number, appId?: number): Promise<{
29
25
  proceed: boolean;
@@ -33,9 +29,7 @@ export declare function handleMigrationSetup(derivedAccountId: number, options:
33
29
  appIdToMigrate?: number | undefined;
34
30
  projectName?: string;
35
31
  projectDest?: string;
36
- isThemesMigration?: boolean;
37
32
  }>;
38
- export declare function handleThemesMigration(projectConfig: LoadedProjectConfig, platformVersion: string): Promise<void>;
39
33
  export declare function beginAppMigration(derivedAccountId: number, appId: number, platformVersion: string): Promise<{
40
34
  migrationId: number;
41
35
  uidMap: Record<string, string>;