feat: standalone binary support with Bun

- Add build:binary script for Bun compilation
- Add paths.ts for cross-platform asset resolution (npm/bun/tsx)
- Add GitHub Actions workflow for automated binary releases
- Update README with installation options

Based on #89 by @steipete
This commit is contained in:
Mario Zechner 2025-12-02 12:18:42 +01:00
parent 4a60bffe3b
commit c4a65ad8b9
17 changed files with 626 additions and 65 deletions

99
.github/workflows/build-binaries.yml vendored Normal file
View file

@ -0,0 +1,99 @@
name: Build Binaries
on:
push:
tags:
- 'v*'
permissions:
contents: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Setup Bun
uses: oven-sh/setup-bun@4bc047ad259df6fc24a6c9b0f9a0cb08cf17fbe5 # v2.0.1
- name: Setup Node.js
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
node-version: '22'
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
run: npm ci
- name: Build
run: |
cd packages/coding-agent
npm run build
- name: Build binaries for all platforms
run: |
cd packages/coding-agent
# Create output directory
mkdir -p binaries
# Build for each platform
declare -a targets=(
"bun-darwin-arm64:pi-darwin-arm64"
"bun-darwin-x64:pi-darwin-x64"
"bun-linux-x64:pi-linux-x64"
"bun-linux-arm64:pi-linux-arm64"
"bun-windows-x64:pi-windows-x64.exe"
)
for target_pair in "${targets[@]}"; do
IFS=':' read -r target outfile <<< "$target_pair"
echo "Building for $target..."
bun build --compile --target="$target" ./dist/cli.js --outfile "binaries/$outfile"
done
- name: Create release archives
run: |
cd packages/coding-agent
# Files to include with each binary
cp package.json binaries/
cp README.md binaries/
cp CHANGELOG.md binaries/
mkdir -p binaries/theme
cp dist/theme/*.json binaries/theme/
# Create archives for each platform
cd binaries
# macOS arm64
tar -czf pi-darwin-arm64.tar.gz pi-darwin-arm64 package.json README.md CHANGELOG.md theme/
# macOS x64
tar -czf pi-darwin-x64.tar.gz pi-darwin-x64 package.json README.md CHANGELOG.md theme/
# Linux x64
tar -czf pi-linux-x64.tar.gz pi-linux-x64 package.json README.md CHANGELOG.md theme/
# Linux arm64
tar -czf pi-linux-arm64.tar.gz pi-linux-arm64 package.json README.md CHANGELOG.md theme/
# Windows x64 (zip)
zip -r pi-windows-x64.zip pi-windows-x64.exe package.json README.md CHANGELOG.md theme/
- name: Upload to GitHub Release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
cd packages/coding-agent/binaries
# Upload all archives to the release
gh release upload "${{ github.ref_name }}" \
pi-darwin-arm64.tar.gz \
pi-darwin-x64.tar.gz \
pi-linux-x64.tar.gz \
pi-linux-arm64.tar.gz \
pi-windows-x64.zip \
--clobber

380
package-lock.json generated
View file

@ -6074,11 +6074,11 @@
},
"packages/agent": {
"name": "@mariozechner/pi-agent-core",
"version": "0.11.6",
"version": "0.12.0",
"license": "MIT",
"dependencies": {
"@mariozechner/pi-ai": "^0.11.5",
"@mariozechner/pi-tui": "^0.11.5"
"@mariozechner/pi-ai": "^0.11.6",
"@mariozechner/pi-tui": "^0.11.6"
},
"devDependencies": {
"@types/node": "^24.3.0",
@ -6089,6 +6089,42 @@
"node": ">=20.0.0"
}
},
"packages/agent/node_modules/@mariozechner/pi-ai": {
"version": "0.11.6",
"resolved": "https://registry.npmjs.org/@mariozechner/pi-ai/-/pi-ai-0.11.6.tgz",
"integrity": "sha512-E96GATRiarbLtvukx69VruA8E2szGkmNbT86SHTFnOg1Nl1Y78hVcYggUFhTTHuuOZznbwG68iBzK6roKybysw==",
"license": "MIT",
"dependencies": {
"@anthropic-ai/sdk": "^0.61.0",
"@google/genai": "^1.30.0",
"@sinclair/typebox": "^0.34.41",
"ajv": "^8.17.1",
"ajv-formats": "^3.0.1",
"chalk": "^5.6.2",
"openai": "5.21.0",
"partial-json": "^0.1.7",
"zod-to-json-schema": "^3.24.6"
},
"engines": {
"node": ">=20.0.0"
}
},
"packages/agent/node_modules/@mariozechner/pi-tui": {
"version": "0.11.6",
"resolved": "https://registry.npmjs.org/@mariozechner/pi-tui/-/pi-tui-0.11.6.tgz",
"integrity": "sha512-tiYPbZ3ZlGYDqZk9nZ/pckFGbUfhfc35O/933QxX11ObPOgGFuj92pDFrPACfPX6rYvG909p1CBvZeGS7Szd+w==",
"license": "MIT",
"dependencies": {
"@types/mime-types": "^2.1.4",
"chalk": "^5.5.0",
"marked": "^15.0.12",
"mime-types": "^3.0.1",
"string-width": "^8.1.0"
},
"engines": {
"node": ">=20.0.0"
}
},
"packages/agent/node_modules/@types/node": {
"version": "24.10.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
@ -6099,6 +6135,31 @@
"undici-types": "~7.16.0"
}
},
"packages/agent/node_modules/mime-db": {
"version": "1.54.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"packages/agent/node_modules/mime-types": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
"integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
"license": "MIT",
"dependencies": {
"mime-db": "^1.54.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"packages/agent/node_modules/undici-types": {
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
@ -6108,7 +6169,7 @@
},
"packages/ai": {
"name": "@mariozechner/pi-ai",
"version": "0.11.6",
"version": "0.12.0",
"license": "MIT",
"dependencies": {
"@anthropic-ai/sdk": "^0.61.0",
@ -6149,12 +6210,12 @@
},
"packages/coding-agent": {
"name": "@mariozechner/pi-coding-agent",
"version": "0.11.6",
"version": "0.12.0",
"license": "MIT",
"dependencies": {
"@mariozechner/pi-agent-core": "^0.11.5",
"@mariozechner/pi-ai": "^0.11.5",
"@mariozechner/pi-tui": "^0.11.5",
"@mariozechner/pi-agent-core": "^0.11.6",
"@mariozechner/pi-ai": "^0.11.6",
"@mariozechner/pi-tui": "^0.11.6",
"chalk": "^5.5.0",
"diff": "^8.0.2",
"glob": "^11.0.3"
@ -6172,6 +6233,55 @@
"node": ">=20.0.0"
}
},
"packages/coding-agent/node_modules/@mariozechner/pi-agent-core": {
"version": "0.11.6",
"resolved": "https://registry.npmjs.org/@mariozechner/pi-agent-core/-/pi-agent-core-0.11.6.tgz",
"integrity": "sha512-NnfZlEyXBitBVzyRsakOfvRcI0AoIpInADDUWgtGefrf/9Vv3QAmN7oDd7Zq5rZUbfMY5rVHUuogvEyZBMpwxQ==",
"license": "MIT",
"dependencies": {
"@mariozechner/pi-ai": "^0.11.6",
"@mariozechner/pi-tui": "^0.11.6"
},
"engines": {
"node": ">=20.0.0"
}
},
"packages/coding-agent/node_modules/@mariozechner/pi-ai": {
"version": "0.11.6",
"resolved": "https://registry.npmjs.org/@mariozechner/pi-ai/-/pi-ai-0.11.6.tgz",
"integrity": "sha512-E96GATRiarbLtvukx69VruA8E2szGkmNbT86SHTFnOg1Nl1Y78hVcYggUFhTTHuuOZznbwG68iBzK6roKybysw==",
"license": "MIT",
"dependencies": {
"@anthropic-ai/sdk": "^0.61.0",
"@google/genai": "^1.30.0",
"@sinclair/typebox": "^0.34.41",
"ajv": "^8.17.1",
"ajv-formats": "^3.0.1",
"chalk": "^5.6.2",
"openai": "5.21.0",
"partial-json": "^0.1.7",
"zod-to-json-schema": "^3.24.6"
},
"engines": {
"node": ">=20.0.0"
}
},
"packages/coding-agent/node_modules/@mariozechner/pi-tui": {
"version": "0.11.6",
"resolved": "https://registry.npmjs.org/@mariozechner/pi-tui/-/pi-tui-0.11.6.tgz",
"integrity": "sha512-tiYPbZ3ZlGYDqZk9nZ/pckFGbUfhfc35O/933QxX11ObPOgGFuj92pDFrPACfPX6rYvG909p1CBvZeGS7Szd+w==",
"license": "MIT",
"dependencies": {
"@types/mime-types": "^2.1.4",
"chalk": "^5.5.0",
"marked": "^15.0.12",
"mime-types": "^3.0.1",
"string-width": "^8.1.0"
},
"engines": {
"node": ">=20.0.0"
}
},
"packages/coding-agent/node_modules/@types/node": {
"version": "24.10.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
@ -6182,6 +6292,31 @@
"undici-types": "~7.16.0"
}
},
"packages/coding-agent/node_modules/mime-db": {
"version": "1.54.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"packages/coding-agent/node_modules/mime-types": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
"integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
"license": "MIT",
"dependencies": {
"mime-db": "^1.54.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"packages/coding-agent/node_modules/undici-types": {
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
@ -6191,12 +6326,12 @@
},
"packages/mom": {
"name": "@mariozechner/pi-mom",
"version": "0.11.6",
"version": "0.12.0",
"license": "MIT",
"dependencies": {
"@anthropic-ai/sandbox-runtime": "^0.0.16",
"@mariozechner/pi-agent-core": "^0.11.5",
"@mariozechner/pi-ai": "^0.11.5",
"@mariozechner/pi-agent-core": "^0.11.6",
"@mariozechner/pi-ai": "^0.11.6",
"@sinclair/typebox": "^0.34.0",
"@slack/socket-mode": "^2.0.0",
"@slack/web-api": "^7.0.0",
@ -6215,6 +6350,55 @@
"node": ">=20.0.0"
}
},
"packages/mom/node_modules/@mariozechner/pi-agent-core": {
"version": "0.11.6",
"resolved": "https://registry.npmjs.org/@mariozechner/pi-agent-core/-/pi-agent-core-0.11.6.tgz",
"integrity": "sha512-NnfZlEyXBitBVzyRsakOfvRcI0AoIpInADDUWgtGefrf/9Vv3QAmN7oDd7Zq5rZUbfMY5rVHUuogvEyZBMpwxQ==",
"license": "MIT",
"dependencies": {
"@mariozechner/pi-ai": "^0.11.6",
"@mariozechner/pi-tui": "^0.11.6"
},
"engines": {
"node": ">=20.0.0"
}
},
"packages/mom/node_modules/@mariozechner/pi-ai": {
"version": "0.11.6",
"resolved": "https://registry.npmjs.org/@mariozechner/pi-ai/-/pi-ai-0.11.6.tgz",
"integrity": "sha512-E96GATRiarbLtvukx69VruA8E2szGkmNbT86SHTFnOg1Nl1Y78hVcYggUFhTTHuuOZznbwG68iBzK6roKybysw==",
"license": "MIT",
"dependencies": {
"@anthropic-ai/sdk": "^0.61.0",
"@google/genai": "^1.30.0",
"@sinclair/typebox": "^0.34.41",
"ajv": "^8.17.1",
"ajv-formats": "^3.0.1",
"chalk": "^5.6.2",
"openai": "5.21.0",
"partial-json": "^0.1.7",
"zod-to-json-schema": "^3.24.6"
},
"engines": {
"node": ">=20.0.0"
}
},
"packages/mom/node_modules/@mariozechner/pi-tui": {
"version": "0.11.6",
"resolved": "https://registry.npmjs.org/@mariozechner/pi-tui/-/pi-tui-0.11.6.tgz",
"integrity": "sha512-tiYPbZ3ZlGYDqZk9nZ/pckFGbUfhfc35O/933QxX11ObPOgGFuj92pDFrPACfPX6rYvG909p1CBvZeGS7Szd+w==",
"license": "MIT",
"dependencies": {
"@types/mime-types": "^2.1.4",
"chalk": "^5.5.0",
"marked": "^15.0.12",
"mime-types": "^3.0.1",
"string-width": "^8.1.0"
},
"engines": {
"node": ">=20.0.0"
}
},
"packages/mom/node_modules/@types/node": {
"version": "24.10.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
@ -6225,6 +6409,31 @@
"undici-types": "~7.16.0"
}
},
"packages/mom/node_modules/mime-db": {
"version": "1.54.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"packages/mom/node_modules/mime-types": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
"integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
"license": "MIT",
"dependencies": {
"mime-db": "^1.54.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"packages/mom/node_modules/undici-types": {
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
@ -6234,10 +6443,10 @@
},
"packages/pods": {
"name": "@mariozechner/pi",
"version": "0.11.6",
"version": "0.12.0",
"license": "MIT",
"dependencies": {
"@mariozechner/pi-agent-core": "^0.11.5",
"@mariozechner/pi-agent-core": "^0.11.6",
"chalk": "^5.5.0"
},
"bin": {
@ -6248,9 +6457,83 @@
"node": ">=20.0.0"
}
},
"packages/pods/node_modules/@mariozechner/pi-agent-core": {
"version": "0.11.6",
"resolved": "https://registry.npmjs.org/@mariozechner/pi-agent-core/-/pi-agent-core-0.11.6.tgz",
"integrity": "sha512-NnfZlEyXBitBVzyRsakOfvRcI0AoIpInADDUWgtGefrf/9Vv3QAmN7oDd7Zq5rZUbfMY5rVHUuogvEyZBMpwxQ==",
"license": "MIT",
"dependencies": {
"@mariozechner/pi-ai": "^0.11.6",
"@mariozechner/pi-tui": "^0.11.6"
},
"engines": {
"node": ">=20.0.0"
}
},
"packages/pods/node_modules/@mariozechner/pi-ai": {
"version": "0.11.6",
"resolved": "https://registry.npmjs.org/@mariozechner/pi-ai/-/pi-ai-0.11.6.tgz",
"integrity": "sha512-E96GATRiarbLtvukx69VruA8E2szGkmNbT86SHTFnOg1Nl1Y78hVcYggUFhTTHuuOZznbwG68iBzK6roKybysw==",
"license": "MIT",
"dependencies": {
"@anthropic-ai/sdk": "^0.61.0",
"@google/genai": "^1.30.0",
"@sinclair/typebox": "^0.34.41",
"ajv": "^8.17.1",
"ajv-formats": "^3.0.1",
"chalk": "^5.6.2",
"openai": "5.21.0",
"partial-json": "^0.1.7",
"zod-to-json-schema": "^3.24.6"
},
"engines": {
"node": ">=20.0.0"
}
},
"packages/pods/node_modules/@mariozechner/pi-tui": {
"version": "0.11.6",
"resolved": "https://registry.npmjs.org/@mariozechner/pi-tui/-/pi-tui-0.11.6.tgz",
"integrity": "sha512-tiYPbZ3ZlGYDqZk9nZ/pckFGbUfhfc35O/933QxX11ObPOgGFuj92pDFrPACfPX6rYvG909p1CBvZeGS7Szd+w==",
"license": "MIT",
"dependencies": {
"@types/mime-types": "^2.1.4",
"chalk": "^5.5.0",
"marked": "^15.0.12",
"mime-types": "^3.0.1",
"string-width": "^8.1.0"
},
"engines": {
"node": ">=20.0.0"
}
},
"packages/pods/node_modules/mime-db": {
"version": "1.54.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"packages/pods/node_modules/mime-types": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
"integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
"license": "MIT",
"dependencies": {
"mime-db": "^1.54.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"packages/proxy": {
"name": "@mariozechner/pi-proxy",
"version": "0.11.6",
"version": "0.12.0",
"dependencies": {
"@hono/node-server": "^1.14.0",
"hono": "^4.6.16"
@ -6266,7 +6549,7 @@
},
"packages/tui": {
"name": "@mariozechner/pi-tui",
"version": "0.11.6",
"version": "0.12.0",
"license": "MIT",
"dependencies": {
"@types/mime-types": "^2.1.4",
@ -6310,12 +6593,12 @@
},
"packages/web-ui": {
"name": "@mariozechner/pi-web-ui",
"version": "0.11.6",
"version": "0.12.0",
"license": "MIT",
"dependencies": {
"@lmstudio/sdk": "^1.5.0",
"@mariozechner/pi-ai": "^0.11.5",
"@mariozechner/pi-tui": "^0.11.5",
"@mariozechner/pi-ai": "^0.11.6",
"@mariozechner/pi-tui": "^0.11.6",
"docx-preview": "^0.3.7",
"jszip": "^3.10.1",
"lucide": "^0.544.0",
@ -6333,6 +6616,67 @@
"@mariozechner/mini-lit": "^0.2.0",
"lit": "^3.3.1"
}
},
"packages/web-ui/node_modules/@mariozechner/pi-ai": {
"version": "0.11.6",
"resolved": "https://registry.npmjs.org/@mariozechner/pi-ai/-/pi-ai-0.11.6.tgz",
"integrity": "sha512-E96GATRiarbLtvukx69VruA8E2szGkmNbT86SHTFnOg1Nl1Y78hVcYggUFhTTHuuOZznbwG68iBzK6roKybysw==",
"license": "MIT",
"dependencies": {
"@anthropic-ai/sdk": "^0.61.0",
"@google/genai": "^1.30.0",
"@sinclair/typebox": "^0.34.41",
"ajv": "^8.17.1",
"ajv-formats": "^3.0.1",
"chalk": "^5.6.2",
"openai": "5.21.0",
"partial-json": "^0.1.7",
"zod-to-json-schema": "^3.24.6"
},
"engines": {
"node": ">=20.0.0"
}
},
"packages/web-ui/node_modules/@mariozechner/pi-tui": {
"version": "0.11.6",
"resolved": "https://registry.npmjs.org/@mariozechner/pi-tui/-/pi-tui-0.11.6.tgz",
"integrity": "sha512-tiYPbZ3ZlGYDqZk9nZ/pckFGbUfhfc35O/933QxX11ObPOgGFuj92pDFrPACfPX6rYvG909p1CBvZeGS7Szd+w==",
"license": "MIT",
"dependencies": {
"@types/mime-types": "^2.1.4",
"chalk": "^5.5.0",
"marked": "^15.0.12",
"mime-types": "^3.0.1",
"string-width": "^8.1.0"
},
"engines": {
"node": ">=20.0.0"
}
},
"packages/web-ui/node_modules/mime-db": {
"version": "1.54.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"packages/web-ui/node_modules/mime-types": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
"integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
"license": "MIT",
"dependencies": {
"mime-db": "^1.54.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
}
}
}

View file

@ -1,6 +1,6 @@
{
"name": "@mariozechner/pi-agent-core",
"version": "0.11.6",
"version": "0.12.0",
"description": "General-purpose agent with transport abstraction, state management, and attachment support",
"type": "module",
"main": "./dist/index.js",
@ -18,8 +18,8 @@
"prepublishOnly": "npm run clean && npm run build"
},
"dependencies": {
"@mariozechner/pi-ai": "^0.11.6",
"@mariozechner/pi-tui": "^0.11.6"
"@mariozechner/pi-ai": "^0.12.0",
"@mariozechner/pi-tui": "^0.12.0"
},
"keywords": [
"ai",

View file

@ -1,6 +1,6 @@
{
"name": "@mariozechner/pi-ai",
"version": "0.11.6",
"version": "0.12.0",
"description": "Unified LLM API with automatic model discovery and provider configuration",
"type": "module",
"main": "./dist/index.js",

View file

@ -2,6 +2,12 @@
## [Unreleased]
## [0.12.0] - 2025-12-02
### Added
- **Standalone Binary Support**: Build a self-contained binary using Bun with `npm run build:binary`. Pre-built binaries for macOS (arm64/x64), Linux (x64/arm64), and Windows (x64) are available on GitHub Releases. Based on [#89](https://github.com/badlogic/pi-mono/pull/89) by [@steipete](https://github.com/steipete), extended with cross-platform path resolution and GitHub Actions for automated release builds.
## [0.11.6] - 2025-12-02
### Added

View file

@ -31,10 +31,51 @@ Works on Linux, macOS, and Windows (barely tested, needs Git Bash running in the
## Installation
### npm (recommended)
```bash
npm install -g @mariozechner/pi-coding-agent
```
### Standalone Binary
Pre-built binaries are available on the [GitHub Releases](https://github.com/badlogic/pi-mono/releases) page. Download the archive for your platform:
- `pi-darwin-arm64.tar.gz` - macOS Apple Silicon
- `pi-darwin-x64.tar.gz` - macOS Intel
- `pi-linux-x64.tar.gz` - Linux x64
- `pi-linux-arm64.tar.gz` - Linux ARM64
- `pi-windows-x64.zip` - Windows x64
Extract and run:
```bash
# macOS/Linux
tar -xzf pi-darwin-arm64.tar.gz
./pi-darwin-arm64
# Windows
unzip pi-windows-x64.zip
pi-windows-x64.exe
```
The archive includes the binary plus supporting files (README, CHANGELOG, themes). Keep them together in the same directory.
### Build Binary from Source
Requires [Bun](https://bun.sh) 1.0+:
```bash
git clone https://github.com/badlogic/pi-mono.git
cd pi-mono
npm install
cd packages/coding-agent
npm run build:binary
# Binary and supporting files are in dist/
./dist/pi
```
## Quick Start
```bash
@ -1093,6 +1134,23 @@ Things that might happen eventually:
- Switch to a model with bigger context (e.g., Gemini) using `/model` and either continue with that model, or let it summarize the session to a .md file to be loaded in a new session
- **Better RPC mode docs**: It works, you'll figure it out (see `test/rpc-example.ts`)
## Development
### Path Resolution
The codebase supports three execution modes:
- **npm**: Running via `node dist/cli.js` after npm install
- **Bun binary**: Standalone compiled binary with files alongside
- **tsx**: Running directly from source via `npx tsx src/cli.ts`
All path resolution for package assets (package.json, README.md, CHANGELOG.md, themes) must go through `src/paths.ts`:
```typescript
import { getPackageDir, getThemeDir, getPackageJsonPath, getReadmePath, getChangelogPath } from "./paths.js";
```
**Never use `__dirname` directly** for resolving package assets. The `paths.ts` module handles the differences between execution modes automatically.
## License
MIT

View file

@ -1,6 +1,6 @@
{
"name": "@mariozechner/pi-coding-agent",
"version": "0.11.6",
"version": "0.12.0",
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
"type": "module",
"bin": {
@ -14,18 +14,19 @@
],
"scripts": {
"clean": "rm -rf dist",
"build": "tsgo -p tsconfig.build.json && chmod +x dist/cli.js && npm run copy-theme-assets",
"build:binary": "command -v bun >/dev/null 2>&1 || { echo 'Error: Bun is required for building the binary. Install it from https://bun.sh'; exit 1; } && npm run build && bun build --compile --bytecode ./dist/cli.js --outfile dist/pi",
"copy-theme-assets": "cp src/theme/*.json dist/theme/",
"build": "tsgo -p tsconfig.build.json && chmod +x dist/cli.js && npm run copy-assets",
"build:binary": "command -v bun >/dev/null 2>&1 || { echo 'Error: Bun is required for building the binary. Install it from https://bun.sh'; exit 1; } && npm run build && bun build --compile ./dist/cli.js --outfile dist/pi && npm run copy-binary-assets",
"copy-assets": "cp src/theme/*.json dist/theme/",
"copy-binary-assets": "cp package.json dist/ && cp README.md dist/ && cp CHANGELOG.md dist/",
"dev": "tsgo -p tsconfig.build.json --watch --preserveWatchOutput",
"check": "tsgo --noEmit",
"test": "vitest --run",
"prepublishOnly": "npm run clean && npm run build"
},
"dependencies": {
"@mariozechner/pi-agent-core": "^0.11.6",
"@mariozechner/pi-ai": "^0.11.6",
"@mariozechner/pi-tui": "^0.11.6",
"@mariozechner/pi-agent-core": "^0.12.0",
"@mariozechner/pi-ai": "^0.12.0",
"@mariozechner/pi-tui": "^0.12.0",
"chalk": "^5.5.0",
"diff": "^8.0.2",
"glob": "^11.0.3"

View file

@ -1,6 +1,4 @@
import { existsSync, readFileSync } from "fs";
import { dirname, join } from "path";
import { fileURLToPath } from "url";
export interface ChangelogEntry {
major: number;
@ -97,11 +95,5 @@ export function getNewEntries(entries: ChangelogEntry[], lastVersion: string): C
return entries.filter((entry) => compareVersions(entry, last) > 0);
}
/**
* Get the path to the CHANGELOG.md file
*/
export function getChangelogPath(): string {
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
return join(__dirname, "../CHANGELOG.md");
}
// Re-export getChangelogPath from paths.ts for convenience
export { getChangelogPath } from "./paths.js";

View file

@ -2,14 +2,12 @@ import type { AgentState } from "@mariozechner/pi-agent-core";
import type { AssistantMessage, Message, ToolResultMessage, UserMessage } from "@mariozechner/pi-ai";
import { existsSync, readFileSync, writeFileSync } from "fs";
import { homedir } from "os";
import { basename, dirname, join } from "path";
import { fileURLToPath } from "url";
import { basename } from "path";
import { getPackageJsonPath } from "./paths.js";
import type { SessionManager } from "./session-manager.js";
// Get version from package.json
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const packageJson = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8"));
const packageJson = JSON.parse(readFileSync(getPackageJsonPath(), "utf-8"));
const VERSION = packageJson.version;
/**

View file

@ -4,11 +4,11 @@ import { ProcessTerminal, TUI } from "@mariozechner/pi-tui";
import chalk from "chalk";
import { existsSync, readFileSync, statSync } from "fs";
import { homedir } from "os";
import { dirname, extname, join, resolve } from "path";
import { fileURLToPath } from "url";
import { extname, join, resolve } from "path";
import { getChangelogPath, getNewEntries, parseChangelog } from "./changelog.js";
import { exportFromFile } from "./export-html.js";
import { findModel, getApiKeyForModel, getAvailableModels } from "./model-config.js";
import { getPackageJsonPath, getReadmePath } from "./paths.js";
import { SessionManager } from "./session-manager.js";
import { SettingsManager } from "./settings-manager.js";
import { expandSlashCommand, loadSlashCommands } from "./slash-commands.js";
@ -19,9 +19,7 @@ import { SessionSelectorComponent } from "./tui/session-selector.js";
import { TuiRenderer } from "./tui/tui-renderer.js";
// Get version from package.json
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const packageJson = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8"));
const packageJson = JSON.parse(readFileSync(getPackageJsonPath(), "utf-8"));
const VERSION = packageJson.version;
const defaultModelPerProvider: Record<KnownProvider, string> = {
@ -374,7 +372,7 @@ function buildSystemPrompt(customPrompt?: string, selectedTools?: ToolName[]): s
});
// Get absolute path to README.md
const readmePath = resolve(join(__dirname, "../README.md"));
const readmePath = getReadmePath();
// Build tools list based on selected tools
const tools = selectedTools || (["read", "bash", "edit", "write"] as ToolName[]);

View file

@ -0,0 +1,66 @@
import { existsSync } from "fs";
import { dirname, join, resolve } from "path";
import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
/**
* Detect if we're running as a Bun compiled binary.
* Bun binaries have import.meta.url starting with "file:///$bunfs/"
*/
export const isBunBinary = import.meta.url.startsWith("file:///$bunfs/");
/**
* Get the base directory for resolving package assets (themes, package.json, README.md, CHANGELOG.md).
* - For Bun binary: returns the directory containing the executable
* - For Node.js (dist/): returns __dirname (the dist/ directory)
* - For tsx (src/): returns parent directory (the package root)
*/
export function getPackageDir(): string {
if (isBunBinary) {
// Bun binary: resolve relative to the executable
return dirname(process.execPath);
}
// Node.js: check if package.json exists in __dirname (dist/) or parent (src/ case)
if (existsSync(join(__dirname, "package.json"))) {
return __dirname;
}
// Running from src/ via tsx - go up one level to package root
return dirname(__dirname);
}
/**
* Get path to the theme directory
* - For Bun binary: dist/theme/ next to executable
* - For Node.js (dist/): dist/theme/
* - For tsx (src/): src/theme/
*/
export function getThemeDir(): string {
if (isBunBinary) {
return join(dirname(process.execPath), "theme");
}
// __dirname is either dist/ or src/ - theme is always a subdirectory
return join(__dirname, "theme");
}
/**
* Get path to package.json
*/
export function getPackageJsonPath(): string {
return join(getPackageDir(), "package.json");
}
/**
* Get path to README.md
*/
export function getReadmePath(): string {
return resolve(join(getPackageDir(), "README.md"));
}
/**
* Get path to CHANGELOG.md
*/
export function getChangelogPath(): string {
return resolve(join(getPackageDir(), "CHANGELOG.md"));
}

View file

@ -1,13 +1,11 @@
import * as fs from "node:fs";
import * as os from "node:os";
import * as path from "node:path";
import { fileURLToPath } from "node:url";
import type { EditorTheme, MarkdownTheme, SelectListTheme } from "@mariozechner/pi-tui";
import { type Static, Type } from "@sinclair/typebox";
import { TypeCompiler } from "@sinclair/typebox/compiler";
import chalk from "chalk";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
import { getThemeDir } from "../paths.js";
// ============================================================================
// Types & Schema
@ -321,8 +319,9 @@ let BUILTIN_THEMES: Record<string, ThemeJson> | undefined;
function getBuiltinThemes(): Record<string, ThemeJson> {
if (!BUILTIN_THEMES) {
const darkPath = path.join(__dirname, "dark.json");
const lightPath = path.join(__dirname, "light.json");
const themeDir = getThemeDir();
const darkPath = path.join(themeDir, "dark.json");
const lightPath = path.join(themeDir, "light.json");
BUILTIN_THEMES = {
dark: JSON.parse(fs.readFileSync(darkPath, "utf-8")) as ThemeJson,
light: JSON.parse(fs.readFileSync(lightPath, "utf-8")) as ThemeJson,

View file

@ -1,6 +1,6 @@
{
"name": "@mariozechner/pi-mom",
"version": "0.11.6",
"version": "0.12.0",
"description": "Slack bot that delegates messages to the pi coding agent",
"type": "module",
"bin": {
@ -21,8 +21,8 @@
},
"dependencies": {
"@anthropic-ai/sandbox-runtime": "^0.0.16",
"@mariozechner/pi-agent-core": "^0.11.6",
"@mariozechner/pi-ai": "^0.11.6",
"@mariozechner/pi-agent-core": "^0.12.0",
"@mariozechner/pi-ai": "^0.12.0",
"@sinclair/typebox": "^0.34.0",
"@slack/socket-mode": "^2.0.0",
"@slack/web-api": "^7.0.0",

View file

@ -1,6 +1,6 @@
{
"name": "@mariozechner/pi",
"version": "0.11.6",
"version": "0.12.0",
"description": "CLI tool for managing vLLM deployments on GPU pods",
"type": "module",
"bin": {
@ -34,7 +34,7 @@
"node": ">=20.0.0"
},
"dependencies": {
"@mariozechner/pi-agent-core": "^0.11.6",
"@mariozechner/pi-agent-core": "^0.12.0",
"chalk": "^5.5.0"
},
"devDependencies": {}

View file

@ -1,6 +1,6 @@
{
"name": "@mariozechner/pi-proxy",
"version": "0.11.6",
"version": "0.12.0",
"type": "module",
"description": "CORS and authentication proxy for pi-ai",
"main": "dist/index.js",

View file

@ -1,6 +1,6 @@
{
"name": "@mariozechner/pi-tui",
"version": "0.11.6",
"version": "0.12.0",
"description": "Terminal User Interface library with differential rendering for efficient text-based applications",
"type": "module",
"main": "dist/index.js",

View file

@ -1,6 +1,6 @@
{
"name": "@mariozechner/pi-web-ui",
"version": "0.11.6",
"version": "0.12.0",
"description": "Reusable web UI components for AI chat interfaces powered by @mariozechner/pi-ai",
"type": "module",
"main": "dist/index.js",
@ -18,8 +18,8 @@
},
"dependencies": {
"@lmstudio/sdk": "^1.5.0",
"@mariozechner/pi-ai": "^0.11.6",
"@mariozechner/pi-tui": "^0.11.6",
"@mariozechner/pi-ai": "^0.12.0",
"@mariozechner/pi-tui": "^0.12.0",
"docx-preview": "^0.3.7",
"jszip": "^3.10.1",
"lucide": "^0.544.0",