@gonkagate/claude-code 0.1.0 → 0.1.2

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/CHANGELOG.md CHANGED
@@ -2,10 +2,35 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ - Improved npm package metadata and README copy for better package-page clarity, discovery, and onboarding.
5
6
  - Added a curated model registry and model picker to the public installer flow.
6
7
  - Added `--model <model-key>` and `--model=<model-key>` support for curated non-secret model selection.
7
8
  - Kept the public Claude Code contract tight by limiting model selection to the curated GonkaGate registry only.
8
9
  - Updated docs and tests for model selection while preserving local git ignore protection and owner-only settings file permissions.
10
+ - Hardened local scope against symlinked `.claude` paths that could redirect secret writes outside the ignored settings path.
11
+ - Extended local git protection to ignore timestamped backup files for `.claude/settings.local.json`.
12
+ - Normalized backup permissions to owner-only mode so secret-bearing backups are not left world-readable.
13
+ - Made local scope fail closed if `.claude/settings.local.json` is already tracked by git before secrets are written.
14
+ - Hardened local scope against symlinked path components anywhere between the repo root and the target settings file.
15
+ - Pinned GitHub Actions workflows to immutable commit SHAs for release pipeline hardening.
16
+ - Restored automated npm publish dispatch after Release Please creates a new release tag.
17
+ - Made publish reruns skip versions that are already present on npm instead of failing with a duplicate-version error.
18
+
19
+ ## [0.1.2](https://github.com/GonkaGate/gonkagate-claude-code/compare/v0.1.1...v0.1.2) (2026-03-31)
20
+
21
+
22
+ ### Bug Fixes
23
+
24
+ * restore automated npm publishes ([42bde87](https://github.com/GonkaGate/gonkagate-claude-code/commit/42bde8795e264c8950017aa5acff0475b4fb0c8e))
25
+ * restore automated npm publishes ([52fcd8e](https://github.com/GonkaGate/gonkagate-claude-code/commit/52fcd8e428ccae967871aba800344dd786a048ce))
26
+
27
+ ## [0.1.1](https://github.com/GonkaGate/gonkagate-claude-code/compare/v0.1.0...v0.1.1) (2026-03-31)
28
+
29
+
30
+ ### Bug Fixes
31
+
32
+ * stop duplicate npm publishes ([1ac876a](https://github.com/GonkaGate/gonkagate-claude-code/commit/1ac876a4fb1c99bd1a1cc892e38ee2c7df3be799))
33
+ * stop duplicate npm publishes ([58d022f](https://github.com/GonkaGate/gonkagate-claude-code/commit/58d022ff2486fa6156badc791cb236f580b68a9c))
9
34
 
10
35
  ## 0.1.0
11
36
 
package/README.md CHANGED
@@ -1,10 +1,12 @@
1
- # gonkagate-claude-code
1
+ # @gonkagate/claude-code
2
2
 
3
- Set up Claude Code to use GonkaGate in one step.
3
+ Set up Claude Code to use GonkaGate in one `npx` command.
4
4
 
5
- This tool configures local `Claude Code` to use GonkaGate's Anthropic-compatible gateway at `https://api.gonkagate.com`.
5
+ This CLI installer is for developers who already have local `Claude Code` and want to use it with GonkaGate, the gateway to Gonka Network, without editing shell profiles, exporting long env var blocks, or writing `.env` files by hand.
6
6
 
7
- It does not install `Claude Code` itself. It sets up an existing local Claude Code install.
7
+ Under the hood it configures Claude Code to use GonkaGate's Anthropic-compatible endpoint at `https://api.gonkagate.com`.
8
+
9
+ It does not install `Claude Code` itself. It configures an existing local Claude Code install.
8
10
 
9
11
  ## Quick Start
10
12
 
@@ -12,9 +14,11 @@ It does not install `Claude Code` itself. It sets up an existing local Claude Co
12
14
  npx @gonkagate/claude-code
13
15
  ```
14
16
 
17
+ Need an API key first? [Create one on GonkaGate](https://gonkagate.com/en).
18
+
15
19
  You will be asked for:
16
20
 
17
- - your GonkaGate API key (`gp-...`)
21
+ - your GonkaGate API key (`gp-...`) in a hidden interactive prompt
18
22
  - a model from the supported GonkaGate list
19
23
  - setup scope: `user` or `local`
20
24
 
@@ -24,6 +28,12 @@ You need:
24
28
  - Node.js 18+
25
29
  - a GonkaGate API key
26
30
 
31
+ ## Supported Model
32
+
33
+ Current public Claude Code model in the curated registry:
34
+
35
+ - `qwen3-235b` -> `qwen/qwen3-235b-a22b-instruct-2507-fp8`
36
+
27
37
  ## What It Does
28
38
 
29
39
  The tool writes Claude Code settings so you can keep running `claude` normally afterward.
@@ -41,7 +51,10 @@ It also:
41
51
  - preserves unrelated Claude Code settings
42
52
  - creates a backup before overwriting an existing settings file
43
53
  - writes settings files with owner-only permissions
44
- - adds `.claude/settings.local.json` to `.git/info/exclude` for local setup inside a git repo
54
+ - writes backup files with owner-only permissions
55
+ - adds `.claude/settings.local.json` and local backup files to `.git/info/exclude` for local setup inside a git repo
56
+ - refuses local setup if `.claude/settings.local.json` is already tracked by git
57
+ - refuses local setup if the target path traverses a symlinked path component, or if `.claude` / the local settings file is a symlink
45
58
 
46
59
  ## Fixed GonkaGate Setup
47
60
 
@@ -53,12 +66,6 @@ These parts are intentionally fixed:
53
66
 
54
67
  This tool does not ask for a custom base URL and does not accept arbitrary custom model IDs.
55
68
 
56
- ## Supported Model
57
-
58
- Current public Claude Code model:
59
-
60
- - `qwen3-235b` -> `qwen/qwen3-235b-a22b-instruct-2507-fp8`
61
-
62
69
  The selected model is written into all Claude Code model env vars used by this setup flow.
63
70
 
64
71
  ## Verify
@@ -73,6 +80,7 @@ After setup:
73
80
  ## What This Tool Does Not Do
74
81
 
75
82
  - It does not configure `claude.ai`
83
+ - It does not install `Claude Code` itself
76
84
  - It does not edit `.zshrc`, `.bashrc`, PowerShell profiles, or other shell startup files
77
85
  - It does not write `.env` files
78
86
  - It does not support arbitrary custom model IDs
@@ -80,9 +88,9 @@ After setup:
80
88
 
81
89
  ## Need Help?
82
90
 
83
- - Troubleshooting: [docs/troubleshooting.md](docs/troubleshooting.md)
84
- - Security notes: [docs/security.md](docs/security.md)
85
- - Internal behavior: [docs/how-it-works.md](docs/how-it-works.md)
91
+ - Troubleshooting: [docs/troubleshooting.md](https://github.com/GonkaGate/gonkagate-claude-code/blob/main/docs/troubleshooting.md)
92
+ - Security notes: [docs/security.md](https://github.com/GonkaGate/gonkagate-claude-code/blob/main/docs/security.md)
93
+ - Internal behavior: [docs/how-it-works.md](https://github.com/GonkaGate/gonkagate-claude-code/blob/main/docs/how-it-works.md)
86
94
 
87
95
  ## Development
88
96
 
@@ -1,10 +1,11 @@
1
- import { copyFile } from "node:fs/promises";
1
+ import { chmod, copyFile } from "node:fs/promises";
2
2
  function toBackupSuffix(timestamp = new Date()) {
3
3
  return timestamp.toISOString().replace(/[:.]/g, "-");
4
4
  }
5
5
  export async function createBackup(filePath) {
6
6
  const backupPath = `${filePath}.backup-${toBackupSuffix()}`;
7
7
  await copyFile(filePath, backupPath);
8
+ await chmod(backupPath, 0o600);
8
9
  return backupPath;
9
10
  }
10
11
  //# sourceMappingURL=backup.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"backup.js","sourceRoot":"","sources":["../../src/install/backup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,SAAS,cAAc,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE;IAC5C,OAAO,SAAS,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;AACvD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,QAAgB;IACjD,MAAM,UAAU,GAAG,GAAG,QAAQ,WAAW,cAAc,EAAE,EAAE,CAAC;IAC5D,MAAM,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IACrC,OAAO,UAAU,CAAC;AACpB,CAAC"}
1
+ {"version":3,"file":"backup.js","sourceRoot":"","sources":["../../src/install/backup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEnD,SAAS,cAAc,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE;IAC5C,OAAO,SAAS,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;AACvD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,QAAgB;IACjD,MAAM,UAAU,GAAG,GAAG,QAAQ,WAAW,cAAc,EAAE,EAAE,CAAC;IAC5D,MAAM,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IACrC,MAAM,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAC/B,OAAO,UAAU,CAAC;AACpB,CAAC"}
@@ -1,31 +1,104 @@
1
- import { access, mkdir, readFile, stat, writeFile } from "node:fs/promises";
1
+ import { execFile } from "node:child_process";
2
+ import { access, lstat, mkdir, readFile, stat, writeFile } from "node:fs/promises";
2
3
  import path from "node:path";
4
+ import { promisify } from "node:util";
5
+ const execFileAsync = promisify(execFile);
3
6
  export async function ensureLocalSettingsIgnored(targetPath) {
4
7
  const gitContext = await findGitContext(path.dirname(targetPath));
8
+ await assertSafeLocalSettingsTarget(targetPath, gitContext?.repoRoot);
5
9
  if (!gitContext) {
6
10
  return;
7
11
  }
8
- const relativeTargetPath = path.relative(gitContext.repoRoot, targetPath);
9
- if (relativeTargetPath.startsWith("..")) {
10
- throw new Error("Expected local Claude Code settings to stay inside the current git repository.");
11
- }
12
+ const relativeTargetPath = requireRepoRelativePath(targetPath, gitContext.repoRoot);
13
+ await assertTargetIsNotTracked(relativeTargetPath, gitContext.repoRoot);
12
14
  const normalizedRelativePath = relativeTargetPath.split(path.sep).join("/");
13
- const ignoreEntry = `/${normalizedRelativePath}`;
15
+ const ignoreEntries = [
16
+ `/${normalizedRelativePath}`,
17
+ `/${normalizedRelativePath}.backup-*`
18
+ ];
14
19
  const excludePath = path.join(gitContext.gitDir, "info", "exclude");
15
20
  const existingContent = await readOptionalFile(excludePath);
16
21
  const existingEntries = new Set(existingContent
17
22
  .split(/\r?\n/)
18
23
  .map((line) => line.trim())
19
24
  .filter((line) => line.length > 0 && !line.startsWith("#")));
20
- if (existingEntries.has(ignoreEntry)) {
25
+ const missingEntries = ignoreEntries.filter((ignoreEntry) => !existingEntries.has(ignoreEntry));
26
+ if (missingEntries.length === 0) {
21
27
  return;
22
28
  }
23
29
  await mkdir(path.dirname(excludePath), { recursive: true });
24
30
  const nextContent = existingContent.length === 0
25
- ? `${ignoreEntry}\n`
26
- : `${existingContent}${existingContent.endsWith("\n") ? "" : "\n"}${ignoreEntry}\n`;
31
+ ? `${missingEntries.join("\n")}\n`
32
+ : `${existingContent}${existingContent.endsWith("\n") ? "" : "\n"}${missingEntries.join("\n")}\n`;
27
33
  await writeFile(excludePath, nextContent, "utf8");
28
34
  }
35
+ async function assertSafeLocalSettingsTarget(targetPath, repoRoot) {
36
+ const pathsToInspect = repoRoot
37
+ ? getPathsFromRepoRoot(repoRoot, targetPath)
38
+ : [path.dirname(targetPath), targetPath];
39
+ for (const currentPath of pathsToInspect) {
40
+ await assertPathIsNotSymlink(currentPath, getSymlinkErrorMessage(currentPath, targetPath, repoRoot));
41
+ }
42
+ }
43
+ async function assertPathIsNotSymlink(filePath, errorMessage) {
44
+ try {
45
+ const fileStats = await lstat(filePath);
46
+ if (fileStats.isSymbolicLink()) {
47
+ throw new Error(errorMessage);
48
+ }
49
+ }
50
+ catch (error) {
51
+ if (isMissingFileError(error)) {
52
+ return;
53
+ }
54
+ throw error;
55
+ }
56
+ }
57
+ function getPathsFromRepoRoot(repoRoot, targetPath) {
58
+ const relativeTargetPath = requireRepoRelativePath(targetPath, repoRoot);
59
+ const targetSegments = relativeTargetPath.split(path.sep).filter((segment) => segment.length > 0);
60
+ const paths = [repoRoot];
61
+ let currentPath = repoRoot;
62
+ for (const segment of targetSegments) {
63
+ currentPath = path.join(currentPath, segment);
64
+ paths.push(currentPath);
65
+ }
66
+ return paths;
67
+ }
68
+ function requireRepoRelativePath(targetPath, repoRoot) {
69
+ const relativeTargetPath = path.relative(repoRoot, targetPath);
70
+ if (relativeTargetPath.length === 0 || relativeTargetPath.startsWith("..") || path.isAbsolute(relativeTargetPath)) {
71
+ throw new Error("Expected local Claude Code settings to stay inside the current git repository.");
72
+ }
73
+ return relativeTargetPath;
74
+ }
75
+ function getSymlinkErrorMessage(currentPath, targetPath, repoRoot) {
76
+ if (currentPath === path.dirname(targetPath)) {
77
+ return 'Refusing to write local Claude Code settings into a symlinked ".claude" directory.';
78
+ }
79
+ if (currentPath === targetPath) {
80
+ return "Refusing to overwrite local Claude Code settings through a symlinked settings file.";
81
+ }
82
+ const label = repoRoot ? path.relative(repoRoot, currentPath) || path.basename(currentPath) : currentPath;
83
+ return `Refusing local Claude Code setup through a symlinked path component: ${label}.`;
84
+ }
85
+ async function assertTargetIsNotTracked(relativeTargetPath, repoRoot) {
86
+ try {
87
+ await execFileAsync("git", ["-C", repoRoot, "ls-files", "--error-unmatch", "--", relativeTargetPath], {
88
+ encoding: "utf8"
89
+ });
90
+ throw new Error(`Refusing local install because ${relativeTargetPath} is already tracked by git. Remove it from the index before writing secrets.`);
91
+ }
92
+ catch (error) {
93
+ if (isMissingGitBinaryError(error)) {
94
+ throw new Error("Git is required to verify that local Claude Code settings are not already tracked before writing secrets.");
95
+ }
96
+ if (isGitPathUnmatchedError(error)) {
97
+ return;
98
+ }
99
+ throw error;
100
+ }
101
+ }
29
102
  async function findGitContext(startDirectory) {
30
103
  let currentDirectory = path.resolve(startDirectory);
31
104
  for (;;) {
@@ -73,4 +146,13 @@ async function readOptionalFile(filePath) {
73
146
  return "";
74
147
  }
75
148
  }
149
+ function isMissingFileError(error) {
150
+ return error instanceof Error && "code" in error && error.code === "ENOENT";
151
+ }
152
+ function isMissingGitBinaryError(error) {
153
+ return error instanceof Error && "code" in error && error.code === "ENOENT";
154
+ }
155
+ function isGitPathUnmatchedError(error) {
156
+ return error instanceof Error && "code" in error && error.code === 1;
157
+ }
76
158
  //# sourceMappingURL=local-git-ignore.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"local-git-ignore.js","sourceRoot":"","sources":["../../src/install/local-git-ignore.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"local-git-ignore.js","sourceRoot":"","sources":["../../src/install/local-git-ignore.ts"],"names":[],"mappings":"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"}
@@ -29,7 +29,9 @@ The installer writes one Claude Code settings file:
29
29
 
30
30
  It does not touch any shell profile or other configuration file.
31
31
 
32
- For `local` scope inside a git repository, it also adds `.claude/settings.local.json` to `.git/info/exclude` before writing the file.
32
+ For `local` scope inside a git repository, it also adds `.claude/settings.local.json` and `.claude/settings.local.json.backup-*` to `.git/info/exclude` before writing the file.
33
+
34
+ For safety, local scope refuses to proceed if `.claude/settings.local.json` is already tracked by git, or if any existing path component on the way to the target is a symlink. That includes a symlinked `.claude` directory and a symlinked local settings file.
33
35
 
34
36
  ## Write behavior
35
37
 
@@ -37,7 +39,7 @@ When the target file already exists, the installer:
37
39
 
38
40
  1. Parses the existing JSON
39
41
  2. Refuses to continue if the JSON is invalid
40
- 3. Creates a timestamped backup
42
+ 3. Creates a timestamped backup with owner-only permissions
41
43
  4. Preserves unrelated settings
42
44
  5. Updates only the relevant `env` keys, including all Claude model env vars with the selected curated model id
43
45
  6. Adds Claude Code's JSON schema if `$schema` is missing
package/docs/security.md CHANGED
@@ -17,14 +17,18 @@ Claude Code reads environment variables from its settings files. That means the
17
17
  - `~/.claude/settings.json`
18
18
  - `.claude/settings.local.json`
19
19
 
20
- This repo does not write `.env` files and does not modify shell startup files. It also writes the target settings file with owner-only permissions.
20
+ This repo does not write `.env` files and does not modify shell startup files. It also writes the target settings file with owner-only permissions and locks backup files down to owner-only permissions too.
21
21
 
22
22
  ## Local scope hygiene
23
23
 
24
- If you choose `local` scope inside a git repository, the installer adds `.claude/settings.local.json` to the repo's `.git/info/exclude` before writing the file so the secret-bearing settings file is not immediately visible to git.
24
+ If you choose `local` scope inside a git repository, the installer first verifies that `.claude/settings.local.json` is not already tracked by git. If it is already tracked, the installer stops instead of writing a secret into a tracked file.
25
+
26
+ For untracked local installs, the installer adds `.claude/settings.local.json` and its timestamped backup pattern to the repo's `.git/info/exclude` before writing the file so secret-bearing local settings do not immediately show up in git.
25
27
 
26
28
  If your team already manages ignores another way, that is still fine. The important rule is that `.claude/settings.local.json` must stay uncommitted.
27
29
 
30
+ The installer also refuses local setup through a symlinked path component on the way to `.claude/settings.local.json`, including a symlinked `.claude` directory or a symlinked local settings file. That prevents a repository from redirecting the secret-bearing write to some other tracked or non-local location.
31
+
28
32
  ## Backup behavior
29
33
 
30
34
  Before overwriting an existing target settings file, the installer creates a timestamped backup next to it. If something looks wrong afterward, restore from that backup and rerun the installer.
@@ -51,6 +51,22 @@ Restore from the timestamped backup next to the settings file, or fix the JSON m
51
51
 
52
52
  Rerun the installer and choose `local` scope. That writes `.claude/settings.local.json` in the current repository instead of editing your global `~/.claude/settings.json`.
53
53
 
54
+ ## Local install refused because `.claude/settings.local.json` is already tracked by git
55
+
56
+ For `local` scope, the installer now stops if `.claude/settings.local.json` is already a tracked file in the repository.
57
+
58
+ That safety check prevents the installer from writing your API key into a file that `git status` would continue to treat as commit-ready.
59
+
60
+ Remove the file from the git index first, or use `user` scope instead, then rerun the installer.
61
+
62
+ ## Local install refused because the target path uses a symlink
63
+
64
+ For `local` scope, the installer now rejects a symlinked path component anywhere on the way to `.claude/settings.local.json`, including a symlinked `.claude` directory or a symlinked `.claude/settings.local.json`.
65
+
66
+ That safety check prevents a repository from redirecting your API key into some other location that git might track.
67
+
68
+ Replace the symlink with a real `.claude` directory, then rerun the installer.
69
+
54
70
  ## Manual settings edits
55
71
 
56
72
  Manual edits to the Claude Code settings file are possible, but they are outside the supported public install flow for this repo.
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@gonkagate/claude-code",
3
- "version": "0.1.0",
4
- "description": "Connect Claude Code to GonkaGate in one step.",
5
- "homepage": "https://github.com/GonkaGate/gonkagate-claude-code",
3
+ "version": "0.1.2",
4
+ "description": "CLI installer for using Claude Code with GonkaGate on Gonka Network.",
5
+ "homepage": "https://github.com/GonkaGate/gonkagate-claude-code#readme",
6
6
  "bugs": {
7
7
  "url": "https://github.com/GonkaGate/gonkagate-claude-code/issues"
8
8
  },
@@ -12,7 +12,7 @@
12
12
  },
13
13
  "type": "module",
14
14
  "bin": {
15
- "gonkagate-claude-code": "./bin/gonkagate-claude-code.js"
15
+ "gonkagate-claude-code": "bin/gonkagate-claude-code.js"
16
16
  },
17
17
  "files": [
18
18
  "bin",
@@ -27,18 +27,22 @@
27
27
  "build": "tsc -p tsconfig.json",
28
28
  "dev": "tsx src/cli.ts",
29
29
  "prepack": "npm run build",
30
- "test": "tsx --test test/**/*.test.ts",
30
+ "test": "node scripts/run-tests.mjs",
31
31
  "ci": "npm test && npm run build"
32
32
  },
33
33
  "engines": {
34
34
  "node": ">=18"
35
35
  },
36
36
  "keywords": [
37
- "claude-code",
38
37
  "gonkagate",
39
- "anthropic",
40
- "installer",
41
- "cli"
38
+ "gonka",
39
+ "gonka ai",
40
+ "gonka-network",
41
+ "gonka blockchain",
42
+ "gonka-api",
43
+ "gonka gateway",
44
+ "claude-code",
45
+ "claude code"
42
46
  ],
43
47
  "license": "Apache-2.0",
44
48
  "packageManager": "npm@11.11.1",
@@ -0,0 +1,49 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { readdirSync } from 'node:fs';
3
+ import { dirname, join, resolve } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ const scriptDir = dirname(fileURLToPath(import.meta.url));
7
+ const repoRoot = resolve(scriptDir, '..');
8
+ const testRoot = join(repoRoot, 'test');
9
+
10
+ function collectTestFiles(directory) {
11
+ const entries = readdirSync(directory, { withFileTypes: true }).sort((a, b) =>
12
+ a.name.localeCompare(b.name),
13
+ );
14
+ const files = [];
15
+
16
+ for (const entry of entries) {
17
+ const fullPath = join(directory, entry.name);
18
+
19
+ if (entry.isDirectory()) {
20
+ files.push(...collectTestFiles(fullPath));
21
+ continue;
22
+ }
23
+
24
+ if (entry.isFile() && entry.name.endsWith('.test.ts')) {
25
+ files.push(fullPath);
26
+ }
27
+ }
28
+
29
+ return files;
30
+ }
31
+
32
+ const testFiles = collectTestFiles(testRoot);
33
+
34
+ if (testFiles.length === 0) {
35
+ console.error('No test files found under test/.');
36
+ process.exit(1);
37
+ }
38
+
39
+ const tsxCliPath = join(repoRoot, 'node_modules', 'tsx', 'dist', 'cli.mjs');
40
+ const result = spawnSync(process.execPath, [tsxCliPath, '--test', ...testFiles], {
41
+ cwd: repoRoot,
42
+ stdio: 'inherit',
43
+ });
44
+
45
+ if (result.error) {
46
+ throw result.error;
47
+ }
48
+
49
+ process.exit(result.status ?? 1);