@proletariat/cli 0.3.42 → 0.3.44

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.
@@ -0,0 +1,13 @@
1
+ import { PromptCommand } from '../../lib/prompt-command.js';
2
+ export default class OrchestratorAttach extends PromptCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ 'current-terminal': import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
+ terminal: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
8
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
+ machine: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ };
11
+ run(): Promise<void>;
12
+ private openInNewTab;
13
+ }
@@ -0,0 +1,140 @@
1
+ import { Flags } from '@oclif/core';
2
+ import { execSync } from 'node:child_process';
3
+ import * as path from 'node:path';
4
+ import * as fs from 'node:fs';
5
+ import * as os from 'node:os';
6
+ import { PromptCommand } from '../../lib/prompt-command.js';
7
+ import { machineOutputFlags } from '../../lib/pmo/index.js';
8
+ import { shouldOutputJson, outputErrorAsJson, outputSuccessAsJson, createMetadata, } from '../../lib/prompt-json.js';
9
+ import { styles } from '../../lib/styles.js';
10
+ import { getHostTmuxSessionNames } from '../../lib/execution/session-utils.js';
11
+ import { ORCHESTRATOR_SESSION_NAME } from './start.js';
12
+ export default class OrchestratorAttach extends PromptCommand {
13
+ static description = 'Attach to the running orchestrator tmux session';
14
+ static examples = [
15
+ '<%= config.bin %> <%= command.id %>',
16
+ '<%= config.bin %> <%= command.id %> --current-terminal',
17
+ ];
18
+ static flags = {
19
+ ...machineOutputFlags,
20
+ 'current-terminal': Flags.boolean({
21
+ char: 'c',
22
+ description: 'Attach in current terminal instead of new tab',
23
+ default: false,
24
+ }),
25
+ terminal: Flags.string({
26
+ char: 't',
27
+ description: 'Terminal app to use (iTerm, Terminal, Ghostty)',
28
+ default: 'iTerm',
29
+ }),
30
+ };
31
+ async run() {
32
+ const { flags } = await this.parse(OrchestratorAttach);
33
+ const jsonMode = shouldOutputJson(flags);
34
+ // Check if orchestrator session exists
35
+ const hostSessions = getHostTmuxSessionNames();
36
+ if (!hostSessions.includes(ORCHESTRATOR_SESSION_NAME)) {
37
+ if (jsonMode) {
38
+ outputErrorAsJson('NOT_RUNNING', 'Orchestrator is not running. Start it with: prlt orchestrator start', createMetadata('orchestrator attach', flags));
39
+ return;
40
+ }
41
+ this.log('');
42
+ this.log(styles.warning('Orchestrator is not running.'));
43
+ this.log(styles.muted('Start it with: prlt orchestrator start'));
44
+ this.log('');
45
+ return;
46
+ }
47
+ if (jsonMode) {
48
+ outputSuccessAsJson({
49
+ sessionId: ORCHESTRATOR_SESSION_NAME,
50
+ status: 'attaching',
51
+ }, createMetadata('orchestrator attach', flags));
52
+ return;
53
+ }
54
+ this.log('');
55
+ this.log(styles.info(`Attaching to orchestrator session: ${ORCHESTRATOR_SESSION_NAME}`));
56
+ if (flags['current-terminal']) {
57
+ try {
58
+ execSync(`tmux attach -t "${ORCHESTRATOR_SESSION_NAME}"`, { stdio: 'inherit' });
59
+ }
60
+ catch {
61
+ this.error(`Failed to attach to orchestrator session "${ORCHESTRATOR_SESSION_NAME}"`);
62
+ }
63
+ }
64
+ else {
65
+ await this.openInNewTab(flags.terminal);
66
+ }
67
+ }
68
+ async openInNewTab(terminalApp) {
69
+ const title = 'Orchestrator';
70
+ const attachCmd = `tmux attach -t "${ORCHESTRATOR_SESSION_NAME}"`;
71
+ const baseDir = path.join(os.homedir(), '.proletariat', 'scripts');
72
+ fs.mkdirSync(baseDir, { recursive: true });
73
+ const scriptPath = path.join(baseDir, `attach-orch-${Date.now()}.sh`);
74
+ const script = `#!/bin/bash
75
+ # Set terminal tab title
76
+ echo -ne "\\033]0;${title}\\007"
77
+ echo -ne "\\033]1;${title}\\007"
78
+
79
+ echo "Attaching to: ${ORCHESTRATOR_SESSION_NAME}"
80
+ ${attachCmd}
81
+
82
+ # Clean up
83
+ rm -f "${scriptPath}"
84
+ exec $SHELL
85
+ `;
86
+ fs.writeFileSync(scriptPath, script, { mode: 0o755 });
87
+ try {
88
+ switch (terminalApp) {
89
+ case 'iTerm':
90
+ execSync(`osascript -e '
91
+ tell application "iTerm"
92
+ activate
93
+ tell current window
94
+ set newTab to (create tab with default profile)
95
+ tell current session of newTab
96
+ set name to "${title}"
97
+ write text "${scriptPath}"
98
+ end tell
99
+ end tell
100
+ end tell
101
+ '`);
102
+ break;
103
+ case 'Ghostty':
104
+ execSync(`osascript -e '
105
+ tell application "Ghostty"
106
+ activate
107
+ end tell
108
+ tell application "System Events"
109
+ tell process "Ghostty"
110
+ keystroke "t" using command down
111
+ delay 0.3
112
+ keystroke "${scriptPath}"
113
+ keystroke return
114
+ end tell
115
+ end tell
116
+ '`);
117
+ break;
118
+ case 'Terminal':
119
+ default:
120
+ execSync(`osascript -e '
121
+ tell application "Terminal"
122
+ activate
123
+ tell application "System Events"
124
+ tell process "Terminal"
125
+ keystroke "t" using command down
126
+ end tell
127
+ end tell
128
+ delay 0.3
129
+ do script "${scriptPath}" in front window
130
+ end tell
131
+ '`);
132
+ break;
133
+ }
134
+ this.log(styles.success('Opened new tab and attaching to orchestrator'));
135
+ }
136
+ catch (error) {
137
+ this.error(`Failed to open terminal tab: ${error instanceof Error ? error.message : error}`);
138
+ }
139
+ }
140
+ }
@@ -0,0 +1,14 @@
1
+ import { PMOCommand } from '../../lib/pmo/index.js';
2
+ export default class Orchestrator extends PMOCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
+ machine: import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
+ project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ };
10
+ protected getPMOOptions(): {
11
+ promptIfMultiple: boolean;
12
+ };
13
+ execute(): Promise<void>;
14
+ }
@@ -0,0 +1,51 @@
1
+ import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
2
+ import { shouldOutputJson } from '../../lib/prompt-json.js';
3
+ export default class Orchestrator extends PMOCommand {
4
+ static description = 'Manage the orchestrator agent (start, attach, status, stop)';
5
+ static examples = [
6
+ '<%= config.bin %> <%= command.id %>',
7
+ '<%= config.bin %> <%= command.id %> start',
8
+ '<%= config.bin %> <%= command.id %> attach',
9
+ '<%= config.bin %> <%= command.id %> status',
10
+ '<%= config.bin %> <%= command.id %> stop',
11
+ ];
12
+ static flags = {
13
+ ...pmoBaseFlags,
14
+ };
15
+ getPMOOptions() {
16
+ return { promptIfMultiple: false };
17
+ }
18
+ async execute() {
19
+ const { flags } = await this.parse(Orchestrator);
20
+ const jsonModeConfig = shouldOutputJson(flags) ? { flags, commandName: 'orchestrator' } : null;
21
+ const { action } = await this.prompt([{
22
+ type: 'list',
23
+ name: 'action',
24
+ message: 'Orchestrator - What would you like to do?',
25
+ choices: [
26
+ { name: 'Start orchestrator', value: 'start', command: 'prlt orchestrator start --json' },
27
+ { name: 'Attach to orchestrator', value: 'attach', command: 'prlt orchestrator attach --json' },
28
+ { name: 'Check orchestrator status', value: 'status', command: 'prlt orchestrator status --json' },
29
+ { name: 'Stop orchestrator', value: 'stop', command: 'prlt orchestrator stop --json' },
30
+ { name: 'Cancel', value: 'cancel' },
31
+ ],
32
+ }], jsonModeConfig);
33
+ if (action === 'cancel') {
34
+ return;
35
+ }
36
+ switch (action) {
37
+ case 'start':
38
+ await this.config.runCommand('orchestrator:start', []);
39
+ break;
40
+ case 'attach':
41
+ await this.config.runCommand('orchestrator:attach', []);
42
+ break;
43
+ case 'status':
44
+ await this.config.runCommand('orchestrator:status', []);
45
+ break;
46
+ case 'stop':
47
+ await this.config.runCommand('orchestrator:stop', []);
48
+ break;
49
+ }
50
+ }
51
+ }
@@ -0,0 +1,17 @@
1
+ import { PromptCommand } from '../../lib/prompt-command.js';
2
+ export declare const ORCHESTRATOR_SESSION_NAME = "prlt-orchestrator-main";
3
+ export default class OrchestratorStart extends PromptCommand {
4
+ static description: string;
5
+ static examples: string[];
6
+ static flags: {
7
+ prompt: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ action: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ executor: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ 'skip-permissions': import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ sandboxed: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
+ background: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
+ machine: import("@oclif/core/interfaces").BooleanFlag<boolean>;
15
+ };
16
+ run(): Promise<void>;
17
+ }
@@ -0,0 +1,287 @@
1
+ import { Flags } from '@oclif/core';
2
+ import * as path from 'node:path';
3
+ import * as fs from 'node:fs';
4
+ import Database from 'better-sqlite3';
5
+ import { PromptCommand } from '../../lib/prompt-command.js';
6
+ import { machineOutputFlags } from '../../lib/pmo/index.js';
7
+ import { findHQRoot } from '../../lib/workspace.js';
8
+ import { shouldOutputJson, outputErrorAsJson, outputSuccessAsJson, createMetadata, buildPromptConfig, outputPromptAsJson, } from '../../lib/prompt-json.js';
9
+ import { styles } from '../../lib/styles.js';
10
+ import { DEFAULT_EXECUTION_CONFIG, } from '../../lib/execution/types.js';
11
+ import { runExecution } from '../../lib/execution/runners.js';
12
+ import { getHostTmuxSessionNames } from '../../lib/execution/session-utils.js';
13
+ import { ExecutionStorage } from '../../lib/execution/storage.js';
14
+ import { loadExecutionConfig, getTerminalApp, promptTerminalPreference, getShell, promptShellPreference, hasTerminalPreference, hasShellPreference, } from '../../lib/execution/config.js';
15
+ export const ORCHESTRATOR_SESSION_NAME = 'prlt-orchestrator-main';
16
+ export default class OrchestratorStart extends PromptCommand {
17
+ static description = 'Start the orchestrator agent in a tmux session';
18
+ static examples = [
19
+ '<%= config.bin %> <%= command.id %>',
20
+ '<%= config.bin %> <%= command.id %> --executor codex',
21
+ '<%= config.bin %> <%= command.id %> --skip-permissions',
22
+ '<%= config.bin %> <%= command.id %> --prompt "coordinate all agents on TKT-100"',
23
+ '<%= config.bin %> <%= command.id %> --background',
24
+ ];
25
+ static flags = {
26
+ ...machineOutputFlags,
27
+ prompt: Flags.string({
28
+ char: 'p',
29
+ description: 'Initial prompt for the orchestrator',
30
+ }),
31
+ action: Flags.string({
32
+ char: 'A',
33
+ description: 'Load an action by name from the actions table (uses its prompt)',
34
+ }),
35
+ executor: Flags.string({
36
+ char: 'e',
37
+ description: 'Executor type',
38
+ options: ['claude-code', 'codex', 'aider', 'custom'],
39
+ }),
40
+ 'skip-permissions': Flags.boolean({
41
+ description: 'Run with --dangerously-skip-permissions',
42
+ default: false,
43
+ exclusive: ['sandboxed'],
44
+ }),
45
+ sandboxed: Flags.boolean({
46
+ description: 'Run in sandboxed mode (requires approval for dangerous operations)',
47
+ default: false,
48
+ exclusive: ['skip-permissions'],
49
+ }),
50
+ background: Flags.boolean({
51
+ char: 'b',
52
+ description: 'Start detached (don\'t open terminal tab)',
53
+ default: false,
54
+ }),
55
+ };
56
+ async run() {
57
+ const { flags } = await this.parse(OrchestratorStart);
58
+ const jsonMode = shouldOutputJson(flags);
59
+ // Check if orchestrator is already running
60
+ const hostSessions = getHostTmuxSessionNames();
61
+ if (hostSessions.includes(ORCHESTRATOR_SESSION_NAME)) {
62
+ if (jsonMode) {
63
+ outputErrorAsJson('ALREADY_RUNNING', `Orchestrator is already running (session: ${ORCHESTRATOR_SESSION_NAME}). Use "prlt orchestrator attach" to reattach.`, createMetadata('orchestrator start', flags));
64
+ return;
65
+ }
66
+ this.log('');
67
+ this.log(styles.warning(`Orchestrator is already running (session: ${ORCHESTRATOR_SESSION_NAME})`));
68
+ this.log('');
69
+ const { choice } = await this.prompt([{
70
+ type: 'list',
71
+ name: 'choice',
72
+ message: 'What would you like to do?',
73
+ choices: [
74
+ { name: 'Attach to running orchestrator', value: 'attach', command: 'prlt orchestrator attach --json' },
75
+ { name: 'Cancel', value: 'cancel' },
76
+ ],
77
+ }], jsonMode ? { flags, commandName: 'orchestrator start' } : null);
78
+ if (choice === 'attach') {
79
+ await this.config.runCommand('orchestrator:attach', []);
80
+ }
81
+ return;
82
+ }
83
+ // Resolve HQ path
84
+ const hqPath = findHQRoot(process.cwd());
85
+ if (!hqPath) {
86
+ if (jsonMode) {
87
+ outputErrorAsJson('NO_HQ', 'Not in an HQ workspace. Run "prlt init" first.', createMetadata('orchestrator start', flags));
88
+ return;
89
+ }
90
+ this.error('Not in an HQ workspace. Run "prlt init" first.');
91
+ }
92
+ // Executor selection
93
+ let selectedExecutor;
94
+ if (flags.executor) {
95
+ selectedExecutor = flags.executor;
96
+ }
97
+ else {
98
+ const executorChoices = [
99
+ { name: 'Claude Code', value: 'claude-code', command: 'prlt orchestrator start --executor claude-code --json' },
100
+ { name: 'Codex', value: 'codex', command: 'prlt orchestrator start --executor codex --json' },
101
+ { name: 'Aider', value: 'aider', command: 'prlt orchestrator start --executor aider --json' },
102
+ { name: 'Custom', value: 'custom', command: 'prlt orchestrator start --executor custom --json' },
103
+ ];
104
+ const executorMessage = 'Select executor:';
105
+ if (jsonMode) {
106
+ outputPromptAsJson(buildPromptConfig('list', 'executor', executorMessage, executorChoices), createMetadata('orchestrator start', flags));
107
+ return;
108
+ }
109
+ const { executor } = await this.prompt([{
110
+ type: 'list',
111
+ name: 'executor',
112
+ message: executorMessage,
113
+ choices: executorChoices,
114
+ }]);
115
+ selectedExecutor = executor;
116
+ }
117
+ // Permission mode selection
118
+ let sandboxed;
119
+ if (flags['skip-permissions']) {
120
+ sandboxed = false;
121
+ }
122
+ else if (flags.sandboxed) {
123
+ sandboxed = true;
124
+ }
125
+ else {
126
+ const permissionChoices = [
127
+ { name: 'Sandboxed (requires approval for dangerous operations)', value: 'sandboxed', command: 'prlt orchestrator start --sandboxed --json' },
128
+ { name: 'Accept all (--dangerously-skip-permissions)', value: 'skip', command: 'prlt orchestrator start --skip-permissions --json' },
129
+ ];
130
+ const permissionMessage = 'Select permission mode:';
131
+ if (jsonMode) {
132
+ outputPromptAsJson(buildPromptConfig('list', 'permissionMode', permissionMessage, permissionChoices), createMetadata('orchestrator start', flags));
133
+ return;
134
+ }
135
+ const { permissionMode } = await this.prompt([{
136
+ type: 'list',
137
+ name: 'permissionMode',
138
+ message: permissionMessage,
139
+ choices: permissionChoices,
140
+ }]);
141
+ sandboxed = permissionMode === 'sandboxed';
142
+ }
143
+ // Resolve action prompt
144
+ let actionPrompt = flags.prompt;
145
+ let actionName = 'orchestrate';
146
+ if (flags.action && !actionPrompt) {
147
+ // Load action from DB
148
+ const dbPath = path.join(hqPath, '.proletariat', 'workspace.db');
149
+ if (fs.existsSync(dbPath)) {
150
+ let db = null;
151
+ try {
152
+ db = new Database(dbPath);
153
+ const row = db.prepare('SELECT prompt, name FROM actions WHERE id = ? OR name = ?').get(flags.action, flags.action);
154
+ if (row) {
155
+ actionPrompt = row.prompt;
156
+ actionName = row.name;
157
+ }
158
+ else {
159
+ if (jsonMode) {
160
+ outputErrorAsJson('ACTION_NOT_FOUND', `Action "${flags.action}" not found.`, createMetadata('orchestrator start', flags));
161
+ return;
162
+ }
163
+ this.error(`Action "${flags.action}" not found.`);
164
+ }
165
+ }
166
+ finally {
167
+ db?.close();
168
+ }
169
+ }
170
+ }
171
+ // Build execution context
172
+ // Use ticketId='prlt', actionName='orchestrator', agentName='main'
173
+ // so buildSessionName produces 'prlt-orchestrator-main'
174
+ const context = {
175
+ ticketId: 'prlt',
176
+ ticketTitle: 'Orchestrator',
177
+ agentName: 'main',
178
+ agentDir: hqPath,
179
+ worktreePath: hqPath,
180
+ branch: 'main',
181
+ actionName: 'orchestrator',
182
+ actionPrompt,
183
+ modifiesCode: false,
184
+ hqPath,
185
+ };
186
+ // Build execution config
187
+ const executionConfig = { ...DEFAULT_EXECUTION_CONFIG };
188
+ executionConfig.outputMode = 'interactive';
189
+ executionConfig.sandboxed = sandboxed;
190
+ // Load saved preferences from workspace DB
191
+ const dbPath = path.join(hqPath, '.proletariat', 'workspace.db');
192
+ let db = null;
193
+ try {
194
+ if (fs.existsSync(dbPath)) {
195
+ db = new Database(dbPath);
196
+ const savedConfig = loadExecutionConfig(db);
197
+ executionConfig.terminal = savedConfig.terminal;
198
+ executionConfig.shell = savedConfig.shell;
199
+ executionConfig.tmux = savedConfig.tmux;
200
+ }
201
+ }
202
+ catch {
203
+ // Ignore config loading errors, use defaults
204
+ }
205
+ // If no terminal preference saved, prompt for it (first run only)
206
+ if (!jsonMode && db) {
207
+ if (!hasTerminalPreference(db)) {
208
+ executionConfig.terminal.app = await promptTerminalPreference(db);
209
+ }
210
+ else {
211
+ executionConfig.terminal.app = await getTerminalApp(db);
212
+ }
213
+ if (!hasShellPreference(db)) {
214
+ executionConfig.shell = await promptShellPreference(db);
215
+ }
216
+ else {
217
+ executionConfig.shell = await getShell(db);
218
+ }
219
+ }
220
+ // Show what we're doing
221
+ if (!jsonMode) {
222
+ this.log('');
223
+ this.log(styles.muted(` Starting orchestrator...`));
224
+ this.log(styles.muted(` Executor: ${selectedExecutor}`));
225
+ this.log(styles.muted(` Permission mode: ${sandboxed ? 'sandboxed' : 'skip-permissions'}`));
226
+ this.log(styles.muted(` Directory: ${hqPath}`));
227
+ if (actionPrompt) {
228
+ this.log(styles.muted(` Prompt: "${actionPrompt.substring(0, 60)}${actionPrompt.length > 60 ? '...' : ''}"`));
229
+ }
230
+ this.log('');
231
+ }
232
+ // Launch orchestrator
233
+ const displayMode = flags.background ? 'background' : 'terminal';
234
+ const result = await runExecution('host', context, selectedExecutor, executionConfig, {
235
+ displayMode,
236
+ });
237
+ if (result.success) {
238
+ // Create execution record so `prlt session poke orchestrator "message"` works
239
+ if (db) {
240
+ try {
241
+ const executionStorage = new ExecutionStorage(db);
242
+ executionStorage.createExecution({
243
+ ticketId: 'ORCH',
244
+ agentName: 'orchestrator',
245
+ executor: selectedExecutor,
246
+ environment: 'host',
247
+ displayMode,
248
+ sandboxed,
249
+ sessionId: result.sessionId || ORCHESTRATOR_SESSION_NAME,
250
+ });
251
+ }
252
+ catch {
253
+ // Non-fatal: poke won't work but orchestrator is running
254
+ }
255
+ }
256
+ if (jsonMode) {
257
+ outputSuccessAsJson({
258
+ sessionId: result.sessionId || ORCHESTRATOR_SESSION_NAME,
259
+ executor: selectedExecutor,
260
+ sandboxed,
261
+ displayMode,
262
+ directory: hqPath,
263
+ }, createMetadata('orchestrator start', flags));
264
+ }
265
+ if (flags.background) {
266
+ this.log(styles.success(`Orchestrator started in background`));
267
+ this.log(styles.muted(` Session: ${result.sessionId || ORCHESTRATOR_SESSION_NAME}`));
268
+ this.log(styles.muted(` Attach with: prlt orchestrator attach`));
269
+ }
270
+ else {
271
+ this.log(styles.success(`Orchestrator started`));
272
+ if (result.sessionId) {
273
+ this.log(styles.muted(` Session: ${result.sessionId}`));
274
+ }
275
+ }
276
+ }
277
+ else {
278
+ if (jsonMode) {
279
+ outputErrorAsJson('EXECUTION_FAILED', `Failed to start orchestrator: ${result.error}`, createMetadata('orchestrator start', flags));
280
+ }
281
+ this.error(`Failed to start orchestrator: ${result.error}`);
282
+ }
283
+ if (db) {
284
+ db.close();
285
+ }
286
+ }
287
+ }
@@ -0,0 +1,12 @@
1
+ import { PromptCommand } from '../../lib/prompt-command.js';
2
+ export default class OrchestratorStatus extends PromptCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ peek: import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
+ lines: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
8
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
+ machine: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ };
11
+ run(): Promise<void>;
12
+ }
@@ -0,0 +1,63 @@
1
+ import { Flags } from '@oclif/core';
2
+ import { PromptCommand } from '../../lib/prompt-command.js';
3
+ import { machineOutputFlags } from '../../lib/pmo/index.js';
4
+ import { shouldOutputJson, outputSuccessAsJson, createMetadata, } from '../../lib/prompt-json.js';
5
+ import { styles } from '../../lib/styles.js';
6
+ import { getHostTmuxSessionNames, captureTmuxPane } from '../../lib/execution/session-utils.js';
7
+ import { ORCHESTRATOR_SESSION_NAME } from './start.js';
8
+ export default class OrchestratorStatus extends PromptCommand {
9
+ static description = 'Check if the orchestrator is running';
10
+ static examples = [
11
+ '<%= config.bin %> <%= command.id %>',
12
+ '<%= config.bin %> <%= command.id %> --peek',
13
+ '<%= config.bin %> <%= command.id %> --peek --lines 50',
14
+ ];
15
+ static flags = {
16
+ ...machineOutputFlags,
17
+ peek: Flags.boolean({
18
+ description: 'Show recent output from the orchestrator',
19
+ default: false,
20
+ }),
21
+ lines: Flags.integer({
22
+ description: 'Number of lines to show when peeking',
23
+ default: 20,
24
+ }),
25
+ };
26
+ async run() {
27
+ const { flags } = await this.parse(OrchestratorStatus);
28
+ const jsonMode = shouldOutputJson(flags);
29
+ const hostSessions = getHostTmuxSessionNames();
30
+ const isRunning = hostSessions.includes(ORCHESTRATOR_SESSION_NAME);
31
+ let recentOutput = null;
32
+ if (isRunning && flags.peek) {
33
+ recentOutput = captureTmuxPane(ORCHESTRATOR_SESSION_NAME, flags.lines);
34
+ }
35
+ if (jsonMode) {
36
+ outputSuccessAsJson({
37
+ running: isRunning,
38
+ sessionId: isRunning ? ORCHESTRATOR_SESSION_NAME : null,
39
+ ...(recentOutput !== null && { recentOutput }),
40
+ }, createMetadata('orchestrator status', flags));
41
+ return;
42
+ }
43
+ this.log('');
44
+ if (isRunning) {
45
+ this.log(styles.success(`Orchestrator is running`));
46
+ this.log(styles.muted(` Session: ${ORCHESTRATOR_SESSION_NAME}`));
47
+ this.log(styles.muted(` Attach: prlt orchestrator attach`));
48
+ this.log(styles.muted(` Poke: prlt session poke orchestrator "message"`));
49
+ if (recentOutput) {
50
+ this.log('');
51
+ this.log(styles.header('Recent output:'));
52
+ this.log(styles.muted('─'.repeat(60)));
53
+ this.log(recentOutput);
54
+ this.log(styles.muted('─'.repeat(60)));
55
+ }
56
+ }
57
+ else {
58
+ this.log(styles.muted('Orchestrator is not running.'));
59
+ this.log(styles.muted('Start it with: prlt orchestrator start'));
60
+ }
61
+ this.log('');
62
+ }
63
+ }
@@ -0,0 +1,11 @@
1
+ import { PromptCommand } from '../../lib/prompt-command.js';
2
+ export default class OrchestratorStop extends PromptCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
+ machine: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
+ };
10
+ run(): Promise<void>;
11
+ }