claude-tempo 0.26.0 → 0.27.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.
Files changed (69) hide show
  1. package/CLAUDE.md +3 -1
  2. package/README.md +21 -21
  3. package/dist/activities/outbox.js +20 -12
  4. package/dist/cli/commands.d.ts +21 -73
  5. package/dist/cli/commands.js +200 -646
  6. package/dist/cli/daemon.d.ts +31 -3
  7. package/dist/cli/daemon.js +58 -16
  8. package/dist/cli/help-text.js +47 -47
  9. package/dist/cli/removed-verbs.d.ts +9 -0
  10. package/dist/cli/removed-verbs.js +75 -0
  11. package/dist/cli/startup.d.ts +103 -0
  12. package/dist/cli/startup.js +615 -0
  13. package/dist/cli.js +54 -170
  14. package/dist/client/ensure-conductor-spawned.d.ts +32 -0
  15. package/dist/client/ensure-conductor-spawned.js +45 -0
  16. package/dist/client/index.d.ts +1 -7
  17. package/dist/client/index.js +447 -0
  18. package/dist/client/interface.d.ts +177 -3
  19. package/dist/config.d.ts +6 -0
  20. package/dist/config.js +45 -7
  21. package/dist/daemon.js +20 -80
  22. package/dist/ensemble/loader.js +9 -0
  23. package/dist/ensemble/saver.js +3 -1
  24. package/dist/ensemble/schema.d.ts +7 -1
  25. package/dist/reconcile/orphans.d.ts +134 -2
  26. package/dist/reconcile/orphans.js +206 -15
  27. package/dist/server.js +14 -8
  28. package/dist/tools/destroy.d.ts +1 -1
  29. package/dist/tools/destroy.js +149 -24
  30. package/dist/tools/{pause-ensemble.d.ts → pause.d.ts} +1 -1
  31. package/dist/tools/pause.js +36 -0
  32. package/dist/tools/{resume-ensemble.d.ts → play.d.ts} +1 -1
  33. package/dist/tools/play.js +57 -0
  34. package/dist/tools/restore.d.ts +4 -0
  35. package/dist/tools/restore.js +91 -0
  36. package/dist/tools/shutdown.d.ts +4 -0
  37. package/dist/tools/shutdown.js +54 -0
  38. package/dist/tui/App.d.ts +63 -0
  39. package/dist/tui/App.js +548 -155
  40. package/dist/tui/bootstrap-types.d.ts +46 -0
  41. package/dist/tui/bootstrap-types.js +7 -0
  42. package/dist/tui/commands.d.ts +48 -1
  43. package/dist/tui/commands.js +383 -177
  44. package/dist/tui/components/DestroyConfirmModal.d.ts +17 -0
  45. package/dist/tui/components/DestroyConfirmModal.js +62 -0
  46. package/dist/tui/components/HomeView.d.ts +54 -0
  47. package/dist/tui/components/HomeView.js +306 -0
  48. package/dist/tui/components/LoadLineupModal.d.ts +18 -0
  49. package/dist/tui/components/LoadLineupModal.js +79 -0
  50. package/dist/tui/components/NewEnsembleModal.d.ts +9 -0
  51. package/dist/tui/components/NewEnsembleModal.js +73 -0
  52. package/dist/tui/components/PromptArea.js +9 -12
  53. package/dist/tui/components/RestoreConfirmModal.d.ts +18 -0
  54. package/dist/tui/components/RestoreConfirmModal.js +71 -0
  55. package/dist/tui/components/StatusBar.d.ts +34 -1
  56. package/dist/tui/components/StatusBar.js +59 -14
  57. package/dist/tui/index.d.ts +8 -0
  58. package/dist/tui/index.js +12 -0
  59. package/dist/tui/removed-commands.d.ts +9 -0
  60. package/dist/tui/removed-commands.js +22 -0
  61. package/dist/tui/store.d.ts +130 -0
  62. package/dist/tui/store.js +139 -1
  63. package/dist/tui/utils/format.d.ts +19 -0
  64. package/dist/tui/utils/format.js +17 -0
  65. package/dist/utils/ensemble-ops.d.ts +61 -0
  66. package/dist/utils/ensemble-ops.js +77 -0
  67. package/package.json +3 -1
  68. package/dist/tools/pause-ensemble.js +0 -58
  69. package/dist/tools/resume-ensemble.js +0 -79
package/CLAUDE.md CHANGED
@@ -28,6 +28,8 @@ src/
28
28
  │ ├── mcp.ts # MCP server registration helpers (init, global vs project)
29
29
  │ ├── output.ts # Shared CLI output formatting helpers
30
30
  │ ├── preflight.ts # Environment preflight checks
31
+ │ ├── removed-verbs.ts # lookup table for the 10 CLI verbs removed in #288 — dispatches migration hints before loading Temporal surface
32
+ │ ├── startup.ts # auto-provisioning bootstrap state machine (#289) — six-step idempotent sequence used by bare `claude-tempo` invocation
31
33
  │ └── upgrade-command.ts # upgrade subcommand — crash-proof; dynamic-imports Temporal only for active-session warning
32
34
  ├── adapters/
33
35
  │ ├── README.md # Adapter contract documentation
@@ -69,7 +71,7 @@ src/
69
71
  │ ├── worktree.ts / stage.ts / stages.ts / cancel-stage.ts
70
72
  │ ├── load-lineup.ts / save-lineup.ts / agent-types.ts / resolve.ts
71
73
  │ ├── set-name.ts / set-part.ts / who-am-i.ts / release.ts
72
- │ ├── pause-ensemble.ts / resume-ensemble.ts
74
+ │ ├── pause.ts / play.ts / shutdown.ts / restore.ts
73
75
  │ ├── hosts.ts
74
76
  │ └── helpers.ts # Zod/MCP tool registration wrapper
75
77
  ├── tui/
package/README.md CHANGED
@@ -59,11 +59,10 @@ claude-tempo up
59
59
  This starts Temporal, registers the MCP server, launches the daemon, and opens a conductor session. Then add players:
60
60
 
61
61
  ```bash
62
- claude-tempo start # open a player session
63
62
  claude-tempo status # see who's active
64
63
  ```
65
64
 
66
- Or ask the conductor to `recruit` players from inside Claude Code.
65
+ Or use the TUI to recruit players, or ask the conductor to `recruit` from inside Claude Code.
67
66
 
68
67
  ### Manual setup
69
68
 
@@ -71,8 +70,7 @@ Or ask the conductor to `recruit` players from inside Claude Code.
71
70
  claude-tempo server # start Temporal dev server
72
71
  claude-tempo init # register MCP server globally
73
72
  claude-tempo preflight # verify environment
74
- claude-tempo conduct # start a conductor
75
- claude-tempo start # start a player
73
+ claude-tempo up # launch conductor via auto-provisioning
76
74
  ```
77
75
 
78
76
  ## Upgrading
@@ -90,12 +88,15 @@ claude-tempo upgrade 0.22.0
90
88
  ## Stopping & Tear Down
91
89
 
92
90
  ```bash
93
- # Stop a specific player session
94
- claude-tempo stop my-ensemble player-name
91
+ # Terminate all sessions in an ensemble
92
+ claude-tempo destroy my-ensemble
95
93
 
96
94
  # Tear down everything (all sessions, schedulers, and Maestro workflows)
97
95
  claude-tempo down --all
98
96
 
97
+ # Tear down and terminate all workflows in one step
98
+ claude-tempo down --destroy -y
99
+
99
100
  # Stop the background daemon
100
101
  claude-tempo daemon stop
101
102
  ```
@@ -107,17 +108,17 @@ claude-tempo daemon stop
107
108
  ## Core Concepts
108
109
 
109
110
  - **Player** — A Claude Code session registered as a Temporal workflow
110
- - **Conductor** — An optional orchestration hub (one per ensemble); receives `report` calls and connects to external interfaces
111
+ - **Conductor** — Required orchestration hub (one per ensemble); receives `report` calls and connects to external interfaces. Lineup schema enforces its presence.
111
112
  - **Ensemble** — A named group of players isolated from other ensembles; defaults to `default`
112
113
  - **Cue** — A message sent to a player by name via Temporal signal
113
114
  - **Lineup** — A YAML file that defines a full team and recruits them in one step
114
115
  - **Player Type** — A reusable agent definition (`.md` with YAML frontmatter) that gives a player a named role
115
116
 
116
- Players in one ensemble cannot see or message players in another:
117
+ Players in one ensemble cannot see or message players in another. Launch `claude-tempo` to open the TUI and switch between ensembles, or target a specific ensemble directly:
117
118
 
118
119
  ```bash
119
- claude-tempo conduct frontend # conduct the "frontend" ensemble
120
- claude-tempo start backend # join the "backend" ensemble
120
+ claude-tempo up frontend # provision and launch conductor in "frontend"
121
+ claude-tempo up backend # provision and launch conductor in "backend"
121
122
  ```
122
123
 
123
124
  ## MCP Tools
@@ -139,17 +140,16 @@ Tools available inside Claude Code sessions connected to claude-tempo:
139
140
  ## CLI
140
141
 
141
142
  ```bash
142
- claude-tempo up [ensemble] # first-time setup
143
- claude-tempo conduct [ensemble] # start a conductor
144
- claude-tempo start [ensemble] # start a player
143
+ claude-tempo # launch TUI (auto-provisions on first run)
144
+ claude-tempo up [ensemble] # provision infrastructure and launch conductor
145
+ claude-tempo down [--destroy] # tear down infrastructure (--destroy also terminates workflows)
145
146
  claude-tempo status [ensemble] # list active sessions
147
+ claude-tempo destroy <ensemble> # terminate all sessions in an ensemble
148
+ claude-tempo restore <ensemble> # restore orphaned sessions on this host
146
149
  claude-tempo hosts # list daemons polling this Temporal namespace (--all/--json)
147
- claude-tempo recall <name> # read a player's message history (--limit/--offset/--preview/--from/--since/--include-sent/--json)
150
+ claude-tempo recall <name> # read a player's message history (--limit/--offset/--preview/--json)
148
151
  claude-tempo attachment-info <name> # inspect a session's phase, holder, lease, and heartbeat age
149
152
  claude-tempo release [ensemble] # release held players (unlock + deliver tasks)
150
- claude-tempo pause [ensemble] # pause all sessions and the scheduler
151
- claude-tempo resume [ensemble] # resume a paused ensemble (--release also releases held players)
152
- claude-tempo tui # open the terminal UI
153
153
  claude-tempo daemon <sub> # manage the worker daemon
154
154
  claude-tempo upgrade # update to latest
155
155
  ```
@@ -240,7 +240,7 @@ claude-tempo tui --ensemble my-ensemble # direct ensemble mode
240
240
  The TUI provides a chat-focused shell for managing your ensemble:
241
241
 
242
242
  - **Ensemble chat feed** — live aggregated view of conductor + player traffic; type bare text to message the conductor, `@player message` to message directly
243
- - **Slash commands** — `/recruit`, `/status`, `/schedule`, `/gates`, `/stages`, `/worktree`, `/go` (release held), `/pause`, `/resume`, and more; type `/help` for the full list
243
+ - **Slash commands** — `/recruit`, `/status`, `/schedule`, `/gates`, `/stages`, `/worktree`, `/go` (release held), `/pause`, `/play`, `/shutdown`, `/restore`, `/home`, and more; type `/help` for the full list
244
244
  - **Interactive overlays and wizards** — step-by-step flows for recruiting players, creating schedules, and managing ensembles
245
245
 
246
246
  📖 [TUI reference → docs/tui.md](docs/tui.md)
@@ -249,10 +249,10 @@ The TUI provides a chat-focused shell for managing your ensemble:
249
249
 
250
250
  > **Experimental** — subject to breaking changes.
251
251
 
252
- GitHub Copilot CLI sessions can join an ensemble using `--agent copilot`:
252
+ GitHub Copilot CLI sessions can join an ensemble using `--agent copilot`. Recruit one from the TUI:
253
253
 
254
- ```bash
255
- claude-tempo start myband --agent copilot -n copilot-1
254
+ ```
255
+ /recruit copilot-1 --agent copilot
256
256
  ```
257
257
 
258
258
  📖 [Copilot bridge setup and limitations → docs/copilot.md](docs/copilot.md)
@@ -575,15 +575,22 @@ function createOutboxActivities(client, config) {
575
575
  // already exists at `~/.claude/projects/<encoded-path>/<uuid>.jsonl`
576
576
  // ("Session ID already in use"). A prior failed spawn can leave that
577
577
  // file behind, wedging every subsequent `fresh` restart that reuses the
578
- // stored sessionId. Since `fresh` already skips `--resume`, we mint a
579
- // new UUID and persist it on the target's metadata so later non-fresh
580
- // restarts resume against the new transcript. Non-fresh restarts keep
581
- // the stored sessionId for deterministic `--resume`.
582
- let spawnSessionId = metadata.sessionId;
583
- if (fresh) {
584
- spawnSessionId = crypto.randomUUID();
585
- await handle.signal(signals_1.updateMetadataSignal, { sessionId: spawnSessionId });
586
- }
578
+ // stored sessionId.
579
+ //
580
+ // #306: `/restart` ALWAYS spawns a fresh Claude Code process — never
581
+ // `--resume <id>`. The transcript `.jsonl` for the prior `spawnSessionId`
582
+ // is NOT guaranteed to have been flushed to disk before the prior
583
+ // adapter was hard-terminated (Windows `taskkill /T /F` is synchronous
584
+ // and unconditional). Claude Code then errors out with "No conversation
585
+ // found with session ID" and the new terminal drops to shell.
586
+ //
587
+ // Context preservation is already handled by the Step 5 replay above —
588
+ // we re-send recent messages to the fresh session. That's authoritative;
589
+ // the session-id `--resume` path was only ever a bonus on top. So we
590
+ // mint a new UUID on every restart, persist it to metadata, and never
591
+ // pass `resume: true` to the spawn.
592
+ const spawnSessionId = crypto.randomUUID();
593
+ await handle.signal(signals_1.updateMetadataSignal, { sessionId: spawnSessionId });
587
594
  // Issue #184: re-resolve on the invoker host against the session's
588
595
  // workDir (NOT the daemon's process.cwd — daemon runs elsewhere than
589
596
  // the session's project, so the project-tier lookup needs the session's
@@ -598,8 +605,9 @@ function createOutboxActivities(client, config) {
598
605
  host: targetHost,
599
606
  attachmentId: token.attachmentId,
600
607
  runId: token.runId,
601
- resume: !fresh,
602
- ...(spawnSessionId ? { sessionId: spawnSessionId } : {}),
608
+ // #306: `/restart` is always a fresh spawn (see comment above).
609
+ resume: false,
610
+ sessionId: spawnSessionId,
603
611
  adapterId,
604
612
  ...(resolved ? {
605
613
  agentDefinition: resolved.name,
@@ -608,7 +616,7 @@ function createOutboxActivities(client, config) {
608
616
  } : {}),
609
617
  }],
610
618
  });
611
- log(`Restart prepared for "${targetPlayerId}" — attachmentId=${token.attachmentId}, spawnEntryId=${spawnEntryId}, host=${targetHost}${fresh ? ` (fresh sessionId=${spawnSessionId})` : ''}`);
619
+ log(`Restart prepared for "${targetPlayerId}" — attachmentId=${token.attachmentId}, spawnEntryId=${spawnEntryId}, host=${targetHost}, fresh sessionId=${spawnSessionId}${fresh ? ' (context replay skipped)' : ''}`);
612
620
  return { success: true };
613
621
  }
614
622
  catch (err) {
@@ -1,28 +1,5 @@
1
1
  import { CliOverrides } from '../config';
2
2
  import { AgentType } from '../types';
3
- interface StartOpts extends CliOverrides {
4
- ensemble: string;
5
- conductor: boolean;
6
- replace?: boolean;
7
- resume?: boolean;
8
- name?: string;
9
- skipPreflight?: boolean;
10
- agent: AgentType;
11
- dir?: string;
12
- /**
13
- * Issue #172: `conduct --lineup <name>` — load a lineup during conductor
14
- * startup and apply the same initial-startup semantics as `up --lineup`:
15
- * conductor instructions deferred, players held, banner shown. Only
16
- * meaningful when `conductor: true`.
17
- */
18
- lineup?: string;
19
- /**
20
- * Issue #172: opt out of the defer-conductor-instructions behavior.
21
- * Forces the legacy immediate-start path even on `conduct --lineup`.
22
- */
23
- noHold?: boolean;
24
- }
25
- export declare function start(opts: StartOpts): Promise<void>;
26
3
  interface StatusOpts extends CliOverrides {
27
4
  ensemble?: string;
28
5
  }
@@ -50,24 +27,16 @@ interface UpOpts extends CliOverrides {
50
27
  }
51
28
  export declare function up(opts: UpOpts): Promise<void>;
52
29
  interface DownOpts extends CliOverrides {
53
- /** Explicitly specified ensemble name. If undefined, auto-detect from running workflows. */
54
- ensemble?: string;
55
- all: boolean;
56
30
  removeMcp: boolean;
57
31
  keepDaemon: boolean;
58
32
  yes: boolean;
33
+ /** When true, terminate every workflow across every ensemble before
34
+ * stopping infra. Without it, workflows stay on the Temporal server and
35
+ * resume on the next `up`. */
36
+ destroy: boolean;
59
37
  dir: string;
60
38
  }
61
39
  export declare function down(opts: DownOpts): Promise<void>;
62
- interface StopOpts extends CliOverrides {
63
- /** Stop a specific player by name. */
64
- name?: string;
65
- /** Stop all sessions in this ensemble. */
66
- ensemble?: string;
67
- /** Stop every session across all ensembles. */
68
- all?: boolean;
69
- }
70
- export declare function stop(opts: StopOpts): Promise<void>;
71
40
  interface AgentTypesCommandOpts {
72
41
  subcommand?: string;
73
42
  name?: string;
@@ -84,27 +53,17 @@ interface VerbOpts extends CliOverrides {
84
53
  name: string;
85
54
  ensemble?: string;
86
55
  }
87
- interface RestartCliOpts extends VerbOpts {
88
- host?: string;
89
- fresh?: boolean;
90
- force?: boolean;
91
- contextMessages?: number;
92
- /** PR-F: required when force-restarting a session attached to a different host. */
93
- yesSteal?: string;
94
- }
95
- interface DetachCliOpts extends VerbOpts {
96
- deadlineMs?: number;
97
- }
98
- interface DestroyCliOpts extends VerbOpts {
99
- reason?: string;
100
- }
101
- interface MigrateCliOpts extends RestartCliOpts {
102
- host: string;
56
+ interface DestroyCliOpts extends CliOverrides {
57
+ ensemble: string;
58
+ yes: boolean;
103
59
  }
104
- export declare function restart(opts: RestartCliOpts): Promise<void>;
105
- export declare function detach(opts: DetachCliOpts): Promise<void>;
60
+ /**
61
+ * `claude-tempo destroy <ensemble> [-y]` — terminate every workflow in an
62
+ * ensemble (#288). Prompts with the ensemble name and workflow count unless
63
+ * `-y` is passed. The per-player destroy path lives in the TUI (`/destroy
64
+ * --player`).
65
+ */
106
66
  export declare function destroy(opts: DestroyCliOpts): Promise<void>;
107
- export declare function migrate(opts: MigrateCliOpts): Promise<void>;
108
67
  export declare function attachmentInfo(opts: VerbOpts): Promise<void>;
109
68
  export interface HostsCliOpts extends CliOverrides {
110
69
  ensemble?: string;
@@ -142,16 +101,15 @@ export interface RecallCliOpts extends VerbOpts {
142
101
  }
143
102
  export declare function recall(opts: RecallCliOpts): Promise<void>;
144
103
  interface RestoreCliOpts extends CliOverrides {
145
- /** Specific player name to restore. Omitted means "interactive picker". */
146
- name?: string;
147
- ensemble?: string;
148
- /** Restore every orphan in the namespace, respecting allowlist. */
149
- all?: boolean;
150
- /** Filter to orphans whose `preferredHost` matches this value. */
151
- fromHost?: string;
152
- /** List candidates without restoring. */
153
- dryRun?: boolean;
104
+ ensemble: string;
154
105
  }
106
+ /**
107
+ * `claude-tempo restore <ensemble>` — delegate to {@link TempoClient.restore},
108
+ * which reattaches orphans AND unpauses maestro + scheduler (#298 — the
109
+ * direct-to-`restoreOrphansOnce` path left the ensemble paused after a
110
+ * `shutdown → restore` roundtrip). The TUI home view (#290) is the picker
111
+ * surface; the CLI is the scriptable bulk operation, one ensemble at a time.
112
+ */
155
113
  export declare function restore(opts: RestoreCliOpts): Promise<void>;
156
114
  interface EnsembleCommandOpts extends CliOverrides {
157
115
  subcommand?: string;
@@ -163,14 +121,4 @@ interface ReleaseOpts extends CliOverrides {
163
121
  }
164
122
  /** Release all held sessions in an ensemble (unlock outbox, deliver initial messages). */
165
123
  export declare function release(opts: ReleaseOpts): Promise<void>;
166
- interface PauseResumeOpts extends CliOverrides {
167
- ensemble: string;
168
- /** Issue #172: when true on `resume`, also fan out `releaseHeldSignal` to every
169
- * session so deferred task messages are delivered and outboxes unlocked in one shot. */
170
- release?: boolean;
171
- }
172
- /** Pause an entire ensemble — sessions, scheduler, and maestro. */
173
- export declare function pause(opts: PauseResumeOpts): Promise<void>;
174
- /** Resume an entire ensemble — sessions, scheduler, and maestro. */
175
- export declare function resume(opts: PauseResumeOpts): Promise<void>;
176
124
  export {};