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
1 change: 1 addition & 0 deletions WARP.md
7 changes: 5 additions & 2 deletions apps/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
"name": "@superset/cli",
"version": "0.0.0",
"license": "MIT",
"bin": "dist/cli.js",
"bin": {
"superset": "dist/cli.js"
},
"type": "module",
"engines": {
"node": ">=16"
},
"scripts": {
"build": "bun build src/cli.tsx --outfile=dist/cli.js --target=node --packages=bundle",
"build": "bun build src/cli.tsx --outfile=dist/cli.js --target=node",
"dev": "tsc --watch",
"start": "bun src/server.ts",
"typecheck": "tsc --noEmit",
Expand All @@ -31,6 +33,7 @@
"ink": "^6.5.0",
"ink-select-input": "^6.2.0",
"ink-table": "^3.1.0",
"ink-text-input": "^6.0.0",
"lowdb": "^7.0.1",
"meow": "^11.0.0",
"react": "^19.1.1"
Expand Down
73 changes: 63 additions & 10 deletions apps/cli/readme.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,66 @@
# CLI
# Superset CLI

To run this, have 2 processes:
CLI for managing environments, workspaces (git worktrees/cloud branches), agents, and changes. Built with Ink + Commander, storage via lowdb JSON.

The first one is the build process:
```
bun dev
```
## Quick start (dev)
- Install deps: `bun install`
- Dev watch: `bun dev` (builds to `dist/`)
- Run CLI from source: `bunx ts-node src/cli.tsx --help`
Or from build: `bun run build && bun start …`
- Binary name: `superset` (points to `dist/cli.js`)

The 2nd one runs the actual dev "server"
```
bun start
```
## Core concepts
- **Environment**: grouping for workspaces (default env seeded).
- **Workspace**: local or cloud. For local, include `--path`; for cloud, include `--branch`. Tracks `defaultAgents`, `lastUsedAt`, `current workspace` pointer.
- **Worktree**: modelled as a workspace in CLI (one repo path/branch per workspace; dedicated worktree creation helpers are TODO).
- **Agent**: tmux-backed interactive session (Claude/Codex/Cursor). Each agent has a `sessionName`; `agent start` launches tmux sessions, `agent attach`/dashboard Enter attaches; detach with `Ctrl-b d`.
- **Change**: change log per workspace; has file diffs.

## Common commands
- `superset init` – Wizard to create workspace (local/cloud), set name/path/branch, choose default agents, set current workspace.
- `superset dashboard` – Ink dashboard; shows workspaces/agents, press Enter on an agent to attach to its tmux session; use `q/ESC` to exit.
- `superset workspace list|get|create|delete|use`
- Local: `workspace create <envId> local --path <path>`
- Cloud: `workspace create <envId> cloud --branch <ref>`
- `workspace use <id>` sets current workspace.
- `superset env list|get|create|delete`
- `superset agent start [workspaceId]` – Uses current workspace if omitted; starts default agents if configured.
- `superset agent attach <agentId|sessionName>` – Attach to tmux session. Detach with `Ctrl-b d`.
- `superset agent list|get|stop <id>|stop-all [--workspace <id>]|delete <id>` – `stop-all` only stops agents and kills their tmux sessions.
- `superset change list <workspaceId>|create <workspaceId> "<summary>"|delete <id>`

## tmux integration
- Requires `tmux` installed and on PATH.
- Sessions are named `agent-<shortId>` unless overridden in storage.
- Launch commands resolve from agent `launchCommand`, env overrides `SUPERSET_AGENT_LAUNCH_<TYPE>`, or defaults (`claude`, `codex`, `cursor`).
- To customize: `export SUPERSET_AGENT_LAUNCH_CLAUDE="your-custom-command"` (similarly for `CODEX`, `CURSOR`)
- Ensure the command stays alive (doesn't exit immediately) to prevent tmux session failures.
- If a session exists, attach; otherwise create detached then attach. Detach with `Ctrl-b d` to return to the dashboard/CLI; agents continue running.
- `stop/stop-all` issue `tmux kill-session` and mark agent stopped.

## Storage
- lowdb JSON at `~/.superset/cli/db.json` (default), seeded with a `default` environment and `state.currentWorkspaceId`.
- Can be overridden with `SUPERSET_CLI_DATA_DIR` environment variable.
- Dates are serialized ISO strings; orchestrators backfill defaults and persist missing fields (status, launchCommand, sessionName, timestamps).
Comment thread
coderabbitai[bot] marked this conversation as resolved.

## Security & Configuration Notes

### Session Names
- Session names are generated internally as `agent-<shortId>` (6-char UUID prefix).
- Only alphanumeric, hyphen, and underscore characters are allowed in tmux session names.
- If custom session names are added in the future, they will be sanitized to meet tmux requirements.

### Launch Commands
- Agent launch commands are executed exactly as provided in environment variables or config.
- **Security**: Only use trusted commands. The CLI does not sanitize or escape launch commands.
- **Best practice**: Use binaries on PATH (e.g., `claude`, `codex`) rather than complex shell expressions.
- **Complex shells**: For wrapped commands or environment setup, set `SUPERSET_AGENT_LAUNCH_<TYPE>` to point to a single, well-known entrypoint script:
```bash
export SUPERSET_AGENT_LAUNCH_CLAUDE="/usr/local/bin/launch-claude.sh"
```
Then put your complex logic in that script.
- **Command validation**: Simple commands (1-2 words) are checked for existence on PATH. Complex commands (with quotes, env vars, or multiple arguments) skip preflight validation to avoid false negatives.

## Tips
- Use `superset` (no args) for a welcome summary and quick commands.
- If attach fails, ensure tmux is installed and the agent has a valid `launchCommand` (set env `SUPERSET_AGENT_LAUNCH_CLAUDE=claude` etc.).
134 changes: 127 additions & 7 deletions apps/cli/src/cli.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,29 @@ import { Command } from "commander";
import { render } from "ink";
import React from "react";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

Remove unused React import to satisfy lint

React is not referenced in this file; JSX elements are only passed to render. You can safely drop this import to clear the noUnusedImports warning:

-import React from "react";

Assuming your TS config uses the modern JSX transform (jsx: "react-jsx"), this will still compile.


🏁 Script executed:

#!/bin/bash
# Verify React import and usage in cli.tsx
echo "=== Examining apps/cli/src/cli.tsx ==="
cat apps/cli/src/cli.tsx | head -20
echo ""
echo "=== Searching for 'React' references in file ==="
rg -n 'React' apps/cli/src/cli.tsx || echo "No React references found"
echo ""
echo "=== Checking for JSX syntax ==="
rg -n 'JSX|<[A-Z]|</' apps/cli/src/cli.tsx | head -10

Length of output: 927


Remove unused React import from line 4

Verified: React is imported but never referenced in the file. The file uses JSX extensively (<Init />, <Dashboard />, <Panels />, etc.), but none of these reference the React object directly. With the modern JSX transform, this import is not required and can be safely removed:

-import React from "react";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import React from "react";
🧰 Tools
🪛 GitHub Actions: CI

[warning] 4-4: lint/correctness/noUnusedImports: Unused import React detected.

🤖 Prompt for AI Agents
In apps/cli/src/cli.tsx around line 4, remove the unused import "import React
from \"react\";" because the file never references the React identifier and the
project uses the modern JSX transform; simply delete that import line and ensure
no other references to React exist so the file still compiles.

import {
AgentAttach,
AgentCreate,
AgentDelete,
AgentGet,
AgentList,
AgentStart,
AgentStop,
AgentStopAll,
ChangeCreate,
ChangeDelete,
ChangeList,
Dashboard,
EnvCreate,
EnvDelete,
EnvGet,
EnvList,
Init,
Panels,
WorkspaceCreate,
WorkspaceDelete,
WorkspaceGet,
WorkspaceList,
WorkspaceUse,
} from "./commands/index";
import { AgentType, ProcessType } from "./types/process";
import { WorkspaceType } from "./types/workspace";
Expand All @@ -33,8 +39,34 @@ program
)
.version("0.1.0");

// Init command
program
.command("init")
.description("Interactive workspace creation wizard")
.action(() => {
render(<Init />);
});

// Dashboard command
program
.command("dashboard")
.description("Show dashboard with all agents and workspaces")
.action(() => {
render(<Dashboard />);
});

// Panels command
program
.command("panels")
.description("Show three-panel IDE-style interface")
.action(() => {
render(<Panels />);
});

// Environment commands
const env = program.command("env").description("Manage environments");
const env = program
.command("env")
.description("Manage environments (list, get, create, delete)");

env
.command("list")
Expand Down Expand Up @@ -69,7 +101,9 @@ env
});

// Workspace commands
const workspace = program.command("workspace").description("Manage workspaces");
const workspace = program
.command("workspace")
.description("Manage workspaces (list, get, create, use, delete)");

workspace
.command("list")
Expand Down Expand Up @@ -128,10 +162,20 @@ workspace
render(<WorkspaceDelete id={id} onComplete={() => process.exit(0)} />);
});

workspace
.command("use")
.description("Set current workspace (updates lastUsedAt)")
.argument("<id>", "Workspace ID")
.action((id: string) => {
render(<WorkspaceUse id={id} onComplete={() => process.exit(0)} />);
});

// Agent/Process commands
const agent = program
.command("agent")
.description("Manage agents and processes");
.description(
"Manage agents and processes (start, stop, stop-all, list, delete)",
);

agent
.command("list")
Expand All @@ -154,6 +198,24 @@ agent
render(<AgentGet id={id} onComplete={() => process.exit(0)} />);
});

agent
.command("start")
.description(
"Start agents (uses current workspace if no ID provided, or workspace's default agents)",
)
.argument("[workspaceId]", "Workspace ID (optional, uses current workspace)")
.action((workspaceId?: string) => {
render(<AgentStart workspaceId={workspaceId} />);
});

agent
.command("attach")
.description("Attach to an agent's tmux session")
.argument("<id>", "Agent ID or session name (e.g., agent-abc123)")
.action((id: string) => {
render(<AgentAttach id={id} onComplete={() => process.exit(0)} />);
});

agent
.command("create")
.description("Create a new agent/process")
Expand Down Expand Up @@ -208,9 +270,17 @@ agent

agent
.command("stop-all")
.description("Stop all agents/processes")
.action(() => {
render(<AgentStopAll onComplete={() => process.exit(0)} />);
.description(
"Stop all agents in workspace (kills tmux sessions, does not affect terminals)",
)
.option("--workspace <workspaceId>", "Workspace ID to stop agents in")
.action((options: { workspace?: string }) => {
render(
<AgentStopAll
workspaceId={options.workspace}
onComplete={() => process.exit(0)}
/>,
);
});

agent
Expand All @@ -222,7 +292,9 @@ agent
});

// Change commands
const change = program.command("change").description("Manage changes");
const change = program
.command("change")
.description("Manage changes (list, create, delete)");

change
.command("list")
Expand Down Expand Up @@ -260,4 +332,52 @@ change
render(<ChangeDelete id={id} onComplete={() => process.exit(0)} />);
});

// Default action when no command is provided
program.action(async () => {
console.log("\n👋 Welcome to Superset CLI!\n");

// Show current workspace if set
try {
const { getDb } = await import("./lib/db");
const { WorkspaceOrchestrator } = await import(
"./lib/orchestrators/workspace-orchestrator"
);
const db = getDb();
const orchestrator = new WorkspaceOrchestrator(db);
const currentWorkspace = await orchestrator.getCurrent();

if (currentWorkspace) {
console.log(
`📁 Current workspace: ${currentWorkspace.name || currentWorkspace.id}`,
);
if ("path" in currentWorkspace && currentWorkspace.path) {
console.log(` Path: ${currentWorkspace.path}`);
}
if ("branch" in currentWorkspace && currentWorkspace.branch) {
console.log(` Branch: ${currentWorkspace.branch}`);
}
console.log("");
} else {
console.log(
"💡 No workspace selected. Run 'superset init' to get started!\n",
);
}
} catch (err) {
// Silently ignore errors (e.g., no database yet)
}

console.log("Get started with these commands:\n");
console.log(" superset init Create workspace (wizard)");
console.log(" superset dashboard Show dashboard overview");
console.log(
" superset panels Show three-panel IDE interface",
);
console.log(" superset workspace use <id> Switch to a workspace");
console.log(
" superset agent start Start agents in current workspace",
);
console.log("\nFor more information, run: superset --help\n");
process.exit(0);
});

program.parse();
Loading
Loading