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
70 changes: 70 additions & 0 deletions .github/workflows/deploy-landing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: Deploy landing

# Publishes the landing + docs site (apps/landing) to GitHub Pages.
# - push to main -> build + deploy the live site (mx.rousanali.com)
# - pull_request -> build only (a check that the site still compiles)
# The custom domain is carried by apps/landing/public/CNAME, which Vite copies
# into dist/ so the Pages artifact always declares mx.rousanali.com.
# Only runs when the site or its build inputs actually change.

on:
push:
branches: [main]
paths:
- 'apps/landing/**'
- '.github/workflows/deploy-landing.yml'
- 'pnpm-lock.yaml'
- 'package.json'
pull_request:
paths:
- 'apps/landing/**'
- '.github/workflows/deploy-landing.yml'
- 'pnpm-lock.yaml'
- 'package.json'
workflow_dispatch:

# GitHub Pages deployment needs these; id-token lets deploy-pages authenticate.
permissions:
contents: read
pages: write
id-token: write

# Allow one in-flight Pages deployment at a time; don't cancel a running one.
concurrency:
group: pages
cancel-in-progress: false

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4

- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm

- run: pnpm install

# Produces the static site (incl. the CNAME) at apps/landing/dist.
- run: pnpm landing:build

# Package dist/ as the Pages artifact for the deploy job to consume.
- uses: actions/upload-pages-artifact@v3
with:
path: apps/landing/dist

deploy:
# Only publish from main; on PRs the build job above is just a check.
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- id: deployment
uses: actions/deploy-pages@v4
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ npm/LICENSE
# dependencies
node_modules/

# VitePress docs-site build output + cache
docs/.vitepress/dist/
docs/.vitepress/cache/

# landing + docs site build output
apps/landing/dist/

# playwright screenshot/console artifacts
.playwright-mcp/

Expand Down
26 changes: 14 additions & 12 deletions CLAUDE.md

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ Source and tooling for **mx** ("multiplexer") — a system for working on severa

This repo is the **source** for mx: a TypeScript pnpm monorepo with the core library, the CLI, the runtime templates, and the docs. The thing it produces — a single `mx/` **runtime** with your cloned repos and live feature environments — lives wherever `$MX_RUNTIME` points (default `~/mx`), fully decoupled from this repo.

Published on npm as **`@roulabs/mx`** (command `mx`). For end-user install/usage, see [`npm/README.md`](npm/README.md). The `npm/` folder is the publishable package — `pnpm build` populates it; `npm publish` ships it.
Published on npm as **`@rousan/mx`** (command `mx`). For end-user install/usage, see [`npm/README.md`](npm/README.md). The `npm/` folder is the publishable package — `pnpm build` populates it; `npm publish` ships it.

## Runtime vs. source

| | what it is | where |
|-------------|--------------------------------------|-------------------------------|
| **source** | the core lib, CLI, templates, docs | this repo (`github.com/roulabs/mx`) |
| **source** | the core lib, CLI, templates, docs | this repo (`github.com/rousan/mx`) |
| **runtime** | clones + live feature environments | `$MX_RUNTIME` (default `~/mx`) |

The runtime's `CLAUDE.md` is **generated** from `templates/CLAUDE.md` at the repo root (run `pnpm build` then `mx sync` to re-stamp). The per-work `work.json` and `.code-workspace` are **owned and generated by the CLI** — feature sessions read/mutate them only through `mx` commands. The runtime is **versioned** via `<runtime>/mx.json` (CLI major ⇄ runtime version); a mismatched runtime is gated until `mx migrate`. See `CLAUDE.md` for the source-of-truth and ownership rules.
Expand Down Expand Up @@ -43,7 +43,7 @@ mx/ # pnpm workspace
│ └── cli/ # @mx/cli (private) — CLI source
│ ├── src/ # tsup -> ../../npm/bin/mx.js
│ └── tsup.config.ts # bundles into npm/ and copies assets on success
└── npm/ # @roulabs/mx — publishable package
└── npm/ # @rousan/mx — publishable package
├── package.json · README.md # committed (public metadata + consumer docs)
└── bin/, templates/, LICENSE # built by `pnpm build` (gitignored)
```
Expand All @@ -57,7 +57,7 @@ mx/ # pnpm workspace
## Develop (including on a fresh machine)

```bash
git clone git@github.com:roulabs/mx.git && cd mx
git clone git@github.com:rousan/mx.git && cd mx
pnpm install
pnpm build # populates npm/ (bin/mx.js + templates/ + LICENSE)
export MX_RUNTIME="$PWD/.mx" # gitignored dev runtime in this repo
Expand All @@ -67,7 +67,7 @@ pnpm mx work new my-feature
pnpm mx info
```

`mx` runs the **built** output, so re-run `pnpm build` (or keep `pnpm dev` watching) after changing CLI/core code. Templates live at `/templates` (no code, just content) and are copied into `npm/templates/` at build — edits take effect on the next build. There is no global PATH coupling to this repo — the global `mx` comes only from installing a build (`npm i -g @roulabs/mx`).
`mx` runs the **built** output, so re-run `pnpm build` (or keep `pnpm dev` watching) after changing CLI/core code. Templates live at `/templates` (no code, just content) and are copied into `npm/templates/` at build — edits take effect on the next build. There is no global PATH coupling to this repo — the global `mx` comes only from installing a build (`npm i -g @rousan/mx`).

## Scripts

Expand Down Expand Up @@ -131,5 +131,5 @@ See **[docs/release.md](docs/release.md)** for the full runbook, the `NPM_TOKEN`
- [x] Env-based runtime discovery (`--runtime` / `$MX_RUNTIME` / default `~/mx`)
- [x] Per-service free-port allocation across all works (no fixed blocks)
- [x] TypeScript pnpm monorepo (`@mx/core` + `apps/cli`), lint/test/build tooling
- [x] npm distribution (`@roulabs/mx`) — CI-driven release on merge to `main`, CI for PR checks
- [x] npm distribution (`@rousan/mx`) — CI-driven release on merge to `main`, CI for PR checks
- [ ] Optional: isolated per-env state (separate DB schema / container) for safe parallel runs
2 changes: 1 addition & 1 deletion apps/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "0.0.0",
"private": true,
"type": "module",
"description": "Source for the @roulabs/mx npm package. Published artifacts live in /npm.",
"description": "Source for the @rousan/mx npm package. Published artifacts live in /npm.",
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
Expand Down
4 changes: 2 additions & 2 deletions apps/cli/src/selfupdate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { compareVersions, maxVersion } from '@mx/core';
import { cliVersion } from './paths';

/** The published package name self-update targets. */
const PKG = '@roulabs/mx';
const PKG = '@rousan/mx';

/**
* Major version number from a semver string (`"2.3.1"` → `2`).
Expand Down Expand Up @@ -92,7 +92,7 @@ export interface SelfUpdateInfo {

/**
* Self-update the CLI to the newest release **within its current major**
* (`npm i -g @roulabs/mx@^<major>`), and detect whether a newer major exists.
* (`npm i -g @rousan/mx@^<major>`), and detect whether a newer major exists.
* Crossing a major is intentionally left to the user (it requires `mx migrate`
* afterwards), so this only ever reports a newer major as a suggestion.
*
Expand Down
23 changes: 23 additions & 0 deletions apps/landing/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>mx — work on parallel features using coding agents</title>
<meta
name="description"
content="mx (multiplexer) combines a coding agent's parallel sessions — Claude Code, Cursor, and the rest — with git worktrees. Every feature gets its own isolated folder, branch, ports, and agent session, so a fleet of agents runs at once without collisions."
/>
<meta property="og:title" content="mx — work on parallel features using coding agents" />
<meta
property="og:description"
content="Combine a coding agent's parallel sessions with git worktrees. Isolated folder, branch, ports, and session per feature — run a fleet of agents without collisions."
/>
<meta property="og:type" content="website" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
25 changes: 25 additions & 0 deletions apps/landing/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "@mx/landing",
"version": "0.0.0",
"private": true,
"type": "module",
"description": "mx landing + docs site — the beginner-friendly public website published at https://mx.rousanali.com. A standalone React/Vite/Tailwind static site (built to apps/landing/dist and deployed to GitHub Pages). It teaches mx from zero; the deep reference lives in docs/.",
"scripts": {
"build": "vite build",
"dev": "vite",
"preview": "vite preview",
"typecheck": "tsc --noEmit -p tsconfig.json"
},
"dependencies": {
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@tailwindcss/vite": "^4.0.0",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@vitejs/plugin-react": "^4.3.0",
"tailwindcss": "^4.0.0",
"vite": "^6.0.0"
}
}
1 change: 1 addition & 0 deletions apps/landing/public/CNAME
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mx.rousanali.com
13 changes: 13 additions & 0 deletions apps/landing/public/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
41 changes: 41 additions & 0 deletions apps/landing/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { useTheme } from './theme';
import { Nav } from './sections/Nav';
import { Hero } from './sections/Hero';
import { Why } from './sections/Why';
import { Agents } from './sections/Agents';
import { Concepts } from './sections/Concepts';
import { Quickstart } from './sections/Quickstart';
import { Workflow } from './sections/Workflow';
import { Features } from './sections/Features';
import { Commands } from './sections/Commands';
import { Faq } from './sections/Faq';
import { Footer } from './sections/Footer';

/**
* The mx landing + docs site: a single scrolling page that teaches mx from zero
* — hook (Hero), problem (Why), the core agent pitch (Agents), vocabulary
* (Concepts), hands-on (Quickstart), capabilities (Features), reference
* (Commands), objections (Faq), then a closing CTA (Footer). Ordered so each
* section builds on the last. Workflow (after Quickstart) shows one concrete
* end-to-end setup for using it day to day.
*/
export function App() {
const { theme, toggle } = useTheme();
return (
<div className="min-h-screen bg-bg">
<Nav theme={theme} onToggleTheme={toggle} />
<main>
<Hero />
<Why />
<Agents />
<Concepts />
<Quickstart />
<Workflow />
<Features />
<Commands />
<Faq />
</main>
<Footer />
</div>
);
}
Loading
Loading