@codescene/codehealth-mcp 1.0.0 → 1.0.1

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/lib/download.js CHANGED
@@ -220,6 +220,12 @@ async function renameExtractedBinary(cacheDir, binaryPath) {
220
220
  * @returns {Promise<string>} Path to the binary
221
221
  */
222
222
  export async function ensureBinary(version) {
223
+ if (typeof version !== "string" || !version) {
224
+ throw new Error(
225
+ `ensureBinary requires a non-empty version string, got: ${JSON.stringify(version)}`
226
+ );
227
+ }
228
+
223
229
  const binaryPath = getCachedBinaryPath(version);
224
230
 
225
231
  if (existsSync(binaryPath)) {
package/lib/index.js CHANGED
@@ -8,20 +8,50 @@
8
8
  import { readFileSync, existsSync } from "node:fs";
9
9
  import { join, dirname, resolve } from "node:path";
10
10
  import { fileURLToPath } from "node:url";
11
+ import { setTimeout as delay } from "node:timers/promises";
11
12
  import { ensureBinary } from "./download.js";
12
13
  import { runBinary } from "./run.js";
13
14
 
14
15
  const __dirname = dirname(fileURLToPath(import.meta.url));
15
16
  const PACKAGE_ROOT = join(__dirname, "..");
16
17
 
18
+ /** Maximum number of retry attempts when package files are temporarily unavailable. */
19
+ const MAX_RETRIES = 5;
20
+
21
+ /** Delay in milliseconds between retry attempts. */
22
+ const RETRY_DELAY_MS = 1000;
23
+
24
+ /**
25
+ * Returns true if the error is transient — caused by package files being
26
+ * replaced during an npm update (file missing, partially written, or
27
+ * missing version field).
28
+ *
29
+ * @param {Error} err
30
+ * @returns {boolean}
31
+ */
32
+ function isTransientStartupError(err) {
33
+ if (err.code === "ENOENT") return true;
34
+ if (err instanceof SyntaxError) return true;
35
+ if (err.message && err.message.includes("missing version")) return true;
36
+ return false;
37
+ }
38
+
17
39
  /**
18
40
  * Reads the package version from package.json.
19
41
  *
20
42
  * @returns {string}
43
+ * @throws {Error} If version is missing or not a string.
21
44
  */
22
45
  function getPackageVersion() {
23
46
  const pkgPath = join(PACKAGE_ROOT, "package.json");
24
47
  const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
48
+ if (typeof pkg.version !== "string" || !pkg.version) {
49
+ throw new Error(
50
+ "Invalid or missing version in package.json — " +
51
+ "the package may be updating. " +
52
+ `Got: ${JSON.stringify(pkg.version)}`
53
+ );
54
+ }
25
55
  return pkg.version;
26
56
  }
27
57
 
@@ -33,19 +63,40 @@ function getPackageVersion() {
33
63
  * 2. Cached binary for the current package version
34
64
  * 3. Download from GitHub releases and cache
35
65
  *
66
+ * When package files are temporarily unavailable (e.g. during an npm
67
+ * update), retries up to MAX_RETRIES times before giving up. This
68
+ * prevents transient errors when VS Code restarts the MCP server
69
+ * while the package is being replaced.
70
+ *
36
71
  * All command-line arguments (except the node binary and script path)
37
72
  * are forwarded to the cs-mcp binary.
38
73
  */
39
74
  export async function main() {
40
75
  const args = process.argv.slice(2);
41
76
 
42
- try {
43
- const binaryPath = await resolveBinaryPath();
44
- runBinary(binaryPath, args);
45
- } catch (err) {
46
- process.stderr.write(`Error: ${err.message}\n`);
47
- process.exit(1);
77
+ let lastErr;
78
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
79
+ try {
80
+ const binaryPath = await resolveBinaryPath();
81
+ runBinary(binaryPath, args);
82
+ return;
83
+ } catch (err) {
84
+ lastErr = err;
85
+ if (attempt < MAX_RETRIES && isTransientStartupError(err)) {
86
+ process.stderr.write(
87
+ `Package files unavailable (${err.code || err.constructor.name}), ` +
88
+ `retrying in ${RETRY_DELAY_MS / 1000}s ` +
89
+ `(${attempt + 1}/${MAX_RETRIES})...\n`
90
+ );
91
+ await delay(RETRY_DELAY_MS);
92
+ continue;
93
+ }
94
+ break;
95
+ }
48
96
  }
97
+
98
+ process.stderr.write(`Error: ${lastErr.message}\n`);
99
+ process.exit(1);
49
100
  }
50
101
 
51
102
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codescene/codehealth-mcp",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "mcpName": "com.codescene/codescene-mcp-server",
5
5
  "description": "CodeScene MCP Server — Code Health analysis for AI coding agents",
6
6
  "type": "module",