@boltic/cli 1.0.27-1 → 1.0.27

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # ⚡ Boltic CLI
2
2
 
3
- > **Professional CLI tool for creating, managing, and publishing Boltic Workflow integrations with enterprise-grade features and seamless developer experience.**
3
+ > **Professional CLI for interacting with the Boltic platform — create, manage, and publish integrations, workflows, MCPs, and more with enterprise-grade features and a seamless developer experience.**
4
4
 
5
5
  [![NPM Version](https://img.shields.io/npm/v/@boltic/cli)](https://www.npmjs.com/package/@boltic/cli)
6
6
  [![GitHub Repo](https://img.shields.io/badge/GitHub-Repo-blue?logo=github)](https://github.com/bolticio/cli)
@@ -13,7 +13,7 @@
13
13
 
14
14
  ![Boltic CLI](https://img.shields.io/badge/Boltic-CLI-00D4AA?style=for-the-badge&logo=node.js&logoColor=white)
15
15
 
16
- **Streamline your integration development workflow with Boltic CLI**
16
+ **Streamline your developer workflow on the Boltic platform**
17
17
 
18
18
  [Features](#-features) • [Installation](#-installation) • [Quick Start](#-quick-start) • [Documentation](#-documentation) • [Contributing](#-contributing)
19
19
 
@@ -28,6 +28,7 @@
28
28
  - [📦 Installation](#-installation)
29
29
  - [🔐 Authentication](#-authentication)
30
30
  - [🧩 Integration Management](#-integration-management)
31
+ - [🧠 MCP](#-mcp-model-context-protocol)
31
32
  - [📚 Command Reference](#-command-reference)
32
33
  - [🛠️ Development Workflow](#️-development-workflow)
33
34
  - [🔧 Configuration](#-configuration)
@@ -42,12 +43,12 @@
42
43
  ## ✨ Features
43
44
 
44
45
  - 🔐 **Secure Authentication** - Enterprise-grade token management with secure storage
45
- - 🚀 **Rapid Integration Development** - Create integrations in minutes, not hours
46
+ - 🚀 **Rapid Development** - Create workflows, integrations, and more in minutes, not hours
46
47
  - 📦 **Smart Project Management** - Automated folder structure and configuration
47
48
  - 🔄 **Real-time Synchronization** - Instant sync with Boltic Cloud platform
48
49
  - 🎯 **Type-safe Development** - Support for Workflow Activities and Triggers
49
50
  - 🎨 **Rich Interactive UI** - Beautiful command-line interface with progress indicators
50
- - 📊 **Comprehensive Validation** - Built-in validation for all integration components
51
+ - 📊 **Comprehensive Validation** - Built-in validation for platform resources and components
51
52
  - 🔧 **Developer Experience** - Hot reload, debugging tools, and comprehensive error handling
52
53
  - 🌐 **Multi-platform Support** - Works seamlessly on Windows, macOS, and Linux
53
54
  - 📈 **Version Control Integration** - Git-friendly workflow with proper ignore patterns
@@ -62,7 +63,7 @@ Get up and running with Boltic CLI in under 2 minutes:
62
63
  # Install Boltic CLI globally
63
64
  npm install -g @boltic/cli
64
65
 
65
- # Authenticate with your Boltic account
66
+ # Authenticate with your Boltic account (if required for commands)
66
67
  boltic login
67
68
 
68
69
  # Create your first integration
@@ -225,9 +226,7 @@ my-integration/
225
226
  │ ├── base.json
226
227
  │ └── webhook.json
227
228
  ├── Authentication.mdx
228
- ├── documentation/
229
- │ ├── integration.mdx
230
- │ └── trigger.mdx
229
+ ├── Documentation.mdx
231
230
  └── spec.json
232
231
  ```
233
232
 
@@ -239,11 +238,9 @@ my-integration/
239
238
  - **`base.json`** - Base integration configuration and parameters
240
239
  - **`webhook.json`** - Webhook configuration (for trigger integrations)
241
240
 
242
- **Documentation files:**
243
- - `Authentication.mdx` - Authentication documentation in Markdown format (optional, created when catalogue is enabled)
244
- - `documentation/integration.mdx` - Activity documentation (required when activity_type is present; falls back to legacy `Documentation.mdx`)
245
- - `documentation/trigger.mdx` - Trigger documentation (required when trigger_type is "CloudTrigger")
246
- - `spec.json` - Integration specification and metadata
241
+ - **`Authentication.mdx`** - Authentication documentation in Markdown format
242
+ - **`Documentation.mdx`** - General integration documentation
243
+ - **`spec.json`** - Integration specification and metadata
247
244
 
248
245
  ### Managing Existing Integrations
249
246
 
@@ -278,6 +275,26 @@ boltic integration submit
278
275
 
279
276
  ---
280
277
 
278
+ ## 🔌 MCP Management
279
+
280
+ The CLI includes utilities to set up MCP connections for popular clients. This helps your AI tools connect to Boltic services via STDIO or Streamable HTTP.
281
+
282
+ ### Usage
283
+
284
+ ```bash
285
+ # Basic: configure MCP for Claude Desktop (default client)
286
+ boltic mcp setup https://mcp.boltic.io/sse
287
+
288
+ # Specify client and a friendly name
289
+ boltic mcp setup https://mcp.boltic.io/sse my-boltic --client claude
290
+
291
+ # Supported clients (examples):
292
+ # claude, cline, roocode, windsurf, cursor, vscode, vscode-insiders,
293
+ # witsy, enconvo, boltai, amazon-bedrock, amazonq, librechat, gemini-cli
294
+ ```
295
+
296
+ ---
297
+
281
298
  ## 📚 Command Reference
282
299
 
283
300
  ### Core Commands
@@ -301,6 +318,13 @@ boltic integration submit
301
318
  | `boltic integration status` | Check integration status | Interactive selection |
302
319
  | `boltic integration help` | Show integration help | |
303
320
 
321
+ ### MCP Commands
322
+
323
+ | Command | Description | Options |
324
+ | ------------------ | -------------------------------------------- | --------------------------------- |
325
+ | `boltic mcp help` | Show help for MCP sub-commands | |
326
+ | `boltic mcp setup` | Configure an MCP server for a specific client| `--client <name>` `--name <alias>`|
327
+
304
328
  ### Help and Documentation
305
329
 
306
330
  ```bash
@@ -423,7 +447,7 @@ boltic --verbose login
423
447
 
424
448
  ```bash
425
449
  # Verify project structure
426
- ls -la # Should show: schemas/, Authentication.mdx (optional), documentation/, spec.json
450
+ ls -la # Should show: schemas/, Authentication.mdx, Documentation.mdx, spec.json
427
451
 
428
452
  # Check file permissions
429
453
  ls -la schemas/
package/cli.js CHANGED
@@ -4,6 +4,7 @@ import path from "path";
4
4
  import { fileURLToPath } from "url";
5
5
  import EnvironmentCommands from "./commands/env.js";
6
6
  import IntegrationCommands from "./commands/integration.js";
7
+ import McpCommands from "./commands/mcp.js";
7
8
  import AuthCommands from "./commands/login.js";
8
9
 
9
10
  // Create a CLI module with functional approach
@@ -22,6 +23,10 @@ const createCLI = (consoleUrl, apiUrl, serviceName, env) => {
22
23
  description: "Manage integrations (create, list)",
23
24
  action: (args) => handleIntegration(args),
24
25
  },
26
+ mcp: {
27
+ description: "Manage MCPs clients and servers",
28
+ action: (args) => handleMcp(args),
29
+ },
25
30
  logout: {
26
31
  description: "Logout and clear access token",
27
32
  action: AuthCommands.handleLogout,
@@ -157,6 +162,10 @@ async function handleEnvironment(args) {
157
162
  await EnvironmentCommands.execute(args);
158
163
  }
159
164
 
165
+ async function handleMcp(args) {
166
+ await McpCommands.execute(args);
167
+ }
168
+
160
169
  async function showVersion() {
161
170
  let version = "1.0.0";
162
171
  try {
@@ -114,16 +114,6 @@ async function readSchemaFiles(currentDir) {
114
114
  // Read documentation files
115
115
  const authDocPath = path.join(currentDir, "Authentication.mdx");
116
116
  const generalDocPath = path.join(currentDir, "Documentation.mdx");
117
- const docsIntegrationPath = path.join(
118
- currentDir,
119
- "documentation",
120
- "integration.mdx"
121
- );
122
- const docsTriggerPath = path.join(
123
- currentDir,
124
- "documentation",
125
- "trigger.mdx"
126
- );
127
117
 
128
118
  if (fs.existsSync(authDocPath)) {
129
119
  schemas.authentication_documentation = fs.readFileSync(
@@ -132,22 +122,8 @@ async function readSchemaFiles(currentDir) {
132
122
  );
133
123
  }
134
124
 
135
- // Build documentation object with backward compatibility
136
- const documentation = {};
137
- if (fs.existsSync(docsIntegrationPath)) {
138
- documentation.integration = fs.readFileSync(
139
- docsIntegrationPath,
140
- "utf8"
141
- );
142
- } else if (fs.existsSync(generalDocPath)) {
143
- // Fallback to legacy Documentation.mdx
144
- documentation.integration = fs.readFileSync(generalDocPath, "utf8");
145
- }
146
- if (fs.existsSync(docsTriggerPath)) {
147
- documentation.trigger = fs.readFileSync(docsTriggerPath, "utf8");
148
- }
149
- if (Object.keys(documentation).length > 0) {
150
- schemas.documentation = documentation;
125
+ if (fs.existsSync(generalDocPath)) {
126
+ schemas.documentation = fs.readFileSync(generalDocPath, "utf8");
151
127
  }
152
128
 
153
129
  return schemas;
@@ -1115,25 +1091,7 @@ async function handleStatus() {
1115
1091
 
1116
1092
  if (integration.documentation) {
1117
1093
  console.log(chalk.cyan("\nDocumentation:"));
1118
- if (
1119
- typeof integration.documentation === "object" &&
1120
- integration.documentation !== null
1121
- ) {
1122
- if (integration.documentation.integration) {
1123
- console.log(
1124
- chalk.dim("integration.mdx:\n") +
1125
- integration.documentation.integration
1126
- );
1127
- }
1128
- if (integration.documentation.trigger) {
1129
- console.log(
1130
- chalk.dim("\ntrigger.mdx:\n") +
1131
- integration.documentation.trigger
1132
- );
1133
- }
1134
- } else {
1135
- console.log(integration.documentation);
1136
- }
1094
+ console.log(integration.documentation);
1137
1095
  }
1138
1096
  } catch (error) {
1139
1097
  if (
@@ -0,0 +1,374 @@
1
+ import chalk from "chalk";
2
+ import fs from "fs";
3
+ import os from "os";
4
+ import path from "path";
5
+ import yaml from "js-yaml";
6
+ import { execFileSync } from "child_process";
7
+
8
+ // Types removed for CommonJS conversion
9
+
10
+ const commands = {
11
+ setup: {
12
+ description: "Setup command for various MCP Clients",
13
+ action: handleSetup,
14
+ },
15
+ help: {
16
+ description: "Show help for mcp commands",
17
+ action: showHelp,
18
+ },
19
+ };
20
+
21
+ // Execute the MCP command
22
+ const execute = async (args) => {
23
+ const subCommand = args[0];
24
+
25
+ if (!subCommand) {
26
+ showHelp();
27
+ return;
28
+ }
29
+
30
+ if (!commands[subCommand]) {
31
+ console.log(chalk.red("Unknown or missing mcp sub-command.\n"));
32
+ showHelp();
33
+ return;
34
+ }
35
+
36
+ const commandObj = commands[subCommand];
37
+ await commandObj.action(args.slice(1));
38
+ };
39
+
40
+ function showHelp() {
41
+ console.log(chalk.cyan("\nMCP Commands:\n"));
42
+ Object.entries(commands).forEach(([cmd, details]) => {
43
+ console.log(chalk.bold(`${cmd}`) + ` - ${details.description}`);
44
+ });
45
+ }
46
+
47
+ // Handle setup subcommand: setup <url> [name] --client <client>
48
+ async function handleSetup(args) {
49
+ // Parse positional args
50
+ const url = args[0];
51
+ let name = args[1] && !args[1].startsWith("--") ? args[1] : undefined;
52
+
53
+ // Parse flags: --client, --name
54
+ let client = "claude";
55
+ for (let i = 0; i < args.length; i++) {
56
+ const token = args[i];
57
+ if (
58
+ token === "--client" &&
59
+ args[i + 1] &&
60
+ !args[i + 1].startsWith("--")
61
+ ) {
62
+ client = args[i + 1];
63
+ i++;
64
+ } else if (token.startsWith("--client=")) {
65
+ client = token.split("=")[1];
66
+ } else if (
67
+ token === "--name" &&
68
+ args[i + 1] &&
69
+ !args[i + 1].startsWith("--")
70
+ ) {
71
+ name = args[i + 1];
72
+ i++;
73
+ } else if (token.startsWith("--name=")) {
74
+ name = token.split("=")[1];
75
+ }
76
+ }
77
+
78
+ if (!url) {
79
+ console.log(
80
+ chalk.red(
81
+ "❌ URL is required. Usage: boltic mcp setup <url> [name] --client <client>"
82
+ )
83
+ );
84
+ return;
85
+ }
86
+
87
+ const newKey =
88
+ name || url.split("/").slice(3).join("/").replace(/\//g, "-");
89
+
90
+ try {
91
+ console.log(chalk.cyan("📝 Configuration Details:"));
92
+ console.log(` URL: ${chalk.green(url)}`);
93
+ console.log(` Client: ${chalk.green(client)}`);
94
+ console.log(` Name: ${chalk.green(newKey)}\n`);
95
+
96
+ const mcpUrl = url;
97
+ const command = `composio --sse "${mcpUrl}"`;
98
+
99
+ console.log(chalk.cyan("💾 Saving configurations..."));
100
+
101
+ saveMcpConfig(url, client, newKey, mcpUrl, command);
102
+
103
+ console.log(
104
+ chalk.cyan(
105
+ `\n🚀 All done! Please restart ${client} for changes to take effect\n`
106
+ )
107
+ );
108
+ } catch (error) {
109
+ console.log(chalk.red("\n❌ Error occurred while setting up MCP:"));
110
+ console.log(
111
+ chalk.red(
112
+ ` ${error && error.message ? error.message : String(error)}`
113
+ )
114
+ );
115
+ console.log(
116
+ chalk.yellow(
117
+ "\nPlease try again or contact support if the issue persists.\n"
118
+ )
119
+ );
120
+ }
121
+ }
122
+
123
+ function saveMcpConfig(url, clientType, name, mcpUrl, command) {
124
+ const config = {
125
+ command: "npx",
126
+ args: ["-y", "mcp-remote", mcpUrl],
127
+ env: {
128
+ npm_config_yes: "true",
129
+ },
130
+ };
131
+
132
+ const sseConfig = {
133
+ url: mcpUrl,
134
+ };
135
+
136
+ const homeDir = os.homedir();
137
+
138
+ const platformPaths = {
139
+ win32: {
140
+ baseDir:
141
+ process.env.APPDATA || path.join(homeDir, "AppData", "Roaming"),
142
+ vscodePath: path.join("Code", "User", "globalStorage"),
143
+ },
144
+ darwin: {
145
+ baseDir: path.join(homeDir, "Library", "Application Support"),
146
+ vscodePath: path.join("Code", "User", "globalStorage"),
147
+ },
148
+ linux: {
149
+ baseDir:
150
+ process.env.XDG_CONFIG_HOME || path.join(homeDir, ".config"),
151
+ vscodePath: path.join("Code/User/globalStorage"),
152
+ },
153
+ };
154
+
155
+ const platform = process.platform;
156
+
157
+ // Check if platform is supported
158
+ if (!platformPaths[platform]) {
159
+ console.log(chalk.red(`\n❌ Platform ${platform} is not supported.`));
160
+ process.exit(1);
161
+ }
162
+
163
+ const { baseDir } = platformPaths[platform];
164
+
165
+ const defaultClaudePath = path.join(
166
+ baseDir,
167
+ "Claude",
168
+ "claude_desktop_config.json"
169
+ );
170
+
171
+ // Define client paths using the platform-specific base directories
172
+ const clientPaths = {
173
+ claude: {
174
+ type: "file",
175
+ path: defaultClaudePath,
176
+ },
177
+ cline: {
178
+ type: "file",
179
+ path: path.join(
180
+ baseDir,
181
+ platformPaths[platform].vscodePath,
182
+ "saoudrizwan.claude-dev",
183
+ "settings",
184
+ "cline_mcp_settings.json"
185
+ ),
186
+ },
187
+ roocode: {
188
+ type: "file",
189
+ path: path.join(
190
+ baseDir,
191
+ platformPaths[platform].vscodePath,
192
+ "rooveterinaryinc.roo-cline",
193
+ "settings",
194
+ "mcp_settings.json"
195
+ ),
196
+ },
197
+ windsurf: {
198
+ type: "file",
199
+ path: path.join(homeDir, ".codeium", "windsurf", "mcp_config.json"),
200
+ },
201
+ witsy: {
202
+ type: "file",
203
+ path: path.join(baseDir, "Witsy", "settings.json"),
204
+ },
205
+ enconvo: {
206
+ type: "file",
207
+ path: path.join(homeDir, ".config", "enconvo", "mcp_config.json"),
208
+ },
209
+ cursor: {
210
+ type: "file",
211
+ path: path.join(homeDir, ".cursor", "mcp.json"),
212
+ },
213
+ vscode: {
214
+ type: "command",
215
+ command: process.platform === "win32" ? "code.cmd" : "code",
216
+ },
217
+ "vscode-insiders": {
218
+ type: "command",
219
+ command:
220
+ process.platform === "win32"
221
+ ? "code-insiders.cmd"
222
+ : "code-insiders",
223
+ },
224
+ boltai: {
225
+ type: "file",
226
+ path: path.join(homeDir, ".boltai", "mcp.json"),
227
+ },
228
+ "amazon-bedrock": {
229
+ type: "file",
230
+ path: path.join(
231
+ homeDir,
232
+ "Amazon Bedrock Client",
233
+ "mcp_config.json"
234
+ ),
235
+ },
236
+ amazonq: {
237
+ type: "file",
238
+ path: path.join(homeDir, ".aws", "amazonq", "mcp.json"),
239
+ },
240
+ librechat: {
241
+ type: "yaml",
242
+ path: path.join(homeDir, "LibreChat", "librechat.yaml"),
243
+ },
244
+ "gemini-cli": {
245
+ type: "file",
246
+ path: path.join(homeDir, ".gemini", "settings.json"),
247
+ },
248
+ };
249
+ if (!clientPaths[clientType]) {
250
+ console.log(chalk.red(`\n❌ Client ${clientType} is not supported.`));
251
+ process.exit(1);
252
+ }
253
+
254
+ const clientConfig = clientPaths[clientType];
255
+ const newKey =
256
+ name || url.split("/").slice(3).join("/").replace(/\//g, "-");
257
+
258
+ if (clientConfig.type === "command") {
259
+ handleCommandClient(clientConfig, newKey, config);
260
+ } else if (clientConfig.type === "yaml") {
261
+ handleYamlClient(clientConfig, newKey, config);
262
+ } else {
263
+ handleFileClient(clientConfig, newKey, config, mcpUrl);
264
+ }
265
+ }
266
+
267
+ function handleCommandClient(clientConfig, serverName, config) {
268
+ if (!clientConfig.command) {
269
+ throw new Error("Command not specified for command-type client");
270
+ }
271
+
272
+ const args = [];
273
+ args.push("--add-mcp", JSON.stringify({ ...config, name: serverName }));
274
+
275
+ try {
276
+ const output = execFileSync(clientConfig.command, args);
277
+ console.log(
278
+ chalk.green(
279
+ `✅ Configuration added via ${clientConfig.command}: ${output.toString()}`
280
+ )
281
+ );
282
+ } catch (error) {
283
+ if (error && error.code === "ENOENT") {
284
+ throw new Error(
285
+ `Command '${clientConfig.command}' not found. Make sure ${clientConfig.command} is installed and on your PATH`
286
+ );
287
+ }
288
+ throw error;
289
+ }
290
+ }
291
+
292
+ function handleYamlClient(clientConfig, serverName, config) {
293
+ if (!clientConfig.path) {
294
+ throw new Error("Path not specified for YAML client");
295
+ }
296
+
297
+ const configDir = path.dirname(clientConfig.path);
298
+ if (!fs.existsSync(configDir)) {
299
+ fs.mkdirSync(configDir, { recursive: true });
300
+ }
301
+
302
+ let existingYaml = {};
303
+
304
+ try {
305
+ if (fs.existsSync(clientConfig.path)) {
306
+ const originalContent = fs.readFileSync(clientConfig.path, "utf8");
307
+ existingYaml = yaml.load(originalContent) || {};
308
+ }
309
+ } catch (error) {
310
+ console.log(chalk.yellow("⚠️ Creating new YAML config file"));
311
+ }
312
+
313
+ // Initialize mcpServers if it doesn't exist
314
+ if (!existingYaml.mcpServers) {
315
+ existingYaml.mcpServers = {};
316
+ }
317
+
318
+ // Add the new server
319
+ existingYaml.mcpServers[serverName] = config;
320
+
321
+ // Write the updated YAML
322
+ const yamlContent = yaml.dump(existingYaml, {
323
+ indent: 2,
324
+ lineWidth: -1,
325
+ noRefs: true,
326
+ });
327
+
328
+ fs.writeFileSync(clientConfig.path, yamlContent);
329
+ console.log(chalk.green(`✅ Configuration saved to: ${clientConfig.path}`));
330
+ }
331
+
332
+ function handleFileClient(clientConfig, serverName, config, mcpUrl) {
333
+ if (!clientConfig.path) {
334
+ throw new Error("Path not specified for file client");
335
+ }
336
+
337
+ const configDir = path.dirname(clientConfig.path);
338
+ if (!fs.existsSync(configDir)) {
339
+ fs.mkdirSync(configDir, { recursive: true });
340
+ }
341
+
342
+ let existingConfig = { mcpServers: {} };
343
+
344
+ try {
345
+ if (fs.existsSync(clientConfig.path)) {
346
+ existingConfig = JSON.parse(
347
+ fs.readFileSync(clientConfig.path, "utf8")
348
+ );
349
+ }
350
+ } catch (error) {
351
+ console.log(chalk.yellow("⚠️ Creating new config file"));
352
+ }
353
+
354
+ // Ensure mcpServers exists
355
+ if (!existingConfig.mcpServers) existingConfig.mcpServers = {};
356
+
357
+ // Special handling for Cursor which uses SSE configuration
358
+ if (clientConfig.path?.includes(".cursor")) {
359
+ const sseConfig = {
360
+ url: mcpUrl,
361
+ };
362
+ existingConfig.mcpServers[serverName] = sseConfig;
363
+ } else {
364
+ existingConfig.mcpServers[serverName] = config;
365
+ }
366
+
367
+ fs.writeFileSync(
368
+ clientConfig.path,
369
+ JSON.stringify(existingConfig, null, 2)
370
+ );
371
+ console.log(chalk.green(`✅ Configuration saved to: ${clientConfig.path}`));
372
+ }
373
+
374
+ export default { execute };
package/helper/folder.js CHANGED
@@ -47,9 +47,6 @@ export const createIntegrationFolderStructure = async (
47
47
  const resourcesDir = path.join(schemasDir, "resources");
48
48
  fs.mkdirSync(resourcesDir, { recursive: true });
49
49
 
50
- const docsDir = path.join(integrationDir, "documentation");
51
- fs.mkdirSync(docsDir, { recursive: true });
52
-
53
50
  // Create template files
54
51
  const files = {
55
52
  "schemas/resources/resource1.json": JSON.stringify(resource1, null, 4),
@@ -72,13 +69,7 @@ export const createIntegrationFolderStructure = async (
72
69
  ...(create_catalogue && {
73
70
  "Authentication.mdx": `# ${name} Authentication`,
74
71
  }),
75
- // Documentation files (new structure)
76
- ...(!isEmpty(activity_type) && {
77
- "documentation/integration.mdx": `# ${name} Integration`,
78
- }),
79
- ...(trigger_type === "CloudTrigger" && {
80
- "documentation/trigger.mdx": `# ${name} Trigger`,
81
- }),
72
+ "Documentation.mdx": `# ${name} Documentation`,
82
73
  };
83
74
 
84
75
  // Create all files
@@ -156,14 +147,7 @@ export const createExistingIntegrationsFolder = async (payload) => {
156
147
  const resourcesDir = path.join(schemasDir, "resources");
157
148
  fs.mkdirSync(resourcesDir, { recursive: true });
158
149
 
159
- const docsDir = path.join(integrationDir, "documentation");
160
- fs.mkdirSync(docsDir, { recursive: true });
161
-
162
150
  const authentication_documentation = authentication?.documentation || "";
163
- const isDocumentationObject =
164
- documentation &&
165
- typeof documentation === "object" &&
166
- !Array.isArray(documentation);
167
151
 
168
152
  // Define files and content
169
153
  const files = {
@@ -184,17 +168,7 @@ export const createExistingIntegrationsFolder = async (payload) => {
184
168
  ...(authentication && {
185
169
  "Authentication.mdx": authentication_documentation || "",
186
170
  }),
187
- ...(!isEmpty(activity_type) && {
188
- "documentation/integration.mdx": isDocumentationObject
189
- ? documentation?.integration || `# ${name} Integration`
190
- : (typeof documentation === "string" && documentation) ||
191
- `# ${name} Integration`,
192
- }),
193
- ...(trigger_type === "CloudTrigger" && {
194
- "documentation/trigger.mdx": isDocumentationObject
195
- ? documentation?.trigger || `# ${name} Trigger`
196
- : `# ${name} Trigger`,
197
- }),
171
+ "Documentation.mdx": documentation || "",
198
172
  };
199
173
 
200
174
  // Write resource files
@@ -2,7 +2,6 @@ import fs from "fs";
2
2
  import isEmpty from "lodash.isempty";
3
3
  import path from "path";
4
4
  import * as componentSchemas from "../templates/component-schemas.js";
5
- import { validateMarkdownFile } from "./markdown.js";
6
5
 
7
6
  const validateOptionObject = (options, fieldName, fileLabel, errors) => {
8
7
  options.forEach((opt, index) => {
@@ -326,82 +325,9 @@ const validateComponentSchemas = (schemas, errors, filename = "") => {
326
325
  // INDIVIDUAL VALIDATORS
327
326
  // ─────────────────────────────────────────────────────────────────────────────
328
327
 
329
- const validateDocumentation = (currentDir, spec, errors) => {
330
- const docsIntegrationPath = path.join(
331
- currentDir,
332
- "documentation",
333
- "integration.mdx"
334
- );
335
- const docsTriggerPath = path.join(
336
- currentDir,
337
- "documentation",
338
- "trigger.mdx"
339
- );
340
- const legacyDocPath = path.join(currentDir, "Documentation.mdx");
341
-
342
- const requiresIntegrationDoc = spec && !isEmpty(spec.activity_type);
343
- const requiresTriggerDoc = spec && spec.trigger_type === "CloudTrigger";
344
-
345
- // Backward-compatibility: if nothing specific is required by spec,
346
- // ensure at least legacy Documentation.mdx or new docs exist
347
- const hasLegacy = fs.existsSync(legacyDocPath);
348
- const hasNewIntegration = fs.existsSync(docsIntegrationPath);
349
- const hasNewTrigger = fs.existsSync(docsTriggerPath);
350
-
351
- if (!requiresIntegrationDoc && !requiresTriggerDoc) {
352
- if (!hasLegacy && !hasNewIntegration && !hasNewTrigger) {
353
- errors.add('"Documentation.mdx" not found in the root directory.');
354
- return; // No further checks needed
355
- }
356
- // If files exist, validate whatever is present
357
- if (hasLegacy) {
358
- validateMarkdownFile(legacyDocPath, errors, "Documentation.mdx");
359
- }
360
- if (hasNewIntegration) {
361
- validateMarkdownFile(
362
- docsIntegrationPath,
363
- errors,
364
- "documentation/integration.mdx"
365
- );
366
- }
367
- if (hasNewTrigger) {
368
- validateMarkdownFile(
369
- docsTriggerPath,
370
- errors,
371
- "documentation/trigger.mdx"
372
- );
373
- }
374
- return;
375
- }
376
-
377
- if (requiresIntegrationDoc) {
378
- const hasNew = validateMarkdownFile(
379
- docsIntegrationPath,
380
- errors,
381
- "documentation/integration.mdx"
382
- );
383
- const hasLegacyValidated = validateMarkdownFile(
384
- legacyDocPath,
385
- errors,
386
- "Documentation.mdx"
387
- );
388
- if (!hasNew && !hasLegacyValidated) {
389
- // Maintain backward-compatible error message expected by tests
390
- errors.add('"Documentation.mdx" not found in the root directory.');
391
- }
392
- }
393
-
394
- if (requiresTriggerDoc) {
395
- const hasTrigger = validateMarkdownFile(
396
- docsTriggerPath,
397
- errors,
398
- "documentation/trigger.mdx"
399
- );
400
- if (!hasTrigger) {
401
- errors.add(
402
- '"documentation/trigger.mdx" not found when trigger_type is "CloudTrigger".'
403
- );
404
- }
328
+ const validateDocumentation = (docPath, errors) => {
329
+ if (!fs.existsSync(docPath)) {
330
+ errors.add(`"Documentation.mdx" not found in the root directory.`);
405
331
  }
406
332
  };
407
333
 
@@ -599,11 +525,13 @@ export const validateIntegrationSchemas = (currentDir) => {
599
525
  spec: path.join(currentDir, "spec.json"),
600
526
  webhook: path.join(currentDir, "schemas", "webhook.json"),
601
527
  authentication: path.join(currentDir, "schemas", "authentication.json"),
528
+ documentation: path.join(currentDir, "Documentation.mdx"),
602
529
  };
603
530
 
604
531
  // Step-by-step validation
532
+ validateDocumentation(paths.documentation, errors);
533
+
605
534
  const spec = validateSpec(paths.spec, errors);
606
- validateDocumentation(currentDir, spec, errors);
607
535
  validateWebhook(paths.webhook, spec, errors);
608
536
  validateAuthentication(paths.authentication, errors);
609
537
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boltic/cli",
3
- "version": "1.0.27-1",
3
+ "version": "1.0.27",
4
4
  "description": "A powerful CLI tool for managing Boltic Workflow integrations - create, sync, test, and publish integrations with ease",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -107,6 +107,7 @@
107
107
  "@inquirer/prompts": "^7.8.0",
108
108
  "axios": "^1.11.0",
109
109
  "chalk": "^5.5.0",
110
+ "js-yaml": "^4.1.0",
110
111
  "keytar": "^7.9.0",
111
112
  "lodash.isempty": "^4.4.0",
112
113
  "open": "^10.2.0",
@@ -1,32 +0,0 @@
1
- import fs from "fs";
2
-
3
- const hasBalancedCodeFences = (content) => {
4
- const fenceCount = (content.match(/```/g) || []).length;
5
- return fenceCount % 2 === 0;
6
- };
7
-
8
- export const validateMarkdownString = (content, fileLabel, errors) => {
9
- const label = fileLabel || "markdown";
10
- if (!content || !content.trim()) {
11
- errors.add(`"${label}" is empty.`);
12
- return false;
13
- }
14
- // Keep checks minimal to avoid false positives in CLI usage
15
- if (!hasBalancedCodeFences(content)) {
16
- errors.add('"' + label + '" has unbalanced code fences (```).');
17
- }
18
- return true;
19
- };
20
-
21
- export const validateMarkdownFile = (filePath, errors, label) => {
22
- try {
23
- if (!fs.existsSync(filePath)) return false;
24
- const content = fs.readFileSync(filePath, "utf8");
25
- return validateMarkdownString(content, label || filePath, errors);
26
- } catch (e) {
27
- errors.add(
28
- `Failed to read markdown file "${label || filePath}": ${e.message}`
29
- );
30
- return false;
31
- }
32
- };