@hubspot/cli 7.6.0-beta.11 → 7.6.0-beta.13

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 (158) hide show
  1. package/commands/app/__tests__/migrate.test.js +1 -0
  2. package/commands/getStarted.js +7 -20
  3. package/commands/mcp/setup.d.ts +0 -1
  4. package/commands/mcp/setup.js +11 -11
  5. package/commands/project/__tests__/add.test.js +64 -0
  6. package/commands/project/__tests__/create.test.js +57 -0
  7. package/commands/project/__tests__/deploy.test.js +3 -2
  8. package/commands/project/add.d.ts +1 -1
  9. package/commands/project/add.js +3 -5
  10. package/commands/project/create.js +7 -2
  11. package/commands/project/deploy.js +9 -61
  12. package/commands/project/dev/index.js +1 -1
  13. package/commands/project/dev/unifiedFlow.js +4 -1
  14. package/commands/project/migrate.js +26 -7
  15. package/commands/project/upload.js +2 -2
  16. package/commands/project/validate.js +1 -1
  17. package/commands/project/watch.js +2 -2
  18. package/lang/en.d.ts +20 -5
  19. package/lang/en.js +38 -22
  20. package/lang/en.lyaml +12 -12
  21. package/lib/__tests__/hasFeature.test.js +145 -7
  22. package/lib/__tests__/importData.test.js +1 -1
  23. package/lib/app/__tests__/migrate.test.js +14 -51
  24. package/lib/app/migrate.d.ts +2 -8
  25. package/lib/app/migrate.js +5 -73
  26. package/lib/constants.d.ts +4 -0
  27. package/lib/constants.js +4 -0
  28. package/lib/errorHandlers/index.d.ts +4 -0
  29. package/lib/errorHandlers/index.js +1 -1
  30. package/lib/hasFeature.js +6 -0
  31. package/lib/importData.js +1 -1
  32. package/lib/links.d.ts +1 -0
  33. package/lib/links.js +10 -3
  34. package/lib/mcp/setup.d.ts +0 -2
  35. package/lib/mcp/setup.js +4 -29
  36. package/lib/projects/__tests__/AppDevModeInterface.test.js +71 -44
  37. package/lib/projects/__tests__/LocalDevProcess.test.js +1 -0
  38. package/lib/projects/__tests__/components.test.js +164 -7
  39. package/lib/projects/__tests__/deploy.test.js +164 -0
  40. package/lib/projects/__tests__/platformVersion.test.d.ts +1 -0
  41. package/lib/projects/__tests__/{buildAndDeploy.test.js → platformVersion.test.js} +2 -2
  42. package/lib/projects/add/__tests__/legacyAddComponent.test.js +49 -6
  43. package/lib/projects/add/__tests__/v3AddComponent.test.js +142 -8
  44. package/lib/projects/add/legacyAddComponent.d.ts +1 -1
  45. package/lib/projects/add/legacyAddComponent.js +5 -1
  46. package/lib/projects/add/v3AddComponent.d.ts +2 -1
  47. package/lib/projects/add/v3AddComponent.js +22 -5
  48. package/lib/projects/components.d.ts +1 -0
  49. package/lib/projects/components.js +27 -1
  50. package/lib/projects/create/__tests__/v3.test.js +97 -9
  51. package/lib/projects/create/index.js +2 -2
  52. package/lib/projects/create/legacy.js +1 -1
  53. package/lib/projects/create/v3.d.ts +2 -2
  54. package/lib/projects/create/v3.js +35 -12
  55. package/lib/projects/deploy.d.ts +13 -0
  56. package/lib/projects/deploy.js +63 -0
  57. package/lib/projects/localDev/AppDevModeInterface.d.ts +5 -3
  58. package/lib/projects/localDev/AppDevModeInterface.js +110 -47
  59. package/lib/projects/localDev/DevServerManagerV2.js +1 -0
  60. package/lib/projects/localDev/LocalDevProcess.js +3 -1
  61. package/lib/projects/localDev/LocalDevState.d.ts +5 -2
  62. package/lib/projects/localDev/LocalDevState.js +9 -1
  63. package/lib/projects/localDev/helpers/project.d.ts +2 -2
  64. package/lib/projects/localDev/helpers/project.js +6 -8
  65. package/lib/projects/platformVersion.d.ts +1 -0
  66. package/lib/projects/platformVersion.js +10 -0
  67. package/lib/projects/{buildAndDeploy.d.ts → pollProjectBuildAndDeploy.d.ts} +0 -1
  68. package/lib/projects/{buildAndDeploy.js → pollProjectBuildAndDeploy.js} +0 -10
  69. package/lib/projects/upload.js +1 -1
  70. package/lib/projects/urls.d.ts +1 -0
  71. package/lib/projects/urls.js +3 -0
  72. package/lib/prompts/__tests__/projectAddPrompt.test.d.ts +1 -0
  73. package/lib/prompts/__tests__/projectAddPrompt.test.js +143 -0
  74. package/lib/prompts/__tests__/selectProjectTemplatePrompt.test.d.ts +1 -0
  75. package/lib/prompts/__tests__/selectProjectTemplatePrompt.test.js +160 -0
  76. package/lib/prompts/createDeveloperTestAccountConfigPrompt.js +1 -0
  77. package/lib/prompts/importDataFilePathPrompt.js +4 -2
  78. package/lib/prompts/installAppPrompt.d.ts +6 -1
  79. package/lib/prompts/installAppPrompt.js +6 -1
  80. package/lib/prompts/projectAddPrompt.js +1 -1
  81. package/lib/prompts/promptUtils.d.ts +5 -0
  82. package/lib/prompts/promptUtils.js +9 -0
  83. package/lib/prompts/selectProjectTemplatePrompt.js +1 -1
  84. package/lib/theme/__tests__/migrate.test.d.ts +1 -0
  85. package/lib/theme/__tests__/migrate.test.js +233 -0
  86. package/lib/theme/migrate.d.ts +13 -0
  87. package/lib/theme/migrate.js +90 -0
  88. package/lib/ui/index.js +3 -6
  89. package/lib/usageTracking.js +2 -2
  90. package/mcp-server/tools/cms/HsCreateFunctionTool.d.ts +32 -0
  91. package/mcp-server/tools/cms/HsCreateFunctionTool.js +96 -0
  92. package/mcp-server/tools/cms/HsCreateModuleTool.d.ts +38 -0
  93. package/mcp-server/tools/cms/HsCreateModuleTool.js +118 -0
  94. package/mcp-server/tools/cms/HsCreateTemplateTool.d.ts +26 -0
  95. package/mcp-server/tools/cms/HsCreateTemplateTool.js +75 -0
  96. package/mcp-server/tools/cms/HsFunctionLogsTool.d.ts +32 -0
  97. package/mcp-server/tools/cms/HsFunctionLogsTool.js +76 -0
  98. package/mcp-server/tools/cms/HsListFunctionsTool.d.ts +23 -0
  99. package/mcp-server/tools/cms/HsListFunctionsTool.js +58 -0
  100. package/mcp-server/tools/cms/HsListTool.js +1 -1
  101. package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.d.ts +1 -0
  102. package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.js +251 -0
  103. package/mcp-server/tools/cms/__tests__/HsCreateModuleTool.test.d.ts +1 -0
  104. package/mcp-server/tools/cms/__tests__/HsCreateModuleTool.test.js +224 -0
  105. package/mcp-server/tools/cms/__tests__/HsCreateTemplateTool.test.d.ts +1 -0
  106. package/mcp-server/tools/cms/__tests__/HsCreateTemplateTool.test.js +206 -0
  107. package/mcp-server/tools/cms/__tests__/HsFunctionLogsTool.test.d.ts +1 -0
  108. package/mcp-server/tools/cms/__tests__/HsFunctionLogsTool.test.js +183 -0
  109. package/mcp-server/tools/cms/__tests__/HsListFunctionsTool.test.d.ts +1 -0
  110. package/mcp-server/tools/cms/__tests__/HsListFunctionsTool.test.js +120 -0
  111. package/mcp-server/tools/cms/__tests__/HsListTool.test.js +1 -1
  112. package/mcp-server/tools/index.js +13 -1
  113. package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +3 -3
  114. package/mcp-server/tools/project/AddFeatureToProjectTool.js +3 -3
  115. package/mcp-server/tools/project/CreateProjectTool.d.ts +3 -3
  116. package/mcp-server/tools/project/CreateProjectTool.js +5 -5
  117. package/mcp-server/tools/project/DeployProjectTool.js +1 -1
  118. package/mcp-server/tools/project/DocFetchTool.js +2 -2
  119. package/mcp-server/tools/project/DocsSearchTool.d.ts +4 -1
  120. package/mcp-server/tools/project/DocsSearchTool.js +7 -7
  121. package/mcp-server/tools/project/GetConfigValuesTool.d.ts +4 -1
  122. package/mcp-server/tools/project/GetConfigValuesTool.js +14 -8
  123. package/mcp-server/tools/project/GuidedWalkthroughTool.js +1 -1
  124. package/mcp-server/tools/project/UploadProjectTools.js +2 -2
  125. package/mcp-server/tools/project/ValidateProjectTool.js +1 -1
  126. package/mcp-server/tools/project/__tests__/AddFeatureToProjectTool.test.js +1 -1
  127. package/mcp-server/tools/project/__tests__/CreateProjectTool.test.js +1 -1
  128. package/mcp-server/tools/project/__tests__/DeployProjectTool.test.js +1 -1
  129. package/mcp-server/tools/project/__tests__/DocFetchTool.test.js +2 -2
  130. package/mcp-server/tools/project/__tests__/DocsSearchTool.test.js +14 -12
  131. package/mcp-server/tools/project/__tests__/GetConfigValuesTool.test.js +9 -8
  132. package/mcp-server/tools/project/__tests__/GuidedWalkthroughTool.test.js +1 -1
  133. package/mcp-server/tools/project/__tests__/UploadProjectTools.test.js +1 -1
  134. package/mcp-server/tools/project/__tests__/ValidateProjectTool.test.js +1 -1
  135. package/mcp-server/tools/project/constants.d.ts +1 -1
  136. package/mcp-server/tools/project/constants.js +14 -6
  137. package/mcp-server/utils/__tests__/cliConfig.test.d.ts +1 -0
  138. package/mcp-server/utils/__tests__/cliConfig.test.js +110 -0
  139. package/mcp-server/utils/cliConfig.d.ts +1 -0
  140. package/mcp-server/utils/cliConfig.js +12 -0
  141. package/package.json +4 -3
  142. package/types/LocalDev.d.ts +2 -1
  143. package/types/Projects.d.ts +1 -0
  144. package/ui/components/BoxWithTitle.d.ts +8 -0
  145. package/ui/components/BoxWithTitle.js +9 -0
  146. package/ui/components/HorizontalSelectPrompt.d.ts +8 -0
  147. package/ui/components/HorizontalSelectPrompt.js +30 -0
  148. package/ui/components/StatusMessageBoxes.d.ts +12 -0
  149. package/ui/components/StatusMessageBoxes.js +31 -0
  150. package/ui/lib/ui-testing-utils.d.ts +9 -0
  151. package/ui/lib/ui-testing-utils.js +47 -0
  152. package/ui/lib/useTerminalSize.d.ts +13 -0
  153. package/ui/lib/useTerminalSize.js +31 -0
  154. package/ui/styles.d.ts +18 -0
  155. package/ui/styles.js +18 -0
  156. package/ui/views/UiSandbox.d.ts +5 -0
  157. package/ui/views/UiSandbox.js +25 -0
  158. /package/lib/projects/__tests__/{buildAndDeploy.test.d.ts → deploy.test.d.ts} +0 -0
@@ -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>;
@@ -2,7 +2,7 @@ import path from 'path';
2
2
  import { getCwd, sanitizeFileName } from '@hubspot/local-dev-lib/path';
3
3
  import { extractZipArchive } from '@hubspot/local-dev-lib/archive';
4
4
  import chalk from 'chalk';
5
- import { validateUid, migrateThemes, getProjectThemeDetails, } from '@hubspot/project-parsing-lib';
5
+ import { validateUid } from '@hubspot/project-parsing-lib';
6
6
  import { UNMIGRATABLE_REASONS } from '@hubspot/local-dev-lib/constants/projects';
7
7
  import { mapToUserFacingType } from '@hubspot/project-parsing-lib/src/lib/transform.js';
8
8
  import { MIGRATION_STATUS } from '@hubspot/local-dev-lib/types/Migration';
@@ -10,19 +10,15 @@ import { downloadProject } from '@hubspot/local-dev-lib/api/projects';
10
10
  import { Separator } from '@inquirer/prompts';
11
11
  import { confirmPrompt, inputPrompt, listPrompt, } from '../prompts/promptUtils.js';
12
12
  import { uiAccountDescription, uiCommandReference, uiLine, uiLink, } from '../ui/index.js';
13
- import { writeProjectConfig } from '../projects/config.js';
14
13
  import { ensureProjectExists } from '../projects/ensureProjectExists.js';
15
14
  import SpinniesManager from '../ui/SpinniesManager.js';
16
15
  import { DEFAULT_POLLING_STATUS_LOOKUP, poll } from '../polling.js';
17
16
  import { checkMigrationStatusV2, CLI_UNMIGRATABLE_REASONS, continueAppMigration, initializeAppMigration, isMigrationStatus, listAppsForMigration, } from '../../api/migrate.js';
18
17
  import fs from 'fs';
19
18
  import { lib } from '../../lang/en.js';
20
- import { PROJECT_CONFIG_FILE } from '../constants.js';
21
19
  import { hasUnfiedAppsAccess } from '../hasFeature.js';
22
20
  import { getProjectBuildDetailUrl, getProjectDetailUrl, } from '../projects/urls.js';
23
21
  import { uiLogger } from '../ui/logger.js';
24
- import { debugError } from '../errorHandlers/index.js';
25
- import { useV3Api } from '../projects/buildAndDeploy.js';
26
22
  export function getUnmigratableReason(reasonCode, projectName, accountId) {
27
23
  switch (reasonCode) {
28
24
  case UNMIGRATABLE_REASONS.UP_TO_DATE:
@@ -59,17 +55,6 @@ export function buildErrorMessageFromMigrationStatus(error) {
59
55
  })
60
56
  .join('\n\t- ')}`;
61
57
  }
62
- export async function getHasMigratableThemes(projectConfig) {
63
- if (!projectConfig?.projectConfig?.name || !projectConfig?.projectDir) {
64
- return { hasMigratableThemes: false, migratableThemesCount: 0 };
65
- }
66
- const projectSrcDir = path.resolve(projectConfig.projectDir, projectConfig.projectConfig.srcDir);
67
- const { legacyThemeDetails, legacyReactThemeDetails } = await getProjectThemeDetails(projectSrcDir);
68
- return {
69
- hasMigratableThemes: legacyThemeDetails.length > 0 || legacyReactThemeDetails.length > 0,
70
- migratableThemesCount: legacyThemeDetails.length + legacyReactThemeDetails.length,
71
- };
72
- }
73
58
  export async function fetchMigrationApps(derivedAccountId, platformVersion, projectConfig) {
74
59
  const { data: { migratableApps, unmigratableApps }, } = await listAppsForMigration(derivedAccountId, platformVersion);
75
60
  const filteredMigratableApps = migratableApps.filter(generateFilterAppsByProjectNameFunction(projectConfig));
@@ -79,23 +64,11 @@ export async function fetchMigrationApps(derivedAccountId, platformVersion, proj
79
64
  unmigratableApps: filteredUnmigratableApps,
80
65
  };
81
66
  }
82
- export async function validateMigrationAppsAndThemes(appId, derivedAccountId, { migratableApps, unmigratableApps, }, hasMigratableThemes, projectConfig) {
67
+ export async function validateMigrationApps(appId, derivedAccountId, { migratableApps, unmigratableApps, }, projectConfig) {
83
68
  const allApps = [...migratableApps, ...unmigratableApps];
84
69
  if (allApps.length > 1 && projectConfig) {
85
70
  throw new Error(lib.migrate.errors.project.multipleApps);
86
71
  }
87
- if (hasMigratableThemes) {
88
- if (useV3Api(projectConfig?.projectConfig?.platformVersion)) {
89
- throw new Error(lib.migrate.errors.project.themesAlreadyMigrated);
90
- }
91
- if (allApps.length > 0 && projectConfig) {
92
- throw new Error(lib.migrate.errors.project.themesAndAppsNotAllowed);
93
- }
94
- if (!projectConfig) {
95
- throw new Error(lib.migrate.errors.project.noProjectForThemesMigration);
96
- }
97
- return;
98
- }
99
72
  if (!projectConfig?.projectConfig) {
100
73
  allApps.forEach(app => {
101
74
  if (app.projectName) {
@@ -178,35 +151,20 @@ export async function handleMigrationSetup(derivedAccountId, options, projectCon
178
151
  text: lib.migrate.spinners.checkingForMigratableComponents,
179
152
  });
180
153
  const { name, dest, appId } = options;
181
- const { hasMigratableThemes, migratableThemesCount } = await getHasMigratableThemes(projectConfig);
182
154
  const { migratableApps, unmigratableApps } = await fetchMigrationApps(derivedAccountId, options.platformVersion, projectConfig);
183
155
  SpinniesManager.remove('checkingForMigratableComponents');
184
- await validateMigrationAppsAndThemes(appId, derivedAccountId, { migratableApps, unmigratableApps }, hasMigratableThemes, projectConfig);
156
+ await validateMigrationApps(appId, derivedAccountId, { migratableApps, unmigratableApps }, projectConfig);
185
157
  const allApps = [...migratableApps, ...unmigratableApps];
186
- let proceed = false;
187
- let appIdToMigrate;
188
- if (hasMigratableThemes) {
189
- uiLogger.log(lib.migrate.prompt.themesMigration(migratableThemesCount));
190
- proceed = await confirmPrompt(lib.migrate.prompt.proceed, {
191
- defaultAnswer: false,
192
- });
193
- }
194
- else {
195
- const appSelectionPrompt = await selectAppToMigrate(allApps, derivedAccountId, appId);
196
- appIdToMigrate = appSelectionPrompt.appIdToMigrate;
197
- proceed = appSelectionPrompt.proceed;
198
- }
158
+ const { proceed, appIdToMigrate } = await selectAppToMigrate(allApps, derivedAccountId, appId);
199
159
  if (!proceed) {
200
160
  return {};
201
161
  }
202
162
  // If it's a project we don't want to prompt for dest and name, so just return early
203
- // Theme migrations can only be initiated from the project directory
204
163
  if (projectConfig &&
205
164
  projectConfig?.projectConfig &&
206
165
  projectConfig?.projectDir) {
207
166
  return {
208
167
  appIdToMigrate,
209
- isThemesMigration: hasMigratableThemes,
210
168
  projectName: projectConfig.projectConfig.name,
211
169
  projectDest: projectConfig.projectDir,
212
170
  };
@@ -231,28 +189,6 @@ export async function handleMigrationSetup(derivedAccountId, options, projectCon
231
189
  }));
232
190
  return { appIdToMigrate, projectName, projectDest };
233
191
  }
234
- export async function handleThemesMigration(projectConfig, platformVersion) {
235
- if (!projectConfig?.projectDir || !projectConfig?.projectConfig?.srcDir) {
236
- throw new Error(lib.migrate.errors.project.invalidConfig);
237
- }
238
- const projectSrcDir = path.resolve(projectConfig.projectDir, projectConfig.projectConfig.srcDir);
239
- try {
240
- await migrateThemes(projectConfig.projectDir, projectSrcDir);
241
- }
242
- catch (error) {
243
- debugError(error);
244
- throw new Error(lib.migrate.errors.project.failedToMigrateThemes);
245
- }
246
- const newProjectConfig = { ...projectConfig.projectConfig };
247
- newProjectConfig.platformVersion = platformVersion;
248
- const projectConfigPath = path.join(projectConfig.projectDir, PROJECT_CONFIG_FILE);
249
- const success = writeProjectConfig(projectConfigPath, newProjectConfig);
250
- if (!success) {
251
- throw new Error(lib.migrate.errors.project.failedToUpdateProjectConfig);
252
- }
253
- uiLogger.log('');
254
- uiLogger.log(lib.migrate.success.themesMigrationSuccess(platformVersion));
255
- }
256
192
  export async function beginAppMigration(derivedAccountId, appId, platformVersion) {
257
193
  SpinniesManager.add('beginningMigration', {
258
194
  text: lib.migrate.spinners.beginningMigration,
@@ -405,11 +341,7 @@ export async function migrateApp2025_2(derivedAccountId, options, projectConfig)
405
341
  throw new Error(lib.migrate.errors.project.doesNotExist(derivedAccountId));
406
342
  }
407
343
  }
408
- const { appIdToMigrate, projectName, projectDest, isThemesMigration } = await handleMigrationSetup(derivedAccountId, options, projectConfig);
409
- if (isThemesMigration) {
410
- await handleThemesMigration(projectConfig, options.platformVersion);
411
- return;
412
- }
344
+ const { appIdToMigrate, projectName, projectDest } = await handleMigrationSetup(derivedAccountId, options, projectConfig);
413
345
  if (!appIdToMigrate || !projectName || !projectDest) {
414
346
  return;
415
347
  }
@@ -80,6 +80,10 @@ export declare const FEATURES: {
80
80
  readonly UNIFIED_APPS: "Developers:UnifiedApps:PrivateBeta";
81
81
  readonly SANDBOXES_V2: "sandboxes:v2:enabled";
82
82
  readonly SANDBOXES_V2_CLI: "sandboxes:v2:cliEnabled";
83
+ readonly APP_EVENTS: "Developers:UnifiedApps:AppEventsAccess";
84
+ readonly APPS_HOME: "UIE:AppHome";
85
+ readonly MCP_ACCESS: "Developers:CLIMCPAccess";
86
+ readonly THEME_MIGRATION_2025_2: "Developers:ProjectThemeMigrations:2025.2";
83
87
  };
84
88
  export declare const LOCAL_DEV_UI_MESSAGE_SEND_TYPES: {
85
89
  UPLOAD_SUCCESS: string;
package/lib/constants.js CHANGED
@@ -72,6 +72,10 @@ export const FEATURES = {
72
72
  UNIFIED_APPS: 'Developers:UnifiedApps:PrivateBeta',
73
73
  SANDBOXES_V2: 'sandboxes:v2:enabled',
74
74
  SANDBOXES_V2_CLI: 'sandboxes:v2:cliEnabled',
75
+ APP_EVENTS: 'Developers:UnifiedApps:AppEventsAccess',
76
+ APPS_HOME: 'UIE:AppHome',
77
+ MCP_ACCESS: 'Developers:CLIMCPAccess',
78
+ THEME_MIGRATION_2025_2: 'Developers:ProjectThemeMigrations:2025.2',
75
79
  };
76
80
  export const LOCAL_DEV_UI_MESSAGE_SEND_TYPES = {
77
81
  UPLOAD_SUCCESS: 'server:uploadSuccess',
@@ -12,3 +12,7 @@ export declare class ApiErrorContext {
12
12
  projectName?: string;
13
13
  });
14
14
  }
15
+ export declare function isErrorWithMessageOrReason(error: unknown): error is {
16
+ message?: string;
17
+ reason?: string;
18
+ };
@@ -85,7 +85,7 @@ export class ApiErrorContext {
85
85
  this.projectName = props.projectName || '';
86
86
  }
87
87
  }
88
- function isErrorWithMessageOrReason(error) {
88
+ export function isErrorWithMessageOrReason(error) {
89
89
  return (typeof error === 'object' &&
90
90
  error !== null &&
91
91
  ('message' in error || 'reason' in error));
package/lib/hasFeature.js CHANGED
@@ -1,7 +1,13 @@
1
1
  import { http } from '@hubspot/local-dev-lib/http';
2
2
  import { fetchEnabledFeatures } from '@hubspot/local-dev-lib/api/localDevAuth';
3
+ import { FEATURES } from './constants.js';
4
+ const FEATURES_THAT_DEFAULT_ON = [FEATURES.APPS_HOME];
3
5
  export async function hasFeature(accountId, feature) {
4
6
  const { data: { enabledFeatures }, } = await fetchEnabledFeatures(accountId);
7
+ if (enabledFeatures[feature] === undefined &&
8
+ FEATURES_THAT_DEFAULT_ON.includes(feature)) {
9
+ return true;
10
+ }
5
11
  return Boolean(enabledFeatures[feature]);
6
12
  }
7
13
  export async function hasUnfiedAppsAccess(accountId) {
package/lib/importData.js CHANGED
@@ -10,7 +10,7 @@ export async function handleImportData(targetAccountId, dataFileNames, importReq
10
10
  const baseUrl = getHubSpotWebsiteOrigin(getEnv());
11
11
  const response = await createImport(targetAccountId, importRequest, dataFileNames);
12
12
  const importId = response.data.id;
13
- uiLogger.success(lib.importData.viewImportLink(baseUrl, targetAccountId, importId));
13
+ uiLogger.info(lib.importData.viewImportLink(baseUrl, targetAccountId, importId));
14
14
  }
15
15
  catch (error) {
16
16
  uiLogger.error(lib.importData.errors.failedToImportData);
package/lib/links.d.ts CHANGED
@@ -7,4 +7,5 @@ type SiteLink = {
7
7
  export declare function getSiteLinksAsArray(accountId: number): SiteLink[];
8
8
  export declare function logSiteLinks(accountId: number): void;
9
9
  export declare function openLink(accountId: number, shortcut: string): void;
10
+ export declare function getProductUpdatesUrl(rolloutId: string, accountId?: number): string;
10
11
  export {};
package/lib/links.js CHANGED
@@ -4,7 +4,7 @@ import { ENVIRONMENTS } from '@hubspot/local-dev-lib/constants/environments';
4
4
  import { getHubSpotWebsiteOrigin } from '@hubspot/local-dev-lib/urls';
5
5
  import { logger } from '@hubspot/local-dev-lib/logger';
6
6
  import { getTableContents, getTableHeader } from './ui/table.js';
7
- const SITE_LINKS = {
7
+ const COMMON_SITE_LINKS = {
8
8
  APPS_MARKETPLACE: {
9
9
  shortcut: 'apps-marketplace',
10
10
  alias: 'apm',
@@ -76,7 +76,7 @@ const SITE_LINKS = {
76
76
  };
77
77
  export function getSiteLinksAsArray(accountId) {
78
78
  const baseUrl = getHubSpotWebsiteOrigin(getEnv() === 'qa' ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD);
79
- return Object.values(SITE_LINKS)
79
+ return Object.values(COMMON_SITE_LINKS)
80
80
  .sort((a, b) => (a.shortcut < b.shortcut ? -1 : 1))
81
81
  .map(l => ({ ...l, url: l.getUrl(accountId, baseUrl) }));
82
82
  }
@@ -90,7 +90,7 @@ export function logSiteLinks(accountId) {
90
90
  logger.log(getTableContents(linksAsArray));
91
91
  }
92
92
  export function openLink(accountId, shortcut) {
93
- const match = Object.values(SITE_LINKS).find(l => l.shortcut === shortcut || (l.alias && l.alias === shortcut));
93
+ const match = Object.values(COMMON_SITE_LINKS).find(l => l.shortcut === shortcut || (l.alias && l.alias === shortcut));
94
94
  if (!match) {
95
95
  logger.error(`We couldn't find a shortcut matching ${shortcut}. Type 'hs open list' to see a list of available shortcuts`);
96
96
  return;
@@ -99,3 +99,10 @@ export function openLink(accountId, shortcut) {
99
99
  open(match.getUrl(accountId, baseUrl), { url: true });
100
100
  logger.success(`We opened ${match.getUrl(accountId, baseUrl)} in your browser`);
101
101
  }
102
+ export function getProductUpdatesUrl(rolloutId, accountId) {
103
+ const baseUrl = getHubSpotWebsiteOrigin(getEnv() === 'qa' ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD);
104
+ if (accountId) {
105
+ return `${baseUrl}/product-updates/${accountId}/in-beta?rollout=${rolloutId}`;
106
+ }
107
+ return `${baseUrl}/l/product-updates/in-beta?rollout=${rolloutId}`;
108
+ }
@@ -15,8 +15,6 @@ interface McpCommand {
15
15
  command: string;
16
16
  args: string[];
17
17
  }
18
- export declare function addMintlifyMcpServer(installTargets: string[]): Promise<void>;
19
- export declare function setupMintlify(derivedTargets?: string[]): Promise<boolean>;
20
18
  export declare function addMcpServerToConfig(targets: string[] | undefined): Promise<string[]>;
21
19
  export declare function setupVsCode(mcpCommand?: McpCommand): Promise<boolean>;
22
20
  export declare function setupClaudeCode(mcpCommand?: McpCommand): Promise<boolean>;
package/lib/mcp/setup.js CHANGED
@@ -4,17 +4,15 @@ import { promptUser } from '../prompts/promptUtils.js';
4
4
  import SpinniesManager from '../ui/SpinniesManager.js';
5
5
  import { logError } from '../errorHandlers/index.js';
6
6
  import { execAsync } from '../../mcp-server/utils/command.js';
7
- import { spawn } from 'node:child_process';
8
7
  import path from 'path';
9
8
  import os from 'os';
10
9
  import fs from 'fs-extra';
11
10
  import { existsSync } from 'fs';
12
- const mcpServerName = 'hubspot-cli-mcp';
11
+ const mcpServerName = 'HubSpot';
13
12
  const claudeCode = 'claude';
14
13
  const windsurf = 'windsurf';
15
14
  const cursor = 'cursor';
16
15
  const vscode = 'vscode';
17
- const supportedMintlifyClients = [windsurf, cursor];
18
16
  export const supportedTools = [
19
17
  { name: commands.mcp.setup.claudeCode, value: claudeCode },
20
18
  { name: commands.mcp.setup.cursor, value: cursor },
@@ -25,28 +23,6 @@ const defaultMcpCommand = {
25
23
  command: 'hs',
26
24
  args: ['mcp', 'start'],
27
25
  };
28
- export async function addMintlifyMcpServer(installTargets) {
29
- await runSetupFunction(() => setupMintlify(installTargets));
30
- }
31
- export async function setupMintlify(derivedTargets = supportedMintlifyClients) {
32
- uiLogger.info(commands.mcp.setup.installingDocSearch);
33
- uiLogger.log('');
34
- return new Promise(resolve => {
35
- const subcommands = ['mint-mcp', 'add', 'hubspot-migration'];
36
- const docsSearchClients = derivedTargets.filter(target => supportedMintlifyClients.includes(target));
37
- const childProcess = spawn(`npx`, docsSearchClients && docsSearchClients.length
38
- ? [...subcommands, '--client', ...docsSearchClients]
39
- : subcommands, {
40
- stdio: 'inherit',
41
- });
42
- childProcess.on('exit', code => {
43
- if (code !== 0) {
44
- resolve(false);
45
- }
46
- resolve(true);
47
- });
48
- });
49
- }
50
26
  export async function addMcpServerToConfig(targets) {
51
27
  try {
52
28
  let derivedTargets = [];
@@ -164,7 +140,7 @@ export async function setupVsCode(mcpCommand = defaultMcpCommand) {
164
140
  name: mcpServerName,
165
141
  ...buildCommandWithAgentString(mcpCommand, vscode),
166
142
  });
167
- await execAsync(`code --add-mcp '${mcpConfig}'`);
143
+ await execAsync(`code --add-mcp ${JSON.stringify(mcpConfig)}`);
168
144
  SpinniesManager.succeed('vsCode', {
169
145
  text: commands.mcp.setup.spinners.configuredVsCode,
170
146
  });
@@ -206,15 +182,14 @@ export async function setupClaudeCode(mcpCommand = defaultMcpCommand) {
206
182
  });
207
183
  await execAsync(`claude mcp remove "${mcpServerName}" --scope user`);
208
184
  }
209
- await execAsync(`claude mcp add-json "${mcpServerName}" '${mcpConfig}' --scope user`);
185
+ await execAsync(`claude mcp add-json "${mcpServerName}" ${JSON.stringify(mcpConfig)} --scope user`);
210
186
  SpinniesManager.succeed('claudeCode', {
211
187
  text: commands.mcp.setup.spinners.configuredClaudeCode,
212
188
  });
213
189
  return true;
214
190
  }
215
191
  catch (error) {
216
- if (error instanceof Error &&
217
- error.message.includes('claude: command not found')) {
192
+ if (error instanceof Error && error.message.includes('claude')) {
218
193
  SpinniesManager.fail('claudeCode', {
219
194
  text: commands.mcp.setup.spinners.claudeCodeNotFound,
220
195
  });