Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
fd36a83
macOS port, phase 1: the workspace compiles and passes clippy on macOS
ShadowfetchStudio Oct 16, 2026
dc72f86
Workspace opens never follow a final symlink on macOS either
ShadowfetchStudio Oct 16, 2026
eb442c4
Workspace paths named through a symlinked folder stay inside
ShadowfetchStudio Oct 16, 2026
32182ef
macOS: every engine unit test passes; symlinked folders and backups f…
ShadowfetchStudio Oct 16, 2026
070a4b6
macOS CI: run the engine unit tests
ShadowfetchStudio Oct 16, 2026
7892e72
macOS: sessions and managed checkouts record real paths
ShadowfetchStudio Oct 16, 2026
2b889af
macOS: SQLite inspection, terminals and RAM detection work
ShadowfetchStudio Oct 16, 2026
40a072d
macOS: worktree cleanup runs there; the integration suite passes
ShadowfetchStudio Oct 16, 2026
e511e0b
macOS: ~/Library folders, the login shell's PATH, process trees
ShadowfetchStudio Oct 16, 2026
b901d2c
macOS: agent shell commands run in the macOS sandbox (Seatbelt)
ShadowfetchStudio Oct 17, 2026
58dbacd
macOS: API keys can live in the login Keychain
ShadowfetchStudio Oct 17, 2026
91d5b47
macOS: the dev-server preview works there
ShadowfetchStudio Oct 17, 2026
27f43f7
macOS: preview and vendor login tests run there too
ShadowfetchStudio Oct 17, 2026
85bfb5b
macOS: helpers die with ShadowCode; Metal runtime and app bundle setup
ShadowfetchStudio Oct 17, 2026
b6100f0
macOS: precompiled Metal shaders, Metal in Settings, app build script
ShadowfetchStudio Oct 17, 2026
32e5297
macOS: say Metal for the GPU; port status in the estimate doc
ShadowfetchStudio Oct 17, 2026
6703a37
macOS: GitHub releases carry the Mac app; one-line installer
ShadowfetchStudio Oct 17, 2026
3735aa4
Show folders on approval cards; warn when memory is short now
ShadowfetchStudio Oct 17, 2026
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
38 changes: 38 additions & 0 deletions .github/workflows/macos.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: macOS
# The macOS port (docs/MACOS_PORT_ESTIMATE.md): the whole workspace must keep
# compiling on macOS with clippy warnings as errors, and the tests that pass
# there must keep passing. Nothing here is packaged or signed.
on:
push:
branches: [main]
paths: ['native/**', 'src-tauri/**', 'ui/**', 'Cargo.toml', 'Cargo.lock', '.github/workflows/macos.yml']
pull_request:
paths: ['native/**', 'src-tauri/**', 'ui/**', 'Cargo.toml', 'Cargo.lock', '.github/workflows/macos.yml']
permissions:
contents: read
concurrency:
group: macos-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
runs-on: macos-15
timeout-minutes: 60
env:
RUSTUP_TOOLCHAIN: '1.95.0'
CARGO_PROFILE_DEV_DEBUG: line-tables-only
CARGO_PROFILE_TEST_DEBUG: line-tables-only
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: '22'
cache: npm
cache-dependency-path: ui/package-lock.json
- name: Install pinned Rust toolchain
run: rustup toolchain install 1.95.0 --profile minimal --component clippy
# The desktop crate embeds the built interface (tauri.conf.json frontendDist).
- run: npm --prefix ui ci
- run: npm --prefix ui run build
- run: cargo +1.95.0 clippy --workspace --all-targets --locked -- -D warnings
# Phase 2: engine unit tests pass on macOS; integration tests join later.
- run: cargo +1.95.0 test -p shadowcode-core --lib --locked
122 changes: 122 additions & 0 deletions .github/workflows/release-macos.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
name: macOS release
# ShadowCode.app as a .dmg for Apple silicon and Intel Macs, attached to a
# published GitHub release with a SHA-256 checksum file
# (ShadowCode_<version>_macos.sha256) that scripts/install-macos.sh checks.
# The images are ad-hoc signed, not notarized: GitHub is the distribution
# channel. On pull requests that touch macOS packaging this only builds and
# smoke-tests the app (nothing is uploaded).
on:
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: 'Release tag to build and attach (for example v2.2.0)'
required: true
pull_request:
paths:
- '.github/workflows/release-macos.yml'
- 'scripts/build-macos-app.sh'
- 'scripts/build-llama.cpp.sh'
- 'scripts/install-macos.sh'
- 'packaging/macos/**'
- 'src-tauri/tauri.macos.conf.json'
- 'src-tauri/Info.plist'
- 'tools/llama.cpp.pin'
permissions:
contents: read
concurrency:
group: release-macos-${{ github.event.release.tag_name || inputs.tag || github.ref }}
cancel-in-progress: false
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- runner: macos-15
arch: aarch64
- runner: macos-15-intel
arch: x64
runs-on: ${{ matrix.runner }}
timeout-minutes: 120
env:
RUSTUP_TOOLCHAIN: '1.95.0'
TAG: ${{ github.event.release.tag_name || inputs.tag || '' }}
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
ref: ${{ env.TAG || github.sha }}
persist-credentials: false
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: '22'
cache: npm
cache-dependency-path: ui/package-lock.json
- name: Install the pinned Rust toolchain
run: rustup toolchain install 1.95.0 --profile minimal
- name: Require the tag to match the app version
if: env.TAG != ''
run: |
version="$(node -p "require('./src-tauri/tauri.conf.json').version")"
test "v$version" = "$TAG" || { echo "Tag $TAG does not match app version $version" >&2; exit 1; }
- name: Build the Metal runtime, ShadowCode.app and the .dmg
env:
SHADOWCODE_COMMIT: ${{ github.sha }}
CARGO_PROFILE_RELEASE_DEBUG: '0'
run: bash scripts/build-macos-app.sh
- name: Smoke-test the bundled app and runtime
run: |
app=target/release/bundle/macos/ShadowCode.app
version="$(node -p "require('./src-tauri/tauri.conf.json').version")"
"$app/Contents/MacOS/shadowcode" --version | grep -Fx "ShadowCode $version"
profile="$(mktemp -d)"
"$app/Contents/MacOS/shadowcode" --profile "$profile" doctor --json > "$profile/doctor.json"
node -e "const d=require(process.argv[1]); const c=(d.checks||[]).find(x=>x.id==='seatbelt'); if(!c||c.status!=='pass') throw new Error('macOS sandbox check: '+JSON.stringify(c))" "$profile/doctor.json"
"$app/Contents/Resources/llama.cpp/llama-server" --version
test -f "$app/Contents/Resources/llama.cpp/default.metallib"
dmg="target/release/bundle/dmg/ShadowCode_${version}_${{ matrix.arch }}.dmg"
test -f "$dmg"
hdiutil verify "$dmg"
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: macos-dmg-${{ matrix.arch }}
path: target/release/bundle/dmg/*.dmg
if-no-files-found: error
compression-level: 0
retention-days: 7
attach:
needs: build
if: github.event_name != 'pull_request'
runs-on: ubuntu-24.04
timeout-minutes: 20
permissions:
contents: write
env:
TAG: ${{ github.event.release.tag_name || inputs.tag }}
GH_TOKEN: ${{ github.token }}
steps:
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
pattern: macos-dmg-*
path: dmg
merge-multiple: true
- name: Attach the images and their checksums without replacing anything
run: |
version="${TAG#v}"
cd dmg
ls
test -f "ShadowCode_${version}_aarch64.dmg"
test -f "ShadowCode_${version}_x64.dmg"
sha256sum "ShadowCode_${version}_aarch64.dmg" "ShadowCode_${version}_x64.dmg" > "ShadowCode_${version}_macos.sha256"
cat "ShadowCode_${version}_macos.sha256"
existing="$(gh release view "$TAG" --repo "$GITHUB_REPOSITORY" --json assets -q '.assets[].name')"
for file in "ShadowCode_${version}_aarch64.dmg" "ShadowCode_${version}_x64.dmg" "ShadowCode_${version}_macos.sha256"; do
if grep -Fxq "$file" <<<"$existing"; then
# Never overwrite a published file: an existing one must match.
gh release download "$TAG" --repo "$GITHUB_REPOSITORY" --pattern "$file" --output "existing-$file"
cmp -s "$file" "existing-$file" || { echo "$file on the release differs from this build; not replacing it" >&2; exit 1; }
else
gh release upload "$TAG" "$file" --repo "$GITHUB_REPOSITORY"
fi
done
37 changes: 37 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 35 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,33 @@ for published versions. The 2.1.0 x86_64 packages and checksums are:
`RELEASE-AUTH.sig` (download these beside the package; the installer checks
the publisher's signature before running anything)

### macOS

From 2.2.0, releases also carry `ShadowCode_VERSION_aarch64.dmg` (Apple
silicon) and `ShadowCode_VERSION_x64.dmg` (Intel), for macOS 12 or later,
with their SHA-256 checksums in `ShadowCode_VERSION_macos.sha256`. The app
includes llama.cpp with Metal for local models.

Install or update with one command; it picks your Mac's image, checks it
against the release's checksums, puts ShadowCode in Applications and links
the `shadowcode` command into `~/.local/bin`:

```bash
curl -fsSL https://raw.githubusercontent.com/ShadowfetchStudio/ShadowCode/main/scripts/install-macos.sh | bash
```

Or download the `.dmg` from the release page and drag ShadowCode into
Applications. The app is distributed on GitHub only and is not notarized by
Apple, so the first time you open a copy downloaded in a browser macOS
refuses it: open **System Settings › Privacy & Security** and choose **Open
Anyway** next to ShadowCode (the installer above does not need this).

On macOS your settings and history are in `~/Library/Application
Support/ShadowCode`, shell commands run in the macOS sandbox (Seatbelt) and
API keys can be kept in the Keychain. To build the app yourself, run
`scripts/build-macos-app.sh` on a Mac with Xcode, Rust 1.95, Node 22 and
cmake.

### AppImage (recommended)

The installer requires signed release packages. Published 0.32.0
Expand Down Expand Up @@ -246,7 +273,8 @@ no version or identifier, and nothing is downloaded or installed for you.
When a newer version exists, **Update available** appears in the status bar;
**Settings › About** links the release notes and shows how to update this
installation (the authenticated installer for the AppImage, `apt upgrade` from the
ShadowCode APT repository or your package manager for the deb). Turn it off there with **Check for updates once a
ShadowCode APT repository or your package manager for the deb, the macOS
installer command for the Mac app). Turn it off there with **Check for updates once a
day**; Offline mode pauses it. Distributions can turn it off for everyone.

## First run
Expand Down Expand Up @@ -658,6 +686,12 @@ The editor's project must be trusted in ShadowCode (or start the agent with
| Your rules, skills, commands and agents for every project, Git imports | `~/.config/shadowcode/profile/` |
| Rules & skills switches, imports and export links | `~/.config/shadow-agent/rulebook.json` (mode 600) |

On macOS, settings, data and state are the `config`, `data` and `state`
folders in `~/Library/Application Support/ShadowCode`, the cache is
`~/Library/Caches/ShadowCode`, and a llama.cpp runtime you build yourself goes
to `~/Library/Application Support/ShadowCode/llama.cpp` (the app carries its
own).

The directories are named `shadow-agent`, and keep that name in every 1.x
version so every upgrade finds your data. `--profile DIR` keeps a separate
set, for example for development. The Antigravity directories follow
Expand Down
Loading
Loading