@proletariat/cli 0.3.48 → 0.3.49

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 (38) hide show
  1. package/dist/commands/claude/index.js +21 -21
  2. package/dist/commands/claude/open.js +1 -1
  3. package/dist/commands/config/index.js +2 -2
  4. package/dist/commands/execution/config.d.ts +2 -2
  5. package/dist/commands/execution/config.js +18 -18
  6. package/dist/commands/execution/list.js +2 -2
  7. package/dist/commands/execution/view.js +2 -2
  8. package/dist/commands/orchestrator/start.d.ts +1 -1
  9. package/dist/commands/orchestrator/start.js +19 -19
  10. package/dist/commands/qa/index.js +12 -12
  11. package/dist/commands/staff/add.js +1 -1
  12. package/dist/commands/work/linear.js +28 -5
  13. package/dist/commands/work/revise.js +8 -8
  14. package/dist/commands/work/spawn.js +1 -1
  15. package/dist/commands/work/start.js +12 -12
  16. package/dist/commands/work/watch.js +3 -3
  17. package/dist/lib/agents/index.js +2 -2
  18. package/dist/lib/database/drizzle-schema.d.ts +7 -7
  19. package/dist/lib/database/drizzle-schema.js +1 -1
  20. package/dist/lib/execution/config.d.ts +6 -6
  21. package/dist/lib/execution/config.js +17 -10
  22. package/dist/lib/execution/devcontainer.d.ts +3 -3
  23. package/dist/lib/execution/devcontainer.js +3 -3
  24. package/dist/lib/execution/runners.d.ts +2 -2
  25. package/dist/lib/execution/runners.js +23 -24
  26. package/dist/lib/execution/spawner.js +3 -3
  27. package/dist/lib/execution/storage.d.ts +2 -2
  28. package/dist/lib/execution/storage.js +3 -3
  29. package/dist/lib/execution/types.d.ts +2 -2
  30. package/dist/lib/execution/types.js +1 -1
  31. package/dist/lib/external-issues/linear.d.ts +6 -0
  32. package/dist/lib/external-issues/linear.js +63 -0
  33. package/dist/lib/pmo/schema.d.ts +1 -1
  34. package/dist/lib/pmo/schema.js +1 -1
  35. package/dist/lib/pmo/storage/base.js +31 -0
  36. package/dist/lib/repos/index.js +1 -1
  37. package/oclif.manifest.json +4318 -4313
  38. package/package.json +1 -1
@@ -143,8 +143,8 @@ export default class Claude extends PromptCommand {
143
143
  else {
144
144
  // Build devcontainer label
145
145
  const devcontainerLabel = hasProjectDevcontainer
146
- ? '🐳 devcontainer (uses project config, sandboxed)'
147
- : '🐳 devcontainer (uses catch-all container, sandboxed)';
146
+ ? '🐳 devcontainer (uses project config, isolated)'
147
+ : '🐳 devcontainer (uses catch-all container, isolated)';
148
148
  // In JSON mode, output environment prompt and exit
149
149
  if (jsonMode) {
150
150
  await this.prompt([
@@ -267,15 +267,15 @@ export default class Claude extends PromptCommand {
267
267
  displayMode = selectedDisplay;
268
268
  }
269
269
  // Prompt for permission mode
270
- let sandboxed = true;
270
+ let permissionMode = 'safe';
271
271
  if (flags['permission-mode']) {
272
- sandboxed = flags['permission-mode'] === 'safe';
272
+ permissionMode = (flags['permission-mode'] || 'safe');
273
273
  }
274
274
  else {
275
- const { permissionMode } = await this.prompt([
275
+ const { selectedMode } = await this.prompt([
276
276
  {
277
277
  type: 'list',
278
- name: 'permissionMode',
278
+ name: 'selectedMode',
279
279
  message: 'Permission mode:',
280
280
  choices: [
281
281
  { name: '⚠️ danger - Skip permission checks (faster)', value: 'danger', command: `prlt claude --slug "${slug}" --environment ${environment} --display-mode ${displayMode} --permission-mode danger --json` },
@@ -286,10 +286,10 @@ export default class Claude extends PromptCommand {
286
286
  ], jsonModeConfig);
287
287
  if (jsonMode)
288
288
  return;
289
- sandboxed = permissionMode === 'safe';
289
+ permissionMode = selectedMode;
290
290
  }
291
291
  // Warn about uncommitted changes in danger mode
292
- if (!sandboxed && isGitRepo(workDir) && hasUncommittedChanges(workDir)) {
292
+ if (permissionMode === 'danger' && isGitRepo(workDir) && hasUncommittedChanges(workDir)) {
293
293
  this.log('');
294
294
  this.warn('Running in danger mode with uncommitted changes!');
295
295
  this.log(styles.muted(' Consider committing or stashing changes first.'));
@@ -347,7 +347,7 @@ export default class Claude extends PromptCommand {
347
347
  };
348
348
  // Load execution config (use defaults for yolo mode)
349
349
  const executionConfig = { ...DEFAULT_EXECUTION_CONFIG };
350
- executionConfig.sandboxed = sandboxed;
350
+ executionConfig.permissionMode = permissionMode;
351
351
  executionConfig.outputMode = 'interactive';
352
352
  // For terminal mode, prompt for terminal preference
353
353
  if (displayMode === 'terminal' && !jsonMode) {
@@ -384,7 +384,7 @@ export default class Claude extends PromptCommand {
384
384
  this.log(styles.muted(` Directory: ${workDir}`));
385
385
  this.log(styles.muted(` Environment: ${environment === 'devcontainer' ? '🐳' : '💻'} ${environment}`));
386
386
  this.log(styles.muted(` Display: ${displayMode}`));
387
- this.log(styles.muted(` Permissions: ${sandboxed ? '🔒 safe' : '⚠️ danger'}`));
387
+ this.log(styles.muted(` Permissions: ${permissionMode === 'safe' ? '🔒 safe' : '⚠️ danger'}`));
388
388
  if (flags.prompt) {
389
389
  this.log(styles.muted(` Initial prompt: "${flags.prompt.substring(0, 50)}${flags.prompt.length > 50 ? '...' : ''}"`));
390
390
  }
@@ -537,8 +537,8 @@ export default class Claude extends PromptCommand {
537
537
  const hasProjectDevcontainer = hasDevcontainerConfig(workDir);
538
538
  // Build devcontainer label
539
539
  const devcontainerLabel = hasProjectDevcontainer
540
- ? '🐳 devcontainer (uses project config, sandboxed)'
541
- : '🐳 devcontainer (uses catch-all container, sandboxed)';
540
+ ? '🐳 devcontainer (uses project config, isolated)'
541
+ : '🐳 devcontainer (uses catch-all container, isolated)';
542
542
  let environment = 'host';
543
543
  if (flags.environment) {
544
544
  environment = flags.environment;
@@ -670,16 +670,16 @@ export default class Claude extends PromptCommand {
670
670
  displayMode = selectedDisplay;
671
671
  }
672
672
  // Prompt for permission mode
673
- let sandboxed = true;
673
+ let permissionMode = 'safe';
674
674
  if (flags['permission-mode']) {
675
- sandboxed = flags['permission-mode'] === 'safe';
675
+ permissionMode = (flags['permission-mode'] || 'safe');
676
676
  }
677
677
  else {
678
678
  const containerNote = environment === 'devcontainer' ? ' (container provides additional isolation)' : '';
679
- const { permissionMode } = await this.prompt([
679
+ const { selectedMode } = await this.prompt([
680
680
  {
681
681
  type: 'list',
682
- name: 'permissionMode',
682
+ name: 'selectedMode',
683
683
  message: `Permission mode${containerNote}:`,
684
684
  choices: [
685
685
  { name: '⚠️ danger - Skip permission checks (faster)', value: 'danger', command: `prlt claude --project ${projectId} --title "${ticketTitle}" --environment ${environment} --display-mode ${displayMode} --permission-mode danger --json` },
@@ -692,10 +692,10 @@ export default class Claude extends PromptCommand {
692
692
  db.close();
693
693
  return;
694
694
  }
695
- sandboxed = permissionMode === 'safe';
695
+ permissionMode = selectedMode;
696
696
  }
697
697
  // Warn about uncommitted changes in danger mode
698
- if (!sandboxed && isGitRepo(workDir) && hasUncommittedChanges(workDir)) {
698
+ if (permissionMode === 'danger' && isGitRepo(workDir) && hasUncommittedChanges(workDir)) {
699
699
  this.log('');
700
700
  this.warn('Running in danger mode with uncommitted changes!');
701
701
  this.log(styles.muted(' Consider committing or stashing changes first.'));
@@ -777,14 +777,14 @@ export default class Claude extends PromptCommand {
777
777
  executor: 'claude-code',
778
778
  environment,
779
779
  displayMode,
780
- sandboxed,
780
+ permissionMode,
781
781
  branch: 'main',
782
782
  });
783
783
  // Update ticket assignee
784
784
  await storage.updateTicket(ticket.id, { assignee: agentName });
785
785
  // Load execution config
786
786
  const executionConfig = loadExecutionConfig(db);
787
- executionConfig.sandboxed = sandboxed;
787
+ executionConfig.permissionMode = permissionMode;
788
788
  executionConfig.outputMode = 'interactive';
789
789
  // For terminal mode, ensure terminal preference is set
790
790
  if (displayMode === 'terminal' && !jsonMode) {
@@ -808,7 +808,7 @@ export default class Claude extends PromptCommand {
808
808
  this.log(styles.muted(` Work ID: ${execution.id}`));
809
809
  this.log(styles.muted(` Environment: ${environment === 'devcontainer' ? '🐳' : '💻'} ${environment}`));
810
810
  this.log(styles.muted(` Display: ${displayMode}`));
811
- this.log(styles.muted(` Permissions: ${sandboxed ? '🔒 safe' : '⚠️ danger'}`));
811
+ this.log(styles.muted(` Permissions: ${permissionMode === 'safe' ? '🔒 safe' : '⚠️ danger'}`));
812
812
  if (flags.prompt) {
813
813
  this.log(styles.muted(` Initial prompt: "${flags.prompt.substring(0, 50)}${flags.prompt.length > 50 ? '...' : ''}"`));
814
814
  }
@@ -102,7 +102,7 @@ export default class Open extends PromptCommand {
102
102
  // Load execution config
103
103
  const executionConfig = { ...DEFAULT_EXECUTION_CONFIG };
104
104
  executionConfig.outputMode = 'interactive';
105
- executionConfig.sandboxed = false; // Default to dangerously-skip-permissions for quick open
105
+ executionConfig.permissionMode = 'danger'; // Default to danger mode for quick open
106
106
  // Try to load saved preferences from workspace DB or home dir
107
107
  const dbPath = hqPath
108
108
  ? path.join(hqPath, '.proletariat', 'workspace.db')
@@ -92,7 +92,7 @@ export default class Config extends PromptCommand {
92
92
  defaultExecutor: config.defaultExecutor,
93
93
  defaultEnvironment: config.defaultEnvironment,
94
94
  outputMode: config.outputMode,
95
- sandboxed: config.sandboxed,
95
+ permissionMode: config.permissionMode,
96
96
  createPrDefault: config.createPrDefault ?? null,
97
97
  firewall: {
98
98
  allowlistDomains: config.firewall.allowlistDomains,
@@ -118,7 +118,7 @@ export default class Config extends PromptCommand {
118
118
  this.log(` defaultExecutor: ${config.defaultExecutor}`);
119
119
  this.log(` defaultEnvironment: ${config.defaultEnvironment}`);
120
120
  this.log(` outputMode: ${config.outputMode}`);
121
- this.log(` sandboxed: ${config.sandboxed}`);
121
+ this.log(` permissionMode: ${config.permissionMode}`);
122
122
  this.log(` createPrDefault: ${config.createPrDefault ?? 'not set (will prompt)'}`);
123
123
  this.log(` firewall.allowlistDomains: ${config.firewall.allowlistDomains.join(', ') || '(none)'}`);
124
124
  this.log('');
@@ -24,7 +24,7 @@ export default class ExecutionConfig extends PMOCommand {
24
24
  */
25
25
  private setOutputMode;
26
26
  /**
27
- * Save sandboxed preference to workspace settings
27
+ * Save permission mode preference to workspace settings
28
28
  */
29
- private setSandboxed;
29
+ private setPermissionMode;
30
30
  }
@@ -14,7 +14,7 @@ export default class ExecutionConfig extends PMOCommand {
14
14
  '<%= config.bin %> execution config --list # Show all settings',
15
15
  '<%= config.bin %> execution config --set defaultEnvironment host',
16
16
  '<%= config.bin %> execution config --set outputMode interactive',
17
- '<%= config.bin %> execution config --set sandboxed true',
17
+ '<%= config.bin %> execution config --set permissionMode safe',
18
18
  '<%= config.bin %> execution config --setting outputMode --json # Show output mode choices',
19
19
  ];
20
20
  static flags = {
@@ -91,7 +91,7 @@ export default class ExecutionConfig extends PMOCommand {
91
91
  defaultEnvironment: config.defaultEnvironment,
92
92
  defaultExecutor: config.defaultExecutor,
93
93
  outputMode: config.outputMode,
94
- sandboxed: config.sandboxed,
94
+ permissionMode: config.permissionMode,
95
95
  }, createMetadata('execution config', flags));
96
96
  }
97
97
  else {
@@ -105,7 +105,7 @@ export default class ExecutionConfig extends PMOCommand {
105
105
  this.log('');
106
106
  this.log(styles.emphasis('Output'));
107
107
  this.log(` outputMode: ${config.outputMode}`);
108
- this.log(` sandboxed: ${config.sandboxed}`);
108
+ this.log(` permissionMode: ${config.permissionMode}`);
109
109
  this.log('');
110
110
  this.log(styles.emphasis('Terminal'));
111
111
  this.log(` app: ${config.terminal.app}`);
@@ -131,7 +131,7 @@ export default class ExecutionConfig extends PMOCommand {
131
131
  const settingChoices = [
132
132
  { name: `Default Environment: ${config.defaultEnvironment}`, value: 'defaultEnvironment', command: 'prlt execution config --setting defaultEnvironment --json' },
133
133
  { name: `Output Mode: ${config.outputMode}`, value: 'outputMode', command: 'prlt execution config --setting outputMode --json' },
134
- { name: `Permission Mode: ${config.sandboxed ? 'safe' : 'danger'}`, value: 'sandboxed', command: 'prlt execution config --setting sandboxed --json' },
134
+ { name: `Permission Mode: ${config.permissionMode}`, value: 'permissionMode', command: 'prlt execution config --setting permissionMode --json' },
135
135
  { name: `Terminal App: ${config.terminal.app}`, value: 'terminal.app', command: 'prlt execution config --setting terminal.app --json' },
136
136
  { name: `Open Tabs in Background: ${config.terminal.openInBackground}`, value: 'terminal.openInBackground', command: 'prlt execution config --setting terminal.openInBackground --json' },
137
137
  { name: `Shell: ${config.shell}`, value: 'shell', command: 'prlt execution config --setting shell --json' },
@@ -177,7 +177,7 @@ export default class ExecutionConfig extends PMOCommand {
177
177
  case 'defaultEnvironment': {
178
178
  const envChoices = [
179
179
  { name: 'host - Run directly on host machine', value: 'host', command: 'prlt execution config --set "defaultEnvironment host" --json' },
180
- { name: 'devcontainer - Run in a devcontainer (sandboxed)', value: 'devcontainer', command: 'prlt execution config --set "defaultEnvironment devcontainer" --json' },
180
+ { name: 'devcontainer - Run in a devcontainer (isolated)', value: 'devcontainer', command: 'prlt execution config --set "defaultEnvironment devcontainer" --json' },
181
181
  { name: 'docker - Run in a Docker container', value: 'docker', command: 'prlt execution config --set "defaultEnvironment docker" --json' },
182
182
  { name: 'vm - Run on a remote VM', value: 'vm', command: 'prlt execution config --set "defaultEnvironment vm" --json' },
183
183
  ];
@@ -212,10 +212,10 @@ export default class ExecutionConfig extends PMOCommand {
212
212
  this.log(styles.success(`Output mode set to: ${newOutput}`));
213
213
  break;
214
214
  }
215
- case 'sandboxed': {
215
+ case 'permissionMode': {
216
216
  const permChoices = [
217
- { name: 'safe - Requires approval for dangerous operations (recommended)', value: 'true', command: 'prlt execution config --set "sandboxed true" --json' },
218
- { name: 'danger - Skip permission checks (--dangerously-skip-permissions)', value: 'false', command: 'prlt execution config --set "sandboxed false" --json' },
217
+ { name: '🔒 safe - Requires approval for dangerous operations (recommended)', value: 'safe', command: 'prlt execution config --set "permissionMode safe" --json' },
218
+ { name: '⚠️ danger - Skip permission checks (--dangerously-skip-permissions)', value: 'danger', command: 'prlt execution config --set "permissionMode danger" --json' },
219
219
  ];
220
220
  const { newPerm } = await this.prompt([
221
221
  {
@@ -223,11 +223,11 @@ export default class ExecutionConfig extends PMOCommand {
223
223
  name: 'newPerm',
224
224
  message: 'Select permission mode:',
225
225
  choices: permChoices,
226
- default: String(config.sandboxed),
226
+ default: config.permissionMode,
227
227
  },
228
228
  ], jsonModeConfig);
229
- this.setConfigValue(db, 'sandboxed', newPerm, false);
230
- this.log(styles.success(`Permission mode set to: ${newPerm === 'true' ? 'safe' : 'danger'}`));
229
+ this.setConfigValue(db, 'permissionMode', newPerm, false);
230
+ this.log(styles.success(`Permission mode set to: ${newPerm}`));
231
231
  break;
232
232
  }
233
233
  case 'terminal.app': {
@@ -324,7 +324,7 @@ export default class ExecutionConfig extends PMOCommand {
324
324
  const VALID_VALUES = {
325
325
  defaultenvironment: ['host', 'devcontainer', 'docker', 'vm'],
326
326
  outputmode: ['interactive', 'print'],
327
- sandboxed: ['true', 'false'],
327
+ permissionmode: ['safe', 'danger'],
328
328
  'terminal.app': ['Terminal', 'iTerm', 'Alacritty', 'Ghostty', 'Kitty', 'tmux', 'Warp', 'WezTerm'],
329
329
  'terminal.openinbackground': ['true', 'false'],
330
330
  shell: ['bash', 'zsh', 'fish'],
@@ -350,9 +350,9 @@ export default class ExecutionConfig extends PMOCommand {
350
350
  // Store output mode - need to add this to the config storage
351
351
  this.setOutputMode(db, value);
352
352
  break;
353
- case 'sandboxed':
354
- // Store sandboxed preference
355
- this.setSandboxed(db, value.toLowerCase() === 'true');
353
+ case 'permissionmode':
354
+ // Store permission mode preference
355
+ this.setPermissionMode(db, value);
356
356
  break;
357
357
  case 'terminal.app':
358
358
  saveTerminalApp(db, value);
@@ -389,9 +389,9 @@ export default class ExecutionConfig extends PMOCommand {
389
389
  saveExecutionSetting(db, 'outputMode', mode);
390
390
  }
391
391
  /**
392
- * Save sandboxed preference to workspace settings
392
+ * Save permission mode preference to workspace settings
393
393
  */
394
- setSandboxed(db, sandboxed) {
395
- saveExecutionSetting(db, 'sandboxed', sandboxed.toString());
394
+ setPermissionMode(db, mode) {
395
+ saveExecutionSetting(db, 'permissionMode', mode);
396
396
  }
397
397
  }
@@ -88,8 +88,8 @@ export default class ExecutionList extends PMOCommand {
88
88
  const timeAgo = formatTimeAgo(exec.startedAt);
89
89
  const envIcon = exec.environment === 'devcontainer' ? '🐳' : (exec.environment === 'host' ? '💻' : '📦');
90
90
  const envStr = `${envIcon} ${exec.environment}`;
91
- const permsStr = exec.sandboxed ? 'safe' : 'danger';
92
- const permsColor = exec.sandboxed ? styles.success : styles.warning;
91
+ const permsStr = exec.permissionMode;
92
+ const permsColor = exec.permissionMode === 'safe' ? styles.success : styles.warning;
93
93
  this.log(visualPadEnd(exec.id, 14) +
94
94
  visualPadEnd(exec.ticketId, 9) +
95
95
  visualPadEnd(exec.agentName, 10) +
@@ -93,7 +93,7 @@ export default class ExecutionView extends PMOCommand {
93
93
  executor: execution.executor,
94
94
  environment: execution.environment,
95
95
  displayMode: execution.displayMode,
96
- sandboxed: execution.sandboxed,
96
+ permissionMode: execution.permissionMode,
97
97
  status: execution.status,
98
98
  branch: execution.branch || null,
99
99
  pid: execution.pid || null,
@@ -124,7 +124,7 @@ export default class ExecutionView extends PMOCommand {
124
124
  const envIcon = getEnvironmentIcon(execution.environment);
125
125
  this.log(`${styles.muted('Type:')} ${envIcon} ${execution.environment}`);
126
126
  this.log(`${styles.muted('Display:')} ${execution.displayMode}`);
127
- this.log(`${styles.muted('Permissions:')} ${execution.sandboxed ? styles.success('sandboxed (safe)') : styles.warning('unrestricted (danger)')}`);
127
+ this.log(`${styles.muted('Permissions:')} ${execution.permissionMode === 'safe' ? styles.success('safe') : styles.warning('danger')}`);
128
128
  if (execution.branch) {
129
129
  this.log(`${styles.muted('Branch:')} ${execution.branch}`);
130
130
  }
@@ -18,7 +18,7 @@ export default class OrchestratorStart extends PromptCommand {
18
18
  action: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
19
19
  executor: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
20
20
  'skip-permissions': import("@oclif/core/interfaces").BooleanFlag<boolean>;
21
- sandboxed: import("@oclif/core/interfaces").BooleanFlag<boolean>;
21
+ 'permission-mode': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
22
22
  name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
23
23
  background: import("@oclif/core/interfaces").BooleanFlag<boolean>;
24
24
  foreground: import("@oclif/core/interfaces").BooleanFlag<boolean>;
@@ -44,7 +44,7 @@ export default class OrchestratorStart extends PromptCommand {
44
44
  static examples = [
45
45
  '<%= config.bin %> <%= command.id %>',
46
46
  '<%= config.bin %> <%= command.id %> --executor codex',
47
- '<%= config.bin %> <%= command.id %> --skip-permissions',
47
+ '<%= config.bin %> <%= command.id %> --permission-mode danger',
48
48
  '<%= config.bin %> <%= command.id %> --prompt "coordinate all agents on TKT-100"',
49
49
  '<%= config.bin %> <%= command.id %> --background',
50
50
  ];
@@ -64,13 +64,13 @@ export default class OrchestratorStart extends PromptCommand {
64
64
  options: ['claude-code', 'codex', 'aider', 'custom'],
65
65
  }),
66
66
  'skip-permissions': Flags.boolean({
67
- description: 'Run with --dangerously-skip-permissions',
67
+ description: 'Skip permission checks (shorthand for --permission-mode danger)',
68
68
  default: false,
69
- exclusive: ['sandboxed'],
69
+ exclusive: ['permission-mode'],
70
70
  }),
71
- sandboxed: Flags.boolean({
72
- description: 'Run in sandboxed mode (requires approval for dangerous operations)',
73
- default: false,
71
+ 'permission-mode': Flags.string({
72
+ description: 'Permission mode for the orchestrator (danger=skip checks, safe=require approval)',
73
+ options: ['danger', 'safe'],
74
74
  exclusive: ['skip-permissions'],
75
75
  }),
76
76
  name: Flags.string({
@@ -157,30 +157,30 @@ export default class OrchestratorStart extends PromptCommand {
157
157
  selectedExecutor = executor;
158
158
  }
159
159
  // Permission mode selection
160
- let sandboxed;
160
+ let permissionMode;
161
161
  if (flags['skip-permissions']) {
162
- sandboxed = false;
162
+ permissionMode = 'danger';
163
163
  }
164
- else if (flags.sandboxed) {
165
- sandboxed = true;
164
+ else if (flags['permission-mode']) {
165
+ permissionMode = flags['permission-mode'];
166
166
  }
167
167
  else {
168
168
  const permissionChoices = [
169
- { name: 'Sandboxed (requires approval for dangerous operations)', value: 'sandboxed', command: 'prlt orchestrator start --sandboxed --json' },
170
- { name: 'Accept all (--dangerously-skip-permissions)', value: 'skip', command: 'prlt orchestrator start --skip-permissions --json' },
169
+ { name: '⚠️ danger - Skip permission checks (faster)', value: 'danger', command: 'prlt orchestrator start --permission-mode danger --json' },
170
+ { name: '🔒 safe - Requires approval for dangerous operations', value: 'safe', command: 'prlt orchestrator start --permission-mode safe --json' },
171
171
  ];
172
- const permissionMessage = 'Select permission mode:';
172
+ const permissionMessage = 'Permission mode:';
173
173
  if (jsonMode) {
174
174
  outputPromptAsJson(buildPromptConfig('list', 'permissionMode', permissionMessage, permissionChoices), createMetadata('orchestrator start', flags));
175
175
  return;
176
176
  }
177
- const { permissionMode } = await this.prompt([{
177
+ const { permissionMode: selectedMode } = await this.prompt([{
178
178
  type: 'list',
179
179
  name: 'permissionMode',
180
180
  message: permissionMessage,
181
181
  choices: permissionChoices,
182
182
  }]);
183
- sandboxed = permissionMode === 'sandboxed';
183
+ permissionMode = selectedMode;
184
184
  }
185
185
  // Resolve action prompt
186
186
  let actionPrompt = flags.prompt;
@@ -228,7 +228,7 @@ export default class OrchestratorStart extends PromptCommand {
228
228
  // Build execution config
229
229
  const executionConfig = { ...DEFAULT_EXECUTION_CONFIG };
230
230
  executionConfig.outputMode = 'interactive';
231
- executionConfig.sandboxed = sandboxed;
231
+ executionConfig.permissionMode = permissionMode;
232
232
  // Load saved preferences from workspace DB
233
233
  const dbPath = path.join(hqPath, '.proletariat', 'workspace.db');
234
234
  let db = null;
@@ -299,7 +299,7 @@ export default class OrchestratorStart extends PromptCommand {
299
299
  this.log('');
300
300
  this.log(styles.muted(` Starting orchestrator...`));
301
301
  this.log(styles.muted(` Executor: ${selectedExecutor}`));
302
- this.log(styles.muted(` Permission mode: ${sandboxed ? 'sandboxed' : 'skip-permissions'}`));
302
+ this.log(styles.muted(` Permission mode: ${permissionMode}`));
303
303
  this.log(styles.muted(` Display mode: ${displayMode}`));
304
304
  this.log(styles.muted(` Directory: ${hqPath}`));
305
305
  if (orchestratorName !== 'main') {
@@ -325,7 +325,7 @@ export default class OrchestratorStart extends PromptCommand {
325
325
  executor: selectedExecutor,
326
326
  environment: 'host',
327
327
  displayMode,
328
- sandboxed,
328
+ permissionMode,
329
329
  sessionId: result.sessionId || sessionName,
330
330
  });
331
331
  }
@@ -337,7 +337,7 @@ export default class OrchestratorStart extends PromptCommand {
337
337
  outputSuccessAsJson({
338
338
  sessionId: result.sessionId || sessionName,
339
339
  executor: selectedExecutor,
340
- sandboxed,
340
+ permissionMode,
341
341
  displayMode,
342
342
  directory: hqPath,
343
343
  name: orchestratorName,
@@ -295,7 +295,7 @@ Clean up your tmux session when done.`,
295
295
  }
296
296
  }
297
297
  // Resolve permission mode (default to danger for QA since it's in a container)
298
- const sandboxed = await this.resolvePermissionMode(flags, jsonMode, jsonModeConfig, environment, displayMode);
298
+ const permissionMode = await this.resolvePermissionMode(flags, jsonMode, jsonModeConfig, environment, displayMode);
299
299
  if (jsonMode && !flags['permission-mode']) {
300
300
  db.close();
301
301
  return;
@@ -359,14 +359,14 @@ Clean up your tmux session when done.`,
359
359
  executor: 'claude-code',
360
360
  environment,
361
361
  displayMode,
362
- sandboxed,
362
+ permissionMode,
363
363
  branch: 'main',
364
364
  });
365
365
  // Update ticket assignee
366
366
  await storage.updateTicket(ticket.id, { assignee: agentName });
367
367
  // Load execution config
368
368
  const executionConfig = loadExecutionConfig(db);
369
- executionConfig.sandboxed = sandboxed;
369
+ executionConfig.permissionMode = permissionMode;
370
370
  executionConfig.outputMode = 'interactive';
371
371
  // For terminal mode, ensure terminal preference is set
372
372
  if (displayMode === 'terminal' && !jsonMode) {
@@ -390,7 +390,7 @@ Clean up your tmux session when done.`,
390
390
  this.log(styles.muted(` Work ID: ${execution.id}`));
391
391
  this.log(styles.muted(` Environment: ${environment === 'devcontainer' ? '🐳' : '💻'} ${environment}`));
392
392
  this.log(styles.muted(` Display: ${displayMode}${flags.watch ? ' (watch mode)' : ''}`));
393
- this.log(styles.muted(` Permissions: ${sandboxed ? '🔒 safe' : '⚠️ danger'}`));
393
+ this.log(styles.muted(` Permissions: ${permissionMode === 'safe' ? '🔒 safe' : '⚠️ danger'}`));
394
394
  this.log('');
395
395
  // Run execution
396
396
  this.log(styles.muted('Starting QA agent...'));
@@ -457,7 +457,7 @@ Clean up your tmux session when done.`,
457
457
  return;
458
458
  }
459
459
  // Resolve permission mode
460
- const sandboxed = await this.resolvePermissionMode(flags, jsonMode, jsonModeConfig, environment, displayMode);
460
+ const permissionMode = await this.resolvePermissionMode(flags, jsonMode, jsonModeConfig, environment, displayMode);
461
461
  if (jsonMode && !flags['permission-mode'])
462
462
  return;
463
463
  const sessionName = `qa-explore-${Date.now().toString(36)}`;
@@ -501,7 +501,7 @@ Clean up your tmux session when done.`,
501
501
  };
502
502
  // Load execution config
503
503
  const executionConfig = { ...DEFAULT_EXECUTION_CONFIG };
504
- executionConfig.sandboxed = sandboxed;
504
+ executionConfig.permissionMode = permissionMode;
505
505
  executionConfig.outputMode = 'interactive';
506
506
  // For terminal mode, prompt for terminal preference
507
507
  if (displayMode === 'terminal' && !jsonMode) {
@@ -535,7 +535,7 @@ Clean up your tmux session when done.`,
535
535
  this.log(styles.muted(` Directory: ${workDir}`));
536
536
  this.log(styles.muted(` Environment: ${environment === 'devcontainer' ? '🐳' : '💻'} ${environment}`));
537
537
  this.log(styles.muted(` Display: ${displayMode}${flags.watch ? ' (watch mode)' : ''}`));
538
- this.log(styles.muted(` Permissions: ${sandboxed ? '🔒 safe' : '⚠️ danger'}`));
538
+ this.log(styles.muted(` Permissions: ${permissionMode === 'safe' ? '🔒 safe' : '⚠️ danger'}`));
539
539
  this.log('');
540
540
  // Run execution
541
541
  this.log(styles.muted('Starting QA agent...'));
@@ -575,8 +575,8 @@ Clean up your tmux session when done.`,
575
575
  }
576
576
  const hasProjectDevcontainer = hasDevcontainerConfig(workDir);
577
577
  const devcontainerLabel = hasProjectDevcontainer
578
- ? '🐳 devcontainer (uses project config, sandboxed)'
579
- : '🐳 devcontainer (uses catch-all container, sandboxed)';
578
+ ? '🐳 devcontainer (uses project config, isolated)'
579
+ : '🐳 devcontainer (uses catch-all container, isolated)';
580
580
  if (jsonMode) {
581
581
  await this.prompt([
582
582
  {
@@ -680,7 +680,7 @@ Clean up your tmux session when done.`,
680
680
  */
681
681
  async resolvePermissionMode(flags, jsonMode, jsonModeConfig, environment, displayMode) {
682
682
  if (flags['permission-mode']) {
683
- return flags['permission-mode'] === 'safe';
683
+ return (flags['permission-mode'] || 'safe');
684
684
  }
685
685
  const containerNote = environment === 'devcontainer' ? ' (container provides isolation)' : '';
686
686
  const { permissionMode } = await this.prompt([
@@ -696,8 +696,8 @@ Clean up your tmux session when done.`,
696
696
  },
697
697
  ], jsonModeConfig);
698
698
  if (jsonMode)
699
- return true; // unreachable
700
- return permissionMode === 'safe';
699
+ return 'safe'; // unreachable
700
+ return permissionMode;
701
701
  }
702
702
  /**
703
703
  * Set up catch-all devcontainer for directories without one.
@@ -264,7 +264,7 @@ export default class Add extends PromptCommand {
264
264
  this.log(chalk.blue(` From theme: ${theme?.display_name || themeId}`));
265
265
  }
266
266
  if (!flags['no-container']) {
267
- this.log(chalk.blue(' Devcontainer config created for sandboxed execution'));
267
+ this.log(chalk.blue(' Devcontainer config created for isolated execution'));
268
268
  }
269
269
  }
270
270
  catch (error) {
@@ -2,7 +2,8 @@ import { Flags } from '@oclif/core';
2
2
  import { PMOCommand, pmoBaseFlags, autoExportToBoard, } from '../../lib/pmo/index.js';
3
3
  import { shouldOutputJson, } from '../../lib/prompt-json.js';
4
4
  import { ExternalIssueAdapterError, } from '../../lib/external-issues/types.js';
5
- import { listLinearIssues, buildLinearIssueChoiceCommand, buildLinearTicketDescription, buildLinearMetadata, buildLinearSpawnContextMessage, } from '../../lib/external-issues/linear.js';
5
+ import { listLinearIssues, getLinearIssueByIdentifier, buildLinearIssueChoiceCommand, buildLinearTicketDescription, buildLinearMetadata, buildLinearSpawnContextMessage, } from '../../lib/external-issues/linear.js';
6
+ import { getLinearApiKey, loadLinearConfig } from '../../lib/linear/index.js';
6
7
  function buildWorkStartArgs(options) {
7
8
  const args = [options.ticketId, '--project', options.projectId, '--ephemeral'];
8
9
  if (options.executor)
@@ -125,6 +126,8 @@ export default class WorkLinear extends PMOCommand {
125
126
  async execute() {
126
127
  const { flags } = await this.parse(WorkLinear);
127
128
  const jsonMode = shouldOutputJson(flags);
129
+ const db = this.storage.getDatabase();
130
+ const linearConfig = loadLinearConfig(db);
128
131
  const projectId = await this.requireProject({
129
132
  jsonMode: {
130
133
  flags,
@@ -132,10 +135,12 @@ export default class WorkLinear extends PMOCommand {
132
135
  baseCommand: 'prlt work linear',
133
136
  },
134
137
  });
135
- const team = flags.team || process.env.PRLT_LINEAR_TEAM;
138
+ const apiKey = getLinearApiKey(db) || undefined;
139
+ const team = flags.team || linearConfig?.defaultTeamKey || process.env.PRLT_LINEAR_TEAM;
136
140
  let issues;
137
141
  try {
138
142
  issues = await listLinearIssues({
143
+ apiKey,
139
144
  team,
140
145
  }, { limit: flags.limit });
141
146
  }
@@ -150,10 +155,25 @@ export default class WorkLinear extends PMOCommand {
150
155
  return this.handleError('NO_LINEAR_ISSUES', 'No active Linear issues found for the configured team.', { jsonMode, commandName: 'work linear', flags });
151
156
  }
152
157
  let selectedIssue = issues.find(issue => issue.source.externalKey === flags.issue);
153
- if (!selectedIssue) {
154
- if (flags.issue) {
158
+ if (!selectedIssue && flags.issue) {
159
+ try {
160
+ selectedIssue = await getLinearIssueByIdentifier({
161
+ apiKey,
162
+ team,
163
+ }, flags.issue) ?? undefined;
164
+ }
165
+ catch (error) {
166
+ if (error instanceof ExternalIssueAdapterError) {
167
+ return this.handleError(error.code, error.message, { jsonMode, commandName: 'work linear', flags });
168
+ }
169
+ const msg = error instanceof Error ? error.message : 'Failed to fetch Linear issue.';
170
+ return this.handleError('LINEAR_REQUEST_FAILED', msg, { jsonMode, commandName: 'work linear', flags });
171
+ }
172
+ if (!selectedIssue) {
155
173
  return this.handleError('LINEAR_ISSUE_NOT_FOUND', `Linear issue "${flags.issue}" was not found.`, { jsonMode, commandName: 'work linear', flags });
156
174
  }
175
+ }
176
+ if (!selectedIssue) {
157
177
  const selectedKey = await this.selectFromList({
158
178
  message: 'Select Linear issue to spawn:',
159
179
  items: issues,
@@ -162,7 +182,10 @@ export default class WorkLinear extends PMOCommand {
162
182
  return `[${priority}] ${issue.source.externalKey} - ${issue.title}`;
163
183
  },
164
184
  getValue: issue => issue.source.externalKey,
165
- getCommand: issue => buildLinearIssueChoiceCommand(issue.source.externalKey, projectId),
185
+ getCommand: issue => {
186
+ const base = buildLinearIssueChoiceCommand(issue.source.externalKey, projectId);
187
+ return team ? `${base} --team ${team}` : base;
188
+ },
166
189
  jsonMode: jsonMode ? { flags, commandName: 'work linear' } : null,
167
190
  });
168
191
  if (!selectedKey) {