@hubspot/cli 8.2.0-experimental.2 → 8.2.1-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 (147) hide show
  1. package/commands/account/clean.js +1 -1
  2. package/commands/account/list.js +1 -1
  3. package/commands/account/remove.js +1 -1
  4. package/commands/account/rename.js +1 -1
  5. package/commands/account/use.js +1 -1
  6. package/commands/api.d.ts +4 -1
  7. package/commands/api.js +11 -14
  8. package/commands/config/set.js +24 -5
  9. package/commands/customObject/createSchema.js +2 -3
  10. package/commands/customObject/updateSchema.js +2 -3
  11. package/commands/getStarted.js +1 -0
  12. package/commands/hubdb/list.d.ts +1 -1
  13. package/commands/hubdb/list.js +7 -8
  14. package/commands/mcp/setup.d.ts +2 -0
  15. package/commands/mcp/setup.js +29 -1
  16. package/commands/project/appInstallStatus.d.ts +2 -1
  17. package/commands/project/appInstallStatus.js +21 -19
  18. package/commands/project/create.js +0 -1
  19. package/commands/project/deploy.d.ts +2 -1
  20. package/commands/project/deploy.js +31 -14
  21. package/commands/project/dev/index.js +18 -64
  22. package/commands/project/dev/unifiedFlow.js +3 -1
  23. package/commands/project/info.d.ts +2 -1
  24. package/commands/project/info.js +8 -6
  25. package/commands/project/installApp.d.ts +2 -1
  26. package/commands/project/installApp.js +26 -27
  27. package/commands/project/list.js +1 -1
  28. package/commands/project/listBuilds.d.ts +1 -1
  29. package/commands/project/listBuilds.js +8 -6
  30. package/commands/project/logs.js +2 -4
  31. package/commands/project/profile/add.d.ts +2 -1
  32. package/commands/project/profile/add.js +22 -1
  33. package/commands/project/profile/delete.d.ts +1 -1
  34. package/commands/project/release/create.d.ts +2 -1
  35. package/commands/project/release/create.js +68 -69
  36. package/commands/project/release/info.d.ts +2 -1
  37. package/commands/project/release/info.js +7 -6
  38. package/commands/project/release/list.d.ts +2 -1
  39. package/commands/project/release/list.js +15 -7
  40. package/commands/project/upload.d.ts +4 -1
  41. package/commands/project/upload.js +48 -21
  42. package/commands/project/validate.d.ts +1 -1
  43. package/commands/project/watch.js +3 -2
  44. package/commands/project.js +2 -2
  45. package/commands/sandbox/delete.js +2 -3
  46. package/commands/testAccount/create.d.ts +2 -1
  47. package/commands/testAccount/create.js +7 -7
  48. package/commands/testAccount/importData.d.ts +1 -1
  49. package/lang/en.d.ts +61 -33
  50. package/lang/en.js +77 -48
  51. package/lib/app/urls.js +2 -3
  52. package/lib/commonOpts.js +6 -1
  53. package/lib/configOptions.d.ts +10 -10
  54. package/lib/configOptions.js +10 -11
  55. package/lib/constants.d.ts +2 -0
  56. package/lib/constants.js +2 -0
  57. package/lib/dependencyManagement.js +7 -7
  58. package/lib/getStartedV2Actions.js +2 -1
  59. package/lib/importData.js +3 -4
  60. package/lib/jsonOutput.d.ts +109 -0
  61. package/lib/jsonOutput.js +94 -0
  62. package/lib/links.js +6 -5
  63. package/lib/mcp/clients.d.ts +1 -1
  64. package/lib/mcp/clients.js +8 -1
  65. package/lib/mcp/setup.d.ts +9 -4
  66. package/lib/mcp/setup.js +115 -35
  67. package/lib/npm/npmCli.d.ts +1 -0
  68. package/lib/npm/npmCli.js +13 -0
  69. package/lib/projects/builds.d.ts +2 -0
  70. package/lib/projects/builds.js +22 -0
  71. package/lib/projects/create/v2.js +1 -0
  72. package/lib/projects/installApp.d.ts +1 -1
  73. package/lib/projects/installApp.js +8 -3
  74. package/lib/projects/localDev/DevSessionManager.d.ts +2 -2
  75. package/lib/projects/localDev/DevSessionManager.js +4 -30
  76. package/lib/projects/localDev/LocalDevProcess.js +7 -5
  77. package/lib/projects/localDev/helpers/account.d.ts +0 -7
  78. package/lib/projects/localDev/helpers/account.js +1 -80
  79. package/lib/projects/localDev/helpers/project.js +3 -2
  80. package/lib/projects/release.d.ts +4 -0
  81. package/lib/projects/release.js +90 -0
  82. package/lib/projects/ui.d.ts +1 -0
  83. package/lib/projects/ui.js +14 -0
  84. package/lib/projects/upload.d.ts +10 -2
  85. package/lib/projects/upload.js +14 -7
  86. package/lib/projects/urls.d.ts +0 -1
  87. package/lib/projects/urls.js +6 -13
  88. package/lib/yargs/makeWrappedYargsHandler.d.ts +5 -1
  89. package/lib/yargs/makeWrappedYargsHandler.js +123 -65
  90. package/mcp-server/Tool.d.ts +8 -3
  91. package/mcp-server/Tool.js +58 -32
  92. package/mcp-server/server.js +10 -6
  93. package/mcp-server/tools/cms/HsCreateFunctionTool.d.ts +2 -2
  94. package/mcp-server/tools/cms/HsCreateFunctionTool.js +10 -17
  95. package/mcp-server/tools/cms/HsCreateModuleTool.d.ts +2 -2
  96. package/mcp-server/tools/cms/HsCreateModuleTool.js +12 -18
  97. package/mcp-server/tools/cms/HsCreateTemplateTool.d.ts +2 -2
  98. package/mcp-server/tools/cms/HsCreateTemplateTool.js +8 -10
  99. package/mcp-server/tools/cms/HsFunctionLogsTool.d.ts +2 -2
  100. package/mcp-server/tools/cms/HsFunctionLogsTool.js +9 -9
  101. package/mcp-server/tools/cms/HsListFunctionsTool.d.ts +2 -2
  102. package/mcp-server/tools/cms/HsListFunctionsTool.js +7 -7
  103. package/mcp-server/tools/cms/HsListTool.d.ts +2 -2
  104. package/mcp-server/tools/cms/HsListTool.js +6 -10
  105. package/mcp-server/tools/index.js +2 -0
  106. package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +3 -2
  107. package/mcp-server/tools/project/AddFeatureToProjectTool.js +8 -9
  108. package/mcp-server/tools/project/AuthAccountTool.d.ts +18 -0
  109. package/mcp-server/tools/project/AuthAccountTool.js +78 -0
  110. package/mcp-server/tools/project/CreateProjectTool.d.ts +3 -2
  111. package/mcp-server/tools/project/CreateProjectTool.js +13 -12
  112. package/mcp-server/tools/project/CreateTestAccountTool.d.ts +2 -2
  113. package/mcp-server/tools/project/CreateTestAccountTool.js +14 -15
  114. package/mcp-server/tools/project/DeployProjectTool.d.ts +2 -2
  115. package/mcp-server/tools/project/DeployProjectTool.js +10 -10
  116. package/mcp-server/tools/project/DocFetchTool.js +1 -1
  117. package/mcp-server/tools/project/DocsSearchTool.js +8 -7
  118. package/mcp-server/tools/project/FindProjectsTool.js +1 -1
  119. package/mcp-server/tools/project/GetApiUsagePatternsByAppIdTool.d.ts +2 -1
  120. package/mcp-server/tools/project/GetApiUsagePatternsByAppIdTool.js +9 -8
  121. package/mcp-server/tools/project/GetApplicationInfoTool.d.ts +2 -1
  122. package/mcp-server/tools/project/GetApplicationInfoTool.js +12 -9
  123. package/mcp-server/tools/project/GetBuildLogsTool.js +11 -7
  124. package/mcp-server/tools/project/GetBuildStatusTool.js +11 -7
  125. package/mcp-server/tools/project/GetConfigValuesTool.d.ts +2 -1
  126. package/mcp-server/tools/project/GetConfigValuesTool.js +9 -7
  127. package/mcp-server/tools/project/GuidedWalkthroughTool.js +1 -1
  128. package/mcp-server/tools/project/UploadProjectTools.d.ts +2 -2
  129. package/mcp-server/tools/project/UploadProjectTools.js +9 -8
  130. package/mcp-server/tools/project/ValidateProjectTool.d.ts +2 -2
  131. package/mcp-server/tools/project/ValidateProjectTool.js +5 -4
  132. package/mcp-server/tools/project/constants.d.ts +1 -0
  133. package/mcp-server/tools/project/constants.js +2 -1
  134. package/mcp-server/utils/command.d.ts +25 -2
  135. package/mcp-server/utils/command.js +67 -17
  136. package/mcp-server/utils/config.js +5 -0
  137. package/mcp-server/utils/logger.d.ts +3 -0
  138. package/mcp-server/utils/logger.js +30 -0
  139. package/mcp-server/utils/toolUsageTracking.js +18 -3
  140. package/package.json +11 -9
  141. package/types/Yargs.d.ts +3 -1
  142. package/commands/project/dev/deprecatedFlow.d.ts +0 -11
  143. package/commands/project/dev/deprecatedFlow.js +0 -180
  144. package/lib/projects/localDev/DevServerManager_DEPRECATED.d.ts +0 -43
  145. package/lib/projects/localDev/DevServerManager_DEPRECATED.js +0 -128
  146. package/lib/projects/localDev/LocalDevManager_DEPRECATED.d.ts +0 -64
  147. package/lib/projects/localDev/LocalDevManager_DEPRECATED.js +0 -394
@@ -1,14 +1,15 @@
1
1
  import { Tool } from '../../Tool.js';
2
2
  import { z } from 'zod';
3
3
  import { formatTextContents } from '../../utils/content.js';
4
- import { absoluteCurrentWorkingDirectory } from './constants.js';
4
+ import { absoluteCurrentWorkingDirectory, absoluteProjectPath, } from './constants.js';
5
5
  import { getIntermediateRepresentationSchema } from '@hubspot/project-parsing-lib/schema';
6
6
  import { mapToInternalType } from '@hubspot/project-parsing-lib/transform';
7
7
  import { isLegacyProject } from '@hubspot/project-parsing-lib/projects';
8
- import { getConfigDefaultAccountIfExists } from '@hubspot/local-dev-lib/config';
9
8
  import { setupHubSpotConfig } from '../../utils/config.js';
9
+ import { discoverAccountTargets } from '../../../lib/accountTargetDiscovery.js';
10
10
  const inputSchema = {
11
11
  absoluteCurrentWorkingDirectory,
12
+ absoluteProjectPath: absoluteProjectPath.optional(),
12
13
  platformVersion: z
13
14
  .string()
14
15
  .describe('The platform version for the project. Located in the hsproject.json file.'),
@@ -25,15 +26,16 @@ export class GetConfigValuesTool extends Tool {
25
26
  constructor(mcpServer, logger) {
26
27
  super(mcpServer, logger, toolName);
27
28
  }
28
- async handler({ platformVersion, featureType, absoluteCurrentWorkingDirectory, }) {
29
- setupHubSpotConfig(absoluteCurrentWorkingDirectory);
29
+ async handler({ platformVersion, featureType, absoluteCurrentWorkingDirectory, absoluteProjectPath, }) {
30
+ setupHubSpotConfig(absoluteProjectPath ?? absoluteCurrentWorkingDirectory);
30
31
  try {
31
32
  if (isLegacyProject(platformVersion)) {
32
33
  return formatTextContents(`Can only be used on projects with a minimum platformVersion of 2025.2`);
33
34
  }
34
- const accountId = getConfigDefaultAccountIfExists()?.accountId;
35
+ const { recommended } = await discoverAccountTargets();
36
+ const accountId = recommended?.accountId;
35
37
  if (!accountId) {
36
- const authErrorMessage = `No account ID found. Please run \`hs account auth\` to configure an account, or set a default account with \`hs account use <account>\``;
38
+ const authErrorMessage = `No account ID found. Call the auth-account tool to authenticate a HubSpot account.`;
37
39
  return formatTextContents(authErrorMessage);
38
40
  }
39
41
  const schema = await getIntermediateRepresentationSchema({
@@ -65,6 +67,6 @@ export class GetConfigValuesTool extends Tool {
65
67
  readOnlyHint: true,
66
68
  openWorldHint: false,
67
69
  },
68
- }, input => this.wrappedHandler(input));
70
+ }, (input, extra) => this.wrappedHandler(input, extra));
69
71
  }
70
72
  }
@@ -43,6 +43,6 @@ export class GuidedWalkthroughTool extends Tool {
43
43
  readOnlyHint: true,
44
44
  openWorldHint: false,
45
45
  },
46
- }, input => this.wrappedHandler(input));
46
+ }, (input, extra) => this.wrappedHandler(input, extra));
47
47
  }
48
48
  }
@@ -2,7 +2,7 @@ import z from 'zod';
2
2
  import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
3
3
  import { McpLogger } from '../../utils/logger.js';
4
4
  import { TextContentResponse } from '../../types.js';
5
- import { Tool } from '../../Tool.js';
5
+ import { Tool, ToolExtra } from '../../Tool.js';
6
6
  declare const inputSchemaZodObject: z.ZodObject<{
7
7
  absoluteProjectPath: z.ZodString;
8
8
  absoluteCurrentWorkingDirectory: z.ZodString;
@@ -12,7 +12,7 @@ declare const inputSchemaZodObject: z.ZodObject<{
12
12
  type InputSchemaType = z.infer<typeof inputSchemaZodObject>;
13
13
  export declare class UploadProjectTools extends Tool<InputSchemaType> {
14
14
  constructor(mcpServer: McpServer, logger: McpLogger);
15
- handler({ absoluteProjectPath, absoluteCurrentWorkingDirectory, profile, uploadMessage, }: InputSchemaType): Promise<TextContentResponse>;
15
+ handler({ absoluteProjectPath, absoluteCurrentWorkingDirectory, profile, uploadMessage, }: InputSchemaType, extra?: ToolExtra): Promise<TextContentResponse>;
16
16
  register(): RegisteredTool;
17
17
  }
18
18
  export {};
@@ -3,10 +3,9 @@ import z from 'zod';
3
3
  import { getAllHsProfiles } from '@hubspot/project-parsing-lib/profiles';
4
4
  import { getProjectConfig } from '../../../lib/projects/config.js';
5
5
  import { Tool } from '../../Tool.js';
6
- import { runCommandInDir } from '../../utils/command.js';
7
6
  import { absoluteCurrentWorkingDirectory, absoluteProjectPath, } from './constants.js';
8
7
  import { formatTextContent, formatTextContents } from '../../utils/content.js';
9
- import { addFlag } from '../../utils/command.js';
8
+ import { HubSpotCommand } from '../../utils/command.js';
10
9
  import { setupHubSpotConfig } from '../../utils/config.js';
11
10
  const inputSchema = {
12
11
  absoluteProjectPath,
@@ -27,15 +26,17 @@ export class UploadProjectTools extends Tool {
27
26
  constructor(mcpServer, logger) {
28
27
  super(mcpServer, logger, toolName);
29
28
  }
30
- async handler({ absoluteProjectPath, absoluteCurrentWorkingDirectory, profile, uploadMessage, }) {
29
+ async handler({ absoluteProjectPath, absoluteCurrentWorkingDirectory, profile, uploadMessage, }, extra) {
31
30
  setupHubSpotConfig(absoluteCurrentWorkingDirectory);
32
- let command = addFlag('hs project upload', 'force-create', true);
31
+ const command = new HubSpotCommand('project upload', [
32
+ { name: 'force', value: true },
33
+ ]);
33
34
  const content = [];
34
35
  if (uploadMessage) {
35
- command = addFlag(command, 'message', uploadMessage);
36
+ command.addFlag('message', uploadMessage);
36
37
  }
37
38
  if (profile) {
38
- command = addFlag(command, 'profile', profile);
39
+ command.addFlag('profile', profile);
39
40
  }
40
41
  else {
41
42
  let hasProfiles = false;
@@ -63,7 +64,7 @@ export class UploadProjectTools extends Tool {
63
64
  content,
64
65
  };
65
66
  }
66
- const { stdout, stderr } = await runCommandInDir(absoluteProjectPath, command);
67
+ const { stdout, stderr } = await this.runCommand(absoluteProjectPath, command, extra);
67
68
  const response = await formatTextContents(stdout, stderr);
68
69
  // Add reminder about cards needing to be added to views
69
70
  response.content.push(formatTextContent('\nIMPORTANT: If this project contains cards, remember that uploading does NOT make them live automatically. Cards must be manually added to a view in HubSpot to become visible to users.'));
@@ -80,6 +81,6 @@ export class UploadProjectTools extends Tool {
80
81
  idempotentHint: true,
81
82
  openWorldHint: true,
82
83
  },
83
- }, input => this.wrappedHandler(input));
84
+ }, (input, extra) => this.wrappedHandler(input, extra));
84
85
  }
85
86
  }
@@ -1,5 +1,5 @@
1
1
  import { TextContentResponse } from '../../types.js';
2
- import { Tool } from '../../Tool.js';
2
+ import { Tool, ToolExtra } from '../../Tool.js';
3
3
  import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
4
4
  import { McpLogger } from '../../utils/logger.js';
5
5
  import { z } from 'zod';
@@ -10,7 +10,7 @@ declare const inputSchemaZodObject: z.ZodObject<{
10
10
  export type CreateProjectInputSchema = z.infer<typeof inputSchemaZodObject>;
11
11
  export declare class ValidateProjectTool extends Tool<CreateProjectInputSchema> {
12
12
  constructor(mcpServer: McpServer, logger: McpLogger);
13
- handler({ absoluteProjectPath, absoluteCurrentWorkingDirectory, }: CreateProjectInputSchema): Promise<TextContentResponse>;
13
+ handler({ absoluteProjectPath, absoluteCurrentWorkingDirectory, }: CreateProjectInputSchema, extra?: ToolExtra): Promise<TextContentResponse>;
14
14
  register(): RegisteredTool;
15
15
  }
16
16
  export {};
@@ -1,10 +1,10 @@
1
1
  import { Tool } from '../../Tool.js';
2
2
  import { z } from 'zod';
3
3
  import { absoluteCurrentWorkingDirectory, absoluteProjectPath, } from './constants.js';
4
- import { runCommandInDir } from '../../utils/command.js';
5
4
  import { formatTextContents } from '../../utils/content.js';
6
5
  import { setupHubSpotConfig } from '../../utils/config.js';
7
6
  import { getErrorMessage } from '../../../lib/errorHandlers/index.js';
7
+ import { HubSpotCommand } from '../../utils/command.js';
8
8
  const inputSchema = {
9
9
  absoluteProjectPath,
10
10
  absoluteCurrentWorkingDirectory,
@@ -16,10 +16,11 @@ export class ValidateProjectTool extends Tool {
16
16
  constructor(mcpServer, logger) {
17
17
  super(mcpServer, logger, toolName);
18
18
  }
19
- async handler({ absoluteProjectPath, absoluteCurrentWorkingDirectory, }) {
19
+ async handler({ absoluteProjectPath, absoluteCurrentWorkingDirectory, }, extra) {
20
20
  setupHubSpotConfig(absoluteCurrentWorkingDirectory);
21
21
  try {
22
- const { stdout, stderr } = await runCommandInDir(absoluteProjectPath, 'hs project validate');
22
+ const command = new HubSpotCommand('project validate');
23
+ const { stdout, stderr } = await this.runCommand(absoluteProjectPath, command, extra);
23
24
  return formatTextContents(stdout, stderr);
24
25
  }
25
26
  catch (error) {
@@ -39,6 +40,6 @@ export class ValidateProjectTool extends Tool {
39
40
  readOnlyHint: true,
40
41
  openWorldHint: false,
41
42
  },
42
- }, input => this.wrappedHandler(input));
43
+ }, (input, extra) => this.wrappedHandler(input, extra));
43
44
  }
44
45
  }
@@ -6,6 +6,7 @@ export declare const features: z.ZodOptional<z.ZodArray<z.ZodEnum<{
6
6
  settings: "settings";
7
7
  "app-event": "app-event";
8
8
  "workflow-action-tool": "workflow-action-tool";
9
+ "crm-bulk-action": "crm-bulk-action";
9
10
  page: "page";
10
11
  webhooks: "webhooks";
11
12
  "app-function": "app-function";
@@ -18,8 +18,9 @@ export const features = z
18
18
  'app-event',
19
19
  'scim',
20
20
  'page',
21
+ 'crm-bulk-action',
21
22
  ]))
22
- .describe('The features to include in the project, multiple options can be selected. "app-function" is also known as a private serverless function. "app-function-endpoint" is a serverless functions that is publicly accessible via endpoint. "workflow-action" is also known as a custom workflow action. "workflow-action-tool" is also known as agent tools.')
23
+ .describe('The features to include in the project, multiple options can be selected. "app-function" is also known as a private serverless function. "app-function-endpoint" is a serverless functions that is publicly accessible via endpoint. "workflow-action" is also known as a custom workflow action. "workflow-action-tool" is also known as agent tools. "crm-bulk-action" is an app actions extension that lets users act on multiple CRM records at once.')
23
24
  .optional();
24
25
  export const docsSearchQuery = z
25
26
  .string()
@@ -1,8 +1,31 @@
1
1
  import { exec } from 'node:child_process';
2
+ export interface Command {
3
+ executable: string;
4
+ args: string[];
5
+ }
6
+ export type FlagValue = string | number | boolean | string[];
7
+ export interface Flag {
8
+ name: string;
9
+ value: FlagValue;
10
+ }
11
+ export declare class HubSpotCommand implements Command {
12
+ readonly executable = "hs";
13
+ private readonly subcommandArgs;
14
+ private readonly positionalArgs;
15
+ private readonly flags;
16
+ constructor(subcommand: string, flags?: Flag[]);
17
+ addArg(value: string): this;
18
+ addFlag(name: string, value: FlagValue): this;
19
+ get args(): string[];
20
+ }
21
+ export declare class NpxHubSpotCommand implements Command {
22
+ readonly executable = "npx";
23
+ readonly args: string[];
24
+ constructor(command: Command, cliPackage?: string);
25
+ }
2
26
  export declare const execAsync: typeof exec.__promisify__;
3
- export declare function addFlag(command: string, flagName: string, value: string | number | boolean | string[]): string;
4
27
  export interface CommandResults {
5
28
  stderr: string;
6
29
  stdout: string;
7
30
  }
8
- export declare function runCommandInDir(directory: string, command: string): Promise<CommandResults>;
31
+ export declare function runCommandInDir(directory: string, command: Command, onData?: (chunk: string, source: 'stdout' | 'stderr') => void): Promise<CommandResults>;
@@ -1,35 +1,85 @@
1
1
  import path from 'path';
2
2
  import fs from 'fs';
3
3
  import util from 'util';
4
- import { exec } from 'node:child_process';
5
- export const execAsync = util.promisify(exec);
6
- export function addFlag(command, flagName, value) {
7
- if (Array.isArray(value)) {
8
- return `${command} --${flagName} ${value.map(item => `"${item}"`).join(' ')}`;
4
+ import { exec, spawn } from 'node:child_process';
5
+ export class HubSpotCommand {
6
+ executable = 'hs';
7
+ subcommandArgs;
8
+ positionalArgs;
9
+ flags;
10
+ constructor(subcommand, flags = []) {
11
+ this.subcommandArgs = subcommand.trim().split(/\s+/);
12
+ this.positionalArgs = [];
13
+ this.flags = [...flags];
14
+ }
15
+ addArg(value) {
16
+ this.positionalArgs.push(value);
17
+ return this;
18
+ }
19
+ addFlag(name, value) {
20
+ this.flags.push({ name, value });
21
+ return this;
22
+ }
23
+ get args() {
24
+ return [
25
+ ...this.subcommandArgs,
26
+ ...this.positionalArgs,
27
+ ...this.flags.flatMap(({ name, value }) => Array.isArray(value)
28
+ ? [`--${name}`, ...value.map(String)]
29
+ : [`--${name}`, String(value)]),
30
+ ];
9
31
  }
10
- return `${command} --${flagName} "${value}"`;
11
32
  }
12
- export async function runCommandInDir(directory, command) {
33
+ export class NpxHubSpotCommand {
34
+ executable = 'npx';
35
+ args;
36
+ constructor(command, cliPackage = '@hubspot/cli') {
37
+ this.args = ['-y', '-p', cliPackage, 'hs', ...command.args];
38
+ }
39
+ }
40
+ export const execAsync = util.promisify(exec);
41
+ export async function runCommandInDir(directory, command, onData) {
13
42
  if (!fs.existsSync(directory)) {
14
43
  fs.mkdirSync(directory);
15
44
  }
16
45
  let finalCommand = command;
17
- if (command.startsWith('hs ')) {
18
- // Check if running in standalone mode
46
+ if (command instanceof HubSpotCommand) {
47
+ command.addFlag('disable-usage-tracking', 'true');
19
48
  if (process.env.HUBSPOT_MCP_STANDALONE === 'true') {
20
49
  const cliPackage = process.env.HUBSPOT_CLI_VERSION
21
50
  ? `@hubspot/cli@${process.env.HUBSPOT_CLI_VERSION}`
22
51
  : '@hubspot/cli';
23
- finalCommand = command.replace(/^hs /, `npx -y -p ${cliPackage} hs `);
52
+ finalCommand = new NpxHubSpotCommand(command, cliPackage);
24
53
  }
25
- finalCommand = addFlag(finalCommand, 'disable-usage-tracking', true);
26
54
  }
27
55
  const resolvedDir = path.resolve(directory);
28
- return execAsync(finalCommand, {
29
- cwd: resolvedDir,
30
- env: {
31
- ...process.env,
32
- INIT_CWD: resolvedDir,
33
- },
56
+ return new Promise((resolve, reject) => {
57
+ const stdoutChunks = [];
58
+ const stderrChunks = [];
59
+ const child = spawn(finalCommand.executable, finalCommand.args, {
60
+ cwd: resolvedDir,
61
+ env: { ...process.env, INIT_CWD: resolvedDir },
62
+ });
63
+ child.stdout.on('data', (chunk) => {
64
+ const text = chunk.toString();
65
+ stdoutChunks.push(text);
66
+ onData?.(text, 'stdout');
67
+ });
68
+ child.stderr.on('data', (chunk) => {
69
+ const text = chunk.toString();
70
+ stderrChunks.push(text);
71
+ onData?.(text, 'stderr');
72
+ });
73
+ child.on('error', reject);
74
+ child.on('close', code => {
75
+ const stdout = stdoutChunks.join('');
76
+ const stderr = stderrChunks.join('');
77
+ if (code !== 0) {
78
+ reject(Object.assign(new Error(`Command failed: ${finalCommand.executable} ${finalCommand.args.join(' ')}\n${stderr}`), { code, stdout, stderr }));
79
+ }
80
+ else {
81
+ resolve({ stdout, stderr });
82
+ }
83
+ });
34
84
  });
35
85
  }
@@ -8,4 +8,9 @@ export function setupHubSpotConfig(absoluteCurrentWorkingDirectory) {
8
8
  if (configPath) {
9
9
  process.env.HUBSPOT_CONFIG_PATH = configPath;
10
10
  }
11
+ else {
12
+ // Clear any value set by a previous tool call so this call doesn't
13
+ // inherit a stale config path from a different project.
14
+ delete process.env.HUBSPOT_CONFIG_PATH;
15
+ }
11
16
  }
@@ -1,10 +1,13 @@
1
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  export declare class McpLogger {
3
3
  private mcpServer;
4
+ private invocationStorage;
4
5
  constructor(mcpServer: McpServer);
5
6
  private log;
6
7
  debug(logger: string, data: unknown): void;
7
8
  info(logger: string, data: unknown): void;
8
9
  warn(logger: string, data: unknown): void;
9
10
  error(logger: string, data: unknown): void;
11
+ runWithBuffer<T>(fn: () => Promise<T>): Promise<T>;
12
+ flushLogsToFile(filenamePrefix: string): string | null;
10
13
  }
@@ -1,10 +1,22 @@
1
+ import os from 'os';
2
+ import path from 'path';
3
+ import { AsyncLocalStorage } from 'node:async_hooks';
4
+ import { LogBuffer } from '@hubspot/local-dev-lib/LogBuffer';
5
+ import { MAX_LOG_FILES } from '../../lib/constants.js';
6
+ const MCP_LOG_DIR = path.join(os.homedir(), '.hscli', 'logs', 'tools');
1
7
  export class McpLogger {
2
8
  mcpServer;
9
+ invocationStorage = new AsyncLocalStorage();
3
10
  constructor(mcpServer) {
4
11
  this.mcpServer = mcpServer;
5
12
  }
6
13
  log(level, logger, data) {
7
14
  try {
15
+ const buffer = this.invocationStorage.getStore();
16
+ if (buffer) {
17
+ const serialized = typeof data === 'string' ? data : JSON.stringify(data);
18
+ buffer.record(level, [logger, serialized]);
19
+ }
8
20
  this.mcpServer.sendLoggingMessage({ level, logger, data });
9
21
  }
10
22
  catch (error) {
@@ -26,4 +38,22 @@ export class McpLogger {
26
38
  error(logger, data) {
27
39
  this.log('error', logger, data);
28
40
  }
41
+ // Runs fn in a fresh per-invocation log buffer context. All log calls made
42
+ // within fn (including nested async callbacks like progress chunks) record
43
+ // into this buffer rather than a shared one, so concurrent tool calls never
44
+ // intermix their logs.
45
+ runWithBuffer(fn) {
46
+ return this.invocationStorage.run(new LogBuffer(), fn);
47
+ }
48
+ flushLogsToFile(filenamePrefix) {
49
+ const buffer = this.invocationStorage.getStore();
50
+ if (!buffer) {
51
+ return null;
52
+ }
53
+ return buffer.writeToFile({
54
+ dir: MCP_LOG_DIR,
55
+ filenamePrefix,
56
+ maxFiles: MAX_LOG_FILES,
57
+ });
58
+ }
29
59
  }
@@ -1,8 +1,16 @@
1
1
  import { EventClass, getExecutionEnvironmentMeta, } from '../../lib/usageTracking.js';
2
- import { getConfig, getConfigDefaultAccountIfExists, } from '@hubspot/local-dev-lib/config';
2
+ import { getConfig } from '@hubspot/local-dev-lib/config';
3
+ import { discoverAccountTargets } from '../../lib/accountTargetDiscovery.js';
3
4
  import { sendUsageEvent } from '../../lib/api/usageTracking.js';
4
5
  export async function trackToolUsage(toolName, meta) {
5
- const config = getConfig();
6
+ let config;
7
+ try {
8
+ config = getConfig();
9
+ }
10
+ catch {
11
+ // No config file exists yet; skip tracking
12
+ return;
13
+ }
6
14
  if (config?.allowUsageTracking === false) {
7
15
  return;
8
16
  }
@@ -13,7 +21,14 @@ export async function trackToolUsage(toolName, meta) {
13
21
  ...getExecutionEnvironmentMeta(),
14
22
  ...meta,
15
23
  };
16
- const accountId = getConfigDefaultAccountIfExists()?.accountId || undefined;
24
+ let accountId;
25
+ try {
26
+ const { recommended } = await discoverAccountTargets();
27
+ accountId = recommended?.accountId;
28
+ }
29
+ catch {
30
+ // Account discovery failed; continue without account ID
31
+ }
17
32
  try {
18
33
  await sendUsageEvent({
19
34
  eventName: 'cli-interaction',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hubspot/cli",
3
- "version": "8.2.0-experimental.2",
3
+ "version": "8.2.1-experimental.0",
4
4
  "description": "The official CLI for developing on HubSpot",
5
5
  "license": "Apache-2.0",
6
6
  "repository": "https://github.com/HubSpot/hubspot-cli",
@@ -10,10 +10,10 @@
10
10
  "!**/__tests__/**"
11
11
  ],
12
12
  "dependencies": {
13
- "@hubspot/local-dev-lib": "5.9.0",
14
- "@hubspot/project-parsing-lib": "0.18.0",
13
+ "@hubspot/local-dev-lib": "5.10.2",
14
+ "@hubspot/project-parsing-lib": "0.4.0-experimental.0",
15
15
  "@hubspot/serverless-dev-runtime": "7.0.7",
16
- "@hubspot/ui-extensions-dev-server": "2.0.10",
16
+ "@hubspot/ui-extensions-dev-server": "2.1.2",
17
17
  "@inquirer/prompts": "7.1.0",
18
18
  "@modelcontextprotocol/sdk": "1.29.0",
19
19
  "archiver": "7.0.1",
@@ -33,16 +33,18 @@
33
33
  "open": "7.4.2",
34
34
  "p-queue": "8.1.0",
35
35
  "react": "19.2.3",
36
+ "semver": "7.6.3",
36
37
  "strip-ansi": "7.1.0",
37
38
  "table": "6.9.0",
38
39
  "tmp": "0.2.4",
39
40
  "update-notifier": "7.3.1",
40
41
  "ws": "8.20.0",
41
42
  "yargs": "17.7.2",
42
- "yargs-parser": "21.1.1"
43
+ "yargs-parser": "21.1.1",
44
+ "zod": "^4.4.3"
43
45
  },
44
46
  "devDependencies": {
45
- "@hubspot/npm-scripts": "0.2.2",
47
+ "@hubspot/npm-scripts": "^0.3.3",
46
48
  "@types/archiver": "^6.0.3",
47
49
  "@types/cli-progress": "^3.11.6",
48
50
  "@types/express": "^5.0.0",
@@ -59,7 +61,7 @@
59
61
  "@typescript-eslint/eslint-plugin": "^8.30.1",
60
62
  "@typescript-eslint/parser": "^8.11.0",
61
63
  "@vitest/coverage-v8": "^2.1.9",
62
- "axios": "1.15.2",
64
+ "axios": "1.18.1",
63
65
  "eslint": "^8.56.0",
64
66
  "eslint-plugin-import": "^2.31.0",
65
67
  "husky": "^4.3.8",
@@ -67,7 +69,6 @@
67
69
  "madge": "^8.0.0",
68
70
  "mock-stdin": "^1.0.0",
69
71
  "prettier": "^3.4.2",
70
- "semver": "^7.6.3",
71
72
  "ts-node": "^10.9.2",
72
73
  "tsx": "^4.20.3",
73
74
  "typescript": "^5.6.2",
@@ -123,6 +124,7 @@
123
124
  "registry": "https://registry.npmjs.org/"
124
125
  },
125
126
  "resolutions": {
126
- "eslint-visitor-keys": "4.2.0"
127
+ "eslint-visitor-keys": "4.2.0",
128
+ "@eslint-community/eslint-utils": "4.9.0"
127
129
  }
128
130
  }
package/types/Yargs.d.ts CHANGED
@@ -38,9 +38,11 @@ export type OverwriteArgs = Options & {
38
38
  export type StringArgType = Options & {
39
39
  type: 'string';
40
40
  };
41
- export type JSONOutputArgs = Options & {
41
+ export type JSONOutputArgs<J extends Record<string, unknown> = Record<string, unknown>> = Options & {
42
42
  json?: boolean;
43
+ jsonSchema?: boolean;
43
44
  formatOutputAsJson?: boolean;
45
+ addJsonOutput: (data: Partial<J>) => void;
44
46
  };
45
47
  export type ProjectDevArgs = CommonArgs & ConfigArgs & EnvironmentArgs & {
46
48
  profile?: string;
@@ -1,11 +0,0 @@
1
- import { ArgumentsCamelCase } from 'yargs';
2
- import { ProjectConfig } from '../../../types/Projects.js';
3
- import { ProjectDevArgs } from '../../../types/Yargs.js';
4
- type DeprecatedProjectDevFlowArgs = {
5
- args: ArgumentsCamelCase<ProjectDevArgs>;
6
- accountId: number;
7
- projectConfig: ProjectConfig;
8
- projectDir: string;
9
- };
10
- export declare function deprecatedProjectDevFlow({ args, accountId, projectConfig, projectDir, }: DeprecatedProjectDevFlowArgs): Promise<void>;
11
- export {};