Skip to content

fix(installer): make pai/opencode work on fresh machines#100

Merged
Steffen025 merged 4 commits intomainfrom
feat/release-blocker-cleanup
Apr 1, 2026
Merged

fix(installer): make pai/opencode work on fresh machines#100
Steffen025 merged 4 commits intomainfrom
feat/release-blocker-cleanup

Conversation

@Steffen025
Copy link
Copy Markdown
Owner

@Steffen025 Steffen025 commented Apr 1, 2026

Problem: fresh installs could finish but leave broken commands. pai failed when bun PATH was missing, opencode was not always resolvable, and pai launcher still tried to spawn claude. This PR hardens installer shell wiring and launcher resolution. Changes: add shell PATH wiring for ~/.bun/bin and ~/.local/bin, add opencode fallback wrapper function, make pai prefer explicit ~/.bun/bin/bun, keep shell block cleanup idempotent, update pai launcher to resolve and run opencode from PATH/local/compat locations, and add critical validation checks for shell path wiring and bun runtime presence. Related to #99 but broader because installer and validation also needed fixes.

Summary by CodeRabbit

  • New Features

    • Switches the CLI to use OpenCode with automatic discovery and PATH-aware fallbacks.
    • Replaces simple alias with a full shell setup for bash/zsh/fish, adding PATH updates and opencode/pai helpers.
    • Adds installer validation for shell PATH wiring and Bun runtime presence.
    • Update flow now invokes OpenCode upgrade and checks latest version via the npm registry.
  • Chores

    • Updated installer messaging, markers and cleanup to reflect the new shell setup.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 1, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b2f0911f-447a-4c96-87f5-353787c9358f

📥 Commits

Reviewing files that changed from the base of the PR and between 0996f10 and 9c30fdf.

📒 Files selected for processing (1)
  • PAI-Install/engine/steps-fresh.ts

📝 Walkthrough

Walkthrough

Replaces the CLI runtime and messaging from Claude/claude to OpenCode/opencode; adds executable resolution for opencode; switches version lookup to the npm registry; updates update/install flows to use opencode upgrade; and consolidates installer shell wiring under a “PAI shell setup” block that defines opencode() and pai() plus PATH wiring and validation.

Changes

Cohort / File(s) Summary
Core Tool
\.opencode/PAI/Tools/pai.ts
Add resolveOpenCodeExecutable(); change version detection to call resolved opencode binary; fetch latest version from https://registry.npmjs.org/opencode-ai/latest; spawn opencode for runs/prompts; change update flow to <resolved> upgrade; update messaging to reference OpenCode/pai.
Installer: actions
PAI-Install/engine/actions.ts
Replace legacy # PAI alias with # PAI shell setup; write PATH exports and opencode() + pai() functions for bash/zsh/fish; cleanup now removes only explicit shell-setup block and previous opencode/pai defs.
Installer: fresh steps
PAI-Install/engine/steps-fresh.ts
Generated shell snippet now prepends $HOME/.bun/bin and $HOME/.local/bin, defines opencode() resolution order (~/.local/bin, ~/.opencode/tools, command opencode), and a pai() wrapper preferring ~/.bun/bin/bun; expanded re-run cleanup to remove prior shell-setup and function variants.
Installer: validation
PAI-Install/engine/validate.ts
Marker detection changed to # PAI shell setup; validation requires both a valid pai implementation and PATH wiring (both $HOME/.bun/bin and $HOME/.local/bin), records source shell, and adds checks for “Shell PATH wiring” and presence of ~/.bun/bin/bun.

Sequence Diagram

sequenceDiagram
    participant User
    participant Shell as Shell\n(bash|zsh|fish)
    participant Wrapper as pai/opencode\nwrapper
    participant Resolver as resolveOpenCodeExecutable()
    participant Binary as opencode\nbinary

    User->>Shell: run `pai` or `opencode`
    Shell->>Wrapper: invoke wrapper function
    Wrapper->>Resolver: resolve opencode path
    alt found in ~/.local/bin
        Resolver-->>Wrapper: "rgba(0,128,0,0.5) ~/.local/bin/opencode"
    else found in ~/.opencode/tools
        Resolver-->>Wrapper: "rgba(0,128,0,0.5) ~/.opencode/tools/opencode"
    else found on PATH
        Resolver-->>Wrapper: "rgba(0,128,0,0.5) opencode (PATH)"
    else not found
        Resolver-->>Wrapper: "rgba(255,0,0,0.5) not found -> error"
    end
    Wrapper->>Binary: exec resolved opencode with args
    Binary-->>User: returns output
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐇 I hopped through shells and neatly peered,
Found opencode where secrets appeared,
Wrapped pai and PATH with a jaunty cheer,
Bun and local bins now close and near,
Hooray — code blooms where rabbits steer!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.78% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main objective: fixing the installer to ensure pai/opencode work correctly on fresh machines by addressing CLI runtime resolution, shell PATH wiring, and validation checks.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/release-blocker-cleanup

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
PAI-Install/engine/validate.ts (1)

229-229: ⚠️ Potential issue | 🔴 Critical

Critical: Marker string mismatch causes validation to always fail.

The validation checks for "# PAI alias" but actions.ts (line 797) and steps-fresh.ts (lines 431-432) write "# PAI shell setup" as the marker. This means the shell alias validation will never find configurations written by the current installer.

🐛 Proposed fix to match the installer's marker
-        if (!content.includes("# PAI alias")) continue;
+        if (!content.includes("# PAI shell setup")) continue;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@PAI-Install/engine/validate.ts` at line 229, The validation currently checks
for the wrong marker string ("# PAI alias") so it never matches installer
output; update the check in validate.ts where it reads if (!content.includes("#
PAI alias")) continue; to use the installer's marker ("# PAI shell setup") so
the shell alias sections created by actions.ts and steps-fresh.ts are correctly
detected.
.opencode/PAI/Tools/pai.ts (1)

476-493: ⚠️ Potential issue | 🟠 Major

Update mechanism still installs Claude Code instead of OpenCode.

The cmdUpdate() function (lines 476-493) attempts to install from https://claude.ai/install.sh and checks for updates against claude-code-dist storage, but pai.ts is located in .opencode/PAI/Tools/ and configured to use ~/.opencode/ paths. The version check (line 169) and installation script need to be updated to either upgrade OpenCode or removed entirely if OpenCode self-updates. Update command usage (lines 14, 604, 631) also needs corresponding documentation changes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.opencode/PAI/Tools/pai.ts around lines 476 - 493, The cmdUpdate() block
currently invokes Claude's installer and checks/update storage named for Claude;
change that to update OpenCode (or remove the installer call if OpenCode
self-updates): replace the spawnSync call that runs "curl -fsSL
https://claude.ai/install.sh | bash" (the claudeResult usage) with the correct
OpenCode install/update command or omit it, and update any storage key or
version-check logic that references "claude-code-dist" to the OpenCode
equivalent (so the version check variable used earlier aligns with the
~/.opencode/ paths). Also keep the Bun upgrade logic (bunResult) but ensure
log/error messages reference OpenCode instead of Claude, and update the command
usage documentation strings or help text elsewhere in the file to reflect
OpenCode update behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.opencode/PAI/Tools/pai.ts:
- Around line 132-147: The resolveOpenCodeExecutable function currently checks
PATH first via spawnSync(["opencode","--version"]) then LOCAL_OPENCODE_BIN and
COMPAT_OPENCODE_BIN, which conflicts with the installer shell wrapper order;
update resolveOpenCodeExecutable to first check existsSync(LOCAL_OPENCODE_BIN),
then existsSync(COMPAT_OPENCODE_BIN), and only then fall back to testing PATH
with spawnSync/exitCode and return "opencode" if found; keep the final
error(...) call unchanged and reference the same symbols
(resolveOpenCodeExecutable, LOCAL_OPENCODE_BIN, COMPAT_OPENCODE_BIN, spawnSync,
error).

In `@PAI-Install/engine/actions.ts`:
- Around line 794-795: The fish block in the aliasLine uses the invalid "and"
operator inside the if/else for the pai function; update the fisk function
definitions (opencode and pai) so the pai function uses a proper if/else branch
instead of "and" — e.g., in the pai function (which sets __pai_oldpwd, __pai_bun
and cds to paiDir) replace the lines "if test -x $__pai_bun\n\t\tand $__pai_bun
run .opencode/PAI/Tools/pai.ts $argv\n\telse\n\t\tand bun run
.opencode/PAI/Tools/pai.ts $argv\n\tend" with a normal if body that directly
invokes "$__pai_bun run .opencode/PAI/Tools/pai.ts $argv" in the if branch and
"bun run .opencode/PAI/Tools/pai.ts $argv" in the else branch (keeping the
surrounding cd and cd back to __pai_oldpwd), and update aliasLine to include
this corrected multi-line fish function string.

In `@PAI-Install/engine/steps-fresh.ts`:
- Line 431: The fish function uses the combinator "and" incorrectly inside the
pai function; update the pai function (and keep opencode unchanged) to call the
commands directly inside the if/else branches: after setting __pai_bun and
changing to the install dir, use "if test -x $__pai_bun" then execute
"$__pai_bun run .opencode/PAI/Tools/pai.ts $argv" in that branch, otherwise
execute "bun run .opencode/PAI/Tools/pai.ts $argv" in the else branch, and then
cd back to $__pai_oldpwd; remove the stray "and" tokens so the fish conditional
blocks contain plain command invocations.

---

Outside diff comments:
In @.opencode/PAI/Tools/pai.ts:
- Around line 476-493: The cmdUpdate() block currently invokes Claude's
installer and checks/update storage named for Claude; change that to update
OpenCode (or remove the installer call if OpenCode self-updates): replace the
spawnSync call that runs "curl -fsSL https://claude.ai/install.sh | bash" (the
claudeResult usage) with the correct OpenCode install/update command or omit it,
and update any storage key or version-check logic that references
"claude-code-dist" to the OpenCode equivalent (so the version check variable
used earlier aligns with the ~/.opencode/ paths). Also keep the Bun upgrade
logic (bunResult) but ensure log/error messages reference OpenCode instead of
Claude, and update the command usage documentation strings or help text
elsewhere in the file to reflect OpenCode update behavior.

In `@PAI-Install/engine/validate.ts`:
- Line 229: The validation currently checks for the wrong marker string ("# PAI
alias") so it never matches installer output; update the check in validate.ts
where it reads if (!content.includes("# PAI alias")) continue; to use the
installer's marker ("# PAI shell setup") so the shell alias sections created by
actions.ts and steps-fresh.ts are correctly detected.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 98bdefe1-8d89-4976-bcf9-99f32db198e1

📥 Commits

Reviewing files that changed from the base of the PR and between c6d9ada and 52a8a90.

📒 Files selected for processing (4)
  • .opencode/PAI/Tools/pai.ts
  • PAI-Install/engine/actions.ts
  • PAI-Install/engine/steps-fresh.ts
  • PAI-Install/engine/validate.ts

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.opencode/PAI/Tools/pai.ts (1)

594-637: ⚠️ Potential issue | 🟠 Major

Help/output still points users at k, not the installed pai command.

Fresh installs only wire pai()/opencode(), so the usage/examples here send users to a nonexistent command. Please finish the rename here and sweep the remaining user-facing k hints in this file.

💡 Update the visible command name
-  k                        Launch OpenCode (no MCPs, max performance)
+  pai                      Launch OpenCode (no MCPs, max performance)
@@
-  k update                 Update runtime dependencies
+  pai update               Update runtime dependencies
@@
-  k update                 Update OpenCode
+  pai update               Update OpenCode
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.opencode/PAI/Tools/pai.ts around lines 594 - 637, The help text returned by
cmdHelp() still uses the old "k" CLI name; update the multi-line string in
function cmdHelp() to replace all user-facing occurrences of "k" with "pai"
(including flags, examples, COMMANDS list, MCP SHORTCUTS examples and any
shorthand usages like "k -m", "k update", "k prompt", etc.), and then search
this file for any remaining visible "k" hints outside cmdHelp() and replace them
with "pai" so all user-facing guidance matches the installed command name.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.opencode/PAI/Tools/pai.ts:
- Around line 132-143: The resolver resolveOpenCodeExecutable currently returns
LOCAL_OPENCODE_BIN or COMPAT_OPENCODE_BIN based only on existsSync; change it to
also check that the file is executable (use fs.accessSync(path,
fs.constants.X_OK) or the equivalent) and only return those paths when the
executable bit is present; if accessSync throws or the check fails, fall through
to the PATH check (the spawnSync block). Also update the fs import to include
accessSync and constants so you can perform the X_OK check.

In `@PAI-Install/engine/actions.ts`:
- Around line 807-816: The current cleanup uses a broad regex on the variable
content that can delete user-added non-comment lines; change to use explicit
start and end markers and only remove between them: introduce a unique end
marker (e.g., "# end PAI shell setup") and update the first replace call that
currently references marker to match from the start marker to that end marker
only (non-greedy, across lines), then when appending the new block ensure you
write both the start marker and the matching end marker around aliasLine (use
the existing marker and aliasLine variables to build the block, e.g., `${marker}
— added by PAI installer\n${aliasLine}\n# end PAI shell setup`), and remove or
relax any other broad regexes that previously tried to clean variants (lines
calling replace(/^#\s*(?:PAI|CORE).../) etc.) so they no longer risk removing
user content—target only the explicit bracketed block for deletion.
- Around line 793-795: The fish `pai` wrapper in the aliasLine overwrites the
child process exit status by doing `cd $__pai_oldpwd` after running the tool;
fix it by capturing the child exit status immediately after the run command (use
Fish's $status, e.g. `set -l __pai_status $status`) and then restore the
directory and return/exit with that saved status (e.g. `cd $__pai_oldpwd` then
`return $__pai_status`); apply the same change to the other fish wrapper in the
code mentioned (steps-fresh's pai wrapper).

In `@PAI-Install/engine/steps-fresh.ts`:
- Around line 443-447: The PAI cleanup regex can run to EOF and misses legacy
markers; update the first content.replace so the PAI block match is explicitly
bounded by the next top-level comment OR the next shell/function block
(lookahead for "\n#" OR the next function starts such as pai(), opencode(),
function pai, function opencode) and make the alternation include older marker
variants (e.g. alias|shell setup|setup|installer|installed|PAI) so legacy
installer markers are removed; similarly ensure the other removals that target
pai()/opencode()/function pai/function opencode use those same bounded
lookaheads so they never greedily consume unrelated trailing shell code.
- Around line 430-431: The fish `pai` function currently runs the bun command
then does `cd $__pai_oldpwd` last, which masks the bun exit status; update the
`pai` function (function pai, variables __pai_oldpwd and __pai_bun) to capture
the child exit code into a local variable immediately after the bun/OpenCode
invocation (using set -l __pai_status $status), then perform `cd $__pai_oldpwd`,
and finally return that saved status (return $__pai_status) so the wrapper
preserves and propagates the original command's exit status.

---

Outside diff comments:
In @.opencode/PAI/Tools/pai.ts:
- Around line 594-637: The help text returned by cmdHelp() still uses the old
"k" CLI name; update the multi-line string in function cmdHelp() to replace all
user-facing occurrences of "k" with "pai" (including flags, examples, COMMANDS
list, MCP SHORTCUTS examples and any shorthand usages like "k -m", "k update",
"k prompt", etc.), and then search this file for any remaining visible "k" hints
outside cmdHelp() and replace them with "pai" so all user-facing guidance
matches the installed command name.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a804dc6e-067e-4744-9c8d-afc82b65371b

📥 Commits

Reviewing files that changed from the base of the PR and between 52a8a90 and e660b74.

📒 Files selected for processing (4)
  • .opencode/PAI/Tools/pai.ts
  • PAI-Install/engine/actions.ts
  • PAI-Install/engine/steps-fresh.ts
  • PAI-Install/engine/validate.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • PAI-Install/engine/validate.ts

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
PAI-Install/engine/steps-fresh.ts (1)

443-462: Cleanup regex could over-delete if shell config has non-standard content after PAI block.

The lookahead boundary (?=\n(?:#|pai\(\)|opencode\(\)|function\s+pai\b|function\s+opencode\b)|$) only stops at:

  • Comments starting with #
  • Function definitions (pai(), opencode(), function pai, function opencode)
  • End of file

If a user has content like alias ll='ls -la' or export JAVA_HOME=... immediately after the PAI block (without a preceding comment), the regex will continue to EOF and delete that content.

This is mitigated if the explicit end marker is added (per previous comment), which would allow using the more precise regex from actions.ts:

/\n?# PAI shell setup  added by PAI installer[\s\S]*?# end PAI shell setup\n?/g
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@PAI-Install/engine/steps-fresh.ts` around lines 443 - 462, The current
cleanup regexes applied to content (the replace calls that remove
pai()/opencode() blocks and PAI sections) use a lookahead that only stops at
comments or specific function signatures, which can over-delete trailing
non-comment shell lines like "alias ..." or "export ..."; update these replace
invocations on content to either require and match an explicit end marker (use
the same explicit marker pattern from actions.ts, e.g. the "PAI shell setup —
added by PAI installer" start and matching "# end PAI shell setup" end) or
broaden the lookahead to include common shell constructs (e.g. lines starting
with alias, export, export -n, set, source, function definitions) so deletion
stops correctly; adjust the regexes that target the PAI block and the
pai()/opencode() function bodies accordingly so pai(), opencode(), and function
pai/opencode patterns are preserved as anchors but safe against removing
unrelated config lines.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@PAI-Install/engine/steps-fresh.ts`:
- Around line 430-432: The fish branch of aliasBlock in steps-fresh.ts is
missing the end marker "# end PAI shell setup", causing mismatched cleanup with
actions.ts; update the fish template (the template string assigned to aliasBlock
when shellName === "fish") to include the same end marker line used in
actions.ts after the generated functions so the start and end markers match the
cleanup regex and ensure both creation and removal work consistently.

---

Nitpick comments:
In `@PAI-Install/engine/steps-fresh.ts`:
- Around line 443-462: The current cleanup regexes applied to content (the
replace calls that remove pai()/opencode() blocks and PAI sections) use a
lookahead that only stops at comments or specific function signatures, which can
over-delete trailing non-comment shell lines like "alias ..." or "export ...";
update these replace invocations on content to either require and match an
explicit end marker (use the same explicit marker pattern from actions.ts, e.g.
the "PAI shell setup — added by PAI installer" start and matching "# end PAI
shell setup" end) or broaden the lookahead to include common shell constructs
(e.g. lines starting with alias, export, export -n, set, source, function
definitions) so deletion stops correctly; adjust the regexes that target the PAI
block and the pai()/opencode() function bodies accordingly so pai(), opencode(),
and function pai/opencode patterns are preserved as anchors but safe against
removing unrelated config lines.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 38c61367-b138-484a-bac5-e67597f757b0

📥 Commits

Reviewing files that changed from the base of the PR and between e660b74 and 0996f10.

📒 Files selected for processing (3)
  • .opencode/PAI/Tools/pai.ts
  • PAI-Install/engine/actions.ts
  • PAI-Install/engine/steps-fresh.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • PAI-Install/engine/actions.ts

@Steffen025 Steffen025 merged commit fbcc2f1 into main Apr 1, 2026
3 checks passed
@Steffen025 Steffen025 deleted the feat/release-blocker-cleanup branch April 1, 2026 17:18
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.

1 participant