@proletariat/cli 0.3.25 → 0.3.27
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/dist/commands/action/index.js +2 -2
- package/dist/commands/action/show.js +7 -1
- package/dist/commands/agent/auth.js +1 -1
- package/dist/commands/agent/cleanup.js +6 -6
- package/dist/commands/agent/discover.js +1 -1
- package/dist/commands/agent/remove.js +4 -4
- package/dist/commands/autocomplete/setup.d.ts +2 -2
- package/dist/commands/autocomplete/setup.js +5 -5
- package/dist/commands/branch/create.js +31 -30
- package/dist/commands/branch/list.js +14 -11
- package/dist/commands/branch/validate.js +10 -1
- package/dist/commands/category/create.js +4 -5
- package/dist/commands/category/delete.js +2 -3
- package/dist/commands/category/rename.js +2 -3
- package/dist/commands/claude.d.ts +2 -8
- package/dist/commands/claude.js +26 -26
- package/dist/commands/commit.d.ts +2 -8
- package/dist/commands/commit.js +4 -26
- package/dist/commands/config/index.d.ts +2 -10
- package/dist/commands/config/index.js +8 -34
- package/dist/commands/docker/clean.js +7 -9
- package/dist/commands/docker/index.d.ts +2 -2
- package/dist/commands/docker/index.js +13 -12
- package/dist/commands/docker/list.d.ts +1 -0
- package/dist/commands/docker/list.js +31 -17
- package/dist/commands/docker/status.d.ts +3 -1
- package/dist/commands/docker/status.js +28 -2
- package/dist/commands/docker/sync.js +7 -6
- package/dist/commands/epic/delete.js +4 -5
- package/dist/commands/epic/list.js +17 -2
- package/dist/commands/execution/list.js +25 -17
- package/dist/commands/feedback/submit.d.ts +2 -2
- package/dist/commands/feedback/submit.js +9 -9
- package/dist/commands/link/index.js +2 -2
- package/dist/commands/pmo/init.d.ts +2 -2
- package/dist/commands/pmo/init.js +29 -10
- package/dist/commands/project/spec.js +6 -6
- package/dist/commands/repo/list.js +14 -8
- package/dist/commands/repo/view.js +2 -1
- package/dist/commands/roadmap/list.js +16 -1
- package/dist/commands/session/health.d.ts +29 -0
- package/dist/commands/session/health.js +496 -0
- package/dist/commands/session/index.js +4 -0
- package/dist/commands/session/list.js +15 -8
- package/dist/commands/spec/edit.js +2 -3
- package/dist/commands/staff/add.d.ts +2 -2
- package/dist/commands/staff/add.js +15 -14
- package/dist/commands/staff/index.js +2 -2
- package/dist/commands/staff/list.d.ts +3 -1
- package/dist/commands/staff/list.js +15 -1
- package/dist/commands/staff/remove.js +4 -4
- package/dist/commands/status/index.js +6 -7
- package/dist/commands/template/apply.js +10 -11
- package/dist/commands/template/create.js +18 -17
- package/dist/commands/template/index.d.ts +2 -2
- package/dist/commands/template/index.js +6 -6
- package/dist/commands/template/save.js +8 -7
- package/dist/commands/template/update.js +6 -7
- package/dist/commands/terminal/title.d.ts +2 -26
- package/dist/commands/terminal/title.js +4 -33
- package/dist/commands/theme/index.d.ts +2 -2
- package/dist/commands/theme/index.js +19 -18
- package/dist/commands/theme/list.d.ts +3 -0
- package/dist/commands/theme/list.js +25 -0
- package/dist/commands/theme/set.d.ts +2 -2
- package/dist/commands/theme/set.js +5 -5
- package/dist/commands/ticket/complete.js +4 -1
- package/dist/commands/ticket/create.d.ts +1 -0
- package/dist/commands/ticket/create.js +64 -16
- package/dist/commands/ticket/delete.js +18 -16
- package/dist/commands/ticket/edit.js +22 -14
- package/dist/commands/ticket/epic.js +12 -10
- package/dist/commands/ticket/list.js +24 -5
- package/dist/commands/ticket/move.js +4 -1
- package/dist/commands/ticket/project.js +11 -9
- package/dist/commands/ticket/reassign.js +23 -19
- package/dist/commands/ticket/spec.js +7 -5
- package/dist/commands/ticket/update.js +55 -53
- package/dist/commands/ticket/view.js +4 -2
- package/dist/commands/whoami.d.ts +3 -0
- package/dist/commands/whoami.js +22 -4
- package/dist/commands/work/complete.js +2 -2
- package/dist/commands/work/ready.js +9 -9
- package/dist/commands/work/revise.js +15 -13
- package/dist/commands/work/spawn.js +154 -57
- package/dist/commands/work/start.d.ts +1 -0
- package/dist/commands/work/start.js +299 -177
- package/dist/commands/workspace/prune.d.ts +3 -2
- package/dist/commands/workspace/prune.js +70 -10
- package/dist/hooks/init.js +4 -0
- package/dist/lib/agents/commands.js +4 -0
- package/dist/lib/agents/index.js +12 -0
- package/dist/lib/execution/devcontainer.d.ts +4 -0
- package/dist/lib/execution/devcontainer.js +63 -0
- package/dist/lib/mcp/helpers.d.ts +15 -0
- package/dist/lib/mcp/helpers.js +15 -0
- package/dist/lib/mcp/tools/action.js +5 -5
- package/dist/lib/mcp/tools/board.js +7 -7
- package/dist/lib/mcp/tools/category.js +5 -5
- package/dist/lib/mcp/tools/cli-passthrough.js +30 -30
- package/dist/lib/mcp/tools/epic.js +8 -8
- package/dist/lib/mcp/tools/phase.js +7 -7
- package/dist/lib/mcp/tools/project.js +10 -10
- package/dist/lib/mcp/tools/roadmap.js +7 -7
- package/dist/lib/mcp/tools/spec.js +9 -9
- package/dist/lib/mcp/tools/status.js +6 -6
- package/dist/lib/mcp/tools/template.js +6 -6
- package/dist/lib/mcp/tools/ticket.js +19 -19
- package/dist/lib/mcp/tools/view.js +4 -4
- package/dist/lib/mcp/tools/work.js +6 -6
- package/dist/lib/mcp/tools/workflow.js +5 -5
- package/dist/lib/pmo/index.js +4 -0
- package/dist/lib/pmo/storage/base.js +49 -0
- package/dist/lib/pr/index.d.ts +9 -0
- package/dist/lib/pr/index.js +101 -14
- package/dist/lib/prompt-command.d.ts +3 -0
- package/dist/lib/prompt-json.d.ts +72 -1
- package/dist/lib/prompt-json.js +46 -0
- package/dist/lib/repos/index.js +4 -0
- package/dist/lib/string-utils.d.ts +10 -0
- package/dist/lib/string-utils.js +16 -0
- package/oclif.manifest.json +594 -449
- package/package.json +3 -2
package/dist/commands/claude.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
2
|
import * as fs from 'node:fs';
|
|
3
3
|
import * as path from 'node:path';
|
|
4
4
|
import * as os from 'node:os';
|
|
5
5
|
import { execSync } from 'node:child_process';
|
|
6
|
-
import
|
|
6
|
+
import { PromptCommand } from '../lib/prompt-command.js';
|
|
7
7
|
import Database from 'better-sqlite3';
|
|
8
8
|
import { findHQRoot } from '../lib/workspace.js';
|
|
9
9
|
import { getWorkspaceInfo, createEphemeralAgent, } from '../lib/agents/commands.js';
|
|
10
|
-
import { shouldOutputJson,
|
|
10
|
+
import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../lib/prompt-json.js';
|
|
11
11
|
import { styles } from '../lib/styles.js';
|
|
12
12
|
import { DEFAULT_EXECUTION_CONFIG, } from '../lib/execution/types.js';
|
|
13
13
|
import { runExecution, isDockerRunning, isGitHubTokenAvailable, isDevcontainerCliInstalled } from '../lib/execution/runners.js';
|
|
@@ -40,7 +40,7 @@ function isGitRepo(dir) {
|
|
|
40
40
|
return false;
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
-
export default class Claude extends
|
|
43
|
+
export default class Claude extends PromptCommand {
|
|
44
44
|
static description = 'Quick launch Claude Code for ad-hoc sessions (works anywhere)';
|
|
45
45
|
static examples = [
|
|
46
46
|
'<%= config.bin %> <%= command.id %>',
|
|
@@ -90,28 +90,6 @@ export default class Claude extends Command {
|
|
|
90
90
|
description: 'Ticket title (inside HQ only)',
|
|
91
91
|
}),
|
|
92
92
|
};
|
|
93
|
-
/**
|
|
94
|
-
* Prompt wrapper - handles both JSON mode and interactive mode.
|
|
95
|
-
* In JSON mode: outputs prompt as JSON and exits.
|
|
96
|
-
* In interactive mode: calls inquirer.prompt normally.
|
|
97
|
-
*/
|
|
98
|
-
async prompt(questions, jsonModeConfig) {
|
|
99
|
-
if (jsonModeConfig && isAgentMode(jsonModeConfig.flags)) {
|
|
100
|
-
const firstQuestion = questions[0];
|
|
101
|
-
if (firstQuestion) {
|
|
102
|
-
const choices = firstQuestion.choices ? normalizeChoices(firstQuestion.choices) : undefined;
|
|
103
|
-
outputPromptAsJson({
|
|
104
|
-
type: firstQuestion.type,
|
|
105
|
-
name: firstQuestion.name,
|
|
106
|
-
message: firstQuestion.message,
|
|
107
|
-
choices,
|
|
108
|
-
default: firstQuestion.default,
|
|
109
|
-
}, createMetadata(jsonModeConfig.commandName, jsonModeConfig.flags));
|
|
110
|
-
}
|
|
111
|
-
return {};
|
|
112
|
-
}
|
|
113
|
-
return inquirer.prompt(questions);
|
|
114
|
-
}
|
|
115
93
|
async run() {
|
|
116
94
|
const { flags } = await this.parse(Claude);
|
|
117
95
|
const jsonMode = shouldOutputJson(flags);
|
|
@@ -155,6 +133,8 @@ export default class Claude extends Command {
|
|
|
155
133
|
validate: (input) => input.trim() ? true : 'Session name required',
|
|
156
134
|
},
|
|
157
135
|
], jsonModeConfig);
|
|
136
|
+
if (jsonMode)
|
|
137
|
+
return;
|
|
158
138
|
slug = inputSlug.trim();
|
|
159
139
|
}
|
|
160
140
|
// Determine if devcontainer is available
|
|
@@ -300,6 +280,8 @@ export default class Claude extends Command {
|
|
|
300
280
|
default: 'terminal',
|
|
301
281
|
},
|
|
302
282
|
], jsonModeConfig);
|
|
283
|
+
if (jsonMode)
|
|
284
|
+
return;
|
|
303
285
|
displayMode = selectedDisplay;
|
|
304
286
|
}
|
|
305
287
|
// Prompt for permission mode
|
|
@@ -320,6 +302,8 @@ export default class Claude extends Command {
|
|
|
320
302
|
default: 'danger',
|
|
321
303
|
},
|
|
322
304
|
], jsonModeConfig);
|
|
305
|
+
if (jsonMode)
|
|
306
|
+
return;
|
|
323
307
|
sandboxed = permissionMode === 'safe';
|
|
324
308
|
}
|
|
325
309
|
// Warn about uncommitted changes in danger mode
|
|
@@ -528,6 +512,10 @@ export default class Claude extends Command {
|
|
|
528
512
|
})),
|
|
529
513
|
},
|
|
530
514
|
], jsonModeConfig);
|
|
515
|
+
if (jsonMode) {
|
|
516
|
+
db.close();
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
531
519
|
projectId = selectedProject;
|
|
532
520
|
}
|
|
533
521
|
}
|
|
@@ -543,6 +531,10 @@ export default class Claude extends Command {
|
|
|
543
531
|
validate: (input) => input.trim() ? true : 'Title required',
|
|
544
532
|
},
|
|
545
533
|
], jsonModeConfig);
|
|
534
|
+
if (jsonMode) {
|
|
535
|
+
db.close();
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
546
538
|
ticketTitle = inputTitle.trim();
|
|
547
539
|
}
|
|
548
540
|
// Get optional description
|
|
@@ -703,6 +695,10 @@ export default class Claude extends Command {
|
|
|
703
695
|
default: 'terminal',
|
|
704
696
|
},
|
|
705
697
|
], jsonModeConfig);
|
|
698
|
+
if (jsonMode) {
|
|
699
|
+
db.close();
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
706
702
|
displayMode = selectedDisplay;
|
|
707
703
|
}
|
|
708
704
|
// Prompt for permission mode
|
|
@@ -724,6 +720,10 @@ export default class Claude extends Command {
|
|
|
724
720
|
default: 'danger',
|
|
725
721
|
},
|
|
726
722
|
], jsonModeConfig);
|
|
723
|
+
if (jsonMode) {
|
|
724
|
+
db.close();
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
727
|
sandboxed = permissionMode === 'safe';
|
|
728
728
|
}
|
|
729
729
|
// Warn about uncommitted changes in danger mode
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PromptCommand } from '../lib/prompt-command.js';
|
|
2
2
|
/**
|
|
3
3
|
* Format context passed to format functions.
|
|
4
4
|
*/
|
|
@@ -50,7 +50,7 @@ export declare const COMMIT_FORMATS: {
|
|
|
50
50
|
};
|
|
51
51
|
export type CommitFormat = keyof typeof COMMIT_FORMATS;
|
|
52
52
|
export declare const DEFAULT_COMMIT_FORMAT: CommitFormat;
|
|
53
|
-
export default class Commit extends
|
|
53
|
+
export default class Commit extends PromptCommand {
|
|
54
54
|
static description: string;
|
|
55
55
|
static examples: string[];
|
|
56
56
|
static args: {
|
|
@@ -66,12 +66,6 @@ export default class Commit extends Command {
|
|
|
66
66
|
'dry-run': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
67
67
|
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
68
68
|
};
|
|
69
|
-
/**
|
|
70
|
-
* Prompt wrapper - handles both JSON mode and interactive mode.
|
|
71
|
-
* In JSON mode: outputs prompt as JSON and exits.
|
|
72
|
-
* In interactive mode: calls inquirer.prompt normally.
|
|
73
|
-
*/
|
|
74
|
-
private prompt;
|
|
75
69
|
run(): Promise<void>;
|
|
76
70
|
}
|
|
77
71
|
export {};
|
package/dist/commands/commit.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
2
|
import { execSync } from 'node:child_process';
|
|
3
|
-
import
|
|
3
|
+
import { PromptCommand } from '../lib/prompt-command.js';
|
|
4
4
|
import { validateBranchName } from '../lib/branch/index.js';
|
|
5
5
|
import { styles } from '../lib/styles.js';
|
|
6
|
-
import { shouldOutputJson,
|
|
6
|
+
import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../lib/prompt-json.js';
|
|
7
7
|
/**
|
|
8
8
|
* Commit message format presets.
|
|
9
9
|
*/
|
|
@@ -140,7 +140,7 @@ function branchTypeToCommitType(branchType) {
|
|
|
140
140
|
};
|
|
141
141
|
return mapping[branchType] || branchType;
|
|
142
142
|
}
|
|
143
|
-
export default class Commit extends
|
|
143
|
+
export default class Commit extends PromptCommand {
|
|
144
144
|
static description = 'Create a commit with ticket ID from branch name';
|
|
145
145
|
static examples = [
|
|
146
146
|
'<%= config.bin %> <%= command.id %> # interactive mode',
|
|
@@ -197,28 +197,6 @@ export default class Commit extends Command {
|
|
|
197
197
|
default: false,
|
|
198
198
|
}),
|
|
199
199
|
};
|
|
200
|
-
/**
|
|
201
|
-
* Prompt wrapper - handles both JSON mode and interactive mode.
|
|
202
|
-
* In JSON mode: outputs prompt as JSON and exits.
|
|
203
|
-
* In interactive mode: calls inquirer.prompt normally.
|
|
204
|
-
*/
|
|
205
|
-
async prompt(questions, jsonModeConfig) {
|
|
206
|
-
if (jsonModeConfig && isAgentMode(jsonModeConfig.flags)) {
|
|
207
|
-
const firstQuestion = questions[0];
|
|
208
|
-
if (firstQuestion) {
|
|
209
|
-
const choices = firstQuestion.choices ? normalizeChoices(firstQuestion.choices) : undefined;
|
|
210
|
-
outputPromptAsJson({
|
|
211
|
-
type: firstQuestion.type,
|
|
212
|
-
name: firstQuestion.name,
|
|
213
|
-
message: firstQuestion.message,
|
|
214
|
-
choices,
|
|
215
|
-
default: firstQuestion.default,
|
|
216
|
-
}, createMetadata(jsonModeConfig.commandName, jsonModeConfig.flags));
|
|
217
|
-
}
|
|
218
|
-
return {};
|
|
219
|
-
}
|
|
220
|
-
return inquirer.prompt(questions);
|
|
221
|
-
}
|
|
222
200
|
async run() {
|
|
223
201
|
const { args, flags } = await this.parse(Commit);
|
|
224
202
|
// Check if JSON output mode is active
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export default class Config extends
|
|
1
|
+
import { PromptCommand } from '../../lib/prompt-command.js';
|
|
2
|
+
export default class Config extends PromptCommand {
|
|
3
3
|
static description: string;
|
|
4
4
|
static examples: string[];
|
|
5
5
|
static flags: {
|
|
@@ -8,14 +8,6 @@ export default class Config extends Command {
|
|
|
8
8
|
list: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
9
9
|
setting: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
10
|
};
|
|
11
|
-
/**
|
|
12
|
-
* Prompt wrapper - drop-in replacement for inquirer.prompt
|
|
13
|
-
*
|
|
14
|
-
* Works in BOTH modes:
|
|
15
|
-
* - Interactive mode: calls inquirer.prompt normally (human sees menu)
|
|
16
|
-
* - JSON/Agent mode: outputs prompt as structured JSON and exits
|
|
17
|
-
*/
|
|
18
|
-
private promptUser;
|
|
19
11
|
run(): Promise<void>;
|
|
20
12
|
/**
|
|
21
13
|
* Handle a specific setting's sub-prompt
|
|
@@ -2,12 +2,12 @@ import { Flags } from '@oclif/core';
|
|
|
2
2
|
import * as path from 'node:path';
|
|
3
3
|
import Database from 'better-sqlite3';
|
|
4
4
|
import inquirer from 'inquirer';
|
|
5
|
-
import {
|
|
5
|
+
import { PromptCommand } from '../../lib/prompt-command.js';
|
|
6
6
|
import { styles } from '../../lib/styles.js';
|
|
7
7
|
import { getWorkspaceInfo } from '../../lib/agents/commands.js';
|
|
8
8
|
import { loadExecutionConfig, saveTerminalApp, saveTerminalOpenInBackground, saveTmuxControlMode, saveShell, } from '../../lib/execution/config.js';
|
|
9
|
-
import { shouldOutputJson,
|
|
10
|
-
export default class Config extends
|
|
9
|
+
import { shouldOutputJson, isNonTTY, outputSuccessAsJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
|
|
10
|
+
export default class Config extends PromptCommand {
|
|
11
11
|
static description = 'View and update workspace configuration';
|
|
12
12
|
static examples = [
|
|
13
13
|
'<%= config.bin %> <%= command.id %> # Interactive menu',
|
|
@@ -37,32 +37,6 @@ export default class Config extends Command {
|
|
|
37
37
|
description: 'Navigate to a specific setting prompt (for agent navigation)',
|
|
38
38
|
}),
|
|
39
39
|
};
|
|
40
|
-
/**
|
|
41
|
-
* Prompt wrapper - drop-in replacement for inquirer.prompt
|
|
42
|
-
*
|
|
43
|
-
* Works in BOTH modes:
|
|
44
|
-
* - Interactive mode: calls inquirer.prompt normally (human sees menu)
|
|
45
|
-
* - JSON/Agent mode: outputs prompt as structured JSON and exits
|
|
46
|
-
*/
|
|
47
|
-
async promptUser(questions, jsonModeConfig) {
|
|
48
|
-
if (jsonModeConfig && isAgentMode(jsonModeConfig.flags)) {
|
|
49
|
-
const firstQuestion = questions[0];
|
|
50
|
-
if (firstQuestion) {
|
|
51
|
-
const choices = firstQuestion.choices
|
|
52
|
-
? normalizeChoices(firstQuestion.choices)
|
|
53
|
-
: undefined;
|
|
54
|
-
outputPromptAsJson({
|
|
55
|
-
type: firstQuestion.type,
|
|
56
|
-
name: firstQuestion.name,
|
|
57
|
-
message: firstQuestion.message,
|
|
58
|
-
choices,
|
|
59
|
-
default: firstQuestion.default,
|
|
60
|
-
}, createMetadata(jsonModeConfig.commandName, jsonModeConfig.flags));
|
|
61
|
-
}
|
|
62
|
-
return {};
|
|
63
|
-
}
|
|
64
|
-
return inquirer.prompt(questions);
|
|
65
|
-
}
|
|
66
40
|
async run() {
|
|
67
41
|
const { flags } = await this.parse(Config);
|
|
68
42
|
const jsonMode = shouldOutputJson(flags);
|
|
@@ -163,7 +137,7 @@ export default class Config extends Command {
|
|
|
163
137
|
{ name: `Shell: ${config.shell}`, value: 'shell', command: 'prlt config --setting shell --json' },
|
|
164
138
|
{ name: `Tmux Control Mode (iTerm -CC): ${config.tmux.controlMode}`, value: 'tmux.controlMode', command: 'prlt config --setting tmux.controlMode --json' },
|
|
165
139
|
];
|
|
166
|
-
const { setting } = await this.
|
|
140
|
+
const { setting } = await this.prompt([
|
|
167
141
|
{
|
|
168
142
|
type: 'list',
|
|
169
143
|
name: 'setting',
|
|
@@ -209,7 +183,7 @@ export default class Config extends Command {
|
|
|
209
183
|
{ name: 'Warp', value: 'Warp', command: 'prlt config --set "terminal.app Warp" --json' },
|
|
210
184
|
{ name: 'tmux', value: 'tmux', command: 'prlt config --set "terminal.app tmux" --json' },
|
|
211
185
|
];
|
|
212
|
-
const { newApp } = await this.
|
|
186
|
+
const { newApp } = await this.prompt([
|
|
213
187
|
{
|
|
214
188
|
type: 'list',
|
|
215
189
|
name: 'newApp',
|
|
@@ -227,7 +201,7 @@ export default class Config extends Command {
|
|
|
227
201
|
{ name: 'Yes - Open tabs in background (don\'t steal focus)', value: 'true', command: 'prlt config --set "terminal.openInBackground true" --json' },
|
|
228
202
|
{ name: 'No - Bring terminal to foreground when opening tabs', value: 'false', command: 'prlt config --set "terminal.openInBackground false" --json' },
|
|
229
203
|
];
|
|
230
|
-
const { openInBg } = await this.
|
|
204
|
+
const { openInBg } = await this.prompt([
|
|
231
205
|
{
|
|
232
206
|
type: 'list',
|
|
233
207
|
name: 'openInBg',
|
|
@@ -246,7 +220,7 @@ export default class Config extends Command {
|
|
|
246
220
|
{ name: 'bash', value: 'bash', command: 'prlt config --set "shell bash" --json' },
|
|
247
221
|
{ name: 'fish', value: 'fish', command: 'prlt config --set "shell fish" --json' },
|
|
248
222
|
];
|
|
249
|
-
const { newShell } = await this.
|
|
223
|
+
const { newShell } = await this.prompt([
|
|
250
224
|
{
|
|
251
225
|
type: 'list',
|
|
252
226
|
name: 'newShell',
|
|
@@ -264,7 +238,7 @@ export default class Config extends Command {
|
|
|
264
238
|
{ name: 'Yes - Use tmux -CC for native iTerm integration', value: 'true', command: 'prlt config --set "tmux.controlMode true" --json' },
|
|
265
239
|
{ name: 'No - Standard tmux interface', value: 'false', command: 'prlt config --set "tmux.controlMode false" --json' },
|
|
266
240
|
];
|
|
267
|
-
const { controlMode } = await this.
|
|
241
|
+
const { controlMode } = await this.prompt([
|
|
268
242
|
{
|
|
269
243
|
type: 'list',
|
|
270
244
|
name: 'controlMode',
|
|
@@ -9,6 +9,7 @@ import { isDockerRunning } from '../../lib/execution/runners.js';
|
|
|
9
9
|
import { sanitizeContainerId } from '../../lib/docker/resolve.js';
|
|
10
10
|
import { FlagResolver, shouldOutputJson } from '../../lib/flags/index.js';
|
|
11
11
|
import { machineOutputFlags } from '../../lib/pmo/base-command.js';
|
|
12
|
+
import { visualPadEnd } from '../../lib/string-utils.js';
|
|
12
13
|
export default class DockerClean extends Command {
|
|
13
14
|
static description = 'Remove orphaned containers (containers without running agents)';
|
|
14
15
|
static examples = [
|
|
@@ -72,15 +73,15 @@ export default class DockerClean extends Command {
|
|
|
72
73
|
// Display orphaned containers
|
|
73
74
|
this.log(`\n${styles.header('Orphaned Containers')}`);
|
|
74
75
|
this.log('='.repeat(80));
|
|
75
|
-
this.log(styles.muted(
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
this.log(styles.muted(visualPadEnd('Container ID', 15) +
|
|
77
|
+
visualPadEnd('Name', 30) +
|
|
78
|
+
visualPadEnd('Status', 15) +
|
|
78
79
|
'Reason'));
|
|
79
80
|
this.log('-'.repeat(80));
|
|
80
81
|
for (const container of orphanedContainers) {
|
|
81
|
-
this.log(
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
this.log(visualPadEnd(container.id, 15) +
|
|
83
|
+
visualPadEnd(truncate(container.name, 28), 30) +
|
|
84
|
+
visualPadEnd(container.status, 15) +
|
|
84
85
|
styles.muted(container.reason));
|
|
85
86
|
}
|
|
86
87
|
if (flags['dry-run']) {
|
|
@@ -205,9 +206,6 @@ export default class DockerClean extends Command {
|
|
|
205
206
|
}
|
|
206
207
|
}
|
|
207
208
|
}
|
|
208
|
-
function padEnd(str, length) {
|
|
209
|
-
return str.padEnd(length);
|
|
210
|
-
}
|
|
211
209
|
function truncate(str, maxLength) {
|
|
212
210
|
if (str.length <= maxLength)
|
|
213
211
|
return str;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export default class Docker extends
|
|
1
|
+
import { PromptCommand } from '../../lib/prompt-command.js';
|
|
2
|
+
export default class Docker extends PromptCommand {
|
|
3
3
|
static description: string;
|
|
4
4
|
static examples: string[];
|
|
5
5
|
static flags: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Command } from '@oclif/core';
|
|
2
1
|
import inquirer from 'inquirer';
|
|
2
|
+
import { PromptCommand } from '../../lib/prompt-command.js';
|
|
3
3
|
import { execSync } from 'node:child_process';
|
|
4
4
|
import * as path from 'node:path';
|
|
5
5
|
import Database from 'better-sqlite3';
|
|
@@ -9,7 +9,8 @@ import { ExecutionStorage, ContainerStorage } from '../../lib/execution/storage.
|
|
|
9
9
|
import { isDockerRunning } from '../../lib/execution/runners.js';
|
|
10
10
|
import { FlagResolver, shouldOutputJson } from '../../lib/flags/index.js';
|
|
11
11
|
import { machineOutputFlags } from '../../lib/pmo/base-command.js';
|
|
12
|
-
|
|
12
|
+
import { visualPadEnd } from '../../lib/string-utils.js';
|
|
13
|
+
export default class Docker extends PromptCommand {
|
|
13
14
|
static description = 'Manage Docker containers used by agents';
|
|
14
15
|
static examples = [
|
|
15
16
|
'<%= config.bin %> <%= command.id %>',
|
|
@@ -127,7 +128,7 @@ export default class Docker extends Command {
|
|
|
127
128
|
// Build choices
|
|
128
129
|
const choices = [];
|
|
129
130
|
// Column header
|
|
130
|
-
const header = styles.muted(` ${'ID'
|
|
131
|
+
const header = styles.muted(` ${visualPadEnd('ID', 14)} ${visualPadEnd('Agent', 15)} Status`);
|
|
131
132
|
// Add active executions first (with container info from DB)
|
|
132
133
|
const activeExecutions = trackedExecutions.filter(e => e.status === 'running' || e.status === 'starting');
|
|
133
134
|
if (activeExecutions.length > 0) {
|
|
@@ -143,7 +144,7 @@ export default class Docker extends Command {
|
|
|
143
144
|
const statusIcon = isRunning ? styles.success('●') : styles.warning('◐');
|
|
144
145
|
// Use agent name from execution record (DB source of truth)
|
|
145
146
|
choices.push({
|
|
146
|
-
name: `${statusIcon} ${exec.id
|
|
147
|
+
name: `${statusIcon} ${visualPadEnd(exec.id, 14)} ${visualPadEnd(exec.agentName, 15)} ${styles.success(exec.status)}`,
|
|
147
148
|
value: exec.id,
|
|
148
149
|
});
|
|
149
150
|
if (exec.containerId) {
|
|
@@ -167,7 +168,7 @@ export default class Docker extends Command {
|
|
|
167
168
|
const statusText = isRunning ? styles.success('running') : styles.muted(container.status);
|
|
168
169
|
// Agent name from DB (source of truth)
|
|
169
170
|
choices.push({
|
|
170
|
-
name: `${statusIcon} ${container.dockerId.substring(0, 12)
|
|
171
|
+
name: `${statusIcon} ${visualPadEnd(container.dockerId.substring(0, 12), 14)} ${visualPadEnd(container.agentName, 15)} ${statusText}`,
|
|
171
172
|
value: container.dockerId,
|
|
172
173
|
});
|
|
173
174
|
addedDockerIds.add(container.dockerId.substring(0, 12));
|
|
@@ -185,7 +186,7 @@ export default class Docker extends Command {
|
|
|
185
186
|
// Parse agent from image only for untracked containers
|
|
186
187
|
const agentName = this.extractAgentFromImage(container.image);
|
|
187
188
|
choices.push({
|
|
188
|
-
name: `${statusIcon} ${container.id.substring(0, 12)
|
|
189
|
+
name: `${statusIcon} ${visualPadEnd(container.id.substring(0, 12), 14)} ${visualPadEnd(agentName, 15)} ${statusText}`,
|
|
189
190
|
value: container.id,
|
|
190
191
|
});
|
|
191
192
|
}
|
|
@@ -198,17 +199,17 @@ export default class Docker extends Command {
|
|
|
198
199
|
db.close();
|
|
199
200
|
if (choices.length <= 2) {
|
|
200
201
|
// Only manual option, just ask for input
|
|
201
|
-
const { target } = await
|
|
202
|
+
const { target } = await this.prompt([
|
|
202
203
|
{
|
|
203
204
|
type: 'input',
|
|
204
205
|
name: 'target',
|
|
205
206
|
message: 'No containers found. Enter container ID:',
|
|
206
207
|
validate: (input) => input.trim().length > 0 || 'Target is required',
|
|
207
208
|
},
|
|
208
|
-
]);
|
|
209
|
+
], null);
|
|
209
210
|
return target.trim();
|
|
210
211
|
}
|
|
211
|
-
const { target } = await
|
|
212
|
+
const { target } = await this.prompt([
|
|
212
213
|
{
|
|
213
214
|
type: 'list',
|
|
214
215
|
name: 'target',
|
|
@@ -216,19 +217,19 @@ export default class Docker extends Command {
|
|
|
216
217
|
choices,
|
|
217
218
|
pageSize: 15,
|
|
218
219
|
},
|
|
219
|
-
]);
|
|
220
|
+
], null);
|
|
220
221
|
if (target === '__cancel__') {
|
|
221
222
|
return null;
|
|
222
223
|
}
|
|
223
224
|
if (target === '__manual__') {
|
|
224
|
-
const { manualTarget } = await
|
|
225
|
+
const { manualTarget } = await this.prompt([
|
|
225
226
|
{
|
|
226
227
|
type: 'input',
|
|
227
228
|
name: 'manualTarget',
|
|
228
229
|
message: 'Enter execution ID (WORK-XXX), agent name, or container ID:',
|
|
229
230
|
validate: (input) => input.trim().length > 0 || 'Target is required',
|
|
230
231
|
},
|
|
231
|
-
]);
|
|
232
|
+
], null);
|
|
232
233
|
return manualTarget.trim();
|
|
233
234
|
}
|
|
234
235
|
return target;
|
|
@@ -5,6 +5,7 @@ export default class DockerList extends Command {
|
|
|
5
5
|
static flags: {
|
|
6
6
|
all: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
7
7
|
running: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
9
|
};
|
|
9
10
|
run(): Promise<void>;
|
|
10
11
|
private getTrackedContainers;
|
|
@@ -6,6 +6,9 @@ import { styles } from '../../lib/styles.js';
|
|
|
6
6
|
import { getWorkspaceInfo } from '../../lib/agents/commands.js';
|
|
7
7
|
import { ExecutionStorage, ContainerStorage } from '../../lib/execution/storage.js';
|
|
8
8
|
import { isDockerRunning } from '../../lib/execution/runners.js';
|
|
9
|
+
import { shouldOutputJson } from '../../lib/prompt-json.js';
|
|
10
|
+
import { machineOutputFlags } from '../../lib/pmo/index.js';
|
|
11
|
+
import { visualPadEnd } from '../../lib/string-utils.js';
|
|
9
12
|
export default class DockerList extends Command {
|
|
10
13
|
static description = 'Show Docker containers from agent_work table with status';
|
|
11
14
|
static examples = [
|
|
@@ -14,6 +17,7 @@ export default class DockerList extends Command {
|
|
|
14
17
|
'<%= config.bin %> <%= command.id %> --running',
|
|
15
18
|
];
|
|
16
19
|
static flags = {
|
|
20
|
+
...machineOutputFlags,
|
|
17
21
|
all: Flags.boolean({
|
|
18
22
|
char: 'a',
|
|
19
23
|
description: 'Show all containers (including non-devcontainer)',
|
|
@@ -27,8 +31,13 @@ export default class DockerList extends Command {
|
|
|
27
31
|
};
|
|
28
32
|
async run() {
|
|
29
33
|
const { flags } = await this.parse(DockerList);
|
|
34
|
+
const jsonMode = shouldOutputJson(flags);
|
|
30
35
|
// Check Docker status first
|
|
31
36
|
if (!isDockerRunning()) {
|
|
37
|
+
if (jsonMode) {
|
|
38
|
+
this.log(JSON.stringify({ error: 'Docker is not running' }, null, 2));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
32
41
|
this.log(`\n${styles.error('Docker is not running')}`);
|
|
33
42
|
this.log(`${styles.muted('Start Docker Desktop or the Docker daemon first.')}\n`);
|
|
34
43
|
return;
|
|
@@ -59,16 +68,24 @@ export default class DockerList extends Command {
|
|
|
59
68
|
const dbContainers = containerStorage.listContainers({ limit: 50 });
|
|
60
69
|
// Get running docker containers
|
|
61
70
|
const runningContainers = this.getDockerContainers(workspaceInfo.agentsPath, flags.all);
|
|
71
|
+
if (jsonMode) {
|
|
72
|
+
this.log(JSON.stringify({
|
|
73
|
+
executions: trackedExecutions,
|
|
74
|
+
containers: runningContainers,
|
|
75
|
+
}, null, 2));
|
|
76
|
+
db.close();
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
62
79
|
this.log(`\n${styles.header('Docker Containers')}`);
|
|
63
80
|
this.log('='.repeat(100));
|
|
64
81
|
// Display active executions from database
|
|
65
82
|
const activeExecutions = trackedExecutions.filter(e => e.status === 'running' || e.status === 'starting');
|
|
66
83
|
if (activeExecutions.length > 0) {
|
|
67
84
|
this.log(styles.subheader('\nActive Executions:'));
|
|
68
|
-
this.log(styles.muted(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
85
|
+
this.log(styles.muted(visualPadEnd('ID', 15) +
|
|
86
|
+
visualPadEnd('Agent', 15) +
|
|
87
|
+
visualPadEnd('Ticket', 12) +
|
|
88
|
+
visualPadEnd('Status', 12) +
|
|
72
89
|
'Container'));
|
|
73
90
|
this.log('-'.repeat(80));
|
|
74
91
|
for (const exec of activeExecutions) {
|
|
@@ -80,10 +97,10 @@ export default class DockerList extends Command {
|
|
|
80
97
|
const isRunning = runningContainers.some(c => c.id.startsWith(exec.containerId.substring(0, 12)));
|
|
81
98
|
containerStatus = isRunning ? styles.success(' (up)') : styles.warning(' (down)');
|
|
82
99
|
}
|
|
83
|
-
this.log(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
statusColor(
|
|
100
|
+
this.log(visualPadEnd(exec.id, 15) +
|
|
101
|
+
visualPadEnd(exec.agentName, 15) +
|
|
102
|
+
visualPadEnd(exec.ticketId, 12) +
|
|
103
|
+
statusColor(visualPadEnd(exec.status, 12)) +
|
|
87
104
|
containerId + containerStatus);
|
|
88
105
|
}
|
|
89
106
|
}
|
|
@@ -93,9 +110,9 @@ export default class DockerList extends Command {
|
|
|
93
110
|
: runningContainers;
|
|
94
111
|
if (filteredContainers.length > 0) {
|
|
95
112
|
this.log(styles.subheader('\nDocker Containers' + (flags.all ? ' (all)' : ' (devcontainers)') + ':'));
|
|
96
|
-
this.log(styles.muted(
|
|
97
|
-
|
|
98
|
-
|
|
113
|
+
this.log(styles.muted(visualPadEnd('Container ID', 15) +
|
|
114
|
+
visualPadEnd('Agent', 15) +
|
|
115
|
+
visualPadEnd('Status', 15) +
|
|
99
116
|
'Uptime'));
|
|
100
117
|
this.log('-'.repeat(70));
|
|
101
118
|
for (const container of filteredContainers) {
|
|
@@ -103,9 +120,9 @@ export default class DockerList extends Command {
|
|
|
103
120
|
// Look up agent name from DB (source of truth)
|
|
104
121
|
const dbContainer = dbContainers.find(c => c.dockerId.startsWith(container.id.substring(0, 12)));
|
|
105
122
|
const agentName = dbContainer?.agentName || this.extractAgentFromImage(container.image) || container.name;
|
|
106
|
-
this.log(
|
|
107
|
-
|
|
108
|
-
statusColor(
|
|
123
|
+
this.log(visualPadEnd(container.id, 15) +
|
|
124
|
+
visualPadEnd(agentName, 15) +
|
|
125
|
+
statusColor(visualPadEnd(container.status, 15)) +
|
|
109
126
|
styles.muted(container.uptime));
|
|
110
127
|
}
|
|
111
128
|
}
|
|
@@ -174,9 +191,6 @@ export default class DockerList extends Command {
|
|
|
174
191
|
return match ? match[1] : null;
|
|
175
192
|
}
|
|
176
193
|
}
|
|
177
|
-
function padEnd(str, length) {
|
|
178
|
-
return str.padEnd(length);
|
|
179
|
-
}
|
|
180
194
|
function getStatusColor(status) {
|
|
181
195
|
switch (status) {
|
|
182
196
|
case 'running':
|
|
@@ -2,6 +2,8 @@ import { Command } from '@oclif/core';
|
|
|
2
2
|
export default class DockerStatus extends Command {
|
|
3
3
|
static description: string;
|
|
4
4
|
static examples: string[];
|
|
5
|
-
static flags: {
|
|
5
|
+
static flags: {
|
|
6
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
};
|
|
6
8
|
run(): Promise<void>;
|
|
7
9
|
}
|
|
@@ -2,16 +2,42 @@ import { Command } from '@oclif/core';
|
|
|
2
2
|
import { execSync } from 'node:child_process';
|
|
3
3
|
import { styles } from '../../lib/styles.js';
|
|
4
4
|
import { isDockerRunning } from '../../lib/execution/runners.js';
|
|
5
|
+
import { shouldOutputJson } from '../../lib/prompt-json.js';
|
|
6
|
+
import { machineOutputFlags } from '../../lib/pmo/index.js';
|
|
5
7
|
export default class DockerStatus extends Command {
|
|
6
8
|
static description = 'Check if Docker daemon is running';
|
|
7
9
|
static examples = [
|
|
8
10
|
'<%= config.bin %> <%= command.id %>',
|
|
9
11
|
];
|
|
10
|
-
static flags = {
|
|
12
|
+
static flags = {
|
|
13
|
+
...machineOutputFlags,
|
|
14
|
+
};
|
|
11
15
|
async run() {
|
|
16
|
+
const { flags } = await this.parse(DockerStatus);
|
|
17
|
+
const jsonMode = shouldOutputJson(flags);
|
|
18
|
+
const running = isDockerRunning();
|
|
19
|
+
if (jsonMode) {
|
|
20
|
+
const result = { running };
|
|
21
|
+
if (running) {
|
|
22
|
+
try {
|
|
23
|
+
result.version = execSync('docker version --format "{{.Server.Version}}"', {
|
|
24
|
+
encoding: 'utf-8',
|
|
25
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
26
|
+
}).trim();
|
|
27
|
+
result.info = execSync('docker info --format "{{.Containers}} containers ({{.ContainersRunning}} running)"', {
|
|
28
|
+
encoding: 'utf-8',
|
|
29
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
30
|
+
}).trim();
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// Ignore errors getting additional info
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
this.log(JSON.stringify(result, null, 2));
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
12
39
|
this.log(`\n${styles.header('Docker Status')}`);
|
|
13
40
|
this.log('─'.repeat(50));
|
|
14
|
-
const running = isDockerRunning();
|
|
15
41
|
if (running) {
|
|
16
42
|
this.log(`${styles.success('Running')} Docker daemon is available`);
|
|
17
43
|
// Get more details
|