From 3752c95b43fc13fd24bc2483bad3840138083665 Mon Sep 17 00:00:00 2001 From: Artur Kowalski Date: Wed, 25 Mar 2026 12:58:47 +0100 Subject: [PATCH] Flatten monorepo to single-package structure --- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 2 +- .gitignore | 2 + .prettierignore | 2 +- README.md | 200 ++++++++++-------- esbuild.mjs | 22 ++ package-lock.json | 106 +++------- package.json | 51 +++-- packages/cli/esbuild.mjs | 39 ---- packages/cli/package.json | 36 ---- packages/cli/tsconfig.json | 8 - packages/core/package.json | 25 --- packages/core/tsconfig.json | 8 - packages/web/package.json | 29 --- packages/web/tsconfig.json | 8 - {packages/web/public => public}/index.html | 0 {packages/web/public => public}/logo.svg | 0 .../src => src/cli}/commands/claude.test.ts | 0 .../cli/src => src/cli}/commands/claude.ts | 2 +- .../cli/src => src/cli}/commands/create.ts | 2 +- .../cli/src => src/cli}/commands/info.ts | 2 +- .../cli/src => src/cli}/commands/list.ts | 2 +- .../cli/src => src/cli}/commands/relay.ts | 2 +- .../cli/src => src/cli}/commands/session.ts | 2 +- .../cli/src => src/cli}/commands/stats.ts | 2 +- {packages/cli/src => src/cli}/commands/web.ts | 4 +- {packages/cli/src => src/cli}/format.test.ts | 0 {packages/cli/src => src/cli}/format.ts | 2 +- .../core/src => src/core}/ProcessDetector.ts | 0 .../src => src/core}/SessionStore.test.ts | 0 .../core/src => src/core}/SessionStore.ts | 0 .../src => src/core}/TerminalParser.test.ts | 0 .../core/src => src/core}/TerminalParser.ts | 0 .../src => src/core}/TmuxControlClient.ts | 0 .../src => src/core}/TmuxSessionManager.ts | 0 {packages/core/src => src/core}/index.ts | 0 {packages/core/src => src/core}/types.ts | 0 {packages/cli/src => src}/index.ts | 21 +- .../web/src => src/web}/routes/sessions.ts | 2 +- .../web/src => src/web}/routes/settings.ts | 0 {packages/web/src => src/web}/server.ts | 2 +- .../web/src => src/web}/websocket/handler.ts | 4 +- tsconfig.base.json => tsconfig.json | 1 + vitest.config.ts | 6 +- 44 files changed, 224 insertions(+), 372 deletions(-) create mode 100644 esbuild.mjs delete mode 100644 packages/cli/esbuild.mjs delete mode 100644 packages/cli/package.json delete mode 100644 packages/cli/tsconfig.json delete mode 100644 packages/core/package.json delete mode 100644 packages/core/tsconfig.json delete mode 100644 packages/web/package.json delete mode 100644 packages/web/tsconfig.json rename {packages/web/public => public}/index.html (100%) rename {packages/web/public => public}/logo.svg (100%) rename {packages/cli/src => src/cli}/commands/claude.test.ts (100%) rename {packages/cli/src => src/cli}/commands/claude.ts (98%) rename {packages/cli/src => src/cli}/commands/create.ts (96%) rename {packages/cli/src => src/cli}/commands/info.ts (96%) rename {packages/cli/src => src/cli}/commands/list.ts (97%) rename {packages/cli/src => src/cli}/commands/relay.ts (96%) rename {packages/cli/src => src/cli}/commands/session.ts (99%) rename {packages/cli/src => src/cli}/commands/stats.ts (96%) rename {packages/cli/src => src/cli}/commands/web.ts (81%) rename {packages/cli/src => src/cli}/format.test.ts (100%) rename {packages/cli/src => src/cli}/format.ts (95%) rename {packages/core/src => src/core}/ProcessDetector.ts (100%) rename {packages/core/src => src/core}/SessionStore.test.ts (100%) rename {packages/core/src => src/core}/SessionStore.ts (100%) rename {packages/core/src => src/core}/TerminalParser.test.ts (100%) rename {packages/core/src => src/core}/TerminalParser.ts (100%) rename {packages/core/src => src/core}/TmuxControlClient.ts (100%) rename {packages/core/src => src/core}/TmuxSessionManager.ts (100%) rename {packages/core/src => src/core}/index.ts (100%) rename {packages/core/src => src/core}/types.ts (100%) rename {packages/cli/src => src}/index.ts (65%) rename {packages/web/src => src/web}/routes/sessions.ts (98%) rename {packages/web/src => src/web}/routes/settings.ts (100%) rename {packages/web/src => src/web}/server.ts (93%) rename {packages/web/src => src/web}/websocket/handler.ts (98%) rename tsconfig.base.json => tsconfig.json (93%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 83ab2d4..4400655 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,4 +48,4 @@ jobs: - uses: actions/upload-artifact@v4 with: name: dist - path: packages/*/dist/ + path: dist/ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c0e4247..cf54cd4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,7 +26,7 @@ jobs: - run: npm run build - name: Publish to npm - run: npm publish -w packages/cli --access public + run: npm publish --access public env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.gitignore b/.gitignore index 41a5648..2105107 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,5 @@ coverage/ # Temporary tmp/ .tmp/ + +CLAUDE.local.md diff --git a/.prettierignore b/.prettierignore index 1137375..9983913 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,4 +2,4 @@ node_modules/ dist/ coverage/ *.db -packages/web/public/ +public/ diff --git a/README.md b/README.md index b96138c..cee1772 100644 --- a/README.md +++ b/README.md @@ -1,147 +1,165 @@ -# Clux +

+ Clux +

-> **v0.2.0 — This project is under active development.** APIs, commands, and features may change without notice. +

Clux

-A tmux session multiplexer with a web dashboard and CLI. Clux lets you create, manage, and monitor multiple tmux sessions from the browser or command line. Run any command — LLM coding agents, dev servers, build scripts, or anything else. It supports inter-pane messaging, session tagging, and real-time output monitoring out of the box. +

+ A tmux session multiplexer for running parallel coding agents, with a web dashboard and CLI. +

-## Features +

+ npm version + license + node version +

-- Web dashboard with WebSocket live updates -- Real-time monitoring of pane output -- Create and manage tmux sessions running any command -- Send input and capture output from any pane without attaching -- Relay messages between panes -- Tag, search, and describe sessions with persistent metadata (SQLite) -- Export sessions to Markdown - -## Prerequisites - -- Node.js >= 18 -- tmux +Clux wraps tmux into a programmable session manager designed for running multiple Claude Code instances (or any command) in parallel. It pairs a full-featured CLI with a browser dashboard that streams live terminal output over WebSocket. Sessions are enriched with tags, descriptions, and statistics, all persisted in SQLite so nothing is lost between restarts. Built-in inter-pane messaging lets your agents coordinate without manual copy-paste. ## Installation ```bash -git clone && cd clux -npm install -npm run build +npm install -g @clux-cli/cli ``` -## Web Dashboard +Requires **Node.js 18+** and **tmux**. -The web package provides a browser UI backed by Express and WebSocket with live session state, pane output, and session management. +
+Install from source ```bash -npm run dev:web +git clone https://github.com/devhelp/clux.git && cd clux +npm install +npm run build ``` -The dashboard runs at `http://127.0.0.1:3456` by default. Configure via environment variables (see `.env.example`). +This compiles TypeScript and bundles the CLI. You can then run it directly with `node dist/index.js` or link it globally with `npm link`. -## CLI +
-After building, you can also manage sessions from the command line: - -```bash -node packages/cli/dist/index.js --help -``` +## Quick Start -Or create an alias: +Start a Claude session in your current project directory: ```bash -alias clux="node $(pwd)/packages/cli/dist/index.js" +clux claude ``` -### Quick Start +The session name is auto-generated from your directory path. If a session for this directory already exists, clux switches you into it instead of creating a duplicate. -Create a session with an LLM agent: +Open the web dashboard in another terminal to see all sessions at a glance: ```bash -clux create my-project --command claude --path ~/projects/my-app +clux web ``` -List running sessions: +The dashboard is available at `http://127.0.0.1:3456` and shows live terminal output, session metadata, and management controls. -```bash -clux ls -``` - -Send a prompt to a running session: +You can also manage sessions without attaching to them: ```bash -clux send my-project "refactor the auth module" +clux ls # list all sessions +clux send my-session "run the tests" # send a command to a pane +clux capture my-session --lines 100 # grab recent output ``` -Capture the current output: +## Claude Sessions + +The `clux claude` command is a shortcut purpose-built for Claude Code workflows. It creates a tmux session running `claude`, tags it with `claude` and `ai`, and immediately attaches. The session name is derived from the working directory — path segments are abbreviated to keep names readable (e.g. `/home/user/projects/clux` becomes `claude_h-u-projects-clux`). ```bash -clux capture my-project --lines 100 +clux claude # auto-name from current directory +clux claude my-agent # explicit name +clux claude --path ~/other/repo # different working directory +clux claude --detach # create without attaching +clux claude --danger # run with --dangerously-skip-permissions ``` -Attach to a session interactively: +The command is idempotent: if a session with the resolved name already exists, clux switches to it rather than failing. -```bash -clux attach my-project -``` +## CLI Commands -### Commands +Every command supports `--help` for detailed usage. | Command | Description | |---------|-------------| -| `create ` | Create a new session. Supports `--command`, `--path`, `--layout` | -| `list` / `ls` | List all sessions. Filter with `--tag` or `--search` | -| `info ` | Detailed session info | -| `attach ` | Attach to a session (Ctrl+B D to detach) | -| `kill ` | Kill a session | -| `send ` | Send text to a pane. Use `--pane` to target a specific pane | -| `capture ` | Capture pane output. `--all` for full scrollback, `-o` to write to file | -| `monitor ` | Watch pane output in real-time | -| `relay ` | Relay messages between panes (`--from`, `--to`, `--message` or `--capture`) | -| `add-window ` | Add a new window to a session | -| `rename-window ` | Rename a window | -| `export ` | Export session to Markdown | -| `tag ` | Add or remove (`-r`) tags | +| `create ` | Create a session. `--command`, `--path`, `--layout`, `--description`, `--tags` | +| `claude [name]` | Create or switch to a Claude session. `--path`, `--detach`, `--danger` | +| `list` / `ls` | List sessions. `--tag`, `--search` | +| `info ` | Detailed session info (windows, panes, metadata, stats) | +| `attach ` | Attach interactively. Ctrl+B D to detach | +| `kill ` | Terminate a session | +| `send ` | Send text to a pane. `--pane`, `--no-enter` | +| `capture ` | Capture pane output. `--lines`, `--all`, `--output` | +| `monitor ` | Watch pane output live. `--pane`, `--interval` | +| `relay ` | Relay between panes. `--from`, `--to`, `--message` or `--capture` | +| `export ` | Export session to Markdown. `--output` | +| `tag ` | Add tags. `-r` to remove | | `describe ` | Set session description | -| `stats [name]` | Session statistics (commands sent, duration) | +| `stats [name]` | Session statistics | +| `add-window ` | Add a window. `--command` | +| `rename-window ` | Rename a window | +| `web` | Start the web dashboard. `--port`, `--host` | -## Project Structure +The `relay` command enables inter-agent coordination. You can capture the output of one pane and pipe it as input to another — useful when one agent produces context that a second agent needs to act on. Use `--capture` to grab recent output from the source pane, or `--message` to send a literal string. -``` -clux/ - packages/ - core/ # Session management, tmux wrapper, metadata store - cli/ # Commander-based CLI (@clux-cli/cli) - web/ # Express + WebSocket dashboard (@clux-cli/web) +```bash +clux relay my-session --from 0.0 --to 0.1 --capture 50 +clux relay my-session --from 0.0 --to 0.1 --message "refactor complete, review the diff" ``` -This is an npm workspaces monorepo. The `core` package is a shared dependency used by both `cli` and `web`. +## Web Dashboard -## Development +Launch the dashboard with `clux web`. It runs at `http://127.0.0.1:3456` by default. For LAN access, bind to all interfaces with `--host 0.0.0.0`. -```bash -# Build everything -npm run build +The dashboard connects via WebSocket and renders live terminal output using xterm.js. The sidebar lists all active sessions with real-time status — sessions running Claude are detected automatically via process inspection and flagged with a distinct badge, no manual tagging required. + +From the browser you can create new sessions, switch between panes using tabs, send commands, relay messages between panes (with a preview of captured output before sending), export sessions to Markdown, and terminate sessions. The interface uses a dark theme built on Radix color tokens and is fully responsive, collapsing to a slide-in sidebar on mobile screens. + +Under the hood, the dashboard leverages tmux control mode for sub-16ms push latency when available, falling back to polling if control mode is not supported. + +## REST API -# Build individual packages -npm run build:core -npm run build:cli -npm run build:web +The web server exposes a REST API for programmatic access and automation. -# Run the web dashboard in dev mode -npm run dev:web +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/api/sessions` | List all sessions | +| GET | `/api/sessions/:name` | Get session details | +| POST | `/api/sessions` | Create session | +| DELETE | `/api/sessions/:name` | Kill session | +| POST | `/api/sessions/:name/send` | Send text to a pane | +| GET | `/api/sessions/:name/capture/:paneId` | Capture pane output (`?lines=N`) | +| GET | `/api/sessions/:name/export` | Export session as Markdown | +| POST | `/api/sessions/:name/relay` | Relay between panes | +| POST | `/api/sessions/:name/windows` | Add a window | +| DELETE | `/api/sessions/:name/panes/:paneId` | Kill a pane | +| PATCH | `/api/sessions/:name/windows/:index` | Rename a window | +| GET | `/api/settings` | Server configuration | -# Run tests -npm test -npm run test:watch -npm run test:coverage +Clients can also connect via WebSocket on the same host and port. Messages are JSON-encoded — use `subscribe` to stream live pane output, `send` to push text to a pane, and `input` to forward raw terminal keystrokes. -# Lint and format -npm run lint -npm run lint:fix -npm run format -npm run format:check +## Architecture -# Clean build artifacts -npm run clean +The codebase is a single TypeScript package organized into three logical modules under `src/`: `core/` (tmux session management, SQLite metadata store, terminal parser, process detection), `cli/` (Commander-based commands), and `web/` (Express + WebSocket dashboard). TypeScript compiles to `dist/`, then esbuild bundles everything into a single `dist/index.js` for distribution. Session metadata is stored in SQLite at `~/.clux/sessions.db` with WAL mode enabled. + +## Configuration + +The web dashboard binds to `127.0.0.1:3456` by default. Override with CLI flags (`clux web --port 8080 --host 0.0.0.0`) or environment variables (`PORT`, `HOST`). All sessions are created with a 50,000-line scrollback buffer. Supported pane layouts: `tiled`, `even-horizontal`, `even-vertical`, `main-horizontal`, `main-vertical`. + +## Development + +For contributors working on clux itself: + +```bash +git clone https://github.com/devhelp/clux.git && cd clux +npm install +npm run build # compile TypeScript + esbuild bundle +npm run dev:web # compile and run web dashboard locally +npm test # run tests (vitest) +npm run test:coverage # tests with coverage +npm run lint # eslint +npm run format:check # prettier check ``` ## License diff --git a/esbuild.mjs b/esbuild.mjs new file mode 100644 index 0000000..2106761 --- /dev/null +++ b/esbuild.mjs @@ -0,0 +1,22 @@ +import * as esbuild from 'esbuild'; +import path from 'path'; +import fs from 'fs'; +import { fileURLToPath } from 'url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +await esbuild.build({ + entryPoints: [path.join(__dirname, 'dist/index.js')], + bundle: true, + platform: 'node', + target: 'node18', + outfile: path.join(__dirname, 'dist/index.js'), + allowOverwrite: true, + external: ['better-sqlite3'], +}); + +fs.cpSync( + path.join(__dirname, 'public'), + path.join(__dirname, 'dist/public'), + { recursive: true }, +); diff --git a/package-lock.json b/package-lock.json index 92613f1..996dedf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,28 +1,37 @@ { - "name": "clux", - "version": "0.4.0", + "name": "@clux-cli/cli", + "version": "0.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "clux", - "version": "0.4.0", + "name": "@clux-cli/cli", + "version": "0.5.0", "license": "MIT", - "workspaces": [ - "packages/core", - "packages/cli", - "packages/web" - ], "dependencies": { - "@clux-cli/cli": "^0.3.0", - "better-sqlite3": "^12.6.2" + "better-sqlite3": "^12.6.2", + "chalk": "^4.1.2", + "cli-table3": "^0.6.5", + "commander": "^13.0.0", + "express": "^4.21.0", + "ora": "^5.4.1", + "ws": "^8.18.0" + }, + "bin": { + "clux": "dist/index.js" }, "devDependencies": { "@eslint/js": "^10.0.1", + "@types/better-sqlite3": "^7.6.13", + "@types/express": "^5.0.0", + "@types/node": "^22.0.0", + "@types/ws": "^8.5.0", "@vitest/coverage-v8": "^4.0.18", + "esbuild": "^0.27.4", "eslint": "^10.0.2", "eslint-config-prettier": "^10.1.8", "prettier": "^3.8.1", + "typescript": "^5.7.0", "typescript-eslint": "^8.56.1", "vitest": "^4.0.18" }, @@ -80,18 +89,6 @@ "node": ">=18" } }, - "node_modules/@clux-cli/cli": { - "resolved": "packages/cli", - "link": true - }, - "node_modules/@clux-cli/core": { - "resolved": "packages/core", - "link": true - }, - "node_modules/@clux-cli/web": { - "resolved": "packages/web", - "link": true - }, "node_modules/@colors/colors": { "version": "1.5.0", "license": "MIT", @@ -170,6 +167,8 @@ }, "node_modules/@esbuild/darwin-arm64": { "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz", + "integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==", "cpu": [ "arm64" ], @@ -372,8 +371,6 @@ }, "node_modules/@esbuild/linux-x64": { "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz", - "integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==", "cpu": [ "x64" ], @@ -767,6 +764,8 @@ }, "node_modules/@rollup/rollup-darwin-arm64": { "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", "cpu": [ "arm64" ], @@ -975,8 +974,6 @@ }, "node_modules/@rollup/rollup-linux-x64-gnu": { "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", - "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", "cpu": [ "x64" ], @@ -989,8 +986,6 @@ }, "node_modules/@rollup/rollup-linux-x64-musl": { "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", - "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", "cpu": [ "x64" ], @@ -2528,7 +2523,10 @@ }, "node_modules/fsevents": { "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, + "hasInstallScript": true, "license": "MIT", "optional": true, "os": [ @@ -4098,56 +4096,6 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } - }, - "packages/cli": { - "name": "@clux-cli/cli", - "version": "0.4.0", - "license": "MIT", - "dependencies": { - "better-sqlite3": "^12.6.2", - "chalk": "^4.1.2", - "cli-table3": "^0.6.5", - "commander": "^13.0.0", - "express": "^4.21.0", - "ora": "^5.4.1", - "ws": "^8.18.0" - }, - "bin": { - "clux": "dist/index.js" - }, - "devDependencies": { - "@types/node": "^22.0.0", - "esbuild": "^0.27.4", - "typescript": "^5.7.0" - } - }, - "packages/core": { - "name": "@clux-cli/core", - "version": "0.4.0", - "license": "MIT", - "dependencies": { - "better-sqlite3": "^12.6.2" - }, - "devDependencies": { - "@types/better-sqlite3": "^7.6.13", - "typescript": "^5.7.0" - } - }, - "packages/web": { - "name": "@clux-cli/web", - "version": "0.4.0", - "license": "MIT", - "dependencies": { - "@clux-cli/core": "*", - "express": "^4.21.0", - "ws": "^8.18.0" - }, - "devDependencies": { - "@types/express": "^5.0.0", - "@types/node": "^22.0.0", - "@types/ws": "^8.5.0", - "typescript": "^5.7.0" - } } } } diff --git a/package.json b/package.json index e5bbd06..26e29d1 100644 --- a/package.json +++ b/package.json @@ -1,26 +1,28 @@ { - "name": "clux", + "name": "@clux-cli/cli", "version": "0.5.0", - "private": true, "license": "MIT", "description": "Clux — tmux session multiplexer with web dashboard and CLI.", - "workspaces": [ - "packages/core", - "packages/cli", - "packages/web" + "main": "dist/index.js", + "bin": { + "clux": "dist/index.js" + }, + "files": [ + "dist" ], + "repository": { + "type": "git", + "url": "https://github.com/devhelp/clux.git" + }, "scripts": { - "build": "npm run build:core && npm run build:web && npm run build:cli", - "build:core": "npm run build -w packages/core", - "build:cli": "npm run build -w packages/cli", - "build:web": "npm run build -w packages/web", - "dev:web": "npm run dev -w packages/web", - "cli": "node packages/cli/dist/index.js", - "clean": "rm -rf packages/*/dist", - "lint": "eslint 'packages/*/src/**/*.ts'", - "lint:fix": "eslint 'packages/*/src/**/*.ts' --fix", - "format": "prettier --write 'packages/*/src/**/*.ts'", - "format:check": "prettier --check 'packages/*/src/**/*.ts'", + "build": "tsc && node esbuild.mjs", + "dev:web": "tsc && cp -r public dist/web/public && node dist/web/server.js", + "cli": "node dist/index.js", + "clean": "rm -rf dist", + "lint": "eslint 'src/**/*.ts'", + "lint:fix": "eslint 'src/**/*.ts' --fix", + "format": "prettier --write 'src/**/*.ts'", + "format:check": "prettier --check 'src/**/*.ts'", "test": "vitest run", "test:watch": "vitest", "test:coverage": "vitest run --coverage" @@ -29,15 +31,26 @@ "node": ">=18.0.0" }, "dependencies": { - "@clux-cli/cli": "^0.3.0", - "better-sqlite3": "^12.6.2" + "better-sqlite3": "^12.6.2", + "commander": "^13.0.0", + "chalk": "^4.1.2", + "cli-table3": "^0.6.5", + "ora": "^5.4.1", + "express": "^4.21.0", + "ws": "^8.18.0" }, "devDependencies": { "@eslint/js": "^10.0.1", + "@types/better-sqlite3": "^7.6.13", + "@types/express": "^5.0.0", + "@types/node": "^22.0.0", + "@types/ws": "^8.5.0", "@vitest/coverage-v8": "^4.0.18", + "esbuild": "^0.27.4", "eslint": "^10.0.2", "eslint-config-prettier": "^10.1.8", "prettier": "^3.8.1", + "typescript": "^5.7.0", "typescript-eslint": "^8.56.1", "vitest": "^4.0.18" } diff --git a/packages/cli/esbuild.mjs b/packages/cli/esbuild.mjs deleted file mode 100644 index 2f460d3..0000000 --- a/packages/cli/esbuild.mjs +++ /dev/null @@ -1,39 +0,0 @@ -import * as esbuild from 'esbuild'; -import path from 'path'; -import fs from 'fs'; -import { fileURLToPath } from 'url'; - -const __dirname = path.dirname(fileURLToPath(import.meta.url)); - -const workspaceResolve = { - name: 'workspace-resolve', - setup(build) { - build.onResolve({ filter: /^@clux-cli\// }, (args) => { - if (args.path === '@clux-cli/core') { - return { path: path.resolve(__dirname, '../core/dist/index.js') }; - } - if ( - args.path === '@clux-cli/web' || - args.path === '@clux-cli/web/dist/server' - ) { - return { path: path.resolve(__dirname, '../web/dist/server.js') }; - } - }); - }, -}; - -await esbuild.build({ - entryPoints: [path.join(__dirname, 'dist/index.js')], - bundle: true, - platform: 'node', - target: 'node18', - outfile: path.join(__dirname, 'dist/index.js'), - allowOverwrite: true, - external: ['better-sqlite3'], - plugins: [workspaceResolve], -}); - -// Copy web public assets into CLI dist so they're available at runtime -const srcPublic = path.resolve(__dirname, '../web/dist/public'); -const destPublic = path.resolve(__dirname, 'dist/public'); -fs.cpSync(srcPublic, destPublic, { recursive: true }); diff --git a/packages/cli/package.json b/packages/cli/package.json deleted file mode 100644 index 2526ce1..0000000 --- a/packages/cli/package.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "@clux-cli/cli", - "version": "0.5.0", - "license": "MIT", - "main": "dist/index.js", - "bin": { - "clux": "dist/index.js" - }, - "files": [ - "dist" - ], - "repository": { - "type": "git", - "url": "https://github.com/michalsikora/clux.git", - "directory": "packages/cli" - }, - "scripts": { - "build": "tsc && node esbuild.mjs", - "watch": "tsc --watch", - "start": "node dist/index.js" - }, - "dependencies": { - "better-sqlite3": "^12.6.2", - "commander": "^13.0.0", - "chalk": "^4.1.2", - "cli-table3": "^0.6.5", - "ora": "^5.4.1", - "express": "^4.21.0", - "ws": "^8.18.0" - }, - "devDependencies": { - "@types/node": "^22.0.0", - "esbuild": "^0.27.4", - "typescript": "^5.7.0" - } -} diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json deleted file mode 100644 index 8f24167..0000000 --- a/packages/cli/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "outDir": "./dist", - "rootDir": "./src" - }, - "include": ["src/**/*"] -} diff --git a/packages/core/package.json b/packages/core/package.json deleted file mode 100644 index 3dcec55..0000000 --- a/packages/core/package.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "@clux-cli/core", - "version": "0.5.0", - "private": true, - "license": "MIT", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "files": ["dist"], - "repository": { - "type": "git", - "url": "https://github.com/michalsikora/clux.git", - "directory": "packages/core" - }, - "scripts": { - "build": "tsc", - "watch": "tsc --watch" - }, - "devDependencies": { - "@types/better-sqlite3": "^7.6.13", - "typescript": "^5.7.0" - }, - "dependencies": { - "better-sqlite3": "^12.6.2" - } -} diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json deleted file mode 100644 index 8f24167..0000000 --- a/packages/core/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "outDir": "./dist", - "rootDir": "./src" - }, - "include": ["src/**/*"] -} diff --git a/packages/web/package.json b/packages/web/package.json deleted file mode 100644 index ad66289..0000000 --- a/packages/web/package.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "@clux-cli/web", - "version": "0.5.0", - "private": true, - "license": "MIT", - "main": "dist/server.js", - "files": ["dist"], - "repository": { - "type": "git", - "url": "https://github.com/michalsikora/clux.git", - "directory": "packages/web" - }, - "scripts": { - "build": "tsc && cp -r public dist/public", - "dev": "node dist/server.js", - "watch": "tsc --watch" - }, - "dependencies": { - "@clux-cli/core": "*", - "express": "^4.21.0", - "ws": "^8.18.0" - }, - "devDependencies": { - "typescript": "^5.7.0", - "@types/node": "^22.0.0", - "@types/express": "^5.0.0", - "@types/ws": "^8.5.0" - } -} diff --git a/packages/web/tsconfig.json b/packages/web/tsconfig.json deleted file mode 100644 index 8f24167..0000000 --- a/packages/web/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "outDir": "./dist", - "rootDir": "./src" - }, - "include": ["src/**/*"] -} diff --git a/packages/web/public/index.html b/public/index.html similarity index 100% rename from packages/web/public/index.html rename to public/index.html diff --git a/packages/web/public/logo.svg b/public/logo.svg similarity index 100% rename from packages/web/public/logo.svg rename to public/logo.svg diff --git a/packages/cli/src/commands/claude.test.ts b/src/cli/commands/claude.test.ts similarity index 100% rename from packages/cli/src/commands/claude.test.ts rename to src/cli/commands/claude.test.ts diff --git a/packages/cli/src/commands/claude.ts b/src/cli/commands/claude.ts similarity index 98% rename from packages/cli/src/commands/claude.ts rename to src/cli/commands/claude.ts index 7697700..ddfe51e 100644 --- a/packages/cli/src/commands/claude.ts +++ b/src/cli/commands/claude.ts @@ -1,5 +1,5 @@ import { Command } from 'commander'; -import { TmuxSessionManager } from '@clux-cli/core'; +import { TmuxSessionManager } from '../../core'; import chalk from 'chalk'; import ora from 'ora'; import { spawn, spawnSync } from 'child_process'; diff --git a/packages/cli/src/commands/create.ts b/src/cli/commands/create.ts similarity index 96% rename from packages/cli/src/commands/create.ts rename to src/cli/commands/create.ts index b2e29bd..5e97043 100644 --- a/packages/cli/src/commands/create.ts +++ b/src/cli/commands/create.ts @@ -1,5 +1,5 @@ import { Command } from 'commander'; -import { TmuxSessionManager } from '@clux-cli/core'; +import { TmuxSessionManager } from '../../core'; import chalk from 'chalk'; import ora from 'ora'; import { printSessionDetails } from '../format'; diff --git a/packages/cli/src/commands/info.ts b/src/cli/commands/info.ts similarity index 96% rename from packages/cli/src/commands/info.ts rename to src/cli/commands/info.ts index b2b55e7..761b858 100644 --- a/packages/cli/src/commands/info.ts +++ b/src/cli/commands/info.ts @@ -1,5 +1,5 @@ import { Command } from 'commander'; -import { TmuxSessionManager } from '@clux-cli/core'; +import { TmuxSessionManager } from '../../core'; import chalk from 'chalk'; import { printSessionDetails } from '../format'; diff --git a/packages/cli/src/commands/list.ts b/src/cli/commands/list.ts similarity index 97% rename from packages/cli/src/commands/list.ts rename to src/cli/commands/list.ts index 27b1875..58557c7 100644 --- a/packages/cli/src/commands/list.ts +++ b/src/cli/commands/list.ts @@ -1,5 +1,5 @@ import { Command } from 'commander'; -import { TmuxSessionManager } from '@clux-cli/core'; +import { TmuxSessionManager } from '../../core'; import chalk from 'chalk'; import Table from 'cli-table3'; import { formatRelativeTime } from '../format'; diff --git a/packages/cli/src/commands/relay.ts b/src/cli/commands/relay.ts similarity index 96% rename from packages/cli/src/commands/relay.ts rename to src/cli/commands/relay.ts index 3ce62d0..7cccc22 100644 --- a/packages/cli/src/commands/relay.ts +++ b/src/cli/commands/relay.ts @@ -1,5 +1,5 @@ import { Command } from 'commander'; -import { TmuxSessionManager } from '@clux-cli/core'; +import { TmuxSessionManager } from '../../core'; import chalk from 'chalk'; export function registerRelayCommand(program: Command, manager: TmuxSessionManager): void { diff --git a/packages/cli/src/commands/session.ts b/src/cli/commands/session.ts similarity index 99% rename from packages/cli/src/commands/session.ts rename to src/cli/commands/session.ts index d3f00c6..e6d93c0 100644 --- a/packages/cli/src/commands/session.ts +++ b/src/cli/commands/session.ts @@ -1,5 +1,5 @@ import { Command } from 'commander'; -import { TmuxSessionManager } from '@clux-cli/core'; +import { TmuxSessionManager } from '../../core'; import chalk from 'chalk'; import { printSessionDetails } from '../format'; diff --git a/packages/cli/src/commands/stats.ts b/src/cli/commands/stats.ts similarity index 96% rename from packages/cli/src/commands/stats.ts rename to src/cli/commands/stats.ts index 9ecb2a3..e2288ca 100644 --- a/packages/cli/src/commands/stats.ts +++ b/src/cli/commands/stats.ts @@ -1,5 +1,5 @@ import { Command } from 'commander'; -import { TmuxSessionManager } from '@clux-cli/core'; +import { TmuxSessionManager } from '../../core'; import chalk from 'chalk'; import Table from 'cli-table3'; import { formatRelativeTime } from '../format'; diff --git a/packages/cli/src/commands/web.ts b/src/cli/commands/web.ts similarity index 81% rename from packages/cli/src/commands/web.ts rename to src/cli/commands/web.ts index a3b2909..791fd86 100644 --- a/packages/cli/src/commands/web.ts +++ b/src/cli/commands/web.ts @@ -1,5 +1,5 @@ import { Command } from 'commander'; -import { TmuxSessionManager } from '@clux-cli/core'; +import { TmuxSessionManager } from '../../core'; export function registerWebCommand(program: Command, _manager: TmuxSessionManager) { program @@ -10,6 +10,6 @@ export function registerWebCommand(program: Command, _manager: TmuxSessionManage .action((opts) => { process.env.PORT = opts.port; process.env.HOST = opts.host; - require('@clux-cli/web/dist/server'); + require('../../web/server'); }); } diff --git a/packages/cli/src/format.test.ts b/src/cli/format.test.ts similarity index 100% rename from packages/cli/src/format.test.ts rename to src/cli/format.test.ts diff --git a/packages/cli/src/format.ts b/src/cli/format.ts similarity index 95% rename from packages/cli/src/format.ts rename to src/cli/format.ts index 24836d7..5736a22 100644 --- a/packages/cli/src/format.ts +++ b/src/cli/format.ts @@ -1,5 +1,5 @@ import chalk from 'chalk'; -import type { TmuxSession } from '@clux-cli/core'; +import type { TmuxSession } from '../core'; export function printSessionDetails(session: TmuxSession): void { console.log(`\n ${chalk.bold(session.name)}`); diff --git a/packages/core/src/ProcessDetector.ts b/src/core/ProcessDetector.ts similarity index 100% rename from packages/core/src/ProcessDetector.ts rename to src/core/ProcessDetector.ts diff --git a/packages/core/src/SessionStore.test.ts b/src/core/SessionStore.test.ts similarity index 100% rename from packages/core/src/SessionStore.test.ts rename to src/core/SessionStore.test.ts diff --git a/packages/core/src/SessionStore.ts b/src/core/SessionStore.ts similarity index 100% rename from packages/core/src/SessionStore.ts rename to src/core/SessionStore.ts diff --git a/packages/core/src/TerminalParser.test.ts b/src/core/TerminalParser.test.ts similarity index 100% rename from packages/core/src/TerminalParser.test.ts rename to src/core/TerminalParser.test.ts diff --git a/packages/core/src/TerminalParser.ts b/src/core/TerminalParser.ts similarity index 100% rename from packages/core/src/TerminalParser.ts rename to src/core/TerminalParser.ts diff --git a/packages/core/src/TmuxControlClient.ts b/src/core/TmuxControlClient.ts similarity index 100% rename from packages/core/src/TmuxControlClient.ts rename to src/core/TmuxControlClient.ts diff --git a/packages/core/src/TmuxSessionManager.ts b/src/core/TmuxSessionManager.ts similarity index 100% rename from packages/core/src/TmuxSessionManager.ts rename to src/core/TmuxSessionManager.ts diff --git a/packages/core/src/index.ts b/src/core/index.ts similarity index 100% rename from packages/core/src/index.ts rename to src/core/index.ts diff --git a/packages/core/src/types.ts b/src/core/types.ts similarity index 100% rename from packages/core/src/types.ts rename to src/core/types.ts diff --git a/packages/cli/src/index.ts b/src/index.ts similarity index 65% rename from packages/cli/src/index.ts rename to src/index.ts index 3a88af3..e82b9d8 100644 --- a/packages/cli/src/index.ts +++ b/src/index.ts @@ -1,10 +1,11 @@ #!/usr/bin/env node import { Command } from 'commander'; -import { TmuxSessionManager } from '@clux-cli/core'; -import { registerCreateCommand } from './commands/create'; -import { registerListCommand } from './commands/list'; -import { registerInfoCommand } from './commands/info'; +import { TmuxSessionManager } from './core'; +import pkg from '../package.json'; +import { registerCreateCommand } from './cli/commands/create'; +import { registerListCommand } from './cli/commands/list'; +import { registerInfoCommand } from './cli/commands/info'; import { registerTagCommand, registerDescribeCommand, @@ -15,16 +16,16 @@ import { registerExportCommand, registerMonitorCommand, registerWindowCommands, -} from './commands/session'; -import { registerRelayCommand } from './commands/relay'; -import { registerStatsCommand } from './commands/stats'; -import { registerClaudeCommand } from './commands/claude'; -import { registerWebCommand } from './commands/web'; +} from './cli/commands/session'; +import { registerRelayCommand } from './cli/commands/relay'; +import { registerStatsCommand } from './cli/commands/stats'; +import { registerClaudeCommand } from './cli/commands/claude'; +import { registerWebCommand } from './cli/commands/web'; const manager = new TmuxSessionManager(); const program = new Command(); -program.name('clux').description('Clux — tmux session multiplexer').version('0.4.0'); +program.name('clux').description('Clux — tmux session multiplexer').version(pkg.version); registerCreateCommand(program, manager); registerListCommand(program, manager); diff --git a/packages/web/src/routes/sessions.ts b/src/web/routes/sessions.ts similarity index 98% rename from packages/web/src/routes/sessions.ts rename to src/web/routes/sessions.ts index bf8a61f..d1ff124 100644 --- a/packages/web/src/routes/sessions.ts +++ b/src/web/routes/sessions.ts @@ -1,5 +1,5 @@ import { Router } from 'express'; -import { TmuxSessionManager } from '@clux-cli/core'; +import { TmuxSessionManager } from '../../core'; export function createSessionRoutes(manager: TmuxSessionManager): Router { const router = Router(); diff --git a/packages/web/src/routes/settings.ts b/src/web/routes/settings.ts similarity index 100% rename from packages/web/src/routes/settings.ts rename to src/web/routes/settings.ts diff --git a/packages/web/src/server.ts b/src/web/server.ts similarity index 93% rename from packages/web/src/server.ts rename to src/web/server.ts index b252850..f933f18 100644 --- a/packages/web/src/server.ts +++ b/src/web/server.ts @@ -1,7 +1,7 @@ import express from 'express'; import http from 'http'; import path from 'path'; -import { TmuxSessionManager } from '@clux-cli/core'; +import { TmuxSessionManager } from '../core'; import { createSessionRoutes } from './routes/sessions'; import { createSettingsRoutes } from './routes/settings'; import { setupWebSocket } from './websocket/handler'; diff --git a/packages/web/src/websocket/handler.ts b/src/web/websocket/handler.ts similarity index 98% rename from packages/web/src/websocket/handler.ts rename to src/web/websocket/handler.ts index 910f741..a74f75b 100644 --- a/packages/web/src/websocket/handler.ts +++ b/src/web/websocket/handler.ts @@ -1,7 +1,7 @@ import { WebSocketServer, WebSocket } from 'ws'; import type { Server } from 'http'; -import { TmuxSessionManager } from '@clux-cli/core'; -import type { PaneOutputEvent, TmuxControlClient } from '@clux-cli/core'; +import { TmuxSessionManager } from '../../core'; +import type { PaneOutputEvent, TmuxControlClient } from '../../core'; const subscribers = new Map>(); const sessionMode = new Map(); diff --git a/tsconfig.base.json b/tsconfig.json similarity index 93% rename from tsconfig.base.json rename to tsconfig.json index 2ad8e2e..b51d085 100644 --- a/tsconfig.base.json +++ b/tsconfig.json @@ -13,5 +13,6 @@ "rootDir": "./src", "sourceMap": true }, + "include": ["src/**/*"], "exclude": ["node_modules", "dist"] } diff --git a/vitest.config.ts b/vitest.config.ts index 5c7aa29..51d7a09 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -4,11 +4,11 @@ export default defineConfig({ test: { globals: true, environment: 'node', - include: ['packages/*/src/**/*.test.ts'], + include: ['src/**/*.test.ts'], coverage: { provider: 'v8', - include: ['packages/*/src/**/*.ts'], - exclude: ['packages/*/src/**/*.test.ts', 'packages/web/src/server.ts'], + include: ['src/**/*.ts'], + exclude: ['src/**/*.test.ts', 'src/web/server.ts'], }, }, });