diff --git a/.github/workflows/_npm-publish.yml b/.github/workflows/_npm-publish.yml
index 67c2364b25..742063b5a5 100644
--- a/.github/workflows/_npm-publish.yml
+++ b/.github/workflows/_npm-publish.yml
@@ -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:
@@ -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}"
@@ -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
@@ -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
@@ -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
diff --git a/.github/workflows/auto-cleanup.yml b/.github/workflows/auto-cleanup.yml
index 226b4648c8..bdecb2293c 100644
--- a/.github/workflows/auto-cleanup.yml
+++ b/.github/workflows/auto-cleanup.yml
@@ -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
diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml
index 9149af4bcf..60613c8b91 100644
--- a/.github/workflows/pr.yml
+++ b/.github/workflows/pr.yml
@@ -57,6 +57,8 @@ jobs:
- 'tests/ui/**'
config:
- '.github/**'
+ - 'scripts/**'
+ - 'packages/mux-compat/**'
- 'jest.config.cjs'
- 'babel.config.cjs'
- 'package.json'
@@ -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:
diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml
index c53b81d408..9ef5713ee5 100644
--- a/.github/workflows/publish-npm.yml
+++ b/.github/workflows/publish-npm.yml
@@ -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:
diff --git a/Makefile b/Makefile
index c26e07866c..e0794b0d6e 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/README.md b/README.md
index ac1d6ba8ea..af87dced4d 100644
--- a/README.md
+++ b/README.md
@@ -13,13 +13,12 @@
[](https://cdr.co/mux-discord)
[](https://x.com/codermux)
-
> [!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.

diff --git a/bun.lock b/bun.lock
index 1e6e403f9c..8b2482bb62 100644
--- a/bun.lock
+++ b/bun.lock
@@ -2,7 +2,7 @@
"lockfileVersion": 1,
"workspaces": {
"": {
- "name": "shux",
+ "name": "@coder/shux",
"dependencies": {
"@agentclientprotocol/sdk": "^0.25.0",
"@ai-sdk/amazon-bedrock": "^5.0.15",
diff --git a/docs/guides/github-actions.mdx b/docs/guides/github-actions.mdx
index 694f095672..c21c8076c0 100644
--- a/docs/guides/github-actions.mdx
+++ b/docs/guides/github-actions.mdx
@@ -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.
@@ -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
diff --git a/docs/install.mdx b/docs/install.mdx
index 575e58dabc..983dfe037d 100644
--- a/docs/install.mdx
+++ b/docs/install.mdx
@@ -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.
diff --git a/docs/integrations/acp.mdx b/docs/integrations/acp.mdx
index dc18c02ffc..fc876d0416 100644
--- a/docs/integrations/acp.mdx
+++ b/docs/integrations/acp.mdx
@@ -39,7 +39,7 @@ Open **Settings** (`Cmd+,`) and add:
"Shux": {
"type": "custom",
"command": "npx",
- "args": ["-y", "shux", "acp"]
+ "args": ["-y", "@coder/shux", "acp"]
}
}
}
diff --git a/docs/reference/cli.mdx b/docs/reference/cli.mdx
index 3fb0b4b493..44fb8a0e05 100644
--- a/docs/reference/cli.mdx
+++ b/docs/reference/cli.mdx
@@ -27,14 +27,14 @@ 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`
@@ -42,7 +42,7 @@ 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"
diff --git a/docs/reference/mux-compatibility.mdx b/docs/reference/mux-compatibility.mdx
index f02c121213..929b00437a 100644
--- a/docs/reference/mux-compatibility.mdx
+++ b/docs/reference/mux-compatibility.mdx
@@ -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 |
diff --git a/package.json b/package.json
index 8cae898fa8..e2bcdc0de6 100644
--- a/package.json
+++ b/package.json
@@ -1,5 +1,5 @@
{
- "name": "shux",
+ "name": "@coder/shux",
"version": "0.28.2",
"desktopName": "shux.desktop",
"description": "shux - coding agent multiplexer",
@@ -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",
@@ -305,7 +305,7 @@
"target": "AppImage",
"category": "Development",
"icon": "build/icons",
- "artifactName": "${name}-${version}-${arch}.${ext}",
+ "artifactName": "shux-${version}-${arch}.${ext}",
"desktop": {
"StartupWMClass": "shux"
}
@@ -313,7 +313,7 @@
"win": {
"target": "nsis",
"icon": "build/icon.png",
- "artifactName": "${name}-${version}-${arch}.${ext}",
+ "artifactName": "shux-${version}-${arch}.${ext}",
"sign": "scripts/sign-windows.js",
"signingHashAlgorithms": [
"sha256"
diff --git a/packages/mux-compat/README.md b/packages/mux-compat/README.md
index 2228771af9..99f3cc55ac 100644
--- a/packages/mux-compat/README.md
+++ b/packages/mux-compat/README.md
@@ -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.
diff --git a/packages/mux-compat/bin/mux.js b/packages/mux-compat/bin/mux.js
index 17ea4fa267..5aa9ab8651 100755
--- a/packages/mux-compat/bin/mux.js
+++ b/packages/mux-compat/bin/mux.js
@@ -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");
diff --git a/packages/mux-compat/package.json b/packages/mux-compat/package.json
index c3fd5da310..82011a0ef1 100644
--- a/packages/mux-compat/package.json
+++ b/packages/mux-compat/package.json
@@ -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"
},
@@ -10,7 +10,7 @@
"README.md"
],
"dependencies": {
- "shux": "0.28.2"
+ "@coder/shux": "0.28.2"
},
"repository": {
"type": "git",
diff --git a/scripts/generate-npm-shrinkwrap.sh b/scripts/generate-npm-shrinkwrap.sh
index eff5403399..92bb6a61e2 100755
--- a/scripts/generate-npm-shrinkwrap.sh
+++ b/scripts/generate-npm-shrinkwrap.sh
@@ -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
diff --git a/scripts/pack-npm-package.sh b/scripts/pack-npm-package.sh
new file mode 100755
index 0000000000..c14035e764
--- /dev/null
+++ b/scripts/pack-npm-package.sh
@@ -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"
diff --git a/scripts/set-package-version.js b/scripts/set-package-version.js
index 4dfd32032c..8069590392 100644
--- a/scripts/set-package-version.js
+++ b/scripts/set-package-version.js
@@ -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,
diff --git a/scripts/smoke-test-mux-compat.sh b/scripts/smoke-test-mux-compat.sh
new file mode 100755
index 0000000000..23b3c4981b
--- /dev/null
+++ b/scripts/smoke-test-mux-compat.sh
@@ -0,0 +1,53 @@
+#!/usr/bin/env bash
+# Smoke test the published `mux` forwarding package against the canonical tarball.
+
+set -euo pipefail
+
+CANONICAL_TARBALL="${CANONICAL_TARBALL:-}"
+MUX_COMPAT_DIR="${MUX_COMPAT_DIR:-packages/mux-compat}"
+
+if [[ -z "$CANONICAL_TARBALL" ]] || [[ ! -f "$CANONICAL_TARBALL" ]]; then
+ echo "CANONICAL_TARBALL must point to the packed @coder/shux tarball" >&2
+ exit 1
+fi
+
+CANONICAL_TARBALL=$(realpath "$CANONICAL_TARBALL")
+MUX_COMPAT_DIR=$(realpath "$MUX_COMPAT_DIR")
+TEST_DIR=$(mktemp -d)
+trap 'rm -rf "$TEST_DIR"' EXIT
+
+# npm cannot resolve the unpublished scoped dependency, so pack a temporary mux
+# package that points at the exact canonical tarball produced by this checkout.
+TEMP_MUX_DIR="$TEST_DIR/mux-package"
+cp -R "$MUX_COMPAT_DIR" "$TEMP_MUX_DIR"
+node - "$TEMP_MUX_DIR/package.json" "$CANONICAL_TARBALL" <<'NODE'
+const fs = require("node:fs");
+const [packageJsonPath, canonicalTarball] = process.argv.slice(2);
+const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
+packageJson.dependencies["@coder/shux"] = `file:${canonicalTarball}`;
+fs.writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`);
+NODE
+
+MUX_TARBALL=$(cd "$TEMP_MUX_DIR" && npm 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);
+});
+')
+MUX_TARBALL="$TEMP_MUX_DIR/$MUX_TARBALL"
+
+INSTALL_DIR="$TEST_DIR/install"
+mkdir -p "$INSTALL_DIR"
+printf '{"name":"mux-compat-smoke","private":true}\n' >"$INSTALL_DIR/package.json"
+(cd "$INSTALL_DIR" && npm install --ignore-scripts --no-audit --no-fund "$MUX_TARBALL")
+
+if [[ ! -x "$INSTALL_DIR/node_modules/.bin/mux" ]]; then
+ echo "mux forwarding binary was not installed" >&2
+ exit 1
+fi
+
+"$INSTALL_DIR/node_modules/.bin/mux" --help >/dev/null
diff --git a/scripts/smoke-test.sh b/scripts/smoke-test.sh
index 7a31f1ca53..0285db7644 100755
--- a/scripts/smoke-test.sh
+++ b/scripts/smoke-test.sh
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
-# Smoke test for the canonical shux npm package and legacy mux binary alias.
+# Smoke test for the canonical @coder/shux npm package and legacy mux binary alias.
# Tests that the package can be installed and the server starts correctly.
set -euo pipefail
@@ -104,7 +104,7 @@ cat >package.json < PackagedAppInfo;
-const LOWERCASE_ARTIFACT_TEMPLATE = "${name}-${version}-${arch}.${ext}";
+const LEGACY_MUX_BIN_PATH = fileURLToPath(
+ new URL("../../../packages/mux-compat/bin/mux.js", import.meta.url)
+);
+const LOWERCASE_ARTIFACT_TEMPLATE = "shux-${version}-${arch}.${ext}";
function expandArtifactName(arch: string, ext: string): string {
- return LOWERCASE_ARTIFACT_TEMPLATE.replaceAll("${name}", packageJson.name)
- .replaceAll("${version}", packageJson.version)
+ return LOWERCASE_ARTIFACT_TEMPLATE.replaceAll("${version}", packageJson.version)
.replaceAll("${arch}", arch)
.replaceAll("${ext}", ext);
}
describe("shux package transition contract", () => {
test("ships one canonical CLI implementation through both command names", () => {
- expect(packageJson.name).toBe("shux");
+ expect(packageJson.name).toBe("@coder/shux");
+ expect(packageJson.publishConfig.access).toBe("public");
expect(packageJson.bin).toEqual({
shux: "dist/cli/index.js",
mux: "dist/cli/index.js",
@@ -40,7 +48,7 @@ describe("shux package transition contract", () => {
test("retains install identity and the legacy deep-link scheme", () => {
expect(packageJson.build.appId).toBe("com.mux.app");
expect(packageJson.build.productName).toBe("Shux");
- expect(packageJson.build.executableName).toBe(packageJson.name);
+ expect(packageJson.build.executableName).toBe("shux");
expect(packageJson.build.protocols[0]?.schemes).toEqual(["shux", "mux"]);
});
@@ -87,20 +95,49 @@ describe("shux package transition contract", () => {
expect(packageJson.build.win.artifactName).toBe(LOWERCASE_ARTIFACT_TEMPLATE);
const expanded = expandArtifactName("arm64", "dmg");
- expect(expanded.startsWith(`${packageJson.name}-`)).toBe(true);
- expect(expanded).toBe(`${packageJson.name}-${packageJson.version}-arm64.dmg`);
+ expect(expanded).toBe(`shux-${packageJson.version}-arm64.dmg`);
+ expect(expanded.startsWith(`${packageJson.name}-`)).toBe(false);
expect(expanded.startsWith(`${packageJson.build.productName}-`)).toBe(false);
});
test("keeps the Linux desktop name visible while WM class follows the slug", () => {
- expect(packageJson.build.linux.desktop?.StartupWMClass).toBe(packageJson.name);
+ expect(packageJson.build.linux.desktop?.StartupWMClass).toBe(packageJson.build.executableName);
expect(packageJson.build.linux.desktop).not.toHaveProperty("Name");
});
- test("keeps the published mux forwarding package version-locked to shux", () => {
+ test("keeps the published mux forwarding package version-locked to @coder/shux", () => {
expect(legacyPackageJson.name).toBe("mux");
expect(legacyPackageJson.version).toBe(packageJson.version);
- expect(legacyPackageJson.dependencies.shux).toBe(packageJson.version);
+ expect(legacyPackageJson.dependencies).toEqual({
+ "@coder/shux": packageJson.version,
+ });
+ });
+
+ test("executes the legacy mux package through the scoped canonical dependency", () => {
+ const tempDir = mkdtempSync(path.join(tmpdir(), "shux-mux-compat-"));
+ const canonicalCliDir = path.join(tempDir, "node_modules", "@coder", "shux", "dist", "cli");
+
+ try {
+ mkdirSync(canonicalCliDir, { recursive: true });
+ writeFileSync(
+ path.join(canonicalCliDir, "index.js"),
+ 'process.stdout.write("forwarded-to-@coder/shux");\n'
+ );
+
+ const result = spawnSync("node", [LEGACY_MUX_BIN_PATH], {
+ encoding: "utf8",
+ env: {
+ ...process.env,
+ NODE_PATH: path.join(tempDir, "node_modules"),
+ },
+ });
+
+ expect(result.status).toBe(0);
+ expect(result.stderr).toBe("");
+ expect(result.stdout).toBe("forwarded-to-@coder/shux");
+ } finally {
+ rmSync(tempDir, { recursive: true, force: true });
+ }
});
test("keeps the VS Code Marketplace identity on mux while showing Shux", () => {
diff --git a/src/node/services/agentSkills/builtInSkillContent.generated.ts b/src/node/services/agentSkills/builtInSkillContent.generated.ts
index 2a16f767a8..320c0f270d 100644
--- a/src/node/services/agentSkills/builtInSkillContent.generated.ts
+++ b/src/node/services/agentSkills/builtInSkillContent.generated.ts
@@ -4757,7 +4757,7 @@ export const BUILTIN_SKILL_FILES: Record> = {
" 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.",
@@ -4853,7 +4853,7 @@ export const BUILTIN_SKILL_FILES: Record> = {
" 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",
@@ -6080,16 +6080,16 @@ export const BUILTIN_SKILL_FILES: Record> = {
"",
"```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.",
@@ -6139,7 +6139,7 @@ export const BUILTIN_SKILL_FILES: Record> = {
' "Shux": {',
' "type": "custom",',
' "command": "npx",',
- ' "args": ["-y", "shux", "acp"]',
+ ' "args": ["-y", "@coder/shux", "acp"]',
" }",
" }",
"}",
@@ -6501,14 +6501,14 @@ export const BUILTIN_SKILL_FILES: Record> = {
"",
"```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`",
"",
@@ -6516,7 +6516,7 @@ export const BUILTIN_SKILL_FILES: Record> = {
"",
"```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"',
@@ -6820,7 +6820,7 @@ export const BUILTIN_SKILL_FILES: Record> = {
"",
"| 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 |",