@proletariat/cli 0.3.27 → 0.3.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/dev.js CHANGED
@@ -2,4 +2,11 @@
2
2
 
3
3
  import {execute} from '@oclif/core'
4
4
 
5
+ // Support -v as shorthand for --version (only when it's the sole argument,
6
+ // to avoid conflicts with command-specific -v flags like repo create --visibility)
7
+ const args = process.argv.slice(2)
8
+ if (args.length === 1 && args[0] === '-v') {
9
+ process.argv[2] = '--version'
10
+ }
11
+
5
12
  await execute({development: true, dir: import.meta.url})
package/bin/run.js CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  import {execute} from '@oclif/core'
4
4
 
5
+ // Support -v as shorthand for --version (only when it's the sole argument,
6
+ // to avoid conflicts with command-specific -v flags like repo create --visibility)
7
+ const args = process.argv.slice(2)
8
+ if (args.length === 1 && args[0] === '-v') {
9
+ process.argv[2] = '--version'
10
+ }
11
+
5
12
  // Handle process termination gracefully
6
13
  process.on('SIGINT', () => {
7
14
  console.log('\n'); // Add newline for clean exit
@@ -151,16 +151,30 @@ export default class Shell extends PMOCommand {
151
151
  // Interactive mode: Prompt for environment
152
152
  let environment = 'host';
153
153
  if (hasDevcontainer) {
154
- const { selectedEnvironment } = await this.prompt([{
155
- type: 'list',
156
- name: 'selectedEnvironment',
157
- message: 'Where should the shell run?',
158
- choices: [
159
- { name: '🐳 devcontainer (recommended)', value: 'devcontainer', command: '' },
160
- { name: '💻 host (agent worktree on your machine)', value: 'host', command: '' },
161
- ],
162
- }], null);
163
- environment = selectedEnvironment;
154
+ let environmentSelected = false;
155
+ while (!environmentSelected) {
156
+ // eslint-disable-next-line no-await-in-loop -- Interactive loop with retry on Docker check
157
+ const { selectedEnvironment } = await this.prompt([{
158
+ type: 'list',
159
+ name: 'selectedEnvironment',
160
+ message: 'Where should the shell run?',
161
+ choices: [
162
+ { name: '🐳 devcontainer (recommended)', value: 'devcontainer', command: '' },
163
+ { name: '💻 host (agent worktree on your machine)', value: 'host', command: '' },
164
+ ],
165
+ }], null);
166
+ if (selectedEnvironment === 'devcontainer') {
167
+ // Dynamically check Docker when selected (user may have started it)
168
+ if (!isDockerRunning()) {
169
+ this.log('');
170
+ this.warn('Docker is not running. Please start Docker and try again.');
171
+ this.log('');
172
+ continue;
173
+ }
174
+ }
175
+ environment = selectedEnvironment;
176
+ environmentSelected = true;
177
+ }
164
178
  }
165
179
  // Interactive mode: Prompt for display mode
166
180
  const { displayMode } = await this.prompt([{
@@ -145,22 +145,10 @@ export default class Claude extends PromptCommand {
145
145
  environment = flags.environment;
146
146
  }
147
147
  else {
148
- // Check devcontainer prerequisites upfront
149
- const dockerRunning = isDockerRunning();
150
- const devcontainerCliInstalled = isDevcontainerCliInstalled();
151
- const devcontainerReady = dockerRunning && devcontainerCliInstalled;
152
- // Build devcontainer label with missing requirements
153
- let devcontainerLabel = hasProjectDevcontainer
148
+ // Build devcontainer label
149
+ const devcontainerLabel = hasProjectDevcontainer
154
150
  ? '🐳 devcontainer (uses project config, sandboxed)'
155
151
  : '🐳 devcontainer (uses catch-all container, sandboxed)';
156
- if (!devcontainerReady) {
157
- const missing = [];
158
- if (!dockerRunning)
159
- missing.push('Docker');
160
- if (!devcontainerCliInstalled)
161
- missing.push('devcontainer CLI');
162
- devcontainerLabel = `🐳 devcontainer (requires: ${missing.join(', ')})`;
163
- }
164
152
  // In JSON mode, output environment prompt and exit
165
153
  if (jsonMode) {
166
154
  await this.prompt([
@@ -172,7 +160,6 @@ export default class Claude extends PromptCommand {
172
160
  {
173
161
  name: devcontainerLabel,
174
162
  value: 'devcontainer',
175
- disabled: !devcontainerReady,
176
163
  command: `prlt claude --slug "${slug}" --environment devcontainer --json`,
177
164
  },
178
165
  {
@@ -181,7 +168,7 @@ export default class Claude extends PromptCommand {
181
168
  command: `prlt claude --slug "${slug}" --environment host --json`,
182
169
  },
183
170
  ],
184
- default: devcontainerReady ? 'devcontainer' : 'host',
171
+ default: 'devcontainer',
185
172
  },
186
173
  ], jsonModeConfig);
187
174
  return; // unreachable, but satisfies TypeScript
@@ -199,18 +186,17 @@ export default class Claude extends PromptCommand {
199
186
  {
200
187
  name: devcontainerLabel,
201
188
  value: 'devcontainer',
202
- disabled: !devcontainerReady,
203
189
  },
204
190
  { name: '💻 host (runs directly on your machine)', value: 'host' },
205
191
  ],
206
- default: devcontainerReady ? 'devcontainer' : 'host',
192
+ default: 'devcontainer',
207
193
  },
208
194
  ], null);
209
195
  if (selectedEnv === 'devcontainer') {
210
- // Double-check prerequisites (in case user retried after starting Docker)
196
+ // Dynamically check Docker when selected (user may have started it)
211
197
  if (!isDockerRunning()) {
212
198
  this.log('');
213
- this.warn('Docker is not running. Please start Docker Desktop or select "host".');
199
+ this.warn('Docker is not running. Please start Docker and try again.');
214
200
  this.log('');
215
201
  continue;
216
202
  }
@@ -553,22 +539,10 @@ export default class Claude extends PromptCommand {
553
539
  }
554
540
  // Prompt for environment first (before creating ticket) so user can cancel early
555
541
  const hasProjectDevcontainer = hasDevcontainerConfig(workDir);
556
- // Check devcontainer prerequisites upfront
557
- const dockerRunning = isDockerRunning();
558
- const devcontainerCliInstalled = isDevcontainerCliInstalled();
559
- const devcontainerReady = dockerRunning && devcontainerCliInstalled;
560
- // Build devcontainer label with missing requirements
561
- let devcontainerLabel = hasProjectDevcontainer
542
+ // Build devcontainer label
543
+ const devcontainerLabel = hasProjectDevcontainer
562
544
  ? '🐳 devcontainer (uses project config, sandboxed)'
563
545
  : '🐳 devcontainer (uses catch-all container, sandboxed)';
564
- if (!devcontainerReady) {
565
- const missing = [];
566
- if (!dockerRunning)
567
- missing.push('Docker');
568
- if (!devcontainerCliInstalled)
569
- missing.push('devcontainer CLI');
570
- devcontainerLabel = `🐳 devcontainer (requires: ${missing.join(', ')})`;
571
- }
572
546
  let environment = 'host';
573
547
  if (flags.environment) {
574
548
  environment = flags.environment;
@@ -585,7 +559,6 @@ export default class Claude extends PromptCommand {
585
559
  {
586
560
  name: devcontainerLabel,
587
561
  value: 'devcontainer',
588
- disabled: !devcontainerReady,
589
562
  command: `prlt claude --project ${projectId} --title "${ticketTitle}" --environment devcontainer --json`,
590
563
  },
591
564
  {
@@ -594,7 +567,7 @@ export default class Claude extends PromptCommand {
594
567
  command: `prlt claude --project ${projectId} --title "${ticketTitle}" --environment host --json`,
595
568
  },
596
569
  ],
597
- default: devcontainerReady ? 'devcontainer' : 'host',
570
+ default: 'devcontainer',
598
571
  },
599
572
  ], jsonModeConfig);
600
573
  db.close();
@@ -613,18 +586,17 @@ export default class Claude extends PromptCommand {
613
586
  {
614
587
  name: devcontainerLabel,
615
588
  value: 'devcontainer',
616
- disabled: !devcontainerReady,
617
589
  },
618
590
  { name: '💻 host (runs directly on your machine)', value: 'host' },
619
591
  ],
620
- default: devcontainerReady ? 'devcontainer' : 'host',
592
+ default: 'devcontainer',
621
593
  },
622
594
  ], null);
623
595
  if (selectedEnv === 'devcontainer') {
624
- // Double-check prerequisites (in case user retried after starting Docker)
596
+ // Dynamically check Docker when selected (user may have started it)
625
597
  if (!isDockerRunning()) {
626
598
  this.log('');
627
- this.warn('Docker is not running. Please start Docker Desktop or select "host".');
599
+ this.warn('Docker is not running. Please start Docker and try again.');
628
600
  this.log('');
629
601
  continue;
630
602
  }
@@ -743,22 +743,9 @@ export default class WorkSpawn extends PMOCommand {
743
743
  }
744
744
  // Prompt for environment (devcontainer vs host) if devcontainer available and not already set
745
745
  if (hasDevcontainer && !batchRunOnHost && !batchDisplay) {
746
- // Check devcontainer prerequisites upfront
747
- const dockerRunning = isDockerRunning();
748
- const devcontainerCliInstalled = isDevcontainerCliInstalled();
749
- const devcontainerReady = dockerRunning && devcontainerCliInstalled;
750
- // Build missing requirements message for devcontainer option
751
- let devcontainerLabel = '🐳 devcontainer (sandboxed, recommended)';
752
- if (!devcontainerReady) {
753
- const missing = [];
754
- if (!dockerRunning)
755
- missing.push('Docker');
756
- if (!devcontainerCliInstalled)
757
- missing.push('devcontainer CLI');
758
- devcontainerLabel = `🐳 devcontainer (requires: ${missing.join(', ')})`;
759
- }
746
+ const devcontainerLabel = '🐳 devcontainer (sandboxed, recommended)';
760
747
  const envChoices = [
761
- { name: devcontainerLabel, value: 'devcontainer', disabled: !devcontainerReady },
748
+ { name: devcontainerLabel, value: 'devcontainer' },
762
749
  { name: '💻 host (runs directly on your machine)', value: 'host' },
763
750
  { name: '✗ cancel', value: 'cancel' },
764
751
  ];
@@ -774,7 +761,7 @@ export default class WorkSpawn extends PMOCommand {
774
761
  flagName: 'selectedEnvironment',
775
762
  type: 'list',
776
763
  message: 'Where should agents run?',
777
- default: devcontainerReady ? 'devcontainer' : 'host',
764
+ default: 'devcontainer',
778
765
  choices: () => envChoices,
779
766
  });
780
767
  await envResolver.resolve();
@@ -791,7 +778,7 @@ export default class WorkSpawn extends PMOCommand {
791
778
  name: 'selectedEnvironment',
792
779
  message: 'Where should agents run?',
793
780
  choices: envChoices,
794
- default: devcontainerReady ? 'devcontainer' : 'host',
781
+ default: 'devcontainer',
795
782
  },
796
783
  ], spawnJsonModeConfig);
797
784
  if (selectedEnvironment === 'cancel') {
@@ -800,12 +787,10 @@ export default class WorkSpawn extends PMOCommand {
800
787
  return;
801
788
  }
802
789
  if (selectedEnvironment === 'devcontainer') {
803
- // Double-check prerequisites (in case user retried after starting Docker)
790
+ // Dynamically check Docker when selected (user may have started it)
804
791
  if (!isDockerRunning()) {
805
792
  this.log('');
806
- this.warn('Docker is not running.\n' +
807
- 'Docker is required for devcontainer execution.\n' +
808
- 'Please start Docker Desktop or select "host" to run directly on your machine.');
793
+ this.warn('Docker is not running. Please start Docker and try again.');
809
794
  this.log('');
810
795
  continue;
811
796
  }
@@ -768,22 +768,9 @@ export default class WorkStart extends PMOCommand {
768
768
  let sandboxed = false; // Whether --dangerously-skip-permissions is NOT used
769
769
  if (hasDevcontainer && !flags.display && !flags['run-on-host']) {
770
770
  // Agent has devcontainer - prompt for environment choice
771
- // Check devcontainer prerequisites upfront
772
- const dockerRunning = isDockerRunning();
773
- const devcontainerCliInstalled = isDevcontainerCliInstalled();
774
- const devcontainerReady = dockerRunning && devcontainerCliInstalled;
775
- // Build missing requirements message for devcontainer option
776
- let devcontainerLabel = '🐳 devcontainer (sandboxed, recommended)';
777
- if (!devcontainerReady) {
778
- const missing = [];
779
- if (!dockerRunning)
780
- missing.push('Docker');
781
- if (!devcontainerCliInstalled)
782
- missing.push('devcontainer CLI');
783
- devcontainerLabel = `🐳 devcontainer (requires: ${missing.join(', ')})`;
784
- }
771
+ const devcontainerLabel = '🐳 devcontainer (sandboxed, recommended)';
785
772
  const envChoices = [
786
- { name: devcontainerLabel, value: 'devcontainer', disabled: !devcontainerReady },
773
+ { name: devcontainerLabel, value: 'devcontainer' },
787
774
  { name: '💻 host (runs directly on your machine)', value: 'host' },
788
775
  { name: '✗ cancel', value: 'cancel' },
789
776
  ];
@@ -799,7 +786,7 @@ export default class WorkStart extends PMOCommand {
799
786
  flagName: 'selectedEnvironment',
800
787
  type: 'list',
801
788
  message: 'Where should the agent run?',
802
- default: devcontainerReady ? 'devcontainer' : 'host',
789
+ default: 'devcontainer',
803
790
  choices: () => envChoices,
804
791
  });
805
792
  await envResolver.resolve();
@@ -817,7 +804,7 @@ export default class WorkStart extends PMOCommand {
817
804
  name: 'selectedEnvironment',
818
805
  message: 'Where should the agent run?',
819
806
  choices: envChoices,
820
- default: devcontainerReady ? 'devcontainer' : 'host',
807
+ default: 'devcontainer',
821
808
  },
822
809
  ], jsonModeConfig);
823
810
  if (selectedEnvironment === 'cancel') {
@@ -826,12 +813,10 @@ export default class WorkStart extends PMOCommand {
826
813
  return;
827
814
  }
828
815
  if (selectedEnvironment === 'devcontainer') {
829
- // Double-check prerequisites (in case user retried after starting Docker)
816
+ // Dynamically check Docker when selected (user may have started it)
830
817
  if (!isDockerRunning()) {
831
818
  this.log('');
832
- this.warn('Docker is not running.\n' +
833
- 'Docker is required for devcontainer execution.\n' +
834
- 'Please start Docker Desktop or select "host" to run directly on your machine.');
819
+ this.warn('Docker is not running. Please start Docker and try again.');
835
820
  this.log('');
836
821
  continue; // Re-prompt for environment selection
837
822
  }
@@ -161,42 +161,66 @@ export default class WorkWatch extends PMOCommand {
161
161
  const agentDir = path.join(workspaceInfo.agentsPath, agent.name);
162
162
  return hasDevcontainerConfig(agentDir);
163
163
  });
164
- // Docker check
165
- const dockerRunning = isDockerRunning();
166
- if (hasDevcontainer && !dockerRunning) {
167
- this.warn('Docker is not running. Agents will run on host instead of devcontainer.\n' +
168
- 'Start Docker Desktop for sandboxed execution.');
169
- }
170
- // Devcontainer CLI check
171
- const devcontainerCliInstalled = isDevcontainerCliInstalled();
172
- if (hasDevcontainer && dockerRunning && !devcontainerCliInstalled) {
173
- this.warn('devcontainer CLI is not installed. Agents will run on host instead of devcontainer.\n' +
174
- 'Install with: npm install -g @devcontainers/cli');
175
- }
176
164
  // Prompt for environment and display mode if not provided
177
165
  this.environment = 'host';
178
166
  this.displayMode = 'terminal';
179
167
  if (!flags.mode) {
180
- if (hasDevcontainer && dockerRunning && devcontainerCliInstalled) {
181
- // Use FlagResolver for environment selection
182
- const envResolver = new FlagResolver({
183
- commandName: 'work watch',
184
- baseCommand: 'prlt work watch',
185
- jsonMode,
186
- flags: {},
187
- });
188
- envResolver.addPrompt({
189
- flagName: 'selectedEnvironment',
190
- type: 'list',
191
- message: 'Where should agents run?',
192
- default: 'devcontainer',
193
- choices: () => [
194
- { name: '🐳 devcontainer (sandboxed, recommended)', value: 'devcontainer' },
195
- { name: '💻 host (runs directly on your machine)', value: 'host' },
196
- ],
197
- });
198
- const envResult = await envResolver.resolve();
199
- this.environment = envResult.selectedEnvironment;
168
+ if (hasDevcontainer) {
169
+ const envChoices = [
170
+ { name: '🐳 devcontainer (sandboxed, recommended)', value: 'devcontainer' },
171
+ { name: '💻 host (runs directly on your machine)', value: 'host' },
172
+ ];
173
+ if (jsonMode) {
174
+ // In JSON mode, use FlagResolver (outputs prompt and exits)
175
+ const envResolver = new FlagResolver({
176
+ commandName: 'work watch',
177
+ baseCommand: 'prlt work watch',
178
+ jsonMode,
179
+ flags: {},
180
+ });
181
+ envResolver.addPrompt({
182
+ flagName: 'selectedEnvironment',
183
+ type: 'list',
184
+ message: 'Where should agents run?',
185
+ default: 'devcontainer',
186
+ choices: () => envChoices,
187
+ });
188
+ await envResolver.resolve();
189
+ return; // unreachable, but satisfies TypeScript
190
+ }
191
+ // Interactive mode: loop to handle Docker not running
192
+ let environmentSelected = false;
193
+ while (!environmentSelected) {
194
+ // eslint-disable-next-line no-await-in-loop -- Interactive loop with retry on Docker check
195
+ const { selectedEnvironment } = await this.prompt([
196
+ {
197
+ type: 'list',
198
+ name: 'selectedEnvironment',
199
+ message: 'Where should agents run?',
200
+ choices: envChoices,
201
+ default: 'devcontainer',
202
+ },
203
+ ], null);
204
+ if (selectedEnvironment === 'devcontainer') {
205
+ // Dynamically check Docker when selected (user may have started it)
206
+ if (!isDockerRunning()) {
207
+ this.log('');
208
+ this.warn('Docker is not running. Please start Docker and try again.');
209
+ this.log('');
210
+ continue;
211
+ }
212
+ if (!isDevcontainerCliInstalled()) {
213
+ this.log('');
214
+ this.warn('devcontainer CLI is not installed.\n' +
215
+ 'Install with: npm install -g @devcontainers/cli\n' +
216
+ 'Or select "host" to run directly on your machine.');
217
+ this.log('');
218
+ continue;
219
+ }
220
+ }
221
+ this.environment = selectedEnvironment;
222
+ environmentSelected = true;
223
+ }
200
224
  }
201
225
  // Use FlagResolver for display mode
202
226
  const displayResolver = new FlagResolver({
@@ -220,7 +244,7 @@ export default class WorkWatch extends PMOCommand {
220
244
  }
221
245
  else {
222
246
  this.displayMode = flags.mode;
223
- this.environment = hasDevcontainer && dockerRunning ? 'devcontainer' : 'host';
247
+ this.environment = hasDevcontainer && isDockerRunning() ? 'devcontainer' : 'host';
224
248
  }
225
249
  // Prompt for execution settings (terminal, output mode, permissions, PR creation)
226
250
  const promptResult = await promptExecutionSettings(db, {
@@ -109,6 +109,9 @@ export function generateDevcontainerJson(options, config) {
109
109
  // Git identity for commit attribution (detected from host's gh/git config)
110
110
  ...(options.gitUserName ? { PRLT_GIT_USER_NAME: options.gitUserName } : {}),
111
111
  ...(options.gitUserEmail ? { PRLT_GIT_USER_EMAIL: options.gitUserEmail } : {}),
112
+ // prlt channel info for version updates at container startup (TKT-954)
113
+ PRLT_REGISTRY: channel.registry,
114
+ ...(!useMount ? { PRLT_VERSION: channel.version || 'latest' } : {}),
112
115
  // /hq/.proletariat/bin contains prlt wrapper with ESM loader for native modules
113
116
  PATH: '/hq/.proletariat/bin:/home/node/.npm-global/bin:/usr/local/bin:/usr/bin:/bin',
114
117
  },
@@ -124,7 +127,7 @@ export function generateDevcontainerJson(options, config) {
124
127
  */
125
128
  export function generateDockerfile(options) {
126
129
  const timezone = options.timezone || 'America/Los_Angeles';
127
- return `FROM node:20
130
+ return `FROM node:22
128
131
 
129
132
  # Ensure we run as root for apt-get and system setup
130
133
  USER root
@@ -515,11 +518,30 @@ configure_git_identity() {
515
518
 
516
519
  configure_git_identity
517
520
 
518
- # Check if prlt is already installed globally (via npm from GitHub Packages)
521
+ # Check if prlt is already installed globally (via npm)
522
+ # TKT-954: Also check for updates - Docker layer caching may have installed an older version
519
523
  if command -v prlt &> /dev/null; then
520
524
  PRLT_PATH=$(which prlt)
521
525
  if [[ "$PRLT_PATH" == "/home/node/.npm-global/bin/prlt" ]]; then
522
- echo "prlt installed via npm, no setup needed"
526
+ DESIRED_VERSION="\${PRLT_VERSION:-latest}"
527
+ CURRENT_VERSION=$(prlt --version 2>/dev/null | grep -oE '[0-9]+\\.[0-9]+\\.[0-9]+' || echo "unknown")
528
+
529
+ # Determine the target version to compare against
530
+ if [ "$DESIRED_VERSION" = "latest" ] || [ "$DESIRED_VERSION" = "dev" ] || [ "$DESIRED_VERSION" = "next" ]; then
531
+ # For tags, check npm registry for the actual version number
532
+ TARGET_VERSION=$(npm view "@proletariat/cli@\${DESIRED_VERSION}" version 2>/dev/null || echo "unknown")
533
+ else
534
+ # For pinned versions (e.g., "1.2.3"), use directly
535
+ TARGET_VERSION="$DESIRED_VERSION"
536
+ fi
537
+
538
+ if [ "$TARGET_VERSION" != "unknown" ] && [ "$CURRENT_VERSION" != "$TARGET_VERSION" ]; then
539
+ echo "Updating prlt from v\${CURRENT_VERSION} to v\${TARGET_VERSION} (channel: \${DESIRED_VERSION})..."
540
+ npm install -g "@proletariat/cli@\${DESIRED_VERSION}" 2>&1
541
+ echo "prlt updated successfully"
542
+ else
543
+ echo "prlt v\${CURRENT_VERSION} is up to date"
544
+ fi
523
545
  exit 0
524
546
  fi
525
547
  fi
@@ -81,7 +81,7 @@ export declare const LEGACY_PRIORITY_MAP: Record<LegacyPriority, Priority>;
81
81
  /**
82
82
  * Ticket categories for classification.
83
83
  */
84
- export declare const TICKET_CATEGORIES: readonly ["feature", "bug", "refactor", "docs", "test", "chore", "performance", "ci", "build", "security", "database", "release"];
84
+ export declare const TICKET_CATEGORIES: readonly ["feature", "bug", "refactor", "docs", "test", "chore", "performance", "ci", "build", "security", "database", "release", "adhoc"];
85
85
  export type TicketCategory = typeof TICKET_CATEGORIES[number];
86
86
  /**
87
87
  * Category types (ticket vs status).
@@ -49,6 +49,7 @@ export const TICKET_CATEGORIES = [
49
49
  'security',
50
50
  'database',
51
51
  'release',
52
+ 'adhoc',
52
53
  ];
53
54
  /**
54
55
  * Check if a string is a valid Priority value.