Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 24 additions & 49 deletions .opencode/PAI/Tools/pai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ function displayBanner() {
}

function getCurrentVersion(): string | null {
const result = spawnSync(["claude", "--version"]);
const result = spawnSync(["opencode", "--version"]);
const output = result.stdout.toString();
const match = output.match(/([0-9]+\.[0-9]+\.[0-9]+)/);
return match ? match[1] : null;
Expand All @@ -147,11 +147,11 @@ function compareVersions(a: string, b: string): number {
async function getLatestVersion(): Promise<string | null> {
try {
const response = await fetch(
"https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases/latest"
"https://registry.npmjs.org/opencode-ai/latest"
);
const version = (await response.text()).trim();
if (/^[0-9]+\.[0-9]+\.[0-9]+/.test(version)) {
return version;
const data = (await response.json()) as { version?: string };
if (data?.version && /^[0-9]+\.[0-9]+\.[0-9]+/.test(data.version)) {
return data.version;
}
} catch {
return null;
Expand Down Expand Up @@ -399,7 +399,7 @@ async function cmdLaunch(options: { mcp?: string; resume?: boolean; skipPerms?:
// (InstantiatePAI.ts is retired — kept for reference only)

displayBanner();
const args = ["claude"];
const args = ["opencode"];

// Handle MCP configuration
if (options.mcp) {
Expand All @@ -408,11 +408,9 @@ async function cmdLaunch(options: { mcp?: string; resume?: boolean; skipPerms?:
}

// Add flags
// NOTE: We no longer use --dangerously-skip-permissions by default.
// The settings.json permission system (allow/deny/ask) provides proper security.
// Use --dangerous flag explicitly if you really need to skip all permission checks.
// NOTE: opencode uses --continue (not --resume) to resume the last session.
if (options.resume) {
args.push("--resume");
args.push("--continue");
}

// Change to PAI directory unless --local flag is set
Expand All @@ -434,44 +432,22 @@ async function cmdLaunch(options: { mcp?: string; resume?: boolean; skipPerms?:
}

async function cmdUpdate() {
log("Checking for updates...", "🔍");
log("Updating OpenCode...", "🔄");

const current = getCurrentVersion();
const latest = await getLatestVersion();

if (!current) {
error("Could not detect current version");
}

console.log(`Current: v${current}`);
if (latest) {
console.log(`Latest: v${latest}`);
if (current) {
console.log(`Current: v${current}`);
}

// Skip if already up to date
if (latest && compareVersions(current, latest) >= 0) {
log("Already up to date", "✅");
return;
}

log("Updating Claude Code...", "🔄");

// Step 1: Update Bun
log("Step 1/2: Updating Bun...", "📦");
const bunResult = spawnSync(["brew", "upgrade", "bun"]);
if (bunResult.exitCode !== 0) {
log("Bun update skipped (may already be latest)", "⚠️");
} else {
log("Bun updated", "✅");
}

// Step 2: Update Claude Code
log("Step 2/2: Installing latest Claude Code...", "🤖");
const claudeResult = spawnSync(["bash", "-c", "curl -fsSL https://claude.ai/install.sh | bash"]);
if (claudeResult.exitCode !== 0) {
error("Claude Code installation failed");
const upgradeResult = spawnSync(["opencode", "upgrade"], {
stdin: "inherit",
stdout: "inherit",
stderr: "inherit",
});
if (upgradeResult.exitCode !== 0) {
error("OpenCode upgrade failed");
}
log("Claude Code updated", "✅");
log("OpenCode updated", "✅");

// Show final version
const newVersion = getCurrentVersion();
Expand Down Expand Up @@ -555,9 +531,8 @@ function cmdMcpList() {
}

async function cmdPrompt(prompt: string) {
// One-shot prompt execution
// NOTE: No --dangerously-skip-permissions - rely on settings.json permissions
const args = ["claude", "-p", prompt];
// One-shot prompt execution via `opencode run <message>`
const args = ["opencode", "run", prompt];
Comment on lines 533 to +535
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify hardcoded claude references in algorithm.ts
rg -n '"claude"' --type ts -C2

Repository: Steffen025/pai-opencode

Length of output: 49


🏁 Script executed:

# Find algorithm.ts file
fd -t f "algorithm\.ts" --type f

Repository: Steffen025/pai-opencode

Length of output: 49


🏁 Script executed:

# Search for spawn patterns and claude references more broadly
rg -i "spawn|claude" --type ts -n | head -50

Repository: Steffen025/pai-opencode

Length of output: 5184


🏁 Script executed:

# Check if algorithm.ts exists and its size
ls -la .opencode/PAI/Tools/ 2>/dev/null || find . -name "algorithm.ts" -type f

Repository: Steffen025/pai-opencode

Length of output: 3395


🏁 Script executed:

# Check lines around 665
sed -n '660,680p' .opencode/PAI/Tools/algorithm.ts | cat -n

Repository: Steffen025/pai-opencode

Length of output: 1000


🏁 Script executed:

# Check lines around 1121
sed -n '1115,1140p' .opencode/PAI/Tools/algorithm.ts | cat -n

Repository: Steffen025/pai-opencode

Length of output: 1190


🏁 Script executed:

# Check lines around 1231
sed -n '1225,1255p' .opencode/PAI/Tools/algorithm.ts | cat -n

Repository: Steffen025/pai-opencode

Length of output: 1373


🏁 Script executed:

# Search for any spawn calls in algorithm.ts
rg -n "spawn|Bun\." .opencode/PAI/Tools/algorithm.ts

Repository: Steffen025/pai-opencode

Length of output: 322


🏁 Script executed:

# Check pai.ts around lines 533-535 to see what's being changed
sed -n '525,545p' .opencode/PAI/Tools/pai.ts | cat -n

Repository: Steffen025/pai-opencode

Length of output: 794


🏁 Script executed:

# Check imports in algorithm.ts to understand spawn vs Bun.spawn
head -50 .opencode/PAI/Tools/algorithm.ts | cat -n

Repository: Steffen025/pai-opencode

Length of output: 2858


🏁 Script executed:

# Search for any issue `#98` references or related comments
rg -i "issue.*98|#98|ENOENT|opencode run" .opencode/PAI/Tools/

Repository: Steffen025/pai-opencode

Length of output: 234


🏁 Script executed:

# Check context around the 3 spawn calls - what functions do they belong to?
rg -B10 'spawn.*"claude"' .opencode/PAI/Tools/algorithm.ts | head -60

Repository: Steffen025/pai-opencode

Length of output: 1208


🏁 Script executed:

# Search for TODO/FIXME about migration in algorithm.ts
rg -i "todo|fixme|migration|issue|claude.*opencode|opencode.*claude" .opencode/PAI/Tools/algorithm.ts

Repository: Steffen025/pai-opencode

Length of output: 49


🏁 Script executed:

# Check the pai.ts spawn call more carefully - verify it matches child_process API
sed -n '533,545p' .opencode/PAI/Tools/pai.ts

Repository: Steffen025/pai-opencode

Length of output: 421


🏁 Script executed:

# Get more context - what are the function names containing these spawns?
rg -B20 'Bun.spawn\(\["claude"' .opencode/PAI/Tools/algorithm.ts | grep -E "^[0-9]+.*function|^[0-9]+.*async function"

Repository: Steffen025/pai-opencode

Length of output: 49


🏁 Script executed:

# Verify pai.ts imports to understand the spawn API being used
head -20 .opencode/PAI/Tools/pai.ts

Repository: Steffen025/pai-opencode

Length of output: 783


🏁 Script executed:

# Check the actual spawn signature used in pai.ts - is it child_process.spawn?
rg -B5 "spawn.*args" .opencode/PAI/Tools/pai.ts | head -20

Repository: Steffen025/pai-opencode

Length of output: 413


🏁 Script executed:

# Check pai.ts imports to see which spawn is being used
grep -n "import.*spawn\|from.*bun" .opencode/PAI/Tools/pai.ts | head -10

Repository: Steffen025/pai-opencode

Length of output: 109


🏁 Script executed:

# Look at the full context of pai.ts spawn usage to understand the API
sed -n '1,100p' .opencode/PAI/Tools/pai.ts | grep -A3 "import"

Repository: Steffen025/pai-opencode

Length of output: 453


Critical: algorithm.ts still hardcodes claude binary—will cause ENOENT crashes after merge.

The pai.ts change correctly migrates to opencode run for one-shot prompts, but algorithm.ts still spawns the claude binary directly in three active code paths:

  • Line 669: Bun.spawn(["claude", "-p", prompt, ...])—parallel agent iteration
  • Line 1122: spawnSync("claude", [...])—sequential loop iteration
  • Line 1235: spawn("claude", [...])—interactive session launch

Users without the claude CLI installed will encounter ENOENT errors when using algorithm features after this PR merges. Either extend this PR to update algorithm.ts or create a follow-up issue to track the migration.

🤖 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 533 - 535, algorithm.ts still spawns
the hardcoded "claude" binary in three places (Bun.spawn(["claude", ...]) in the
parallel agent loop, spawnSync("claude", [...]) in the sequential loop, and
spawn("claude", [...]) in the interactive session), which will cause ENOENT for
users without that CLI; update those three call sites to invoke the unified
opencode entrypoint (e.g., "opencode" with args ["run", prompt, ...]) or call a
new/shared wrapper (create or reuse a helper similar to cmdPrompt) that
constructs and spawns the correct command, preserves existing flags/args and I/O
handling, and surfaces spawn errors cleanly instead of assuming "claude" exists.


process.chdir(OPENCODE_DIR);

Expand All @@ -575,14 +550,14 @@ function cmdHelp() {
pai - Personal AI CLI Tool (v2.0.0)

USAGE:
k Launch Claude (no MCPs, max performance)
k Launch OpenCode (no MCPs, max performance)
k -m <mcp> Launch with specific MCP(s)
k -m bd,ap Launch with multiple MCPs
k -r, --resume Resume last session
k -l, --local Stay in current directory (don't cd to ~/.opencode)

COMMANDS:
k update Update Claude Code to latest version
k update Update OpenCode to latest version
k version, -v Show version information
k profiles List available MCP profiles
k mcp list List all available MCPs
Expand All @@ -609,7 +584,7 @@ EXAMPLES:
k -m bd,ap,chrome Start with multiple MCPs
k -r Resume last session
k mcp set research Switch to research profile
k update Update Claude Code
k update Update OpenCode
k prompt "What time is it?" One-shot prompt
k -w List available wallpapers
k -w circuit-board Switch wallpaper (Kitty + macOS)
Expand Down