@aashari/boilerplate-mcp-server 1.0.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 (41) hide show
  1. package/.eslintrc.json +19 -0
  2. package/.gitkeep +0 -0
  3. package/.node-version +1 -0
  4. package/.releaserc.json +34 -0
  5. package/CHANGELOG.md +107 -0
  6. package/README.md +357 -0
  7. package/dist/cli/index.d.ts +1 -0
  8. package/dist/cli/index.js +28 -0
  9. package/dist/cli/index.js.bak +28 -0
  10. package/dist/cli/ipaddress.cli.d.ts +10 -0
  11. package/dist/cli/ipaddress.cli.js +31 -0
  12. package/dist/controllers/ipaddress.controller.d.ts +7 -0
  13. package/dist/controllers/ipaddress.controller.js +33 -0
  14. package/dist/controllers/ipaddress.test.d.ts +1 -0
  15. package/dist/index.d.ts +6 -0
  16. package/dist/index.js +88 -0
  17. package/dist/index.js.bak +88 -0
  18. package/dist/resources/ipaddress.resource.d.ts +10 -0
  19. package/dist/resources/ipaddress.resource.js +37 -0
  20. package/dist/services/vendor.ip-api.com.service.d.ts +6 -0
  21. package/dist/services/vendor.ip-api.com.service.js +43 -0
  22. package/dist/services/vendor.ip-api.com.test.d.ts +1 -0
  23. package/dist/services/vendor.ip-api.com.type.d.ts +16 -0
  24. package/dist/services/vendor.ip-api.com.type.js +2 -0
  25. package/dist/tools/ipaddress.tool.d.ts +6 -0
  26. package/dist/tools/ipaddress.tool.js +33 -0
  27. package/dist/tools/ipaddress.type.d.ts +10 -0
  28. package/dist/tools/ipaddress.type.js +8 -0
  29. package/dist/utils/config.util.d.ts +43 -0
  30. package/dist/utils/config.util.js +116 -0
  31. package/dist/utils/error.util.d.ts +62 -0
  32. package/dist/utils/error.util.js +112 -0
  33. package/dist/utils/error.util.test.d.ts +1 -0
  34. package/dist/utils/logger.util.d.ts +9 -0
  35. package/dist/utils/logger.util.js +44 -0
  36. package/eslint.config.mjs +46 -0
  37. package/jest.config.mjs +19 -0
  38. package/package.json +88 -0
  39. package/package.json.bak +88 -0
  40. package/scripts/package.json +3 -0
  41. package/scripts/update-version.js +202 -0
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.McpError = exports.ErrorType = void 0;
4
+ exports.createAuthMissingError = createAuthMissingError;
5
+ exports.createAuthInvalidError = createAuthInvalidError;
6
+ exports.createApiError = createApiError;
7
+ exports.createUnexpectedError = createUnexpectedError;
8
+ exports.ensureMcpError = ensureMcpError;
9
+ exports.formatErrorForMcpTool = formatErrorForMcpTool;
10
+ exports.formatErrorForMcpResource = formatErrorForMcpResource;
11
+ exports.handleCliError = handleCliError;
12
+ const logger_util_js_1 = require("./logger.util.js");
13
+ /**
14
+ * Error types for classification
15
+ */
16
+ var ErrorType;
17
+ (function (ErrorType) {
18
+ ErrorType["AUTH_MISSING"] = "AUTH_MISSING";
19
+ ErrorType["AUTH_INVALID"] = "AUTH_INVALID";
20
+ ErrorType["API_ERROR"] = "API_ERROR";
21
+ ErrorType["UNEXPECTED_ERROR"] = "UNEXPECTED_ERROR";
22
+ })(ErrorType || (exports.ErrorType = ErrorType = {}));
23
+ /**
24
+ * Custom error class with type classification
25
+ */
26
+ class McpError extends Error {
27
+ constructor(message, type, statusCode, originalError) {
28
+ super(message);
29
+ this.name = 'McpError';
30
+ this.type = type;
31
+ this.statusCode = statusCode;
32
+ this.originalError = originalError;
33
+ }
34
+ }
35
+ exports.McpError = McpError;
36
+ /**
37
+ * Create an authentication missing error
38
+ */
39
+ function createAuthMissingError(message = 'Authentication credentials are missing') {
40
+ return new McpError(message, ErrorType.AUTH_MISSING);
41
+ }
42
+ /**
43
+ * Create an authentication invalid error
44
+ */
45
+ function createAuthInvalidError(message = 'Authentication credentials are invalid') {
46
+ return new McpError(message, ErrorType.AUTH_INVALID, 401);
47
+ }
48
+ /**
49
+ * Create an API error
50
+ */
51
+ function createApiError(message, statusCode, originalError) {
52
+ return new McpError(message, ErrorType.API_ERROR, statusCode, originalError);
53
+ }
54
+ /**
55
+ * Create an unexpected error
56
+ */
57
+ function createUnexpectedError(message = 'An unexpected error occurred', originalError) {
58
+ return new McpError(message, ErrorType.UNEXPECTED_ERROR, undefined, originalError);
59
+ }
60
+ /**
61
+ * Ensure an error is an McpError
62
+ */
63
+ function ensureMcpError(error) {
64
+ if (error instanceof McpError) {
65
+ return error;
66
+ }
67
+ if (error instanceof Error) {
68
+ return createUnexpectedError(error.message, error);
69
+ }
70
+ return createUnexpectedError(String(error));
71
+ }
72
+ /**
73
+ * Format error for MCP tool response
74
+ */
75
+ function formatErrorForMcpTool(error) {
76
+ const mcpError = ensureMcpError(error);
77
+ logger_util_js_1.logger.error(`[src/utils/error.util.ts@formatErrorForMcpTool] ${mcpError.type} error`, mcpError);
78
+ return {
79
+ content: [
80
+ {
81
+ type: 'text',
82
+ text: `Error: ${mcpError.message}`,
83
+ },
84
+ ],
85
+ };
86
+ }
87
+ /**
88
+ * Format error for MCP resource response
89
+ */
90
+ function formatErrorForMcpResource(error, uri) {
91
+ const mcpError = ensureMcpError(error);
92
+ logger_util_js_1.logger.error(`[src/utils/error.util.ts@formatErrorForMcpResource] ${mcpError.type} error`, mcpError);
93
+ return {
94
+ contents: [
95
+ {
96
+ uri,
97
+ text: `Error: ${mcpError.message}`,
98
+ mimeType: 'text/plain',
99
+ description: `Error: ${mcpError.type}`,
100
+ },
101
+ ],
102
+ };
103
+ }
104
+ /**
105
+ * Handle error in CLI context
106
+ */
107
+ function handleCliError(error) {
108
+ const mcpError = ensureMcpError(error);
109
+ logger_util_js_1.logger.error(`[src/utils/error.util.ts@handleCliError] ${mcpError.type} error`, mcpError);
110
+ console.error(`Error: ${mcpError.message}`);
111
+ process.exit(1);
112
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,9 @@
1
+ declare class Logger {
2
+ _log(level: 'info' | 'warn' | 'error' | 'debug', message: string, ...args: unknown[]): void;
3
+ info(message: string, ...args: unknown[]): void;
4
+ warn(message: string, ...args: unknown[]): void;
5
+ error(message: string, ...args: unknown[]): void;
6
+ debug(message: string, ...args: unknown[]): void;
7
+ }
8
+ export declare const logger: Logger;
9
+ export {};
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.logger = void 0;
4
+ /**
5
+ * Format a timestamp for logging
6
+ * @returns Formatted timestamp [HH:MM:SS]
7
+ */
8
+ function getTimestamp() {
9
+ const now = new Date();
10
+ return `[${now.toISOString().split('T')[1].split('.')[0]}]`;
11
+ }
12
+ class Logger {
13
+ _log(level, message, ...args) {
14
+ // Skip debug messages if DEBUG is not set to true
15
+ if (level === 'debug' && process.env.DEBUG !== 'true') {
16
+ return;
17
+ }
18
+ const timestamp = getTimestamp();
19
+ const prefix = `${timestamp} [${level.toUpperCase()}]`;
20
+ let logMessage = `${prefix} ${message}`;
21
+ if (args.length > 0) {
22
+ logMessage += ` ${args.map((arg) => JSON.stringify(arg)).join(' ')}`;
23
+ }
24
+ if (process.env.NODE_ENV === 'test') {
25
+ console[level](logMessage);
26
+ }
27
+ else {
28
+ console.error(logMessage);
29
+ }
30
+ }
31
+ info(message, ...args) {
32
+ this._log('info', message, ...args);
33
+ }
34
+ warn(message, ...args) {
35
+ this._log('warn', message, ...args);
36
+ }
37
+ error(message, ...args) {
38
+ this._log('error', message, ...args);
39
+ }
40
+ debug(message, ...args) {
41
+ this._log('debug', message, ...args);
42
+ }
43
+ }
44
+ exports.logger = new Logger();
@@ -0,0 +1,46 @@
1
+ import eslint from '@eslint/js';
2
+ import tseslint from 'typescript-eslint';
3
+ import prettierPlugin from 'eslint-plugin-prettier';
4
+ import eslintConfigPrettier from 'eslint-config-prettier';
5
+
6
+ export default tseslint.config(
7
+ {
8
+ ignores: ['node_modules/**', 'dist/**', 'examples/**'],
9
+ },
10
+ eslint.configs.recommended,
11
+ ...tseslint.configs.recommended,
12
+ {
13
+ plugins: {
14
+ prettier: prettierPlugin,
15
+ },
16
+ rules: {
17
+ 'prettier/prettier': 'error',
18
+ indent: ['error', 'tab', { SwitchCase: 1 }],
19
+ '@typescript-eslint/no-unused-vars': [
20
+ 'error',
21
+ { argsIgnorePattern: '^_' },
22
+ ],
23
+ },
24
+ languageOptions: {
25
+ parserOptions: {
26
+ ecmaVersion: 'latest',
27
+ sourceType: 'module',
28
+ },
29
+ globals: {
30
+ node: 'readonly',
31
+ jest: 'readonly',
32
+ },
33
+ },
34
+ },
35
+ // Special rules for test files
36
+ {
37
+ files: ['**/*.test.ts'],
38
+ rules: {
39
+ '@typescript-eslint/no-explicit-any': 'off',
40
+ '@typescript-eslint/no-require-imports': 'off',
41
+ '@typescript-eslint/no-unsafe-function-type': 'off',
42
+ '@typescript-eslint/no-unused-vars': 'off',
43
+ },
44
+ },
45
+ eslintConfigPrettier,
46
+ );
@@ -0,0 +1,19 @@
1
+ /** @type {import('ts-jest').JestConfigWithTsJest} */
2
+ export default {
3
+ preset: 'ts-jest',
4
+ testEnvironment: 'node',
5
+ testMatch: ['**/src/**/*.test.ts'],
6
+ transform: {
7
+ '^.+\\.tsx?$': [
8
+ 'ts-jest',
9
+ {
10
+ useESM: true,
11
+ },
12
+ ],
13
+ },
14
+ moduleNameMapper: {
15
+ '(.*)\\.(js|jsx)$': '$1',
16
+ },
17
+ extensionsToTreatAsEsm: ['.ts'],
18
+ moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
19
+ };
package/package.json ADDED
@@ -0,0 +1,88 @@
1
+ {
2
+ "name": "@aashari/boilerplate-mcp-server",
3
+ "version": "1.0.0",
4
+ "description": "A TypeScript-based Model Context Protocol (MCP) server boilerplate for building AI-connected tools. Features IP lookup tools, CLI support, MCP Inspector integration, and extensible architecture for connecting Claude/Anthropic AI systems to external data sources.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "type": "commonjs",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/aashari/boilerplate-mcp-server.git"
11
+ },
12
+ "bin": {
13
+ "mcp-server": "./dist/index.js"
14
+ },
15
+ "scripts": {
16
+ "build": "tsc",
17
+ "test": "jest",
18
+ "test:coverage": "jest --coverage",
19
+ "lint": "eslint src --ext .ts --config eslint.config.mjs",
20
+ "format": "prettier --write 'src/**/*.ts' 'scripts/**/*.js'",
21
+ "publish:npm": "npm publish",
22
+ "update:check": "npx npm-check-updates",
23
+ "update:deps": "npx npm-check-updates -u && npm install --legacy-peer-deps",
24
+ "update:version": "node scripts/update-version.js",
25
+ "start": "node dist/index.js",
26
+ "dev": "nodemon --watch src --ext ts --exec 'npm run build && npm start'",
27
+ "inspect": "npx @modelcontextprotocol/inspector node dist/index.js",
28
+ "inspect:debug": "npx @modelcontextprotocol/inspector -e DEBUG=true node dist/index.js"
29
+ },
30
+ "keywords": [
31
+ "mcp",
32
+ "typescript",
33
+ "claude",
34
+ "anthropic",
35
+ "ai",
36
+ "boilerplate",
37
+ "server",
38
+ "model-context-protocol",
39
+ "tools",
40
+ "resources",
41
+ "tooling",
42
+ "ai-integration",
43
+ "mcp-server",
44
+ "llm",
45
+ "ai-connector",
46
+ "external-tools",
47
+ "cli",
48
+ "mcp-inspector"
49
+ ],
50
+ "author": "",
51
+ "license": "ISC",
52
+ "devDependencies": {
53
+ "@eslint/js": "^9.23.0",
54
+ "@semantic-release/changelog": "^6.0.3",
55
+ "@semantic-release/exec": "^7.0.3",
56
+ "@semantic-release/git": "^10.0.1",
57
+ "@semantic-release/github": "^11.0.1",
58
+ "@semantic-release/npm": "^12.0.1",
59
+ "@types/jest": "^29.5.14",
60
+ "@types/node": "^22.13.11",
61
+ "@typescript-eslint/eslint-plugin": "^8.27.0",
62
+ "@typescript-eslint/parser": "^8.27.0",
63
+ "eslint": "^9.23.0",
64
+ "eslint-config-prettier": "^10.1.1",
65
+ "eslint-plugin-prettier": "^5.2.3",
66
+ "jest": "^29.7.0",
67
+ "nodemon": "^3.1.9",
68
+ "npm-check-updates": "^17.1.16",
69
+ "prettier": "^3.5.3",
70
+ "semantic-release": "^24.2.3",
71
+ "ts-jest": "^29.2.6",
72
+ "typescript": "^5.8.2",
73
+ "typescript-eslint": "^8.27.0"
74
+ },
75
+ "publishConfig": {
76
+ "registry": "https://registry.npmjs.org/",
77
+ "access": "public"
78
+ },
79
+ "dependencies": {
80
+ "commander": "^13.1.0",
81
+ "@modelcontextprotocol/sdk": "^1.7.0",
82
+ "dotenv": "^16.4.7",
83
+ "zod": "^3.24.2"
84
+ },
85
+ "directories": {
86
+ "example": "examples"
87
+ }
88
+ }
@@ -0,0 +1,88 @@
1
+ {
2
+ "name": "@aashari/boilerplate-mcp-server",
3
+ "version": "1.1.0",
4
+ "description": "A TypeScript-based Model Context Protocol (MCP) server boilerplate for building AI-connected tools. Features IP lookup tools, CLI support, MCP Inspector integration, and extensible architecture for connecting Claude/Anthropic AI systems to external data sources.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "type": "commonjs",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/aashari/boilerplate-mcp-server.git"
11
+ },
12
+ "bin": {
13
+ "mcp-server": "./dist/index.js"
14
+ },
15
+ "scripts": {
16
+ "build": "tsc",
17
+ "test": "jest",
18
+ "test:coverage": "jest --coverage",
19
+ "lint": "eslint src --ext .ts --config eslint.config.mjs",
20
+ "format": "prettier --write 'src/**/*.ts' 'scripts/**/*.js'",
21
+ "publish:npm": "npm publish",
22
+ "update:check": "npx npm-check-updates",
23
+ "update:deps": "npx npm-check-updates -u && npm install --legacy-peer-deps",
24
+ "update:version": "node scripts/update-version.js",
25
+ "start": "node dist/index.js",
26
+ "dev": "nodemon --watch src --ext ts --exec 'npm run build && npm start'",
27
+ "inspect": "npx @modelcontextprotocol/inspector node dist/index.js",
28
+ "inspect:debug": "npx @modelcontextprotocol/inspector -e DEBUG=true node dist/index.js"
29
+ },
30
+ "keywords": [
31
+ "mcp",
32
+ "typescript",
33
+ "claude",
34
+ "anthropic",
35
+ "ai",
36
+ "boilerplate",
37
+ "server",
38
+ "model-context-protocol",
39
+ "tools",
40
+ "resources",
41
+ "tooling",
42
+ "ai-integration",
43
+ "mcp-server",
44
+ "llm",
45
+ "ai-connector",
46
+ "external-tools",
47
+ "cli",
48
+ "mcp-inspector"
49
+ ],
50
+ "author": "",
51
+ "license": "ISC",
52
+ "devDependencies": {
53
+ "@eslint/js": "^9.23.0",
54
+ "@semantic-release/changelog": "^6.0.3",
55
+ "@semantic-release/exec": "^7.0.3",
56
+ "@semantic-release/git": "^10.0.1",
57
+ "@semantic-release/github": "^11.0.1",
58
+ "@semantic-release/npm": "^12.0.1",
59
+ "@types/jest": "^29.5.14",
60
+ "@types/node": "^22.13.11",
61
+ "@typescript-eslint/eslint-plugin": "^8.27.0",
62
+ "@typescript-eslint/parser": "^8.27.0",
63
+ "eslint": "^9.23.0",
64
+ "eslint-config-prettier": "^10.1.1",
65
+ "eslint-plugin-prettier": "^5.2.3",
66
+ "jest": "^29.7.0",
67
+ "nodemon": "^3.1.9",
68
+ "npm-check-updates": "^17.1.16",
69
+ "prettier": "^3.5.3",
70
+ "semantic-release": "^24.2.3",
71
+ "ts-jest": "^29.2.6",
72
+ "typescript": "^5.8.2",
73
+ "typescript-eslint": "^8.27.0"
74
+ },
75
+ "publishConfig": {
76
+ "registry": "https://registry.npmjs.org/",
77
+ "access": "public"
78
+ },
79
+ "dependencies": {
80
+ "commander": "^13.1.0",
81
+ "@modelcontextprotocol/sdk": "^1.7.0",
82
+ "dotenv": "^16.4.7",
83
+ "zod": "^3.24.2"
84
+ },
85
+ "directories": {
86
+ "example": "examples"
87
+ }
88
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
@@ -0,0 +1,202 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Script to update version numbers across the project
5
+ * Usage: node scripts/update-version.js [version] [options]
6
+ * Options:
7
+ * --dry-run Show what changes would be made without applying them
8
+ * --verbose Show detailed logging information
9
+ *
10
+ * If no version is provided, it will use the version from package.json
11
+ */
12
+
13
+ import fs from 'fs';
14
+ import path from 'path';
15
+ import { fileURLToPath } from 'url';
16
+
17
+ // Get the directory name of the current module
18
+ const __filename = fileURLToPath(import.meta.url);
19
+ const __dirname = path.dirname(__filename);
20
+ const rootDir = path.resolve(__dirname, '..');
21
+
22
+ // Parse command line arguments
23
+ const args = process.argv.slice(2);
24
+ const options = {
25
+ dryRun: args.includes('--dry-run'),
26
+ verbose: args.includes('--verbose'),
27
+ };
28
+
29
+ // Get the version (first non-flag argument)
30
+ let newVersion = args.find((arg) => !arg.startsWith('--'));
31
+
32
+ // Log helper function
33
+ const log = (message, verbose = false) => {
34
+ if (!verbose || options.verbose) {
35
+ console.log(message);
36
+ }
37
+ };
38
+
39
+ // File paths that may contain version information
40
+ const versionFiles = [
41
+ {
42
+ path: path.join(rootDir, 'package.json'),
43
+ pattern: /"version": "([^"]*)"/,
44
+ replacement: (match, currentVersion) =>
45
+ match.replace(currentVersion, newVersion),
46
+ },
47
+ {
48
+ path: path.join(rootDir, 'src', 'cli', 'index.ts'),
49
+ pattern: /const VERSION = ['"]([^'"]*)['"]/,
50
+ replacement: (match, currentVersion) =>
51
+ match.replace(currentVersion, newVersion),
52
+ },
53
+ {
54
+ path: path.join(rootDir, 'src', 'index.ts'),
55
+ pattern: /version: ['"]([^'"]*)['"]/,
56
+ replacement: (match, currentVersion) =>
57
+ match.replace(currentVersion, newVersion),
58
+ },
59
+ // Also update the compiled JavaScript files if they exist
60
+ {
61
+ path: path.join(rootDir, 'dist', 'cli', 'index.js'),
62
+ pattern: /const VERSION = ['"]([^'"]*)['"]/,
63
+ replacement: (match, currentVersion) =>
64
+ match.replace(currentVersion, newVersion),
65
+ },
66
+ {
67
+ path: path.join(rootDir, 'dist', 'index.js'),
68
+ pattern: /version: ['"]([^'"]*)['"]/,
69
+ replacement: (match, currentVersion) =>
70
+ match.replace(currentVersion, newVersion),
71
+ },
72
+ // Additional files can be added here with their patterns and replacement logic
73
+ ];
74
+
75
+ /**
76
+ * Read the version from package.json
77
+ * @returns {string} The version from package.json
78
+ */
79
+ function getPackageVersion() {
80
+ try {
81
+ const packageJsonPath = path.join(rootDir, 'package.json');
82
+ log(`Reading version from ${packageJsonPath}`, true);
83
+
84
+ const packageJson = JSON.parse(
85
+ fs.readFileSync(packageJsonPath, 'utf8'),
86
+ );
87
+
88
+ if (!packageJson.version) {
89
+ throw new Error('No version field found in package.json');
90
+ }
91
+
92
+ return packageJson.version;
93
+ } catch (error) {
94
+ console.error(`Error reading package.json: ${error.message}`);
95
+ process.exit(1);
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Validate the semantic version format
101
+ * @param {string} version - The version to validate
102
+ * @returns {boolean} True if valid, throws error if invalid
103
+ */
104
+ function validateVersion(version) {
105
+ // More comprehensive semver regex
106
+ const semverRegex =
107
+ /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
108
+
109
+ if (!semverRegex.test(version)) {
110
+ throw new Error(
111
+ `Invalid version format: ${version}\nPlease use semantic versioning format (e.g., 1.2.3, 1.2.3-beta.1, etc.)`,
112
+ );
113
+ }
114
+
115
+ return true;
116
+ }
117
+
118
+ /**
119
+ * Update version in a specific file
120
+ * @param {Object} fileConfig - Configuration for the file to update
121
+ */
122
+ function updateFileVersion(fileConfig) {
123
+ const { path: filePath, pattern, replacement } = fileConfig;
124
+
125
+ try {
126
+ log(`Checking ${filePath}...`, true);
127
+
128
+ if (!fs.existsSync(filePath)) {
129
+ console.warn(`Warning: File not found: ${filePath}`);
130
+ return;
131
+ }
132
+
133
+ // Read file content
134
+ const fileContent = fs.readFileSync(filePath, 'utf8');
135
+ const match = fileContent.match(pattern);
136
+
137
+ if (!match) {
138
+ console.warn(`Warning: Version pattern not found in ${filePath}`);
139
+ return;
140
+ }
141
+
142
+ const currentVersion = match[1];
143
+ if (currentVersion === newVersion) {
144
+ log(
145
+ `Version in ${path.basename(filePath)} is already ${newVersion}`,
146
+ true,
147
+ );
148
+ return;
149
+ }
150
+
151
+ // Create new content with the updated version
152
+ const updatedContent = fileContent.replace(pattern, replacement);
153
+
154
+ // Write the changes or log them in dry run mode
155
+ if (options.dryRun) {
156
+ log(
157
+ `Would update version in ${filePath} from ${currentVersion} to ${newVersion}`,
158
+ );
159
+ } else {
160
+ // Create a backup of the original file
161
+ fs.writeFileSync(`${filePath}.bak`, fileContent);
162
+ log(`Backup created: ${filePath}.bak`, true);
163
+
164
+ // Write the updated content
165
+ fs.writeFileSync(filePath, updatedContent);
166
+ log(
167
+ `Updated version in ${path.basename(filePath)} from ${currentVersion} to ${newVersion}`,
168
+ );
169
+ }
170
+ } catch (error) {
171
+ console.error(`Error updating ${filePath}: ${error.message}`);
172
+ process.exit(1);
173
+ }
174
+ }
175
+
176
+ // Main execution
177
+ try {
178
+ // If no version specified, get from package.json
179
+ if (!newVersion) {
180
+ newVersion = getPackageVersion();
181
+ log(
182
+ `No version specified, using version from package.json: ${newVersion}`,
183
+ );
184
+ }
185
+
186
+ // Validate the version format
187
+ validateVersion(newVersion);
188
+
189
+ // Update all configured files
190
+ for (const fileConfig of versionFiles) {
191
+ updateFileVersion(fileConfig);
192
+ }
193
+
194
+ if (options.dryRun) {
195
+ log(`\nDry run completed. No files were modified.`);
196
+ } else {
197
+ log(`\nVersion successfully updated to ${newVersion}`);
198
+ }
199
+ } catch (error) {
200
+ console.error(`\nVersion update failed: ${error.message}`);
201
+ process.exit(1);
202
+ }