@hubspot/cli 8.0.10-experimental.1 → 8.0.10-experimental.2

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 (71) hide show
  1. package/commands/cms/__tests__/watch.test.js +0 -8
  2. package/commands/cms/function/logs.js +1 -0
  3. package/commands/cms/theme/preview.js +64 -11
  4. package/commands/cms/watch.d.ts +0 -1
  5. package/commands/cms/watch.js +2 -8
  6. package/commands/feedback.js +1 -1
  7. package/commands/mcp/__tests__/start.test.js +8 -1
  8. package/commands/mcp/setup.js +1 -9
  9. package/commands/mcp/start.js +0 -1
  10. package/commands/project/__tests__/create.test.js +1 -1
  11. package/commands/project/create.js +2 -2
  12. package/commands/project/watch.js +15 -2
  13. package/lang/en.d.ts +2 -6
  14. package/lang/en.js +2 -6
  15. package/lib/__tests__/serverlessLogs.test.js +71 -65
  16. package/lib/constants.d.ts +1 -1
  17. package/lib/constants.js +1 -1
  18. package/lib/generateSelectors.js +1 -2
  19. package/lib/mcp/__tests__/setup.test.js +357 -28
  20. package/lib/mcp/setup.d.ts +1 -0
  21. package/lib/mcp/setup.js +77 -30
  22. package/lib/projects/create/__tests__/legacy.test.js +6 -24
  23. package/lib/projects/create/index.js +1 -4
  24. package/lib/projects/create/legacy.js +3 -8
  25. package/lib/projects/create/v2.js +1 -9
  26. package/lib/projects/ensureProjectExists.js +1 -2
  27. package/lib/projects/pollProjectBuildAndDeploy.js +90 -85
  28. package/lib/projects/upload.d.ts +1 -0
  29. package/lib/projects/upload.js +37 -46
  30. package/lib/projects/watch.d.ts +2 -1
  31. package/lib/projects/watch.js +32 -24
  32. package/lib/serverlessLogs.js +50 -44
  33. package/mcp-server/tools/cms/HsCreateFunctionTool.js +1 -1
  34. package/mcp-server/tools/cms/HsCreateModuleTool.js +1 -1
  35. package/mcp-server/tools/cms/HsCreateTemplateTool.js +1 -1
  36. package/mcp-server/tools/cms/HsFunctionLogsTool.js +1 -1
  37. package/mcp-server/tools/cms/HsListFunctionsTool.js +1 -1
  38. package/mcp-server/tools/cms/HsListTool.js +1 -1
  39. package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.js +1 -2
  40. package/mcp-server/tools/cms/__tests__/HsCreateModuleTool.test.js +1 -2
  41. package/mcp-server/tools/cms/__tests__/HsCreateTemplateTool.test.js +1 -2
  42. package/mcp-server/tools/cms/__tests__/HsFunctionLogsTool.test.js +1 -2
  43. package/mcp-server/tools/cms/__tests__/HsListFunctionsTool.test.js +1 -2
  44. package/mcp-server/tools/cms/__tests__/HsListTool.test.js +1 -2
  45. package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +1 -1
  46. package/mcp-server/tools/project/AddFeatureToProjectTool.js +1 -1
  47. package/mcp-server/tools/project/CreateProjectTool.d.ts +1 -1
  48. package/mcp-server/tools/project/CreateProjectTool.js +1 -1
  49. package/mcp-server/tools/project/CreateTestAccountTool.js +1 -1
  50. package/mcp-server/tools/project/DeployProjectTool.js +1 -1
  51. package/mcp-server/tools/project/UploadProjectTools.js +1 -1
  52. package/mcp-server/tools/project/ValidateProjectTool.js +1 -1
  53. package/mcp-server/tools/project/__tests__/AddFeatureToProjectTool.test.js +1 -2
  54. package/mcp-server/tools/project/__tests__/CreateProjectTool.test.js +1 -2
  55. package/mcp-server/tools/project/__tests__/CreateTestAccountTool.test.js +1 -2
  56. package/mcp-server/tools/project/__tests__/DeployProjectTool.test.js +1 -2
  57. package/mcp-server/tools/project/__tests__/UploadProjectTools.test.js +10 -2
  58. package/mcp-server/tools/project/__tests__/ValidateProjectTool.test.js +2 -2
  59. package/mcp-server/tools/project/constants.d.ts +1 -1
  60. package/mcp-server/utils/__tests__/command.test.js +233 -3
  61. package/mcp-server/utils/__tests__/feedbackTracking.test.js +9 -64
  62. package/mcp-server/utils/command.d.ts +5 -0
  63. package/mcp-server/utils/command.js +24 -0
  64. package/mcp-server/utils/feedbackTracking.js +2 -17
  65. package/package.json +4 -4
  66. package/lib/cms/devServerProcess.d.ts +0 -13
  67. package/lib/cms/devServerProcess.js +0 -200
  68. package/mcp-server/utils/__tests__/project.test.d.ts +0 -1
  69. package/mcp-server/utils/__tests__/project.test.js +0 -140
  70. package/mcp-server/utils/project.d.ts +0 -5
  71. package/mcp-server/utils/project.js +0 -18
@@ -2,7 +2,7 @@ import { Tool } from '../../types.js';
2
2
  import { z } from 'zod';
3
3
  import { addFlag } from '../../utils/command.js';
4
4
  import { absoluteCurrentWorkingDirectory } from '../project/constants.js';
5
- import { runCommandInDir } from '../../utils/project.js';
5
+ import { runCommandInDir } from '../../utils/command.js';
6
6
  import { formatTextContents } from '../../utils/content.js';
7
7
  import { trackToolUsage } from '../../utils/toolUsageTracking.js';
8
8
  import { setupHubSpotConfig } from '../../utils/config.js';
@@ -2,7 +2,7 @@ import { Tool } from '../../types.js';
2
2
  import { z } from 'zod';
3
3
  import { addFlag } from '../../utils/command.js';
4
4
  import { absoluteCurrentWorkingDirectory } from '../project/constants.js';
5
- import { runCommandInDir } from '../../utils/project.js';
5
+ import { runCommandInDir } from '../../utils/command.js';
6
6
  import { formatTextContents } from '../../utils/content.js';
7
7
  import { trackToolUsage } from '../../utils/toolUsageTracking.js';
8
8
  import { setupHubSpotConfig } from '../../utils/config.js';
@@ -2,7 +2,7 @@ import { Tool } from '../../types.js';
2
2
  import { z } from 'zod';
3
3
  import { addFlag } from '../../utils/command.js';
4
4
  import { absoluteCurrentWorkingDirectory } from '../project/constants.js';
5
- import { runCommandInDir } from '../../utils/project.js';
5
+ import { runCommandInDir } from '../../utils/command.js';
6
6
  import { formatTextContents } from '../../utils/content.js';
7
7
  import { trackToolUsage } from '../../utils/toolUsageTracking.js';
8
8
  import { setupHubSpotConfig } from '../../utils/config.js';
@@ -1,11 +1,10 @@
1
1
  import { describe, it, expect, vi, beforeEach } from 'vitest';
2
2
  import { HsCreateFunctionTool } from '../HsCreateFunctionTool.js';
3
- import { runCommandInDir } from '../../../utils/project.js';
3
+ import { runCommandInDir } from '../../../utils/command.js';
4
4
  import { addFlag } from '../../../utils/command.js';
5
5
  import { mcpFeedbackRequest } from '../../../utils/feedbackTracking.js';
6
6
  import { trackToolUsage } from '../../../utils/toolUsageTracking.js';
7
7
  vi.mock('@modelcontextprotocol/sdk/server/mcp.js');
8
- vi.mock('../../../utils/project');
9
8
  vi.mock('../../../utils/command');
10
9
  vi.mock('../../../utils/toolUsageTracking');
11
10
  vi.mock('../../../utils/feedbackTracking');
@@ -1,11 +1,10 @@
1
1
  import { describe, it, expect, vi, beforeEach } from 'vitest';
2
2
  import { HsCreateModuleTool } from '../HsCreateModuleTool.js';
3
- import { runCommandInDir } from '../../../utils/project.js';
3
+ import { runCommandInDir } from '../../../utils/command.js';
4
4
  import { addFlag } from '../../../utils/command.js';
5
5
  import { mcpFeedbackRequest } from '../../../utils/feedbackTracking.js';
6
6
  import { trackToolUsage } from '../../../utils/toolUsageTracking.js';
7
7
  vi.mock('@modelcontextprotocol/sdk/server/mcp.js');
8
- vi.mock('../../../utils/project');
9
8
  vi.mock('../../../utils/command');
10
9
  vi.mock('../../../utils/toolUsageTracking');
11
10
  vi.mock('../../../utils/feedbackTracking');
@@ -1,11 +1,10 @@
1
1
  import { describe, it, expect, vi, beforeEach } from 'vitest';
2
2
  import { HsCreateTemplateTool } from '../HsCreateTemplateTool.js';
3
- import { runCommandInDir } from '../../../utils/project.js';
3
+ import { runCommandInDir } from '../../../utils/command.js';
4
4
  import { addFlag } from '../../../utils/command.js';
5
5
  import { mcpFeedbackRequest } from '../../../utils/feedbackTracking.js';
6
6
  import { trackToolUsage } from '../../../utils/toolUsageTracking.js';
7
7
  vi.mock('@modelcontextprotocol/sdk/server/mcp.js');
8
- vi.mock('../../../utils/project');
9
8
  vi.mock('../../../utils/command');
10
9
  vi.mock('../../../utils/toolUsageTracking');
11
10
  vi.mock('../../../utils/feedbackTracking');
@@ -1,11 +1,10 @@
1
1
  import { describe, it, expect, vi, beforeEach } from 'vitest';
2
2
  import { HsFunctionLogsTool } from '../HsFunctionLogsTool.js';
3
- import { runCommandInDir } from '../../../utils/project.js';
3
+ import { runCommandInDir } from '../../../utils/command.js';
4
4
  import { addFlag } from '../../../utils/command.js';
5
5
  import { mcpFeedbackRequest } from '../../../utils/feedbackTracking.js';
6
6
  import { trackToolUsage } from '../../../utils/toolUsageTracking.js';
7
7
  vi.mock('@modelcontextprotocol/sdk/server/mcp.js');
8
- vi.mock('../../../utils/project');
9
8
  vi.mock('../../../utils/command');
10
9
  vi.mock('../../../utils/toolUsageTracking');
11
10
  vi.mock('../../../utils/feedbackTracking');
@@ -1,11 +1,10 @@
1
1
  import { describe, it, expect, vi, beforeEach } from 'vitest';
2
2
  import { HsListFunctionsTool } from '../HsListFunctionsTool.js';
3
- import { runCommandInDir } from '../../../utils/project.js';
3
+ import { runCommandInDir } from '../../../utils/command.js';
4
4
  import { addFlag } from '../../../utils/command.js';
5
5
  import { mcpFeedbackRequest } from '../../../utils/feedbackTracking.js';
6
6
  import { trackToolUsage } from '../../../utils/toolUsageTracking.js';
7
7
  vi.mock('@modelcontextprotocol/sdk/server/mcp.js');
8
- vi.mock('../../../utils/project');
9
8
  vi.mock('../../../utils/command');
10
9
  vi.mock('../../../utils/toolUsageTracking');
11
10
  vi.mock('../../../utils/feedbackTracking');
@@ -1,11 +1,10 @@
1
1
  import { describe, it, expect, vi, beforeEach } from 'vitest';
2
2
  import { HsListTool } from '../HsListTool.js';
3
- import { runCommandInDir } from '../../../utils/project.js';
3
+ import { runCommandInDir } from '../../../utils/command.js';
4
4
  import { addFlag } from '../../../utils/command.js';
5
5
  import { mcpFeedbackRequest } from '../../../utils/feedbackTracking.js';
6
6
  import { trackToolUsage } from '../../../utils/toolUsageTracking.js';
7
7
  vi.mock('@modelcontextprotocol/sdk/server/mcp.js');
8
- vi.mock('../../../utils/project');
9
8
  vi.mock('../../../utils/command');
10
9
  vi.mock('../../../utils/toolUsageTracking');
11
10
  vi.mock('../../../utils/feedbackTracking');
@@ -17,8 +17,8 @@ declare const inputSchemaZodObject: z.ZodObject<{
17
17
  card: "card";
18
18
  settings: "settings";
19
19
  "app-event": "app-event";
20
- page: "page";
21
20
  "workflow-action-tool": "workflow-action-tool";
21
+ page: "page";
22
22
  webhooks: "webhooks";
23
23
  "workflow-action": "workflow-action";
24
24
  "app-function": "app-function";
@@ -3,7 +3,7 @@ import { z } from 'zod';
3
3
  import { APP_AUTH_TYPES, APP_DISTRIBUTION_TYPES, } from '../../../lib/constants.js';
4
4
  import { addFlag } from '../../utils/command.js';
5
5
  import { absoluteCurrentWorkingDirectory, absoluteProjectPath, features, } from './constants.js';
6
- import { runCommandInDir } from '../../utils/project.js';
6
+ import { runCommandInDir } from '../../utils/command.js';
7
7
  import { formatTextContents, formatTextContent } from '../../utils/content.js';
8
8
  import { trackToolUsage } from '../../utils/toolUsageTracking.js';
9
9
  import { setupHubSpotConfig } from '../../utils/config.js';
@@ -21,8 +21,8 @@ declare const inputSchemaZodObject: z.ZodObject<{
21
21
  card: "card";
22
22
  settings: "settings";
23
23
  "app-event": "app-event";
24
- page: "page";
25
24
  "workflow-action-tool": "workflow-action-tool";
25
+ page: "page";
26
26
  webhooks: "webhooks";
27
27
  "workflow-action": "workflow-action";
28
28
  "app-function": "app-function";
@@ -3,7 +3,7 @@ import { z } from 'zod';
3
3
  import { APP_AUTH_TYPES, APP_DISTRIBUTION_TYPES, EMPTY_PROJECT, PROJECT_WITH_APP, } from '../../../lib/constants.js';
4
4
  import { addFlag } from '../../utils/command.js';
5
5
  import { absoluteCurrentWorkingDirectory, features } from './constants.js';
6
- import { runCommandInDir } from '../../utils/project.js';
6
+ import { runCommandInDir } from '../../utils/command.js';
7
7
  import { formatTextContents, formatTextContent } from '../../utils/content.js';
8
8
  import { trackToolUsage } from '../../utils/toolUsageTracking.js';
9
9
  import { setupHubSpotConfig } from '../../utils/config.js';
@@ -3,7 +3,7 @@ import fs from 'fs';
3
3
  import { trackToolUsage } from '../../utils/toolUsageTracking.js';
4
4
  import { formatTextContents, formatTextContent } from '../../utils/content.js';
5
5
  import { addFlag } from '../../utils/command.js';
6
- import { runCommandInDir } from '../../utils/project.js';
6
+ import { runCommandInDir } from '../../utils/command.js';
7
7
  import { ACCOUNT_LEVELS, ACCOUNT_LEVEL_CHOICES, } from '../../../lib/constants.js';
8
8
  import { Tool } from '../../types.js';
9
9
  import { absoluteCurrentWorkingDirectory } from './constants.js';
@@ -2,7 +2,7 @@ import { Tool } from '../../types.js';
2
2
  import { z } from 'zod';
3
3
  import { addFlag } from '../../utils/command.js';
4
4
  import { absoluteCurrentWorkingDirectory, absoluteProjectPath, } from './constants.js';
5
- import { runCommandInDir } from '../../utils/project.js';
5
+ import { runCommandInDir } from '../../utils/command.js';
6
6
  import { formatTextContents, formatTextContent } from '../../utils/content.js';
7
7
  import { trackToolUsage } from '../../utils/toolUsageTracking.js';
8
8
  import { setupHubSpotConfig } from '../../utils/config.js';
@@ -3,7 +3,7 @@ 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 '../../types.js';
6
- import { runCommandInDir } from '../../utils/project.js';
6
+ import { runCommandInDir } from '../../utils/command.js';
7
7
  import { absoluteCurrentWorkingDirectory, absoluteProjectPath, } from './constants.js';
8
8
  import { formatTextContent, formatTextContents } from '../../utils/content.js';
9
9
  import { trackToolUsage } from '../../utils/toolUsageTracking.js';
@@ -1,7 +1,7 @@
1
1
  import { Tool } from '../../types.js';
2
2
  import { z } from 'zod';
3
3
  import { absoluteCurrentWorkingDirectory, absoluteProjectPath, } from './constants.js';
4
- import { runCommandInDir } from '../../utils/project.js';
4
+ import { runCommandInDir } from '../../utils/command.js';
5
5
  import { formatTextContents } from '../../utils/content.js';
6
6
  import { trackToolUsage } from '../../utils/toolUsageTracking.js';
7
7
  import { setupHubSpotConfig } from '../../utils/config.js';
@@ -1,11 +1,10 @@
1
1
  import { AddFeatureToProjectTool, } from '../AddFeatureToProjectTool.js';
2
- import { runCommandInDir } from '../../../utils/project.js';
2
+ import { runCommandInDir } from '../../../utils/command.js';
3
3
  import { addFlag } from '../../../utils/command.js';
4
4
  import { APP_AUTH_TYPES, APP_DISTRIBUTION_TYPES, } from '../../../../lib/constants.js';
5
5
  import { mcpFeedbackRequest } from '../../../utils/feedbackTracking.js';
6
6
  import { trackToolUsage } from '../../../utils/toolUsageTracking.js';
7
7
  vi.mock('@modelcontextprotocol/sdk/server/mcp.js');
8
- vi.mock('../../../utils/project');
9
8
  vi.mock('../../../utils/command');
10
9
  vi.mock('../../../../lib/constants');
11
10
  vi.mock('../../../utils/toolUsageTracking');
@@ -1,11 +1,10 @@
1
1
  import { CreateProjectTool, } from '../CreateProjectTool.js';
2
- import { runCommandInDir } from '../../../utils/project.js';
2
+ import { runCommandInDir } from '../../../utils/command.js';
3
3
  import { addFlag } from '../../../utils/command.js';
4
4
  import { APP_DISTRIBUTION_TYPES, EMPTY_PROJECT, PROJECT_WITH_APP, } from '../../../../lib/constants.js';
5
5
  import { mcpFeedbackRequest } from '../../../utils/feedbackTracking.js';
6
6
  import { trackToolUsage } from '../../../utils/toolUsageTracking.js';
7
7
  vi.mock('@modelcontextprotocol/sdk/server/mcp.js');
8
- vi.mock('../../../utils/project');
9
8
  vi.mock('../../../utils/command');
10
9
  vi.mock('../../../../lib/constants');
11
10
  vi.mock('../../../../lib/projects/create/v2');
@@ -1,12 +1,11 @@
1
1
  import { CreateTestAccountTool, } from '../CreateTestAccountTool.js';
2
- import { runCommandInDir } from '../../../utils/project.js';
2
+ import { runCommandInDir } from '../../../utils/command.js';
3
3
  import { addFlag } from '../../../utils/command.js';
4
4
  import { mcpFeedbackRequest } from '../../../utils/feedbackTracking.js';
5
5
  import { trackToolUsage } from '../../../utils/toolUsageTracking.js';
6
6
  import fs from 'fs';
7
7
  import * as config from '@hubspot/local-dev-lib/config';
8
8
  vi.mock('@modelcontextprotocol/sdk/server/mcp.js');
9
- vi.mock('../../../utils/project');
10
9
  vi.mock('../../../utils/command');
11
10
  vi.mock('../../../utils/toolUsageTracking');
12
11
  vi.mock('../../../utils/feedbackTracking');
@@ -1,10 +1,9 @@
1
1
  import { DeployProjectTool } from '../DeployProjectTool.js';
2
- import { runCommandInDir } from '../../../utils/project.js';
2
+ import { runCommandInDir } from '../../../utils/command.js';
3
3
  import { addFlag } from '../../../utils/command.js';
4
4
  import { mcpFeedbackRequest } from '../../../utils/feedbackTracking.js';
5
5
  import { trackToolUsage } from '../../../utils/toolUsageTracking.js';
6
6
  vi.mock('@modelcontextprotocol/sdk/server/mcp.js');
7
- vi.mock('../../../utils/project');
8
7
  vi.mock('../../../utils/command');
9
8
  vi.mock('../../../utils/toolUsageTracking');
10
9
  vi.mock('../../../utils/feedbackTracking');
@@ -1,13 +1,21 @@
1
1
  import { UploadProjectTools } from '../UploadProjectTools.js';
2
2
  import { getAllHsProfiles } from '@hubspot/project-parsing-lib/profiles';
3
3
  import { getProjectConfig } from '../../../../lib/projects/config.js';
4
- import { runCommandInDir } from '../../../utils/project.js';
4
+ import { runCommandInDir } from '../../../utils/command.js';
5
5
  import { mcpFeedbackRequest } from '../../../utils/feedbackTracking.js';
6
6
  import { trackToolUsage } from '../../../utils/toolUsageTracking.js';
7
7
  vi.mock('@modelcontextprotocol/sdk/server/mcp.js');
8
8
  vi.mock('@hubspot/project-parsing-lib/profiles');
9
9
  vi.mock('../../../../lib/projects/config.js');
10
- vi.mock('../../../utils/project');
10
+ vi.mock('../../../utils/command', () => ({
11
+ runCommandInDir: vi.fn(),
12
+ addFlag: vi.fn((command, flagName, value) => {
13
+ if (Array.isArray(value)) {
14
+ return `${command} --${flagName} ${value.map(item => `"${item}"`).join(' ')}`;
15
+ }
16
+ return `${command} --${flagName} "${value}"`;
17
+ }),
18
+ }));
11
19
  vi.mock('../../../utils/toolUsageTracking');
12
20
  vi.mock('../../../utils/feedbackTracking');
13
21
  const mockTrackToolUsage = trackToolUsage;
@@ -1,9 +1,9 @@
1
1
  import { ValidateProjectTool, } from '../ValidateProjectTool.js';
2
- import { runCommandInDir } from '../../../utils/project.js';
2
+ import { runCommandInDir } from '../../../utils/command.js';
3
3
  import { mcpFeedbackRequest } from '../../../utils/feedbackTracking.js';
4
4
  import { trackToolUsage } from '../../../utils/toolUsageTracking.js';
5
5
  vi.mock('@modelcontextprotocol/sdk/server/mcp.js');
6
- vi.mock('../../../utils/project');
6
+ vi.mock('../../../utils/command');
7
7
  vi.mock('../../../utils/toolUsageTracking');
8
8
  vi.mock('../../../utils/feedbackTracking');
9
9
  const mockTrackToolUsage = trackToolUsage;
@@ -5,8 +5,8 @@ export declare const features: z.ZodOptional<z.ZodArray<z.ZodEnum<{
5
5
  card: "card";
6
6
  settings: "settings";
7
7
  "app-event": "app-event";
8
- page: "page";
9
8
  "workflow-action-tool": "workflow-action-tool";
9
+ page: "page";
10
10
  webhooks: "webhooks";
11
11
  "workflow-action": "workflow-action";
12
12
  "app-function": "app-function";
@@ -1,6 +1,20 @@
1
- import { addFlag } from '../command.js';
2
- vi.mock('child_process');
3
- vi.mock('util');
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ const mockExecAsync = vi.fn();
4
+ vi.mock('node:child_process');
5
+ vi.mock('util', () => ({
6
+ default: {
7
+ promisify: () => mockExecAsync,
8
+ },
9
+ promisify: () => mockExecAsync,
10
+ }));
11
+ vi.mock('fs');
12
+ vi.mock('path');
13
+ // Import after mocks are set up
14
+ const { addFlag, runCommandInDir } = await import('../command.js');
15
+ const mockExistsSync = vi.mocked(fs.existsSync);
16
+ const mockMkdirSync = vi.mocked(fs.mkdirSync);
17
+ const mockResolve = vi.mocked(path.resolve);
4
18
  describe('mcp-server/utils/command', () => {
5
19
  describe('addFlag', () => {
6
20
  it('should add string flag to command', () => {
@@ -42,4 +56,220 @@ describe('mcp-server/utils/command', () => {
42
56
  expect(result).toBe('hs project create --features "card with spaces" "settings"');
43
57
  });
44
58
  });
59
+ describe('runCommandInDir', () => {
60
+ const mockDirectory = '/test/directory';
61
+ const mockCommand = 'npm install';
62
+ const mockResolvedPath = '/resolved/test/directory';
63
+ beforeEach(() => {
64
+ mockResolve.mockReturnValue(mockResolvedPath);
65
+ });
66
+ it('should run command in existing directory', async () => {
67
+ const expectedResult = {
68
+ stdout: 'command output',
69
+ stderr: '',
70
+ };
71
+ mockExistsSync.mockReturnValue(true);
72
+ mockExecAsync.mockResolvedValue(expectedResult);
73
+ const result = await runCommandInDir(mockDirectory, mockCommand);
74
+ expect(mockExistsSync).toHaveBeenCalledWith(mockDirectory);
75
+ expect(mockMkdirSync).not.toHaveBeenCalled();
76
+ expect(mockResolve).toHaveBeenCalledWith(mockDirectory);
77
+ expect(mockExecAsync).toHaveBeenCalledWith(mockCommand, expect.objectContaining({
78
+ cwd: mockResolvedPath,
79
+ env: expect.any(Object),
80
+ }));
81
+ expect(result).toEqual(expectedResult);
82
+ });
83
+ it('should create directory if it does not exist', async () => {
84
+ const expectedResult = {
85
+ stdout: 'command output',
86
+ stderr: '',
87
+ };
88
+ mockExistsSync.mockReturnValue(false);
89
+ mockExecAsync.mockResolvedValue(expectedResult);
90
+ const result = await runCommandInDir(mockDirectory, mockCommand);
91
+ expect(mockExistsSync).toHaveBeenCalledWith(mockDirectory);
92
+ expect(mockMkdirSync).toHaveBeenCalledWith(mockDirectory);
93
+ expect(mockResolve).toHaveBeenCalledWith(mockDirectory);
94
+ expect(mockExecAsync).toHaveBeenCalledWith(mockCommand, expect.objectContaining({
95
+ cwd: mockResolvedPath,
96
+ env: expect.any(Object),
97
+ }));
98
+ expect(result).toEqual(expectedResult);
99
+ });
100
+ it('should propagate execAsync errors', async () => {
101
+ const error = new Error('Command failed');
102
+ mockExistsSync.mockReturnValue(true);
103
+ mockExecAsync.mockRejectedValue(error);
104
+ await expect(runCommandInDir(mockDirectory, mockCommand)).rejects.toThrow('Command failed');
105
+ expect(mockExecAsync).toHaveBeenCalledWith(mockCommand, expect.objectContaining({
106
+ cwd: mockResolvedPath,
107
+ env: expect.any(Object),
108
+ }));
109
+ });
110
+ it('should handle stderr in results', async () => {
111
+ const expectedResult = {
112
+ stdout: 'some output',
113
+ stderr: 'warning message',
114
+ };
115
+ mockExistsSync.mockReturnValue(true);
116
+ mockExecAsync.mockResolvedValue(expectedResult);
117
+ const result = await runCommandInDir(mockDirectory, mockCommand);
118
+ expect(result.stdout).toBe('some output');
119
+ expect(result.stderr).toBe('warning message');
120
+ });
121
+ it('should add --disable-usage-tracking flag to hs commands', async () => {
122
+ const hsCommand = 'hs project upload';
123
+ const expectedResult = {
124
+ stdout: 'success',
125
+ stderr: '',
126
+ };
127
+ mockExistsSync.mockReturnValue(true);
128
+ mockExecAsync.mockResolvedValue(expectedResult);
129
+ await runCommandInDir(mockDirectory, hsCommand);
130
+ expect(mockExecAsync).toHaveBeenCalledWith('hs project upload --disable-usage-tracking "true"', expect.objectContaining({
131
+ cwd: mockResolvedPath,
132
+ env: expect.any(Object),
133
+ }));
134
+ });
135
+ it('should not add --disable-usage-tracking flag to non-hs commands', async () => {
136
+ const nonHsCommand = 'npm install';
137
+ const expectedResult = {
138
+ stdout: 'success',
139
+ stderr: '',
140
+ };
141
+ mockExistsSync.mockReturnValue(true);
142
+ mockExecAsync.mockResolvedValue(expectedResult);
143
+ await runCommandInDir(mockDirectory, nonHsCommand);
144
+ expect(mockExecAsync).toHaveBeenCalledWith('npm install', expect.objectContaining({
145
+ cwd: mockResolvedPath,
146
+ env: expect.any(Object),
147
+ }));
148
+ });
149
+ it('should add --disable-usage-tracking flag to hs commands with existing flags', async () => {
150
+ const hsCommand = 'hs project upload --profile prod';
151
+ const expectedResult = {
152
+ stdout: 'success',
153
+ stderr: '',
154
+ };
155
+ mockExistsSync.mockReturnValue(true);
156
+ mockExecAsync.mockResolvedValue(expectedResult);
157
+ await runCommandInDir(mockDirectory, hsCommand);
158
+ expect(mockExecAsync).toHaveBeenCalledWith('hs project upload --profile prod --disable-usage-tracking "true"', expect.objectContaining({
159
+ cwd: mockResolvedPath,
160
+ env: expect.any(Object),
161
+ }));
162
+ });
163
+ it('should handle hs commands that start with whitespace', async () => {
164
+ const hsCommand = 'hs init';
165
+ const expectedResult = {
166
+ stdout: 'success',
167
+ stderr: '',
168
+ };
169
+ mockExistsSync.mockReturnValue(true);
170
+ mockExecAsync.mockResolvedValue(expectedResult);
171
+ await runCommandInDir(mockDirectory, hsCommand);
172
+ expect(mockExecAsync).toHaveBeenCalledWith('hs init --disable-usage-tracking "true"', expect.objectContaining({
173
+ cwd: mockResolvedPath,
174
+ env: expect.any(Object),
175
+ }));
176
+ });
177
+ it('should use npx -p @hubspot/cli when HUBSPOT_MCP_STANDALONE is true', async () => {
178
+ const originalEnv = process.env.HUBSPOT_MCP_STANDALONE;
179
+ process.env.HUBSPOT_MCP_STANDALONE = 'true';
180
+ const hsCommand = 'hs project upload';
181
+ const expectedResult = {
182
+ stdout: 'success',
183
+ stderr: '',
184
+ };
185
+ mockExistsSync.mockReturnValue(true);
186
+ mockExecAsync.mockResolvedValue(expectedResult);
187
+ await runCommandInDir(mockDirectory, hsCommand);
188
+ expect(mockExecAsync).toHaveBeenCalledWith('npx -y -p @hubspot/cli hs project upload --disable-usage-tracking "true"', expect.objectContaining({
189
+ cwd: mockResolvedPath,
190
+ env: expect.any(Object),
191
+ }));
192
+ // Restore original env
193
+ if (originalEnv === undefined) {
194
+ delete process.env.HUBSPOT_MCP_STANDALONE;
195
+ }
196
+ else {
197
+ process.env.HUBSPOT_MCP_STANDALONE = originalEnv;
198
+ }
199
+ });
200
+ it('should use regular hs command when HUBSPOT_MCP_STANDALONE is not set', async () => {
201
+ const originalEnv = process.env.HUBSPOT_MCP_STANDALONE;
202
+ delete process.env.HUBSPOT_MCP_STANDALONE;
203
+ const hsCommand = 'hs project upload';
204
+ const expectedResult = {
205
+ stdout: 'success',
206
+ stderr: '',
207
+ };
208
+ mockExistsSync.mockReturnValue(true);
209
+ mockExecAsync.mockResolvedValue(expectedResult);
210
+ await runCommandInDir(mockDirectory, hsCommand);
211
+ expect(mockExecAsync).toHaveBeenCalledWith('hs project upload --disable-usage-tracking "true"', expect.objectContaining({
212
+ cwd: mockResolvedPath,
213
+ env: expect.any(Object),
214
+ }));
215
+ // Restore original env
216
+ if (originalEnv !== undefined) {
217
+ process.env.HUBSPOT_MCP_STANDALONE = originalEnv;
218
+ }
219
+ });
220
+ it('should use npx -p @hubspot/cli for hs commands with flags in standalone mode', async () => {
221
+ const originalEnv = process.env.HUBSPOT_MCP_STANDALONE;
222
+ process.env.HUBSPOT_MCP_STANDALONE = 'true';
223
+ const hsCommand = 'hs project upload --profile prod';
224
+ const expectedResult = {
225
+ stdout: 'success',
226
+ stderr: '',
227
+ };
228
+ mockExistsSync.mockReturnValue(true);
229
+ mockExecAsync.mockResolvedValue(expectedResult);
230
+ await runCommandInDir(mockDirectory, hsCommand);
231
+ expect(mockExecAsync).toHaveBeenCalledWith('npx -y -p @hubspot/cli hs project upload --profile prod --disable-usage-tracking "true"', expect.objectContaining({
232
+ cwd: mockResolvedPath,
233
+ env: expect.any(Object),
234
+ }));
235
+ // Restore original env
236
+ if (originalEnv === undefined) {
237
+ delete process.env.HUBSPOT_MCP_STANDALONE;
238
+ }
239
+ else {
240
+ process.env.HUBSPOT_MCP_STANDALONE = originalEnv;
241
+ }
242
+ });
243
+ it('should use pinned CLI version when HUBSPOT_CLI_VERSION is set in standalone mode', async () => {
244
+ const originalStandaloneEnv = process.env.HUBSPOT_MCP_STANDALONE;
245
+ const originalVersionEnv = process.env.HUBSPOT_CLI_VERSION;
246
+ process.env.HUBSPOT_MCP_STANDALONE = 'true';
247
+ process.env.HUBSPOT_CLI_VERSION = '8.1.0';
248
+ const hsCommand = 'hs project upload';
249
+ const expectedResult = {
250
+ stdout: 'success',
251
+ stderr: '',
252
+ };
253
+ mockExistsSync.mockReturnValue(true);
254
+ mockExecAsync.mockResolvedValue(expectedResult);
255
+ await runCommandInDir(mockDirectory, hsCommand);
256
+ expect(mockExecAsync).toHaveBeenCalledWith('npx -y -p @hubspot/cli@8.1.0 hs project upload --disable-usage-tracking "true"', expect.objectContaining({
257
+ cwd: mockResolvedPath,
258
+ env: expect.any(Object),
259
+ }));
260
+ // Restore original env
261
+ if (originalStandaloneEnv === undefined) {
262
+ delete process.env.HUBSPOT_MCP_STANDALONE;
263
+ }
264
+ else {
265
+ process.env.HUBSPOT_MCP_STANDALONE = originalStandaloneEnv;
266
+ }
267
+ if (originalVersionEnv === undefined) {
268
+ delete process.env.HUBSPOT_CLI_VERSION;
269
+ }
270
+ else {
271
+ process.env.HUBSPOT_CLI_VERSION = originalVersionEnv;
272
+ }
273
+ });
274
+ });
45
275
  });