@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
|
@@ -6,6 +6,7 @@ import { styles } from '../../lib/styles.js';
|
|
|
6
6
|
import { getWorkspaceInfo } from '../../lib/agents/commands.js';
|
|
7
7
|
import { ContainerStorage } from '../../lib/execution/storage.js';
|
|
8
8
|
import { isDockerRunning } from '../../lib/execution/runners.js';
|
|
9
|
+
import { visualPadEnd } from '../../lib/string-utils.js';
|
|
9
10
|
export default class DockerSync extends Command {
|
|
10
11
|
static description = 'Sync container status from Docker into the database';
|
|
11
12
|
static examples = [
|
|
@@ -54,9 +55,9 @@ export default class DockerSync extends Command {
|
|
|
54
55
|
if (containers.length > 0) {
|
|
55
56
|
this.log(styles.subheader('Tracked Containers:'));
|
|
56
57
|
this.log(styles.muted(' ' +
|
|
57
|
-
'ID'
|
|
58
|
-
'Agent'
|
|
59
|
-
'Status'
|
|
58
|
+
visualPadEnd('ID', 18) +
|
|
59
|
+
visualPadEnd('Agent', 15) +
|
|
60
|
+
visualPadEnd('Status', 12) +
|
|
60
61
|
'Docker ID'));
|
|
61
62
|
this.log(styles.muted(' ' + '-'.repeat(65)));
|
|
62
63
|
for (const container of containers) {
|
|
@@ -64,9 +65,9 @@ export default class DockerSync extends Command {
|
|
|
64
65
|
container.status === 'exited' ? styles.muted :
|
|
65
66
|
container.status === 'removed' ? styles.error : styles.warning;
|
|
66
67
|
this.log(' ' +
|
|
67
|
-
container.id
|
|
68
|
-
container.agentName
|
|
69
|
-
statusColor(container.status
|
|
68
|
+
visualPadEnd(container.id, 18) +
|
|
69
|
+
visualPadEnd(container.agentName, 15) +
|
|
70
|
+
statusColor(visualPadEnd(container.status, 12)) +
|
|
70
71
|
styles.muted(container.dockerId.substring(0, 12)));
|
|
71
72
|
}
|
|
72
73
|
this.log('');
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Args, Flags } from '@oclif/core';
|
|
2
|
-
import inquirer from 'inquirer';
|
|
3
2
|
import { autoExportToBoard, PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
|
|
4
3
|
import { styles } from '../../lib/styles.js';
|
|
5
4
|
import { deleteEpicFile } from '../../lib/pmo/epic-files.js';
|
|
@@ -67,12 +66,12 @@ export default class EpicDelete extends PMOCommand {
|
|
|
67
66
|
outputPromptAsJson(buildPromptConfig('list', 'id', 'Select epic to delete:', choices), createMetadata('epic delete', flags));
|
|
68
67
|
return;
|
|
69
68
|
}
|
|
70
|
-
const { selected } = await
|
|
69
|
+
const { selected } = await this.prompt([{
|
|
71
70
|
type: 'list',
|
|
72
71
|
name: 'selected',
|
|
73
72
|
message: 'Select epic to delete:',
|
|
74
73
|
choices,
|
|
75
|
-
}]);
|
|
74
|
+
}], null);
|
|
76
75
|
epicId = selected;
|
|
77
76
|
}
|
|
78
77
|
// Get the epic to show details
|
|
@@ -97,7 +96,7 @@ export default class EpicDelete extends PMOCommand {
|
|
|
97
96
|
this.log(` Title: ${epic.title}`);
|
|
98
97
|
this.log(` Status: ${epic.status}`);
|
|
99
98
|
this.log(` Tickets: ${tickets.length} (will be unlinked, not deleted)`);
|
|
100
|
-
const { confirmed } = await
|
|
99
|
+
const { confirmed } = await this.prompt([{
|
|
101
100
|
type: 'list',
|
|
102
101
|
name: 'confirmed',
|
|
103
102
|
message: `Delete epic '${epic.title}'?`,
|
|
@@ -106,7 +105,7 @@ export default class EpicDelete extends PMOCommand {
|
|
|
106
105
|
{ name: 'Yes, delete permanently', value: true },
|
|
107
106
|
],
|
|
108
107
|
default: 0,
|
|
109
|
-
}]);
|
|
108
|
+
}], null);
|
|
110
109
|
if (!confirmed) {
|
|
111
110
|
this.log(styles.muted('Deletion cancelled.'));
|
|
112
111
|
return;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
2
|
import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
|
|
3
3
|
import { styles } from '../../lib/styles.js';
|
|
4
|
+
import { shouldOutputJson } from '../../lib/prompt-json.js';
|
|
4
5
|
// Progress bar helper
|
|
5
6
|
function progressBar(percent, width = 20) {
|
|
6
7
|
const filled = Math.round((percent / 100) * width);
|
|
@@ -23,15 +24,18 @@ export default class EpicList extends PMOCommand {
|
|
|
23
24
|
};
|
|
24
25
|
async execute() {
|
|
25
26
|
const { flags } = await this.parse(EpicList);
|
|
27
|
+
const jsonMode = shouldOutputJson(flags);
|
|
26
28
|
const projectId = await this.requireProject();
|
|
27
29
|
const epics = await this.storage.listEpics(projectId, flags.status ? { status: flags.status } : undefined);
|
|
28
30
|
if (epics.length === 0) {
|
|
31
|
+
if (jsonMode) {
|
|
32
|
+
this.log(JSON.stringify([], null, 2));
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
29
35
|
this.log(styles.muted('\nNo epics found.'));
|
|
30
36
|
this.log(styles.muted('Create one with: prlt epic create'));
|
|
31
37
|
return;
|
|
32
38
|
}
|
|
33
|
-
// Group epics by status
|
|
34
|
-
const grouped = this.groupByStatus(epics);
|
|
35
39
|
// Get ticket counts for each epic in parallel
|
|
36
40
|
const ticketCounts = await Promise.all(epics.map(async (epic) => {
|
|
37
41
|
const tickets = await this.storage.getTicketsForEpic(projectId, epic.id);
|
|
@@ -39,6 +43,17 @@ export default class EpicList extends PMOCommand {
|
|
|
39
43
|
return { epicId: epic.id, done, total: tickets.length };
|
|
40
44
|
}));
|
|
41
45
|
const epicProgress = new Map(ticketCounts.map(({ epicId, done, total }) => [epicId, { done, total }]));
|
|
46
|
+
if (jsonMode) {
|
|
47
|
+
const epicsWithProgress = epics.map(epic => {
|
|
48
|
+
const progress = epicProgress.get(epic.id) || { done: 0, total: 0 };
|
|
49
|
+
const percent = progress.total > 0 ? Math.round((progress.done / progress.total) * 100) : 0;
|
|
50
|
+
return { ...epic, progress: { ...progress, percent } };
|
|
51
|
+
});
|
|
52
|
+
this.log(JSON.stringify(epicsWithProgress, null, 2));
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
// Group epics by status
|
|
56
|
+
const grouped = this.groupByStatus(epics);
|
|
42
57
|
const projectName = await this.getProjectName(projectId);
|
|
43
58
|
this.log(`\n🎯 ${styles.emphasis('Epics')} - ${projectName}`);
|
|
44
59
|
this.log('═'.repeat(55));
|
|
@@ -5,6 +5,8 @@ import { styles } from '../../lib/styles.js';
|
|
|
5
5
|
import { getWorkspaceInfo } from '../../lib/agents/commands.js';
|
|
6
6
|
import { ExecutionStorage } from '../../lib/execution/storage.js';
|
|
7
7
|
import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
|
|
8
|
+
import { shouldOutputJson } from '../../lib/prompt-json.js';
|
|
9
|
+
import { visualPadEnd } from '../../lib/string-utils.js';
|
|
8
10
|
export default class ExecutionList extends PMOCommand {
|
|
9
11
|
static description = 'List running and recent executions';
|
|
10
12
|
static examples = [
|
|
@@ -36,6 +38,7 @@ export default class ExecutionList extends PMOCommand {
|
|
|
36
38
|
}
|
|
37
39
|
async execute() {
|
|
38
40
|
const { flags } = await this.parse(ExecutionList);
|
|
41
|
+
const jsonMode = shouldOutputJson(flags);
|
|
39
42
|
// Get workspace info
|
|
40
43
|
let workspaceInfo;
|
|
41
44
|
try {
|
|
@@ -55,20 +58,28 @@ export default class ExecutionList extends PMOCommand {
|
|
|
55
58
|
limit: flags.limit,
|
|
56
59
|
});
|
|
57
60
|
if (executions.length === 0) {
|
|
61
|
+
if (jsonMode) {
|
|
62
|
+
this.log(JSON.stringify([], null, 2));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
58
65
|
this.log(styles.muted('\nNo executions found.\n'));
|
|
59
66
|
return;
|
|
60
67
|
}
|
|
68
|
+
if (jsonMode) {
|
|
69
|
+
this.log(JSON.stringify(executions, null, 2));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
61
72
|
// Display header
|
|
62
73
|
this.log('');
|
|
63
74
|
this.log(styles.header('🚀 Agent Work'));
|
|
64
75
|
this.log('═'.repeat(100));
|
|
65
|
-
this.log(styles.muted(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
76
|
+
this.log(styles.muted(visualPadEnd('ID', 14) +
|
|
77
|
+
visualPadEnd('Ticket', 9) +
|
|
78
|
+
visualPadEnd('Agent', 10) +
|
|
79
|
+
visualPadEnd('Env', 13) +
|
|
80
|
+
visualPadEnd('Display', 11) +
|
|
81
|
+
visualPadEnd('Perms', 8) +
|
|
82
|
+
visualPadEnd('Status', 10) +
|
|
72
83
|
'Started'));
|
|
73
84
|
this.log('─'.repeat(100));
|
|
74
85
|
// Display executions
|
|
@@ -79,13 +90,13 @@ export default class ExecutionList extends PMOCommand {
|
|
|
79
90
|
const envStr = `${envIcon} ${exec.environment}`;
|
|
80
91
|
const permsStr = exec.sandboxed ? 'safe' : 'danger';
|
|
81
92
|
const permsColor = exec.sandboxed ? styles.success : styles.warning;
|
|
82
|
-
this.log(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
permsColor(
|
|
88
|
-
statusColor(
|
|
93
|
+
this.log(visualPadEnd(exec.id, 14) +
|
|
94
|
+
visualPadEnd(exec.ticketId, 9) +
|
|
95
|
+
visualPadEnd(exec.agentName, 10) +
|
|
96
|
+
visualPadEnd(envStr, 13) +
|
|
97
|
+
visualPadEnd(exec.displayMode, 11) +
|
|
98
|
+
permsColor(visualPadEnd(permsStr, 8)) +
|
|
99
|
+
statusColor(visualPadEnd(exec.status, 10)) +
|
|
89
100
|
styles.muted(timeAgo));
|
|
90
101
|
}
|
|
91
102
|
this.log('═'.repeat(100));
|
|
@@ -110,9 +121,6 @@ export default class ExecutionList extends PMOCommand {
|
|
|
110
121
|
// =============================================================================
|
|
111
122
|
// Helper Functions
|
|
112
123
|
// =============================================================================
|
|
113
|
-
function padEnd(str, length) {
|
|
114
|
-
return str.padEnd(length);
|
|
115
|
-
}
|
|
116
124
|
function getStatusColor(status) {
|
|
117
125
|
switch (status) {
|
|
118
126
|
case 'running':
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export default class FeedbackSubmit extends
|
|
1
|
+
import { PromptCommand } from '../../lib/prompt-command.js';
|
|
2
|
+
export default class FeedbackSubmit extends PromptCommand {
|
|
3
3
|
static description: string;
|
|
4
4
|
static examples: string[];
|
|
5
5
|
static flags: {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
2
|
import { execSync } from 'node:child_process';
|
|
3
3
|
import * as os from 'node:os';
|
|
4
|
-
import inquirer from 'inquirer';
|
|
5
4
|
import chalk from 'chalk';
|
|
5
|
+
import { PromptCommand } from '../../lib/prompt-command.js';
|
|
6
6
|
import { styles } from '../../lib/styles.js';
|
|
7
7
|
import { isGHInstalled, isGHAuthenticated } from '../../lib/pr/index.js';
|
|
8
8
|
import { isMachineOutput, outputSuccessAsJson, outputErrorAsJson, outputPromptAsJson, buildPromptConfig, createMetadata, } from '../../lib/prompt-json.js';
|
|
@@ -15,7 +15,7 @@ const CATEGORY_LABELS = {
|
|
|
15
15
|
feature: 'enhancement',
|
|
16
16
|
general: 'feedback',
|
|
17
17
|
};
|
|
18
|
-
export default class FeedbackSubmit extends
|
|
18
|
+
export default class FeedbackSubmit extends PromptCommand {
|
|
19
19
|
static description = 'Submit feedback, bug reports, or feature requests to GitHub Issues';
|
|
20
20
|
static examples = [
|
|
21
21
|
'<%= config.bin %> <%= command.id %>',
|
|
@@ -69,12 +69,12 @@ export default class FeedbackSubmit extends Command {
|
|
|
69
69
|
outputPromptAsJson(buildPromptConfig('list', 'category', categoryMessage, categoryChoices), createMetadata('feedback submit', flags));
|
|
70
70
|
return;
|
|
71
71
|
}
|
|
72
|
-
const { selectedCategory } = await
|
|
72
|
+
const { selectedCategory } = await this.prompt([{
|
|
73
73
|
type: 'list',
|
|
74
74
|
name: 'selectedCategory',
|
|
75
75
|
message: categoryMessage,
|
|
76
76
|
choices: categoryChoices,
|
|
77
|
-
}]);
|
|
77
|
+
}], null);
|
|
78
78
|
category = selectedCategory;
|
|
79
79
|
}
|
|
80
80
|
// Collect title
|
|
@@ -85,7 +85,7 @@ export default class FeedbackSubmit extends Command {
|
|
|
85
85
|
outputPromptAsJson(buildPromptConfig('input', 'title', titleMessage), createMetadata('feedback submit', { ...flags, category }));
|
|
86
86
|
return;
|
|
87
87
|
}
|
|
88
|
-
const { inputTitle } = await
|
|
88
|
+
const { inputTitle } = await this.prompt([{
|
|
89
89
|
type: 'input',
|
|
90
90
|
name: 'inputTitle',
|
|
91
91
|
message: titleMessage,
|
|
@@ -98,7 +98,7 @@ export default class FeedbackSubmit extends Command {
|
|
|
98
98
|
}
|
|
99
99
|
return true;
|
|
100
100
|
},
|
|
101
|
-
}]);
|
|
101
|
+
}], null);
|
|
102
102
|
title = inputTitle;
|
|
103
103
|
}
|
|
104
104
|
// Collect body/description
|
|
@@ -109,7 +109,7 @@ export default class FeedbackSubmit extends Command {
|
|
|
109
109
|
outputPromptAsJson(buildPromptConfig('editor', 'body', bodyMessage), createMetadata('feedback submit', { ...flags, category, title }));
|
|
110
110
|
return;
|
|
111
111
|
}
|
|
112
|
-
const { inputBody } = await
|
|
112
|
+
const { inputBody } = await this.prompt([{
|
|
113
113
|
type: 'editor',
|
|
114
114
|
name: 'inputBody',
|
|
115
115
|
message: bodyMessage,
|
|
@@ -119,7 +119,7 @@ export default class FeedbackSubmit extends Command {
|
|
|
119
119
|
}
|
|
120
120
|
return true;
|
|
121
121
|
},
|
|
122
|
-
}]);
|
|
122
|
+
}], null);
|
|
123
123
|
body = inputBody;
|
|
124
124
|
}
|
|
125
125
|
// Build diagnostics section
|
|
@@ -42,7 +42,7 @@ export default class Link extends PMOCommand {
|
|
|
42
42
|
outputPromptAsJson(buildPromptConfig('list', 'action', message, menuChoices), createMetadata('link', flags));
|
|
43
43
|
return;
|
|
44
44
|
}
|
|
45
|
-
const { selected } = await
|
|
45
|
+
const { selected } = await this.prompt([{
|
|
46
46
|
type: 'list',
|
|
47
47
|
name: 'selected',
|
|
48
48
|
message,
|
|
@@ -51,7 +51,7 @@ export default class Link extends PMOCommand {
|
|
|
51
51
|
new inquirer.Separator(),
|
|
52
52
|
{ name: menuChoices[3].name, value: menuChoices[3].value }
|
|
53
53
|
]
|
|
54
|
-
}]);
|
|
54
|
+
}], null);
|
|
55
55
|
if (selected === 'cancel') {
|
|
56
56
|
this.log(styles.muted('\nCancelled.'));
|
|
57
57
|
return;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export default class PMOInit extends
|
|
1
|
+
import { PromptCommand } from '../../lib/prompt-command.js';
|
|
2
|
+
export default class PMOInit extends PromptCommand {
|
|
3
3
|
static description: string;
|
|
4
4
|
static examples: string[];
|
|
5
5
|
static flags: {
|
|
@@ -1,9 +1,9 @@
|
|
|
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 { execSync } from 'node:child_process';
|
|
5
5
|
import chalk from 'chalk';
|
|
6
|
-
import
|
|
6
|
+
import { PromptCommand } from '../../lib/prompt-command.js';
|
|
7
7
|
import Database from 'better-sqlite3';
|
|
8
8
|
import { SQLiteStorage, getColumnsForTemplate, createPMO, promptForPMOLocation, promptForBoardTemplate, promptForBoardName, promptForCustomColumns, determinePMOPath, getPickerTemplates, } from '../../lib/pmo/index.js';
|
|
9
9
|
import { styles } from '../../lib/styles.js';
|
|
@@ -11,7 +11,7 @@ import { isGHInstalled, isGHAuthenticated, getGHUsername, isGHTokenInEnv } from
|
|
|
11
11
|
import { shouldOutputJson, outputPromptAsJson, createMetadata, buildPromptConfig, } from '../../lib/prompt-json.js';
|
|
12
12
|
// Build template options dynamically from shared definitions (picker templates + custom)
|
|
13
13
|
const PICKER_TEMPLATE_IDS = [...getPickerTemplates().map(t => t.id), 'custom'];
|
|
14
|
-
export default class PMOInit extends
|
|
14
|
+
export default class PMOInit extends PromptCommand {
|
|
15
15
|
static description = 'Initialize PMO (Project Management Org) in current directory or HQ';
|
|
16
16
|
static examples = [
|
|
17
17
|
'<%= config.bin %> <%= command.id %>',
|
|
@@ -102,6 +102,9 @@ export default class PMOInit extends Command {
|
|
|
102
102
|
if (flags.location) {
|
|
103
103
|
location = flags.location;
|
|
104
104
|
}
|
|
105
|
+
else if (jsonMode) {
|
|
106
|
+
location = 'separate';
|
|
107
|
+
}
|
|
105
108
|
else {
|
|
106
109
|
location = await promptForPMOLocation(hqRoot);
|
|
107
110
|
}
|
|
@@ -111,6 +114,9 @@ export default class PMOInit extends Command {
|
|
|
111
114
|
if (flags.template) {
|
|
112
115
|
template = flags.template;
|
|
113
116
|
}
|
|
117
|
+
else if (jsonMode) {
|
|
118
|
+
template = 'kanban';
|
|
119
|
+
}
|
|
114
120
|
else {
|
|
115
121
|
let storage;
|
|
116
122
|
if (hqRoot) {
|
|
@@ -131,14 +137,27 @@ export default class PMOInit extends Command {
|
|
|
131
137
|
}
|
|
132
138
|
// Get columns for template
|
|
133
139
|
let columns = getColumnsForTemplate(template);
|
|
134
|
-
if (template === 'custom') {
|
|
140
|
+
if (template === 'custom' && !jsonMode) {
|
|
135
141
|
columns = await promptForCustomColumns();
|
|
136
142
|
}
|
|
143
|
+
else if (template === 'custom' && jsonMode) {
|
|
144
|
+
// Custom column prompts not supported in JSON mode — use default columns
|
|
145
|
+
this.log(JSON.stringify({ warning: 'Custom columns not supported in JSON mode, using default columns. Use a named template (e.g. --template kanban) for predictable results.' }));
|
|
146
|
+
}
|
|
137
147
|
// Get board name using shared prompt (or from flag)
|
|
138
148
|
// Default to {hqname}-kanban pattern
|
|
139
149
|
const hqName = hqRoot ? path.basename(hqRoot).replace(/-hq$/, '') : undefined;
|
|
140
|
-
const defaultBoardName = hqName ? `${hqName}-kanban` :
|
|
141
|
-
|
|
150
|
+
const defaultBoardName = hqName ? `${hqName}-kanban` : 'Project Board';
|
|
151
|
+
let boardName;
|
|
152
|
+
if (flags.name) {
|
|
153
|
+
boardName = flags.name;
|
|
154
|
+
}
|
|
155
|
+
else if (jsonMode) {
|
|
156
|
+
boardName = defaultBoardName;
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
boardName = await promptForBoardName(defaultBoardName);
|
|
160
|
+
}
|
|
142
161
|
// For standalone PMO (no HQ), we need to create mini-HQ structure first
|
|
143
162
|
const isStandalone = !hqRoot;
|
|
144
163
|
let effectiveHqPath;
|
|
@@ -268,13 +287,13 @@ export default class PMOInit extends Command {
|
|
|
268
287
|
this.log(chalk.gray(` Location: ${pmoPath}`));
|
|
269
288
|
this.log(chalk.gray(` Projects: ${projectCount}`));
|
|
270
289
|
this.log(chalk.gray(` Tickets: ${ticketCount}\n`));
|
|
271
|
-
const result = await
|
|
290
|
+
const result = await this.prompt([{
|
|
272
291
|
type: 'list',
|
|
273
292
|
name: 'action',
|
|
274
293
|
message: 'What would you like to do?',
|
|
275
294
|
choices: actionChoices,
|
|
276
295
|
default: 'cancel',
|
|
277
|
-
}]);
|
|
296
|
+
}], null);
|
|
278
297
|
action = result.action;
|
|
279
298
|
}
|
|
280
299
|
if (action === 'cancel') {
|
|
@@ -303,11 +322,11 @@ export default class PMOInit extends Command {
|
|
|
303
322
|
this.log(chalk.red(' • All tickets and boards'));
|
|
304
323
|
this.log(chalk.red(' • All specs and documentation'));
|
|
305
324
|
this.log(chalk.red(' • Database tables (pmo_*)\n'));
|
|
306
|
-
const result = await
|
|
325
|
+
const result = await this.prompt([{
|
|
307
326
|
type: 'input',
|
|
308
327
|
name: 'confirmation',
|
|
309
328
|
message: 'Type "delete pmo" to confirm:',
|
|
310
|
-
}]);
|
|
329
|
+
}], null);
|
|
311
330
|
confirmation = result.confirmation;
|
|
312
331
|
}
|
|
313
332
|
if (confirmation !== 'delete pmo') {
|
|
@@ -140,7 +140,7 @@ export default class ProjectSpec extends PMOCommand {
|
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
142
|
// eslint-disable-next-line no-await-in-loop -- Interactive user prompt
|
|
143
|
-
const { action } = await
|
|
143
|
+
const { action } = await this.prompt([{
|
|
144
144
|
type: 'list',
|
|
145
145
|
name: 'action',
|
|
146
146
|
message: `Manage specs for ${projectId}:`,
|
|
@@ -150,7 +150,7 @@ export default class ProjectSpec extends PMOCommand {
|
|
|
150
150
|
new inquirer.Separator(),
|
|
151
151
|
{ name: 'Done', value: 'done' },
|
|
152
152
|
],
|
|
153
|
-
}]);
|
|
153
|
+
}], null);
|
|
154
154
|
if (action === 'done') {
|
|
155
155
|
continueLoop = false;
|
|
156
156
|
continue;
|
|
@@ -165,7 +165,7 @@ export default class ProjectSpec extends PMOCommand {
|
|
|
165
165
|
continue;
|
|
166
166
|
}
|
|
167
167
|
// eslint-disable-next-line no-await-in-loop -- User selection prompt
|
|
168
|
-
const { selected } = await
|
|
168
|
+
const { selected } = await this.prompt([{
|
|
169
169
|
type: 'checkbox',
|
|
170
170
|
name: 'selected',
|
|
171
171
|
message: 'Select specs to add:',
|
|
@@ -173,7 +173,7 @@ export default class ProjectSpec extends PMOCommand {
|
|
|
173
173
|
name: `${s.id} - ${s.title} (${s.status})`,
|
|
174
174
|
value: s.id,
|
|
175
175
|
})),
|
|
176
|
-
}]);
|
|
176
|
+
}], null);
|
|
177
177
|
if (selected.length === 0) {
|
|
178
178
|
this.log(styles.muted(' No specs selected.'));
|
|
179
179
|
continue;
|
|
@@ -191,7 +191,7 @@ export default class ProjectSpec extends PMOCommand {
|
|
|
191
191
|
continue;
|
|
192
192
|
}
|
|
193
193
|
// eslint-disable-next-line no-await-in-loop -- User selection prompt
|
|
194
|
-
const { selected } = await
|
|
194
|
+
const { selected } = await this.prompt([{
|
|
195
195
|
type: 'checkbox',
|
|
196
196
|
name: 'selected',
|
|
197
197
|
message: 'Select specs to remove:',
|
|
@@ -199,7 +199,7 @@ export default class ProjectSpec extends PMOCommand {
|
|
|
199
199
|
name: `${s.id} - ${s.title} (${s.status})`,
|
|
200
200
|
value: s.id,
|
|
201
201
|
})),
|
|
202
|
-
}]);
|
|
202
|
+
}], null);
|
|
203
203
|
if (selected.length === 0) {
|
|
204
204
|
this.log(styles.muted(' No specs selected.'));
|
|
205
205
|
continue;
|
|
@@ -2,6 +2,8 @@ import { Flags } from '@oclif/core';
|
|
|
2
2
|
import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
|
|
3
3
|
import { colors, format } from '../../lib/colors.js';
|
|
4
4
|
import { findHQRoot, getWorkspaceRepoInfo } from '../../lib/repos/index.js';
|
|
5
|
+
import { isNonTTY } from '../../lib/prompt-json.js';
|
|
6
|
+
import { visualPadEnd } from '../../lib/string-utils.js';
|
|
5
7
|
export default class List extends PMOCommand {
|
|
6
8
|
static description = 'List all repositories in the HQ';
|
|
7
9
|
static examples = [
|
|
@@ -23,6 +25,10 @@ export default class List extends PMOCommand {
|
|
|
23
25
|
}
|
|
24
26
|
async execute() {
|
|
25
27
|
const { flags } = await this.parse(List);
|
|
28
|
+
// Default format to 'json' in non-TTY environments (piped output, CI, agents)
|
|
29
|
+
if (flags.format === 'table' && isNonTTY()) {
|
|
30
|
+
flags.format = 'json';
|
|
31
|
+
}
|
|
26
32
|
// Find HQ root
|
|
27
33
|
const hqPath = findHQRoot();
|
|
28
34
|
if (!hqPath) {
|
|
@@ -53,10 +59,10 @@ export default class List extends PMOCommand {
|
|
|
53
59
|
this.log(format.title(`📦 Repositories (${repositories.length})`));
|
|
54
60
|
this.log('');
|
|
55
61
|
// Header
|
|
56
|
-
this.log(colors.textMuted('Name'
|
|
57
|
-
'Status'
|
|
58
|
-
'Branch'
|
|
59
|
-
'Commits'
|
|
62
|
+
this.log(colors.textMuted(visualPadEnd('Name', 20) +
|
|
63
|
+
visualPadEnd('Status', 10) +
|
|
64
|
+
visualPadEnd('Branch', 15) +
|
|
65
|
+
visualPadEnd('Commits', 12) +
|
|
60
66
|
'Added'));
|
|
61
67
|
this.log(colors.textMuted('─'.repeat(70)));
|
|
62
68
|
for (const repo of repositories) {
|
|
@@ -74,10 +80,10 @@ export default class List extends PMOCommand {
|
|
|
74
80
|
commits = `${repo.commitsBehind} behind`;
|
|
75
81
|
}
|
|
76
82
|
const added = repo.addedAt ? new Date(repo.addedAt).toLocaleDateString() : '-';
|
|
77
|
-
this.log(colors.text(repo.name
|
|
78
|
-
statusColor(repo.status
|
|
79
|
-
colors.warning((repo.branch || '-'
|
|
80
|
-
colors.text(commits
|
|
83
|
+
this.log(colors.text(visualPadEnd(repo.name, 20)) +
|
|
84
|
+
statusColor(visualPadEnd(repo.status, 10)) +
|
|
85
|
+
colors.warning(visualPadEnd(repo.branch || '-', 15)) +
|
|
86
|
+
colors.text(visualPadEnd(commits, 12)) +
|
|
81
87
|
colors.textMuted(added));
|
|
82
88
|
}
|
|
83
89
|
// Summary
|
|
@@ -4,6 +4,7 @@ import { colors, format } from '../../lib/colors.js';
|
|
|
4
4
|
import { findHQRoot, getWorkspaceRepoInfo, } from '../../lib/repos/index.js';
|
|
5
5
|
import { openWorkspaceDatabase } from '../../lib/database/index.js';
|
|
6
6
|
import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
|
|
7
|
+
import { visualPadEnd } from '../../lib/string-utils.js';
|
|
7
8
|
export default class View extends PMOCommand {
|
|
8
9
|
static description = 'View detailed information about a repository';
|
|
9
10
|
static examples = [
|
|
@@ -113,7 +114,7 @@ export default class View extends PMOCommand {
|
|
|
113
114
|
for (const wt of worktrees) {
|
|
114
115
|
const wtStatus = wt.is_clean ? 'clean' : 'dirty';
|
|
115
116
|
const wtColor = wt.is_clean ? colors.repoClean : colors.repoDirty;
|
|
116
|
-
let line = ` • ${colors.agentName(wt.agent_name
|
|
117
|
+
let line = ` • ${colors.agentName(visualPadEnd(wt.agent_name, 10))} - ${wtColor(wtStatus)}`;
|
|
117
118
|
if (wt.commits_ahead > 0) {
|
|
118
119
|
line += colors.commitsAhead(` (${wt.commits_ahead} ahead)`);
|
|
119
120
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
|
|
2
2
|
import { styles } from '../../lib/styles.js';
|
|
3
|
+
import { shouldOutputJson } from '../../lib/prompt-json.js';
|
|
3
4
|
export default class RoadmapList extends PMOCommand {
|
|
4
5
|
static description = 'List all roadmaps';
|
|
5
6
|
static examples = [
|
|
@@ -12,17 +13,31 @@ export default class RoadmapList extends PMOCommand {
|
|
|
12
13
|
return { promptIfMultiple: false };
|
|
13
14
|
}
|
|
14
15
|
async execute() {
|
|
16
|
+
const { flags } = await this.parse(RoadmapList);
|
|
17
|
+
const jsonMode = shouldOutputJson(flags);
|
|
15
18
|
const roadmaps = await this.storage.listRoadmaps();
|
|
16
19
|
if (roadmaps.length === 0) {
|
|
20
|
+
if (jsonMode) {
|
|
21
|
+
this.log(JSON.stringify([], null, 2));
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
17
24
|
this.log(styles.muted('No roadmaps found. Create one with: prlt roadmap create'));
|
|
18
25
|
return;
|
|
19
26
|
}
|
|
20
|
-
this.log(styles.title('\nRoadmaps\n'));
|
|
21
27
|
// Fetch all project counts in parallel
|
|
22
28
|
const projectCounts = await Promise.all(roadmaps.map(async (roadmap) => {
|
|
23
29
|
const projects = await this.storage.listRoadmapProjects(roadmap.id);
|
|
24
30
|
return projects.length;
|
|
25
31
|
}));
|
|
32
|
+
if (jsonMode) {
|
|
33
|
+
const roadmapsWithCounts = roadmaps.map((roadmap, i) => ({
|
|
34
|
+
...roadmap,
|
|
35
|
+
projectCount: projectCounts[i],
|
|
36
|
+
}));
|
|
37
|
+
this.log(JSON.stringify(roadmapsWithCounts, null, 2));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
this.log(styles.title('\nRoadmaps\n'));
|
|
26
41
|
for (let i = 0; i < roadmaps.length; i++) {
|
|
27
42
|
const roadmap = roadmaps[i];
|
|
28
43
|
const markers = [];
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { PMOCommand } from '../../lib/pmo/index.js';
|
|
2
|
+
export default class SessionHealth extends PMOCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
fix: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
watch: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
interval: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
threshold: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
};
|
|
13
|
+
protected getPMOOptions(): {
|
|
14
|
+
promptIfMultiple: boolean;
|
|
15
|
+
};
|
|
16
|
+
execute(): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Single health check pass. Returns the list of agent health infos.
|
|
19
|
+
*/
|
|
20
|
+
private runHealthCheck;
|
|
21
|
+
/**
|
|
22
|
+
* Display the health status table.
|
|
23
|
+
*/
|
|
24
|
+
private displayHealthTable;
|
|
25
|
+
/**
|
|
26
|
+
* Watchdog mode: continuously monitor and auto-recover hung agents.
|
|
27
|
+
*/
|
|
28
|
+
private watchMode;
|
|
29
|
+
}
|