@funnycode/myclaude 0.1.22 → 0.1.23

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
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.1.23] - 2026-06-17
4
+
5
+ ### Fixed
6
+ - **Git Bash "no available terminals" fork error** — enhanced Windows spawn reliability (fixes #6)
7
+ - Reduced spawn semaphore concurrency from 24 → 8 to prevent MSYS2 pty pool exhaustion under heavy parallelism
8
+ - Skip login shell (`-l` flag) on Git Bash/Windows to reduce per-fork startup overhead
9
+ - Added automatic retry with exponential backoff (500ms, 1s, 2s) when pty exhaustion is detected
10
+ - Better error detection: identifies "no available terminals", "cannot fork", and "resource temporarily unavailable" errors
11
+ - Shared cleanup path ensures semaphore is always released, preventing deadlocks
12
+
3
13
  ## [0.1.22] - 2026-06-17
4
14
 
5
15
  ### Added
package/README.md CHANGED
@@ -232,6 +232,7 @@ The following features have been tested and confirmed working:
232
232
  - ✅ Keybinding customization
233
233
  - ✅ API key authentication (with any Anthropic-compatible provider)
234
234
  - ✅ Multi-model providers (Bedrock / Vertex / Foundry)
235
+ - ✅ Claude Code config compatibility — reads `~/.claude/settings.json`, skills, MCPs, plugins, and hooks shared with Claude Code
235
236
 
236
237
  ---
237
238
 
package/dist/myclaude.js CHANGED
@@ -4,8 +4,8 @@
4
4
  // MACRO - build-time constants (injected by build.ts)
5
5
  // MACRO injected by build script
6
6
  globalThis.MACRO = {
7
- VERSION: "0.1.22",
8
- BUILD_TIME: "2026-06-16T12:51:32.359Z",
7
+ VERSION: "0.1.23",
8
+ BUILD_TIME: "2026-06-16T13:12:22.731Z",
9
9
  PACKAGE_URL: "@funnycode/myclaude",
10
10
  NATIVE_PACKAGE_URL: "@funnycode/myclaude",
11
11
  VERSION_CHANGELOG: '',
@@ -347542,10 +347542,17 @@ async function createBashShellProvider(shellPath, options) {
347542
347542
  },
347543
347543
  getSpawnArgs(commandString) {
347544
347544
  const skipLoginShell = lastSnapshotFilePath !== undefined;
347545
- if (skipLoginShell) {
347546
- logForDebugging("Spawning shell without login (-l flag skipped)");
347545
+ const onWindows = getPlatform() === "windows";
347546
+ if (skipLoginShell || onWindows) {
347547
+ if (!onWindows) {
347548
+ logForDebugging("Spawning shell without login (-l flag skipped)");
347549
+ }
347547
347550
  }
347548
- return ["-c", ...skipLoginShell ? [] : ["-l"], commandString];
347551
+ return [
347552
+ "-c",
347553
+ ...skipLoginShell || onWindows ? [] : ["-l"],
347554
+ commandString
347555
+ ];
347549
347556
  },
347550
347557
  async getEnvironmentOverrides(command) {
347551
347558
  const commandUsesTmux = command.includes("tmux");
@@ -347595,7 +347602,7 @@ var init_bashProvider = __esm(() => {
347595
347602
  init_sessionEnvVars();
347596
347603
  init_tmuxSocket();
347597
347604
  init_windowsPaths();
347598
- BASH_SPAWN_SEMAPHORE = createSpawnSemaphore(getPlatform() === "windows" ? 24 : Infinity);
347605
+ BASH_SPAWN_SEMAPHORE = createSpawnSemaphore(getPlatform() === "windows" ? 8 : Infinity);
347599
347606
  });
347600
347607
 
347601
347608
  // src/utils/shell/powershellDetection.ts
@@ -347823,6 +347830,12 @@ async function exec3(command, abortSignal, shellType, options) {
347823
347830
  const shellArgs = isSandboxedPowerShell ? ["-c", commandString] : provider.getSpawnArgs(commandString);
347824
347831
  const envOverrides = await provider.getEnvironmentOverrides(command);
347825
347832
  const isGitBash = shellType === "bash" && getPlatform() === "windows" && !isSandboxedPowerShell;
347833
+ const MAX_GITBASH_RETRIES = 3;
347834
+ const GITBASH_RETRY_BASE_MS = 500;
347835
+ function isGitBashPtyExhaustion(err2) {
347836
+ const msg = errorMessage(err2).toLowerCase();
347837
+ return msg.includes("no available terminals") || msg.includes("cannot fork") || msg.includes("resource temporarily unavailable");
347838
+ }
347826
347839
  if (isGitBash) {
347827
347840
  await getBashSpawnSemaphore().acquire();
347828
347841
  }
@@ -347866,14 +347879,14 @@ async function exec3(command, abortSignal, shellType, options) {
347866
347879
  onStdout(typeof chunk === "string" ? chunk : chunk.toString());
347867
347880
  });
347868
347881
  }
347869
- const nativeCwdFilePath = getPlatform() === "windows" ? posixPathToWindowsPath(cwdFilePath) : cwdFilePath;
347882
+ const nativeCwdFilePath2 = getPlatform() === "windows" ? posixPathToWindowsPath(cwdFilePath) : cwdFilePath;
347870
347883
  shellCommand.result.then(async (result) => {
347871
347884
  if (shouldUseSandbox) {
347872
347885
  SandboxManager2.cleanupAfterCommand();
347873
347886
  }
347874
347887
  if (result && !preventCwdChanges && !result.backgroundTaskId) {
347875
347888
  try {
347876
- let newCwd = readFileSync17(nativeCwdFilePath, {
347889
+ let newCwd = readFileSync17(nativeCwdFilePath2, {
347877
347890
  encoding: "utf8"
347878
347891
  }).trim();
347879
347892
  if (getPlatform() === "windows") {
@@ -347889,13 +347902,95 @@ async function exec3(command, abortSignal, shellType, options) {
347889
347902
  }
347890
347903
  }
347891
347904
  try {
347892
- unlinkSync3(nativeCwdFilePath);
347905
+ unlinkSync3(nativeCwdFilePath2);
347893
347906
  } catch {}
347894
347907
  });
347895
347908
  return shellCommand;
347896
347909
  } catch (error49) {
347897
- if (isGitBash) {
347910
+ if (isGitBash && isGitBashPtyExhaustion(error49)) {
347898
347911
  getBashSpawnSemaphore().release();
347912
+ for (let retry = 1;retry <= MAX_GITBASH_RETRIES; retry++) {
347913
+ const delay = GITBASH_RETRY_BASE_MS * Math.pow(2, retry - 1);
347914
+ logForDebugging(`Git Bash pty exhausted, retry ${retry}/${MAX_GITBASH_RETRIES} in ${delay}ms`);
347915
+ await new Promise((resolve30) => setTimeout(resolve30, delay));
347916
+ if (outputHandle !== undefined) {
347917
+ try {
347918
+ await outputHandle.close();
347919
+ } catch {}
347920
+ outputHandle = undefined;
347921
+ }
347922
+ taskOutput.clear();
347923
+ const retryTaskOutput = new TaskOutput(taskId, onProgress ?? null, !usePipeMode);
347924
+ if (!usePipeMode) {
347925
+ const O_NOFOLLOW = fsConstants4.O_NOFOLLOW ?? 0;
347926
+ try {
347927
+ outputHandle = await open8(taskOutput.path, process.platform === "win32" ? "w" : fsConstants4.O_WRONLY | fsConstants4.O_CREAT | fsConstants4.O_APPEND | O_NOFOLLOW);
347928
+ } catch {}
347929
+ }
347930
+ await getBashSpawnSemaphore().acquire();
347931
+ try {
347932
+ const childProcess = spawn8(spawnBinary, shellArgs, {
347933
+ env: {
347934
+ ...subprocessEnv(),
347935
+ SHELL: shellType === "bash" ? binShell : undefined,
347936
+ GIT_EDITOR: "true",
347937
+ CLAUDECODE: "1",
347938
+ ...envOverrides
347939
+ },
347940
+ cwd: cwd2,
347941
+ stdio: usePipeMode ? ["pipe", "pipe", "pipe"] : ["pipe", outputHandle?.fd, outputHandle?.fd],
347942
+ detached: provider.detached,
347943
+ windowsHide: true
347944
+ });
347945
+ childProcess.once("close", () => getBashSpawnSemaphore().release());
347946
+ const retryCmd = wrapSpawn(childProcess, abortSignal, commandTimeout, retryTaskOutput, shouldAutoBackground);
347947
+ if (outputHandle !== undefined) {
347948
+ try {
347949
+ await outputHandle.close();
347950
+ } catch {}
347951
+ }
347952
+ if (childProcess.stdout && onStdout) {
347953
+ childProcess.stdout.on("data", (chunk) => {
347954
+ onStdout(typeof chunk === "string" ? chunk : chunk.toString());
347955
+ });
347956
+ }
347957
+ retryCmd.result.then(async (result) => {
347958
+ if (shouldUseSandbox)
347959
+ SandboxManager2.cleanupAfterCommand();
347960
+ if (result && !preventCwdChanges && !result.backgroundTaskId) {
347961
+ try {
347962
+ let newCwd = readFileSync17(nativeCwdFilePath, { encoding: "utf8" }).trim();
347963
+ if (getPlatform() === "windows")
347964
+ newCwd = posixPathToWindowsPath(newCwd);
347965
+ if (newCwd.normalize("NFC") !== cwd2) {
347966
+ setCwd(newCwd, cwd2);
347967
+ invalidateSessionEnvCache();
347968
+ onCwdChangedForHooks(cwd2, newCwd);
347969
+ }
347970
+ } catch {
347971
+ logEvent("tengu_shell_set_cwd", { success: false });
347972
+ }
347973
+ }
347974
+ try {
347975
+ unlinkSync3(nativeCwdFilePath);
347976
+ } catch {}
347977
+ });
347978
+ return retryCmd;
347979
+ } catch (retryError) {
347980
+ getBashSpawnSemaphore().release();
347981
+ if (!isGitBashPtyExhaustion(retryError)) {
347982
+ logForDebugging(`Git Bash retry ${retry} failed with non-pty error: ${errorMessage(retryError)}`);
347983
+ break;
347984
+ }
347985
+ if (retry === MAX_GITBASH_RETRIES) {
347986
+ logForDebugging(`Git Bash all ${MAX_GITBASH_RETRIES} retries exhausted, giving up`);
347987
+ }
347988
+ }
347989
+ }
347990
+ } else {
347991
+ if (isGitBash) {
347992
+ getBashSpawnSemaphore().release();
347993
+ }
347899
347994
  }
347900
347995
  if (outputHandle !== undefined) {
347901
347996
  try {
package/dist/myclaude.mjs CHANGED
@@ -4,8 +4,8 @@
4
4
  // MACRO - build-time constants (injected by build.ts)
5
5
  // MACRO injected by build script
6
6
  globalThis.MACRO = {
7
- VERSION: "0.1.22",
8
- BUILD_TIME: "2026-06-16T12:51:32.359Z",
7
+ VERSION: "0.1.23",
8
+ BUILD_TIME: "2026-06-16T13:12:22.731Z",
9
9
  PACKAGE_URL: "@funnycode/myclaude",
10
10
  NATIVE_PACKAGE_URL: "@funnycode/myclaude",
11
11
  VERSION_CHANGELOG: '',
@@ -347542,10 +347542,17 @@ async function createBashShellProvider(shellPath, options) {
347542
347542
  },
347543
347543
  getSpawnArgs(commandString) {
347544
347544
  const skipLoginShell = lastSnapshotFilePath !== undefined;
347545
- if (skipLoginShell) {
347546
- logForDebugging("Spawning shell without login (-l flag skipped)");
347545
+ const onWindows = getPlatform() === "windows";
347546
+ if (skipLoginShell || onWindows) {
347547
+ if (!onWindows) {
347548
+ logForDebugging("Spawning shell without login (-l flag skipped)");
347549
+ }
347547
347550
  }
347548
- return ["-c", ...skipLoginShell ? [] : ["-l"], commandString];
347551
+ return [
347552
+ "-c",
347553
+ ...skipLoginShell || onWindows ? [] : ["-l"],
347554
+ commandString
347555
+ ];
347549
347556
  },
347550
347557
  async getEnvironmentOverrides(command) {
347551
347558
  const commandUsesTmux = command.includes("tmux");
@@ -347595,7 +347602,7 @@ var init_bashProvider = __esm(() => {
347595
347602
  init_sessionEnvVars();
347596
347603
  init_tmuxSocket();
347597
347604
  init_windowsPaths();
347598
- BASH_SPAWN_SEMAPHORE = createSpawnSemaphore(getPlatform() === "windows" ? 24 : Infinity);
347605
+ BASH_SPAWN_SEMAPHORE = createSpawnSemaphore(getPlatform() === "windows" ? 8 : Infinity);
347599
347606
  });
347600
347607
 
347601
347608
  // src/utils/shell/powershellDetection.ts
@@ -347823,6 +347830,12 @@ async function exec3(command, abortSignal, shellType, options) {
347823
347830
  const shellArgs = isSandboxedPowerShell ? ["-c", commandString] : provider.getSpawnArgs(commandString);
347824
347831
  const envOverrides = await provider.getEnvironmentOverrides(command);
347825
347832
  const isGitBash = shellType === "bash" && getPlatform() === "windows" && !isSandboxedPowerShell;
347833
+ const MAX_GITBASH_RETRIES = 3;
347834
+ const GITBASH_RETRY_BASE_MS = 500;
347835
+ function isGitBashPtyExhaustion(err2) {
347836
+ const msg = errorMessage(err2).toLowerCase();
347837
+ return msg.includes("no available terminals") || msg.includes("cannot fork") || msg.includes("resource temporarily unavailable");
347838
+ }
347826
347839
  if (isGitBash) {
347827
347840
  await getBashSpawnSemaphore().acquire();
347828
347841
  }
@@ -347866,14 +347879,14 @@ async function exec3(command, abortSignal, shellType, options) {
347866
347879
  onStdout(typeof chunk === "string" ? chunk : chunk.toString());
347867
347880
  });
347868
347881
  }
347869
- const nativeCwdFilePath = getPlatform() === "windows" ? posixPathToWindowsPath(cwdFilePath) : cwdFilePath;
347882
+ const nativeCwdFilePath2 = getPlatform() === "windows" ? posixPathToWindowsPath(cwdFilePath) : cwdFilePath;
347870
347883
  shellCommand.result.then(async (result) => {
347871
347884
  if (shouldUseSandbox) {
347872
347885
  SandboxManager2.cleanupAfterCommand();
347873
347886
  }
347874
347887
  if (result && !preventCwdChanges && !result.backgroundTaskId) {
347875
347888
  try {
347876
- let newCwd = readFileSync17(nativeCwdFilePath, {
347889
+ let newCwd = readFileSync17(nativeCwdFilePath2, {
347877
347890
  encoding: "utf8"
347878
347891
  }).trim();
347879
347892
  if (getPlatform() === "windows") {
@@ -347889,13 +347902,95 @@ async function exec3(command, abortSignal, shellType, options) {
347889
347902
  }
347890
347903
  }
347891
347904
  try {
347892
- unlinkSync3(nativeCwdFilePath);
347905
+ unlinkSync3(nativeCwdFilePath2);
347893
347906
  } catch {}
347894
347907
  });
347895
347908
  return shellCommand;
347896
347909
  } catch (error49) {
347897
- if (isGitBash) {
347910
+ if (isGitBash && isGitBashPtyExhaustion(error49)) {
347898
347911
  getBashSpawnSemaphore().release();
347912
+ for (let retry = 1;retry <= MAX_GITBASH_RETRIES; retry++) {
347913
+ const delay = GITBASH_RETRY_BASE_MS * Math.pow(2, retry - 1);
347914
+ logForDebugging(`Git Bash pty exhausted, retry ${retry}/${MAX_GITBASH_RETRIES} in ${delay}ms`);
347915
+ await new Promise((resolve30) => setTimeout(resolve30, delay));
347916
+ if (outputHandle !== undefined) {
347917
+ try {
347918
+ await outputHandle.close();
347919
+ } catch {}
347920
+ outputHandle = undefined;
347921
+ }
347922
+ taskOutput.clear();
347923
+ const retryTaskOutput = new TaskOutput(taskId, onProgress ?? null, !usePipeMode);
347924
+ if (!usePipeMode) {
347925
+ const O_NOFOLLOW = fsConstants4.O_NOFOLLOW ?? 0;
347926
+ try {
347927
+ outputHandle = await open8(taskOutput.path, process.platform === "win32" ? "w" : fsConstants4.O_WRONLY | fsConstants4.O_CREAT | fsConstants4.O_APPEND | O_NOFOLLOW);
347928
+ } catch {}
347929
+ }
347930
+ await getBashSpawnSemaphore().acquire();
347931
+ try {
347932
+ const childProcess = spawn8(spawnBinary, shellArgs, {
347933
+ env: {
347934
+ ...subprocessEnv(),
347935
+ SHELL: shellType === "bash" ? binShell : undefined,
347936
+ GIT_EDITOR: "true",
347937
+ CLAUDECODE: "1",
347938
+ ...envOverrides
347939
+ },
347940
+ cwd: cwd2,
347941
+ stdio: usePipeMode ? ["pipe", "pipe", "pipe"] : ["pipe", outputHandle?.fd, outputHandle?.fd],
347942
+ detached: provider.detached,
347943
+ windowsHide: true
347944
+ });
347945
+ childProcess.once("close", () => getBashSpawnSemaphore().release());
347946
+ const retryCmd = wrapSpawn(childProcess, abortSignal, commandTimeout, retryTaskOutput, shouldAutoBackground);
347947
+ if (outputHandle !== undefined) {
347948
+ try {
347949
+ await outputHandle.close();
347950
+ } catch {}
347951
+ }
347952
+ if (childProcess.stdout && onStdout) {
347953
+ childProcess.stdout.on("data", (chunk) => {
347954
+ onStdout(typeof chunk === "string" ? chunk : chunk.toString());
347955
+ });
347956
+ }
347957
+ retryCmd.result.then(async (result) => {
347958
+ if (shouldUseSandbox)
347959
+ SandboxManager2.cleanupAfterCommand();
347960
+ if (result && !preventCwdChanges && !result.backgroundTaskId) {
347961
+ try {
347962
+ let newCwd = readFileSync17(nativeCwdFilePath, { encoding: "utf8" }).trim();
347963
+ if (getPlatform() === "windows")
347964
+ newCwd = posixPathToWindowsPath(newCwd);
347965
+ if (newCwd.normalize("NFC") !== cwd2) {
347966
+ setCwd(newCwd, cwd2);
347967
+ invalidateSessionEnvCache();
347968
+ onCwdChangedForHooks(cwd2, newCwd);
347969
+ }
347970
+ } catch {
347971
+ logEvent("tengu_shell_set_cwd", { success: false });
347972
+ }
347973
+ }
347974
+ try {
347975
+ unlinkSync3(nativeCwdFilePath);
347976
+ } catch {}
347977
+ });
347978
+ return retryCmd;
347979
+ } catch (retryError) {
347980
+ getBashSpawnSemaphore().release();
347981
+ if (!isGitBashPtyExhaustion(retryError)) {
347982
+ logForDebugging(`Git Bash retry ${retry} failed with non-pty error: ${errorMessage(retryError)}`);
347983
+ break;
347984
+ }
347985
+ if (retry === MAX_GITBASH_RETRIES) {
347986
+ logForDebugging(`Git Bash all ${MAX_GITBASH_RETRIES} retries exhausted, giving up`);
347987
+ }
347988
+ }
347989
+ }
347990
+ } else {
347991
+ if (isGitBash) {
347992
+ getBashSpawnSemaphore().release();
347993
+ }
347899
347994
  }
347900
347995
  if (outputHandle !== undefined) {
347901
347996
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@funnycode/myclaude",
3
- "version": "0.1.22",
3
+ "version": "0.1.23",
4
4
  "private": false,
5
5
  "description": "An open-source AI coding assistant in your terminal - powered by Claude",
6
6
  "license": "MIT",