claude-tempo 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/.mcp.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "mcpServers": {
3
+ "claude-tempo": {
4
+ "command": "node",
5
+ "args": ["dist/server.js"],
6
+ "cwd": "/Users/drewpayment/dev/claude-tempo"
7
+ }
8
+ }
9
+ }
package/CLAUDE.md ADDED
@@ -0,0 +1,84 @@
1
+ # CLAUDE.md
2
+
3
+ ## What is this?
4
+
5
+ claude-tempo is an MCP server that enables multiple Claude Code sessions to coordinate via Temporal.
6
+
7
+ ## Tech Stack
8
+
9
+ - **Runtime**: Node.js 18+ with TypeScript
10
+ - **MCP**: `@modelcontextprotocol/sdk` (stdio transport)
11
+ - **Temporal**: `@temporalio/client`, `@temporalio/worker`, `@temporalio/workflow`, `@temporalio/activity`
12
+ - **No other dependencies** — no database, no custom broker
13
+
14
+ ## Project Structure
15
+
16
+ ```
17
+ src/
18
+ ├── server.ts # MCP server entry point
19
+ ├── worker.ts # Temporal worker setup
20
+ ├── workflows/
21
+ │ ├── session.ts # claude-session workflow
22
+ │ └── signals.ts # Signal/query type definitions
23
+ ├── tools/
24
+ │ ├── ensemble.ts # Discover active sessions
25
+ │ ├── cue.ts # Send message to peer
26
+ │ ├── set-name.ts # Set session name
27
+ │ ├── set-part.ts # Update own summary
28
+ │ ├── resolve.ts # Search-attribute session lookup
29
+ │ ├── listen.ts # Manual message check
30
+ │ ├── recruit.ts # Spawn new session
31
+ │ ├── report.ts # Report to conductor
32
+ │ ├── terminate.ts # Terminate a session
33
+ │ └── helpers.ts # Zod/MCP tool registration wrapper
34
+ ├── types.ts # Shared type definitions
35
+ ├── channel.ts # Claude channel notification helper
36
+ └── config.ts # Env var handling
37
+ ```
38
+
39
+ ## Development
40
+
41
+ ```bash
42
+ # Install dependencies
43
+ npm install
44
+
45
+ # Start Temporal dev server (separate terminal)
46
+ temporal server start-dev
47
+
48
+ # Run in development
49
+ npx ts-node src/server.ts
50
+
51
+ # Build (compiles TS and pre-bundles workflow code)
52
+ npm run build
53
+
54
+ # Test
55
+ npm test
56
+ ```
57
+
58
+ > **Important**: Always run `npm run build` after changing workflow code (`src/workflows/`).
59
+ > The build pre-bundles workflows into `workflow-bundle.js` so all workers use identical code.
60
+
61
+ ## Key Concepts
62
+
63
+ - **Player**: A Claude Code session registered as a Temporal workflow
64
+ - **Conductor**: A special player that acts as orchestration hub, connected to external interfaces (one per ensemble)
65
+ - **Ensemble**: The set of all active players, namespaced by `CLAUDE_TEMPO_ENSEMBLE`
66
+ - **Cue**: A message sent to a player by name via Temporal signal
67
+ - **Part**: A player's description of what it's working on
68
+ - **Recruit**: Spawning a new Claude Code session as a player
69
+ - **set_name**: Players start with a random hex ID; `set_name` updates the `ClaudeTempoPlayerId` search attribute to a human-readable name
70
+
71
+ ## Dashboard
72
+
73
+ The ensemble dashboard (Maestro) lives in a separate repository: [vinceblank/maestro](https://github.com/vinceblank/maestro)
74
+
75
+ It provides a web UI for managing ensembles, communicating with conductors, and monitoring player activity.
76
+
77
+ ## Commit Convention
78
+
79
+ Use conventional commits: `type(scope): message`
80
+
81
+ Examples:
82
+ - `feat(tools): add ensemble discovery tool`
83
+ - `fix(workflow): handle signal delivery edge case`
84
+ - `docs: update getting started guide`
package/README.md ADDED
@@ -0,0 +1,400 @@
1
+ # claude-tempo
2
+
3
+ MCP server for multi-session Claude Code coordination via [Temporal](https://temporal.io).
4
+
5
+ Multiple Claude Code sessions discover each other, exchange messages in real time, and coordinate work — across machines, not just localhost.
6
+
7
+ Inspired by [claude-peers](https://github.com/louislva/claude-peers-mcp) and seeing how it interacted with Claude Code's experimental channel capability. claude-tempo takes the concept further with Temporal as the coordination backbone — adding durable state, cross-machine messaging, structured orchestration, and automatic stale session cleanup.
8
+
9
+ ## How it works
10
+
11
+ **claude-tempo** uses Temporal workflows as the coordination layer:
12
+
13
+ - Each Claude Code session registers as a **player** (a Temporal workflow)
14
+ - Players belong to an **ensemble** — a named group of sessions that can see and message each other
15
+ - Players discover each other via `ensemble`, message via `cue`, and spawn new sessions via `recruit`
16
+ - Players can interact directly (peer-to-peer) — no central hub required
17
+ - An optional **conductor** player acts as an orchestration hub for the ensemble, connected to external interfaces like Discord or Telegram
18
+
19
+ ### Ensembles
20
+
21
+ An **ensemble** is an isolated group of players identified by name (e.g., `frontend`, `backend`, `default`). Players in one ensemble cannot see or message players in another — they are completely independent.
22
+
23
+ Each ensemble can have:
24
+ - Any number of **players** working on tasks
25
+ - One optional **conductor** coordinating work and connected to external interfaces
26
+
27
+ By default, all sessions join the `default` ensemble. Pass an ensemble name when starting a session to create or join a different one:
28
+
29
+ ```bash
30
+ claude-tempo conduct frontend # conduct the "frontend" ensemble
31
+ claude-tempo start backend # join the "backend" ensemble
32
+ claude-tempo conduct # conduct the "default" ensemble
33
+ ```
34
+
35
+ This lets you run separate groups of sessions for different projects or concerns without interference.
36
+
37
+ ```mermaid
38
+ graph TD
39
+ You["You (Discord / Telegram / CLI / Claude Code)"]
40
+ You -->|signal / query| Conductor
41
+
42
+ subgraph Temporal["Temporal Server"]
43
+ Conductor["Conductor Workflow"]
44
+ PA["Player A Workflow"]
45
+ PB["Player B Workflow"]
46
+ PC["Player C Workflow"]
47
+ Conductor -->|cue| PA
48
+ Conductor -->|cue| PB
49
+ Conductor -->|cue| PC
50
+ end
51
+
52
+ subgraph Host1["Host 1"]
53
+ S1["Claude Session A"]
54
+ S2["Claude Session B"]
55
+ end
56
+
57
+ subgraph Host2["Host 2"]
58
+ S3["Claude Session C"]
59
+ end
60
+
61
+ PA -.-> S1
62
+ PB -.-> S2
63
+ PC -.-> S3
64
+ ```
65
+
66
+ ## Tools
67
+
68
+ | Tool | Description |
69
+ |------|-------------|
70
+ | `ensemble` | Discover active sessions in your ensemble. Scope: `machine`, `repo`, `all`. |
71
+ | `cue` | Send a message to any session by player name. Instant via Temporal signal. |
72
+ | `set_name` | Set a human-readable name for this session. Used by other players to message you. |
73
+ | `set_part` | Describe what you're working on. Visible to others via `ensemble`. |
74
+ | `listen` | Manual fallback for checking pending messages. |
75
+ | `recruit` | Start a named Claude Code session in a directory. Opens a new terminal window automatically. |
76
+ | `report` | Send updates to the conductor (surfaces to Discord/Telegram). No-op if no conductor. |
77
+ | `terminate` | Terminate a player session by name. Use to clean up orphaned sessions. |
78
+
79
+ ## Prerequisites
80
+
81
+ - [Node.js](https://nodejs.org/) 18+
82
+ - [Temporal CLI](https://docs.temporal.io/cli) (for local dev server)
83
+ - [Claude Code](https://claude.ai/code)
84
+
85
+ ## Quick start
86
+
87
+ The fastest way to get going — one command handles everything:
88
+
89
+ ```bash
90
+ # Install
91
+ npm install -g claude-tempo
92
+
93
+ # Go to your project and run `up`
94
+ cd your-project
95
+ claude-tempo up
96
+ ```
97
+
98
+ `claude-tempo up` will:
99
+ 1. Check that the Temporal CLI is installed
100
+ 2. Start the Temporal dev server if it's not already running (data persists in `~/.claude-tempo/`)
101
+ 3. Register the required search attributes automatically
102
+ 4. Create `.mcp.json` in your project if it doesn't exist
103
+ 5. Launch a conductor session in a new terminal window
104
+
105
+ After `up` completes, you're ready to add players:
106
+
107
+ ```bash
108
+ claude-tempo start # open a player session
109
+ claude-tempo status # see who's active
110
+ ```
111
+
112
+ Or ask the conductor to `recruit` players for you from inside Claude Code.
113
+
114
+ ### Manual setup
115
+
116
+ If you prefer more control, you can run each step individually:
117
+
118
+ ```bash
119
+ # Start Temporal dev server (keep this running)
120
+ claude-tempo server
121
+
122
+ # In your project directory, create .mcp.json
123
+ cd your-project
124
+ claude-tempo init
125
+
126
+ # Verify everything is ready
127
+ claude-tempo preflight
128
+
129
+ # Start a conductor
130
+ claude-tempo conduct
131
+
132
+ # Add players
133
+ claude-tempo start
134
+ ```
135
+
136
+ ## CLI
137
+
138
+ The `claude-tempo` CLI handles setup, session management, and diagnostics.
139
+
140
+ ### Commands
141
+
142
+ | Command | Description |
143
+ |---------|-------------|
144
+ | `up [ensemble]` | First-time setup: start Temporal, configure MCP, launch conductor |
145
+ | `server` | Start the Temporal dev server and register search attributes |
146
+ | `conduct [ensemble]` | Start a conductor session (one per ensemble) |
147
+ | `start [ensemble]` | Start a player session |
148
+ | `status [ensemble]` | Show active sessions and Temporal health |
149
+ | `init` | Create `.mcp.json` config in the current directory |
150
+ | `preflight` | Run environment checks only |
151
+ | `help` | Show usage info |
152
+
153
+ ### Options
154
+
155
+ ```
156
+ --temporal-address <addr> Temporal server address (default: localhost:7233)
157
+ -n, --name <name> Set the session window name (start/conduct/up)
158
+ --skip-preflight Skip preflight checks (start/conduct)
159
+ --background, -d Run Temporal in background (server only)
160
+ --dir <path> Target directory for init (default: cwd)
161
+ ```
162
+
163
+ ### `up` — first-time setup
164
+
165
+ `claude-tempo up` is the recommended way to get started. It handles everything in order:
166
+
167
+ ```
168
+ $ claude-tempo up myband
169
+
170
+ claude-tempo setup
171
+ pass temporal CLI installed
172
+ ... Starting Temporal dev server...
173
+ pass Temporal started (pid 12345, data in ~/.claude-tempo/)
174
+ ok Registered search attribute: ClaudeTempoHostname
175
+ ok Registered search attribute: ClaudeTempoGitRoot
176
+ ok Registered search attribute: ClaudeTempoEnsemble
177
+ ok Registered search attribute: ClaudeTempoPlayerId
178
+ pass .mcp.json created
179
+
180
+ Launching conductor in ensemble myband...
181
+
182
+ ok You're all set!
183
+ Conductor launched (pid 12346)
184
+ Ensemble: myband
185
+
186
+ What next?
187
+ claude-tempo start myband Add a player session
188
+ claude-tempo status myband See who's active
189
+ Or ask the conductor to recruit players for you
190
+ ```
191
+
192
+ ### `server` — Temporal management
193
+
194
+ `claude-tempo server` starts the Temporal dev server with automatic search attribute registration:
195
+
196
+ ```bash
197
+ claude-tempo server # foreground (Ctrl+C to stop)
198
+ claude-tempo server --background # daemonize
199
+ claude-tempo server -d # shorthand
200
+ ```
201
+
202
+ - Stores data in `~/.claude-tempo/temporal-data.db` (persists across restarts)
203
+ - Registers all required search attributes automatically
204
+ - If Temporal is already running, just registers attributes and exits
205
+
206
+ ### `init` — MCP configuration
207
+
208
+ `claude-tempo init` creates a `.mcp.json` in the current directory (or merges into an existing one):
209
+
210
+ ```json
211
+ {
212
+ "mcpServers": {
213
+ "claude-tempo": {
214
+ "command": "claude-tempo-server",
215
+ "args": []
216
+ }
217
+ }
218
+ }
219
+ ```
220
+
221
+ No source code or absolute paths needed — `claude-tempo-server` is installed on PATH via the npm package.
222
+
223
+ ### `status` — ensemble overview
224
+
225
+ `claude-tempo status` shows all active sessions:
226
+
227
+ ```
228
+ Ensemble: myband
229
+ 3 active sessions
230
+
231
+ conductor (conductor)
232
+ Orchestrating the team
233
+ /Users/me/projects/app main my-machine.local
234
+
235
+ alice
236
+ Building the REST endpoints
237
+ /Users/me/projects/app feat/api my-machine.local
238
+
239
+ bob
240
+ Working on the dashboard
241
+ /Users/me/projects/app feat/ui my-machine.local
242
+ ```
243
+
244
+ ### `preflight` — environment checks
245
+
246
+ `claude-tempo preflight` verifies your environment:
247
+
248
+ - Node.js >= 18
249
+ - Temporal server reachable
250
+ - `claude` binary on PATH
251
+ - `claude-tempo-server` binary on PATH
252
+ - `.mcp.json` configured in the current directory
253
+
254
+ ## Starting a conductor
255
+
256
+ A **conductor** is an optional special player that acts as an orchestration hub for the ensemble. Use a conductor when you want:
257
+
258
+ - A single session coordinating work across multiple players
259
+ - External access to the ensemble via Discord, Telegram, or any Temporal client
260
+ - A central point for players to `report` progress, blockers, and questions
261
+
262
+ Without a conductor, players still work fine — they discover each other via `ensemble` and communicate directly via `cue`. The conductor is a hub, not a gatekeeper.
263
+
264
+ There is one conductor per ensemble. Start one with:
265
+
266
+ ```bash
267
+ claude-tempo conduct # conductor in "default" ensemble
268
+ claude-tempo conduct my-project # conductor in "my-project" ensemble
269
+ ```
270
+
271
+ ### External access
272
+
273
+ The conductor's Temporal workflow exposes a signal/query API that anyone can use — no Claude Code session needed:
274
+
275
+ ```typescript
276
+ import { Client } from '@temporalio/client';
277
+
278
+ const client = new Client();
279
+ // Conductor workflow ID: claude-session-{ensemble}-conductor
280
+ const conductor = client.workflow.getHandle('claude-session-default-conductor');
281
+
282
+ // Send a command
283
+ await conductor.signal('command', {
284
+ text: 'recruit /repos/api and run tests',
285
+ source: 'cli',
286
+ });
287
+
288
+ // Check history of commands and reports
289
+ const history = await conductor.query('history');
290
+ ```
291
+
292
+ You can also connect external channel plugins (e.g., Discord):
293
+
294
+ ```bash
295
+ CLAUDE_TEMPO_CONDUCTOR=true claude \
296
+ --channels plugin:discord@claude-plugins-official \
297
+ --dangerously-skip-permissions --dangerously-load-development-channels server:claude-tempo
298
+ ```
299
+
300
+ ## Starting players
301
+
302
+ The recommended way to build an ensemble is to use the CLI to start sessions. Each session opens in a new terminal window with the full shell environment preserved.
303
+
304
+ ```bash
305
+ # Terminal 1 — conductor
306
+ claude-tempo conduct my-project
307
+
308
+ # Terminal 2 — frontend player
309
+ claude-tempo start my-project -n frontend
310
+
311
+ # Terminal 3 — backend player
312
+ claude-tempo start my-project -n backend
313
+ ```
314
+
315
+ Or let the conductor `recruit` players directly — this spawns new terminal windows automatically.
316
+
317
+ Once sessions are running, try:
318
+ - "Show me the ensemble" — discovers other sessions
319
+ - "Set your name to 'frontend'" — gives your session a human-readable name
320
+ - "Cue frontend: what are you working on?" — sends a message by name
321
+
322
+ ### Terminal support
323
+
324
+ The `recruit` tool and CLI automatically detect and open sessions in your terminal:
325
+
326
+ | Terminal | macOS | Linux | Windows |
327
+ |----------|-------|-------|---------|
328
+ | Ghostty | `initial input` via AppleScript | — | — |
329
+ | iTerm2 | `write text` via AppleScript | — | — |
330
+ | Terminal.app | `.command` file | — | — |
331
+ | gnome-terminal | — | `--` flag | — |
332
+ | konsole / xterm | — | `-e` flag | — |
333
+ | cmd.exe / PowerShell | — | — | `shell:true` |
334
+
335
+ All macOS terminals use approaches that preserve the user's full shell environment (fish, zsh, bash) including node version managers (fnm, nvm).
336
+
337
+ ### Session naming
338
+
339
+ Sessions start with a random 8-character hex ID. Use `set_name` to give a session a human-readable name:
340
+
341
+ - Names are stored as Temporal search attributes (`ClaudeTempoPlayerId`) and updated in-place — no workflow restart needed
342
+ - Other players use the name to send messages via `cue` and discover sessions via `ensemble`
343
+ - `recruit` automatically tells the new session to set its name
344
+ - Names must be unique within an ensemble — `set_name` rejects duplicates
345
+ - Names must contain only letters, numbers, hyphens, and underscores
346
+
347
+ ## Configuration
348
+
349
+ | Environment Variable | Default | Description |
350
+ |---------------------|---------|-------------|
351
+ | `TEMPORAL_ADDRESS` | `localhost:7233` | Temporal server address |
352
+ | `TEMPORAL_NAMESPACE` | `default` | Temporal namespace |
353
+ | `CLAUDE_TEMPO_TASK_QUEUE` | `claude-tempo` | Task queue name |
354
+ | `CLAUDE_TEMPO_ENSEMBLE` | `default` | Ensemble name (isolates groups of players) |
355
+ | `CLAUDE_TEMPO_CONDUCTOR` | `false` | Set to `true` to enable conductor mode |
356
+
357
+ ## Development
358
+
359
+ ```bash
360
+ # Clone and install
361
+ git clone https://github.com/vinceblank/claude-tempo.git
362
+ cd claude-tempo && npm install
363
+
364
+ # Build (compiles TypeScript and pre-bundles workflow code)
365
+ npm run build
366
+
367
+ # Run MCP server in development
368
+ npx ts-node src/server.ts
369
+
370
+ # Link CLI for local testing
371
+ npm link
372
+ ```
373
+
374
+ > **Important**: Run `npm run build` after changing workflow code (`src/workflows/`). The build pre-bundles workflows into `workflow-bundle.js` so all workers use identical code.
375
+
376
+ ## Why Temporal?
377
+
378
+ - **Cross-machine**: Any session that can reach the Temporal server can join the ensemble
379
+ - **Instant signaling**: Temporal signals deliver messages between sessions with no broker polling
380
+ - **Durable history**: Full audit trail of every message in Temporal's event history
381
+ - **No custom infrastructure**: No broker daemon, no database — just Temporal
382
+ - **Extensible**: The conductor's signal/query contract is a public API anyone can build on
383
+
384
+ ## Stale session cleanup
385
+
386
+ When a Claude Code session crashes or is closed without graceful shutdown, its Temporal workflow detects the problem automatically:
387
+
388
+ - If a message is sent to a dead session and remains undelivered for **3 minutes**, the workflow self-completes
389
+ - Before exiting, it notifies the conductor with the undelivered message content so work can be reassigned
390
+ - Idle sessions with no pending messages remain running (they aren't hurting anyone) until the 24-hour execution timeout
391
+
392
+ This means you don't need to manually clean up crashed sessions — just `cue` the dead player and the system handles the rest.
393
+
394
+ ## Known limitations
395
+
396
+ - **`recruit` requires manual acknowledgment**: Recruited sessions use `--dangerously-load-development-channels` to enable channel-based message delivery. Claude Code shows an interactive confirmation prompt that must be manually acknowledged (press Enter) in the spawned terminal window. This will be resolved once claude-tempo is published as an approved channel plugin.
397
+
398
+ ## License
399
+
400
+ MIT
package/dist/cli.js ADDED
@@ -0,0 +1,169 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ const commands_1 = require("./cli/commands");
38
+ const preflight_1 = require("./cli/preflight");
39
+ const out = __importStar(require("./cli/output"));
40
+ function parseArgs(argv) {
41
+ const result = {
42
+ command: 'help',
43
+ positional: [],
44
+ temporalAddress: process.env.TEMPORAL_ADDRESS || 'localhost:7233',
45
+ dir: process.cwd(),
46
+ skipPreflight: false,
47
+ background: false,
48
+ keepMcp: false,
49
+ };
50
+ let i = 0;
51
+ while (i < argv.length) {
52
+ const arg = argv[i];
53
+ if (arg === '--temporal-address' && i + 1 < argv.length) {
54
+ result.temporalAddress = argv[++i];
55
+ }
56
+ else if ((arg === '-n' || arg === '--name') && i + 1 < argv.length) {
57
+ result.name = argv[++i];
58
+ }
59
+ else if (arg === '--dir' && i + 1 < argv.length) {
60
+ result.dir = argv[++i];
61
+ }
62
+ else if (arg === '--skip-preflight') {
63
+ result.skipPreflight = true;
64
+ }
65
+ else if (arg === '--background' || arg === '-d') {
66
+ result.background = true;
67
+ }
68
+ else if (arg === '--keep-mcp') {
69
+ result.keepMcp = true;
70
+ }
71
+ else if (arg === '--help' || arg === '-h') {
72
+ result.command = 'help';
73
+ }
74
+ else if (arg === '--version' || arg === '-v') {
75
+ result.command = 'version';
76
+ }
77
+ else if (!arg.startsWith('-')) {
78
+ result.positional.push(arg);
79
+ }
80
+ else {
81
+ out.error(`Unknown option: ${arg}`);
82
+ out.log(`Run ${out.dim('claude-tempo help')} for usage.`);
83
+ process.exit(1);
84
+ }
85
+ i++;
86
+ }
87
+ if (result.positional.length > 0) {
88
+ result.command = result.positional[0];
89
+ }
90
+ return result;
91
+ }
92
+ async function main() {
93
+ const args = parseArgs(process.argv.slice(2));
94
+ const ensemble = args.positional[1] || process.env.CLAUDE_TEMPO_ENSEMBLE || 'default';
95
+ switch (args.command) {
96
+ case 'conduct':
97
+ await (0, commands_1.start)({
98
+ ensemble,
99
+ conductor: true,
100
+ temporalAddress: args.temporalAddress,
101
+ name: args.name,
102
+ skipPreflight: args.skipPreflight,
103
+ });
104
+ break;
105
+ case 'start':
106
+ await (0, commands_1.start)({
107
+ ensemble,
108
+ conductor: false,
109
+ temporalAddress: args.temporalAddress,
110
+ name: args.name,
111
+ skipPreflight: args.skipPreflight,
112
+ });
113
+ break;
114
+ case 'status':
115
+ await (0, commands_1.status)({
116
+ ensemble: args.positional[1], // undefined = show all
117
+ temporalAddress: args.temporalAddress,
118
+ });
119
+ break;
120
+ case 'server':
121
+ await (0, commands_1.server)({
122
+ temporalAddress: args.temporalAddress,
123
+ background: args.background,
124
+ });
125
+ break;
126
+ case 'down':
127
+ await (0, commands_1.down)({
128
+ temporalAddress: args.temporalAddress,
129
+ removeMcp: !args.keepMcp,
130
+ dir: args.dir,
131
+ });
132
+ break;
133
+ case 'up':
134
+ await (0, commands_1.up)({
135
+ ensemble,
136
+ temporalAddress: args.temporalAddress,
137
+ name: args.name,
138
+ });
139
+ break;
140
+ case 'init':
141
+ await (0, commands_1.init)({ dir: args.dir });
142
+ break;
143
+ case 'preflight':
144
+ const result = await (0, preflight_1.runPreflight)({
145
+ temporalAddress: args.temporalAddress,
146
+ projectDir: args.dir,
147
+ });
148
+ for (const w of result.warnings)
149
+ out.warn(w);
150
+ if (!result.ok) {
151
+ for (const e of result.errors)
152
+ out.error(e);
153
+ process.exit(1);
154
+ }
155
+ out.success('All checks passed');
156
+ break;
157
+ case 'version':
158
+ (0, commands_1.version)();
159
+ break;
160
+ case 'help':
161
+ default:
162
+ (0, commands_1.help)();
163
+ break;
164
+ }
165
+ }
166
+ main().catch((err) => {
167
+ out.error(err.message || String(err));
168
+ process.exit(1);
169
+ });