@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
@@ -29,7 +29,7 @@ describe('mcp-server/tools/project/CreateProjectTool', () => {
29
29
  describe('register', () => {
30
30
  it('should register tool with correct parameters', () => {
31
31
  const result = tool.register();
32
- expect(mockMcpServer.registerTool).toHaveBeenCalledWith('create-hubspot-project', {
32
+ expect(mockMcpServer.registerTool).toHaveBeenCalledWith('create-project', {
33
33
  title: 'Create HubSpot Project',
34
34
  description: 'Creates a HubSpot project with the provided name and outputs it in the provided destination',
35
35
  inputSchema: expect.any(Object),
@@ -26,7 +26,7 @@ describe('mcp-server/tools/project/DeployProject', () => {
26
26
  describe('register', () => {
27
27
  it('should register tool with correct parameters', () => {
28
28
  const result = tool.register();
29
- expect(mockMcpServer.registerTool).toHaveBeenCalledWith('deploy-hubspot-project', {
29
+ expect(mockMcpServer.registerTool).toHaveBeenCalledWith('deploy-project', {
30
30
  title: 'Deploy a build of HubSpot Project',
31
31
  description: expect.stringContaining('Takes a build number and a project name and deploys that build of the project'),
32
32
  inputSchema: expect.any(Object),
@@ -24,9 +24,9 @@ describe('mcp-server/tools/project/DocFetchTool', () => {
24
24
  describe('register', () => {
25
25
  it('should register tool with correct parameters', () => {
26
26
  const result = tool.register();
27
- expect(mockMcpServer.registerTool).toHaveBeenCalledWith('fetch-hubspot-doc', {
27
+ expect(mockMcpServer.registerTool).toHaveBeenCalledWith('fetch-doc', {
28
28
  title: 'Fetch HubSpot Developer Documentation (single file)',
29
- description: 'Always use this immediately after `search-hubspot-docs` and before creating a plan, writing code, or answering technical questions. This tool retrieves the full, authoritative content of a HubSpot Developer Documentation page from its URL, ensuring responses are accurate, up-to-date, and grounded in the official docs.',
29
+ description: 'Always use this immediately after `search-docs` and before creating a plan, writing code, or answering technical questions. This tool retrieves the full, authoritative content of a HubSpot Developer Documentation page from its URL, ensuring responses are accurate, up-to-date, and grounded in the official docs.',
30
30
  inputSchema: expect.any(Object),
31
31
  }, tool.handler);
32
32
  expect(result).toBe(mockRegisteredTool);
@@ -1,14 +1,14 @@
1
1
  import { DocsSearchTool } from '../DocsSearchTool.js';
2
2
  import { http } from '@hubspot/local-dev-lib/http';
3
- import { getAccountId } from '@hubspot/local-dev-lib/config';
4
3
  import { isHubSpotHttpError } from '@hubspot/local-dev-lib/errors/index';
4
+ import { getAccountIdFromCliConfig } from '../../../utils/cliConfig.js';
5
5
  vi.mock('@modelcontextprotocol/sdk/server/mcp.js');
6
6
  vi.mock('@hubspot/local-dev-lib/http');
7
- vi.mock('@hubspot/local-dev-lib/config');
8
7
  vi.mock('@hubspot/local-dev-lib/errors/index');
9
8
  vi.mock('../../../utils/toolUsageTracking');
9
+ vi.mock('../../../utils/cliConfig.js');
10
10
  const mockHttp = http;
11
- const mockGetAccountId = getAccountId;
11
+ const mockGetAccountIdFromCliConfig = getAccountIdFromCliConfig;
12
12
  const mockIsHubSpotHttpError = vi.mocked(isHubSpotHttpError);
13
13
  describe('mcp-server/tools/project/DocsSearchTool', () => {
14
14
  let mockMcpServer;
@@ -27,9 +27,9 @@ describe('mcp-server/tools/project/DocsSearchTool', () => {
27
27
  describe('register', () => {
28
28
  it('should register tool with correct parameters', () => {
29
29
  const result = tool.register();
30
- expect(mockMcpServer.registerTool).toHaveBeenCalledWith('search-hubspot-docs', {
30
+ expect(mockMcpServer.registerTool).toHaveBeenCalledWith('search-docs', {
31
31
  title: 'Search HubSpot Developer Documentation',
32
- description: 'Use this first whenever you need details about HubSpot APIs, SDKs, integrations, or developer platform features. This searches the official HubSpot Developer Documentation and returns the most relevant pages, each with a URL for use in `fetch-hubspot-doc`. Always follow this with a fetch to get the full, authoritative content before making plans or writing answers.',
32
+ description: 'Use this first whenever you need details about HubSpot APIs, SDKs, integrations, or developer platform features. This searches the official HubSpot Developer Documentation and returns the most relevant pages, each with a URL for use in `fetch-doc`. Always follow this with a fetch to get the full, authoritative content before making plans or writing answers.',
33
33
  inputSchema: expect.any(Object),
34
34
  }, tool.handler);
35
35
  expect(result).toBe(mockRegisteredTool);
@@ -38,9 +38,10 @@ describe('mcp-server/tools/project/DocsSearchTool', () => {
38
38
  describe('handler', () => {
39
39
  const mockInput = {
40
40
  docsSearchQuery: 'test query',
41
+ absoluteCurrentWorkingDirectory: '/foo',
41
42
  };
42
43
  it('should return auth error message when no account ID is found', async () => {
43
- mockGetAccountId.mockReturnValue(null);
44
+ mockGetAccountIdFromCliConfig.mockReturnValue(null);
44
45
  const result = await tool.handler(mockInput);
45
46
  expect(result).toEqual({
46
47
  content: [
@@ -52,7 +53,7 @@ describe('mcp-server/tools/project/DocsSearchTool', () => {
52
53
  });
53
54
  });
54
55
  it('should return successful results when docs are found', async () => {
55
- mockGetAccountId.mockReturnValue(12345);
56
+ mockGetAccountIdFromCliConfig.mockReturnValue(12345);
56
57
  const mockResponse = {
57
58
  results: [
58
59
  {
@@ -76,6 +77,7 @@ describe('mcp-server/tools/project/DocsSearchTool', () => {
76
77
  data: mockResponse,
77
78
  });
78
79
  const result = await tool.handler(mockInput);
80
+ expect(mockGetAccountIdFromCliConfig).toHaveBeenCalledWith('/foo');
79
81
  expect(mockHttp.post).toHaveBeenCalledWith(12345, {
80
82
  url: 'dev/docs/llms/v1/docs-search',
81
83
  data: {
@@ -103,7 +105,7 @@ describe('mcp-server/tools/project/DocsSearchTool', () => {
103
105
  expect(resultText).toContain('Test content 2');
104
106
  });
105
107
  it('should return no results message when no documentation is found', async () => {
106
- mockGetAccountId.mockReturnValue(12345);
108
+ mockGetAccountIdFromCliConfig.mockReturnValue(12345);
107
109
  const mockResponse = {
108
110
  results: [],
109
111
  };
@@ -122,7 +124,7 @@ describe('mcp-server/tools/project/DocsSearchTool', () => {
122
124
  });
123
125
  });
124
126
  it('should return no results message when results is null', async () => {
125
- mockGetAccountId.mockReturnValue(12345);
127
+ mockGetAccountIdFromCliConfig.mockReturnValue(12345);
126
128
  const mockResponse = {
127
129
  results: null,
128
130
  };
@@ -141,7 +143,7 @@ describe('mcp-server/tools/project/DocsSearchTool', () => {
141
143
  });
142
144
  });
143
145
  it('should handle HubSpot HTTP errors', async () => {
144
- mockGetAccountId.mockReturnValue(12345);
146
+ mockGetAccountIdFromCliConfig.mockReturnValue(12345);
145
147
  const mockError = {
146
148
  toString: () => 'HubSpot API Error: 404 Not Found',
147
149
  };
@@ -158,7 +160,7 @@ describe('mcp-server/tools/project/DocsSearchTool', () => {
158
160
  });
159
161
  });
160
162
  it('should handle generic errors', async () => {
161
- mockGetAccountId.mockReturnValue(12345);
163
+ mockGetAccountIdFromCliConfig.mockReturnValue(12345);
162
164
  const mockError = new Error('Network error');
163
165
  mockHttp.post.mockRejectedValue(mockError);
164
166
  mockIsHubSpotHttpError.mockReturnValue(false);
@@ -173,7 +175,7 @@ describe('mcp-server/tools/project/DocsSearchTool', () => {
173
175
  });
174
176
  });
175
177
  it('should handle non-Error rejections', async () => {
176
- mockGetAccountId.mockReturnValue(12345);
178
+ mockGetAccountIdFromCliConfig.mockReturnValue(12345);
177
179
  mockHttp.post.mockRejectedValue('String error');
178
180
  mockIsHubSpotHttpError.mockReturnValue(false);
179
181
  const result = await tool.handler(mockInput);
@@ -1,13 +1,13 @@
1
1
  import { GetConfigValuesTool } from '../GetConfigValuesTool.js';
2
2
  import { getIntermediateRepresentationSchema, mapToInternalType, } from '@hubspot/project-parsing-lib';
3
- import { getAccountId } from '@hubspot/local-dev-lib/config';
3
+ import { getAccountIdFromCliConfig } from '../../../utils/cliConfig.js';
4
4
  vi.mock('@modelcontextprotocol/sdk/server/mcp.js');
5
5
  vi.mock('@hubspot/project-parsing-lib');
6
- vi.mock('@hubspot/local-dev-lib/config');
6
+ vi.mock('../../../utils/cliConfig.js');
7
7
  vi.mock('../../../utils/toolUsageTracking');
8
8
  const mockGetIntermediateRepresentationSchema = getIntermediateRepresentationSchema;
9
9
  const mockMapToInternalType = mapToInternalType;
10
- const mockGetAccountId = getAccountId;
10
+ const mockGetAccountIdFromCliConfig = getAccountIdFromCliConfig;
11
11
  describe('mcp-server/tools/project/GetConfigValuesTool', () => {
12
12
  let mockMcpServer;
13
13
  let tool;
@@ -25,7 +25,7 @@ describe('mcp-server/tools/project/GetConfigValuesTool', () => {
25
25
  describe('register', () => {
26
26
  it('should register tool with correct parameters', () => {
27
27
  const result = tool.register();
28
- expect(mockMcpServer.registerTool).toHaveBeenCalledWith('get-hubspot-feature-config-schema', {
28
+ expect(mockMcpServer.registerTool).toHaveBeenCalledWith('get-feature-config-schema', {
29
29
  title: 'Fetch the JSON Schema for component',
30
30
  description: expect.stringContaining('Fetches and returns the JSON schema for the provided feature'),
31
31
  inputSchema: expect.objectContaining({
@@ -44,9 +44,10 @@ describe('mcp-server/tools/project/GetConfigValuesTool', () => {
44
44
  const input = {
45
45
  platformVersion: '2025.2',
46
46
  featureType: 'card',
47
+ absoluteCurrentWorkingDirectory: '/foo',
47
48
  };
48
49
  beforeEach(() => {
49
- mockGetAccountId.mockReturnValue(123456789);
50
+ mockGetAccountIdFromCliConfig.mockReturnValue(123456789);
50
51
  });
51
52
  it('should return config schema when component type exists', async () => {
52
53
  const mockSchema = {
@@ -61,6 +62,7 @@ describe('mcp-server/tools/project/GetConfigValuesTool', () => {
61
62
  mockGetIntermediateRepresentationSchema.mockResolvedValue(mockSchema);
62
63
  mockMapToInternalType.mockReturnValue('internal-card-type');
63
64
  const result = await tool.handler(input);
65
+ expect(mockGetAccountIdFromCliConfig).toHaveBeenCalledWith('/foo');
64
66
  expect(mockGetIntermediateRepresentationSchema).toHaveBeenCalledWith({
65
67
  platformVersion: '2025.2',
66
68
  projectSourceDir: '',
@@ -133,14 +135,13 @@ describe('mcp-server/tools/project/GetConfigValuesTool', () => {
133
135
  });
134
136
  });
135
137
  it('should handle null account id', async () => {
136
- mockGetAccountId.mockReturnValue(null);
137
- mockGetIntermediateRepresentationSchema.mockRejectedValue(new Error('No account ID'));
138
+ mockGetAccountIdFromCliConfig.mockReturnValue(null);
138
139
  const result = await tool.handler(input);
139
140
  expect(result).toEqual({
140
141
  content: [
141
142
  {
142
143
  type: 'text',
143
- text: 'Unable to locate JSON schema for type card',
144
+ text: 'No account ID found. Please run `hs account auth` to configure an account, or set a default account with `hs account use <account>`',
144
145
  },
145
146
  ],
146
147
  });
@@ -21,7 +21,7 @@ describe('mcp-server/tools/project/GuidedWalkthroughTool', () => {
21
21
  describe('register', () => {
22
22
  it('should register tool with correct parameters', () => {
23
23
  const result = tool.register();
24
- expect(mockMcpServer.registerTool).toHaveBeenCalledWith('guided-walkthrough-hubspot-cli', {
24
+ expect(mockMcpServer.registerTool).toHaveBeenCalledWith('guided-walkthrough-cli', {
25
25
  title: 'Guided walkthrough of the CLI',
26
26
  description: 'Give the user a guided walkthrough of the HubSpot CLI.',
27
27
  inputSchema: expect.any(Object),
@@ -21,7 +21,7 @@ describe('mcp-server/tools/project/UploadProjectTools', () => {
21
21
  describe('register', () => {
22
22
  it('should register tool with correct parameters', () => {
23
23
  const result = tool.register();
24
- expect(mockMcpServer.registerTool).toHaveBeenCalledWith('upload-hubspot-project', {
24
+ expect(mockMcpServer.registerTool).toHaveBeenCalledWith('upload-project', {
25
25
  title: 'Upload HubSpot Project',
26
26
  description: expect.stringContaining('Uploads the HubSpot project in current working directory.'),
27
27
  inputSchema: expect.any(Object),
@@ -21,7 +21,7 @@ describe('mcp-server/tools/project/ValidateProjectTool', () => {
21
21
  describe('register', () => {
22
22
  it('should register tool with correct parameters', () => {
23
23
  const result = tool.register();
24
- expect(mockMcpServer.registerTool).toHaveBeenCalledWith('validate-hubspot-project', {
24
+ expect(mockMcpServer.registerTool).toHaveBeenCalledWith('validate-project', {
25
25
  title: expect.stringContaining('Validate HubSpot Project'),
26
26
  description: expect.stringContaining('Validates the HubSpot project and its configuration files.'),
27
27
  inputSchema: expect.any(Object),
@@ -1,6 +1,6 @@
1
1
  import z from 'zod';
2
2
  export declare const absoluteProjectPath: z.ZodString;
3
3
  export declare const absoluteCurrentWorkingDirectory: z.ZodString;
4
- export declare const features: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodLiteral<"card">, z.ZodLiteral<"settings">, z.ZodLiteral<"app-function">, z.ZodLiteral<"webhooks">, z.ZodLiteral<"workflow-action">, z.ZodLiteral<"workflow-action-tool">, z.ZodLiteral<"app-object">, z.ZodLiteral<"scim">]>, "many">>;
4
+ export declare const features: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodLiteral<"card">, z.ZodLiteral<"settings">, z.ZodLiteral<"app-function">, z.ZodLiteral<"webhooks">, z.ZodLiteral<"workflow-action">, z.ZodLiteral<"workflow-action-tool">, z.ZodLiteral<"app-object">, z.ZodLiteral<"app-event">, z.ZodLiteral<"scim">, z.ZodLiteral<"page">]>, "many">>;
5
5
  export declare const docsSearchQuery: z.ZodString;
6
6
  export declare const docUrl: z.ZodString;
@@ -9,12 +9,18 @@ export const features = z
9
9
  .array(z.union([
10
10
  z.literal('card'),
11
11
  z.literal('settings'),
12
- z.literal('app-function'),
12
+ z
13
+ .literal('app-function')
14
+ .describe('Also known as a public serverless function'),
13
15
  z.literal('webhooks'),
14
- z.literal('workflow-action'),
15
- z.literal('workflow-action-tool'),
16
+ z
17
+ .literal('workflow-action')
18
+ .describe('Also known as a custom workflow action.'),
19
+ z.literal('workflow-action-tool').describe('Also known as agent tools.'),
16
20
  z.literal('app-object'),
21
+ z.literal('app-event'),
17
22
  z.literal('scim'),
23
+ z.literal('page'),
18
24
  ]))
19
25
  .describe('The features to include in the project, multiple options can be selected')
20
26
  .optional();
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,110 @@
1
+ import { getAccountIdFromCliConfig } from '../cliConfig.js';
2
+ import { configFileExists, findConfig, getAccountId, loadConfig, } from '@hubspot/local-dev-lib/config';
3
+ vi.mock('@hubspot/local-dev-lib/config');
4
+ const mockConfigFileExists = configFileExists;
5
+ const mockFindConfig = findConfig;
6
+ const mockGetAccountId = getAccountId;
7
+ const mockLoadConfig = loadConfig;
8
+ describe('mcp-server/utils/cliConfig', () => {
9
+ const mockWorkingDirectory = '/test/working/directory';
10
+ beforeEach(() => {
11
+ vi.clearAllMocks();
12
+ });
13
+ describe('getAccountIdFromCliConfig', () => {
14
+ it('should load global config when it exists and return account ID', () => {
15
+ const expectedAccountId = 12345;
16
+ mockConfigFileExists.mockReturnValue(true);
17
+ mockGetAccountId.mockReturnValue(expectedAccountId);
18
+ const result = getAccountIdFromCliConfig(mockWorkingDirectory);
19
+ expect(mockConfigFileExists).toHaveBeenCalledWith(true);
20
+ expect(mockLoadConfig).toHaveBeenCalledWith('');
21
+ expect(mockFindConfig).not.toHaveBeenCalled();
22
+ expect(mockGetAccountId).toHaveBeenCalledWith(undefined);
23
+ expect(result).toBe(expectedAccountId);
24
+ });
25
+ it('should load local config when global config does not exist and return account ID', () => {
26
+ const expectedAccountId = 67890;
27
+ const localConfigPath = '/path/to/local/config';
28
+ mockConfigFileExists.mockReturnValue(false);
29
+ mockFindConfig.mockReturnValue(localConfigPath);
30
+ mockGetAccountId.mockReturnValue(expectedAccountId);
31
+ const result = getAccountIdFromCliConfig(mockWorkingDirectory);
32
+ expect(mockConfigFileExists).toHaveBeenCalledWith(true);
33
+ expect(mockFindConfig).toHaveBeenCalledWith(mockWorkingDirectory);
34
+ expect(mockLoadConfig).toHaveBeenCalledWith(localConfigPath);
35
+ expect(mockGetAccountId).toHaveBeenCalledWith(undefined);
36
+ expect(result).toBe(expectedAccountId);
37
+ });
38
+ it('should pass accountNameOrId parameter to getAccountId when provided as string', () => {
39
+ const expectedAccountId = 11111;
40
+ const accountName = 'test-account';
41
+ mockConfigFileExists.mockReturnValue(true);
42
+ mockGetAccountId.mockReturnValue(expectedAccountId);
43
+ const result = getAccountIdFromCliConfig(mockWorkingDirectory, accountName);
44
+ expect(mockConfigFileExists).toHaveBeenCalledWith(true);
45
+ expect(mockLoadConfig).toHaveBeenCalledWith('');
46
+ expect(mockGetAccountId).toHaveBeenCalledWith(accountName);
47
+ expect(result).toBe(expectedAccountId);
48
+ });
49
+ it('should pass accountNameOrId parameter to getAccountId when provided as number', () => {
50
+ const expectedAccountId = 22222;
51
+ const accountId = 22222;
52
+ mockConfigFileExists.mockReturnValue(true);
53
+ mockGetAccountId.mockReturnValue(expectedAccountId);
54
+ const result = getAccountIdFromCliConfig(mockWorkingDirectory, accountId);
55
+ expect(mockConfigFileExists).toHaveBeenCalledWith(true);
56
+ expect(mockLoadConfig).toHaveBeenCalledWith('');
57
+ expect(mockGetAccountId).toHaveBeenCalledWith(accountId);
58
+ expect(result).toBe(expectedAccountId);
59
+ });
60
+ it('should return null when getAccountId returns null', () => {
61
+ mockConfigFileExists.mockReturnValue(true);
62
+ mockGetAccountId.mockReturnValue(null);
63
+ const result = getAccountIdFromCliConfig(mockWorkingDirectory);
64
+ expect(mockConfigFileExists).toHaveBeenCalledWith(true);
65
+ expect(mockLoadConfig).toHaveBeenCalledWith('');
66
+ expect(mockGetAccountId).toHaveBeenCalledWith(undefined);
67
+ expect(result).toBeNull();
68
+ });
69
+ it('should handle findConfig returning null by passing null to loadConfig', () => {
70
+ const expectedAccountId = 33333;
71
+ mockConfigFileExists.mockReturnValue(false);
72
+ mockFindConfig.mockReturnValue(null);
73
+ mockGetAccountId.mockReturnValue(expectedAccountId);
74
+ const result = getAccountIdFromCliConfig(mockWorkingDirectory);
75
+ expect(mockConfigFileExists).toHaveBeenCalledWith(true);
76
+ expect(mockFindConfig).toHaveBeenCalledWith(mockWorkingDirectory);
77
+ expect(mockLoadConfig).toHaveBeenCalledWith(null);
78
+ expect(mockGetAccountId).toHaveBeenCalledWith(undefined);
79
+ expect(result).toBe(expectedAccountId);
80
+ });
81
+ it('should work with local config when provided with account name parameter', () => {
82
+ const expectedAccountId = 44444;
83
+ const accountName = 'local-test-account';
84
+ const localConfigPath = '/path/to/local/config';
85
+ mockConfigFileExists.mockReturnValue(false);
86
+ mockFindConfig.mockReturnValue(localConfigPath);
87
+ mockGetAccountId.mockReturnValue(expectedAccountId);
88
+ const result = getAccountIdFromCliConfig(mockWorkingDirectory, accountName);
89
+ expect(mockConfigFileExists).toHaveBeenCalledWith(true);
90
+ expect(mockFindConfig).toHaveBeenCalledWith(mockWorkingDirectory);
91
+ expect(mockLoadConfig).toHaveBeenCalledWith(localConfigPath);
92
+ expect(mockGetAccountId).toHaveBeenCalledWith(accountName);
93
+ expect(result).toBe(expectedAccountId);
94
+ });
95
+ it('should work with local config when provided with account ID parameter', () => {
96
+ const expectedAccountId = 55555;
97
+ const accountId = 55555;
98
+ const localConfigPath = '/path/to/local/config';
99
+ mockConfigFileExists.mockReturnValue(false);
100
+ mockFindConfig.mockReturnValue(localConfigPath);
101
+ mockGetAccountId.mockReturnValue(expectedAccountId);
102
+ const result = getAccountIdFromCliConfig(mockWorkingDirectory, accountId);
103
+ expect(mockConfigFileExists).toHaveBeenCalledWith(true);
104
+ expect(mockFindConfig).toHaveBeenCalledWith(mockWorkingDirectory);
105
+ expect(mockLoadConfig).toHaveBeenCalledWith(localConfigPath);
106
+ expect(mockGetAccountId).toHaveBeenCalledWith(accountId);
107
+ expect(result).toBe(expectedAccountId);
108
+ });
109
+ });
110
+ });
@@ -0,0 +1 @@
1
+ export declare function getAccountIdFromCliConfig(absolutePathToWorkingDirectory: string, accountNameOrId?: string | number): number | null;
@@ -0,0 +1,12 @@
1
+ import { configFileExists, findConfig, getAccountId, loadConfig, } from '@hubspot/local-dev-lib/config';
2
+ export function getAccountIdFromCliConfig(absolutePathToWorkingDirectory, accountNameOrId) {
3
+ const globalConfigExists = configFileExists(true);
4
+ if (globalConfigExists) {
5
+ loadConfig('');
6
+ }
7
+ else {
8
+ const configPath = findConfig(absolutePathToWorkingDirectory);
9
+ loadConfig(configPath);
10
+ }
11
+ return getAccountId(accountNameOrId);
12
+ }
package/package.json CHANGED
@@ -1,17 +1,16 @@
1
1
  {
2
2
  "name": "@hubspot/cli",
3
- "version": "7.8.0-experimental.0",
3
+ "version": "7.8.2-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",
7
7
  "type": "module",
8
8
  "dependencies": {
9
- "@hubspot/local-dev-lib": "3.18.0",
10
- "@hubspot/project-parsing-lib": "0.8.2",
9
+ "@hubspot/local-dev-lib": "3.19.1",
10
+ "@hubspot/project-parsing-lib": "0.0.22-experimental.0",
11
11
  "@hubspot/serverless-dev-runtime": "7.0.6",
12
12
  "@hubspot/theme-preview-dev-server": "0.0.10",
13
- "@hubspot/ui-extensions-dev-server": "0.9.8",
14
- "@inkjs/ui": "^2.0.0",
13
+ "@hubspot/ui-extensions-dev-server": "0.10.0",
15
14
  "archiver": "7.0.1",
16
15
  "boxen": "8.0.1",
17
16
  "chalk": "5.4.1",
@@ -19,12 +18,9 @@
19
18
  "cli-cursor": "3.1.0",
20
19
  "cli-progress": "3.12.0",
21
20
  "express": "4.21.2",
22
- "figlet": "^1.8.2",
23
- "figures": "^6.1.0",
24
21
  "findup-sync": "4.0.0",
25
22
  "fs-extra": "8.1.0",
26
23
  "ink": "5.2.1",
27
- "ink-link": "^4.1.0",
28
24
  "inquirer": "12.7.0",
29
25
  "js-yaml": "4.1.0",
30
26
  "moment": "2.30.1",
@@ -43,7 +39,6 @@
43
39
  "@types/archiver": "^6.0.3",
44
40
  "@types/cli-progress": "^3.11.6",
45
41
  "@types/express": "^5.0.0",
46
- "@types/figlet": "^1.7.0",
47
42
  "@types/findup-sync": "^4.0.5",
48
43
  "@types/fs-extra": "^11.0.4",
49
44
  "@types/inquirer": "^9.0.8",
@@ -1,17 +1,17 @@
1
1
  import { HSProfileVariables, IntermediateRepresentationNodeLocalDev } from '@hubspot/project-parsing-lib/src/lib/types.js';
2
2
  import { Environment } from '@hubspot/local-dev-lib/types/Config';
3
3
  import { ValueOf } from '@hubspot/local-dev-lib/types/Utils';
4
+ import { Project } from '@hubspot/local-dev-lib/types/Project';
4
5
  import { ProjectConfig } from './Projects.js';
5
6
  import LocalDevState from '../lib/projects/localDev/LocalDevState.js';
6
- import { APP_INSTALLATION_STATES, LOCAL_DEV_SERVER_MESSAGE_TYPES } from '../lib/constants.js';
7
+ import { APP_INSTALLATION_STATES, LOCAL_DEV_UI_MESSAGE_RECEIVE_TYPES, LOCAL_DEV_SERVER_MESSAGE_TYPES } from '../lib/constants.js';
7
8
  export type LocalDevStateConstructorOptions = {
8
9
  targetProjectAccountId: number;
9
10
  targetTestingAccountId: number;
10
11
  profile?: string;
11
12
  projectConfig: ProjectConfig;
12
13
  projectDir: string;
13
- projectId: number;
14
- projectName: string;
14
+ projectData: Project;
15
15
  debug?: boolean;
16
16
  initialProjectNodes: {
17
17
  [key: string]: IntermediateRepresentationNodeLocalDev;
@@ -23,6 +23,12 @@ export type LocalDevWebsocketMessage = {
23
23
  type: string;
24
24
  data?: unknown;
25
25
  };
26
+ export type LocalDevDeployWebsocketMessage = {
27
+ type: typeof LOCAL_DEV_UI_MESSAGE_RECEIVE_TYPES.DEPLOY;
28
+ data: {
29
+ force: boolean;
30
+ };
31
+ };
26
32
  export type LocalDevStateListener<K extends keyof LocalDevState> = (value: LocalDevState[K]) => void;
27
33
  export type AppLocalDevData = {
28
34
  id: number;
@@ -32,3 +38,13 @@ export type AppLocalDevData = {
32
38
  scopeGroupIds: number[];
33
39
  };
34
40
  export type LocalDevServerMessage = ValueOf<typeof LOCAL_DEV_SERVER_MESSAGE_TYPES>;
41
+ export type LocalDevProjectUploadResult = {
42
+ uploadSuccess: boolean;
43
+ buildSuccess: boolean;
44
+ deploySuccess?: boolean;
45
+ deployId?: number;
46
+ };
47
+ export type LocalDevProjectDeployResult = {
48
+ success: boolean;
49
+ deployId?: number;
50
+ };
@@ -26,5 +26,5 @@ export function HorizontalSelectPrompt({ defaultOption, options, onSelect, promp
26
26
  onSelect(options[selectedIndex]);
27
27
  }
28
28
  });
29
- return (_jsxs(Box, { ...CONTAINER_STYLES, flexDirection: "column", marginTop: 1, width: "100%", justifyContent: "flex-start", children: [prompt && (_jsx(Box, { marginBottom: 1, alignSelf: "flex-start", children: _jsx(Text, { children: prompt }) })), _jsx(Box, { flexDirection: "row", justifyContent: "flex-start", flexWrap: "wrap", width: "100%", gap: 1, children: options.map((option, index) => (_jsx(Box, { children: _jsx(Text, { backgroundColor: index === selectedIndex ? INK_COLORS.INFO_BLUE : undefined, bold: index === selectedIndex, children: ` ${option} ` }) }, index))) }), _jsx(Box, { marginTop: 1, alignSelf: "flex-start", justifyContent: "flex-start", children: _jsx(Text, { dimColor: true, children: "Use arrow keys to navigate, Enter to select" }) })] }));
29
+ return (_jsxs(Box, { ...CONTAINER_STYLES, flexDirection: "column", marginTop: 1, width: "100%", alignSelf: "center", justifyContent: "center", children: [prompt && (_jsx(Box, { marginBottom: 1, children: _jsx(Text, { children: prompt }) })), _jsx(Box, { flexDirection: "row", justifyContent: "center", flexWrap: "wrap", width: "100%", gap: 1, children: options.map((option, index) => (_jsx(Box, { children: _jsx(Text, { backgroundColor: index === selectedIndex ? INK_COLORS.INFO_BLUE : undefined, bold: index === selectedIndex, children: ` ${option} ` }) }, index))) }), _jsx(Box, { marginTop: 1, alignSelf: "center", justifyContent: "center", children: _jsx(Text, { dimColor: true, children: "Use arrow keys to navigate, Enter to select" }) })] }));
30
30
  }
package/ui/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { render } from 'ink';
2
- // Ink components will be enabled by setting the ENABLE_INK environment variable
2
+ // Ink components will be enabled by setting the HUBSPOT_ENABLE_INK environment variable
3
3
  export async function renderInline(component) {
4
4
  const { unmount } = render(component, { patchConsole: false });
5
5
  unmount();
@@ -1,9 +0,0 @@
1
- import { AccountArgs, YargsCommandModule, CommonArgs, ConfigArgs, EnvironmentArgs } from '../types/Yargs.js';
2
- export declare const command = "get-started-v2";
3
- export declare const describe: undefined;
4
- export type GetStartedArgs = CommonArgs & ConfigArgs & AccountArgs & EnvironmentArgs & {
5
- name?: string;
6
- dest?: string;
7
- };
8
- declare const getStartedV2Command: YargsCommandModule<unknown, GetStartedArgs>;
9
- export default getStartedV2Command;
@@ -1,39 +0,0 @@
1
- import { commands } from '../lang/en.js';
2
- import { makeYargsBuilder } from '../lib/yargsUtils.js';
3
- import { getGetStarted } from '../ui/views/GetStarted.js';
4
- import { render } from 'ink';
5
- export const command = 'get-started-v2';
6
- export const describe = undefined;
7
- async function handler(args) {
8
- render(getGetStarted({ args }));
9
- }
10
- function getStartedBuilder(yargs) {
11
- yargs.options({
12
- name: {
13
- describe: commands.getStarted.options.name.describe,
14
- type: 'string',
15
- },
16
- dest: {
17
- describe: commands.getStarted.options.dest.describe,
18
- type: 'string',
19
- },
20
- 'template-source': {
21
- describe: commands.getStarted.options.templateSource.describe,
22
- type: 'string',
23
- },
24
- });
25
- return yargs;
26
- }
27
- const builder = makeYargsBuilder(getStartedBuilder, command, commands.getStarted.verboseDescribe, {
28
- useGlobalOptions: true,
29
- useAccountOptions: true,
30
- useConfigOptions: true,
31
- useEnvironmentOptions: true,
32
- });
33
- const getStartedV2Command = {
34
- command,
35
- describe,
36
- handler,
37
- builder,
38
- };
39
- export default getStartedV2Command;
@@ -1,10 +0,0 @@
1
- import React from 'react';
2
- import figlet from 'figlet';
3
- type AsciiProps = {
4
- font?: figlet.Fonts;
5
- horizontalLayout?: figlet.KerningMethods;
6
- verticalLayout?: figlet.KerningMethods;
7
- text?: string;
8
- };
9
- export declare function Ascii({ font, horizontalLayout, verticalLayout, text, }: AsciiProps): React.ReactNode;
10
- export default Ascii;
@@ -1,11 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import figlet from 'figlet';
3
- import { Box, Text } from 'ink';
4
- export function Ascii({ font = 'Slant Relief', horizontalLayout = 'default', verticalLayout = 'default', text = '', }) {
5
- return (_jsx(Box, { alignSelf: "center", children: _jsx(Text, { color: "#FF7A59", children: figlet.textSync(text, {
6
- font,
7
- horizontalLayout,
8
- verticalLayout,
9
- }) }) }));
10
- }
11
- export default Ascii;
@@ -1,7 +0,0 @@
1
- import { ArgumentsCamelCase } from 'yargs';
2
- import { GetStartedArgs } from '../../commands/getStarted.js';
3
- export type GetStartedProps = {
4
- args: ArgumentsCamelCase<GetStartedArgs>;
5
- };
6
- export declare function getGetStarted(props: GetStartedProps): React.ReactNode;
7
- export declare function GetStarted({ args }: GetStartedProps): React.ReactNode;