@hubspot/cli 7.8.0-experimental.0 → 7.8.2-experimental.0

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 (143) hide show
  1. package/bin/cli.js +0 -2
  2. package/commands/__tests__/getStarted.test.js +2 -2
  3. package/commands/__tests__/mcp.test.js +1 -1
  4. package/commands/__tests__/project.test.js +0 -3
  5. package/commands/app/__tests__/migrate.test.js +0 -1
  6. package/commands/app/migrate.js +4 -5
  7. package/commands/app/secret/add.js +2 -1
  8. package/commands/app/secret/delete.js +2 -1
  9. package/commands/app/secret/list.js +2 -1
  10. package/commands/app/secret/update.js +2 -1
  11. package/commands/app/secret.js +2 -1
  12. package/commands/app.js +2 -2
  13. package/commands/config/set.js +0 -1
  14. package/commands/feedback.js +1 -1
  15. package/commands/getStarted.d.ts +1 -3
  16. package/commands/getStarted.js +66 -18
  17. package/commands/mcp/__tests__/setup.test.js +2 -2
  18. package/commands/mcp/setup.js +11 -2
  19. package/commands/mcp.js +3 -3
  20. package/commands/project/__tests__/create.test.js +6 -6
  21. package/commands/project/__tests__/deploy.test.js +0 -3
  22. package/commands/project/__tests__/devUnifiedFlow.test.js +2 -4
  23. package/commands/project/__tests__/logs.test.js +0 -3
  24. package/commands/project/__tests__/migrate.test.js +1 -2
  25. package/commands/project/__tests__/migrateApp.test.js +1 -2
  26. package/commands/project/__tests__/profile.test.js +1 -1
  27. package/commands/project/add.js +1 -5
  28. package/commands/project/create.js +3 -9
  29. package/commands/project/deploy.js +2 -2
  30. package/commands/project/dev/index.js +4 -3
  31. package/commands/project/dev/unifiedFlow.js +6 -4
  32. package/commands/project/download.js +1 -2
  33. package/commands/project/installDeps.js +1 -2
  34. package/commands/project/listBuilds.js +2 -2
  35. package/commands/project/logs.js +2 -2
  36. package/commands/project/migrate.js +28 -10
  37. package/commands/project/migrateApp.js +1 -2
  38. package/commands/project/open.js +1 -2
  39. package/commands/project/profile/add.js +3 -3
  40. package/commands/project/profile/delete.js +1 -2
  41. package/commands/project/profile.js +2 -3
  42. package/commands/project/upload.js +2 -2
  43. package/commands/project/validate.js +1 -1
  44. package/commands/project/watch.js +2 -2
  45. package/commands/project.js +1 -2
  46. package/commands/sandbox/delete.js +1 -1
  47. package/commands/testAccount/importData.d.ts +1 -1
  48. package/commands/testAccount/importData.js +1 -1
  49. package/commands/testAccount.js +1 -1
  50. package/lang/en.d.ts +15 -4
  51. package/lang/en.js +18 -6
  52. package/lib/__tests__/hasFeature.test.js +145 -7
  53. package/lib/app/__tests__/migrate.test.js +14 -51
  54. package/lib/app/migrate.d.ts +2 -8
  55. package/lib/app/migrate.js +5 -80
  56. package/lib/constants.d.ts +8 -0
  57. package/lib/constants.js +8 -0
  58. package/lib/dependencyManagement.d.ts +0 -5
  59. package/lib/dependencyManagement.js +0 -9
  60. package/lib/hasFeature.js +6 -0
  61. package/lib/links.d.ts +1 -0
  62. package/lib/links.js +10 -3
  63. package/lib/mcp/setup.js +1 -1
  64. package/lib/middleware/fireAlarmMiddleware.js +15 -5
  65. package/lib/projects/__tests__/LocalDevProcess.test.js +227 -16
  66. package/lib/projects/__tests__/LocalDevWebsocketServer.test.js +16 -21
  67. package/lib/projects/__tests__/deploy.test.js +71 -6
  68. package/lib/projects/__tests__/localDevProjectHelpers.test.js +4 -2
  69. package/lib/projects/create/__tests__/v3.test.js +79 -4
  70. package/lib/projects/create/v3.js +11 -8
  71. package/lib/projects/localDev/AppDevModeInterface.js +5 -5
  72. package/lib/projects/localDev/LocalDevLogger.d.ts +4 -0
  73. package/lib/projects/localDev/LocalDevLogger.js +22 -0
  74. package/lib/projects/localDev/LocalDevProcess.d.ts +7 -5
  75. package/lib/projects/localDev/LocalDevProcess.js +90 -19
  76. package/lib/projects/localDev/LocalDevState.d.ts +9 -8
  77. package/lib/projects/localDev/LocalDevState.js +18 -17
  78. package/lib/projects/localDev/LocalDevWebsocketServer.d.ts +2 -0
  79. package/lib/projects/localDev/LocalDevWebsocketServer.js +55 -23
  80. package/lib/projects/localDev/helpers/project.d.ts +2 -2
  81. package/lib/projects/localDev/helpers/project.js +10 -7
  82. package/lib/projects/localDev/localDevWebsocketServerUtils.d.ts +4 -0
  83. package/lib/projects/localDev/localDevWebsocketServerUtils.js +10 -0
  84. package/lib/projects/pollProjectBuildAndDeploy.js +4 -4
  85. package/lib/prompts/projectAddPrompt.js +2 -1
  86. package/lib/prompts/promptUtils.js +3 -0
  87. package/lib/prompts/selectProjectTemplatePrompt.js +2 -0
  88. package/lib/theme/__tests__/migrate.test.d.ts +1 -0
  89. package/lib/theme/__tests__/migrate.test.js +233 -0
  90. package/lib/theme/migrate.d.ts +13 -0
  91. package/lib/theme/migrate.js +90 -0
  92. package/lib/ui/SpinniesManager.js +105 -8
  93. package/lib/usageTracking.js +2 -2
  94. package/mcp-server/tools/cms/HsCreateFunctionTool.js +1 -1
  95. package/mcp-server/tools/cms/HsCreateModuleTool.js +1 -1
  96. package/mcp-server/tools/cms/HsCreateTemplateTool.js +1 -1
  97. package/mcp-server/tools/cms/HsFunctionLogsTool.js +2 -2
  98. package/mcp-server/tools/cms/HsListFunctionsTool.js +1 -1
  99. package/mcp-server/tools/cms/HsListTool.js +1 -1
  100. package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.js +1 -1
  101. package/mcp-server/tools/cms/__tests__/HsCreateModuleTool.test.js +1 -1
  102. package/mcp-server/tools/cms/__tests__/HsCreateTemplateTool.test.js +1 -1
  103. package/mcp-server/tools/cms/__tests__/HsFunctionLogsTool.test.js +2 -2
  104. package/mcp-server/tools/cms/__tests__/HsListFunctionsTool.test.js +1 -1
  105. package/mcp-server/tools/cms/__tests__/HsListTool.test.js +1 -1
  106. package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +3 -3
  107. package/mcp-server/tools/project/AddFeatureToProjectTool.js +3 -3
  108. package/mcp-server/tools/project/CreateProjectTool.d.ts +3 -3
  109. package/mcp-server/tools/project/CreateProjectTool.js +5 -5
  110. package/mcp-server/tools/project/DeployProjectTool.js +1 -1
  111. package/mcp-server/tools/project/DocFetchTool.js +2 -2
  112. package/mcp-server/tools/project/DocsSearchTool.d.ts +4 -1
  113. package/mcp-server/tools/project/DocsSearchTool.js +7 -7
  114. package/mcp-server/tools/project/GetConfigValuesTool.d.ts +4 -1
  115. package/mcp-server/tools/project/GetConfigValuesTool.js +11 -5
  116. package/mcp-server/tools/project/GuidedWalkthroughTool.js +1 -1
  117. package/mcp-server/tools/project/UploadProjectTools.js +2 -2
  118. package/mcp-server/tools/project/ValidateProjectTool.js +1 -1
  119. package/mcp-server/tools/project/__tests__/AddFeatureToProjectTool.test.js +1 -1
  120. package/mcp-server/tools/project/__tests__/CreateProjectTool.test.js +1 -1
  121. package/mcp-server/tools/project/__tests__/DeployProjectTool.test.js +1 -1
  122. package/mcp-server/tools/project/__tests__/DocFetchTool.test.js +2 -2
  123. package/mcp-server/tools/project/__tests__/DocsSearchTool.test.js +14 -12
  124. package/mcp-server/tools/project/__tests__/GetConfigValuesTool.test.js +9 -8
  125. package/mcp-server/tools/project/__tests__/GuidedWalkthroughTool.test.js +1 -1
  126. package/mcp-server/tools/project/__tests__/UploadProjectTools.test.js +1 -1
  127. package/mcp-server/tools/project/__tests__/ValidateProjectTool.test.js +1 -1
  128. package/mcp-server/tools/project/constants.d.ts +1 -1
  129. package/mcp-server/tools/project/constants.js +9 -3
  130. package/mcp-server/utils/__tests__/cliConfig.test.d.ts +1 -0
  131. package/mcp-server/utils/__tests__/cliConfig.test.js +110 -0
  132. package/mcp-server/utils/cliConfig.d.ts +1 -0
  133. package/mcp-server/utils/cliConfig.js +12 -0
  134. package/package.json +4 -9
  135. package/types/LocalDev.d.ts +19 -3
  136. package/ui/components/HorizontalSelectPrompt.js +1 -1
  137. package/ui/index.js +1 -1
  138. package/commands/getStartedV2.d.ts +0 -9
  139. package/commands/getStartedV2.js +0 -39
  140. package/ui/components/Ascii.d.ts +0 -10
  141. package/ui/components/Ascii.js +0 -11
  142. package/ui/views/GetStarted.d.ts +0 -7
  143. package/ui/views/GetStarted.js +0 -157
package/bin/cli.js CHANGED
@@ -42,7 +42,6 @@ import appCommand from '../commands/app.js';
42
42
  import testAccountCommands from '../commands/testAccount.js';
43
43
  import getStartedCommand from '../commands/getStarted.js';
44
44
  import mcpCommand from '../commands/mcp.js';
45
- import getStartedV2Command from '../commands/getStartedV2.js';
46
45
  function getTerminalWidth() {
47
46
  const width = yargs().terminalWidth();
48
47
  if (width >= 100)
@@ -93,7 +92,6 @@ const argv = yargs(process.argv.slice(2))
93
92
  type: 'boolean',
94
93
  })
95
94
  .check(performChecks)
96
- .command(getStartedV2Command)
97
95
  .command(authCommand)
98
96
  .command(initCommand)
99
97
  .command(logsCommand)
@@ -34,8 +34,8 @@ describe('commands/get-started', () => {
34
34
  });
35
35
  });
36
36
  describe('describe', () => {
37
- it('should have undefined describe property', () => {
38
- expect(getStartedCommand.describe).toBeUndefined();
37
+ it('should have a defined describe property', () => {
38
+ expect(getStartedCommand.describe).toBeDefined();
39
39
  });
40
40
  });
41
41
  describe('command structure', () => {
@@ -19,7 +19,7 @@ describe('commands/mcp', () => {
19
19
  });
20
20
  describe('describe', () => {
21
21
  it('should provide a description', () => {
22
- expect(mcpCommand.describe).toBeUndefined();
22
+ expect(mcpCommand.describe).toBeDefined();
23
23
  });
24
24
  });
25
25
  describe('builder', () => {
@@ -51,9 +51,6 @@ describe('commands/project', () => {
51
51
  });
52
52
  });
53
53
  describe('describe', () => {
54
- it('should contain the beta tag', () => {
55
- expect(projectCommand.describe).toContain('[BETA]');
56
- });
57
54
  it('should provide a description', () => {
58
55
  expect(projectCommand.describe).toBeDefined();
59
56
  });
@@ -103,7 +103,6 @@ describe('commands/app/migrate', () => {
103
103
  'platform-version': expect.objectContaining({
104
104
  type: 'string',
105
105
  default: '2025.2',
106
- hidden: true,
107
106
  }),
108
107
  }));
109
108
  });
@@ -6,13 +6,13 @@ import { i18n } from '../../lib/lang.js';
6
6
  import { ApiErrorContext, logError } from '../../lib/errorHandlers/index.js';
7
7
  import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
8
8
  import { migrateApp2025_2 } from '../../lib/app/migrate.js';
9
- import { uiBetaTag, uiCommandReference, uiLink } from '../../lib/ui/index.js';
9
+ import { uiCommandReference, uiLink } from '../../lib/ui/index.js';
10
10
  import { migrateApp2023_2 } from '../../lib/app/migrate_legacy.js';
11
11
  import { getIsInProject } from '../../lib/projects/config.js';
12
12
  import { makeYargsBuilder } from '../../lib/yargsUtils.js';
13
13
  const { v2023_2, v2025_2 } = PLATFORM_VERSIONS;
14
14
  const command = 'migrate';
15
- const describe = undefined; // uiBetaTag(i18n(`commands.project.subcommands.migrateApp.header.text.describe`), false);
15
+ const describe = i18n(`commands.project.subcommands.migrateApp.describe`);
16
16
  export function handlerGenerator(commandTrackingName) {
17
17
  return async function handler(args) {
18
18
  const { derivedAccountId, platformVersion, unstable } = args;
@@ -23,7 +23,7 @@ export function handlerGenerator(commandTrackingName) {
23
23
  return process.exit(EXIT_CODES.ERROR);
24
24
  }
25
25
  logger.log('');
26
- logger.log(uiBetaTag(i18n(`commands.project.subcommands.migrateApp.header.text`), false));
26
+ logger.log(i18n(`commands.project.subcommands.migrateApp.header.text`));
27
27
  logger.log(uiLink(i18n(`commands.project.subcommands.migrateApp.header.link`), 'https://developers.hubspot.com/docs/platform/migrate-a-public-app-to-projects'));
28
28
  logger.log('');
29
29
  try {
@@ -76,7 +76,6 @@ function appMigrateBuilder(yargs) {
76
76
  'platform-version': {
77
77
  type: 'string',
78
78
  choices: [v2023_2, v2025_2],
79
- hidden: true,
80
79
  default: v2025_2,
81
80
  },
82
81
  unstable: {
@@ -93,7 +92,7 @@ function appMigrateBuilder(yargs) {
93
92
  ]);
94
93
  return yargs;
95
94
  }
96
- const builder = makeYargsBuilder(appMigrateBuilder, command, uiBetaTag(i18n(`commands.project.subcommands.migrateApp.describe`), false), {
95
+ const builder = makeYargsBuilder(appMigrateBuilder, command, i18n(`commands.project.subcommands.migrateApp.describe`), {
97
96
  useGlobalOptions: true,
98
97
  useConfigOptions: true,
99
98
  useAccountOptions: true,
@@ -7,8 +7,9 @@ import { commands } from '../../../lang/en.js';
7
7
  import { EXIT_CODES } from '../../../lib/enums/exitCodes.js';
8
8
  import { makeYargsBuilder } from '../../../lib/yargsUtils.js';
9
9
  import { uiLogger } from '../../../lib/ui/logger.js';
10
+ import { uiBetaTag } from '../../../lib/ui/index.js';
10
11
  const command = 'add [name]';
11
- const describe = commands.app.subcommands.secret.subcommands.add.describe;
12
+ const describe = uiBetaTag(commands.app.subcommands.secret.subcommands.add.describe, false);
12
13
  async function handler(args) {
13
14
  const { derivedAccountId } = args;
14
15
  trackCommandUsage('app-secret-add', {}, derivedAccountId);
@@ -7,8 +7,9 @@ import { commands } from '../../../lang/en.js';
7
7
  import { EXIT_CODES } from '../../../lib/enums/exitCodes.js';
8
8
  import { makeYargsBuilder } from '../../../lib/yargsUtils.js';
9
9
  import { uiLogger } from '../../../lib/ui/logger.js';
10
+ import { uiBetaTag } from '../../../lib/ui/index.js';
10
11
  const command = 'delete [name]';
11
- const describe = commands.app.subcommands.secret.subcommands.delete.describe;
12
+ const describe = uiBetaTag(commands.app.subcommands.secret.subcommands.delete.describe, false);
12
13
  async function handler(args) {
13
14
  const { derivedAccountId, force } = args;
14
15
  trackCommandUsage('app-secret-delete', {}, derivedAccountId);
@@ -6,8 +6,9 @@ import { EXIT_CODES } from '../../../lib/enums/exitCodes.js';
6
6
  import { makeYargsBuilder } from '../../../lib/yargsUtils.js';
7
7
  import { selectAppPrompt } from '../../../lib/prompts/selectAppPrompt.js';
8
8
  import { uiLogger } from '../../../lib/ui/logger.js';
9
+ import { uiBetaTag } from '../../../lib/ui/index.js';
9
10
  const command = 'list';
10
- const describe = commands.app.subcommands.secret.subcommands.list.describe;
11
+ const describe = uiBetaTag(commands.app.subcommands.secret.subcommands.list.describe, false);
11
12
  async function handler(args) {
12
13
  const { derivedAccountId } = args;
13
14
  trackCommandUsage('app-secret-list', {}, derivedAccountId);
@@ -8,8 +8,9 @@ import { commands } from '../../../lang/en.js';
8
8
  import { EXIT_CODES } from '../../../lib/enums/exitCodes.js';
9
9
  import { makeYargsBuilder } from '../../../lib/yargsUtils.js';
10
10
  import { uiLogger } from '../../../lib/ui/logger.js';
11
+ import { uiBetaTag } from '../../../lib/ui/index.js';
11
12
  const command = 'update [name]';
12
- const describe = commands.app.subcommands.secret.subcommands.update.describe;
13
+ const describe = uiBetaTag(commands.app.subcommands.secret.subcommands.update.describe, false);
13
14
  async function handler(args) {
14
15
  const { derivedAccountId } = args;
15
16
  trackCommandUsage('app-secret-update', {}, derivedAccountId);
@@ -4,8 +4,9 @@ import addAppSecretCommand from './secret/add.js';
4
4
  import deleteAppSecretCommand from './secret/delete.js';
5
5
  import listAppSecretsCommand from './secret/list.js';
6
6
  import updateAppSecretCommand from './secret/update.js';
7
+ import { uiBetaTag } from '../../lib/ui/index.js';
7
8
  const command = ['secret', 'secrets'];
8
- const describe = undefined; // commands.app.subcommands.secret.describe;
9
+ const describe = uiBetaTag(commands.app.subcommands.secret.describe, false);
9
10
  function appSecretBuilder(yargs) {
10
11
  yargs
11
12
  .command(addAppSecretCommand)
package/commands/app.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import migrateCommand from './app/migrate.js';
2
2
  import appSecretCommand from './app/secret.js';
3
3
  import { makeYargsBuilder } from '../lib/yargsUtils.js';
4
+ import { commands } from '../lang/en.js';
4
5
  const command = ['app', 'apps'];
5
- // Keep the command hidden for now
6
- const describe = undefined;
6
+ const describe = commands.app.describe;
7
7
  function appBuilder(yargs) {
8
8
  yargs.command(migrateCommand).command(appSecretCommand).demandCommand(1, '');
9
9
  return yargs;
@@ -84,7 +84,6 @@ function configSetBuilder(yargs) {
84
84
  'auto-open-browser': {
85
85
  describe: commands.config.subcommands.set.options.autoOpenBrowser.describe,
86
86
  type: 'boolean',
87
- hidden: true,
88
87
  },
89
88
  })
90
89
  .conflicts(getExclusiveConflicts([
@@ -5,7 +5,7 @@ import { confirmPrompt } from '../lib/prompts/promptUtils.js';
5
5
  import { makeYargsBuilder } from '../lib/yargsUtils.js';
6
6
  import { EXIT_CODES } from '../lib/enums/exitCodes.js';
7
7
  import { uiLink } from '../lib/ui/index.js';
8
- const FEEDBACK_URL = 'https://developers.hubspot.com/feedback.js';
8
+ const FEEDBACK_URL = 'https://developers.hubspot.com/feedback';
9
9
  const command = 'feedback';
10
10
  const describe = i18n(`commands.project.subcommands.feedback.describe`);
11
11
  async function handler() {
@@ -1,7 +1,5 @@
1
1
  import { AccountArgs, YargsCommandModule, CommonArgs, ConfigArgs, EnvironmentArgs } from '../types/Yargs.js';
2
- export declare const command = "get-started";
3
- export declare const describe: undefined;
4
- export type GetStartedArgs = CommonArgs & ConfigArgs & AccountArgs & EnvironmentArgs & {
2
+ type GetStartedArgs = CommonArgs & ConfigArgs & AccountArgs & EnvironmentArgs & {
5
3
  name?: string;
6
4
  dest?: string;
7
5
  };
@@ -4,6 +4,7 @@ import open from 'open';
4
4
  import { getCwd } from '@hubspot/local-dev-lib/path';
5
5
  import { cloneGithubRepo } from '@hubspot/local-dev-lib/github';
6
6
  import { commands } from '../lang/en.js';
7
+ import { trackCommandMetadataUsage, trackCommandUsage, } from '../lib/usageTracking.js';
7
8
  import { EXIT_CODES } from '../lib/enums/exitCodes.js';
8
9
  import { makeYargsBuilder } from '../lib/yargsUtils.js';
9
10
  import { promptUser } from '../lib/prompts/promptUtils.js';
@@ -22,14 +23,13 @@ import { getStaticAuthAppInstallUrl } from '../lib/app/urls.js';
22
23
  import { getEnv } from '@hubspot/local-dev-lib/config';
23
24
  import { ENVIRONMENTS } from '@hubspot/local-dev-lib/constants/environments';
24
25
  import { fetchPublicAppsForPortal } from '@hubspot/local-dev-lib/api/appsDev';
25
- export const command = 'get-started';
26
- export const describe = undefined;
26
+ const command = 'get-started';
27
+ const describe = commands.getStarted.describe;
27
28
  async function handler(args) {
28
29
  const { derivedAccountId } = args;
29
30
  const env = getEnv(derivedAccountId) === 'qa' ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD;
31
+ await trackCommandUsage('get-started', {}, derivedAccountId);
30
32
  const accountName = uiAccountDescription(derivedAccountId);
31
- // TODO: Put this in constants.ts once we have a defined place for the template before INBOUND
32
- const templateSource = 'robrown-hubspot/hubspot-project-components-ua-app-objects-beta';
33
33
  uiInfoSection(commands.getStarted.startTitle, () => {
34
34
  uiLogger.log(commands.getStarted.startDescription);
35
35
  uiLogger.log(commands.getStarted.guideOverview(accountName));
@@ -52,6 +52,8 @@ async function handler(args) {
52
52
  default: GET_STARTED_OPTIONS.APP,
53
53
  },
54
54
  ]);
55
+ // Track user's initial choice
56
+ await trackCommandMetadataUsage('get-started', { step: 'select-option', type: selectedOption }, derivedAccountId);
55
57
  if (selectedOption === GET_STARTED_OPTIONS.CMS) {
56
58
  uiLogger.log(' ');
57
59
  uiLogger.log(commands.getStarted.designManager);
@@ -64,6 +66,11 @@ async function handler(args) {
64
66
  message: commands.getStarted.openDesignManagerPrompt,
65
67
  },
66
68
  ]);
69
+ // Track Design Manager browser action
70
+ await trackCommandMetadataUsage('get-started', {
71
+ step: 'open-design-manager',
72
+ type: shouldOpen ? 'opened' : 'declined',
73
+ }, derivedAccountId);
67
74
  if (shouldOpen) {
68
75
  uiLogger.log('');
69
76
  openLink(derivedAccountId, 'design-manager');
@@ -74,36 +81,37 @@ async function handler(args) {
74
81
  else {
75
82
  uiLogger.log(' ');
76
83
  uiLogger.log(commands.getStarted.logs.appSelected);
77
- // 1. Fetch project templates
78
- let latestRepoReleaseTag;
79
84
  const { dest, name } = await projectNameAndDestPrompt(args);
80
- // Specific template for get-started command
81
- const projectTemplate = {
82
- name: 'private-app-get-started-template',
83
- label: 'CRM getting started project with private apps',
84
- path: 'projects/private-app-get-started-template',
85
- };
86
- // 3. Create the project files
87
85
  const projectDest = path.resolve(getCwd(), dest);
88
86
  const { projectConfig: existingProjectConfig, projectDir: existingProjectDir, } = await getProjectConfig(projectDest);
89
87
  if (existingProjectConfig &&
90
88
  existingProjectDir &&
91
89
  projectDest.startsWith(existingProjectDir)) {
90
+ // Track nested project error
91
+ await trackCommandMetadataUsage('get-started', {
92
+ successful: false,
93
+ step: 'project-creation',
94
+ }, derivedAccountId);
92
95
  uiLogger.log(' ');
93
96
  uiLogger.error(commands.project.create.errors.cannotNestProjects(existingProjectDir));
94
97
  process.exit(EXIT_CODES.ERROR);
95
98
  }
96
- const repo = templateSource || HUBSPOT_PROJECT_COMPONENTS_GITHUB_PATH;
97
99
  // 4. Clone the project template from GitHub
98
- // This is temporary until we have the UA template in the main repo
99
100
  try {
100
- await cloneGithubRepo(repo, projectDest, {
101
- sourceDir: projectTemplate.path,
102
- tag: latestRepoReleaseTag,
101
+ await cloneGithubRepo(HUBSPOT_PROJECT_COMPONENTS_GITHUB_PATH, projectDest, {
102
+ sourceDir: '2025.2/private-app-get-started-template',
103
103
  hideLogs: true,
104
104
  });
105
+ await trackCommandMetadataUsage('get-started', {
106
+ successful: true,
107
+ step: 'github-clone',
108
+ }, derivedAccountId);
105
109
  }
106
110
  catch (err) {
111
+ await trackCommandMetadataUsage('get-started', {
112
+ successful: false,
113
+ step: 'github-clone',
114
+ }, derivedAccountId);
107
115
  debugError(err);
108
116
  uiLogger.log(' ');
109
117
  uiLogger.error(commands.project.create.errors.failedToDownloadProject);
@@ -122,6 +130,11 @@ async function handler(args) {
122
130
  uiLogger.log(' ');
123
131
  uiLogger.log(commands.getStarted.prompts.projectCreated.description);
124
132
  uiLogger.log(' ');
133
+ // Track successful project creation
134
+ await trackCommandMetadataUsage('get-started', {
135
+ successful: true,
136
+ step: 'project-creation',
137
+ }, derivedAccountId);
125
138
  // 5. Install dependencies
126
139
  const installLocations = await getProjectPackageJsonLocations(projectDest);
127
140
  try {
@@ -147,11 +160,21 @@ async function handler(args) {
147
160
  default: true,
148
161
  },
149
162
  ]);
163
+ // Track upload decision
164
+ await trackCommandMetadataUsage('get-started', {
165
+ step: 'upload-decision',
166
+ type: shouldUpload ? 'upload' : 'skip',
167
+ }, derivedAccountId);
150
168
  if (shouldUpload) {
151
169
  try {
152
170
  // Get the project config for the newly created project
153
171
  const { projectConfig: newProjectConfig, projectDir: newProjectDir } = await getProjectConfig(projectDest);
154
172
  if (!newProjectConfig || !newProjectDir) {
173
+ // Track config file not found error
174
+ await trackCommandMetadataUsage('get-started', {
175
+ successful: false,
176
+ step: 'config-file-not-found',
177
+ }, derivedAccountId);
155
178
  uiLogger.log(' ');
156
179
  uiLogger.error(commands.getStarted.errors.configFileNotFound);
157
180
  process.exit(EXIT_CODES.ERROR);
@@ -172,11 +195,21 @@ async function handler(args) {
172
195
  skipValidation: false,
173
196
  });
174
197
  if (uploadError) {
198
+ // Track upload failure
199
+ await trackCommandMetadataUsage('get-started', {
200
+ successful: false,
201
+ step: 'upload',
202
+ }, derivedAccountId);
175
203
  uiLogger.log(' ');
176
204
  uiLogger.error(commands.getStarted.errors.uploadFailed);
177
205
  debugError(uploadError);
178
206
  }
179
207
  else if (result) {
208
+ // Track successful upload completion
209
+ await trackCommandMetadataUsage('get-started', {
210
+ successful: true,
211
+ step: 'upload',
212
+ }, derivedAccountId);
180
213
  uiLogger.log(' ');
181
214
  uiLogger.success(commands.getStarted.logs.uploadSuccess);
182
215
  const { data: { results }, } = await fetchPublicAppsForPortal(derivedAccountId);
@@ -192,6 +225,11 @@ async function handler(args) {
192
225
  message: commands.getStarted.openInstallUrl,
193
226
  },
194
227
  ]);
228
+ // Track Developer Overview browser action
229
+ await trackCommandMetadataUsage('get-started', {
230
+ step: 'open-distribution-page',
231
+ type: shouldOpenOverview ? 'opened' : 'declined',
232
+ }, derivedAccountId);
195
233
  if (shouldOpenOverview) {
196
234
  open(getStaticAuthAppInstallUrl({
197
235
  targetAccountId: derivedAccountId,
@@ -207,6 +245,11 @@ async function handler(args) {
207
245
  }
208
246
  }
209
247
  catch (err) {
248
+ // Track upload exception
249
+ await trackCommandMetadataUsage('get-started', {
250
+ successful: false,
251
+ step: 'upload',
252
+ }, derivedAccountId);
210
253
  uiLogger.log(' ');
211
254
  uiLogger.error(commands.getStarted.errors.uploadFailed);
212
255
  debugError(err);
@@ -214,6 +257,11 @@ async function handler(args) {
214
257
  }
215
258
  }
216
259
  }
260
+ // Track successful completion of get-started command
261
+ await trackCommandMetadataUsage('get-started', {
262
+ successful: true,
263
+ step: 'command-completed',
264
+ }, derivedAccountId);
217
265
  process.exit(EXIT_CODES.SUCCESS);
218
266
  }
219
267
  function getStartedBuilder(yargs) {
@@ -3,12 +3,12 @@ vi.mock('../../../lib/commonOpts');
3
3
  describe('commands/mcp/setup', () => {
4
4
  describe('command', () => {
5
5
  it('should have the correct command structure', () => {
6
- expect(setupCommand.command).toEqual(['setup', 'update']);
6
+ expect(setupCommand.command).toEqual(['setup']);
7
7
  });
8
8
  });
9
9
  describe('describe', () => {
10
10
  it('should be undefined to keep the command hidden', () => {
11
- expect(setupCommand.describe).toBeUndefined();
11
+ expect(setupCommand.describe).toBeDefined();
12
12
  });
13
13
  });
14
14
  describe('builder', () => {
@@ -4,9 +4,18 @@ import { commands } from '../../lang/en.js';
4
4
  import { uiLogger } from '../../lib/ui/logger.js';
5
5
  import { addMcpServerToConfig, supportedTools } from '../../lib/mcp/setup.js';
6
6
  import { trackCommandUsage } from '../../lib/usageTracking.js';
7
- const command = ['setup', 'update'];
8
- const describe = undefined; // Leave hidden for now
7
+ import { hasFeature } from '../../lib/hasFeature.js';
8
+ import { FEATURES } from '../../lib/constants.js';
9
+ import { uiBetaTag } from '../../lib/ui/index.js';
10
+ const command = ['setup'];
11
+ const describe = uiBetaTag(commands.mcp.setup.describe, false);
9
12
  async function handler(args) {
13
+ const { derivedAccountId } = args;
14
+ const hasMcpAccess = await hasFeature(derivedAccountId, FEATURES.MCP_ACCESS);
15
+ if (!hasMcpAccess) {
16
+ uiLogger.error(commands.mcp.setup.errors.needsMcpAccess(derivedAccountId));
17
+ process.exit(EXIT_CODES.ERROR);
18
+ }
10
19
  try {
11
20
  await import('@modelcontextprotocol/sdk/server/mcp.js');
12
21
  }
package/commands/mcp.js CHANGED
@@ -2,14 +2,14 @@ import startCommand from './mcp/start.js';
2
2
  import setupCommand from './mcp/setup.js';
3
3
  import { makeYargsBuilder } from '../lib/yargsUtils.js';
4
4
  import { commands } from '../lang/en.js';
5
+ import { uiBetaTag } from '../lib/ui/index.js';
5
6
  const command = 'mcp';
6
- // Leave this as undefined to hide the command
7
- const describe = undefined;
7
+ const describe = uiBetaTag(commands.mcp.describe, false);
8
8
  function mcpBuilder(yargs) {
9
9
  yargs.command(startCommand).command(setupCommand).demandCommand(1, '');
10
10
  return yargs;
11
11
  }
12
- const builder = makeYargsBuilder(mcpBuilder, command, commands.mcp.describe, {
12
+ const builder = makeYargsBuilder(mcpBuilder, command, describe, {
13
13
  useGlobalOptions: true,
14
14
  });
15
15
  const mcpCommand = {
@@ -48,10 +48,10 @@ describe('commands/project/create', () => {
48
48
  projectCreateCommand.builder(yargsMock);
49
49
  const optionsCall = optionsSpy.mock.calls[0][0];
50
50
  expect(optionsCall['platform-version']).toEqual(expect.objectContaining({
51
- hidden: true,
51
+ describe: 'The target platform version for the new project.',
52
52
  type: 'string',
53
53
  choices: ['2023.2', '2025.1', '2025.2'],
54
- default: '2023.2',
54
+ default: '2025.2',
55
55
  }));
56
56
  });
57
57
  it('should define project base option with correct choices', () => {
@@ -59,7 +59,7 @@ describe('commands/project/create', () => {
59
59
  projectCreateCommand.builder(yargsMock);
60
60
  const optionsCall = optionsSpy.mock.calls[0][0];
61
61
  expect(optionsCall['project-base']).toEqual(expect.objectContaining({
62
- hidden: true,
62
+ describe: 'The top level component to include in the project.',
63
63
  type: 'string',
64
64
  choices: ['empty', 'app'],
65
65
  }));
@@ -69,7 +69,7 @@ describe('commands/project/create', () => {
69
69
  projectCreateCommand.builder(yargsMock);
70
70
  const optionsCall = optionsSpy.mock.calls[0][0];
71
71
  expect(optionsCall.distribution).toEqual(expect.objectContaining({
72
- hidden: true,
72
+ describe: 'How the app will be distributed.',
73
73
  type: 'string',
74
74
  choices: ['private', 'marketplace'],
75
75
  }));
@@ -79,7 +79,7 @@ describe('commands/project/create', () => {
79
79
  projectCreateCommand.builder(yargsMock);
80
80
  const optionsCall = optionsSpy.mock.calls[0][0];
81
81
  expect(optionsCall.auth).toEqual(expect.objectContaining({
82
- hidden: true,
82
+ describe: 'Authentication model for the application.',
83
83
  type: 'string',
84
84
  choices: ['oauth', 'static'],
85
85
  }));
@@ -89,7 +89,7 @@ describe('commands/project/create', () => {
89
89
  projectCreateCommand.builder(yargsMock);
90
90
  const optionsCall = optionsSpy.mock.calls[0][0];
91
91
  expect(optionsCall.features).toEqual(expect.objectContaining({
92
- hidden: true,
92
+ describe: 'Features to include in the project. Only valid if project-base is app',
93
93
  type: 'array',
94
94
  }));
95
95
  });
@@ -62,9 +62,6 @@ describe('commands/project/deploy', () => {
62
62
  });
63
63
  });
64
64
  describe('describe', () => {
65
- it('should contain the beta tag', () => {
66
- expect(projectDeployCommand.describe).toContain('[BETA]');
67
- });
68
65
  it('should provide a description', () => {
69
66
  expect(projectDeployCommand.describe).toBeDefined();
70
67
  });
@@ -324,8 +324,7 @@ describe('unifiedProjectDevFlow', () => {
324
324
  });
325
325
  expect(createNewProjectForLocalDev).not.toHaveBeenCalled();
326
326
  expect(LocalDevProcess).toHaveBeenCalledWith(expect.objectContaining({
327
- projectId: mockProject.id,
328
- projectName: mockProject.name,
327
+ projectData: mockProject,
329
328
  }));
330
329
  });
331
330
  });
@@ -346,8 +345,7 @@ describe('unifiedProjectDevFlow', () => {
346
345
  targetTestingAccountId: mockProvidedTargetTestingAccountId,
347
346
  projectConfig: mockProjectConfig,
348
347
  projectDir: mockProjectDir,
349
- projectName: mockProject.name,
350
- projectId: mockProject.id,
348
+ projectData: mockProject,
351
349
  env: ENVIRONMENTS.PROD,
352
350
  });
353
351
  });
@@ -45,9 +45,6 @@ describe('commands/project/logs', () => {
45
45
  });
46
46
  });
47
47
  describe('describe', () => {
48
- it('should contain the beta tag', () => {
49
- expect(projectLogsCommand.describe).toContain('[BETA]');
50
- });
51
48
  it('should provide a description', () => {
52
49
  expect(projectLogsCommand.describe).toBeDefined();
53
50
  });
@@ -44,7 +44,7 @@ describe('commands/project/migrate', () => {
44
44
  });
45
45
  describe('describe', () => {
46
46
  it('should provide a description', () => {
47
- expect(migrateCommand.describe).toBe(undefined);
47
+ expect(migrateCommand.describe).toBeDefined();
48
48
  });
49
49
  });
50
50
  describe('builder', () => {
@@ -54,7 +54,6 @@ describe('commands/project/migrate', () => {
54
54
  type: 'string',
55
55
  choices: [v2025_2],
56
56
  default: v2025_2,
57
- hidden: true,
58
57
  });
59
58
  expect(optionsSpy).toHaveBeenCalledWith('unstable', {
60
59
  type: 'boolean',
@@ -57,8 +57,7 @@ describe('commands/project/migrateApp', () => {
57
57
  'platform-version': expect.objectContaining({
58
58
  type: 'string',
59
59
  choices: [v2023_2, v2025_2],
60
- hidden: true,
61
- default: v2023_2,
60
+ default: v2025_2,
62
61
  }),
63
62
  });
64
63
  expect(exampleSpy).toHaveBeenCalled();
@@ -19,7 +19,7 @@ describe('commands/project', () => {
19
19
  });
20
20
  describe('describe', () => {
21
21
  it('should not provide a description', () => {
22
- expect(profileCommand.describe).not.toBeDefined();
22
+ expect(profileCommand.describe).toBeDefined();
23
23
  });
24
24
  });
25
25
  describe('builder', () => {
@@ -1,6 +1,5 @@
1
1
  import { logError } from '../../lib/errorHandlers/index.js';
2
2
  import { getProjectConfig } from '../../lib/projects/config.js';
3
- import { uiBetaTag } from '../../lib/ui/index.js';
4
3
  import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
5
4
  import { makeYargsBuilder } from '../../lib/yargsUtils.js';
6
5
  import { commands } from '../../lang/en.js';
@@ -10,7 +9,7 @@ import { v3AddComponent } from '../../lib/projects/add/v3AddComponent.js';
10
9
  import { marketplaceDistribution, oAuth, privateDistribution, staticAuth, } from '../../lib/constants.js';
11
10
  import { uiLogger } from '../../lib/ui/logger.js';
12
11
  const command = 'add';
13
- const describe = uiBetaTag(commands.project.add.describe, false);
12
+ const describe = commands.project.add.describe;
14
13
  async function handler(args) {
15
14
  try {
16
15
  const { derivedAccountId } = args;
@@ -45,19 +44,16 @@ function projectAddBuilder(yargs) {
45
44
  },
46
45
  distribution: {
47
46
  describe: commands.project.add.options.distribution.describe,
48
- hidden: true,
49
47
  type: 'string',
50
48
  choices: [privateDistribution, marketplaceDistribution],
51
49
  },
52
50
  auth: {
53
51
  describe: commands.project.add.options.auth.describe,
54
- hidden: true,
55
52
  type: 'string',
56
53
  choices: [oAuth, staticAuth],
57
54
  },
58
55
  features: {
59
56
  describe: commands.project.add.options.features.describe,
60
- hidden: true,
61
57
  type: 'array',
62
58
  },
63
59
  });