@proletariat/cli 0.3.43 → 0.3.45

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.
@@ -1,8 +1,7 @@
1
1
  import { Flags } from '@oclif/core';
2
2
  import chalk from 'chalk';
3
- import * as path from 'node:path';
4
3
  import * as fs from 'node:fs';
5
- import { getWorkspaceInfo, getAllAgentsStatus, getAgentTmuxSessions } from '../../lib/agents/commands.js';
4
+ import { getWorkspaceInfo, getAllAgentsStatus, getAgentTmuxSessions, resolveAgentDir } from '../../lib/agents/commands.js';
6
5
  import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
7
6
  import { shouldOutputJson } from '../../lib/prompt-json.js';
8
7
  export default class List extends PMOCommand {
@@ -139,7 +138,7 @@ export default class List extends PMOCommand {
139
138
  }
140
139
  }
141
140
  else {
142
- const agentDir = path.join(workspaceInfo.agentsPath, agentStatus.name);
141
+ const agentDir = resolveAgentDir(workspaceInfo, agentStatus.name);
143
142
  const dirExists = fs.existsSync(agentDir);
144
143
  if (dirExists) {
145
144
  this.log(chalk.red(` Invalid or broken worktrees`));
@@ -3,7 +3,7 @@ import * as path from 'node:path';
3
3
  import * as fs from 'node:fs';
4
4
  import { execSync } from 'node:child_process';
5
5
  import { colors } from '../../lib/colors.js';
6
- import { getWorkspaceInfo, formatAgentList } from '../../lib/agents/commands.js';
6
+ import { getWorkspaceInfo, formatAgentList, resolveAgentDir } from '../../lib/agents/commands.js';
7
7
  import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
8
8
  import { isDockerRunning, getAgentContainerName, isContainerRunning, getContainerId, } from '../../lib/execution/runners.js';
9
9
  import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
@@ -74,7 +74,7 @@ export default class Login extends PMOCommand {
74
74
  if (!agent) {
75
75
  this.error(`Agent "${agentName}" not found. Available: ${formatAgentList(workspaceInfo.agents)}`);
76
76
  }
77
- const agentDir = path.join(workspaceInfo.agentsPath, agentName);
77
+ const agentDir = resolveAgentDir(workspaceInfo, agentName);
78
78
  // Check if Docker config exists
79
79
  const dockerfilePath = path.join(agentDir, '.devcontainer', 'Dockerfile');
80
80
  if (!fs.existsSync(dockerfilePath)) {
@@ -4,7 +4,7 @@ import { promisify } from 'node:util';
4
4
  import * as path from 'node:path';
5
5
  import * as fs from 'node:fs';
6
6
  import { colors } from '../../lib/colors.js';
7
- import { getWorkspaceInfo } from '../../lib/agents/commands.js';
7
+ import { getWorkspaceInfo, resolveAgentDir } from '../../lib/agents/commands.js';
8
8
  import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
9
9
  import { isDockerRunning } from '../../lib/execution/runners.js';
10
10
  import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
@@ -76,8 +76,7 @@ export default class AgentRebuild extends PMOCommand {
76
76
  this.error('Not in a proletariat workspace. Run `prlt init` first.');
77
77
  }
78
78
  this.log(colors.primary(`🔨 Rebuilding agent: ${agentName}\n`));
79
- const agentsPath = path.join(workspaceInfo.path, 'agents', 'staff');
80
- const agentDir = path.join(agentsPath, agentName);
79
+ const agentDir = resolveAgentDir(workspaceInfo, agentName);
81
80
  try {
82
81
  this.log(colors.textSecondary(' Building devcontainer...'));
83
82
  const buildCommand = [
@@ -4,7 +4,7 @@ import * as fs from 'node:fs';
4
4
  import { execSync, spawn } from 'node:child_process';
5
5
  import Database from 'better-sqlite3';
6
6
  import { colors } from '../../lib/colors.js';
7
- import { getWorkspaceInfo, getAgentTmuxSessions, formatAgentList } from '../../lib/agents/commands.js';
7
+ import { getWorkspaceInfo, getAgentTmuxSessions, formatAgentList, resolveAgentDir } from '../../lib/agents/commands.js';
8
8
  import { hasDevcontainerConfig } from '../../lib/execution/devcontainer.js';
9
9
  import { getTerminalApp } from '../../lib/execution/config.js';
10
10
  import { isDockerRunning, getAgentContainerName, isContainerRunning, getContainerId, } from '../../lib/execution/runners.js';
@@ -110,7 +110,7 @@ export default class Shell extends PMOCommand {
110
110
  // If 'continue', proceed with opening a new shell
111
111
  this.log(colors.warning('\nProceeding with new shell - be careful of conflicts!\n'));
112
112
  }
113
- const agentDir = path.join(workspaceInfo.agentsPath, agentName);
113
+ const agentDir = resolveAgentDir(workspaceInfo, agentName);
114
114
  // Check if agent has devcontainer
115
115
  const hasDevcontainer = hasDevcontainerConfig(agentDir);
116
116
  // In JSON mode with agent name provided, output combined config prompt
@@ -1,6 +1,6 @@
1
1
  import { Args } from '@oclif/core';
2
2
  import { colors, format } from '../../lib/colors.js';
3
- import { getWorkspaceInfo, getAgentStatus, getAllAgentsStatus, formatAgentList } from '../../lib/agents/commands.js';
3
+ import { getWorkspaceInfo, getAgentStatus, getAllAgentsStatus, formatAgentList, resolveAgentDir } from '../../lib/agents/commands.js';
4
4
  import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
5
5
  import { shouldOutputJson, outputErrorAsJson, outputSuccessAsJson, createMetadata, } from '../../lib/prompt-json.js';
6
6
  export default class Status extends PMOCommand {
@@ -83,7 +83,7 @@ export default class Status extends PMOCommand {
83
83
  name: agentName,
84
84
  type: agent.type,
85
85
  exists: agentStatus.exists,
86
- path: `${workspaceInfo.agentsPath}/${agentName}`,
86
+ path: resolveAgentDir(workspaceInfo, agentName),
87
87
  branch: agentStatus.branch,
88
88
  repositories: agentStatus.repositories.map(r => ({
89
89
  name: r.name,
@@ -107,7 +107,7 @@ export default class Status extends PMOCommand {
107
107
  return;
108
108
  }
109
109
  // Location
110
- this.log(`📍 Location: ${colors.path(`${workspaceInfo.agentsPath}/${agentName}`)}`);
110
+ this.log(`📍 Location: ${colors.path(resolveAgentDir(workspaceInfo, agentName))}`);
111
111
  // Branch info
112
112
  if (agentStatus.branch) {
113
113
  this.log(`🌿 Branch: ${colors.warning(agentStatus.branch)}`);
@@ -1,7 +1,7 @@
1
1
  import { Args } from '@oclif/core';
2
2
  import * as path from 'node:path';
3
3
  import { colors } from '../../lib/colors.js';
4
- import { getWorkspaceInfo, formatAgentList } from '../../lib/agents/commands.js';
4
+ import { getWorkspaceInfo, formatAgentList, resolveAgentDir } from '../../lib/agents/commands.js';
5
5
  import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
6
6
  import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
7
7
  export default class Visit extends PMOCommand {
@@ -74,7 +74,7 @@ export default class Visit extends PMOCommand {
74
74
  return handleError('AGENT_NOT_FOUND', `Agent "${agentName}" not found. Available: ${formatAgentList(workspaceInfo.agents)}`);
75
75
  }
76
76
  // Calculate path to agent directory
77
- const agentDir = path.join(workspaceInfo.agentsPath, agentName);
77
+ const agentDir = resolveAgentDir(workspaceInfo, agentName);
78
78
  const relativePath = path.relative(process.cwd(), agentDir);
79
79
  // Display navigation command
80
80
  this.log(colors.primary(`🤖 Visiting agent: ${agentName}`));
@@ -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
+ }