@hubspot/cli 7.7.3-experimental.0 → 7.7.5-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.
- package/bin/cli.js +2 -0
- package/commands/account/auth.d.ts +2 -0
- package/commands/account/auth.js +15 -7
- package/commands/auth.d.ts +2 -0
- package/commands/auth.js +17 -8
- package/commands/hubdb/list.d.ts +4 -0
- package/commands/hubdb/list.js +83 -0
- package/commands/hubdb.js +2 -0
- package/commands/mcp/setup.d.ts +13 -0
- package/commands/mcp/setup.js +248 -0
- package/commands/mcp/start.d.ts +10 -0
- package/commands/mcp/start.js +66 -0
- package/commands/mcp.d.ts +10 -0
- package/commands/mcp.js +20 -0
- package/commands/project/listBuilds.js +2 -5
- package/lang/en.d.ts +26 -4
- package/lang/en.js +28 -4
- package/lib/prompts/personalAccessKeyPrompt.js +35 -24
- package/lib/prompts/projectAddPrompt.js +1 -1
- package/lib/prompts/promptUtils.d.ts +2 -1
- package/lib/prompts/promptUtils.js +2 -1
- package/mcp-server/server.d.ts +1 -0
- package/mcp-server/server.js +18 -0
- package/mcp-server/tools/index.d.ts +2 -0
- package/mcp-server/tools/index.js +17 -0
- package/mcp-server/tools/project/AddFeatureToProject.d.ts +6 -0
- package/mcp-server/tools/project/AddFeatureToProject.js +100 -0
- package/mcp-server/tools/project/CreateProjectTool.d.ts +6 -0
- package/mcp-server/tools/project/CreateProjectTool.js +119 -0
- package/mcp-server/tools/project/DeployProject.d.ts +6 -0
- package/mcp-server/tools/project/DeployProject.js +50 -0
- package/mcp-server/tools/project/GuidedWalkthroughTool.d.ts +6 -0
- package/mcp-server/tools/project/GuidedWalkthroughTool.js +54 -0
- package/mcp-server/tools/project/UploadProjectTools.d.ts +5 -0
- package/mcp-server/tools/project/UploadProjectTools.js +26 -0
- package/mcp-server/tools/project/constants.d.ts +3 -0
- package/mcp-server/tools/project/constants.js +13 -0
- package/mcp-server/types.d.ts +8 -0
- package/mcp-server/types.js +7 -0
- package/mcp-server/utils/command.d.ts +3 -0
- package/mcp-server/utils/command.js +16 -0
- package/mcp-server/utils/project.d.ts +5 -0
- package/mcp-server/utils/project.js +17 -0
- package/package.json +4 -2
package/bin/cli.js
CHANGED
|
@@ -44,6 +44,7 @@ const feedback_1 = __importDefault(require("../commands/feedback"));
|
|
|
44
44
|
const doctor_1 = __importDefault(require("../commands/doctor"));
|
|
45
45
|
const completion_1 = __importDefault(require("../commands/completion"));
|
|
46
46
|
const app_1 = __importDefault(require("../commands/app"));
|
|
47
|
+
const mcp_1 = __importDefault(require("../commands/mcp"));
|
|
47
48
|
function getTerminalWidth() {
|
|
48
49
|
const width = yargs_1.default.terminalWidth();
|
|
49
50
|
if (width >= 100)
|
|
@@ -122,6 +123,7 @@ const argv = yargs_1.default
|
|
|
122
123
|
.command(doctor_1.default)
|
|
123
124
|
.command(completion_1.default)
|
|
124
125
|
.command(app_1.default)
|
|
126
|
+
.command(mcp_1.default)
|
|
125
127
|
.help()
|
|
126
128
|
.alias('h', 'help')
|
|
127
129
|
.recommendCommands()
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { CommonArgs, ConfigArgs, YargsCommandModule } from '../../types/Yargs';
|
|
2
2
|
type AccountAuthArgs = CommonArgs & ConfigArgs & {
|
|
3
3
|
disableTracking?: boolean;
|
|
4
|
+
} & {
|
|
5
|
+
personalAccessKey?: string;
|
|
4
6
|
};
|
|
5
7
|
declare const accountAuthCommand: YargsCommandModule<unknown, AccountAuthArgs>;
|
|
6
8
|
export default accountAuthCommand;
|
package/commands/account/auth.js
CHANGED
|
@@ -26,12 +26,14 @@ const TRACKING_STATUS = {
|
|
|
26
26
|
COMPLETE: 'complete',
|
|
27
27
|
};
|
|
28
28
|
const authType = auth_1.PERSONAL_ACCESS_KEY_AUTH_METHOD.value;
|
|
29
|
-
async function updateConfigWithNewAccount(env, configAlreadyExists, accountId) {
|
|
29
|
+
async function updateConfigWithNewAccount(env, configAlreadyExists, providedPersonalAccessKey, accountId) {
|
|
30
30
|
try {
|
|
31
|
-
const { personalAccessKey } =
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
const { personalAccessKey } = providedPersonalAccessKey
|
|
32
|
+
? { personalAccessKey: providedPersonalAccessKey }
|
|
33
|
+
: await (0, personalAccessKeyPrompt_1.personalAccessKeyPrompt)({
|
|
34
|
+
env,
|
|
35
|
+
account: accountId,
|
|
36
|
+
});
|
|
35
37
|
const token = await (0, personalAccessKey_1.getAccessToken)(personalAccessKey, env);
|
|
36
38
|
const defaultAccountName = token.hubName
|
|
37
39
|
? (0, text_1.toKebabCase)(token.hubName)
|
|
@@ -96,7 +98,7 @@ async function handleConfigMigration() {
|
|
|
96
98
|
const describe = en_1.commands.account.subcommands.auth.describe;
|
|
97
99
|
const command = 'auth';
|
|
98
100
|
async function handler(args) {
|
|
99
|
-
const { providedAccountId, disableTracking } = args;
|
|
101
|
+
const { providedAccountId, disableTracking, personalAccessKey: providedPersonalAccessKey, } = args;
|
|
100
102
|
if (!disableTracking) {
|
|
101
103
|
(0, usageTracking_1.trackCommandUsage)('account-auth', {}, providedAccountId);
|
|
102
104
|
await (0, usageTracking_1.trackAuthAction)('account-auth', authType, TRACKING_STATUS.STARTED);
|
|
@@ -112,7 +114,7 @@ async function handler(args) {
|
|
|
112
114
|
}
|
|
113
115
|
(0, config_1.loadConfig)('');
|
|
114
116
|
(0, process_1.handleExit)(config_1.deleteEmptyConfigFile);
|
|
115
|
-
const updatedConfig = await updateConfigWithNewAccount(args.qa ? environments_1.ENVIRONMENTS.QA : environments_1.ENVIRONMENTS.PROD, configAlreadyExists, providedAccountId);
|
|
117
|
+
const updatedConfig = await updateConfigWithNewAccount(args.qa ? environments_1.ENVIRONMENTS.QA : environments_1.ENVIRONMENTS.PROD, configAlreadyExists, providedPersonalAccessKey, providedAccountId);
|
|
116
118
|
if (!updatedConfig) {
|
|
117
119
|
if (!disableTracking) {
|
|
118
120
|
await (0, usageTracking_1.trackAuthAction)('account-auth', authType, TRACKING_STATUS.ERROR);
|
|
@@ -151,6 +153,12 @@ function accountAuthBuilder(yargs) {
|
|
|
151
153
|
hidden: true,
|
|
152
154
|
default: false,
|
|
153
155
|
},
|
|
156
|
+
'personal-access-key': {
|
|
157
|
+
describe: en_1.commands.account.subcommands.auth.options.personalAccessKey,
|
|
158
|
+
type: 'string',
|
|
159
|
+
hidden: false,
|
|
160
|
+
alias: 'pak',
|
|
161
|
+
},
|
|
154
162
|
});
|
|
155
163
|
return yargs;
|
|
156
164
|
}
|
package/commands/auth.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { AccountArgs, CommonArgs, ConfigArgs, TestingArgs, YargsCommandModule } from '../types/Yargs';
|
|
2
2
|
type AuthArgs = CommonArgs & ConfigArgs & TestingArgs & AccountArgs & {
|
|
3
3
|
authType?: string;
|
|
4
|
+
} & {
|
|
5
|
+
personalAccessKey?: string;
|
|
4
6
|
};
|
|
5
7
|
declare const authCommand: YargsCommandModule<unknown, AuthArgs>;
|
|
6
8
|
export default authCommand;
|
package/commands/auth.js
CHANGED
|
@@ -20,6 +20,7 @@ const oauth_1 = require("../lib/oauth");
|
|
|
20
20
|
const exitCodes_1 = require("../lib/enums/exitCodes");
|
|
21
21
|
const ui_1 = require("../lib/ui");
|
|
22
22
|
const index_1 = require("../lib/errorHandlers/index");
|
|
23
|
+
const en_1 = require("../lang/en");
|
|
23
24
|
const TRACKING_STATUS = {
|
|
24
25
|
STARTED: 'started',
|
|
25
26
|
ERROR: 'error',
|
|
@@ -33,7 +34,7 @@ const SUPPORTED_AUTHENTICATION_PROTOCOLS_TEXT = (0, text_1.commaSeparatedValues)
|
|
|
33
34
|
const command = 'auth';
|
|
34
35
|
const describe = (0, lang_1.i18n)('commands.auth.describe');
|
|
35
36
|
async function handler(args) {
|
|
36
|
-
const { authType: authTypeFlagValue, config: configFlagValue, qa, providedAccountId, } = args;
|
|
37
|
+
const { authType: authTypeFlagValue, config: configFlagValue, qa, providedAccountId, personalAccessKey: providedPersonalAccessKey, } = args;
|
|
37
38
|
const authType = (authTypeFlagValue && authTypeFlagValue.toLowerCase()) ||
|
|
38
39
|
auth_1.PERSONAL_ACCESS_KEY_AUTH_METHOD.value;
|
|
39
40
|
(0, commonOpts_1.setLogLevel)(args);
|
|
@@ -68,14 +69,16 @@ async function handler(args) {
|
|
|
68
69
|
successAuthMethod = auth_1.OAUTH_AUTH_METHOD.name;
|
|
69
70
|
break;
|
|
70
71
|
case auth_1.PERSONAL_ACCESS_KEY_AUTH_METHOD.value:
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
const { personalAccessKey } = providedPersonalAccessKey
|
|
73
|
+
? { personalAccessKey: providedPersonalAccessKey }
|
|
74
|
+
: await (0, personalAccessKeyPrompt_1.personalAccessKeyPrompt)({
|
|
75
|
+
env,
|
|
76
|
+
account: providedAccountId,
|
|
77
|
+
});
|
|
75
78
|
try {
|
|
76
|
-
token = await (0, personalAccessKey_1.getAccessToken)(
|
|
79
|
+
token = await (0, personalAccessKey_1.getAccessToken)(personalAccessKey, env);
|
|
77
80
|
defaultName = token.hubName ? (0, text_1.toKebabCase)(token.hubName) : undefined;
|
|
78
|
-
updatedConfig = await (0, personalAccessKey_1.updateConfigWithAccessToken)(token,
|
|
81
|
+
updatedConfig = await (0, personalAccessKey_1.updateConfigWithAccessToken)(token, personalAccessKey, env);
|
|
79
82
|
}
|
|
80
83
|
catch (e) {
|
|
81
84
|
(0, index_1.logError)(e);
|
|
@@ -108,7 +111,7 @@ async function handler(args) {
|
|
|
108
111
|
await (0, usageTracking_1.trackAuthAction)('auth', authType, TRACKING_STATUS.ERROR, providedAccountId);
|
|
109
112
|
process.exit(exitCodes_1.EXIT_CODES.ERROR);
|
|
110
113
|
}
|
|
111
|
-
const nameFromConfigData = 'name' in configData ? configData.name : undefined;
|
|
114
|
+
const nameFromConfigData = configData && 'name' in configData ? configData.name : undefined;
|
|
112
115
|
const accountName = (updatedConfig && updatedConfig.name) || validName || nameFromConfigData;
|
|
113
116
|
await (0, setAsDefaultAccountPrompt_1.setAsDefaultAccountPrompt)(accountName);
|
|
114
117
|
logger_1.logger.success((0, lang_1.i18n)('commands.auth.success.configFileUpdated', {
|
|
@@ -141,6 +144,12 @@ function authBuilder(yargs) {
|
|
|
141
144
|
type: 'string',
|
|
142
145
|
alias: 'a',
|
|
143
146
|
},
|
|
147
|
+
'personal-access-key': {
|
|
148
|
+
describe: en_1.commands.auth.options.personalAccessKey.describe,
|
|
149
|
+
type: 'string',
|
|
150
|
+
hidden: false,
|
|
151
|
+
alias: 'pak',
|
|
152
|
+
},
|
|
144
153
|
});
|
|
145
154
|
return yargs;
|
|
146
155
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { CommonArgs, ConfigArgs, AccountArgs, EnvironmentArgs, YargsCommandModule } from '../../types/Yargs';
|
|
2
|
+
type HubdbListArgs = CommonArgs & ConfigArgs & AccountArgs & EnvironmentArgs;
|
|
3
|
+
declare const hubdbListCommand: YargsCommandModule<unknown, HubdbListArgs>;
|
|
4
|
+
export default hubdbListCommand;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const hubdb_1 = require("@hubspot/local-dev-lib/api/hubdb");
|
|
4
|
+
const config_1 = require("@hubspot/local-dev-lib/config");
|
|
5
|
+
const urls_1 = require("@hubspot/local-dev-lib/urls");
|
|
6
|
+
const exitCodes_1 = require("../../lib/enums/exitCodes");
|
|
7
|
+
const logger_1 = require("../../lib/ui/logger");
|
|
8
|
+
const index_1 = require("../../lib/errorHandlers/index");
|
|
9
|
+
const en_1 = require("../../lang/en");
|
|
10
|
+
const yargsUtils_1 = require("../../lib/yargsUtils");
|
|
11
|
+
const usageTracking_1 = require("../../lib/usageTracking");
|
|
12
|
+
const table_1 = require("../../lib/ui/table");
|
|
13
|
+
const command = ['list', 'ls'];
|
|
14
|
+
const describe = en_1.commands.hubdb.subcommands.list.describe;
|
|
15
|
+
async function getTableData(accountId) {
|
|
16
|
+
try {
|
|
17
|
+
const response = await (0, hubdb_1.fetchTables)(accountId);
|
|
18
|
+
return response.data;
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
(0, index_1.logError)(err);
|
|
22
|
+
process.exit(exitCodes_1.EXIT_CODES.ERROR);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// stripping the types and unnecessary fields so this data can be turned into a UI table
|
|
26
|
+
function mapTablesToUI(tables) {
|
|
27
|
+
return tables.map(({ id, label, name, columnCount, rowCount }) => [
|
|
28
|
+
`${id}`,
|
|
29
|
+
label,
|
|
30
|
+
name,
|
|
31
|
+
`${columnCount || 0}`,
|
|
32
|
+
`${rowCount}`,
|
|
33
|
+
]);
|
|
34
|
+
}
|
|
35
|
+
async function handler(args) {
|
|
36
|
+
const { derivedAccountId } = args;
|
|
37
|
+
(0, usageTracking_1.trackCommandUsage)('hubdb-list', {}, derivedAccountId);
|
|
38
|
+
const { results: tables, total } = await getTableData(derivedAccountId);
|
|
39
|
+
const tableUIData = mapTablesToUI(tables);
|
|
40
|
+
tableUIData.unshift((0, table_1.getTableHeader)([
|
|
41
|
+
en_1.commands.hubdb.subcommands.list.labels.id,
|
|
42
|
+
en_1.commands.hubdb.subcommands.list.labels.label,
|
|
43
|
+
en_1.commands.hubdb.subcommands.list.labels.name,
|
|
44
|
+
en_1.commands.hubdb.subcommands.list.labels.columns,
|
|
45
|
+
en_1.commands.hubdb.subcommands.list.labels.rows,
|
|
46
|
+
]));
|
|
47
|
+
logger_1.uiLogger.success(en_1.commands.hubdb.subcommands.list.success(derivedAccountId));
|
|
48
|
+
logger_1.uiLogger.log(' ');
|
|
49
|
+
// link devs to the hubdb page in hubspot for easy access
|
|
50
|
+
// TODO: This is hacky, we should make a util like getBaseUrl()
|
|
51
|
+
const baseUrl = (0, urls_1.getHubSpotWebsiteOrigin)((0, config_1.getEnv)());
|
|
52
|
+
logger_1.uiLogger.log(en_1.commands.hubdb.subcommands.list.viewTablesLink(baseUrl, derivedAccountId));
|
|
53
|
+
// don't bother showing an empty list of tables
|
|
54
|
+
if (tables.length > 0) {
|
|
55
|
+
// if truncated is 0, it will be interpreted as falsy
|
|
56
|
+
const truncated = total - tables.length;
|
|
57
|
+
logger_1.uiLogger.log(en_1.commands.hubdb.subcommands.list.tablesDisplayed(tables.length, total, truncated));
|
|
58
|
+
logger_1.uiLogger.log('--------------------------------');
|
|
59
|
+
logger_1.uiLogger.log(en_1.commands.hubdb.subcommands.list.tables);
|
|
60
|
+
logger_1.uiLogger.log((0, table_1.getTableContents)(tableUIData, { border: { bodyLeft: ' ' } }));
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
logger_1.uiLogger.log(en_1.commands.hubdb.subcommands.list.noTables(derivedAccountId));
|
|
64
|
+
}
|
|
65
|
+
process.exit(exitCodes_1.EXIT_CODES.SUCCESS);
|
|
66
|
+
}
|
|
67
|
+
function hubdbListBuilder(yargs) {
|
|
68
|
+
yargs.example([['$0 hubdb list']]);
|
|
69
|
+
return yargs;
|
|
70
|
+
}
|
|
71
|
+
const builder = (0, yargsUtils_1.makeYargsBuilder)(hubdbListBuilder, command, describe, {
|
|
72
|
+
useGlobalOptions: true,
|
|
73
|
+
useConfigOptions: true,
|
|
74
|
+
useAccountOptions: true,
|
|
75
|
+
useEnvironmentOptions: true,
|
|
76
|
+
});
|
|
77
|
+
const hubdbListCommand = {
|
|
78
|
+
command,
|
|
79
|
+
describe,
|
|
80
|
+
handler,
|
|
81
|
+
builder,
|
|
82
|
+
};
|
|
83
|
+
exports.default = hubdbListCommand;
|
package/commands/hubdb.js
CHANGED
|
@@ -8,6 +8,7 @@ const create_1 = __importDefault(require("./hubdb/create"));
|
|
|
8
8
|
const fetch_1 = __importDefault(require("./hubdb/fetch"));
|
|
9
9
|
const delete_1 = __importDefault(require("./hubdb/delete"));
|
|
10
10
|
const clear_1 = __importDefault(require("./hubdb/clear"));
|
|
11
|
+
const list_1 = __importDefault(require("./hubdb/list"));
|
|
11
12
|
const lang_1 = require("../lib/lang");
|
|
12
13
|
const yargsUtils_1 = require("../lib/yargsUtils");
|
|
13
14
|
exports.command = 'hubdb';
|
|
@@ -18,6 +19,7 @@ function hubdbBuilder(yargs) {
|
|
|
18
19
|
.command(create_1.default)
|
|
19
20
|
.command(fetch_1.default)
|
|
20
21
|
.command(delete_1.default)
|
|
22
|
+
.command(list_1.default)
|
|
21
23
|
.demandCommand(1, '');
|
|
22
24
|
return yargs;
|
|
23
25
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ArgumentsCamelCase, Argv } from 'yargs';
|
|
2
|
+
interface MCPSetupArgs {
|
|
3
|
+
targets?: string[];
|
|
4
|
+
}
|
|
5
|
+
declare function builder(yargs: Argv): Argv;
|
|
6
|
+
declare function handler(argv: ArgumentsCamelCase<MCPSetupArgs>): Promise<void>;
|
|
7
|
+
declare const _default: {
|
|
8
|
+
command: string[];
|
|
9
|
+
describe: string;
|
|
10
|
+
builder: typeof builder;
|
|
11
|
+
handler: typeof handler;
|
|
12
|
+
};
|
|
13
|
+
export default _default;
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const logger_1 = require("@hubspot/local-dev-lib/logger");
|
|
7
|
+
const child_process_1 = require("child_process");
|
|
8
|
+
const util_1 = require("util");
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const fs_1 = __importDefault(require("fs"));
|
|
11
|
+
const os_1 = __importDefault(require("os"));
|
|
12
|
+
const SpinniesManager_1 = __importDefault(require("../../lib/ui/SpinniesManager"));
|
|
13
|
+
const exitCodes_1 = require("../../lib/enums/exitCodes");
|
|
14
|
+
const promptUtils_1 = require("../../lib/prompts/promptUtils");
|
|
15
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
16
|
+
const command = ['setup', 'update'];
|
|
17
|
+
const describe = 'Add or update MCP server configuration to claude and cursor';
|
|
18
|
+
const mcpServerPath = path_1.default.join(os_1.default.homedir(), 'src', 'hubspot-cli', 'dist', 'mcp-server', 'server.js');
|
|
19
|
+
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
20
|
+
const supportedTools = [
|
|
21
|
+
{ name: 'Claude', value: 'claude' },
|
|
22
|
+
{ name: 'Claude Desktop', value: 'claude-desktop' },
|
|
23
|
+
{ name: 'Cursor', value: 'cursor' },
|
|
24
|
+
];
|
|
25
|
+
function builder(yargs) {
|
|
26
|
+
yargs.option('targets', {
|
|
27
|
+
describe: 'Target application to configure',
|
|
28
|
+
type: 'array',
|
|
29
|
+
choices: [...supportedTools.map(tool => tool.value)],
|
|
30
|
+
});
|
|
31
|
+
return yargs;
|
|
32
|
+
}
|
|
33
|
+
async function handler(argv) {
|
|
34
|
+
await addMcpServerToConfig(argv.targets);
|
|
35
|
+
}
|
|
36
|
+
exports.default = { command, describe, builder, handler };
|
|
37
|
+
async function addMcpServerToConfig(targets) {
|
|
38
|
+
try {
|
|
39
|
+
let derivedTargets = [];
|
|
40
|
+
if (!targets || targets.length === 0) {
|
|
41
|
+
const { selectedTargets } = await (0, promptUtils_1.promptUser)({
|
|
42
|
+
name: 'selectedTargets',
|
|
43
|
+
type: 'checkbox',
|
|
44
|
+
message: '[--targets] Which tools would you like to add the HubSpot CLI MCP server to?',
|
|
45
|
+
choices: supportedTools,
|
|
46
|
+
validate: (choices) => {
|
|
47
|
+
return choices.length === 0 ? 'Must choose at least one tool' : true;
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
derivedTargets = selectedTargets;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
derivedTargets = targets;
|
|
54
|
+
}
|
|
55
|
+
SpinniesManager_1.default.init();
|
|
56
|
+
if (derivedTargets.includes('claude-desktop')) {
|
|
57
|
+
await runSetupFunction(setupClaudeDesktop);
|
|
58
|
+
}
|
|
59
|
+
if (derivedTargets.includes('claude')) {
|
|
60
|
+
await runSetupFunction(setupClaudeCode);
|
|
61
|
+
}
|
|
62
|
+
if (derivedTargets.includes('cursor')) {
|
|
63
|
+
await runSetupFunction(setupCursor);
|
|
64
|
+
}
|
|
65
|
+
logger_1.logger.info(`You can now use the HubSpot CLI tools in ${derivedTargets.join(', ')}. ${chalk_1.default.bold('You may need to restart these tools to apply the changes')}.`);
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
SpinniesManager_1.default.fail('mcpSetup', {
|
|
69
|
+
text: 'Failed to configure HubSpot CLI MCP server.',
|
|
70
|
+
});
|
|
71
|
+
logger_1.logger.error(error);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async function runSetupFunction(func) {
|
|
75
|
+
const result = await func();
|
|
76
|
+
if (!result) {
|
|
77
|
+
process.exit(exitCodes_1.EXIT_CODES.ERROR);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async function setupClaudeDesktop() {
|
|
81
|
+
try {
|
|
82
|
+
const configPath = getClaudeDesktopConfigPath();
|
|
83
|
+
SpinniesManager_1.default.add('claudeDesktop', {
|
|
84
|
+
text: 'Configuring Claude Desktop...',
|
|
85
|
+
});
|
|
86
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
87
|
+
let config = {};
|
|
88
|
+
// Read existing config if it exists
|
|
89
|
+
if (fs_1.default.existsSync(configPath)) {
|
|
90
|
+
try {
|
|
91
|
+
const configContent = fs_1.default.readFileSync(configPath, 'utf8');
|
|
92
|
+
config = JSON.parse(configContent);
|
|
93
|
+
logger_1.logger.debug(`Found existing Claude Desktop config at ${configPath}`);
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
logger_1.logger.debug(`Could not parse existing Claude Desktop config, creating new one: ${error}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
// Create config directory if it doesn't exist
|
|
101
|
+
const configDir = path_1.default.dirname(configPath);
|
|
102
|
+
fs_1.default.mkdirSync(configDir, { recursive: true });
|
|
103
|
+
logger_1.logger.debug(`Created Claude Desktop config directory at ${configDir}`);
|
|
104
|
+
}
|
|
105
|
+
// Initialize mcpServers if it doesn't exist
|
|
106
|
+
if (!config.mcpServers) {
|
|
107
|
+
config.mcpServers = {};
|
|
108
|
+
}
|
|
109
|
+
// Add or update HubSpot CLI MCP server
|
|
110
|
+
config.mcpServers['hubspot-cli-mcp'] = {
|
|
111
|
+
// TODO: Before merging, change these to `hs mcp start` and do a test publish
|
|
112
|
+
command: 'node',
|
|
113
|
+
args: [mcpServerPath],
|
|
114
|
+
};
|
|
115
|
+
// Write the updated config
|
|
116
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
117
|
+
SpinniesManager_1.default.succeed('claudeDesktop', {
|
|
118
|
+
text: 'Configured Claude Desktop',
|
|
119
|
+
});
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
SpinniesManager_1.default.fail('claudeDesktop', {
|
|
124
|
+
text: 'Failed to configure Claude Desktop',
|
|
125
|
+
});
|
|
126
|
+
logger_1.logger.debug(`Failed to configure Claude Desktop: ${error}`);
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
async function setupClaudeCode() {
|
|
131
|
+
try {
|
|
132
|
+
SpinniesManager_1.default.add('claudeCode', {
|
|
133
|
+
text: 'Configuring Claude Code...',
|
|
134
|
+
});
|
|
135
|
+
try {
|
|
136
|
+
// Check if claude command is available
|
|
137
|
+
await execAsync('claude --version');
|
|
138
|
+
// Run claude mcp add command
|
|
139
|
+
const mcpConfig = JSON.stringify({
|
|
140
|
+
type: 'stdio',
|
|
141
|
+
// TODO: Before merging, change these to `hs mcp start` and do a test publish
|
|
142
|
+
command: 'node',
|
|
143
|
+
args: [mcpServerPath],
|
|
144
|
+
});
|
|
145
|
+
const { stdout } = await execAsync('claude mcp list');
|
|
146
|
+
if (stdout.includes('hubspot-cli-mcp')) {
|
|
147
|
+
SpinniesManager_1.default.update('claudeCode', {
|
|
148
|
+
text: 'CLI mcp server already installed, reinstalling',
|
|
149
|
+
});
|
|
150
|
+
try {
|
|
151
|
+
await execAsync('claude mcp remove "hubspot-cli-mcp" --scope user');
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
// @ts-ignore
|
|
155
|
+
logger_1.logger.warn(error.stderr);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
await execAsync(`claude mcp add-json "hubspot-cli-mcp" '${mcpConfig}' --scope user`);
|
|
159
|
+
SpinniesManager_1.default.succeed('claudeCode', {
|
|
160
|
+
text: 'Configured Claude Code',
|
|
161
|
+
});
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
if (error instanceof Error &&
|
|
166
|
+
error.message.includes('claude: command not found')) {
|
|
167
|
+
SpinniesManager_1.default.fail('claudeCode', {
|
|
168
|
+
text: 'Claude Code CLI not found - skipping configuration',
|
|
169
|
+
});
|
|
170
|
+
logger_1.logger.info(' Install Claude Code CLI to enable this configuration');
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
SpinniesManager_1.default.fail('claudeCode', {
|
|
174
|
+
text: 'Claude Code CLI not working - skipping configuration',
|
|
175
|
+
});
|
|
176
|
+
logger_1.logger.error(`Error: ${error}`);
|
|
177
|
+
}
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
SpinniesManager_1.default.fail('claudeCode', {
|
|
183
|
+
text: 'Failed to configure Claude Code',
|
|
184
|
+
});
|
|
185
|
+
logger_1.logger.error(`Failed to configure Claude Code: ${error}`);
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
async function setupCursor() {
|
|
190
|
+
try {
|
|
191
|
+
SpinniesManager_1.default.add('cursor', {
|
|
192
|
+
text: 'Configuring Cursor...',
|
|
193
|
+
});
|
|
194
|
+
const cursorConfigPath = path_1.default.join(os_1.default.homedir(), '.cursor', 'mcp.json');
|
|
195
|
+
if (!fs_1.default.existsSync(cursorConfigPath)) {
|
|
196
|
+
SpinniesManager_1.default.succeed('cursor', {
|
|
197
|
+
text: 'No .cursor/mcp.json file found - skipping Cursor configuration',
|
|
198
|
+
});
|
|
199
|
+
logger_1.logger.info(' Create a .cursor/mcp.json file in your project to enable Cursor MCP support');
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
203
|
+
let config = {};
|
|
204
|
+
// Read existing config
|
|
205
|
+
try {
|
|
206
|
+
const configContent = fs_1.default.readFileSync(cursorConfigPath, 'utf8');
|
|
207
|
+
config = JSON.parse(configContent);
|
|
208
|
+
logger_1.logger.debug(`Found existing Cursor config at ${cursorConfigPath}`);
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
logger_1.logger.warn(`Could not parse existing Cursor config, creating new one: ${error}`);
|
|
212
|
+
}
|
|
213
|
+
// Initialize mcpServers if it doesn't exist
|
|
214
|
+
if (!config.mcpServers) {
|
|
215
|
+
config.mcpServers = {};
|
|
216
|
+
}
|
|
217
|
+
// Add or update HubSpot CLI MCP server
|
|
218
|
+
config.mcpServers['hubspot-cli-mcp'] = {
|
|
219
|
+
// TODO: Before merging, change these to `hs mcp start` and do a test publish
|
|
220
|
+
command: 'node',
|
|
221
|
+
args: [mcpServerPath],
|
|
222
|
+
};
|
|
223
|
+
// Write the updated config
|
|
224
|
+
fs_1.default.writeFileSync(cursorConfigPath, JSON.stringify(config, null, 2));
|
|
225
|
+
SpinniesManager_1.default.succeed('cursor', {
|
|
226
|
+
text: 'Configured Cursor',
|
|
227
|
+
});
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
SpinniesManager_1.default.fail('cursor', {
|
|
232
|
+
text: 'Failed to configure Cursor',
|
|
233
|
+
});
|
|
234
|
+
logger_1.logger.error(`Failed to configure Cursor: ${error}`);
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
function getClaudeDesktopConfigPath() {
|
|
239
|
+
const platform = os_1.default.platform();
|
|
240
|
+
const homeDir = os_1.default.homedir();
|
|
241
|
+
if (platform === 'win32') {
|
|
242
|
+
const appData = process.env.APPDATA || path_1.default.join(homeDir, 'AppData', 'Roaming');
|
|
243
|
+
return path_1.default.join(appData, 'Claude', 'claude_desktop_config.json');
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
return path_1.default.join(homeDir, '.config', 'claude', 'claude_desktop_config.json');
|
|
247
|
+
}
|
|
248
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Argv } from 'yargs';
|
|
2
|
+
declare function builder(yargs: Argv): Argv;
|
|
3
|
+
declare function handler(): Promise<void>;
|
|
4
|
+
declare const _default: {
|
|
5
|
+
command: string;
|
|
6
|
+
describe: undefined;
|
|
7
|
+
builder: typeof builder;
|
|
8
|
+
handler: typeof handler;
|
|
9
|
+
};
|
|
10
|
+
export default _default;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const logger_1 = require("@hubspot/local-dev-lib/logger");
|
|
7
|
+
const child_process_1 = require("child_process");
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const command = 'start';
|
|
11
|
+
const describe = undefined; // Keep this command hidden, doesn't seem useful to expose
|
|
12
|
+
function builder(yargs) {
|
|
13
|
+
return yargs;
|
|
14
|
+
}
|
|
15
|
+
async function handler() {
|
|
16
|
+
await startMcpServer();
|
|
17
|
+
}
|
|
18
|
+
exports.default = { command, describe, builder, handler };
|
|
19
|
+
async function startMcpServer() {
|
|
20
|
+
try {
|
|
21
|
+
const serverPath = path_1.default.join(__dirname, '..', '..', 'mcp-server', 'server.js');
|
|
22
|
+
// Check if server file exists
|
|
23
|
+
if (!fs_1.default.existsSync(serverPath)) {
|
|
24
|
+
logger_1.logger.error(`MCP server file not found at ${serverPath}`);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
logger_1.logger.info('Starting HubSpot CLI MCP server...');
|
|
28
|
+
logger_1.logger.info(`Server path: ${serverPath}`);
|
|
29
|
+
logger_1.logger.info('The server will run in stdio mode for MCP client connections');
|
|
30
|
+
logger_1.logger.info('Press Ctrl+C to stop the server');
|
|
31
|
+
// Start the server using ts-node
|
|
32
|
+
const child = (0, child_process_1.spawn)('node', [serverPath], {
|
|
33
|
+
stdio: 'inherit',
|
|
34
|
+
env: {
|
|
35
|
+
...process.env,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
// Handle server process events
|
|
39
|
+
child.on('error', error => {
|
|
40
|
+
logger_1.logger.error(error);
|
|
41
|
+
logger_1.logger.error('Failed to start MCP server:', error.message);
|
|
42
|
+
});
|
|
43
|
+
child.on('close', code => {
|
|
44
|
+
if (code === 0) {
|
|
45
|
+
logger_1.logger.info('MCP server stopped gracefully');
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
logger_1.logger.error(`MCP server exited with code ${code}`);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
// Handle graceful shutdown
|
|
52
|
+
process.on('SIGINT', () => {
|
|
53
|
+
logger_1.logger.info('Shutting down MCP server...');
|
|
54
|
+
child.kill('SIGTERM');
|
|
55
|
+
process.exit(0);
|
|
56
|
+
});
|
|
57
|
+
process.on('SIGTERM', () => {
|
|
58
|
+
logger_1.logger.info('Shutting down MCP server...');
|
|
59
|
+
child.kill('SIGTERM');
|
|
60
|
+
process.exit(0);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
logger_1.logger.error('Error starting MCP server:', error);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Argv } from 'yargs';
|
|
2
|
+
declare function builder(yargs: Argv): Argv;
|
|
3
|
+
declare function handler(): void;
|
|
4
|
+
declare const _default: {
|
|
5
|
+
command: string;
|
|
6
|
+
describe: string;
|
|
7
|
+
builder: typeof builder;
|
|
8
|
+
handler: typeof handler;
|
|
9
|
+
};
|
|
10
|
+
export default _default;
|
package/commands/mcp.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const start_1 = __importDefault(require("./mcp/start"));
|
|
7
|
+
const setup_1 = __importDefault(require("./mcp/setup"));
|
|
8
|
+
const command = 'mcp';
|
|
9
|
+
const describe = 'Manage the Model Context Protocol server';
|
|
10
|
+
function builder(yargs) {
|
|
11
|
+
return yargs
|
|
12
|
+
.command(start_1.default)
|
|
13
|
+
.command(setup_1.default)
|
|
14
|
+
.demandCommand(1, 'You must specify a subcommand')
|
|
15
|
+
.help();
|
|
16
|
+
}
|
|
17
|
+
function handler() {
|
|
18
|
+
// This function is required by yargs but not used since we have subcommands
|
|
19
|
+
}
|
|
20
|
+
exports.default = { command, describe, builder, handler };
|
|
@@ -17,6 +17,7 @@ const lang_1 = require("../../lib/lang");
|
|
|
17
17
|
const index_2 = require("../../lib/errorHandlers/index");
|
|
18
18
|
const exitCodes_1 = require("../../lib/enums/exitCodes");
|
|
19
19
|
const yargsUtils_1 = require("../../lib/yargsUtils");
|
|
20
|
+
const en_1 = require("../../lang/en");
|
|
20
21
|
const command = 'list-builds';
|
|
21
22
|
const describe = (0, ui_1.uiBetaTag)((0, lang_1.i18n)(`commands.project.subcommands.listBuilds.describe`), false);
|
|
22
23
|
async function fetchAndDisplayBuilds(accountId, project, options) {
|
|
@@ -29,11 +30,7 @@ async function fetchAndDisplayBuilds(accountId, project, options) {
|
|
|
29
30
|
}));
|
|
30
31
|
}
|
|
31
32
|
else {
|
|
32
|
-
logger_1.logger.log((0,
|
|
33
|
-
count: results.length,
|
|
34
|
-
projectName: project.name,
|
|
35
|
-
viewBuildsLink: (0, ui_1.uiLink)((0, lang_1.i18n)(`commands.project.subcommands.listBuilds.logs.viewAllBuildsLink`), (0, urls_1.getProjectDetailUrl)(project.name, accountId)),
|
|
36
|
-
}));
|
|
33
|
+
logger_1.logger.log(en_1.commands.project.listBuilds.showingRecentBuilds(results.length, project.name, (0, ui_1.uiLink)(en_1.commands.project.listBuilds.viewAllBuildsLink, (0, urls_1.getProjectDetailUrl)(project.name, accountId))));
|
|
37
34
|
}
|
|
38
35
|
if (results.length === 0) {
|
|
39
36
|
logger_1.logger.log((0, lang_1.i18n)(`commands.project.subcommands.listBuilds.errors.noBuilds`));
|