@hubspot/cli 8.9.1-beta.0 → 8.10.0-beta.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 (53) hide show
  1. package/commands/account/auth.js +2 -0
  2. package/commands/app/logs.js +4 -0
  3. package/commands/getStarted.js +3 -0
  4. package/commands/init.js +2 -0
  5. package/commands/mcp/setup.js +2 -2
  6. package/commands/project/appInstallStatus.js +2 -2
  7. package/commands/project/create.js +2 -0
  8. package/commands/project/dev/unifiedFlow.js +3 -1
  9. package/commands/project/installApp.d.ts +7 -0
  10. package/commands/project/installApp.js +181 -0
  11. package/commands/project/installDeps.js +2 -0
  12. package/commands/project/upload.d.ts +2 -0
  13. package/commands/project/upload.js +26 -5
  14. package/commands/project.js +2 -0
  15. package/commands/upgrade.js +2 -0
  16. package/lang/en.d.ts +66 -0
  17. package/lang/en.js +73 -7
  18. package/lib/accountTargetDiscovery.d.ts +15 -0
  19. package/lib/accountTargetDiscovery.js +175 -0
  20. package/lib/app/install.d.ts +22 -0
  21. package/lib/app/install.js +42 -0
  22. package/lib/app/logs.js +1 -1
  23. package/lib/auth/awaitPersonalAccessKeyOverWebsocket.js +3 -2
  24. package/lib/doctor/Doctor.d.ts +1 -0
  25. package/lib/doctor/Doctor.js +26 -0
  26. package/lib/getStartedV2Actions.js +2 -1
  27. package/lib/mcp/clients.d.ts +13 -0
  28. package/lib/mcp/clients.js +55 -0
  29. package/lib/mcp/promotion.d.ts +3 -0
  30. package/lib/mcp/promotion.js +109 -0
  31. package/lib/mcp/setup.d.ts +3 -2
  32. package/lib/mcp/setup.js +17 -12
  33. package/lib/projects/installApp.d.ts +58 -0
  34. package/lib/projects/installApp.js +283 -0
  35. package/lib/projects/localDev/AppDevModeInterface.d.ts +0 -3
  36. package/lib/projects/localDev/AppDevModeInterface.js +22 -44
  37. package/lib/projects/localDev/LocalDevProcess.js +4 -2
  38. package/lib/projects/localDev/helpers/project.js +3 -2
  39. package/lib/projects/ui.d.ts +1 -0
  40. package/lib/projects/ui.js +14 -0
  41. package/lib/projects/upload.d.ts +10 -2
  42. package/lib/projects/upload.js +15 -4
  43. package/lib/prompts/projectDevTargetAccountPrompt.js +3 -2
  44. package/lib/theme/cmsDevServerProcess.js +1 -1
  45. package/lib/usageTracking.d.ts +1 -0
  46. package/lib/usageTracking.js +9 -0
  47. package/mcp-server/tools/project/CreateTestAccountTool.js +1 -1
  48. package/mcp-server/tools/project/UploadProjectTools.js +1 -1
  49. package/package.json +5 -5
  50. package/types/AccountTargets.d.ts +37 -0
  51. package/types/AccountTargets.js +12 -0
  52. package/types/LocalDev.d.ts +3 -2
  53. package/types/ProjectComponents.d.ts +2 -1
@@ -11,6 +11,7 @@ import { makeYargsBuilder } from '../../lib/yargsUtils.js';
11
11
  import { commands } from '../../lang/en.js';
12
12
  import { uiLogger } from '../../lib/ui/logger.js';
13
13
  import { authenticateNewAccount } from '../../lib/accountAuth.js';
14
+ import { showMcpPromotionNudge } from '../../lib/mcp/promotion.js';
14
15
  const TRACKING_STATUS = {
15
16
  STARTED: 'started',
16
17
  ERROR: 'error',
@@ -56,6 +57,7 @@ async function handler(args) {
56
57
  'accountAuthCommand',
57
58
  'accountsListCommand',
58
59
  ]);
60
+ await showMcpPromotionNudge(args._.join(' '));
59
61
  if (!disableTracking) {
60
62
  await trackAuthAction('account-auth', authType, TRACKING_STATUS.COMPLETE, accountId);
61
63
  }
@@ -6,6 +6,7 @@ import { selectAppPrompt } from '../../lib/prompts/selectAppPrompt.js';
6
6
  import { listPrompt } from '../../lib/prompts/promptUtils.js';
7
7
  import { getTypeChoices, handleLogsRequest, SYSTEM_TYPE_CHOICES, tailAppLogs, toSystemType, } from '../../lib/app/logs.js';
8
8
  import { logError } from '../../lib/errorHandlers/index.js';
9
+ import { showMcpPromotionNudge } from '../../lib/mcp/promotion.js';
9
10
  const command = 'logs';
10
11
  const describe = commands.app.subcommands.logs.describe;
11
12
  const handler = async (options) => {
@@ -37,6 +38,9 @@ const handler = async (options) => {
37
38
  }
38
39
  else {
39
40
  await handleLogsRequest(derivedAccountId, appId, systemType, options);
41
+ if (!options.json) {
42
+ await showMcpPromotionNudge(options._.join(' '));
43
+ }
40
44
  }
41
45
  return exit(EXIT_CODES.SUCCESS);
42
46
  }
@@ -24,6 +24,7 @@ import ProjectValidationError from '../lib/errors/ProjectValidationError.js';
24
24
  import { openLink } from '../lib/links.js';
25
25
  import { runGetStartedV2 } from '../lib/getStarted/getStartedV2.js';
26
26
  import { isLegacyProject } from '@hubspot/project-parsing-lib/projects';
27
+ import { showMcpPromotionNudge } from '../lib/mcp/promotion.js';
27
28
  const command = 'get-started';
28
29
  const describe = commands.getStarted.describe;
29
30
  async function handler(args) {
@@ -206,6 +207,7 @@ async function handler(args) {
206
207
  isUploadCommand: false,
207
208
  sendIR: !isLegacyProject(newProjectConfig.platformVersion),
208
209
  skipValidation: false,
210
+ force: true,
209
211
  });
210
212
  if (uploadError) {
211
213
  // Track upload failure
@@ -275,6 +277,7 @@ async function handler(args) {
275
277
  return exit(EXIT_CODES.SUCCESS);
276
278
  }
277
279
  }
280
+ await showMcpPromotionNudge(args._.join(' '));
278
281
  // Track successful completion of get-started command
279
282
  await trackCommandMetadataUsage('get-started', {
280
283
  successful: true,
package/commands/init.js CHANGED
@@ -24,6 +24,7 @@ import { makeWrappedYargsHandler } from '../lib/yargs/makeWrappedYargsHandler.js
24
24
  import { uiLogger } from '../lib/ui/logger.js';
25
25
  import { commands } from '../lang/en.js';
26
26
  import { parseStringToNumber } from '../lib/parsing.js';
27
+ import { showMcpPromotionNudge } from '../lib/mcp/promotion.js';
27
28
  const TRACKING_STATUS = {
28
29
  STARTED: 'started',
29
30
  ERROR: 'error',
@@ -142,6 +143,7 @@ async function handler(args) {
142
143
  'authCommand',
143
144
  'accountsListCommand',
144
145
  ]);
146
+ await showMcpPromotionNudge(args._.join(' '));
145
147
  if (!disableTracking) {
146
148
  await trackAuthAction('init', authType, TRACKING_STATUS.COMPLETE, accountId);
147
149
  }
@@ -2,13 +2,13 @@ import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
2
2
  import { makeYargsBuilder } from '../../lib/yargsUtils.js';
3
3
  import { commands } from '../../lang/en.js';
4
4
  import { makeWrappedYargsHandler } from '../../lib/yargs/makeWrappedYargsHandler.js';
5
- import { addMcpServerToConfig, supportedTools } from '../../lib/mcp/setup.js';
5
+ import { configureMcpServer, supportedTools } from '../../lib/mcp/setup.js';
6
6
  const command = ['setup'];
7
7
  const describe = commands.mcp.setup.describe;
8
8
  async function handler(args) {
9
9
  const { exit } = args;
10
10
  try {
11
- await addMcpServerToConfig(args.client);
11
+ await configureMcpServer(args.client);
12
12
  }
13
13
  catch (e) {
14
14
  return exit(EXIT_CODES.ERROR);
@@ -28,12 +28,12 @@ async function handler(args) {
28
28
  }
29
29
  let appNode;
30
30
  try {
31
- const { intermediateNodesIndexedByUid } = await translateForLocalDev({
31
+ const { intermediateRepresentation } = await translateForLocalDev({
32
32
  projectSourceDir: path.join(projectDir, projectConfig.srcDir),
33
33
  platformVersion: projectConfig.platformVersion,
34
34
  accountId: derivedAccountId,
35
35
  }, { skipValidation: true });
36
- appNode = Object.values(intermediateNodesIndexedByUid).find(isAppIRNode);
36
+ appNode = Object.values(intermediateRepresentation.intermediateNodesIndexedByUid).find(isAppIRNode);
37
37
  }
38
38
  catch (error) {
39
39
  debugError(error);
@@ -17,6 +17,7 @@ import { uiLogger } from '../../lib/ui/logger.js';
17
17
  import { handleProjectCreationFlow, } from '../../lib/projects/create/index.js';
18
18
  import { updateHsMetaFilesWithAutoGeneratedFields } from '../../lib/projects/components.js';
19
19
  import SpinniesManager from '../../lib/ui/SpinniesManager.js';
20
+ import { showMcpPromotionNudge } from '../../lib/mcp/promotion.js';
20
21
  const command = ['create', 'init'];
21
22
  const describe = commands.project.create.describe;
22
23
  const BETA_VERSIONS = [
@@ -107,6 +108,7 @@ async function handler(args) {
107
108
  if (isProjectEmpty) {
108
109
  fs.ensureDirSync(path.join(projectDest, 'src'));
109
110
  }
111
+ await showMcpPromotionNudge(args._.join(' '));
110
112
  return exit(EXIT_CODES.SUCCESS);
111
113
  }
112
114
  function projectCreateBuilder(yargs) {
@@ -6,6 +6,7 @@ import { isTranslationError, translateForLocalDev, } from '@hubspot/project-pars
6
6
  import { getConfigAccountEnvironment, getLinkedOrAllConfigAccounts, getConfigAccountById, getConfigAccountIfExists, } from '@hubspot/local-dev-lib/config';
7
7
  import { logError } from '../../../lib/errorHandlers/index.js';
8
8
  import { EXIT_CODES } from '../../../lib/enums/exitCodes.js';
9
+ import { warnAboutSkippedHsMetaFiles } from '../../../lib/projects/ui.js';
9
10
  import { ensureProjectExists } from '../../../lib/projects/ensureProjectExists.js';
10
11
  import { createInitialBuildForNewProject, createNewProjectForLocalDev, compareLocalProjectToDeployed, checkAndInstallDependencies, } from '../../../lib/projects/localDev/helpers/project.js';
11
12
  import { useExistingDevTestAccount, createDeveloperTestAccountForLocalDev, selectAccountTypePrompt, createSandboxForLocalDev, } from '../../../lib/projects/localDev/helpers/account.js';
@@ -31,7 +32,7 @@ export async function unifiedProjectDevFlow({ args, targetProjectAccountId, prov
31
32
  let projectProfileData;
32
33
  // Get IR
33
34
  try {
34
- const intermediateRepresentation = await translateForLocalDev({
35
+ const { intermediateRepresentation, skippedHsMetaFiles } = await translateForLocalDev({
35
36
  projectSourceDir: path.join(projectDir, projectConfig.srcDir),
36
37
  platformVersion: projectConfig.platformVersion,
37
38
  accountId: targetProjectAccountId,
@@ -39,6 +40,7 @@ export async function unifiedProjectDevFlow({ args, targetProjectAccountId, prov
39
40
  projectNodes = intermediateRepresentation.intermediateNodesIndexedByUid;
40
41
  projectProfileData = intermediateRepresentation.profileData;
41
42
  uiLogger.debug(util.inspect(projectNodes, false, null, true));
43
+ await warnAboutSkippedHsMetaFiles(skippedHsMetaFiles, true, false);
42
44
  }
43
45
  catch (e) {
44
46
  if (isTranslationError(e)) {
@@ -0,0 +1,7 @@
1
+ import { AccountArgs, CommonArgs, ConfigArgs, EnvironmentArgs, JSONOutputArgs, YargsCommandModule } from '../../types/Yargs.js';
2
+ type ProjectInstallAppArgs = CommonArgs & ConfigArgs & AccountArgs & EnvironmentArgs & JSONOutputArgs & {
3
+ force: boolean;
4
+ profile?: string;
5
+ };
6
+ declare const projectInstallAppCommand: YargsCommandModule<unknown, ProjectInstallAppArgs>;
7
+ export default projectInstallAppCommand;
@@ -0,0 +1,181 @@
1
+ import { getConfigAccountIfExists } from '@hubspot/local-dev-lib/config';
2
+ import { isLegacyProject } from '@hubspot/project-parsing-lib/projects';
3
+ import { makeWrappedYargsHandler } from '../../lib/yargs/makeWrappedYargsHandler.js';
4
+ import { makeYargsBuilder } from '../../lib/yargsUtils.js';
5
+ import { uiLogger } from '../../lib/ui/logger.js';
6
+ import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
7
+ import { commands } from '../../lang/en.js';
8
+ import { getProjectConfig } from '../../lib/projects/config.js';
9
+ import { APP_INSTALLATION_STATES } from '../../lib/constants.js';
10
+ import { getAppInstallationState } from '../../lib/app/install.js';
11
+ import { confirmInstallAppAction, fetchProjectAppInstallationData, installStaticAuthAppForAccount, loadInstallableAppNode, resolveAppMetadata, resolveProjectId, resolveProjectAccountId, resolveValidInstallAccount, } from '../../lib/projects/installApp.js';
12
+ const command = 'install-app';
13
+ const describe = commands.project.installApp.describe;
14
+ const verboseDescribe = commands.project.installApp.verboseDescribe;
15
+ async function handler(args) {
16
+ const { derivedAccountId, formatOutputAsJson, force, profile: profileOption, useEnv: useEnvOption, exit, } = args;
17
+ const { projectConfig, projectDir } = await getProjectConfig();
18
+ if (!projectConfig || !projectDir) {
19
+ uiLogger.error(commands.project.installApp.errors.noProjectConfig);
20
+ return exit(EXIT_CODES.ERROR);
21
+ }
22
+ if (isLegacyProject(projectConfig.platformVersion)) {
23
+ uiLogger.error(commands.project.installApp.errors.unsupportedPlatformVersion(projectConfig.platformVersion));
24
+ return exit(EXIT_CODES.ERROR);
25
+ }
26
+ const resolvedProjectAccount = await resolveProjectAccountId(derivedAccountId, projectConfig, projectDir, profileOption, !!useEnvOption);
27
+ if (resolvedProjectAccount === null) {
28
+ return exit(EXIT_CODES.ERROR);
29
+ }
30
+ const appNode = await loadInstallableAppNode(projectConfig, projectDir, resolvedProjectAccount.accountId, force);
31
+ if (!appNode) {
32
+ return exit(EXIT_CODES.ERROR);
33
+ }
34
+ const installAccountId = await resolveValidInstallAccount(resolvedProjectAccount.accountId, force, Boolean(formatOutputAsJson));
35
+ if (installAccountId === null) {
36
+ return exit(EXIT_CODES.ERROR);
37
+ }
38
+ const targetAccountConfig = getConfigAccountIfExists(installAccountId);
39
+ // A profile only applies if we didn't switch away from its account.
40
+ const uploadProfile = installAccountId === resolvedProjectAccount.accountId
41
+ ? resolvedProjectAccount.profileName
42
+ : undefined;
43
+ const projectId = await resolveProjectId({
44
+ accountId: installAccountId,
45
+ projectConfig,
46
+ projectDir,
47
+ profile: uploadProfile,
48
+ force,
49
+ formatOutputAsJson: Boolean(formatOutputAsJson),
50
+ });
51
+ if (projectId === null) {
52
+ return exit(EXIT_CODES.ERROR);
53
+ }
54
+ const installationData = await fetchProjectAppInstallationData(installAccountId, projectId, appNode, projectConfig);
55
+ if (installationData === null) {
56
+ return exit(EXIT_CODES.ERROR);
57
+ }
58
+ let appId = installationData.appId;
59
+ const { isInstalledWithScopeGroups, previouslyAuthorizedScopeGroups } = installationData;
60
+ const needsReinstall = !isInstalledWithScopeGroups && previouslyAuthorizedScopeGroups.length > 0;
61
+ const installationState = getAppInstallationState(isInstalledWithScopeGroups, previouslyAuthorizedScopeGroups);
62
+ if (isInstalledWithScopeGroups) {
63
+ if (formatOutputAsJson) {
64
+ uiLogger.json({
65
+ appId,
66
+ appUid: appNode.uid,
67
+ accountId: installAccountId,
68
+ projectId,
69
+ installationState,
70
+ installed: true,
71
+ reinstalled: false,
72
+ });
73
+ }
74
+ else {
75
+ uiLogger.success(commands.project.installApp.alreadyInstalled(appNode.config.name, installAccountId));
76
+ }
77
+ return exit(EXIT_CODES.SUCCESS);
78
+ }
79
+ const shouldInstall = await confirmInstallAppAction({
80
+ appName: appNode.config.name,
81
+ targetAccountId: installAccountId,
82
+ force,
83
+ needsReinstall,
84
+ });
85
+ if (!shouldInstall) {
86
+ return exit(EXIT_CODES.SUCCESS);
87
+ }
88
+ const metadata = await resolveAppMetadata({
89
+ appId,
90
+ projectId,
91
+ appUid: appNode.uid,
92
+ appName: appNode.config.name,
93
+ accountId: installAccountId,
94
+ projectConfig,
95
+ projectDir,
96
+ profile: uploadProfile,
97
+ force,
98
+ formatOutputAsJson: Boolean(formatOutputAsJson),
99
+ });
100
+ if (metadata === null) {
101
+ return exit(EXIT_CODES.ERROR);
102
+ }
103
+ appId = metadata.appId;
104
+ const installed = await installStaticAuthAppForAccount({
105
+ appId,
106
+ appNode,
107
+ appName: appNode.config.name,
108
+ targetAccountId: installAccountId,
109
+ targetAccountConfig,
110
+ projectId,
111
+ projectName: projectConfig.name,
112
+ scopeGroupIds: metadata.scopeGroupIds,
113
+ ownerPortalId: metadata.ownerPortalId,
114
+ installationState,
115
+ formatOutputAsJson: Boolean(formatOutputAsJson),
116
+ });
117
+ if (!installed) {
118
+ return exit(EXIT_CODES.ERROR);
119
+ }
120
+ if (formatOutputAsJson) {
121
+ uiLogger.json({
122
+ appId,
123
+ appUid: appNode.uid,
124
+ accountId: installAccountId,
125
+ projectId,
126
+ installationState: APP_INSTALLATION_STATES.INSTALLED,
127
+ installed: true,
128
+ reinstalled: needsReinstall,
129
+ });
130
+ }
131
+ else {
132
+ uiLogger.success(commands.project.installApp.success(appNode.config.name, installAccountId));
133
+ }
134
+ return exit(EXIT_CODES.SUCCESS);
135
+ }
136
+ function projectInstallAppBuilder(yargs) {
137
+ yargs.options({
138
+ force: {
139
+ alias: ['f'],
140
+ describe: commands.project.installApp.options.force,
141
+ default: false,
142
+ type: 'boolean',
143
+ },
144
+ profile: {
145
+ alias: 'p',
146
+ describe: commands.project.installApp.options.profile,
147
+ type: 'string',
148
+ },
149
+ });
150
+ yargs.conflicts('profile', 'account');
151
+ yargs.example([
152
+ ['$0 project install-app', commands.project.installApp.examples.default],
153
+ [
154
+ '$0 project install-app --account=12345678',
155
+ commands.project.installApp.examples.withAccount,
156
+ ],
157
+ [
158
+ '$0 project install-app --profile=qa',
159
+ commands.project.installApp.examples.withProfile,
160
+ ],
161
+ [
162
+ '$0 project install-app --json',
163
+ commands.project.installApp.examples.json,
164
+ ],
165
+ ]);
166
+ return yargs;
167
+ }
168
+ const builder = makeYargsBuilder(projectInstallAppBuilder, command, verboseDescribe, {
169
+ useGlobalOptions: true,
170
+ useConfigOptions: true,
171
+ useAccountOptions: true,
172
+ useEnvironmentOptions: true,
173
+ useJSONOutputOptions: true,
174
+ });
175
+ const projectInstallAppCommand = {
176
+ command,
177
+ describe,
178
+ handler: makeWrappedYargsHandler('project-install-app', handler),
179
+ builder,
180
+ };
181
+ export default projectInstallAppCommand;
@@ -9,6 +9,7 @@ import { uiLogger } from '../../lib/ui/logger.js';
9
9
  import { makeWrappedYargsHandler } from '../../lib/yargs/makeWrappedYargsHandler.js';
10
10
  import { logError } from '../../lib/errorHandlers/index.js';
11
11
  import { makeYargsBuilder } from '../../lib/yargsUtils.js';
12
+ import { showMcpPromotionNudge } from '../../lib/mcp/promotion.js';
12
13
  const command = 'install-deps [packages..]';
13
14
  const describe = commands.project.installDeps.help.describe;
14
15
  async function handler(args) {
@@ -48,6 +49,7 @@ async function handler(args) {
48
49
  packages,
49
50
  installLocations,
50
51
  });
52
+ await showMcpPromotionNudge(args._.join(' '));
51
53
  }
52
54
  catch (e) {
53
55
  if (isPromptExitError(e)) {
@@ -1,10 +1,12 @@
1
1
  import { CommonArgs, JSONOutputArgs, YargsCommandModule } from '../../types/Yargs.js';
2
2
  export type ProjectUploadArgs = CommonArgs & JSONOutputArgs & {
3
+ force: boolean;
3
4
  forceCreate: boolean;
4
5
  message: string;
5
6
  m: string;
6
7
  skipValidation: boolean;
7
8
  skipNpmAudit: boolean;
9
+ skipAutoDeploy: boolean;
8
10
  profile?: string;
9
11
  preview: boolean;
10
12
  target?: number;
@@ -16,6 +16,7 @@ import { EXIT_CODES } from '../../lib/enums/exitCodes.js';
16
16
  import { makeWrappedYargsHandler } from '../../lib/yargs/makeWrappedYargsHandler.js';
17
17
  import { makeYargsBuilder } from '../../lib/yargsUtils.js';
18
18
  import { projectProfilePrompt } from '../../lib/prompts/projectProfilePrompt.js';
19
+ import { showMcpPromotionNudge } from '../../lib/mcp/promotion.js';
19
20
  const command = 'upload';
20
21
  const describe = commands.project.upload.describe;
21
22
  async function handlePreview(accountId, projectId, buildId, targetPortalId) {
@@ -33,7 +34,7 @@ async function handlePreview(accountId, projectId, buildId, targetPortalId) {
33
34
  };
34
35
  }
35
36
  async function handler(args) {
36
- const { forceCreate, message, derivedAccountId, skipValidation, skipNpmAudit, formatOutputAsJson, profile: profileOption, useEnv: useEnvOption, preview, target: targetPortalId, exit, addUsageMetadata, } = args;
37
+ const { force = false, forceCreate = false, message, derivedAccountId, skipValidation, skipNpmAudit, skipAutoDeploy, formatOutputAsJson, profile: profileOption, useEnv: useEnvOption, preview, target: targetPortalId, exit, addUsageMetadata, } = args;
37
38
  const jsonOutput = {};
38
39
  const { projectConfig, projectDir } = await getProjectConfig();
39
40
  try {
@@ -71,21 +72,26 @@ async function handler(args) {
71
72
  assetType: projectConfig.platformVersion,
72
73
  });
73
74
  try {
74
- const { result, uploadError, projectId } = await handleProjectUpload({
75
+ const { result, uploadError, projectId, userDeclined } = await handleProjectUpload({
75
76
  accountId: targetAccountId,
76
77
  projectConfig,
77
78
  projectDir,
78
- callbackFunc: preview
79
+ callbackFunc: preview || skipAutoDeploy
79
80
  ? (...args) => pollProjectBuildAndDeploy(...args, { skipDeploy: true })
80
81
  : pollProjectBuildAndDeploy,
81
82
  uploadMessage: message,
82
- forceCreate,
83
+ forceCreate: forceCreate || force,
83
84
  isUploadCommand: true,
84
85
  sendIR: !isLegacyProject(projectConfig.platformVersion),
85
86
  skipValidation,
86
87
  skipNpmAudit,
88
+ skipAutoDeploy,
87
89
  profile: profileName,
90
+ force,
88
91
  });
92
+ if (userDeclined) {
93
+ return exit(EXIT_CODES.SUCCESS);
94
+ }
89
95
  if (uploadError) {
90
96
  if (isSpecifiedError(uploadError, {
91
97
  subCategory: PROJECT_ERROR_TYPES.PROJECT_LOCKED,
@@ -104,7 +110,7 @@ async function handler(args) {
104
110
  }
105
111
  if (result &&
106
112
  result.succeeded &&
107
- (!result.buildResult.isAutoDeployEnabled || preview)) {
113
+ (!result.buildResult.isAutoDeployEnabled || preview || skipAutoDeploy)) {
108
114
  uiLogger.log(chalk.bold(commands.project.upload.logs.buildSucceeded(result.buildId)));
109
115
  if (!preview) {
110
116
  uiLogger.log(commands.project.upload.logs.autoDeployDisabled(`hs project deploy --build=${result.buildId}`));
@@ -144,14 +150,24 @@ async function handler(args) {
144
150
  if (formatOutputAsJson) {
145
151
  uiLogger.json(jsonOutput);
146
152
  }
153
+ else {
154
+ await showMcpPromotionNudge(args._.join(' '));
155
+ }
147
156
  return exit(EXIT_CODES.SUCCESS);
148
157
  }
149
158
  function projectUploadBuilder(yargs) {
150
159
  yargs.options({
160
+ force: {
161
+ alias: 'f',
162
+ describe: commands.project.upload.options.force.describe,
163
+ type: 'boolean',
164
+ default: false,
165
+ },
151
166
  'force-create': {
152
167
  describe: commands.project.upload.options.forceCreate.describe,
153
168
  type: 'boolean',
154
169
  default: false,
170
+ hidden: true,
155
171
  },
156
172
  message: {
157
173
  alias: 'm',
@@ -169,6 +185,11 @@ function projectUploadBuilder(yargs) {
169
185
  type: 'boolean',
170
186
  default: false,
171
187
  },
188
+ 'skip-auto-deploy': {
189
+ describe: commands.project.upload.options.skipAutoDeploy.describe,
190
+ type: 'boolean',
191
+ default: false,
192
+ },
172
193
  profile: {
173
194
  type: 'string',
174
195
  alias: 'p',
@@ -21,6 +21,7 @@ import list from './project/list.js';
21
21
  import info from './project/info.js';
22
22
  import deleteProject from './project/delete.js';
23
23
  import appInstallStatus from './project/appInstallStatus.js';
24
+ import installApp from './project/installApp.js';
24
25
  import { makeYargsBuilder } from '../lib/yargsUtils.js';
25
26
  import { getProjectConfig } from '../lib/projects/config.js';
26
27
  import { isSupportedPlatformVersion, LATEST_SUPPORTED_PLATFORM_VERSION, } from '@hubspot/project-parsing-lib/projects';
@@ -70,6 +71,7 @@ function projectBuilder(yargs) {
70
71
  .command(release)
71
72
  .command(projectValidate)
72
73
  .command(appInstallStatus)
74
+ .command(installApp)
73
75
  .demandCommand(1, '');
74
76
  return yargs;
75
77
  }
@@ -11,6 +11,7 @@ import { getLatestCliVersion } from '../lib/cliUpgradeUtils.js';
11
11
  import { pkg } from '../lib/jsonLoader.js';
12
12
  import SpinniesManager from '../lib/ui/SpinniesManager.js';
13
13
  import { debugError } from '../lib/errorHandlers/index.js';
14
+ import { showMcpPromotionNudge } from '../lib/mcp/promotion.js';
14
15
  const command = ['upgrade [version]', 'update [version]'];
15
16
  const describe = commands.upgrade.describe;
16
17
  const handler = async (args) => {
@@ -84,6 +85,7 @@ const handler = async (args) => {
84
85
  uiLogger.log('');
85
86
  uiLogger.log(commands.upgrade.autoUpgradeMessage);
86
87
  }
88
+ await showMcpPromotionNudge(args._.join(' '));
87
89
  return exit(EXIT_CODES.SUCCESS);
88
90
  };
89
91
  function upgradeBuilder(yargs) {
package/lang/en.d.ts CHANGED
@@ -1316,6 +1316,9 @@ export declare const commands: {
1316
1316
  };
1317
1317
  mcp: {
1318
1318
  describe: string;
1319
+ promotion: {
1320
+ activeNudge: string;
1321
+ };
1319
1322
  setup: {
1320
1323
  describe: string;
1321
1324
  installingDocSearch: string;
@@ -1885,6 +1888,9 @@ export declare const commands: {
1885
1888
  targetRequiresPreview: string;
1886
1889
  };
1887
1890
  options: {
1891
+ force: {
1892
+ describe: string;
1893
+ };
1888
1894
  forceCreate: {
1889
1895
  describe: string;
1890
1896
  };
@@ -1897,6 +1903,9 @@ export declare const commands: {
1897
1903
  skipNpmAudit: {
1898
1904
  describe: string;
1899
1905
  };
1906
+ skipAutoDeploy: {
1907
+ describe: string;
1908
+ };
1900
1909
  preview: {
1901
1910
  describe: string;
1902
1911
  };
@@ -2168,6 +2177,47 @@ export declare const commands: {
2168
2177
  force: string;
2169
2178
  };
2170
2179
  };
2180
+ installApp: {
2181
+ describe: string;
2182
+ verboseDescribe: string;
2183
+ examples: {
2184
+ default: string;
2185
+ withAccount: string;
2186
+ withProfile: string;
2187
+ json: string;
2188
+ };
2189
+ options: {
2190
+ force: string;
2191
+ profile: string;
2192
+ };
2193
+ errors: {
2194
+ noProjectConfig: string;
2195
+ unsupportedPlatformVersion: (platformVersion: string) => string;
2196
+ failedToParseProject: string;
2197
+ noAppInProject: string;
2198
+ unsupportedAuthType: (authType: string) => string;
2199
+ unsupportedDistribution: (distribution: string) => string;
2200
+ invalidAppDeveloperAccount: (accountId: number) => string;
2201
+ projectNotFound: (accountId: number, projectName: string) => string;
2202
+ appNotDeployed: (appName: string, accountId: number) => string;
2203
+ automaticInstallUnavailable: (appName: string, accountId: number) => string;
2204
+ installFailed: (appName: string, accountId: number) => string;
2205
+ };
2206
+ jsonErrors: {
2207
+ automaticInstallUnavailable: string;
2208
+ installFailed: (appName: string, accountId: number) => string;
2209
+ };
2210
+ profileMessage: (profileName: string, accountId: number) => string;
2211
+ appDeveloperAccountNotice: (accountId: number) => string;
2212
+ selectInstallAccountPrompt: string;
2213
+ uploadAndDeployPrompt: (accountId: number) => string;
2214
+ alreadyInstalled: (appName: string, accountId: number) => string;
2215
+ outdatedScopes: (appName: string, accountId: number) => string;
2216
+ reinstallPrompt: string;
2217
+ installPrompt: (appName: string, accountId: number) => string;
2218
+ installFromBrowser: (installUrl: string) => string;
2219
+ success: (appName: string, accountId: number) => string;
2220
+ };
2171
2221
  installStatus: {
2172
2222
  describe: string;
2173
2223
  examples: {
@@ -3269,6 +3319,11 @@ export declare const lib: {
3269
3319
  configMiddleWare: {
3270
3320
  invalidAccountIdEnvironmentVariable: string;
3271
3321
  };
3322
+ accountTargetDiscovery: {
3323
+ errors: {
3324
+ explicitAccountNotFound: (account: string | number) => string;
3325
+ };
3326
+ };
3272
3327
  process: {
3273
3328
  exitDebug: (signal: string) => string;
3274
3329
  };
@@ -3533,6 +3588,11 @@ export declare const lib: {
3533
3588
  feedbackHeader: string;
3534
3589
  feedbackMessage: string;
3535
3590
  };
3591
+ skippedHsMetaFiles: {
3592
+ warning: (files: string[]) => string;
3593
+ uploadWarning: (files: string[]) => string;
3594
+ prompt: string;
3595
+ };
3536
3596
  components: {
3537
3597
  unableToGetUidFromHsmeta: string;
3538
3598
  buildSuccessMessage: {
@@ -3837,6 +3897,7 @@ export declare const lib: {
3837
3897
  confirmUseExistingDeveloperTestAccount: (accountName: string) => string;
3838
3898
  confirmLinkExistingDeveloperTestAccount: (accountName: string) => string;
3839
3899
  noAccountId: string;
3900
+ fetchDeveloperTestAccountsError: string;
3840
3901
  };
3841
3902
  projectLogsPrompt: {
3842
3903
  functionName: (projectName: string) => string;
@@ -4241,6 +4302,11 @@ export declare const lib: {
4241
4302
  installed: (npmVersion: string) => string;
4242
4303
  unableToDetermine: string;
4243
4304
  };
4305
+ mcpChecks: {
4306
+ configured: (clients: string[]) => string;
4307
+ notConfigured: (clients: string[]) => string;
4308
+ notConfiguredSecondary: string;
4309
+ };
4244
4310
  hsChecks: {
4245
4311
  notLatest: (hsVersion: string) => string;
4246
4312
  notLatestSecondary: (command: string, hsVersion: string) => string;