@proletariat/cli 0.3.34 → 0.3.36

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 (198) hide show
  1. package/dist/commands/agent/auth.d.ts +15 -3
  2. package/dist/commands/agent/auth.js +136 -15
  3. package/dist/commands/agent/index.js +11 -2
  4. package/dist/commands/agent/list.js +16 -7
  5. package/dist/commands/agent/staff/add.d.ts +1 -0
  6. package/dist/commands/agent/staff/add.js +1 -0
  7. package/dist/commands/agent/staff/index.d.ts +15 -0
  8. package/dist/commands/agent/staff/index.js +83 -0
  9. package/dist/commands/agent/staff/list.d.ts +1 -0
  10. package/dist/commands/agent/staff/list.js +1 -0
  11. package/dist/commands/agent/staff/remove.d.ts +1 -0
  12. package/dist/commands/agent/staff/remove.js +1 -0
  13. package/dist/commands/agent/status.js +32 -4
  14. package/dist/commands/agent/themes/add-names.d.ts +1 -0
  15. package/dist/commands/agent/themes/add-names.js +1 -0
  16. package/dist/commands/agent/themes/create.d.ts +1 -0
  17. package/dist/commands/agent/themes/create.js +1 -0
  18. package/dist/commands/agent/themes/index.d.ts +10 -0
  19. package/dist/commands/agent/themes/index.js +144 -0
  20. package/dist/commands/agent/themes/list.d.ts +1 -0
  21. package/dist/commands/agent/themes/list.js +1 -0
  22. package/dist/commands/agent/themes/set.d.ts +1 -0
  23. package/dist/commands/agent/themes/set.js +1 -0
  24. package/dist/commands/agents/themes/add-names.d.ts +1 -0
  25. package/dist/commands/agents/themes/add-names.js +1 -0
  26. package/dist/commands/agents/themes/create.d.ts +1 -0
  27. package/dist/commands/agents/themes/create.js +1 -0
  28. package/dist/commands/agents/themes/list.d.ts +1 -0
  29. package/dist/commands/agents/themes/list.js +1 -0
  30. package/dist/commands/board/watch.js +6 -0
  31. package/dist/commands/branch/list.d.ts +1 -0
  32. package/dist/commands/branch/list.js +43 -12
  33. package/dist/commands/branch/where.js +3 -2
  34. package/dist/commands/category/list.d.ts +2 -1
  35. package/dist/commands/category/list.js +38 -13
  36. package/dist/commands/{claude.d.ts → claude/index.d.ts} +1 -1
  37. package/dist/commands/{claude.js → claude/index.js} +12 -12
  38. package/dist/commands/claude/open.d.ts +13 -0
  39. package/dist/commands/claude/open.js +175 -0
  40. package/dist/commands/diet.js +18 -2
  41. package/dist/commands/docker/logs.js +7 -3
  42. package/dist/commands/docker/shell.js +6 -0
  43. package/dist/commands/docker/start.js +20 -4
  44. package/dist/commands/docker/sync.d.ts +4 -0
  45. package/dist/commands/docker/sync.js +30 -2
  46. package/dist/commands/epic/show.d.ts +13 -0
  47. package/dist/commands/epic/show.js +16 -0
  48. package/dist/commands/epic/view.js +27 -0
  49. package/dist/commands/execution/config.d.ts +0 -4
  50. package/dist/commands/execution/config.js +10 -32
  51. package/dist/commands/execution/index.js +2 -1
  52. package/dist/commands/execution/logs.js +1 -1
  53. package/dist/commands/execution/stop.js +2 -1
  54. package/dist/commands/execution/view.js +22 -26
  55. package/dist/commands/init.js +2 -19
  56. package/dist/commands/label/create.d.ts +20 -0
  57. package/dist/commands/label/create.js +57 -0
  58. package/dist/commands/label/delete.d.ts +17 -0
  59. package/dist/commands/label/delete.js +32 -0
  60. package/dist/commands/label/group/create.d.ts +20 -0
  61. package/dist/commands/label/group/create.js +55 -0
  62. package/dist/commands/label/group/list.d.ts +14 -0
  63. package/dist/commands/label/group/list.js +52 -0
  64. package/dist/commands/label/index.d.ts +15 -0
  65. package/dist/commands/label/index.js +58 -0
  66. package/dist/commands/label/list.d.ts +16 -0
  67. package/dist/commands/label/list.js +83 -0
  68. package/dist/commands/link/list.js +3 -2
  69. package/dist/commands/mcp-server.js +27 -1
  70. package/dist/commands/phase/template/apply.d.ts +26 -0
  71. package/dist/commands/phase/template/apply.js +14 -0
  72. package/dist/commands/phase/template/create.d.ts +23 -0
  73. package/dist/commands/phase/template/create.js +14 -0
  74. package/dist/commands/phase/template/delete.d.ts +18 -0
  75. package/dist/commands/phase/template/delete.js +61 -0
  76. package/dist/commands/phase/template/list.d.ts +17 -0
  77. package/dist/commands/phase/template/list.js +89 -0
  78. package/dist/commands/phase/template/update.d.ts +1 -0
  79. package/dist/commands/phase/template/update.js +1 -0
  80. package/dist/commands/priority/add.js +1 -1
  81. package/dist/commands/project/create.js +3 -4
  82. package/dist/commands/project/update.js +5 -8
  83. package/dist/commands/pull.js +24 -0
  84. package/dist/commands/roadmap/generate.js +1 -2
  85. package/dist/commands/session/create.d.ts +19 -0
  86. package/dist/commands/session/create.js +102 -0
  87. package/dist/commands/session/health.js +2 -21
  88. package/dist/commands/session/index.js +14 -1
  89. package/dist/commands/session/list.js +26 -7
  90. package/dist/commands/session/peek.d.ts +38 -0
  91. package/dist/commands/session/peek.js +316 -0
  92. package/dist/commands/session/poke.d.ts +27 -0
  93. package/dist/commands/session/poke.js +219 -0
  94. package/dist/commands/spec/link/depends.d.ts +18 -0
  95. package/dist/commands/spec/link/depends.js +86 -0
  96. package/dist/commands/spec/link/index.d.ts +17 -0
  97. package/dist/commands/spec/link/index.js +92 -0
  98. package/dist/commands/spec/link/remove.d.ts +18 -0
  99. package/dist/commands/spec/link/remove.js +90 -0
  100. package/dist/commands/spec/view.js +29 -0
  101. package/dist/commands/support/logs.js +2 -2
  102. package/dist/commands/template/apply.js +5 -4
  103. package/dist/commands/template/create.js +1 -1
  104. package/dist/commands/template/list.js +2 -1
  105. package/dist/commands/theme/add-names.d.ts +4 -0
  106. package/dist/commands/theme/add-names.js +11 -1
  107. package/dist/commands/theme/create.d.ts +2 -0
  108. package/dist/commands/theme/create.js +8 -0
  109. package/dist/commands/ticket/bulk.js +2 -2
  110. package/dist/commands/ticket/complete.js +2 -2
  111. package/dist/commands/ticket/create.js +21 -0
  112. package/dist/commands/ticket/delete.js +8 -0
  113. package/dist/commands/ticket/edit.js +25 -0
  114. package/dist/commands/ticket/index.js +2 -2
  115. package/dist/commands/ticket/link/block.d.ts +15 -0
  116. package/dist/commands/ticket/link/block.js +95 -0
  117. package/dist/commands/ticket/link/index.d.ts +14 -0
  118. package/dist/commands/ticket/link/index.js +96 -0
  119. package/dist/commands/ticket/list.d.ts +1 -0
  120. package/dist/commands/ticket/list.js +6 -0
  121. package/dist/commands/ticket/move.js +25 -2
  122. package/dist/commands/ticket/resolve.js +4 -5
  123. package/dist/commands/ticket/show.d.ts +13 -0
  124. package/dist/commands/ticket/show.js +16 -0
  125. package/dist/commands/ticket/template/apply.d.ts +26 -0
  126. package/dist/commands/ticket/template/apply.js +14 -0
  127. package/dist/commands/ticket/template/delete.d.ts +18 -0
  128. package/dist/commands/ticket/template/delete.js +61 -0
  129. package/dist/commands/ticket/template/list.d.ts +17 -0
  130. package/dist/commands/ticket/template/list.js +78 -0
  131. package/dist/commands/ticket/template/save.d.ts +17 -0
  132. package/dist/commands/ticket/template/save.js +97 -0
  133. package/dist/commands/ticket/view.js +30 -0
  134. package/dist/commands/work/index.js +4 -0
  135. package/dist/commands/work/ready.js +17 -0
  136. package/dist/commands/work/resolve.js +1 -1
  137. package/dist/commands/work/spawn.js +4 -4
  138. package/dist/commands/work/start.d.ts +1 -0
  139. package/dist/commands/work/start.js +203 -93
  140. package/dist/commands/work/status.d.ts +14 -0
  141. package/dist/commands/work/status.js +60 -0
  142. package/dist/commands/workflow/index.js +2 -1
  143. package/dist/commands/workflow/show.d.ts +13 -0
  144. package/dist/commands/workflow/show.js +16 -0
  145. package/dist/commands/workspace/add.js +15 -0
  146. package/dist/commands/workspace/list.js +2 -1
  147. package/dist/commands/workspace/prune.js +5 -5
  148. package/dist/lib/branch/index.d.ts +1 -0
  149. package/dist/lib/database/index.d.ts +1 -1
  150. package/dist/lib/database/index.js +20 -0
  151. package/dist/lib/execution/config.d.ts +15 -1
  152. package/dist/lib/execution/config.js +28 -0
  153. package/dist/lib/execution/devcontainer.js +3 -1
  154. package/dist/lib/execution/runners.d.ts +18 -2
  155. package/dist/lib/execution/runners.js +71 -29
  156. package/dist/lib/execution/session-utils.d.ts +11 -1
  157. package/dist/lib/execution/session-utils.js +26 -1
  158. package/dist/lib/execution/storage.d.ts +5 -0
  159. package/dist/lib/execution/storage.js +18 -3
  160. package/dist/lib/execution/types.d.ts +3 -0
  161. package/dist/lib/flags/resolver.js +1 -0
  162. package/dist/lib/mcp/helpers.d.ts +1 -2
  163. package/dist/lib/mcp/tools/board.js +4 -6
  164. package/dist/lib/mcp/tools/cli-passthrough.js +25 -6
  165. package/dist/lib/mcp/tools/diet.js +1 -0
  166. package/dist/lib/mcp/tools/epic.js +8 -3
  167. package/dist/lib/mcp/tools/index.d.ts +1 -0
  168. package/dist/lib/mcp/tools/index.js +1 -0
  169. package/dist/lib/mcp/tools/label.d.ts +6 -0
  170. package/dist/lib/mcp/tools/label.js +338 -0
  171. package/dist/lib/mcp/tools/spec.js +1 -1
  172. package/dist/lib/mcp/tools/ticket.js +57 -19
  173. package/dist/lib/mcp/tools/work.js +96 -6
  174. package/dist/lib/mcp/types.d.ts +10 -0
  175. package/dist/lib/multiline-input.js +8 -19
  176. package/dist/lib/pmo/base-command.d.ts +0 -1
  177. package/dist/lib/pmo/base-command.js +4 -5
  178. package/dist/lib/pmo/schema.d.ts +6 -0
  179. package/dist/lib/pmo/schema.js +44 -0
  180. package/dist/lib/pmo/storage/actions.js +1 -1
  181. package/dist/lib/pmo/storage/base.d.ts +6 -0
  182. package/dist/lib/pmo/storage/base.js +311 -52
  183. package/dist/lib/pmo/storage/index.d.ts +23 -1
  184. package/dist/lib/pmo/storage/index.js +59 -1
  185. package/dist/lib/pmo/storage/labels.d.ts +55 -0
  186. package/dist/lib/pmo/storage/labels.js +346 -0
  187. package/dist/lib/pmo/storage/tickets.js +17 -0
  188. package/dist/lib/pmo/storage/types.d.ts +25 -0
  189. package/dist/lib/pmo/types.d.ts +44 -0
  190. package/dist/lib/pmo/utils.js +1 -1
  191. package/dist/lib/prompt-command.d.ts +20 -0
  192. package/dist/lib/prompt-command.js +38 -2
  193. package/dist/lib/prompt-json.d.ts +36 -4
  194. package/dist/lib/prompt-json.js +129 -7
  195. package/dist/lib/styles.d.ts +37 -0
  196. package/dist/lib/styles.js +73 -0
  197. package/oclif.manifest.json +6399 -3799
  198. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
- import { Command } from '@oclif/core';
2
- export default class Auth extends Command {
1
+ import { PromptCommand } from '../../lib/prompt-command.js';
2
+ export default class Auth extends PromptCommand {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static flags: {
@@ -7,7 +7,13 @@ export default class Auth extends Command {
7
7
  machine: import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
8
  check: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
9
  force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ 'api-key': import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
11
  };
12
+ /**
13
+ * Try to open the workspace database for saving auth preferences.
14
+ * Returns null if not in an HQ directory (auth still works, just won't save preference).
15
+ */
16
+ private tryOpenDb;
11
17
  /**
12
18
  * Check if the claude-credentials volume exists
13
19
  */
@@ -17,7 +23,9 @@ export default class Auth extends Command {
17
23
  */
18
24
  private createVolume;
19
25
  /**
20
- * Check if valid credentials exist in the volume
26
+ * Check if valid credentials exist in the volume.
27
+ * Don't check expiration - access tokens are short-lived but Claude Code
28
+ * handles token refresh internally using stored refresh tokens.
21
29
  */
22
30
  private credentialsExist;
23
31
  /**
@@ -28,5 +36,9 @@ export default class Auth extends Command {
28
36
  * Run the interactive login flow in a temporary container
29
37
  */
30
38
  private runLoginFlow;
39
+ /**
40
+ * Handle API key authentication flow
41
+ */
42
+ private handleApiKey;
31
43
  run(): Promise<void>;
32
44
  }
@@ -1,16 +1,22 @@
1
- import { Command, Flags } from '@oclif/core';
1
+ import { Flags } from '@oclif/core';
2
2
  import { execSync, spawnSync } from 'node:child_process';
3
+ import { PromptCommand } from '../../lib/prompt-command.js';
3
4
  import { colors } from '../../lib/colors.js';
4
5
  import { machineOutputFlags } from '../../lib/pmo/index.js';
5
6
  import { isDockerRunning } from '../../lib/execution/runners.js';
6
7
  import { shouldOutputJson, outputSuccessAsJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
8
+ import { findHQRoot } from '../../lib/workspace.js';
9
+ import { getWorkspaceDbPath } from '../../lib/workspace.js';
10
+ import { saveAuthMethod } from '../../lib/execution/config.js';
11
+ import Database from 'better-sqlite3';
7
12
  const CLAUDE_CREDENTIALS_VOLUME = 'claude-credentials';
8
- export default class Auth extends Command {
13
+ export default class Auth extends PromptCommand {
9
14
  static description = 'Set up Claude Code authentication for Docker containers (one-time setup)';
10
15
  static examples = [
11
16
  '<%= config.bin %> <%= command.id %>',
12
17
  '<%= config.bin %> <%= command.id %> --check',
13
18
  '<%= config.bin %> <%= command.id %> --force',
19
+ '<%= config.bin %> <%= command.id %> --api-key',
14
20
  ];
15
21
  static flags = {
16
22
  check: Flags.boolean({
@@ -21,8 +27,30 @@ export default class Auth extends Command {
21
27
  description: 'Force re-authentication even if credentials exist',
22
28
  default: false,
23
29
  }),
30
+ 'api-key': Flags.boolean({
31
+ description: 'Use ANTHROPIC_API_KEY instead of OAuth (saves preference)',
32
+ default: false,
33
+ }),
24
34
  ...machineOutputFlags,
25
35
  };
36
+ /**
37
+ * Try to open the workspace database for saving auth preferences.
38
+ * Returns null if not in an HQ directory (auth still works, just won't save preference).
39
+ */
40
+ tryOpenDb() {
41
+ try {
42
+ const hqPath = findHQRoot();
43
+ if (!hqPath)
44
+ return null;
45
+ const dbPath = getWorkspaceDbPath(hqPath);
46
+ const db = new Database(dbPath);
47
+ db.pragma('foreign_keys = ON');
48
+ return db;
49
+ }
50
+ catch {
51
+ return null;
52
+ }
53
+ }
26
54
  /**
27
55
  * Check if the claude-credentials volume exists
28
56
  */
@@ -47,19 +75,16 @@ export default class Auth extends Command {
47
75
  }
48
76
  }
49
77
  /**
50
- * Check if valid credentials exist in the volume
78
+ * Check if valid credentials exist in the volume.
79
+ * Don't check expiration - access tokens are short-lived but Claude Code
80
+ * handles token refresh internally using stored refresh tokens.
51
81
  */
52
82
  credentialsExist() {
53
83
  try {
54
84
  const result = execSync(`docker run --rm -v ${CLAUDE_CREDENTIALS_VOLUME}:/data alpine cat /data/.credentials.json 2>/dev/null`, { stdio: 'pipe', encoding: 'utf-8' });
55
- // Parse and validate the credentials
56
85
  const creds = JSON.parse(result);
57
- if (creds.claudeAiOauth?.accessToken && creds.claudeAiOauth?.expiresAt) {
58
- // Check if expired
59
- const expiresAt = creds.claudeAiOauth.expiresAt;
60
- if (expiresAt > Date.now()) {
61
- return true;
62
- }
86
+ if (creds.claudeAiOauth?.accessToken) {
87
+ return true;
63
88
  }
64
89
  return false;
65
90
  }
@@ -93,8 +118,7 @@ export default class Auth extends Command {
93
118
  this.log(colors.primary('🔐 Starting Claude Code authentication...'));
94
119
  this.log('');
95
120
  this.log(colors.text('A temporary container will start with Claude Code.'));
96
- this.log(colors.text('When prompted, type: /login'));
97
- this.log(colors.text('Then complete the browser authentication.'));
121
+ this.log(colors.text('Complete the browser authentication when prompted.'));
98
122
  this.log('');
99
123
  this.log(colors.textSecondary('Press Ctrl+C to cancel.'));
100
124
  this.log('');
@@ -108,7 +132,8 @@ export default class Auth extends Command {
108
132
  'node:20',
109
133
  'bash', '-c',
110
134
  // Install as root, then run claude as node user (so credentials have correct ownership)
111
- 'npm install -g @anthropic-ai/claude-code@latest --silent 2>/dev/null && chown -R node:node /home/node/.claude && echo "" && echo "Type: /login" && echo "" && su -s /bin/bash -c "HOME=/home/node CLAUDE_CONFIG_DIR=/home/node/.claude claude" node'
135
+ // Pass "/login" as initial prompt so user doesn't have to type it manually
136
+ 'npm install -g @anthropic-ai/claude-code@latest --silent 2>/dev/null && chown -R node:node /home/node/.claude && su -s /bin/bash -c "HOME=/home/node CLAUDE_CONFIG_DIR=/home/node/.claude claude \\"/login\\"" node'
112
137
  ], { stdio: 'inherit' });
113
138
  return result.status === 0;
114
139
  }
@@ -117,12 +142,74 @@ export default class Auth extends Command {
117
142
  return false;
118
143
  }
119
144
  }
145
+ /**
146
+ * Handle API key authentication flow
147
+ */
148
+ handleApiKey(jsonMode, flags) {
149
+ const apiKey = process.env.ANTHROPIC_API_KEY;
150
+ if (!apiKey) {
151
+ if (jsonMode) {
152
+ outputErrorAsJson('API_KEY_NOT_SET', 'ANTHROPIC_API_KEY is not set in your environment. Export it and try again.', createMetadata('agent auth', flags));
153
+ }
154
+ this.error('ANTHROPIC_API_KEY is not set in your environment.\nExport it with: export ANTHROPIC_API_KEY=sk-ant-...');
155
+ }
156
+ // Save preference to config if in an HQ
157
+ const db = this.tryOpenDb();
158
+ if (db) {
159
+ try {
160
+ saveAuthMethod(db, 'apikey');
161
+ }
162
+ finally {
163
+ db.close();
164
+ }
165
+ }
166
+ if (jsonMode) {
167
+ outputSuccessAsJson({
168
+ authenticated: true,
169
+ method: 'apikey',
170
+ message: 'ANTHROPIC_API_KEY is set. Auth method saved as default.',
171
+ }, createMetadata('agent auth', flags));
172
+ return;
173
+ }
174
+ this.log(colors.success('✓ ANTHROPIC_API_KEY is set'));
175
+ this.log(colors.textSecondary(' Auth method saved as default: apikey'));
176
+ this.log(colors.textSecondary(' Containers will use your API key for authentication.'));
177
+ this.log('');
178
+ this.log(colors.warning(' Note: This uses API credits, not your Max subscription.'));
179
+ this.log(colors.textSecondary(` Run "${this.config.bin} agent auth" (without --api-key) to switch to OAuth.`));
180
+ }
120
181
  async run() {
121
182
  const { flags } = await this.parse(Auth);
122
183
  // Check if JSON output mode is active
123
184
  const jsonMode = shouldOutputJson(flags);
124
- // Check Docker is running
185
+ const jsonModeConfig = jsonMode ? { flags, commandName: 'agent auth' } : null;
186
+ // Handle --api-key shortcut: validate key and save preference
187
+ if (flags['api-key']) {
188
+ this.handleApiKey(jsonMode, flags);
189
+ return;
190
+ }
191
+ // Check Docker is running (needed for OAuth flow)
125
192
  if (!isDockerRunning()) {
193
+ // If Docker isn't running, offer API key as alternative
194
+ const hasApiKey = !!process.env.ANTHROPIC_API_KEY;
195
+ if (hasApiKey && !flags.check) {
196
+ this.log(colors.warning('Docker is not running.'));
197
+ this.log('');
198
+ const { authChoice } = await this.prompt([{
199
+ type: 'list',
200
+ name: 'authChoice',
201
+ message: 'Docker is required for OAuth. Use API key instead?',
202
+ choices: [
203
+ { name: 'Use ANTHROPIC_API_KEY instead (API credits)', value: 'apikey', command: `${this.config.bin} agent auth --api-key --json` },
204
+ { name: 'Cancel (start Docker first for OAuth)', value: 'cancel' },
205
+ ],
206
+ default: 'apikey',
207
+ }], jsonModeConfig);
208
+ if (authChoice === 'apikey') {
209
+ this.handleApiKey(jsonMode, flags);
210
+ return;
211
+ }
212
+ }
126
213
  if (jsonMode) {
127
214
  outputErrorAsJson('DOCKER_NOT_RUNNING', 'Docker is not running. Please start Docker Desktop and try again.', createMetadata('agent auth', flags));
128
215
  }
@@ -144,6 +231,7 @@ export default class Auth extends Command {
144
231
  if (jsonMode) {
145
232
  outputSuccessAsJson({
146
233
  authenticated: true,
234
+ method: 'oauth',
147
235
  subscriptionType: info?.subscriptionType || 'unknown',
148
236
  expiresAt: info?.expiresAt.toISOString(),
149
237
  }, createMetadata('agent auth', flags));
@@ -169,6 +257,7 @@ export default class Auth extends Command {
169
257
  if (jsonMode) {
170
258
  outputSuccessAsJson({
171
259
  authenticated: true,
260
+ method: 'oauth',
172
261
  subscriptionType: info?.subscriptionType || 'unknown',
173
262
  expiresAt: info?.expiresAt.toISOString(),
174
263
  message: 'Credentials already configured. Use --force to re-authenticate.',
@@ -184,17 +273,49 @@ export default class Auth extends Command {
184
273
  this.log(colors.text('Use --force to re-authenticate.'));
185
274
  return;
186
275
  }
276
+ // Prompt for auth method choice (OAuth or API key)
277
+ const hasApiKey = !!process.env.ANTHROPIC_API_KEY;
278
+ // Only prompt if there's a real choice (API key is available)
279
+ let selectedMethod = 'oauth';
280
+ if (hasApiKey) {
281
+ const { selectedMethod: chosen } = await this.prompt([{
282
+ type: 'list',
283
+ name: 'selectedMethod',
284
+ message: 'Which authentication method would you like to use?',
285
+ choices: [
286
+ { name: 'OAuth (recommended — uses Max subscription)', value: 'oauth', command: `${this.config.bin} agent auth --force --json` },
287
+ { name: 'API key (uses API credits, not Max subscription)', value: 'apikey', command: `${this.config.bin} agent auth --api-key --json` },
288
+ ],
289
+ default: 'oauth',
290
+ }], jsonModeConfig);
291
+ selectedMethod = chosen;
292
+ }
293
+ if (selectedMethod === 'apikey') {
294
+ this.handleApiKey(jsonMode, flags);
295
+ return;
296
+ }
187
297
  // JSON mode cannot handle interactive login flow
188
298
  if (jsonMode) {
189
299
  outputErrorAsJson('INTERACTIVE_REQUIRED', 'Authentication requires interactive login. Run without --json flag to authenticate.', createMetadata('agent auth', flags));
190
300
  }
191
- // Run the login flow
301
+ // Run the OAuth login flow
192
302
  const success = this.runLoginFlow();
193
303
  if (success && this.credentialsExist()) {
304
+ // Save OAuth as auth method preference
305
+ const db = this.tryOpenDb();
306
+ if (db) {
307
+ try {
308
+ saveAuthMethod(db, 'oauth');
309
+ }
310
+ finally {
311
+ db.close();
312
+ }
313
+ }
194
314
  this.log('');
195
315
  this.log(colors.success('✓ Authentication successful!'));
196
316
  this.log(colors.textSecondary(' Credentials saved to Docker volume: ' + CLAUDE_CREDENTIALS_VOLUME));
197
317
  this.log(colors.textSecondary(' All agent containers will share these credentials.'));
318
+ this.log(colors.textSecondary(' Auth method saved as default: oauth'));
198
319
  }
199
320
  else {
200
321
  this.log('');
@@ -45,6 +45,7 @@ export default class Agent extends PMOCommand {
45
45
  { name: '🗑️ Remove agent', value: 'remove', command: 'prlt agent remove --machine' },
46
46
  // Management group
47
47
  { name: '👔 Manage staff agents', value: 'staff', command: 'prlt agent staff --machine' },
48
+ { name: '⏱️ Manage temp agents', value: 'temp', command: 'prlt agent temp --machine' },
48
49
  { name: '🧹 Cleanup agents', value: 'cleanup', command: 'prlt agent cleanup --machine' },
49
50
  { name: '🎨 Manage themes', value: 'themes', command: 'prlt agent themes --machine' },
50
51
  // Operations group
@@ -89,11 +90,19 @@ export default class Agent extends PMOCommand {
89
90
  break;
90
91
  }
91
92
  case 'staff': {
92
- const { default: StaffCommand } = await import('../staff/index.js');
93
+ const { default: StaffCommand } = await import('./staff/index.js');
93
94
  const cmd = new StaffCommand([], this.config);
94
95
  await cmd.run();
95
96
  break;
96
97
  }
98
+ case 'temp': {
99
+ // @ts-expect-error -- temp subcommand not yet implemented
100
+ // eslint-disable-next-line import/no-unresolved -- temp subcommand not yet implemented
101
+ const { default: TempCommand } = await import('./temp/index.js');
102
+ const cmd = new TempCommand([], this.config);
103
+ await cmd.run();
104
+ break;
105
+ }
97
106
  case 'cleanup': {
98
107
  const { default: CleanupCommand } = await import('./cleanup.js');
99
108
  const cmd = new CleanupCommand([], this.config);
@@ -101,7 +110,7 @@ export default class Agent extends PMOCommand {
101
110
  break;
102
111
  }
103
112
  case 'themes': {
104
- const { default: ThemeCommand } = await import('../theme/index.js');
113
+ const { default: ThemeCommand } = await import('./themes/index.js');
105
114
  const cmd = new ThemeCommand([], this.config);
106
115
  await cmd.run();
107
116
  break;
@@ -35,8 +35,8 @@ export default class List extends PMOCommand {
35
35
  const activeAgents = workspaceInfo.agents.filter(a => a.status === 'active');
36
36
  // Determine type filter - prompt if not provided (but not in JSON mode)
37
37
  let typeFilter = flags.type;
38
- // In JSON mode without type filter, output all agents grouped by type
39
- if (jsonMode && !typeFilter) {
38
+ // In JSON mode, output agents as JSON respecting the type filter
39
+ if (jsonMode) {
40
40
  const staffAgents = activeAgents.filter(a => a.type === 'persistent');
41
41
  const tempAgents = activeAgents.filter(a => a.type === 'ephemeral');
42
42
  const agentsStatus = getAllAgentsStatus(workspaceInfo);
@@ -56,11 +56,20 @@ export default class List extends PMOCommand {
56
56
  };
57
57
  });
58
58
  };
59
- const output = {
60
- staff: formatAgentJson(staffAgents),
61
- temp: formatAgentJson(tempAgents),
62
- all: formatAgentJson(activeAgents),
63
- };
59
+ let output;
60
+ if (typeFilter === 'staff') {
61
+ output = { staff: formatAgentJson(staffAgents) };
62
+ }
63
+ else if (typeFilter === 'temp') {
64
+ output = { temp: formatAgentJson(tempAgents) };
65
+ }
66
+ else {
67
+ // No filter or 'all' - return both groups without redundant combined array
68
+ output = {
69
+ staff: formatAgentJson(staffAgents),
70
+ temp: formatAgentJson(tempAgents),
71
+ };
72
+ }
64
73
  this.log(JSON.stringify(output, null, 2));
65
74
  return;
66
75
  }
@@ -0,0 +1 @@
1
+ export { default } from '../../staff/add.js';
@@ -0,0 +1 @@
1
+ export { default } from '../../staff/add.js';
@@ -0,0 +1,15 @@
1
+ import { PMOCommand } from '../../../lib/pmo/index.js';
2
+ export default class AgentStaff extends PMOCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ 'no-interactive': import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
+ machine: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
+ project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ };
11
+ protected getPMOOptions(): {
12
+ promptIfMultiple: boolean;
13
+ };
14
+ execute(): Promise<void>;
15
+ }
@@ -0,0 +1,83 @@
1
+ import { Flags } from '@oclif/core';
2
+ import inquirer from 'inquirer';
3
+ import { colors } from '../../../lib/colors.js';
4
+ import { PMOCommand, pmoBaseFlags } from '../../../lib/pmo/index.js';
5
+ import { shouldOutputJson, outputPromptAsJson, createMetadata, buildPromptConfig, } from '../../../lib/prompt-json.js';
6
+ export default class AgentStaff extends PMOCommand {
7
+ static description = 'Manage staff (persistent) agents';
8
+ static examples = [
9
+ '<%= config.bin %> <%= command.id %> list',
10
+ '<%= config.bin %> <%= command.id %> add',
11
+ '<%= config.bin %> <%= command.id %> remove camry',
12
+ ];
13
+ static flags = {
14
+ ...pmoBaseFlags,
15
+ 'no-interactive': Flags.boolean({
16
+ description: 'Alias for --json flag',
17
+ default: false,
18
+ }),
19
+ };
20
+ getPMOOptions() {
21
+ return { promptIfMultiple: false };
22
+ }
23
+ async execute() {
24
+ const { flags } = await this.parse(AgentStaff);
25
+ const jsonMode = shouldOutputJson(flags);
26
+ const menuChoices = [
27
+ { name: 'List staff agents', value: 'list', command: 'prlt agent staff list --machine' },
28
+ { name: 'Add staff agent', value: 'add', command: 'prlt agent staff add --machine' },
29
+ { name: 'Remove staff agent', value: 'remove', command: 'prlt agent staff remove --machine' },
30
+ { name: 'Cancel', value: 'cancel', command: '' },
31
+ ];
32
+ const message = 'What would you like to do?';
33
+ if (jsonMode) {
34
+ outputPromptAsJson(buildPromptConfig('list', 'action', message, menuChoices), createMetadata('agent staff', flags));
35
+ return;
36
+ }
37
+ this.log(colors.primary('Staff Agents'));
38
+ this.log(colors.textMuted('Persistent agents with dedicated worktrees.\n'));
39
+ const { action } = await this.prompt([{
40
+ type: 'list',
41
+ name: 'action',
42
+ message,
43
+ choices: [
44
+ { name: '📋 ' + menuChoices[0].name, value: menuChoices[0].value },
45
+ { name: '➕ ' + menuChoices[1].name, value: menuChoices[1].value },
46
+ { name: '🗑️ ' + menuChoices[2].name, value: menuChoices[2].value },
47
+ new inquirer.Separator(),
48
+ { name: '❌ ' + menuChoices[3].name, value: menuChoices[3].value },
49
+ ]
50
+ }], null);
51
+ if (action === 'cancel') {
52
+ this.log(colors.textMuted('Operation cancelled.'));
53
+ return;
54
+ }
55
+ try {
56
+ switch (action) {
57
+ case 'list': {
58
+ const { default: ListCommand } = await import('./list.js');
59
+ const cmd = new ListCommand([], this.config);
60
+ await cmd.run();
61
+ break;
62
+ }
63
+ case 'add': {
64
+ const { default: AddCommand } = await import('./add.js');
65
+ const cmd = new AddCommand([], this.config);
66
+ await cmd.run();
67
+ break;
68
+ }
69
+ case 'remove': {
70
+ const { default: RemoveCommand } = await import('./remove.js');
71
+ const cmd = new RemoveCommand([], this.config);
72
+ await cmd.run();
73
+ break;
74
+ }
75
+ default:
76
+ this.error(`Unknown action: ${action}`);
77
+ }
78
+ }
79
+ catch (error) {
80
+ this.error(`Failed to execute staff ${action}: ${error instanceof Error ? error.message : String(error)}`);
81
+ }
82
+ }
83
+ }
@@ -0,0 +1 @@
1
+ export { default } from '../../staff/list.js';
@@ -0,0 +1 @@
1
+ export { default } from '../../staff/list.js';
@@ -0,0 +1 @@
1
+ export { default } from '../../staff/remove.js';
@@ -0,0 +1 @@
1
+ export { default } from '../../staff/remove.js';
@@ -1,8 +1,8 @@
1
1
  import { Args } from '@oclif/core';
2
2
  import { colors, format } from '../../lib/colors.js';
3
- import { getWorkspaceInfo, getAgentStatus, formatAgentList } from '../../lib/agents/commands.js';
3
+ import { getWorkspaceInfo, getAgentStatus, getAllAgentsStatus, formatAgentList } from '../../lib/agents/commands.js';
4
4
  import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
5
- import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
5
+ import { shouldOutputJson, outputErrorAsJson, outputSuccessAsJson, createMetadata, } from '../../lib/prompt-json.js';
6
6
  export default class Status extends PMOCommand {
7
7
  static description = 'Show detailed status for a specific agent';
8
8
  static examples = [
@@ -36,6 +36,13 @@ export default class Status extends PMOCommand {
36
36
  return;
37
37
  }
38
38
  let agentName = args.name;
39
+ // In JSON mode with no agent specified, return all agent statuses
40
+ if (jsonMode && !agentName) {
41
+ const allStatuses = getAllAgentsStatus(workspaceInfo);
42
+ outputSuccessAsJson({
43
+ agents: allStatuses,
44
+ }, createMetadata('agent status', flags));
45
+ }
39
46
  // Agent mode config for prompts
40
47
  const agentConfig = jsonMode ? { flags, commandName: 'agent status' } : null;
41
48
  // Interactive mode if no agent specified
@@ -59,15 +66,36 @@ export default class Status extends PMOCommand {
59
66
  }], agentConfig);
60
67
  agentName = selected;
61
68
  }
62
- await this.showDetailedStatus(workspaceInfo, agentName);
69
+ await this.showDetailedStatus(workspaceInfo, agentName, jsonMode);
63
70
  }
64
- async showDetailedStatus(workspaceInfo, agentName) {
71
+ async showDetailedStatus(workspaceInfo, agentName, jsonMode = false) {
65
72
  // Validate agent exists
66
73
  const agent = workspaceInfo.agents.find((a) => a.name === agentName);
67
74
  if (!agent) {
68
75
  this.error(`Agent "${agentName}" not found. Available: ${formatAgentList(workspaceInfo.agents)}`);
69
76
  }
70
77
  const agentStatus = getAgentStatus(workspaceInfo, agentName);
78
+ // JSON output mode
79
+ if (jsonMode) {
80
+ this.log(JSON.stringify({
81
+ success: true,
82
+ agent: {
83
+ name: agentName,
84
+ type: agent.type,
85
+ exists: agentStatus.exists,
86
+ path: `${workspaceInfo.agentsPath}/${agentName}`,
87
+ branch: agentStatus.branch,
88
+ repositories: agentStatus.repositories.map(r => ({
89
+ name: r.name,
90
+ status: r.status,
91
+ commitsAhead: r.commitsAhead,
92
+ })),
93
+ assignedTickets: agentStatus.assignedTickets,
94
+ completedTickets: agentStatus.completedTickets,
95
+ },
96
+ }, null, 2));
97
+ return;
98
+ }
71
99
  this.log(format.title(`🤖 Agent: ${agentName}`));
72
100
  // Basic status
73
101
  const statusIcon = agentStatus.exists ? '🟢' : '🔴';
@@ -0,0 +1 @@
1
+ export { default } from '../../theme/add-names.js';
@@ -0,0 +1 @@
1
+ export { default } from '../../theme/add-names.js';
@@ -0,0 +1 @@
1
+ export { default } from '../../theme/create.js';
@@ -0,0 +1 @@
1
+ export { default } from '../../theme/create.js';
@@ -0,0 +1,10 @@
1
+ import { PromptCommand } from '../../../lib/prompt-command.js';
2
+ export default class AgentThemes extends PromptCommand {
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
+ };
9
+ run(): Promise<void>;
10
+ }