Skip to content

Conversation

@shuv1337
Copy link
Collaborator

@shuv1337 shuv1337 commented Jan 2, 2026

Summary

Fixes issue #245 where shuvcode fails with "preload not found @opentui/solid/preload" when run from directories with their own bunfig.toml.

Changes

Fixes

  • Add postinstall.mjs that creates a symlink from bin/shuvcode to the platform-specific compiled binary, matching upstream opencode behavior
  • Update build.ts to enable autoloadTsconfig and autoloadPackageJson compiler options
  • Update publish.ts to include postinstall.mjs in the published package and configure the postinstall script

Documentation

  • Add planning document CONTEXT/PLAN-245-bunfig-preload-conflict-fix-2026-01-02.md explaining the root cause analysis and solution

Root Cause

The shuvcode package used a Node.js launcher script at bin/shuvcode, while upstream opencode uses a symlink to the compiled ELF binary. When Bun executes the launcher script, it reads the CWD's bunfig.toml and attempts to apply preloads before the actual binary runs.

Breaking Changes

None

Testing

Manual testing required:

  1. Install shuvcode globally: bun install -g shuvcode
  2. Navigate to a directory with a bunfig.toml containing preloads
  3. Run shuvcode - should launch successfully without preload errors

Greptile Summary

Fixes issue #245 by adding a postinstall script that creates a symlink from bin/shuvcode to the platform-specific compiled binary, matching upstream opencode-ai behavior. This ensures the compiled ELF binary runs directly instead of through a Node.js launcher script, preventing Bun from loading the CWD's bunfig.toml configuration.

Key changes:

  • Added postinstall.mjs that detects platform/arch and creates symlink to the appropriate binary package
  • Updated publish.ts to include postinstall script and copy postinstall.mjs to published package
  • Updated build.ts to enable autoloadTsconfig and autoloadPackageJson compiler options

Issue found:

  • The postinstall.mjs script only tries to resolve the exact package name (e.g., shuvcode-linux-x64) and will fail for baseline variants like shuvcode-linux-x64-baseline. The existing bin/shuvcode launcher handles this by searching for packages that start with the base name. The postinstall script needs similar fallback logic.

Confidence Score: 3/5

  • Safe to merge with caution - fix addresses the core issue but may fail for baseline CPU variants
  • The implementation correctly addresses the root cause (symlink to binary instead of Node launcher), but contains a logic bug that will cause postinstall failures for users on systems requiring baseline variants (older CPUs without AVX2). The existing launcher's fallback logic needs to be replicated in the postinstall script.
  • Pay close attention to packages/opencode/postinstall.mjs - missing baseline variant fallback will cause installation failures on some systems

Important Files Changed

Filename Overview
packages/opencode/postinstall.mjs Creates symlink from bin/shuvcode to platform-specific binary; missing baseline variant fallback logic
packages/opencode/script/build.ts Added autoloadTsconfig: true and autoloadPackageJson: true to compiler options
packages/opencode/script/publish.ts Added postinstall script configuration and copies postinstall.mjs to published package

Sequence Diagram

sequenceDiagram
    participant User
    participant Shell
    participant NPM as npm/bun
    participant PostInstall as postinstall.mjs
    participant BinDir as bin/shuvcode
    participant PlatformBinary as shuvcode-linux-x64/bin/shuvcode
    participant BunRuntime as Bun Runtime

    Note over User,BunRuntime: Installation Phase
    User->>NPM: bun install -g shuvcode
    NPM->>NPM: Install main package
    NPM->>NPM: Install optional dependency (shuvcode-linux-x64)
    NPM->>PostInstall: Run postinstall script
    PostInstall->>PostInstall: detectPlatformAndArch()
    PostInstall->>PostInstall: findBinary() - resolve shuvcode-linux-x64
    PostInstall->>BinDir: Create bin/ directory
    PostInstall->>BinDir: fs.symlinkSync(PlatformBinary, bin/shuvcode)
    BinDir-->>PostInstall: Symlink created
    PostInstall-->>NPM: Success
    NPM-->>User: Installation complete

    Note over User,BunRuntime: Execution Phase (Before Fix)
    User->>Shell: shuvcode (in dir with bunfig.toml)
    Shell->>BinDir: Execute bin/shuvcode (Node launcher script)
    BinDir->>BunRuntime: #!/usr/bin/env node
    BunRuntime->>BunRuntime: Read CWD's bunfig.toml
    BunRuntime->>BunRuntime: Try to load preload "@opentui/solid/preload"
    BunRuntime-->>User: ERROR: preload not found

    Note over User,BunRuntime: Execution Phase (After Fix)
    User->>Shell: shuvcode (in dir with bunfig.toml)
    Shell->>BinDir: Resolve bin/shuvcode symlink
    BinDir->>PlatformBinary: Follow symlink to ELF binary
    PlatformBinary->>PlatformBinary: Execute compiled binary directly
    PlatformBinary->>PlatformBinary: autoloadBunfig: false (compiled in)
    PlatformBinary->>PlatformBinary: Ignore CWD's bunfig.toml
    PlatformBinary-->>User: TUI launches successfully
Loading

Add postinstall.mjs that creates a symlink from bin/shuvcode to the
platform-specific compiled binary, matching upstream opencode behavior.
This ensures users execute the binary directly, bypassing Bun's
bunfig.toml loading that causes preload resolution failures.

Changes:
- Add postinstall.mjs with platform detection and symlink creation
- Update build.ts to enable autoloadTsconfig and autoloadPackageJson
- Update publish.ts to include postinstall.mjs in published package
Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines 50 to 68
function findBinary() {
const { platform, arch } = detectPlatformAndArch()
const packageName = `shuvcode-${platform}-${arch}`
const binaryName = platform === "windows" ? "shuvcode.exe" : "shuvcode"

try {
// Use require.resolve to find the package
const packageJsonPath = require.resolve(`${packageName}/package.json`)
const packageDir = path.dirname(packageJsonPath)
const binaryPath = path.join(packageDir, "bin", binaryName)

if (!fs.existsSync(binaryPath)) {
throw new Error(`Binary not found at ${binaryPath}`)
}

return { binaryPath, binaryName }
} catch (error) {
throw new Error(`Could not find package ${packageName}: ${error.message}`)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Missing baseline variant fallback - will fail for baseline packages like shuvcode-linux-x64-baseline

The current implementation only tries the exact package name shuvcode-${platform}-${arch}, but the build script creates baseline variants (e.g., shuvcode-linux-x64-baseline). The existing bin/shuvcode launcher handles this by searching for any package that starts with the base name (line 57: if (!entry.startsWith(base))).

Suggested change
function findBinary() {
const { platform, arch } = detectPlatformAndArch()
const packageName = `shuvcode-${platform}-${arch}`
const binaryName = platform === "windows" ? "shuvcode.exe" : "shuvcode"
try {
// Use require.resolve to find the package
const packageJsonPath = require.resolve(`${packageName}/package.json`)
const packageDir = path.dirname(packageJsonPath)
const binaryPath = path.join(packageDir, "bin", binaryName)
if (!fs.existsSync(binaryPath)) {
throw new Error(`Binary not found at ${binaryPath}`)
}
return { binaryPath, binaryName }
} catch (error) {
throw new Error(`Could not find package ${packageName}: ${error.message}`)
}
function findBinary() {
const { platform, arch } = detectPlatformAndArch()
const basePackageName = `shuvcode-${platform}-${arch}`
const binaryName = platform === "windows" ? "shuvcode.exe" : "shuvcode"
try {
// Try exact package name first
try {
const packageJsonPath = require.resolve(`${basePackageName}/package.json`)
const packageDir = path.dirname(packageJsonPath)
const binaryPath = path.join(packageDir, "bin", binaryName)
if (fs.existsSync(binaryPath)) {
return { binaryPath, binaryName }
}
} catch (error) {
// Exact match failed, try baseline variant
}
// Fallback: search for baseline variants (e.g., shuvcode-linux-x64-baseline)
const nodeModulesPath = path.join(__dirname, "..", "node_modules")
if (fs.existsSync(nodeModulesPath)) {
const entries = fs.readdirSync(nodeModulesPath)
for (const entry of entries) {
if (entry.startsWith(basePackageName)) {
const binaryPath = path.join(nodeModulesPath, entry, "bin", binaryName)
if (fs.existsSync(binaryPath)) {
return { binaryPath, binaryName }
}
}
}
}
throw new Error(`No binary package found for ${basePackageName}`)
} catch (error) {
throw new Error(`Could not find package ${basePackageName}: ${error.message}`)
}
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/opencode/postinstall.mjs
Line: 50:68

Comment:
**logic:** Missing baseline variant fallback - will fail for baseline packages like `shuvcode-linux-x64-baseline`

The current implementation only tries the exact package name `shuvcode-${platform}-${arch}`, but the build script creates baseline variants (e.g., `shuvcode-linux-x64-baseline`). The existing `bin/shuvcode` launcher handles this by searching for any package that **starts with** the base name (line 57: `if (!entry.startsWith(base))`).

```suggestion
function findBinary() {
  const { platform, arch } = detectPlatformAndArch()
  const basePackageName = `shuvcode-${platform}-${arch}`
  const binaryName = platform === "windows" ? "shuvcode.exe" : "shuvcode"

  try {
    // Try exact package name first
    try {
      const packageJsonPath = require.resolve(`${basePackageName}/package.json`)
      const packageDir = path.dirname(packageJsonPath)
      const binaryPath = path.join(packageDir, "bin", binaryName)

      if (fs.existsSync(binaryPath)) {
        return { binaryPath, binaryName }
      }
    } catch (error) {
      // Exact match failed, try baseline variant
    }

    // Fallback: search for baseline variants (e.g., shuvcode-linux-x64-baseline)
    const nodeModulesPath = path.join(__dirname, "..", "node_modules")
    if (fs.existsSync(nodeModulesPath)) {
      const entries = fs.readdirSync(nodeModulesPath)
      for (const entry of entries) {
        if (entry.startsWith(basePackageName)) {
          const binaryPath = path.join(nodeModulesPath, entry, "bin", binaryName)
          if (fs.existsSync(binaryPath)) {
            return { binaryPath, binaryName }
          }
        }
      }
    }

    throw new Error(`No binary package found for ${basePackageName}`)
  } catch (error) {
    throw new Error(`Could not find package ${basePackageName}: ${error.message}`)
  }
}
```

How can I resolve this? If you propose a fix, please make it concise.

Apply Greptile suggestion to handle baseline packages like
shuvcode-linux-x64-baseline by searching for packages that start
with the base package name when exact match fails.
@shuv1337 shuv1337 closed this in f471591 Jan 2, 2026
@shuv1337 shuv1337 merged commit f471591 into integration Jan 2, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants