@hubspot/cli 7.7.27-experimental.2 → 7.7.29-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 (130) hide show
  1. package/README.md +0 -4
  2. package/api/__tests__/migrate.test.js +5 -5
  3. package/api/migrate.d.ts +10 -4
  4. package/api/migrate.js +2 -2
  5. package/commands/__tests__/create.test.js +20 -0
  6. package/commands/__tests__/testAccount.test.js +2 -0
  7. package/commands/app/__tests__/migrate.test.js +1 -0
  8. package/commands/create/function.js +2 -2
  9. package/commands/create/module.js +2 -2
  10. package/commands/create/template.js +2 -2
  11. package/commands/create.js +47 -0
  12. package/commands/getStarted.js +66 -4
  13. package/commands/mcp/setup.d.ts +0 -1
  14. package/commands/mcp/setup.js +3 -11
  15. package/commands/project/__tests__/create.test.js +57 -0
  16. package/commands/project/__tests__/devUnifiedFlow.test.js +18 -30
  17. package/commands/project/create.js +6 -1
  18. package/commands/project/deploy.js +31 -1
  19. package/commands/project/dev/deprecatedFlow.js +2 -1
  20. package/commands/project/dev/index.js +32 -12
  21. package/commands/project/dev/unifiedFlow.d.ts +1 -1
  22. package/commands/project/dev/unifiedFlow.js +10 -16
  23. package/commands/project/profile/delete.js +26 -14
  24. package/commands/testAccount/__tests__/importData.test.d.ts +1 -0
  25. package/commands/testAccount/__tests__/importData.test.js +93 -0
  26. package/commands/testAccount/create.js +23 -13
  27. package/commands/testAccount/importData.d.ts +9 -0
  28. package/commands/testAccount/importData.js +61 -0
  29. package/commands/testAccount.js +2 -0
  30. package/lang/en.d.ts +162 -46
  31. package/lang/en.js +177 -59
  32. package/lang/en.lyaml +35 -14
  33. package/lib/__tests__/importData.test.d.ts +1 -0
  34. package/lib/__tests__/importData.test.js +89 -0
  35. package/lib/accountTypes.js +2 -3
  36. package/lib/app/__tests__/migrate.test.js +81 -36
  37. package/lib/app/migrate.d.ts +17 -4
  38. package/lib/app/migrate.js +97 -19
  39. package/lib/constants.d.ts +1 -0
  40. package/lib/constants.js +1 -0
  41. package/lib/hasFeature.d.ts +1 -0
  42. package/lib/hasFeature.js +7 -0
  43. package/lib/importData.d.ts +3 -0
  44. package/lib/importData.js +50 -0
  45. package/lib/mcp/setup.d.ts +3 -5
  46. package/lib/mcp/setup.js +39 -139
  47. package/lib/process.js +15 -4
  48. package/lib/projects/__tests__/AppDevModeInterface.test.js +3 -3
  49. package/lib/projects/__tests__/LocalDevProcess.test.js +5 -95
  50. package/lib/projects/__tests__/LocalDevWebsocketServer.test.js +6 -6
  51. package/lib/projects/__tests__/components.test.js +164 -7
  52. package/lib/projects/__tests__/localDevProjectHelpers.test.d.ts +1 -0
  53. package/lib/projects/__tests__/localDevProjectHelpers.test.js +118 -0
  54. package/lib/projects/add/v3AddComponent.js +16 -4
  55. package/lib/projects/components.d.ts +1 -0
  56. package/lib/projects/components.js +27 -1
  57. package/lib/projects/localDev/AppDevModeInterface.js +35 -3
  58. package/lib/projects/localDev/LocalDevLogger.d.ts +0 -4
  59. package/lib/projects/localDev/LocalDevLogger.js +2 -19
  60. package/lib/projects/localDev/LocalDevManager.js +1 -1
  61. package/lib/projects/localDev/LocalDevProcess.d.ts +1 -2
  62. package/lib/projects/localDev/LocalDevProcess.js +3 -26
  63. package/lib/projects/localDev/LocalDevState.d.ts +6 -7
  64. package/lib/projects/localDev/LocalDevState.js +16 -15
  65. package/lib/projects/localDev/LocalDevWebsocketServer.d.ts +1 -0
  66. package/lib/projects/localDev/LocalDevWebsocketServer.js +17 -2
  67. package/lib/projects/localDev/{helpers.d.ts → helpers/account.d.ts} +1 -7
  68. package/lib/projects/localDev/{helpers.js → helpers/account.js} +44 -144
  69. package/lib/projects/localDev/helpers/project.d.ts +12 -0
  70. package/lib/projects/localDev/helpers/project.js +173 -0
  71. package/lib/projects/urls.d.ts +1 -0
  72. package/lib/projects/urls.js +4 -0
  73. package/lib/prompts/__tests__/createFunctionPrompt.test.d.ts +1 -0
  74. package/lib/prompts/__tests__/createFunctionPrompt.test.js +129 -0
  75. package/lib/prompts/__tests__/createModulePrompt.test.d.ts +1 -0
  76. package/lib/prompts/__tests__/createModulePrompt.test.js +187 -0
  77. package/lib/prompts/__tests__/createTemplatePrompt.test.d.ts +1 -0
  78. package/lib/prompts/__tests__/createTemplatePrompt.test.js +102 -0
  79. package/lib/prompts/confirmImportDataPrompt.d.ts +1 -0
  80. package/lib/prompts/confirmImportDataPrompt.js +12 -0
  81. package/lib/prompts/createFunctionPrompt.d.ts +2 -1
  82. package/lib/prompts/createFunctionPrompt.js +36 -7
  83. package/lib/prompts/createModulePrompt.d.ts +2 -1
  84. package/lib/prompts/createModulePrompt.js +48 -1
  85. package/lib/prompts/createTemplatePrompt.d.ts +3 -24
  86. package/lib/prompts/createTemplatePrompt.js +9 -1
  87. package/lib/prompts/importDataFilePathPrompt.d.ts +1 -0
  88. package/lib/prompts/importDataFilePathPrompt.js +24 -0
  89. package/lib/prompts/importDataTestAccountSelectPrompt.d.ts +3 -0
  90. package/lib/prompts/importDataTestAccountSelectPrompt.js +29 -0
  91. package/lib/prompts/projectDevTargetAccountPrompt.js +1 -0
  92. package/lib/prompts/promptUtils.d.ts +7 -1
  93. package/lib/prompts/promptUtils.js +14 -1
  94. package/lib/ui/__tests__/removeAnsiCodes.test.d.ts +1 -0
  95. package/lib/ui/__tests__/removeAnsiCodes.test.js +84 -0
  96. package/lib/ui/index.js +3 -6
  97. package/lib/ui/removeAnsiCodes.d.ts +1 -0
  98. package/lib/ui/removeAnsiCodes.js +4 -0
  99. package/mcp-server/server.js +2 -1
  100. package/mcp-server/tools/cms/HsCreateModuleTool.d.ts +38 -0
  101. package/mcp-server/tools/cms/HsCreateModuleTool.js +118 -0
  102. package/mcp-server/tools/cms/HsListTool.d.ts +23 -0
  103. package/mcp-server/tools/cms/HsListTool.js +58 -0
  104. package/mcp-server/tools/cms/__tests__/HsCreateModuleTool.test.d.ts +1 -0
  105. package/mcp-server/tools/cms/__tests__/HsCreateModuleTool.test.js +224 -0
  106. package/mcp-server/tools/cms/__tests__/HsListTool.test.d.ts +1 -0
  107. package/mcp-server/tools/cms/__tests__/HsListTool.test.js +120 -0
  108. package/mcp-server/tools/index.d.ts +1 -0
  109. package/mcp-server/tools/index.js +12 -0
  110. package/mcp-server/tools/project/DocFetchTool.d.ts +17 -0
  111. package/mcp-server/tools/project/DocFetchTool.js +49 -0
  112. package/mcp-server/tools/project/DocsSearchTool.d.ts +26 -0
  113. package/mcp-server/tools/project/DocsSearchTool.js +62 -0
  114. package/mcp-server/tools/project/GetConfigValuesTool.js +3 -2
  115. package/mcp-server/tools/project/__tests__/DocFetchTool.test.d.ts +1 -0
  116. package/mcp-server/tools/project/__tests__/DocFetchTool.test.js +117 -0
  117. package/mcp-server/tools/project/__tests__/DocsSearchTool.test.d.ts +1 -0
  118. package/mcp-server/tools/project/__tests__/DocsSearchTool.test.js +190 -0
  119. package/mcp-server/tools/project/__tests__/GetConfigValuesTool.test.js +1 -1
  120. package/mcp-server/tools/project/constants.d.ts +2 -0
  121. package/mcp-server/tools/project/constants.js +6 -0
  122. package/mcp-server/utils/toolUsageTracking.d.ts +3 -1
  123. package/mcp-server/utils/toolUsageTracking.js +2 -1
  124. package/package.json +9 -6
  125. package/types/Cms.d.ts +16 -0
  126. package/types/Cms.js +25 -1
  127. package/types/LocalDev.d.ts +0 -3
  128. package/types/Prompts.d.ts +1 -0
  129. package/ui/index.d.ts +1 -0
  130. package/ui/index.js +6 -0
@@ -0,0 +1,3 @@
1
+ export declare function importDataTestAccountSelectPrompt(parentAccountId: number): Promise<{
2
+ selectedAccountId: number;
3
+ }>;
@@ -0,0 +1,29 @@
1
+ import { getAccountIdentifier } from '@hubspot/local-dev-lib/config/getAccountIdentifier';
2
+ import { promptUser } from './promptUtils.js';
3
+ import { getConfigAccounts } from '@hubspot/local-dev-lib/config';
4
+ import { uiAccountDescription } from '../ui/index.js';
5
+ import { lib } from '../../lang/en.js';
6
+ export async function importDataTestAccountSelectPrompt(parentAccountId) {
7
+ const accounts = getConfigAccounts();
8
+ if (!accounts) {
9
+ throw new Error(lib.prompts.importDataTestAccountSelectPrompt.errors.noAccountsFound);
10
+ }
11
+ const childAccounts = accounts
12
+ .filter(account => account.parentAccountId === parentAccountId)
13
+ .map(account => {
14
+ return {
15
+ name: uiAccountDescription(getAccountIdentifier(account)),
16
+ value: getAccountIdentifier(account),
17
+ };
18
+ })
19
+ .filter(account => account.value !== undefined && account.name !== undefined);
20
+ if (childAccounts.length === 0) {
21
+ throw new Error(lib.prompts.importDataTestAccountSelectPrompt.errors.noChildTestAccountsFound(parentAccountId));
22
+ }
23
+ return promptUser({
24
+ type: 'list',
25
+ name: 'selectedAccountId',
26
+ message: '[--account] Select a developer test account:',
27
+ choices: childAccounts,
28
+ });
29
+ }
@@ -151,6 +151,7 @@ async function selectTargetAccountPrompt(defaultAccountId, accountType, choices)
151
151
  accountType,
152
152
  }),
153
153
  choices,
154
+ loop: false,
154
155
  },
155
156
  ]);
156
157
  return targetAccountInfo;
@@ -1,15 +1,21 @@
1
1
  import { Separator as _Separator } from '@inquirer/prompts';
2
2
  import { PromptConfig, GenericPromptResponse, PromptWhen, PromptChoices } from '../../types/Prompts.js';
3
3
  export declare const Separator: _Separator;
4
+ export declare const PROMPT_THEME: {
5
+ prefix: {
6
+ idle: string;
7
+ };
8
+ };
4
9
  export declare function promptUser<T extends GenericPromptResponse>(config: PromptConfig<T> | PromptConfig<T>[]): Promise<T>;
5
10
  export declare function confirmPrompt(message: string, options?: {
6
11
  defaultAnswer?: boolean;
7
12
  }): Promise<boolean>;
8
- export declare function listPrompt<T = string>(message: string, { choices, when, defaultAnswer, validate, }: {
13
+ export declare function listPrompt<T = string>(message: string, { choices, when, defaultAnswer, validate, loop, }: {
9
14
  choices: PromptChoices<T>;
10
15
  when?: PromptWhen;
11
16
  defaultAnswer?: string | number | boolean;
12
17
  validate?: (input: T[]) => (boolean | string) | Promise<boolean | string>;
18
+ loop?: boolean;
13
19
  }): Promise<T>;
14
20
  export declare function inputPrompt(message: string, { when, validate, defaultAnswer, }?: {
15
21
  when?: boolean | (() => boolean);
@@ -1,6 +1,8 @@
1
1
  import { confirm, Separator as _Separator, select, input, checkbox, password, number, } from '@inquirer/prompts';
2
2
  import { EXIT_CODES } from '../enums/exitCodes.js';
3
+ import chalk from 'chalk';
3
4
  export const Separator = new _Separator();
5
+ export const PROMPT_THEME = { prefix: { idle: chalk.green('?') } };
4
6
  function isUserCancellationError(error) {
5
7
  return error instanceof Error && error.name === 'ExitPromptError';
6
8
  }
@@ -102,6 +104,8 @@ function handleRawListPrompt(config) {
102
104
  choices: choices,
103
105
  pageSize: config.pageSize,
104
106
  default: config.default,
107
+ loop: config.loop,
108
+ theme: PROMPT_THEME,
105
109
  }).then(resp => ({ [config.name]: resp }));
106
110
  }
107
111
  function handleNumberPrompt(config) {
@@ -109,6 +113,7 @@ function handleNumberPrompt(config) {
109
113
  message: config.message,
110
114
  default: config.default,
111
115
  validate: config.validate,
116
+ theme: PROMPT_THEME,
112
117
  }).then(resp => ({ [config.name]: resp }));
113
118
  }
114
119
  function handlePasswordPrompt(config) {
@@ -116,6 +121,7 @@ function handlePasswordPrompt(config) {
116
121
  message: config.message,
117
122
  mask: '*',
118
123
  validate: config.validate,
124
+ theme: PROMPT_THEME,
119
125
  }).then(resp => ({ [config.name]: resp }));
120
126
  }
121
127
  function handleCheckboxPrompt(config) {
@@ -125,6 +131,8 @@ function handleCheckboxPrompt(config) {
125
131
  choices: choices,
126
132
  pageSize: config.pageSize,
127
133
  validate: config.validate,
134
+ loop: config.loop,
135
+ theme: PROMPT_THEME,
128
136
  }).then(resp => ({ [config.name]: resp }));
129
137
  }
130
138
  function handleConfirmPrompt(config) {
@@ -138,6 +146,7 @@ function handleInputPrompt(config) {
138
146
  default: config.default,
139
147
  validate: config.validate,
140
148
  transformer: config.transformer,
149
+ theme: PROMPT_THEME,
141
150
  }).then(resp => ({ [config.name]: resp }));
142
151
  }
143
152
  function handleSelectPrompt(config) {
@@ -147,6 +156,8 @@ function handleSelectPrompt(config) {
147
156
  choices: choices,
148
157
  default: config.default,
149
158
  pageSize: config.pageSize,
159
+ loop: config.loop,
160
+ theme: PROMPT_THEME,
150
161
  }).then(resp => ({ [config.name]: resp }));
151
162
  }
152
163
  export async function confirmPrompt(message, options = {}) {
@@ -154,10 +165,11 @@ export async function confirmPrompt(message, options = {}) {
154
165
  const choice = await confirm({
155
166
  message,
156
167
  default: defaultAnswer,
168
+ theme: PROMPT_THEME,
157
169
  });
158
170
  return choice;
159
171
  }
160
- export async function listPrompt(message, { choices, when, defaultAnswer, validate, }) {
172
+ export async function listPrompt(message, { choices, when, defaultAnswer, validate, loop, }) {
161
173
  const { choice } = await promptUser({
162
174
  name: 'choice',
163
175
  type: 'list',
@@ -166,6 +178,7 @@ export async function listPrompt(message, { choices, when, defaultAnswer, valida
166
178
  when,
167
179
  default: defaultAnswer,
168
180
  validate,
181
+ loop,
169
182
  });
170
183
  return choice;
171
184
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,84 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import chalk from 'chalk';
3
+ import { removeAnsiCodes } from '../removeAnsiCodes.js';
4
+ describe('removeAnsiCodes', () => {
5
+ describe('basic functionality', () => {
6
+ it('should remove ANSI codes from colored text', () => {
7
+ const coloredText = chalk.red('Error message');
8
+ const cleanText = removeAnsiCodes(coloredText);
9
+ expect(cleanText).toBe('Error message');
10
+ });
11
+ it('should return unchanged text when no ANSI codes are present', () => {
12
+ const plainText = 'This is plain text';
13
+ const result = removeAnsiCodes(plainText);
14
+ expect(result).toBe('This is plain text');
15
+ });
16
+ it('should handle empty strings', () => {
17
+ const result = removeAnsiCodes('');
18
+ expect(result).toBe('');
19
+ });
20
+ });
21
+ describe('background colors', () => {
22
+ it('should remove background color codes', () => {
23
+ const text = chalk.bgRed('Text with red background');
24
+ expect(removeAnsiCodes(text)).toBe('Text with red background');
25
+ });
26
+ it('should remove multiple background colors', () => {
27
+ const text = chalk.bgBlue('Blue bg') + ' ' + chalk.bgYellow('Yellow bg');
28
+ expect(removeAnsiCodes(text)).toBe('Blue bg Yellow bg');
29
+ });
30
+ });
31
+ describe('text formatting', () => {
32
+ it('should remove bold formatting', () => {
33
+ const text = chalk.bold('Bold text');
34
+ expect(removeAnsiCodes(text)).toBe('Bold text');
35
+ });
36
+ });
37
+ describe('combined styles', () => {
38
+ it('should remove multiple formatting styles', () => {
39
+ const text = chalk.bold.red('Bold red text');
40
+ expect(removeAnsiCodes(text)).toBe('Bold red text');
41
+ });
42
+ it('should remove complex combinations', () => {
43
+ const text = chalk.bold.underline.red.bgYellow('Complex styling');
44
+ expect(removeAnsiCodes(text)).toBe('Complex styling');
45
+ });
46
+ it('should handle chained styles', () => {
47
+ const text = chalk.red('Red') + chalk.green(' Green') + chalk.blue(' Blue');
48
+ expect(removeAnsiCodes(text)).toBe('Red Green Blue');
49
+ });
50
+ });
51
+ describe('multiline text', () => {
52
+ it('should remove ANSI codes from multiline text', () => {
53
+ const text = chalk.red('Line 1\n') + chalk.green('Line 2\n') + chalk.blue('Line 3');
54
+ const expected = 'Line 1\nLine 2\nLine 3';
55
+ expect(removeAnsiCodes(text)).toBe(expected);
56
+ });
57
+ it('should handle mixed formatted and plain lines', () => {
58
+ const text = chalk.bold('Formatted line\n') +
59
+ 'Plain line\n' +
60
+ chalk.italic('Another formatted line');
61
+ const expected = 'Formatted line\nPlain line\nAnother formatted line';
62
+ expect(removeAnsiCodes(text)).toBe(expected);
63
+ });
64
+ });
65
+ describe('edge cases', () => {
66
+ it('should handle text with only ANSI codes (no visible text)', () => {
67
+ const text = chalk.red('');
68
+ expect(removeAnsiCodes(text)).toBe('');
69
+ });
70
+ it('should handle multiple consecutive ANSI codes', () => {
71
+ // Create text with multiple style applications
72
+ const text = chalk.red(chalk.bold(chalk.underline('Heavily styled')));
73
+ expect(removeAnsiCodes(text)).toBe('Heavily styled');
74
+ });
75
+ it('should handle mixed content with spaces and special characters', () => {
76
+ const text = chalk.green('Success:') + ' ' + chalk.red('Error!') + ' @#$%^&*()';
77
+ expect(removeAnsiCodes(text)).toBe('Success: Error! @#$%^&*()');
78
+ });
79
+ it('should handle text with tabs and newlines', () => {
80
+ const text = chalk.yellow('\tTabbed text\n') + chalk.cyan('New line text');
81
+ expect(removeAnsiCodes(text)).toBe('\tTabbed text\nNew line text');
82
+ });
83
+ });
84
+ });
package/lib/ui/index.js CHANGED
@@ -65,16 +65,13 @@ export function uiCommandReference(command, withQuotes = true) {
65
65
  }
66
66
  export function uiFeatureHighlight(features, title) {
67
67
  uiInfoSection(title ? title : i18n(`lib.ui.featureHighlight.defaultTitle`), () => {
68
- features.forEach((c, i) => {
69
- const featureKey = `lib.ui.featureHighlight.featureKeys.${c}`;
68
+ features.forEach(feature => {
69
+ const featureKey = `lib.ui.featureHighlight.featureKeys.${feature}`;
70
70
  const message = i18n(`${featureKey}.message`, {
71
71
  command: uiCommandReference(i18n(`${featureKey}.command`)),
72
72
  link: uiLink(i18n(`${featureKey}.linkText`), i18n(`${featureKey}.url`)),
73
73
  });
74
- if (i !== 0) {
75
- logger.log('');
76
- }
77
- logger.log(message);
74
+ logger.log(` - ${message}`);
78
75
  });
79
76
  });
80
77
  }
@@ -0,0 +1 @@
1
+ export declare function removeAnsiCodes(str: string): string;
@@ -0,0 +1,4 @@
1
+ const ANSI_CODE_REGEX = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
2
+ export function removeAnsiCodes(str) {
3
+ return str.replace(ANSI_CODE_REGEX, '');
4
+ }
@@ -1,6 +1,6 @@
1
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
- import { registerProjectTools } from './tools/index.js';
3
+ import { registerProjectTools, registerCmsTools } from './tools/index.js';
4
4
  const server = new McpServer({
5
5
  name: 'HubSpot CLI MCP Server',
6
6
  version: '0.0.1',
@@ -11,6 +11,7 @@ const server = new McpServer({
11
11
  },
12
12
  });
13
13
  registerProjectTools(server);
14
+ registerCmsTools(server);
14
15
  // Start receiving messages on stdin and sending messages on stdout
15
16
  const transport = new StdioServerTransport();
16
17
  server.connect(transport);
@@ -0,0 +1,38 @@
1
+ import { TextContentResponse, Tool } from '../../types.js';
2
+ import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ import { z } from 'zod';
4
+ declare const inputSchemaZodObject: z.ZodObject<{
5
+ absoluteCurrentWorkingDirectory: z.ZodString;
6
+ userSuppliedName: z.ZodOptional<z.ZodString>;
7
+ dest: z.ZodOptional<z.ZodString>;
8
+ moduleLabel: z.ZodOptional<z.ZodString>;
9
+ reactType: z.ZodOptional<z.ZodBoolean>;
10
+ contentTypes: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
11
+ global: z.ZodOptional<z.ZodBoolean>;
12
+ availableForNewContent: z.ZodOptional<z.ZodBoolean>;
13
+ }, "strip", z.ZodTypeAny, {
14
+ absoluteCurrentWorkingDirectory: string;
15
+ dest?: string | undefined;
16
+ global?: boolean | undefined;
17
+ moduleLabel?: string | undefined;
18
+ reactType?: boolean | undefined;
19
+ contentTypes?: string | undefined;
20
+ availableForNewContent?: boolean | undefined;
21
+ userSuppliedName?: string | undefined;
22
+ }, {
23
+ absoluteCurrentWorkingDirectory: string;
24
+ dest?: string | undefined;
25
+ global?: boolean | undefined;
26
+ moduleLabel?: string | undefined;
27
+ reactType?: boolean | undefined;
28
+ contentTypes?: string | undefined;
29
+ availableForNewContent?: boolean | undefined;
30
+ userSuppliedName?: string | undefined;
31
+ }>;
32
+ export type HsCreateModuleInputSchema = z.infer<typeof inputSchemaZodObject>;
33
+ export declare class HsCreateModuleTool extends Tool<HsCreateModuleInputSchema> {
34
+ constructor(mcpServer: McpServer);
35
+ handler({ userSuppliedName, dest, moduleLabel, reactType, contentTypes, global, availableForNewContent, absoluteCurrentWorkingDirectory, }: HsCreateModuleInputSchema): Promise<TextContentResponse>;
36
+ register(): RegisteredTool;
37
+ }
38
+ export {};
@@ -0,0 +1,118 @@
1
+ import { Tool } from '../../types.js';
2
+ import { z } from 'zod';
3
+ import { absoluteCurrentWorkingDirectory } from '../project/constants.js';
4
+ import { runCommandInDir } from '../../utils/project.js';
5
+ import { formatTextContents, formatTextContent } from '../../utils/content.js';
6
+ import { trackToolUsage } from '../../utils/toolUsageTracking.js';
7
+ import { addFlag } from '../../utils/command.js';
8
+ import { CONTENT_TYPES } from '../../../types/Cms.js';
9
+ const inputSchema = {
10
+ absoluteCurrentWorkingDirectory,
11
+ userSuppliedName: z
12
+ .string()
13
+ .describe('REQUIRED - If not specified by the user, DO NOT choose. Ask the user to specify the name of the module they want to create.')
14
+ .optional(),
15
+ dest: z
16
+ .string()
17
+ .describe('The destination path where the module should be created on the current computer.')
18
+ .optional(),
19
+ moduleLabel: z
20
+ .string()
21
+ .describe('Label for module creation. Required for non-interactive module creation. If not provided, ask the user to provide it.')
22
+ .optional(),
23
+ reactType: z
24
+ .boolean()
25
+ .describe('Whether to create a React module. If the user has not specified that they want a React module, DO NOT choose for them, ask them what type of module they want to create HubL or React.')
26
+ .optional(),
27
+ contentTypes: z
28
+ .string()
29
+ .refine(val => {
30
+ if (!val)
31
+ return true; // optional
32
+ const types = val.split(',').map(t => t.trim().toUpperCase());
33
+ return types.every(type => CONTENT_TYPES.includes(type));
34
+ }, {
35
+ message: `Content types must be a comma-separated list of valid values: ${CONTENT_TYPES.join(', ')}`,
36
+ })
37
+ .describe(`Content types where the module can be used. Comma-separated list. Valid values: ${CONTENT_TYPES.join(', ')}. Defaults to "ANY".`)
38
+ .optional(),
39
+ global: z
40
+ .boolean()
41
+ .describe('Whether the module is global. Defaults to false.')
42
+ .optional(),
43
+ availableForNewContent: z
44
+ .boolean()
45
+ .describe('Whether the module is available for new content. Defaults to true.')
46
+ .optional(),
47
+ };
48
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
49
+ const inputSchemaZodObject = z.object({ ...inputSchema });
50
+ const toolName = 'create-hubspot-cms-module';
51
+ export class HsCreateModuleTool extends Tool {
52
+ constructor(mcpServer) {
53
+ super(mcpServer);
54
+ }
55
+ async handler({ userSuppliedName, dest, moduleLabel, reactType, contentTypes, global, availableForNewContent, absoluteCurrentWorkingDirectory, }) {
56
+ await trackToolUsage(toolName);
57
+ const content = [];
58
+ // Always require a name
59
+ if (!userSuppliedName) {
60
+ content.push(formatTextContent(`Ask the user to specify the name of the module they want to create.`));
61
+ }
62
+ // Require module label
63
+ if (!moduleLabel) {
64
+ content.push(formatTextContent(`Ask the user to provide a label for the module.`));
65
+ }
66
+ // Ask about React vs HubL if not specified
67
+ if (reactType === undefined) {
68
+ content.push(formatTextContent(`Ask the user what type of module they want to create: HubL or React?`));
69
+ }
70
+ // If we have missing required information, return the prompts
71
+ if (content.length > 0) {
72
+ return {
73
+ content,
74
+ };
75
+ }
76
+ // Build the command
77
+ let command = 'hs create module';
78
+ if (userSuppliedName) {
79
+ command += ` "${userSuppliedName}"`;
80
+ }
81
+ if (dest) {
82
+ command += ` "${dest}"`;
83
+ }
84
+ // Add module-specific flags
85
+ if (moduleLabel) {
86
+ command = addFlag(command, 'module-label', moduleLabel);
87
+ }
88
+ if (reactType !== undefined) {
89
+ command = addFlag(command, 'react-type', reactType);
90
+ }
91
+ if (contentTypes) {
92
+ command = addFlag(command, 'content-types', contentTypes);
93
+ }
94
+ else {
95
+ command = addFlag(command, 'content-types', 'ANY');
96
+ }
97
+ if (global !== undefined) {
98
+ command = addFlag(command, 'global', global);
99
+ }
100
+ if (availableForNewContent !== undefined) {
101
+ command = addFlag(command, 'available-for-new-content', availableForNewContent);
102
+ }
103
+ try {
104
+ const { stdout, stderr } = await runCommandInDir(absoluteCurrentWorkingDirectory, command);
105
+ return formatTextContents(stdout, stderr);
106
+ }
107
+ catch (error) {
108
+ return formatTextContents(error instanceof Error ? error.message : `${error}`);
109
+ }
110
+ }
111
+ register() {
112
+ return this.mcpServer.registerTool(toolName, {
113
+ title: 'Create HubSpot CMS Module',
114
+ description: 'Creates a new HubSpot CMS module using the hs create module command. Modules can be created non-interactively by specifying moduleLabel and other module options. You can create either HubL or React modules by setting the reactType parameter.',
115
+ inputSchema,
116
+ }, this.handler);
117
+ }
118
+ }
@@ -0,0 +1,23 @@
1
+ import { TextContentResponse, Tool } from '../../types.js';
2
+ import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ import { z } from 'zod';
4
+ declare const inputSchemaZodObject: z.ZodObject<{
5
+ absoluteCurrentWorkingDirectory: z.ZodString;
6
+ path: z.ZodOptional<z.ZodString>;
7
+ account: z.ZodOptional<z.ZodString>;
8
+ }, "strip", z.ZodTypeAny, {
9
+ absoluteCurrentWorkingDirectory: string;
10
+ account?: string | undefined;
11
+ path?: string | undefined;
12
+ }, {
13
+ absoluteCurrentWorkingDirectory: string;
14
+ account?: string | undefined;
15
+ path?: string | undefined;
16
+ }>;
17
+ export type HsListInputSchema = z.infer<typeof inputSchemaZodObject>;
18
+ export declare class HsListTool extends Tool<HsListInputSchema> {
19
+ constructor(mcpServer: McpServer);
20
+ handler({ path, account, absoluteCurrentWorkingDirectory, }: HsListInputSchema): Promise<TextContentResponse>;
21
+ register(): RegisteredTool;
22
+ }
23
+ export {};
@@ -0,0 +1,58 @@
1
+ import { Tool } from '../../types.js';
2
+ import { z } from 'zod';
3
+ import { addFlag } from '../../utils/command.js';
4
+ import { absoluteCurrentWorkingDirectory } from '../project/constants.js';
5
+ import { runCommandInDir } from '../../utils/project.js';
6
+ import { formatTextContents } from '../../utils/content.js';
7
+ import { trackToolUsage } from '../../utils/toolUsageTracking.js';
8
+ const inputSchema = {
9
+ absoluteCurrentWorkingDirectory,
10
+ path: z
11
+ .string()
12
+ .describe('The remote directory path in the HubSpot CMS to list contents. If not specified, lists the root directory.')
13
+ .optional(),
14
+ account: z
15
+ .string()
16
+ .describe('The HubSpot account id or name from the HubSpot config file to use for the operation.')
17
+ .optional(),
18
+ };
19
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
20
+ const inputSchemaZodObject = z.object({ ...inputSchema });
21
+ const toolName = 'list-hubspot-cms-remote-contents';
22
+ export class HsListTool extends Tool {
23
+ constructor(mcpServer) {
24
+ super(mcpServer);
25
+ }
26
+ async handler({ path, account, absoluteCurrentWorkingDirectory, }) {
27
+ await trackToolUsage(toolName);
28
+ let command = 'hs list';
29
+ if (path) {
30
+ command += ` ${path}`;
31
+ }
32
+ if (account) {
33
+ command = addFlag(command, 'account', account);
34
+ }
35
+ try {
36
+ const { stdout, stderr } = await runCommandInDir(absoluteCurrentWorkingDirectory, command);
37
+ return formatTextContents(stdout, stderr);
38
+ }
39
+ catch (error) {
40
+ const errorMessage = error instanceof Error ? error.message : String(error);
41
+ return {
42
+ content: [
43
+ {
44
+ type: 'text',
45
+ text: `Error executing hs list command: ${errorMessage}`,
46
+ },
47
+ ],
48
+ };
49
+ }
50
+ }
51
+ register() {
52
+ return this.mcpServer.registerTool(toolName, {
53
+ title: 'List HubSpot CMS Directory Contents',
54
+ description: 'List remote contents of a HubSpot CMS directory.',
55
+ inputSchema,
56
+ }, this.handler);
57
+ }
58
+ }