@tomagranate/toolui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 toolui contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,225 @@
1
+ # ToolUI
2
+
3
+ A Terminal User Interface (TUI) for managing multiple local development processes. View real-time logs, monitor status, and control all your dev servers from a single dashboard.
4
+
5
+ Built with [OpenTUI](https://github.com/anomalyco/opentui).
6
+
7
+ ## Why ToolUI?
8
+
9
+ When working on a full-stack project, you often need to run multiple processes simultaneously—a frontend dev server, a backend API, database containers, workers, etc. ToolUI gives you:
10
+
11
+ - **Single dashboard** for all your processes with tabbed log viewing
12
+ - **Real-time logs** with search and ANSI color support
13
+ - **Status monitoring** to see at a glance what's running, stopped, or crashed
14
+ - **Health checks** to monitor service availability
15
+ - **AI integration** via MCP to let your IDE assistant read logs and control processes
16
+
17
+ ## Installation
18
+
19
+ ### Homebrew (macOS and Linux)
20
+
21
+ ```bash
22
+ brew install tomagranate/toolui/toolui
23
+ ```
24
+
25
+ ### NPM
26
+
27
+ ```bash
28
+ npm install -g @tomagranate/toolui
29
+ ```
30
+
31
+ ### curl (macOS and Linux)
32
+
33
+ ```bash
34
+ curl -fsSL https://raw.githubusercontent.com/tomagranate/toolui/main/install.sh | bash
35
+ ```
36
+
37
+ ### Manual Download
38
+
39
+ Download the latest binary for your platform from [Releases](https://github.com/tomagranate/toolui/releases).
40
+
41
+ | Platform | Download |
42
+ |----------|----------|
43
+ | macOS (Apple Silicon) | `toolui-darwin-arm64.tar.gz` |
44
+ | macOS (Intel) | `toolui-darwin-x64.tar.gz` |
45
+ | Linux (x64) | `toolui-linux-x64.tar.gz` |
46
+ | Linux (ARM64) | `toolui-linux-arm64.tar.gz` |
47
+ | Windows (x64) | `toolui-windows-x64.zip` |
48
+
49
+ ## Quick Start
50
+
51
+ 1. Create a config file in your project:
52
+
53
+ ```bash
54
+ toolui init
55
+ ```
56
+
57
+ 2. Edit `toolui.config.toml` to add your processes:
58
+
59
+ ```toml
60
+ [[tools]]
61
+ name = "frontend"
62
+ command = "npm"
63
+ args = ["run", "dev"]
64
+ cwd = "./frontend"
65
+
66
+ [[tools]]
67
+ name = "backend"
68
+ command = "python"
69
+ args = ["-m", "uvicorn", "main:app", "--reload"]
70
+ cwd = "./backend"
71
+ ```
72
+
73
+ 3. Start the dashboard:
74
+
75
+ ```bash
76
+ toolui
77
+ ```
78
+
79
+ ## CLI Reference
80
+
81
+ ### Commands
82
+
83
+ | Command | Description |
84
+ |---------|-------------|
85
+ | `toolui` | Start the TUI dashboard |
86
+ | `toolui init` | Create a sample config file in the current directory |
87
+ | `toolui mcp` | Start the MCP server for AI agent integration |
88
+
89
+ ### Options
90
+
91
+ | Option | Description |
92
+ |--------|-------------|
93
+ | `-c, --config <path>` | Path to config file (default: `toolui.config.toml`) |
94
+ | `-h, --help` | Show help message |
95
+
96
+ ### Examples
97
+
98
+ ```bash
99
+ # Start with default config
100
+ toolui
101
+
102
+ # Use a custom config file
103
+ toolui --config ./configs/dev.toml
104
+ toolui -c ./configs/dev.toml
105
+
106
+ # Create a new config file
107
+ toolui init
108
+
109
+ # Start MCP server for AI integration
110
+ toolui mcp
111
+ ```
112
+
113
+ ## Configuration
114
+
115
+ ToolUI is configured via a TOML file. By default, it looks for `toolui.config.toml` in the current directory.
116
+
117
+ ### Minimal Example
118
+
119
+ ```toml
120
+ [[tools]]
121
+ name = "server"
122
+ command = "npm"
123
+ args = ["run", "dev"]
124
+ ```
125
+
126
+ ### Full Example
127
+
128
+ ```toml
129
+ [home]
130
+ enabled = true
131
+ title = "My Project"
132
+
133
+ [ui]
134
+ theme = "mist"
135
+ showTabNumbers = true
136
+
137
+ [mcp]
138
+ enabled = true
139
+
140
+ [[tools]]
141
+ name = "web"
142
+ command = "npm"
143
+ args = ["run", "dev"]
144
+ cwd = "./web"
145
+ description = "Next.js frontend"
146
+
147
+ [tools.ui]
148
+ label = "Open App"
149
+ url = "http://localhost:3000"
150
+
151
+ [tools.healthCheck]
152
+ url = "http://localhost:3000/api/health"
153
+ interval = 5000
154
+
155
+ [[tools]]
156
+ name = "api"
157
+ command = "cargo"
158
+ args = ["watch", "-x", "run"]
159
+ cwd = "./api"
160
+ description = "Rust API server"
161
+ cleanup = ["pkill -f 'target/debug/api'"]
162
+
163
+ [tools.env]
164
+ RUST_LOG = "debug"
165
+ ```
166
+
167
+ For a complete reference of all configuration options, see the [sample config file](src/sample-config.toml).
168
+
169
+
170
+
171
+ ## Themes
172
+
173
+ ToolUI includes several built-in themes. Set in your config:
174
+
175
+ ```toml
176
+ [ui]
177
+ theme = "mist"
178
+ ```
179
+
180
+ Available themes: `default` (Moss), `mist`, `cappuccino`, `synthwave`, `terminal` (auto-detect from your terminal).
181
+
182
+ ## MCP Integration
183
+
184
+ ToolUI can expose an HTTP API for AI agents (Cursor, Claude, etc.) via the Model Context Protocol.
185
+
186
+ ### Enable in Config
187
+
188
+ ```toml
189
+ [mcp]
190
+ enabled = true
191
+ port = 18765
192
+ ```
193
+
194
+ ### Configure Your IDE
195
+
196
+ Add to your MCP configuration (e.g., `~/.cursor/mcp.json`):
197
+
198
+ ```json
199
+ {
200
+ "mcpServers": {
201
+ "toolui": {
202
+ "command": "toolui",
203
+ "args": ["mcp"]
204
+ }
205
+ }
206
+ }
207
+ ```
208
+
209
+ ### Available MCP Tools
210
+
211
+ | Tool | Description |
212
+ |------|-------------|
213
+ | `list_processes` | List all processes with their status |
214
+ | `get_logs` | Get recent logs (supports search and line limits) |
215
+ | `stop_process` | Stop a running process |
216
+ | `restart_process` | Restart a process |
217
+ | `clear_logs` | Clear logs for a process |
218
+
219
+ ## Contributing
220
+
221
+ See the [Contributing Guide](CONTRIBUTING.md) for development setup and guidelines.
222
+
223
+ ## License
224
+
225
+ MIT
package/bin/toolui ADDED
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { spawn } = require("child_process");
4
+ const { existsSync } = require("fs");
5
+ const { join, dirname } = require("path");
6
+
7
+ const binDir = dirname(__filename);
8
+ const platform = process.platform;
9
+ const arch = process.arch;
10
+
11
+ // Map Node.js platform/arch to our binary names
12
+ const platformMap = {
13
+ darwin: "darwin",
14
+ linux: "linux",
15
+ win32: "windows",
16
+ };
17
+
18
+ const archMap = {
19
+ arm64: "arm64",
20
+ x64: "x64",
21
+ };
22
+
23
+ const os = platformMap[platform];
24
+ const cpu = archMap[arch];
25
+
26
+ if (!os || !cpu) {
27
+ console.error(`Unsupported platform: ${platform}-${arch}`);
28
+ process.exit(1);
29
+ }
30
+
31
+ const binaryName = `toolui-${os}-${cpu}${platform === "win32" ? ".exe" : ""}`;
32
+ const binaryPath = join(binDir, binaryName);
33
+
34
+ if (!existsSync(binaryPath)) {
35
+ console.error(`Binary not found: ${binaryPath}`);
36
+ console.error(
37
+ "This may happen if the postinstall script failed to download the binary."
38
+ );
39
+ console.error("Try reinstalling: npm install -g toolui");
40
+ process.exit(1);
41
+ }
42
+
43
+ // Execute the binary with all arguments passed through
44
+ const child = spawn(binaryPath, process.argv.slice(2), {
45
+ stdio: "inherit",
46
+ windowsHide: true,
47
+ });
48
+
49
+ child.on("error", (err) => {
50
+ console.error(`Failed to start toolui: ${err.message}`);
51
+ process.exit(1);
52
+ });
53
+
54
+ child.on("exit", (code, signal) => {
55
+ if (signal) {
56
+ process.kill(process.pid, signal);
57
+ } else {
58
+ process.exit(code ?? 0);
59
+ }
60
+ });
Binary file
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@tomagranate/toolui",
3
+ "version": "0.1.0",
4
+ "description": "A Terminal User Interface (TUI) for running multiple local development servers and tools simultaneously",
5
+ "type": "module",
6
+ "bin": {
7
+ "toolui": "bin/toolui"
8
+ },
9
+ "files": [
10
+ "bin",
11
+ "scripts"
12
+ ],
13
+ "scripts": {
14
+ "dev": "bun run src/index.tsx",
15
+ "build": "bun build src/index.tsx --compile --minify --outfile dist/toolui",
16
+ "build:all": "bun run scripts/build-all.ts",
17
+ "postinstall": "node scripts/postinstall.cjs",
18
+ "check": "biome check --fix",
19
+ "check:nofix": "biome check",
20
+ "lint": "bun check",
21
+ "format": "bun check",
22
+ "typecheck": "tsc --noEmit",
23
+ "test": "bun test",
24
+ "test:watch": "bun test --watch",
25
+ "prepublishOnly": "bun run typecheck && bun run check:nofix"
26
+ },
27
+ "keywords": [
28
+ "cli",
29
+ "tui",
30
+ "terminal",
31
+ "devtools",
32
+ "process-manager",
33
+ "dev-server",
34
+ "multiplexer"
35
+ ],
36
+ "author": "",
37
+ "license": "MIT",
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "git+https://github.com/tomagranate/toolui.git"
41
+ },
42
+ "homepage": "https://github.com/tomagranate/toolui#readme",
43
+ "bugs": {
44
+ "url": "https://github.com/tomagranate/toolui/issues"
45
+ },
46
+ "engines": {
47
+ "node": ">=18.0.0"
48
+ },
49
+ "devDependencies": {
50
+ "@biomejs/biome": "^2.3.11",
51
+ "@types/bun": "latest"
52
+ },
53
+ "peerDependencies": {
54
+ "typescript": "^5.9.3"
55
+ },
56
+ "dependencies": {
57
+ "@iarna/toml": "^2.2.5",
58
+ "@modelcontextprotocol/sdk": "^1.25.3",
59
+ "@opentui/core": "^0.1.72",
60
+ "@opentui/react": "^0.1.72",
61
+ "fuzzysort": "^3.1.0",
62
+ "react": "^19.2.3",
63
+ "zod": "^4.3.6"
64
+ }
65
+ }
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env bun
2
+
3
+ /**
4
+ * Build toolui binaries for all supported platforms.
5
+ *
6
+ * Usage:
7
+ * bun run scripts/build-all.ts
8
+ * bun run scripts/build-all.ts --version 0.1.0
9
+ */
10
+
11
+ import { mkdir, readFile } from "node:fs/promises";
12
+ import { join } from "node:path";
13
+ import { $ } from "bun";
14
+
15
+ interface BuildTarget {
16
+ target: string;
17
+ os: string;
18
+ arch: string;
19
+ extension: string;
20
+ }
21
+
22
+ const TARGETS: BuildTarget[] = [
23
+ { target: "bun-darwin-arm64", os: "darwin", arch: "arm64", extension: "" },
24
+ { target: "bun-darwin-x64", os: "darwin", arch: "x64", extension: "" },
25
+ { target: "bun-linux-x64", os: "linux", arch: "x64", extension: "" },
26
+ { target: "bun-linux-arm64", os: "linux", arch: "arm64", extension: "" },
27
+ {
28
+ target: "bun-windows-x64",
29
+ os: "windows",
30
+ arch: "x64",
31
+ extension: ".exe",
32
+ },
33
+ ];
34
+
35
+ async function getVersion(): Promise<string> {
36
+ // Check for --version flag
37
+ const versionIndex = process.argv.indexOf("--version");
38
+ const versionArg = process.argv[versionIndex + 1];
39
+ if (versionIndex !== -1 && versionArg) {
40
+ return versionArg;
41
+ }
42
+
43
+ // Read from package.json
44
+ const packageJson = JSON.parse(
45
+ await readFile(join(import.meta.dir, "..", "package.json"), "utf-8"),
46
+ ) as { version: string };
47
+ return packageJson.version;
48
+ }
49
+
50
+ async function buildTarget(target: BuildTarget, outDir: string): Promise<void> {
51
+ const outputName = `toolui-${target.os}-${target.arch}${target.extension}`;
52
+ const outputPath = join(outDir, outputName);
53
+
54
+ console.log(`Building ${outputName}...`);
55
+
56
+ await $`bun build src/index.tsx --compile --minify --target=${target.target} --outfile=${outputPath}`;
57
+
58
+ console.log(` ✓ ${outputName}`);
59
+ }
60
+
61
+ async function main() {
62
+ const version = await getVersion();
63
+ const outDir = join(import.meta.dir, "..", "dist");
64
+
65
+ console.log(`\nBuilding toolui v${version} for all platforms\n`);
66
+
67
+ // Create output directory
68
+ await mkdir(outDir, { recursive: true });
69
+
70
+ // Build all targets
71
+ for (const target of TARGETS) {
72
+ await buildTarget(target, outDir);
73
+ }
74
+
75
+ console.log(`\n✓ All binaries built in ${outDir}\n`);
76
+ }
77
+
78
+ main().catch((error) => {
79
+ console.error("Build failed:", error);
80
+ process.exit(1);
81
+ });
@@ -0,0 +1,219 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Postinstall script for toolui NPM package.
5
+ * Downloads the appropriate binary for the current platform.
6
+ */
7
+
8
+ const https = require("node:https");
9
+ const fs = require("node:fs");
10
+ const path = require("node:path");
11
+ const { execSync } = require("node:child_process");
12
+ const zlib = require("node:zlib");
13
+
14
+ // Configuration
15
+ const REPO = "tomagranate/toolui";
16
+ const GITHUB_RELEASES = `https://github.com/${REPO}/releases`;
17
+
18
+ // Platform mappings
19
+ const PLATFORM_MAP = {
20
+ darwin: "darwin",
21
+ linux: "linux",
22
+ win32: "windows",
23
+ };
24
+
25
+ const ARCH_MAP = {
26
+ arm64: "arm64",
27
+ x64: "x64",
28
+ };
29
+
30
+ /**
31
+ * Make an HTTPS GET request
32
+ */
33
+ function httpsGet(url, options = {}) {
34
+ return new Promise((resolve, reject) => {
35
+ const opts = {
36
+ ...options,
37
+ headers: {
38
+ "User-Agent": "toolui-postinstall",
39
+ ...options.headers,
40
+ },
41
+ };
42
+
43
+ https
44
+ .get(url, opts, (res) => {
45
+ // Handle redirects
46
+ if (
47
+ res.statusCode >= 300 &&
48
+ res.statusCode < 400 &&
49
+ res.headers.location
50
+ ) {
51
+ return httpsGet(res.headers.location, options)
52
+ .then(resolve)
53
+ .catch(reject);
54
+ }
55
+
56
+ if (res.statusCode !== 200) {
57
+ reject(new Error(`HTTP ${res.statusCode}: ${url}`));
58
+ return;
59
+ }
60
+
61
+ const chunks = [];
62
+ res.on("data", (chunk) => chunks.push(chunk));
63
+ res.on("end", () => resolve(Buffer.concat(chunks)));
64
+ res.on("error", reject);
65
+ })
66
+ .on("error", reject);
67
+ });
68
+ }
69
+
70
+ /**
71
+ * Get the version from package.json
72
+ */
73
+ function getVersion() {
74
+ const packageJson = JSON.parse(
75
+ fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf-8"),
76
+ );
77
+ return packageJson.version;
78
+ }
79
+
80
+ /**
81
+ * Download and extract the binary
82
+ */
83
+ async function downloadBinary(url, destPath) {
84
+ console.log(`Downloading from ${url}...`);
85
+ const data = await httpsGet(url);
86
+
87
+ // Extract tar.gz
88
+ return new Promise((resolve, reject) => {
89
+ const gunzip = zlib.createGunzip();
90
+ const chunks = [];
91
+
92
+ gunzip.on("data", (chunk) => chunks.push(chunk));
93
+ gunzip.on("end", () => {
94
+ const tarData = Buffer.concat(chunks);
95
+ // Simple tar extraction - find the file content
96
+ // tar format: 512-byte header blocks followed by file content
97
+ let offset = 0;
98
+ while (offset < tarData.length) {
99
+ const header = tarData.slice(offset, offset + 512);
100
+ if (header[0] === 0) break; // End of archive
101
+
102
+ // Get filename (bytes 0-99)
103
+ const filename = header
104
+ .slice(0, 100)
105
+ .toString("utf-8")
106
+ .replace(/\0/g, "");
107
+
108
+ // Get file size (bytes 124-135, octal)
109
+ const sizeStr = header
110
+ .slice(124, 136)
111
+ .toString("utf-8")
112
+ .replace(/\0/g, "")
113
+ .trim();
114
+ const size = parseInt(sizeStr, 8) || 0;
115
+
116
+ offset += 512; // Move past header
117
+
118
+ if (filename && size > 0 && filename.startsWith("toolui")) {
119
+ const content = tarData.slice(offset, offset + size);
120
+ fs.writeFileSync(destPath, content);
121
+ fs.chmodSync(destPath, 0o755);
122
+ resolve();
123
+ return;
124
+ }
125
+
126
+ // Move to next file (content is padded to 512-byte blocks)
127
+ offset += Math.ceil(size / 512) * 512;
128
+ }
129
+ reject(new Error("Could not find toolui binary in archive"));
130
+ });
131
+ gunzip.on("error", reject);
132
+
133
+ gunzip.end(data);
134
+ });
135
+ }
136
+
137
+ /**
138
+ * Download Windows zip and extract
139
+ */
140
+ async function downloadWindowsBinary(url, destPath) {
141
+ console.log(`Downloading from ${url}...`);
142
+ const data = await httpsGet(url);
143
+
144
+ // Write zip to temp file
145
+ const zipPath = `${destPath}.zip`;
146
+ fs.writeFileSync(zipPath, data);
147
+
148
+ // Use unzip if available, otherwise try PowerShell
149
+ try {
150
+ if (process.platform === "win32") {
151
+ execSync(
152
+ `powershell -command "Expand-Archive -Path '${zipPath}' -DestinationPath '${path.dirname(destPath)}' -Force"`,
153
+ { stdio: "pipe" },
154
+ );
155
+ } else {
156
+ execSync(`unzip -o "${zipPath}" -d "${path.dirname(destPath)}"`, {
157
+ stdio: "pipe",
158
+ });
159
+ }
160
+ } finally {
161
+ fs.unlinkSync(zipPath);
162
+ }
163
+ }
164
+
165
+ async function main() {
166
+ const platform = PLATFORM_MAP[process.platform];
167
+ const arch = ARCH_MAP[process.arch];
168
+
169
+ if (!platform || !arch) {
170
+ console.log(`Unsupported platform: ${process.platform}-${process.arch}`);
171
+ console.log("You may need to build from source or download manually.");
172
+ process.exit(0); // Don't fail installation
173
+ }
174
+
175
+ const version = getVersion();
176
+ const binaryName = `toolui-${platform}-${arch}`;
177
+ const binDir = path.join(__dirname, "..", "bin");
178
+ const destPath = path.join(
179
+ binDir,
180
+ binaryName + (platform === "windows" ? ".exe" : ""),
181
+ );
182
+
183
+ // Skip if binary already exists
184
+ if (fs.existsSync(destPath)) {
185
+ console.log(`Binary already exists: ${destPath}`);
186
+ return;
187
+ }
188
+
189
+ // Ensure bin directory exists
190
+ if (!fs.existsSync(binDir)) {
191
+ fs.mkdirSync(binDir, { recursive: true });
192
+ }
193
+
194
+ const archiveExt = platform === "windows" ? "zip" : "tar.gz";
195
+ const downloadUrl = `${GITHUB_RELEASES}/download/v${version}/${binaryName}.${archiveExt}`;
196
+
197
+ try {
198
+ if (platform === "windows") {
199
+ await downloadWindowsBinary(downloadUrl, destPath);
200
+ } else {
201
+ await downloadBinary(downloadUrl, destPath);
202
+ }
203
+ console.log(`Successfully installed toolui binary to ${destPath}`);
204
+ } catch (error) {
205
+ console.error(`Failed to download binary: ${error.message}`);
206
+ console.error("");
207
+ console.error("You can manually download the binary from:");
208
+ console.error(` ${GITHUB_RELEASES}/latest`);
209
+ console.error("");
210
+ console.error("Or install via the install script:");
211
+ console.error(
212
+ ` curl -fsSL https://raw.githubusercontent.com/${REPO}/main/install.sh | bash`,
213
+ );
214
+ // Don't fail the install - the user can still use the package
215
+ process.exit(0);
216
+ }
217
+ }
218
+
219
+ main();