@proletariat/cli 0.3.35 → 0.3.40
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/README.md +37 -2
- package/bin/dev.js +0 -0
- package/dist/commands/agent/auth.d.ts +12 -2
- package/dist/commands/agent/auth.js +128 -4
- package/dist/commands/agent/list.js +16 -7
- package/dist/commands/agent/status.js +32 -4
- package/dist/commands/board/watch.js +6 -0
- package/dist/commands/branch/list.d.ts +1 -0
- package/dist/commands/branch/list.js +43 -12
- package/dist/commands/branch/where.js +9 -19
- package/dist/commands/category/list.d.ts +2 -1
- package/dist/commands/category/list.js +38 -13
- package/dist/commands/{claude.d.ts → claude/index.d.ts} +1 -1
- package/dist/commands/{claude.js → claude/index.js} +12 -12
- package/dist/commands/claude/open.d.ts +13 -0
- package/dist/commands/claude/open.js +175 -0
- package/dist/commands/diet.js +18 -2
- package/dist/commands/docker/logs.js +7 -3
- package/dist/commands/docker/shell.js +6 -0
- package/dist/commands/docker/start.js +20 -4
- package/dist/commands/docker/sync.d.ts +4 -0
- package/dist/commands/docker/sync.js +30 -2
- package/dist/commands/epic/show.d.ts +13 -0
- package/dist/commands/epic/show.js +16 -0
- package/dist/commands/epic/ticket.js +7 -24
- package/dist/commands/epic/view.js +27 -0
- package/dist/commands/execution/config.d.ts +0 -4
- package/dist/commands/execution/config.js +14 -46
- package/dist/commands/execution/index.js +2 -1
- package/dist/commands/execution/logs.js +7 -1
- package/dist/commands/execution/stop.js +2 -1
- package/dist/commands/execution/view.js +30 -26
- package/dist/commands/init.js +2 -19
- package/dist/commands/label/create.js +2 -1
- package/dist/commands/label/delete.js +2 -1
- package/dist/commands/label/group/create.js +2 -1
- package/dist/commands/label/group/list.js +2 -1
- package/dist/commands/label/list.js +2 -1
- package/dist/commands/mcp-server.js +27 -1
- package/dist/commands/phase/template/list.js +2 -1
- package/dist/commands/pmo/init.js +12 -40
- package/dist/commands/project/create.js +3 -4
- package/dist/commands/project/update.js +5 -6
- package/dist/commands/pull.js +24 -0
- package/dist/commands/qa/index.d.ts +54 -0
- package/dist/commands/qa/index.js +762 -0
- package/dist/commands/repo/view.js +2 -8
- package/dist/commands/session/attach.js +4 -4
- package/dist/commands/session/create.d.ts +19 -0
- package/dist/commands/session/create.js +102 -0
- package/dist/commands/session/health.js +4 -23
- package/dist/commands/session/index.js +14 -1
- package/dist/commands/session/list.js +9 -8
- package/dist/commands/session/peek.d.ts +38 -0
- package/dist/commands/session/peek.js +316 -0
- package/dist/commands/session/poke.d.ts +27 -0
- package/dist/commands/session/poke.js +219 -0
- package/dist/commands/spec/view.js +29 -0
- package/dist/commands/template/list.js +2 -1
- package/dist/commands/theme/add-names.d.ts +4 -0
- package/dist/commands/theme/add-names.js +11 -1
- package/dist/commands/theme/create.d.ts +2 -0
- package/dist/commands/theme/create.js +8 -0
- package/dist/commands/ticket/bulk.js +2 -2
- package/dist/commands/ticket/complete.js +2 -2
- package/dist/commands/ticket/create.js +21 -0
- package/dist/commands/ticket/delete.js +8 -0
- package/dist/commands/ticket/edit.js +25 -0
- package/dist/commands/ticket/epic.js +17 -43
- package/dist/commands/ticket/index.js +2 -2
- package/dist/commands/ticket/move.js +25 -2
- package/dist/commands/ticket/resolve.js +3 -4
- package/dist/commands/ticket/show.d.ts +13 -0
- package/dist/commands/ticket/show.js +16 -0
- package/dist/commands/ticket/template/list.js +2 -1
- package/dist/commands/ticket/view.d.ts +0 -1
- package/dist/commands/ticket/view.js +30 -1
- package/dist/commands/work/index.js +4 -0
- package/dist/commands/work/spawn-all.js +1 -1
- package/dist/commands/work/spawn.js +15 -4
- package/dist/commands/work/start.js +186 -103
- package/dist/commands/work/status.d.ts +14 -0
- package/dist/commands/work/status.js +60 -0
- package/dist/commands/work/watch.js +1 -1
- package/dist/commands/workflow/index.js +2 -1
- package/dist/commands/workflow/show.d.ts +13 -0
- package/dist/commands/workflow/show.js +16 -0
- package/dist/commands/workspace/add.js +15 -0
- package/dist/commands/workspace/list.js +2 -1
- package/dist/commands/workspace/prune.js +7 -7
- package/dist/hooks/init.js +10 -2
- package/dist/lib/agents/commands.d.ts +5 -0
- package/dist/lib/agents/commands.js +143 -97
- package/dist/lib/branch/index.d.ts +1 -0
- package/dist/lib/database/drizzle-schema.d.ts +465 -0
- package/dist/lib/database/drizzle-schema.js +53 -0
- package/dist/lib/database/index.d.ts +47 -1
- package/dist/lib/database/index.js +138 -20
- package/dist/lib/execution/config.d.ts +15 -1
- package/dist/lib/execution/config.js +28 -0
- package/dist/lib/execution/runners.d.ts +45 -0
- package/dist/lib/execution/runners.js +187 -26
- package/dist/lib/execution/session-utils.d.ts +16 -1
- package/dist/lib/execution/session-utils.js +71 -4
- package/dist/lib/execution/spawner.js +15 -2
- package/dist/lib/execution/storage.d.ts +6 -1
- package/dist/lib/execution/storage.js +35 -5
- package/dist/lib/execution/types.d.ts +3 -0
- package/dist/lib/mcp/tools/board.js +4 -6
- package/dist/lib/mcp/tools/cli-passthrough.js +25 -6
- package/dist/lib/mcp/tools/epic.js +8 -3
- package/dist/lib/mcp/tools/index.d.ts +1 -0
- package/dist/lib/mcp/tools/index.js +1 -0
- package/dist/lib/mcp/tools/spec.js +1 -1
- package/dist/lib/mcp/tools/ticket.js +11 -9
- package/dist/lib/mcp/tools/tmux.d.ts +16 -0
- package/dist/lib/mcp/tools/tmux.js +182 -0
- package/dist/lib/mcp/tools/work.js +148 -6
- package/dist/lib/mcp/types.d.ts +10 -0
- package/dist/lib/multiline-input.js +2 -1
- package/dist/lib/pmo/base-command.js +4 -4
- package/dist/lib/pmo/schema.d.ts +1 -1
- package/dist/lib/pmo/schema.js +1 -0
- package/dist/lib/pmo/storage/actions.js +1 -1
- package/dist/lib/pmo/storage/base.js +402 -50
- package/dist/lib/pmo/storage/dependencies.d.ts +1 -0
- package/dist/lib/pmo/storage/dependencies.js +11 -3
- package/dist/lib/pmo/storage/epics.js +1 -1
- package/dist/lib/pmo/storage/helpers.d.ts +4 -4
- package/dist/lib/pmo/storage/helpers.js +36 -26
- package/dist/lib/pmo/storage/projects.d.ts +2 -0
- package/dist/lib/pmo/storage/projects.js +207 -119
- package/dist/lib/pmo/storage/specs.d.ts +2 -0
- package/dist/lib/pmo/storage/specs.js +274 -188
- package/dist/lib/pmo/storage/tickets.d.ts +2 -0
- package/dist/lib/pmo/storage/tickets.js +350 -290
- package/dist/lib/pmo/storage/types.d.ts +1 -0
- package/dist/lib/pmo/storage/views.d.ts +2 -0
- package/dist/lib/pmo/storage/views.js +183 -130
- package/dist/lib/prompt-command.d.ts +20 -0
- package/dist/lib/prompt-command.js +38 -2
- package/dist/lib/prompt-json.d.ts +41 -4
- package/dist/lib/prompt-json.js +138 -7
- package/dist/lib/styles.d.ts +37 -0
- package/dist/lib/styles.js +73 -0
- package/oclif.manifest.json +4046 -3385
- package/package.json +11 -6
- package/LICENSE +0 -190
package/README.md
CHANGED
|
@@ -24,7 +24,10 @@
|
|
|
24
24
|
**prlt** is an agent orchestration platform for AI labor. Spin up workers on demand, coordinate multi-agent development from one CLI. Isolated workspaces, secure containers, persistent state.
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
|
-
|
|
27
|
+
brew install chrismcdermut/proletariat/prlt # macOS (Homebrew)
|
|
28
|
+
# or
|
|
29
|
+
npm install -g @proletariat/cli # any platform (npm)
|
|
30
|
+
|
|
28
31
|
prlt init
|
|
29
32
|
prlt ticket create --title "Add OAuth" --category feature
|
|
30
33
|
prlt work spawn # Interactive: select tickets, environment, action
|
|
@@ -48,7 +51,10 @@ Agent spawns in its own branch, writes code, opens PR. You review and merge.
|
|
|
48
51
|
# Quick Start
|
|
49
52
|
|
|
50
53
|
```bash
|
|
51
|
-
npm install -g @proletariat/cli # Install
|
|
54
|
+
npm install -g @proletariat/cli # Install (npm)
|
|
55
|
+
# or
|
|
56
|
+
brew install chrismcdermut/proletariat/prlt # Install (Homebrew, macOS)
|
|
57
|
+
|
|
52
58
|
prlt init # Create HQ, add repos, choose theme
|
|
53
59
|
prlt ticket create --title "Add OAuth" --category feature
|
|
54
60
|
prlt work spawn # Interactive: select tickets, environment, action
|
|
@@ -117,6 +123,35 @@ Select tickets to spawn, grouped by priority:
|
|
|
117
123
|
| Starting agents is heavyweight | **Ephemeral** - Spawn on demand, they work, they PR, they're done |
|
|
118
124
|
| Context lost between agent runs | **Persistent** - Tickets accumulate context, hand off between agents |
|
|
119
125
|
|
|
126
|
+
### Installation
|
|
127
|
+
|
|
128
|
+
#### Homebrew (macOS)
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
brew install chrismcdermut/proletariat/prlt
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Works on both Apple Silicon (arm64) and Intel (x86_64) Macs.
|
|
135
|
+
|
|
136
|
+
**Upgrade:**
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
brew update
|
|
140
|
+
brew upgrade prlt
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**Verify:**
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
prlt --version
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
#### npm (all platforms)
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
npm install -g @proletariat/cli
|
|
153
|
+
```
|
|
154
|
+
|
|
120
155
|
### Data Model
|
|
121
156
|
|
|
122
157
|
```
|
package/bin/dev.js
CHANGED
|
File without changes
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export default class Auth extends
|
|
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
|
*/
|
|
@@ -30,5 +36,9 @@ export default class Auth extends Command {
|
|
|
30
36
|
* Run the interactive login flow in a temporary container
|
|
31
37
|
*/
|
|
32
38
|
private runLoginFlow;
|
|
39
|
+
/**
|
|
40
|
+
* Handle API key authentication flow
|
|
41
|
+
*/
|
|
42
|
+
private handleApiKey;
|
|
33
43
|
run(): Promise<void>;
|
|
34
44
|
}
|
|
@@ -1,16 +1,22 @@
|
|
|
1
|
-
import {
|
|
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
|
|
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
|
*/
|
|
@@ -114,12 +142,74 @@ export default class Auth extends Command {
|
|
|
114
142
|
return false;
|
|
115
143
|
}
|
|
116
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
|
+
}
|
|
117
181
|
async run() {
|
|
118
182
|
const { flags } = await this.parse(Auth);
|
|
119
183
|
// Check if JSON output mode is active
|
|
120
184
|
const jsonMode = shouldOutputJson(flags);
|
|
121
|
-
|
|
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)
|
|
122
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
|
+
}
|
|
123
213
|
if (jsonMode) {
|
|
124
214
|
outputErrorAsJson('DOCKER_NOT_RUNNING', 'Docker is not running. Please start Docker Desktop and try again.', createMetadata('agent auth', flags));
|
|
125
215
|
}
|
|
@@ -141,6 +231,7 @@ export default class Auth extends Command {
|
|
|
141
231
|
if (jsonMode) {
|
|
142
232
|
outputSuccessAsJson({
|
|
143
233
|
authenticated: true,
|
|
234
|
+
method: 'oauth',
|
|
144
235
|
subscriptionType: info?.subscriptionType || 'unknown',
|
|
145
236
|
expiresAt: info?.expiresAt.toISOString(),
|
|
146
237
|
}, createMetadata('agent auth', flags));
|
|
@@ -166,6 +257,7 @@ export default class Auth extends Command {
|
|
|
166
257
|
if (jsonMode) {
|
|
167
258
|
outputSuccessAsJson({
|
|
168
259
|
authenticated: true,
|
|
260
|
+
method: 'oauth',
|
|
169
261
|
subscriptionType: info?.subscriptionType || 'unknown',
|
|
170
262
|
expiresAt: info?.expiresAt.toISOString(),
|
|
171
263
|
message: 'Credentials already configured. Use --force to re-authenticate.',
|
|
@@ -181,17 +273,49 @@ export default class Auth extends Command {
|
|
|
181
273
|
this.log(colors.text('Use --force to re-authenticate.'));
|
|
182
274
|
return;
|
|
183
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
|
+
}
|
|
184
297
|
// JSON mode cannot handle interactive login flow
|
|
185
298
|
if (jsonMode) {
|
|
186
299
|
outputErrorAsJson('INTERACTIVE_REQUIRED', 'Authentication requires interactive login. Run without --json flag to authenticate.', createMetadata('agent auth', flags));
|
|
187
300
|
}
|
|
188
|
-
// Run the login flow
|
|
301
|
+
// Run the OAuth login flow
|
|
189
302
|
const success = this.runLoginFlow();
|
|
190
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
|
+
}
|
|
191
314
|
this.log('');
|
|
192
315
|
this.log(colors.success('✓ Authentication successful!'));
|
|
193
316
|
this.log(colors.textSecondary(' Credentials saved to Docker volume: ' + CLAUDE_CREDENTIALS_VOLUME));
|
|
194
317
|
this.log(colors.textSecondary(' All agent containers will share these credentials.'));
|
|
318
|
+
this.log(colors.textSecondary(' Auth method saved as default: oauth'));
|
|
195
319
|
}
|
|
196
320
|
else {
|
|
197
321
|
this.log('');
|
|
@@ -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
|
|
39
|
-
if (jsonMode
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
}
|
|
@@ -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 ? '🟢' : '🔴';
|
|
@@ -3,6 +3,7 @@ import * as fs from 'node:fs';
|
|
|
3
3
|
import * as path from 'node:path';
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import { PMOCommand, pmoBaseFlags, runWatcherForeground } from '../../lib/pmo/index.js';
|
|
6
|
+
import { shouldOutputJson } from '../../lib/prompt-json.js';
|
|
6
7
|
import { styles } from '../../lib/styles.js';
|
|
7
8
|
export default class BoardWatch extends PMOCommand {
|
|
8
9
|
static description = 'Watch board.md for changes and auto-sync to SQLite';
|
|
@@ -23,6 +24,11 @@ export default class BoardWatch extends PMOCommand {
|
|
|
23
24
|
}
|
|
24
25
|
async execute() {
|
|
25
26
|
const { flags } = await this.parse(BoardWatch);
|
|
27
|
+
if (shouldOutputJson(flags)) {
|
|
28
|
+
this.log(JSON.stringify({ type: 'error', error: { code: 'REQUIRES_TTY', message: 'board watch is a long-running interactive command. Use "prlt board" for non-interactive board data.' } }));
|
|
29
|
+
this.exit(1);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
26
32
|
// Load PMO config
|
|
27
33
|
const configPath = path.join(this.pmoPath, 'config.json');
|
|
28
34
|
if (!fs.existsSync(configPath)) {
|
|
@@ -2,6 +2,7 @@ import { Flags } from '@oclif/core';
|
|
|
2
2
|
import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
|
|
3
3
|
import { styles } from '../../lib/styles.js';
|
|
4
4
|
import { BRANCH_TYPES, listBranches, isGitRepo, } from '../../lib/branch/index.js';
|
|
5
|
+
import { getWorkspaceRepoInfo } from '../../lib/repos/index.js';
|
|
5
6
|
import { isNonTTY } from '../../lib/prompt-json.js';
|
|
6
7
|
import { visualPadEnd } from '../../lib/string-utils.js';
|
|
7
8
|
export default class BranchList extends PMOCommand {
|
|
@@ -40,12 +41,15 @@ export default class BranchList extends PMOCommand {
|
|
|
40
41
|
if (flags.format === 'table' && isNonTTY()) {
|
|
41
42
|
flags.format = 'json';
|
|
42
43
|
}
|
|
43
|
-
|
|
44
|
-
if (
|
|
45
|
-
|
|
44
|
+
let branches;
|
|
45
|
+
if (isGitRepo()) {
|
|
46
|
+
// In a git repo - list branches for current repo
|
|
47
|
+
branches = listBranches(undefined, flags.all);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
// Not in a git repo - list branches across all registered HQ repos
|
|
51
|
+
branches = this.listBranchesAcrossRepos(flags.all);
|
|
46
52
|
}
|
|
47
|
-
// Get branches
|
|
48
|
-
let branches = listBranches(undefined, flags.all);
|
|
49
53
|
// Filter by type
|
|
50
54
|
if (flags.type) {
|
|
51
55
|
branches = branches.filter((b) => b.type === flags.type);
|
|
@@ -59,31 +63,55 @@ export default class BranchList extends PMOCommand {
|
|
|
59
63
|
}
|
|
60
64
|
return;
|
|
61
65
|
}
|
|
66
|
+
// Check if we have multi-repo results
|
|
67
|
+
const hasRepoInfo = branches.some((b) => b.repo);
|
|
62
68
|
// Output based on format
|
|
63
69
|
switch (flags.format) {
|
|
64
70
|
case 'json':
|
|
65
71
|
this.log(JSON.stringify(branches, null, 2));
|
|
66
72
|
break;
|
|
67
73
|
case 'compact':
|
|
68
|
-
this.outputCompact(branches);
|
|
74
|
+
this.outputCompact(branches, hasRepoInfo);
|
|
69
75
|
break;
|
|
70
76
|
case 'table':
|
|
71
77
|
default:
|
|
72
|
-
this.outputTable(branches);
|
|
78
|
+
this.outputTable(branches, hasRepoInfo);
|
|
73
79
|
break;
|
|
74
80
|
}
|
|
75
81
|
}
|
|
76
|
-
|
|
82
|
+
listBranchesAcrossRepos(includeRemote) {
|
|
83
|
+
let repoInfo;
|
|
84
|
+
try {
|
|
85
|
+
repoInfo = getWorkspaceRepoInfo();
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
this.error('Not in a git repository and no HQ workspace found.');
|
|
89
|
+
}
|
|
90
|
+
const allBranches = [];
|
|
91
|
+
for (const repo of repoInfo.repositories) {
|
|
92
|
+
if (repo.status === 'missing')
|
|
93
|
+
continue;
|
|
94
|
+
const repoBranches = listBranches(repo.fullPath, includeRemote);
|
|
95
|
+
for (const branch of repoBranches) {
|
|
96
|
+
branch.repo = repo.name;
|
|
97
|
+
allBranches.push(branch);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return allBranches;
|
|
101
|
+
}
|
|
102
|
+
outputTable(branches, hasRepoInfo = false) {
|
|
77
103
|
this.log('');
|
|
78
104
|
this.log(styles.header(`🌿 Branches (${branches.length})`));
|
|
79
105
|
this.log('');
|
|
80
106
|
// Header
|
|
81
|
-
|
|
107
|
+
const repoHeader = hasRepoInfo ? visualPadEnd('Repo', 18) : '';
|
|
108
|
+
this.log(styles.muted(repoHeader +
|
|
109
|
+
visualPadEnd('Name', 35) +
|
|
82
110
|
visualPadEnd('Type', 8) +
|
|
83
111
|
visualPadEnd('Owner', 12) +
|
|
84
112
|
visualPadEnd('Description', 25) +
|
|
85
113
|
'Status'));
|
|
86
|
-
this.log('─'.repeat(90));
|
|
114
|
+
this.log('─'.repeat(hasRepoInfo ? 108 : 90));
|
|
87
115
|
// Rows
|
|
88
116
|
for (const branch of branches) {
|
|
89
117
|
const marker = branch.current ? '* ' : ' ';
|
|
@@ -91,6 +119,7 @@ export default class BranchList extends PMOCommand {
|
|
|
91
119
|
const typeDisplay = branch.type || '-';
|
|
92
120
|
const ownerDisplay = branch.owner || '-';
|
|
93
121
|
const descDisplay = branch.description || '-';
|
|
122
|
+
const repoCol = hasRepoInfo ? visualPadEnd((branch.repo || '-').substring(0, 16), 18) : '';
|
|
94
123
|
let status = 'local';
|
|
95
124
|
if (branch.tracking) {
|
|
96
125
|
status = `tracking ${branch.tracking}`;
|
|
@@ -99,6 +128,7 @@ export default class BranchList extends PMOCommand {
|
|
|
99
128
|
status = 'current';
|
|
100
129
|
}
|
|
101
130
|
this.log(marker +
|
|
131
|
+
repoCol +
|
|
102
132
|
nameStyle(visualPadEnd(branch.name.substring(0, 33), 33)) +
|
|
103
133
|
visualPadEnd(typeDisplay, 8) +
|
|
104
134
|
visualPadEnd(ownerDisplay.substring(0, 10), 12) +
|
|
@@ -109,14 +139,15 @@ export default class BranchList extends PMOCommand {
|
|
|
109
139
|
this.log(styles.muted('Legend: * = current branch'));
|
|
110
140
|
this.log('');
|
|
111
141
|
}
|
|
112
|
-
outputCompact(branches) {
|
|
142
|
+
outputCompact(branches, hasRepoInfo = false) {
|
|
113
143
|
this.log('');
|
|
114
144
|
for (const branch of branches) {
|
|
115
145
|
const marker = branch.current ? '* ' : ' ';
|
|
116
146
|
const icon = branch.type ? '🌿' : ' ';
|
|
117
147
|
const typeInfo = branch.type ? ` (${branch.type})` : '';
|
|
148
|
+
const repoPrefix = hasRepoInfo && branch.repo ? `[${branch.repo}] ` : '';
|
|
118
149
|
const nameStyle = branch.current ? styles.success : (s) => s;
|
|
119
|
-
this.log(`${icon} ${marker}${nameStyle(branch.name)}${styles.muted(typeInfo)}`);
|
|
150
|
+
this.log(`${icon} ${marker}${repoPrefix}${nameStyle(branch.name)}${styles.muted(typeInfo)}`);
|
|
120
151
|
}
|
|
121
152
|
this.log('');
|
|
122
153
|
}
|
|
@@ -2,9 +2,10 @@ import { Args } from '@oclif/core';
|
|
|
2
2
|
import { execSync } from 'node:child_process';
|
|
3
3
|
import * as path from 'node:path';
|
|
4
4
|
import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
|
|
5
|
+
import { shouldOutputJson } from '../../lib/prompt-json.js';
|
|
5
6
|
import { styles } from '../../lib/styles.js';
|
|
6
7
|
import { isGitRepo, isTicketId } from '../../lib/branch/index.js';
|
|
7
|
-
import {
|
|
8
|
+
import { findWorktreesByBranch as findDbWorktreesByBranch } from '../../lib/database/index.js';
|
|
8
9
|
export default class BranchWhere extends PMOCommand {
|
|
9
10
|
static description = 'Find which directory a branch is checked out in';
|
|
10
11
|
static examples = [
|
|
@@ -40,7 +41,7 @@ export default class BranchWhere extends PMOCommand {
|
|
|
40
41
|
// Combine results, preferring git worktree info
|
|
41
42
|
const allMatches = this.mergeResults(matches, dbMatches);
|
|
42
43
|
if (allMatches.length === 0) {
|
|
43
|
-
if (flags
|
|
44
|
+
if (shouldOutputJson(flags)) {
|
|
44
45
|
this.log(JSON.stringify({ found: false, search, matches: [] }, null, 2));
|
|
45
46
|
}
|
|
46
47
|
else {
|
|
@@ -48,7 +49,7 @@ export default class BranchWhere extends PMOCommand {
|
|
|
48
49
|
}
|
|
49
50
|
return;
|
|
50
51
|
}
|
|
51
|
-
if (flags
|
|
52
|
+
if (shouldOutputJson(flags)) {
|
|
52
53
|
this.log(JSON.stringify({
|
|
53
54
|
found: true,
|
|
54
55
|
search,
|
|
@@ -147,24 +148,13 @@ export default class BranchWhere extends PMOCommand {
|
|
|
147
148
|
const workspacePath = this.getWorkspacePath();
|
|
148
149
|
if (!workspacePath)
|
|
149
150
|
return [];
|
|
150
|
-
const db = openWorkspaceDatabase(workspacePath);
|
|
151
151
|
const searchLower = search.toLowerCase();
|
|
152
152
|
const isTicket = isTicketId(search);
|
|
153
|
-
//
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
query = 'SELECT * FROM agent_worktrees WHERE LOWER(branch) LIKE ?';
|
|
159
|
-
params = [`${searchLower}/%`];
|
|
160
|
-
}
|
|
161
|
-
else {
|
|
162
|
-
// Match branches containing the search term
|
|
163
|
-
query = 'SELECT * FROM agent_worktrees WHERE LOWER(branch) LIKE ?';
|
|
164
|
-
params = [`%${searchLower}%`];
|
|
165
|
-
}
|
|
166
|
-
const rows = db.prepare(query).all(...params);
|
|
167
|
-
db.close();
|
|
153
|
+
// Build LIKE pattern based on search type
|
|
154
|
+
const branchPattern = isTicket
|
|
155
|
+
? `${searchLower}/%` // Match branches starting with ticket ID
|
|
156
|
+
: `%${searchLower}%`; // Match branches containing the search term
|
|
157
|
+
const rows = findDbWorktreesByBranch(workspacePath, branchPattern);
|
|
168
158
|
return rows.map(row => ({
|
|
169
159
|
path: path.join(workspacePath, row.worktree_path),
|
|
170
160
|
branch: row.branch,
|
|
@@ -3,7 +3,7 @@ export default class CategoryList extends PMOCommand {
|
|
|
3
3
|
static description: string;
|
|
4
4
|
static examples: string[];
|
|
5
5
|
static flags: {
|
|
6
|
-
type: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
6
|
+
type: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
7
|
builtin: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
8
|
custom: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
9
9
|
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
@@ -14,5 +14,6 @@ export default class CategoryList extends PMOCommand {
|
|
|
14
14
|
promptIfMultiple: boolean;
|
|
15
15
|
};
|
|
16
16
|
execute(): Promise<void>;
|
|
17
|
+
private printCategoryGroup;
|
|
17
18
|
private printCategory;
|
|
18
19
|
}
|