Skip to content
Merged
Show file tree
Hide file tree
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
30 changes: 19 additions & 11 deletions .github/workflows/_npm-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ name: NPM Publish (Reusable)
# Called by publish-npm.yml (the OIDC-trusted caller).
#
# OIDC TRUSTED PUBLISHING SETUP (on npmjs.com):
# For each package (shux, mux, @coder/mux-chat-components):
# New packages must be published manually once before npm allows trusted publisher setup.
# For each package (@coder/shux, mux, @coder/mux-chat-components):
# 1. Go to package settings → Trusted Publisher → GitHub Actions
# 2. Configure:
# - Owner: coder
# - Repository: mux
# - Workflow filename: publish-npm.yml (the caller, NOT this reusable)
# - Allowed action: npm publish
# - Environment: (leave blank)

on:
Expand Down Expand Up @@ -91,7 +93,7 @@ jobs:
echo "version=${NPM_VERSION}" >> "$GITHUB_OUTPUT"
echo "tag=${NPM_TAG}" >> "$GITHUB_OUTPUT"

# Keep the canonical shux package and legacy mux forwarding package in lockstep.
# Keep the canonical @coder/shux package and legacy mux forwarding package in lockstep.
node ./scripts/set-package-version.js "$NPM_VERSION"

echo "Updated package versions to ${NPM_VERSION}"
Expand All @@ -106,25 +108,26 @@ jobs:
run: make build

- name: Pack npm package for smoke test
run: npm pack

- name: Find package tarball
id: find-tarball
id: pack
run: |
# shellcheck disable=SC2012 # ls is fine here - known filename pattern in controlled directory
TARBALL=$(ls shux-*.tgz | head -1)
TARBALL=$(./scripts/pack-npm-package.sh)
echo "tarball=$TARBALL" >> "$GITHUB_OUTPUT"
echo "Found package: $TARBALL"

- name: Run smoke test
env:
PACKAGE_TARBALL: ${{ steps.find-tarball.outputs.tarball }}
PACKAGE_TARBALL: ${{ steps.pack.outputs.tarball }}
SERVER_PORT: 3000
SERVER_HOST: localhost
STARTUP_TIMEOUT: 30
run: |
./scripts/smoke-test.sh

- name: Run legacy mux forwarding package smoke test
env:
CANONICAL_TARBALL: ${{ steps.pack.outputs.tarball }}
run: ./scripts/smoke-test-mux-compat.sh

- name: Upload server logs on smoke test failure
if: failure()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
Expand All @@ -140,7 +143,7 @@ jobs:
# don't respect shrinkwrap. Uses a different port to avoid conflicts.
- name: Run smoke test (without shrinkwrap)
env:
PACKAGE_TARBALL: ${{ steps.find-tarball.outputs.tarball }}
PACKAGE_TARBALL: ${{ steps.pack.outputs.tarball }}
SERVER_PORT: 3001
SERVER_HOST: localhost
STARTUP_TIMEOUT: 30
Expand All @@ -160,7 +163,12 @@ jobs:

- name: Clean up tarball
if: always()
run: rm -f shux-*.tgz
env:
PACKAGE_TARBALL: ${{ steps.pack.outputs.tarball }}
run: |
if [ -n "$PACKAGE_TARBALL" ]; then
rm -f "$PACKAGE_TARBALL"
fi

- name: Check if version exists
id: check-exists
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/auto-cleanup.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ jobs:
ANTHROPIC_API_KEY: ${{ secrets.AUTO_CLEANUP_ANTHROPIC_API_KEY }}
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: |
bunx shux@next run \
bunx @coder/shux@next run \
--model anthropic:claude-opus-5 \
--thinking xhigh \
< .github/prompts/auto-cleanup.md
16 changes: 11 additions & 5 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ jobs:
- 'tests/ui/**'
config:
- '.github/**'
- 'scripts/**'
- 'packages/mux-compat/**'
- 'jest.config.cjs'
- 'babel.config.cjs'
- 'package.json'
Expand Down Expand Up @@ -424,15 +426,19 @@ jobs:
- uses: ./.github/actions/setup-mux
- run: ./scripts/generate-npm-shrinkwrap.sh
- run: make build
- run: npm pack
- name: Pack npm package
id: pack
run: echo "tarball=$(./scripts/pack-npm-package.sh)" >> "$GITHUB_OUTPUT"
- env:
PACKAGE_TARBALL: ${{ steps.pack.outputs.tarball }}
SERVER_PORT: 3001
SERVER_HOST: localhost
STARTUP_TIMEOUT: 30
run: |
# shellcheck disable=SC2012 # ls is fine here - known filename pattern in controlled directory
TARBALL=$(ls shux-*.tgz | head -1)
PACKAGE_TARBALL="$TARBALL" ./scripts/smoke-test.sh
run: ./scripts/smoke-test.sh
- name: Test legacy mux forwarding package
env:
CANONICAL_TARBALL: ${{ steps.pack.outputs.tarball }}
run: ./scripts/smoke-test-mux-compat.sh
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
if: failure()
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish-npm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ name: Publish to NPM
#
# IMPORTANT: This workflow's filename (publish-npm.yml) must match the OIDC
# trusted publisher configured on npmjs.com. Do not rename without updating
# the npm trusted publisher settings for 'shux', 'mux', and
# the npm trusted publisher settings for '@coder/shux', 'mux', and
# '@coder/mux-chat-components'. Publisher setup remains external.

on:
Expand Down
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -431,10 +431,10 @@ test-coverage: ## Run tests with coverage

smoke-test: build ## Run smoke test on npm package
@echo "Building npm package tarball..."
@npm pack
@TARBALL=$$(ls shux-*.tgz | head -1); \
@TARBALL=$$(./scripts/pack-npm-package.sh) || exit $$?; \
echo "Running smoke test on $$TARBALL..."; \
PACKAGE_TARBALL="$$TARBALL" ./scripts/smoke-test.sh; \
PACKAGE_TARBALL="$$TARBALL" ./scripts/smoke-test.sh && \
CANONICAL_TARBALL="$$TARBALL" ./scripts/smoke-test-mux-compat.sh; \
EXIT_CODE=$$?; \
rm -f "$$TARBALL"; \
exit $$EXIT_CODE
Expand Down
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,12 @@
[![Discord](https://img.shields.io/discord/1446553342699507907?logo=discord&label=Discord)](https://cdr.co/mux-discord)
[![X (formerly Twitter)](https://img.shields.io/badge/Follow-%40codermux-black?logo=x)](https://x.com/codermux)


</div>

> [!IMPORTANT]
> This project was renamed from Mux to Shux after Mux.com raised a trademark concern. “Mux” is a common technical abbreviation of “multiplexer” and we do not expect confusion between the projects, but chose to rename ours rather than spend more time on the dispute. “Shux” captures our reaction to the process and bears no other significance.
>
Shux is a desktop & browser application for parallel agentic development. It enables developers to plan and execute tasks with multiple AI agents on local or remote compute.
>
> Shux is a desktop & browser application for parallel agentic development. It enables developers to plan and execute tasks with multiple AI agents on local or remote compute.

<p><img src="./docs/img/mux-demo.gif" alt="Shux product demo" width="100%" /></p>

Expand Down
2 changes: 1 addition & 1 deletion bun.lock

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

4 changes: 2 additions & 2 deletions docs/guides/github-actions.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Here's a minimal example that runs Shux in a GitHub Action:
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: bunx shux run "Review this PR for critical issues. If any are found, exit with 1 to block merge."
run: bunx @coder/shux run "Review this PR for critical issues. If any are found, exit with 1 to block merge."
```

`shux run` supports agent-controlled exit codes, so the workflow step can fail when issues are found.
Expand Down Expand Up @@ -115,7 +115,7 @@ jobs:
ANTHROPIC_API_KEY: ${{ secrets.AUTO_CLEANUP_ANTHROPIC_API_KEY }}
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: |
bunx shux@next run \
bunx @coder/shux@next run \
--model anthropic:claude-opus-5 \
--thinking xhigh \
< .github/prompts/auto-cleanup.md
Expand Down
6 changes: 3 additions & 3 deletions docs/install.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -88,16 +88,16 @@ The Shux CLI can also be run directly via `npx` without installing the desktop a

```bash
# Run agent tasks
npx shux run "Fix the failing tests"
npx @coder/shux run "Fix the failing tests"

# Start the server for remote/mobile access
npx shux server --port 3000
npx @coder/shux server --port 3000
```

Or install globally:

```bash
npm install -g shux
npm install -g @coder/shux
```

The legacy `mux` package and command forward to the matching Shux release during the transition. See [Mux compatibility](/reference/mux-compatibility) for storage, environment-variable, and downgrade details.
Expand Down
2 changes: 1 addition & 1 deletion docs/integrations/acp.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Open **Settings** (`Cmd+,`) and add:
"Shux": {
"type": "custom",
"command": "npx",
"args": ["-y", "shux", "acp"]
"args": ["-y", "@coder/shux", "acp"]
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions docs/reference/cli.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,22 @@ The CLI is available via npm and can be run directly with `npx`:

```bash
# Run without installing
npx shux run "Fix the failing tests"
npx @coder/shux run "Fix the failing tests"

# Or install globally
npm install -g shux
npm install -g @coder/shux
shux run "Fix the failing tests"
```

Using `npx shux` is especially convenient for CI/CD pipelines where you don't want to manage a global installation.
Using `npx @coder/shux` is especially convenient for CI/CD pipelines where you don't want to manage a global installation.

## `shux run`

Execute a one-off agent task:

```bash
# Basic usage - run in current directory
npx shux run "Fix the failing tests"
npx @coder/shux run "Fix the failing tests"

# Specify a directory
shux run --dir /path/to/project "Add authentication"
Expand Down
2 changes: 1 addition & 1 deletion docs/reference/mux-compatibility.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Shux is the canonical product, package, command, protocol, and local data-direct

| Surface | Canonical | Compatibility alias |
| --------------------- | ---------------------------------- | --------------------------------------------------------- |
| npm package | `shux` | `mux` forwarding package |
| npm package | `@coder/shux` | `mux` forwarding package |
| CLI | `shux` | `mux` |
| Local data | `~/.shux` | `~/.mux` and older `~/.cmux` links |
| Development data | `~/.shux-dev` | `~/.mux-dev` link |
Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "shux",
"name": "@coder/shux",
Comment thread
ammar-agent marked this conversation as resolved.
"version": "0.28.2",
"desktopName": "shux.desktop",
"description": "shux - coding agent multiplexer",
Expand Down Expand Up @@ -294,7 +294,7 @@
"zip"
],
"icon": "build/icon.icns",
"artifactName": "${name}-${version}-${arch}.${ext}",
"artifactName": "shux-${version}-${arch}.${ext}",
"hardenedRuntime": true,
"gatekeeperAssess": false,
"entitlements": "build/entitlements.mac.plist",
Expand All @@ -305,15 +305,15 @@
"target": "AppImage",
"category": "Development",
"icon": "build/icons",
"artifactName": "${name}-${version}-${arch}.${ext}",
"artifactName": "shux-${version}-${arch}.${ext}",
"desktop": {
"StartupWMClass": "shux"
}
},
"win": {
"target": "nsis",
"icon": "build/icon.png",
"artifactName": "${name}-${version}-${arch}.${ext}",
"artifactName": "shux-${version}-${arch}.${ext}",
"sign": "scripts/sign-windows.js",
"signingHashAlgorithms": [
"sha256"
Expand Down
6 changes: 3 additions & 3 deletions packages/mux-compat/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# mux compatibility package

`mux` is the legacy package and command name for [shux](https://github.com/coder/mux).
It forwards to the matching `shux` release so existing scripts keep working during
the rename. New installations should use `shux`.
`mux` is the legacy package and command name for [Shux](https://github.com/coder/mux).
It forwards to the matching `@coder/shux` release so existing scripts keep working during
the rename. New installations should use the `@coder/shux` package and `shux` command.
4 changes: 2 additions & 2 deletions packages/mux-compat/bin/mux.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env node

// Keep the published `mux` package as a tiny forwarding boundary. The implementation
// stays entirely in `shux`, so the compatibility package can be retired without
// stays entirely in `@coder/shux`, so the compatibility package can be retired without
// leaving duplicate CLI logic behind.
require("shux/dist/cli/index.js");
require("@coder/shux/dist/cli/index.js");
4 changes: 2 additions & 2 deletions packages/mux-compat/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "mux",
"version": "0.28.2",
"description": "Compatibility package forwarding the legacy mux command to shux",
"description": "Compatibility package forwarding the legacy mux command to @coder/shux",
"bin": {
"mux": "bin/mux.js"
},
Expand All @@ -10,7 +10,7 @@
"README.md"
],
"dependencies": {
"shux": "0.28.2"
"@coder/shux": "0.28.2"
},
"repository": {
"type": "git",
Expand Down
2 changes: 1 addition & 1 deletion scripts/generate-npm-shrinkwrap.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# Generate npm-shrinkwrap.json for the root mux package.
# Generate npm-shrinkwrap.json for the canonical @coder/shux package.
# We rely on npm's resolver and only include production dependencies.

set -euo pipefail
Expand Down
27 changes: 27 additions & 0 deletions scripts/pack-npm-package.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/usr/bin/env bash

set -euo pipefail

ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
cd "$ROOT_DIR"

# Scoped package tarballs include the scope in their generated filename, so derive
# the path from npm's machine-readable output instead of duplicating that convention.
PACK_JSON=$(npm pack --json)
TARBALL=$(printf '%s' "$PACK_JSON" | node -e '
let input = "";
process.stdin.setEncoding("utf8");
process.stdin.on("data", (chunk) => (input += chunk));
process.stdin.on("end", () => {
const filename = JSON.parse(input)[0]?.filename;
if (!filename) process.exit(1);
process.stdout.write(filename);
});
')

if [[ ! -f "$TARBALL" ]]; then
echo "npm pack did not create the reported tarball: $TARBALL" >&2
exit 1
fi

printf '%s/%s\n' "$ROOT_DIR" "$TARBALL"
2 changes: 1 addition & 1 deletion scripts/set-package-version.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ packageJson.version = version;
const legacyCompatPackageJsonPath = "packages/mux-compat/package.json";
const legacyCompatPackageJson = JSON.parse(fs.readFileSync(legacyCompatPackageJsonPath, "utf8"));
legacyCompatPackageJson.version = version;
legacyCompatPackageJson.dependencies.shux = version;
legacyCompatPackageJson.dependencies = { [packageJson.name]: version };

// When version includes a prerelease suffix like "-nightly.N", tell
// electron-builder to generate channel-specific manifests (e.g. nightly.yml,
Expand Down
Loading
Loading