@hubspot/cli 7.6.0-beta.11 → 7.6.0-beta.12

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 (142) hide show
  1. package/commands/app/__tests__/migrate.test.js +1 -0
  2. package/commands/getStarted.js +7 -20
  3. package/commands/mcp/setup.d.ts +0 -1
  4. package/commands/mcp/setup.js +3 -11
  5. package/commands/project/__tests__/add.test.js +64 -0
  6. package/commands/project/__tests__/create.test.js +57 -0
  7. package/commands/project/__tests__/deploy.test.js +3 -2
  8. package/commands/project/add.d.ts +1 -1
  9. package/commands/project/add.js +3 -5
  10. package/commands/project/create.js +7 -2
  11. package/commands/project/deploy.js +9 -61
  12. package/commands/project/dev/index.js +1 -1
  13. package/commands/project/dev/unifiedFlow.js +4 -1
  14. package/commands/project/upload.js +2 -2
  15. package/commands/project/validate.js +1 -1
  16. package/commands/project/watch.js +2 -2
  17. package/lang/en.d.ts +17 -5
  18. package/lang/en.js +34 -22
  19. package/lang/en.lyaml +12 -12
  20. package/lib/__tests__/hasFeature.test.js +145 -7
  21. package/lib/__tests__/importData.test.js +1 -1
  22. package/lib/app/migrate.js +9 -2
  23. package/lib/constants.d.ts +2 -0
  24. package/lib/constants.js +2 -0
  25. package/lib/errorHandlers/index.d.ts +4 -0
  26. package/lib/errorHandlers/index.js +1 -1
  27. package/lib/hasFeature.js +6 -0
  28. package/lib/importData.js +1 -1
  29. package/lib/mcp/setup.d.ts +0 -2
  30. package/lib/mcp/setup.js +4 -29
  31. package/lib/projects/__tests__/AppDevModeInterface.test.js +71 -44
  32. package/lib/projects/__tests__/LocalDevProcess.test.js +1 -0
  33. package/lib/projects/__tests__/components.test.js +164 -7
  34. package/lib/projects/__tests__/deploy.test.js +164 -0
  35. package/lib/projects/__tests__/platformVersion.test.d.ts +1 -0
  36. package/lib/projects/__tests__/{buildAndDeploy.test.js → platformVersion.test.js} +2 -2
  37. package/lib/projects/add/__tests__/legacyAddComponent.test.js +49 -6
  38. package/lib/projects/add/__tests__/v3AddComponent.test.js +142 -8
  39. package/lib/projects/add/legacyAddComponent.d.ts +1 -1
  40. package/lib/projects/add/legacyAddComponent.js +5 -1
  41. package/lib/projects/add/v3AddComponent.d.ts +2 -1
  42. package/lib/projects/add/v3AddComponent.js +22 -5
  43. package/lib/projects/components.d.ts +1 -0
  44. package/lib/projects/components.js +27 -1
  45. package/lib/projects/create/__tests__/v3.test.js +97 -9
  46. package/lib/projects/create/index.js +2 -2
  47. package/lib/projects/create/legacy.js +1 -1
  48. package/lib/projects/create/v3.d.ts +2 -2
  49. package/lib/projects/create/v3.js +35 -12
  50. package/lib/projects/deploy.d.ts +13 -0
  51. package/lib/projects/deploy.js +63 -0
  52. package/lib/projects/localDev/AppDevModeInterface.d.ts +5 -3
  53. package/lib/projects/localDev/AppDevModeInterface.js +110 -47
  54. package/lib/projects/localDev/DevServerManagerV2.js +1 -0
  55. package/lib/projects/localDev/LocalDevProcess.js +3 -1
  56. package/lib/projects/localDev/LocalDevState.d.ts +5 -2
  57. package/lib/projects/localDev/LocalDevState.js +9 -1
  58. package/lib/projects/localDev/helpers/project.d.ts +2 -2
  59. package/lib/projects/localDev/helpers/project.js +6 -8
  60. package/lib/projects/platformVersion.d.ts +1 -0
  61. package/lib/projects/platformVersion.js +10 -0
  62. package/lib/projects/{buildAndDeploy.d.ts → pollProjectBuildAndDeploy.d.ts} +0 -1
  63. package/lib/projects/{buildAndDeploy.js → pollProjectBuildAndDeploy.js} +0 -10
  64. package/lib/projects/upload.js +1 -1
  65. package/lib/projects/urls.d.ts +1 -0
  66. package/lib/projects/urls.js +3 -0
  67. package/lib/prompts/__tests__/projectAddPrompt.test.d.ts +1 -0
  68. package/lib/prompts/__tests__/projectAddPrompt.test.js +143 -0
  69. package/lib/prompts/__tests__/selectProjectTemplatePrompt.test.d.ts +1 -0
  70. package/lib/prompts/__tests__/selectProjectTemplatePrompt.test.js +160 -0
  71. package/lib/prompts/createDeveloperTestAccountConfigPrompt.js +1 -0
  72. package/lib/prompts/importDataFilePathPrompt.js +4 -2
  73. package/lib/prompts/installAppPrompt.d.ts +6 -1
  74. package/lib/prompts/installAppPrompt.js +6 -1
  75. package/lib/prompts/projectAddPrompt.js +1 -1
  76. package/lib/prompts/promptUtils.d.ts +5 -0
  77. package/lib/prompts/promptUtils.js +9 -0
  78. package/lib/prompts/selectProjectTemplatePrompt.js +1 -1
  79. package/lib/ui/index.js +3 -6
  80. package/mcp-server/tools/cms/HsCreateFunctionTool.d.ts +32 -0
  81. package/mcp-server/tools/cms/HsCreateFunctionTool.js +96 -0
  82. package/mcp-server/tools/cms/HsCreateModuleTool.d.ts +38 -0
  83. package/mcp-server/tools/cms/HsCreateModuleTool.js +118 -0
  84. package/mcp-server/tools/cms/HsCreateTemplateTool.d.ts +26 -0
  85. package/mcp-server/tools/cms/HsCreateTemplateTool.js +75 -0
  86. package/mcp-server/tools/cms/HsFunctionLogsTool.d.ts +32 -0
  87. package/mcp-server/tools/cms/HsFunctionLogsTool.js +76 -0
  88. package/mcp-server/tools/cms/HsListFunctionsTool.d.ts +23 -0
  89. package/mcp-server/tools/cms/HsListFunctionsTool.js +58 -0
  90. package/mcp-server/tools/cms/HsListTool.js +1 -1
  91. package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.d.ts +1 -0
  92. package/mcp-server/tools/cms/__tests__/HsCreateFunctionTool.test.js +251 -0
  93. package/mcp-server/tools/cms/__tests__/HsCreateModuleTool.test.d.ts +1 -0
  94. package/mcp-server/tools/cms/__tests__/HsCreateModuleTool.test.js +224 -0
  95. package/mcp-server/tools/cms/__tests__/HsCreateTemplateTool.test.d.ts +1 -0
  96. package/mcp-server/tools/cms/__tests__/HsCreateTemplateTool.test.js +206 -0
  97. package/mcp-server/tools/cms/__tests__/HsFunctionLogsTool.test.d.ts +1 -0
  98. package/mcp-server/tools/cms/__tests__/HsFunctionLogsTool.test.js +183 -0
  99. package/mcp-server/tools/cms/__tests__/HsListFunctionsTool.test.d.ts +1 -0
  100. package/mcp-server/tools/cms/__tests__/HsListFunctionsTool.test.js +120 -0
  101. package/mcp-server/tools/cms/__tests__/HsListTool.test.js +1 -1
  102. package/mcp-server/tools/index.js +13 -1
  103. package/mcp-server/tools/project/AddFeatureToProjectTool.d.ts +3 -3
  104. package/mcp-server/tools/project/AddFeatureToProjectTool.js +3 -3
  105. package/mcp-server/tools/project/CreateProjectTool.d.ts +3 -3
  106. package/mcp-server/tools/project/CreateProjectTool.js +5 -5
  107. package/mcp-server/tools/project/DeployProjectTool.js +1 -1
  108. package/mcp-server/tools/project/DocFetchTool.js +2 -2
  109. package/mcp-server/tools/project/DocsSearchTool.js +2 -2
  110. package/mcp-server/tools/project/GetConfigValuesTool.js +4 -4
  111. package/mcp-server/tools/project/GuidedWalkthroughTool.js +1 -1
  112. package/mcp-server/tools/project/UploadProjectTools.js +2 -2
  113. package/mcp-server/tools/project/ValidateProjectTool.js +1 -1
  114. package/mcp-server/tools/project/__tests__/AddFeatureToProjectTool.test.js +1 -1
  115. package/mcp-server/tools/project/__tests__/CreateProjectTool.test.js +1 -1
  116. package/mcp-server/tools/project/__tests__/DeployProjectTool.test.js +1 -1
  117. package/mcp-server/tools/project/__tests__/DocFetchTool.test.js +2 -2
  118. package/mcp-server/tools/project/__tests__/DocsSearchTool.test.js +2 -2
  119. package/mcp-server/tools/project/__tests__/GetConfigValuesTool.test.js +1 -1
  120. package/mcp-server/tools/project/__tests__/GuidedWalkthroughTool.test.js +1 -1
  121. package/mcp-server/tools/project/__tests__/UploadProjectTools.test.js +1 -1
  122. package/mcp-server/tools/project/__tests__/ValidateProjectTool.test.js +1 -1
  123. package/mcp-server/tools/project/constants.d.ts +1 -1
  124. package/mcp-server/tools/project/constants.js +14 -6
  125. package/package.json +4 -3
  126. package/types/LocalDev.d.ts +2 -1
  127. package/types/Projects.d.ts +1 -0
  128. package/ui/components/BoxWithTitle.d.ts +8 -0
  129. package/ui/components/BoxWithTitle.js +9 -0
  130. package/ui/components/HorizontalSelectPrompt.d.ts +8 -0
  131. package/ui/components/HorizontalSelectPrompt.js +30 -0
  132. package/ui/components/StatusMessageBoxes.d.ts +12 -0
  133. package/ui/components/StatusMessageBoxes.js +31 -0
  134. package/ui/lib/ui-testing-utils.d.ts +9 -0
  135. package/ui/lib/ui-testing-utils.js +47 -0
  136. package/ui/lib/useTerminalSize.d.ts +13 -0
  137. package/ui/lib/useTerminalSize.js +31 -0
  138. package/ui/styles.d.ts +18 -0
  139. package/ui/styles.js +18 -0
  140. package/ui/views/UiSandbox.d.ts +5 -0
  141. package/ui/views/UiSandbox.js +25 -0
  142. /package/lib/projects/__tests__/{buildAndDeploy.test.d.ts → deploy.test.d.ts} +0 -0
@@ -10,11 +10,11 @@ const inputSchema = {
10
10
  absoluteCurrentWorkingDirectory,
11
11
  name: z
12
12
  .string()
13
- .describe('The name of the project to be created. This name is how your project will appear in HubSpot. If not specified by the user, do not choose for them. Changing this is potentially destructive.')
13
+ .describe('If not specified by the user, DO NOT choose for them. Changing this is potentially destructive.The name of the project to be created. This name is how your project will appear in HubSpot. ')
14
14
  .optional(),
15
15
  destination: z
16
16
  .string()
17
- .describe('Relative path to the directory the project will be created in. DO NOT use the current directory unless the user has explicitly stated to do so.'),
17
+ .describe('DO NOT use the current directory unless the user has explicitly stated to do so. Relative path to the directory the project will be created in.'),
18
18
  projectBase: z
19
19
  .union([z.literal(EMPTY_PROJECT), z.literal(PROJECT_WITH_APP)])
20
20
  .describe('Empty will create an empty project, and app will create a project with an app inside of it.'),
@@ -23,19 +23,19 @@ const inputSchema = {
23
23
  z.literal(APP_DISTRIBUTION_TYPES.MARKETPLACE),
24
24
  z.literal(APP_DISTRIBUTION_TYPES.PRIVATE),
25
25
  ]))
26
- .describe('Private is used if you do not wish to distribute your application on the HubSpot marketplace. If not specified by the user, do not choose for them. This cannot be changed after a project is uploaded.'),
26
+ .describe('If not specified by the user, DO NOT choose for them. This cannot be changed after a project is uploaded. Private is used if you do not wish to distribute your application on the HubSpot marketplace. '),
27
27
  auth: z
28
28
  .optional(z.union([
29
29
  z.literal(APP_AUTH_TYPES.STATIC),
30
30
  z.literal(APP_AUTH_TYPES.OAUTH),
31
31
  ]))
32
- .describe('Static uses a static non changing authentication token, and is only available for private distribution. If not specified by the user, do not choose for them. This cannot be changed after a project is uploaded.')
32
+ .describe('If not specified by the user, DO NOT choose for them. This cannot be changed after a project is uploaded. Static uses a static non changing authentication token, and is only available for private distribution. ')
33
33
  .optional(),
34
34
  features,
35
35
  };
36
36
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
37
37
  const inputSchemaZodObject = z.object({ ...inputSchema });
38
- const toolName = 'create-hubspot-project';
38
+ const toolName = 'create-project';
39
39
  export class CreateProjectTool extends Tool {
40
40
  constructor(mcpServer) {
41
41
  super(mcpServer);
@@ -15,7 +15,7 @@ const inputSchema = {
15
15
  const inputSchemaZodObject = z.object({
16
16
  ...inputSchema,
17
17
  });
18
- const toolName = 'deploy-hubspot-project';
18
+ const toolName = 'deploy-project';
19
19
  export class DeployProjectTool extends Tool {
20
20
  constructor(mcpServer) {
21
21
  super(mcpServer);
@@ -12,7 +12,7 @@ const inputSchema = {
12
12
  const inputSchemaZodObject = z.object({
13
13
  ...inputSchema,
14
14
  });
15
- const toolName = 'fetch-hubspot-doc';
15
+ const toolName = 'fetch-doc';
16
16
  export class DocFetchTool extends Tool {
17
17
  constructor(mcpServer) {
18
18
  super(mcpServer);
@@ -42,7 +42,7 @@ export class DocFetchTool extends Tool {
42
42
  register() {
43
43
  return this.mcpServer.registerTool(toolName, {
44
44
  title: 'Fetch HubSpot Developer Documentation (single file)',
45
- 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.',
45
+ 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.',
46
46
  inputSchema,
47
47
  }, this.handler);
48
48
  }
@@ -13,7 +13,7 @@ const inputSchema = {
13
13
  const inputSchemaZodObject = z.object({
14
14
  ...inputSchema,
15
15
  });
16
- const toolName = 'search-hubspot-docs';
16
+ const toolName = 'search-docs';
17
17
  export class DocsSearchTool extends Tool {
18
18
  constructor(mcpServer) {
19
19
  super(mcpServer);
@@ -55,7 +55,7 @@ export class DocsSearchTool extends Tool {
55
55
  register() {
56
56
  return this.mcpServer.registerTool(toolName, {
57
57
  title: 'Search HubSpot Developer Documentation',
58
- 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.',
58
+ 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.',
59
59
  inputSchema,
60
60
  }, this.handler);
61
61
  }
@@ -3,7 +3,7 @@ import { z } from 'zod';
3
3
  import { formatTextContents } from '../../utils/content.js';
4
4
  import { getIntermediateRepresentationSchema, mapToInternalType, } from '@hubspot/project-parsing-lib';
5
5
  import { getAccountId, getConfigPath, loadConfig, } from '@hubspot/local-dev-lib/config';
6
- import { useV3Api } from '../../../lib/projects/buildAndDeploy.js';
6
+ import { useV3Api } from '../../../lib/projects/platformVersion.js';
7
7
  const inputSchema = {
8
8
  platformVersion: z
9
9
  .string()
@@ -16,7 +16,7 @@ const inputSchema = {
16
16
  const inputSchemaZodObject = z.object({
17
17
  ...inputSchema,
18
18
  });
19
- const toolName = 'get-hubspot-feature-config-schema';
19
+ const toolName = 'get-feature-config-schema';
20
20
  export class GetConfigValuesTool extends Tool {
21
21
  constructor(mcpServer) {
22
22
  super(mcpServer);
@@ -43,8 +43,8 @@ export class GetConfigValuesTool extends Tool {
43
43
  register() {
44
44
  return this.mcpServer.registerTool(toolName, {
45
45
  title: 'Fetch the JSON Schema for component',
46
- description: `Fetches and returns the JSON schema for the provided feature 'type' found in -hsmeta.json file.
47
- This should be called before editing a '-hsmeta.json' file to get the list of possible values and restrictions on those values.
46
+ description: `Fetches and returns the JSON schema for the provided feature 'type' found in -hsmeta.json file.
47
+ This should be called before editing a '-hsmeta.json' file to get the list of possible values and restrictions on those values.
48
48
  This will only work for projects with platformVersion 2025.2 and beyond`,
49
49
  inputSchema,
50
50
  }, this.handler);
@@ -24,7 +24,7 @@ const inputSchema = {
24
24
  const inputSchemaZodObject = z.object({
25
25
  ...inputSchema,
26
26
  });
27
- const toolName = 'guided-walkthrough-hubspot-cli';
27
+ const toolName = 'guided-walkthrough-cli';
28
28
  export class GuidedWalkthroughTool extends Tool {
29
29
  constructor(mcpServer) {
30
30
  super(mcpServer);
@@ -11,7 +11,7 @@ const inputSchema = {
11
11
  const inputSchemaZodObject = z.object({
12
12
  ...inputSchema,
13
13
  });
14
- const toolName = 'upload-hubspot-project';
14
+ const toolName = 'upload-project';
15
15
  export class UploadProjectTools extends Tool {
16
16
  constructor(mcpServer) {
17
17
  super(mcpServer);
@@ -24,7 +24,7 @@ export class UploadProjectTools extends Tool {
24
24
  register() {
25
25
  return this.mcpServer.registerTool(toolName, {
26
26
  title: 'Upload HubSpot Project',
27
- description: 'Uploads the HubSpot project in current working directory. If the project does not exist, it will be created. MUST be ran from within the project directory. DO NOT run this tool unless the user specifies they would like to upload the project, it is potentially destructive',
27
+ description: 'DO NOT run this tool unless the user specifies they would like to upload the project, it is potentially destructive. Uploads the HubSpot project in current working directory. If the project does not exist, it will be created. MUST be ran from within the project directory.',
28
28
  inputSchema,
29
29
  }, this.handler);
30
30
  }
@@ -9,7 +9,7 @@ const inputSchema = {
9
9
  };
10
10
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
11
11
  const inputSchemaZodObject = z.object({ ...inputSchema });
12
- const toolName = 'validate-hubspot-project';
12
+ const toolName = 'validate-project';
13
13
  export class ValidateProjectTool extends Tool {
14
14
  constructor(mcpServer) {
15
15
  super(mcpServer);
@@ -28,7 +28,7 @@ describe('mcp-server/tools/project/AddFeatureToProject', () => {
28
28
  describe('register', () => {
29
29
  it('should register tool with correct parameters', () => {
30
30
  const result = tool.register();
31
- expect(mockMcpServer.registerTool).toHaveBeenCalledWith('add-feature-to-hubspot-project', expect.objectContaining({
31
+ expect(mockMcpServer.registerTool).toHaveBeenCalledWith('add-feature-to-project', expect.objectContaining({
32
32
  title: 'Add feature to HubSpot Project',
33
33
  description: expect.stringContaining('Adds a feature to an existing HubSpot project'),
34
34
  inputSchema: expect.any(Object),
@@ -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);
@@ -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);
@@ -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({
@@ -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">]>, "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;
@@ -6,15 +6,23 @@ export const absoluteCurrentWorkingDirectory = z
6
6
  .string()
7
7
  .describe('The absolute path to the current working directory.');
8
8
  export const features = z
9
- .array(z
10
- .union([
9
+ .array(z.union([
11
10
  z.literal('card'),
12
11
  z.literal('settings'),
13
- z.literal('app-function'),
12
+ z
13
+ .literal('app-function')
14
+ .describe('Also known as a public serverless function'),
14
15
  z.literal('webhooks'),
15
- z.literal('workflow-action'),
16
- ])
17
- .describe('The features to include in the project, multiple options can be selected'))
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.'),
20
+ z.literal('app-object'),
21
+ z.literal('app-event'),
22
+ z.literal('scim'),
23
+ z.literal('page'),
24
+ ]))
25
+ .describe('The features to include in the project, multiple options can be selected')
18
26
  .optional();
19
27
  export const docsSearchQuery = z
20
28
  .string()
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@hubspot/cli",
3
- "version": "7.6.0-beta.11",
3
+ "version": "7.6.0-beta.12",
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.17.0",
10
- "@hubspot/project-parsing-lib": "0.8.0",
9
+ "@hubspot/local-dev-lib": "3.18.0",
10
+ "@hubspot/project-parsing-lib": "0.8.3",
11
11
  "@hubspot/serverless-dev-runtime": "7.0.6",
12
12
  "@hubspot/theme-preview-dev-server": "0.0.10",
13
13
  "@hubspot/ui-extensions-dev-server": "0.9.8",
@@ -84,6 +84,7 @@
84
84
  "mcp-local": "yarn tsx ./scripts/mcp-local.ts",
85
85
  "prettier:write": "prettier --write './**/*.{ts,js,json}'",
86
86
  "release": "yarn tsx ./scripts/release.ts release",
87
+ "view-ui": "yarn build && yarn tsx ./scripts/view-ui.ts",
87
88
  "test": "vitest run",
88
89
  "test-dev": "vitest",
89
90
  "test-cli": "yarn build && yarn --cwd 'acceptance-tests' test-ci",
@@ -1,4 +1,4 @@
1
- import { IntermediateRepresentationNodeLocalDev } from '@hubspot/project-parsing-lib/src/lib/types.js';
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
4
  import { ProjectConfig } from './Projects.js';
@@ -16,6 +16,7 @@ export type LocalDevStateConstructorOptions = {
16
16
  initialProjectNodes: {
17
17
  [key: string]: IntermediateRepresentationNodeLocalDev;
18
18
  };
19
+ initialProjectProfileData: HSProfileVariables;
19
20
  env: Environment;
20
21
  };
21
22
  export type LocalDevWebsocketMessage = {
@@ -10,6 +10,7 @@ export type ComponentTemplate = {
10
10
  path: string;
11
11
  label: string;
12
12
  type: string;
13
+ cliSelector?: string;
13
14
  parentComponent?: string;
14
15
  supportedAuthTypes?: string[];
15
16
  supportedDistributions?: string[];
@@ -0,0 +1,8 @@
1
+ export interface BoxWithTitleProps {
2
+ title: string;
3
+ message: string;
4
+ titleBackgroundColor?: string;
5
+ borderColor?: string;
6
+ }
7
+ export declare function getBoxWithTitle(props: BoxWithTitleProps): React.ReactNode;
8
+ export declare function BoxWithTitle({ title, message, titleBackgroundColor, borderColor, }: BoxWithTitleProps): React.ReactNode;
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text } from 'ink';
3
+ import { CONTAINER_STYLES } from '../styles.js';
4
+ export function getBoxWithTitle(props) {
5
+ return _jsx(BoxWithTitle, { ...props });
6
+ }
7
+ export function BoxWithTitle({ title, message, titleBackgroundColor, borderColor, }) {
8
+ return (_jsxs(Box, { ...CONTAINER_STYLES, borderStyle: "round", borderColor: borderColor, children: [_jsx(Box, { position: "absolute", marginTop: -2, paddingX: 0, alignSelf: "flex-start", justifyContent: "center", alignItems: "center", children: _jsx(Text, { backgroundColor: titleBackgroundColor, bold: true, children: ` ${title} ` }) }), _jsx(Box, { justifyContent: "center", alignItems: "center", children: _jsx(Text, { children: message }) })] }));
9
+ }
@@ -0,0 +1,8 @@
1
+ export interface HorizontalSelectPromptProps {
2
+ defaultOption?: string;
3
+ options: string[];
4
+ onSelect: (value: string) => void;
5
+ prompt?: string;
6
+ }
7
+ export declare function getHorizontalSelectPrompt(props: HorizontalSelectPromptProps): React.ReactNode;
8
+ export declare function HorizontalSelectPrompt({ defaultOption, options, onSelect, prompt, }: HorizontalSelectPromptProps): React.ReactNode;
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text, useInput } from 'ink';
3
+ import { useState } from 'react';
4
+ import { CONTAINER_STYLES, INK_COLORS } from '../styles.js';
5
+ export function getHorizontalSelectPrompt(props) {
6
+ return _jsx(HorizontalSelectPrompt, { ...props });
7
+ }
8
+ export function HorizontalSelectPrompt({ defaultOption, options, onSelect, prompt, }) {
9
+ const [selectedIndex, setSelectedIndex] = useState(defaultOption && options.indexOf(defaultOption) !== -1
10
+ ? options.indexOf(defaultOption)
11
+ : 0);
12
+ const moveRight = () => {
13
+ setSelectedIndex(prev => (prev < options.length - 1 ? prev + 1 : 0));
14
+ };
15
+ const moveLeft = () => {
16
+ setSelectedIndex(prev => (prev > 0 ? prev - 1 : options.length - 1));
17
+ };
18
+ useInput((_, key) => {
19
+ if (key.leftArrow) {
20
+ moveLeft();
21
+ }
22
+ else if (key.rightArrow) {
23
+ moveRight();
24
+ }
25
+ else if (key.return) {
26
+ onSelect(options[selectedIndex]);
27
+ }
28
+ });
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
+ }
@@ -0,0 +1,12 @@
1
+ export interface StatusMessageBoxProps {
2
+ title: string;
3
+ message: string;
4
+ }
5
+ export declare function getWarningBox(props: StatusMessageBoxProps): React.ReactNode;
6
+ export declare function WarningBox({ title, message, }: StatusMessageBoxProps): React.ReactNode;
7
+ export declare function getAlertBox(props: StatusMessageBoxProps): React.ReactNode;
8
+ export declare function AlertBox({ title, message, }: StatusMessageBoxProps): React.ReactNode;
9
+ export declare function getSuccessBox(props: StatusMessageBoxProps): React.ReactNode;
10
+ export declare function SuccessBox({ title, message, }: StatusMessageBoxProps): React.ReactNode;
11
+ export declare function getInfoBox(props: StatusMessageBoxProps): React.ReactNode;
12
+ export declare function InfoBox({ title, message, }: StatusMessageBoxProps): React.ReactNode;
@@ -0,0 +1,31 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { INK_COLORS } from '../styles.js';
3
+ import { BoxWithTitle } from './BoxWithTitle.js';
4
+ export function getWarningBox(props) {
5
+ return _jsx(WarningBox, { ...props });
6
+ }
7
+ export function WarningBox({ title, message, }) {
8
+ const color = INK_COLORS.WARNING_YELLOW;
9
+ return (_jsx(BoxWithTitle, { title: title, message: message, titleBackgroundColor: color, borderColor: color }));
10
+ }
11
+ export function getAlertBox(props) {
12
+ return _jsx(AlertBox, { ...props });
13
+ }
14
+ export function AlertBox({ title, message, }) {
15
+ const color = INK_COLORS.ALERT_RED;
16
+ return (_jsx(BoxWithTitle, { title: title, message: message, titleBackgroundColor: color, borderColor: color }));
17
+ }
18
+ export function getSuccessBox(props) {
19
+ return _jsx(SuccessBox, { ...props });
20
+ }
21
+ export function SuccessBox({ title, message, }) {
22
+ const color = INK_COLORS.SUCCESS_GREEN;
23
+ return (_jsx(BoxWithTitle, { title: title, message: message, titleBackgroundColor: color, borderColor: color }));
24
+ }
25
+ export function getInfoBox(props) {
26
+ return _jsx(InfoBox, { ...props });
27
+ }
28
+ export function InfoBox({ title, message, }) {
29
+ const color = INK_COLORS.INFO_BLUE;
30
+ return (_jsx(BoxWithTitle, { title: title, message: message, titleBackgroundColor: color, borderColor: color }));
31
+ }
@@ -0,0 +1,9 @@
1
+ export type ComponentPropPair = {
2
+ component: React.ReactNode;
3
+ signature: string;
4
+ };
5
+ /**
6
+ * These components will be used by the sandbox ui. Please add any new components here.
7
+ */
8
+ export declare const populatedComponents: Record<string, ComponentPropPair>;
9
+ export declare function getComponentOptions(): string[];
@@ -0,0 +1,47 @@
1
+ import { getSuccessBox, getInfoBox, getWarningBox, getAlertBox, } from '../components/StatusMessageBoxes.js';
2
+ import { getBoxWithTitle } from '../components/BoxWithTitle.js';
3
+ import { SuccessBox, InfoBox, WarningBox, AlertBox, } from '../components/StatusMessageBoxes.js';
4
+ import { BoxWithTitle } from '../components/BoxWithTitle.js';
5
+ /**
6
+ * These components will be used by the sandbox ui. Please add any new components here.
7
+ */
8
+ export const populatedComponents = {
9
+ SuccessBox: {
10
+ component: getSuccessBox({
11
+ title: 'Success',
12
+ message: 'This is a success message',
13
+ }),
14
+ signature: SuccessBox.toString(),
15
+ },
16
+ InfoBox: {
17
+ component: getInfoBox({
18
+ title: 'Info',
19
+ message: 'This is an info message',
20
+ }),
21
+ signature: InfoBox.toString(),
22
+ },
23
+ WarningBox: {
24
+ component: getWarningBox({
25
+ title: 'Warning',
26
+ message: 'This is a warning message',
27
+ }),
28
+ signature: WarningBox.toString(),
29
+ },
30
+ AlertBox: {
31
+ component: getAlertBox({
32
+ title: 'Alert',
33
+ message: 'This is an alert message',
34
+ }),
35
+ signature: AlertBox.toString(),
36
+ },
37
+ BoxWithTitle: {
38
+ component: getBoxWithTitle({
39
+ title: 'Title',
40
+ message: 'This is a box with a title',
41
+ }),
42
+ signature: BoxWithTitle.toString(),
43
+ },
44
+ };
45
+ export function getComponentOptions() {
46
+ return Object.keys(populatedComponents);
47
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * This hook is used to get the current terminal size.
3
+ * It will return the current terminal size, and if a component is using this hook to set height/width,
4
+ * it will re-render when the terminal size changes.
5
+ * ONLY USE THIS HOOK WITH SCREENS. THIS HOOK WILL DESTROY ANY PRIOR LOG OUTPUT IF USED TO SET HEIGHT/WIDTH.
6
+ * @param minHeight - The minimum height of the terminal.
7
+ * @param minWidth - The minimum width of the terminal.
8
+ * @returns The current terminal size.
9
+ */
10
+ export declare function useTerminalSize(minHeight?: number, minWidth?: number): {
11
+ columns: number;
12
+ rows: number;
13
+ };
@@ -0,0 +1,31 @@
1
+ import { useStdout } from 'ink';
2
+ import { useEffect, useState } from 'react';
3
+ /**
4
+ * This hook is used to get the current terminal size.
5
+ * It will return the current terminal size, and if a component is using this hook to set height/width,
6
+ * it will re-render when the terminal size changes.
7
+ * ONLY USE THIS HOOK WITH SCREENS. THIS HOOK WILL DESTROY ANY PRIOR LOG OUTPUT IF USED TO SET HEIGHT/WIDTH.
8
+ * @param minHeight - The minimum height of the terminal.
9
+ * @param minWidth - The minimum width of the terminal.
10
+ * @returns The current terminal size.
11
+ */
12
+ export function useTerminalSize(minHeight, minWidth) {
13
+ const { stdout } = useStdout();
14
+ const [size, setSize] = useState({
15
+ columns: Math.max(stdout.columns, minWidth ?? 0),
16
+ rows: Math.max(stdout.rows, minHeight ?? 0),
17
+ });
18
+ useEffect(() => {
19
+ const handleResize = () => {
20
+ setSize({
21
+ columns: Math.max(stdout.columns, minWidth ?? 0),
22
+ rows: Math.max(stdout.rows, minHeight ?? 0),
23
+ });
24
+ };
25
+ stdout.on('resize', handleResize);
26
+ return () => {
27
+ stdout.off('resize', handleResize);
28
+ };
29
+ }, [stdout]);
30
+ return size;
31
+ }
package/ui/styles.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ export declare const CONTAINER_STYLES: {
2
+ readonly padding: 1;
3
+ readonly marginY: 0.5;
4
+ readonly flexDirection: "column";
5
+ readonly flexWrap: "wrap";
6
+ };
7
+ /**
8
+ * Any color that can be used well in both WHITE and BLACK terminals.
9
+ * This is a best effort to ensure that the color is good looking in both
10
+ * light and dark modes.
11
+ */
12
+ export declare const INK_COLORS: {
13
+ ALERT_RED: string;
14
+ SUCCESS_GREEN: string;
15
+ INFO_BLUE: string;
16
+ WARNING_YELLOW: string;
17
+ WHITE: string;
18
+ };
package/ui/styles.js ADDED
@@ -0,0 +1,18 @@
1
+ export const CONTAINER_STYLES = {
2
+ padding: 1,
3
+ marginY: 0.5,
4
+ flexDirection: 'column',
5
+ flexWrap: 'wrap',
6
+ };
7
+ /**
8
+ * Any color that can be used well in both WHITE and BLACK terminals.
9
+ * This is a best effort to ensure that the color is good looking in both
10
+ * light and dark modes.
11
+ */
12
+ export const INK_COLORS = {
13
+ ALERT_RED: '#fc7272',
14
+ SUCCESS_GREEN: '#4deb7a',
15
+ INFO_BLUE: '#4dcbeb',
16
+ WARNING_YELLOW: '#EEB117',
17
+ WHITE: 'white',
18
+ };
@@ -0,0 +1,5 @@
1
+ export type UiSandboxProps = {
2
+ componentName: string | undefined;
3
+ };
4
+ export declare function getUiSandbox(props: UiSandboxProps): React.ReactNode;
5
+ export declare function UiSandbox({ componentName }: UiSandboxProps): React.ReactNode;