@proletariat/cli 0.3.9 → 0.3.11
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 +25 -0
- package/bin/dev.js +0 -0
- package/dist/commands/action/index.js +1 -1
- package/dist/commands/action/run.js +8 -12
- package/dist/commands/agent/auth.d.ts +30 -0
- package/dist/commands/agent/auth.js +172 -0
- package/dist/commands/agent/discover.d.ts +9 -0
- package/dist/commands/agent/discover.js +67 -0
- package/dist/commands/agent/index.js +47 -12
- package/dist/commands/agent/list.d.ts +4 -1
- package/dist/commands/agent/list.js +78 -16
- package/dist/commands/agent/login.js +35 -31
- package/dist/commands/agent/restart.js +2 -0
- package/dist/commands/agent/shell.js +78 -19
- package/dist/commands/agent/staff/add.js +1 -12
- package/dist/commands/agent/staff/remove.js +9 -7
- package/dist/commands/agent/status.js +17 -4
- package/dist/commands/agent/temp/cleanup.js +7 -3
- package/dist/commands/agent/themes/index.js +4 -5
- package/dist/commands/agent/themes/list.js +5 -5
- package/dist/commands/agent/visit.js +17 -4
- package/dist/commands/branch/create.d.ts +4 -0
- package/dist/commands/branch/create.js +16 -8
- package/dist/commands/branch/index.js +1 -1
- package/dist/commands/branch/where.js +1 -0
- package/dist/commands/claude.d.ts +38 -0
- package/dist/commands/claude.js +899 -0
- package/dist/commands/commit.js +1 -1
- package/dist/commands/config/index.d.ts +12 -0
- package/dist/commands/config/index.js +271 -0
- package/dist/commands/docker/clean.js +2 -2
- package/dist/commands/docker/index.js +2 -2
- package/dist/commands/docker/list.js +3 -8
- package/dist/commands/docker/logs.js +2 -2
- package/dist/commands/docker/prune.js +1 -1
- package/dist/commands/docker/restart.js +2 -2
- package/dist/commands/docker/shell.js +2 -2
- package/dist/commands/docker/start.js +2 -2
- package/dist/commands/docker/status.js +1 -1
- package/dist/commands/docker/stop.js +2 -2
- package/dist/commands/docker/sync.js +2 -2
- package/dist/commands/epic/index.js +1 -1
- package/dist/commands/epic/link/index.js +25 -14
- package/dist/commands/epic/link/remove.js +2 -0
- package/dist/commands/epic/list.js +5 -5
- package/dist/commands/epic/progress.js +10 -4
- package/dist/commands/epic/spec.js +2 -0
- package/dist/commands/epic/ticket.js +3 -0
- package/dist/commands/execution/stop.js +1 -0
- package/dist/commands/init.js +4 -4
- package/dist/commands/project/index.js +1 -1
- package/dist/commands/project/spec.js +7 -0
- package/dist/commands/repo/add.js +1 -0
- package/dist/commands/repo/remove.js +1 -0
- package/dist/commands/roadmap/add-project.d.ts +18 -0
- package/dist/commands/roadmap/add-project.js +135 -0
- package/dist/commands/roadmap/create.d.ts +22 -0
- package/dist/commands/roadmap/create.js +156 -0
- package/dist/commands/roadmap/delete.d.ts +17 -0
- package/dist/commands/roadmap/delete.js +104 -0
- package/dist/commands/roadmap/generate.d.ts +22 -0
- package/dist/commands/roadmap/generate.js +201 -0
- package/dist/commands/roadmap/index.d.ts +13 -0
- package/dist/commands/roadmap/index.js +61 -0
- package/dist/commands/roadmap/list.d.ts +12 -0
- package/dist/commands/roadmap/list.js +42 -0
- package/dist/commands/roadmap/remove-project.d.ts +18 -0
- package/dist/commands/roadmap/remove-project.js +147 -0
- package/dist/commands/roadmap/reorder.d.ts +17 -0
- package/dist/commands/roadmap/reorder.js +157 -0
- package/dist/commands/roadmap/update.d.ts +19 -0
- package/dist/commands/roadmap/update.js +136 -0
- package/dist/commands/roadmap/view.d.ts +16 -0
- package/dist/commands/roadmap/view.js +103 -0
- package/dist/commands/spec/index.js +1 -1
- package/dist/commands/spec/link/index.js +24 -13
- package/dist/commands/spec/link/remove.js +2 -0
- package/dist/commands/status/index.js +1 -1
- package/dist/commands/status/list.js +0 -8
- package/dist/commands/template/delete.js +2 -0
- package/dist/commands/terminal/title.d.ts +12 -0
- package/dist/commands/terminal/title.js +48 -0
- package/dist/commands/ticket/complete.js +2 -0
- package/dist/commands/ticket/create.js +4 -2
- package/dist/commands/ticket/delete.js +2 -0
- package/dist/commands/ticket/edit.js +8 -2
- package/dist/commands/ticket/link/index.js +17 -3
- package/dist/commands/ticket/link/remove.js +2 -0
- package/dist/commands/ticket/list.js +1 -2
- package/dist/commands/ticket/move.js +2 -0
- package/dist/commands/ticket/project.js +3 -1
- package/dist/commands/ticket/reassign.js +2 -0
- package/dist/commands/ticket/spec.js +4 -2
- package/dist/commands/ticket/template/apply.js +4 -3
- package/dist/commands/ticket/template/create.js +2 -0
- package/dist/commands/ticket/template/index.js +1 -1
- package/dist/commands/ticket/update.js +2 -0
- package/dist/commands/work/index.js +1 -1
- package/dist/commands/work/revise.js +7 -1
- package/dist/commands/work/spawn.d.ts +2 -1
- package/dist/commands/work/spawn.js +131 -36
- package/dist/commands/work/start.d.ts +2 -1
- package/dist/commands/work/start.js +349 -69
- package/dist/commands/work/watch.js +10 -2
- package/dist/commands/workflow/create.js +3 -3
- package/dist/commands/workflow/switch.js +2 -1
- package/dist/commands/workspace/remove.js +0 -8
- package/dist/commands/workspace/use.js +1 -9
- package/dist/lib/agents/commands.js +18 -13
- package/dist/lib/database/index.d.ts +19 -12
- package/dist/lib/database/index.js +158 -42
- package/dist/lib/docker/resolve.js +1 -1
- package/dist/lib/execution/config.d.ts +6 -0
- package/dist/lib/execution/config.js +15 -2
- package/dist/lib/execution/devcontainer.d.ts +2 -0
- package/dist/lib/execution/devcontainer.js +41 -9
- package/dist/lib/execution/runners.d.ts +85 -3
- package/dist/lib/execution/runners.js +925 -228
- package/dist/lib/execution/spawner.d.ts +2 -2
- package/dist/lib/execution/spawner.js +4 -3
- package/dist/lib/execution/storage.d.ts +2 -1
- package/dist/lib/execution/storage.js +9 -13
- package/dist/lib/execution/types.d.ts +10 -1
- package/dist/lib/execution/types.js +3 -1
- package/dist/lib/init/index.js +1 -0
- package/dist/lib/machine-config.js +1 -1
- package/dist/lib/pmo/base-command.js +5 -9
- package/dist/lib/pmo/index.js +2 -0
- package/dist/lib/pmo/schema.d.ts +6 -0
- package/dist/lib/pmo/schema.js +36 -0
- package/dist/lib/pmo/storage/base.js +3 -3
- package/dist/lib/pmo/storage/index.d.ts +16 -1
- package/dist/lib/pmo/storage/index.js +45 -0
- package/dist/lib/pmo/storage/roadmaps.d.ts +62 -0
- package/dist/lib/pmo/storage/roadmaps.js +301 -0
- package/dist/lib/pmo/storage/specs.js +2 -0
- package/dist/lib/pmo/storage/types.d.ts +14 -0
- package/dist/lib/pmo/sync-manager.d.ts +1 -1
- package/dist/lib/pmo/sync-manager.js +1 -1
- package/dist/lib/pmo/types.d.ts +41 -0
- package/dist/lib/pmo/utils.d.ts +2 -0
- package/dist/lib/pmo/utils.js +22 -1
- package/dist/lib/repos/index.js +7 -1
- package/dist/lib/terminal.d.ts +31 -0
- package/dist/lib/terminal.js +48 -0
- package/dist/lib/themes.d.ts +21 -3
- package/dist/lib/themes.js +80 -23
- package/dist/lib/workspace-config.d.ts +80 -0
- package/dist/lib/workspace-config.js +100 -0
- package/oclif.manifest.json +4065 -3225
- package/package.json +10 -6
- package/LICENSE +0 -21
|
@@ -6,7 +6,7 @@ import inquirer from 'inquirer';
|
|
|
6
6
|
import { colors } from '../../lib/colors.js';
|
|
7
7
|
import { getWorkspaceInfo } from '../../lib/agents/commands.js';
|
|
8
8
|
import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
|
|
9
|
-
import { isDockerRunning } from '../../lib/execution/runners.js';
|
|
9
|
+
import { isDockerRunning, getAgentContainerName, isContainerRunning, getContainerId, } from '../../lib/execution/runners.js';
|
|
10
10
|
import { shouldOutputJson, outputPromptAsJson, outputErrorAsJson, createMetadata, buildPromptConfig, } from '../../lib/prompt-json.js';
|
|
11
11
|
export default class Login extends PMOCommand {
|
|
12
12
|
static description = 'Authenticate Claude Code inside an agent container (one-time setup)';
|
|
@@ -65,15 +65,28 @@ export default class Login extends PMOCommand {
|
|
|
65
65
|
outputPromptAsJson(buildPromptConfig('list', 'name', 'Select agent to authenticate:', agentChoices), createMetadata('agent login', flags));
|
|
66
66
|
return;
|
|
67
67
|
}
|
|
68
|
+
// Group agents by type
|
|
69
|
+
const staffAgents = workspaceInfo.agents.filter(a => a.type === 'persistent');
|
|
70
|
+
const tempAgents = workspaceInfo.agents.filter(a => a.type === 'ephemeral');
|
|
71
|
+
const choices = [];
|
|
72
|
+
if (staffAgents.length > 0) {
|
|
73
|
+
choices.push(new inquirer.Separator('── Staff Agents ──'));
|
|
74
|
+
for (const agent of staffAgents) {
|
|
75
|
+
choices.push({ name: `👔 ${agent.name}`, value: agent.name });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (tempAgents.length > 0) {
|
|
79
|
+
choices.push(new inquirer.Separator('── Temp Agents ──'));
|
|
80
|
+
for (const agent of tempAgents) {
|
|
81
|
+
choices.push({ name: `⏱️ ${agent.name}`, value: agent.name });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
68
84
|
const { selected } = await inquirer.prompt([
|
|
69
85
|
{
|
|
70
86
|
type: 'list',
|
|
71
87
|
name: 'selected',
|
|
72
88
|
message: 'Select agent to authenticate:',
|
|
73
|
-
choices
|
|
74
|
-
name: agent.name,
|
|
75
|
-
value: agent.name
|
|
76
|
-
}))
|
|
89
|
+
choices
|
|
77
90
|
}
|
|
78
91
|
]);
|
|
79
92
|
agentName = selected;
|
|
@@ -84,38 +97,29 @@ export default class Login extends PMOCommand {
|
|
|
84
97
|
this.error(`Agent "${agentName}" not found. Available agents: ${workspaceInfo.agents.map(a => a.name).join(', ')}`);
|
|
85
98
|
}
|
|
86
99
|
const agentDir = path.join(workspaceInfo.agentsPath, agentName);
|
|
87
|
-
// Check if
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
catch {
|
|
93
|
-
this.error(`Agent "${agentName}" does not have a devcontainer configuration. Run "prlt agent add ${agentName}" to initialize.`);
|
|
100
|
+
// Check if Docker config exists
|
|
101
|
+
const dockerfilePath = path.join(agentDir, '.devcontainer', 'Dockerfile');
|
|
102
|
+
if (!fs.existsSync(dockerfilePath)) {
|
|
103
|
+
this.error(`Agent "${agentName}" does not have a Docker configuration. Run "prlt agent add ${agentName}" to initialize.`);
|
|
94
104
|
}
|
|
95
|
-
// Get container
|
|
105
|
+
// Get container using the standard naming convention
|
|
96
106
|
this.log(colors.primary(`🔐 Authenticating agent: ${agentName}`));
|
|
97
107
|
this.log('');
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
this.error('Failed to find running container. Make sure the agent container is running.');
|
|
108
|
+
const containerName = getAgentContainerName(agentName);
|
|
109
|
+
let containerId = null;
|
|
110
|
+
// Check if container is running
|
|
111
|
+
if (isContainerRunning(containerName)) {
|
|
112
|
+
containerId = getContainerId(containerName);
|
|
104
113
|
}
|
|
105
114
|
if (!containerId) {
|
|
106
|
-
this.log(colors.warning('Container is not running.
|
|
115
|
+
this.log(colors.warning('Container is not running.'));
|
|
107
116
|
this.log('');
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
containerId = execSync(`docker ps --filter "label=devcontainer.local_folder=${agentDir}" --format "{{.ID}}"`, { encoding: 'utf-8' }).trim();
|
|
115
|
-
}
|
|
116
|
-
catch {
|
|
117
|
-
this.error('Failed to start container.');
|
|
118
|
-
}
|
|
117
|
+
this.log('Start the container first by running a work command:');
|
|
118
|
+
this.log(' prlt work start <ticket-id>');
|
|
119
|
+
this.log('');
|
|
120
|
+
this.log('Or start an interactive session:');
|
|
121
|
+
this.log(' prlt agent shell ' + agentName);
|
|
122
|
+
this.error('Container must be running to authenticate.');
|
|
119
123
|
}
|
|
120
124
|
// Create a helper script to launch interactive session
|
|
121
125
|
this.log(colors.success(`✓ Container running: ${containerId}`));
|
|
@@ -80,8 +80,10 @@ export default class AgentRestart extends PMOCommand {
|
|
|
80
80
|
}
|
|
81
81
|
for (const containerName of containerNames) {
|
|
82
82
|
this.log(colors.textSecondary(` Stopping container: ${containerName}...`));
|
|
83
|
+
// eslint-disable-next-line no-await-in-loop -- Sequential container operations with user feedback
|
|
83
84
|
await execAsync(`docker stop ${containerName}`);
|
|
84
85
|
this.log(colors.textSecondary(` Removing container: ${containerName}...`));
|
|
86
|
+
// eslint-disable-next-line no-await-in-loop
|
|
85
87
|
await execAsync(`docker rm ${containerName}`);
|
|
86
88
|
}
|
|
87
89
|
this.log('');
|
|
@@ -5,10 +5,10 @@ import { execSync, spawn } from 'node:child_process';
|
|
|
5
5
|
import inquirer from 'inquirer';
|
|
6
6
|
import Database from 'better-sqlite3';
|
|
7
7
|
import { colors } from '../../lib/colors.js';
|
|
8
|
-
import { getWorkspaceInfo } from '../../lib/agents/commands.js';
|
|
8
|
+
import { getWorkspaceInfo, getAgentTmuxSessions } from '../../lib/agents/commands.js';
|
|
9
9
|
import { hasDevcontainerConfig } from '../../lib/execution/devcontainer.js';
|
|
10
10
|
import { getTerminalApp } from '../../lib/execution/config.js';
|
|
11
|
-
import { isDockerRunning } from '../../lib/execution/runners.js';
|
|
11
|
+
import { isDockerRunning, getAgentContainerName, isContainerRunning, getContainerId, } from '../../lib/execution/runners.js';
|
|
12
12
|
import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
|
|
13
13
|
import { shouldOutputJson, outputPromptAsJson, outputErrorAsJson, createMetadata, buildPromptConfig, } from '../../lib/prompt-json.js';
|
|
14
14
|
export default class Shell extends PMOCommand {
|
|
@@ -58,19 +58,34 @@ export default class Shell extends PMOCommand {
|
|
|
58
58
|
let agentName = args.name;
|
|
59
59
|
// Interactive mode if no agent specified
|
|
60
60
|
if (!agentName) {
|
|
61
|
-
// Build choices once, use for both JSON and interactive modes
|
|
62
|
-
const agentChoices = workspaceInfo.agents.map((agent) => ({ name: agent.name, value: agent.name }));
|
|
63
61
|
const selectMessage = 'Select agent to open shell in:';
|
|
64
62
|
// In JSON mode, output agent selection prompt and exit
|
|
65
63
|
if (jsonMode) {
|
|
64
|
+
const agentChoices = workspaceInfo.agents.map((agent) => ({ name: agent.name, value: agent.name }));
|
|
66
65
|
outputPromptAsJson(buildPromptConfig('list', 'name', selectMessage, agentChoices), createMetadata('agent shell', flags));
|
|
67
66
|
}
|
|
67
|
+
// Group agents by type for interactive mode
|
|
68
|
+
const staffAgents = workspaceInfo.agents.filter(a => a.type === 'persistent');
|
|
69
|
+
const tempAgents = workspaceInfo.agents.filter(a => a.type === 'ephemeral');
|
|
70
|
+
const choices = [];
|
|
71
|
+
if (staffAgents.length > 0) {
|
|
72
|
+
choices.push(new inquirer.Separator('── Staff Agents ──'));
|
|
73
|
+
for (const agent of staffAgents) {
|
|
74
|
+
choices.push({ name: `👔 ${agent.name}`, value: agent.name });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (tempAgents.length > 0) {
|
|
78
|
+
choices.push(new inquirer.Separator('── Temp Agents ──'));
|
|
79
|
+
for (const agent of tempAgents) {
|
|
80
|
+
choices.push({ name: `⏱️ ${agent.name}`, value: agent.name });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
68
83
|
const { selected } = await inquirer.prompt([
|
|
69
84
|
{
|
|
70
85
|
type: 'list',
|
|
71
86
|
name: 'selected',
|
|
72
87
|
message: selectMessage,
|
|
73
|
-
choices
|
|
88
|
+
choices
|
|
74
89
|
}
|
|
75
90
|
]);
|
|
76
91
|
agentName = selected;
|
|
@@ -80,6 +95,44 @@ export default class Shell extends PMOCommand {
|
|
|
80
95
|
if (!agent) {
|
|
81
96
|
return handleError('AGENT_NOT_FOUND', `Agent "${agentName}" not found. Available agents: ${workspaceInfo.agents.map(a => a.name).join(', ')}`);
|
|
82
97
|
}
|
|
98
|
+
// Check for existing tmux sessions
|
|
99
|
+
const existingSessions = getAgentTmuxSessions(agentName);
|
|
100
|
+
if (existingSessions.length > 0 && !jsonMode) {
|
|
101
|
+
this.log(colors.warning(`\n⚠️ Agent "${agentName}" has ${existingSessions.length} active tmux session(s):`));
|
|
102
|
+
for (const session of existingSessions) {
|
|
103
|
+
this.log(colors.textMuted(` • ${session}`));
|
|
104
|
+
}
|
|
105
|
+
this.log('');
|
|
106
|
+
const { sessionAction } = await inquirer.prompt([{
|
|
107
|
+
type: 'list',
|
|
108
|
+
name: 'sessionAction',
|
|
109
|
+
message: 'What would you like to do?',
|
|
110
|
+
choices: [
|
|
111
|
+
{ name: '🔗 Attach to existing session', value: 'attach' },
|
|
112
|
+
{ name: '⚠️ Open new shell anyway (may cause conflicts)', value: 'continue' },
|
|
113
|
+
{ name: '❌ Cancel', value: 'cancel' },
|
|
114
|
+
],
|
|
115
|
+
}]);
|
|
116
|
+
if (sessionAction === 'cancel') {
|
|
117
|
+
this.log(colors.textMuted('Operation cancelled.'));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (sessionAction === 'attach') {
|
|
121
|
+
// Attach to the first session
|
|
122
|
+
const sessionName = existingSessions[0];
|
|
123
|
+
this.log(colors.primary(`\nAttaching to session: ${sessionName}`));
|
|
124
|
+
try {
|
|
125
|
+
execSync(`tmux attach-session -t "${sessionName}"`, { stdio: 'inherit' });
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// User detached or session ended
|
|
129
|
+
this.log(colors.textMuted('\nDetached from session.'));
|
|
130
|
+
}
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
// If 'continue', proceed with opening a new shell
|
|
134
|
+
this.log(colors.warning('\nProceeding with new shell - be careful of conflicts!\n'));
|
|
135
|
+
}
|
|
83
136
|
const agentDir = path.join(workspaceInfo.agentsPath, agentName);
|
|
84
137
|
// Check if agent has devcontainer
|
|
85
138
|
const hasDevcontainer = hasDevcontainerConfig(agentDir);
|
|
@@ -156,22 +209,28 @@ export default class Shell extends PMOCommand {
|
|
|
156
209
|
if (!isDockerRunning()) {
|
|
157
210
|
this.error('Docker is not running. Please start Docker Desktop and try again.');
|
|
158
211
|
}
|
|
159
|
-
//
|
|
160
|
-
|
|
161
|
-
execSync(`devcontainer up --workspace-folder "${agentDir}"`, {
|
|
162
|
-
stdio: 'pipe',
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
catch (error) {
|
|
166
|
-
this.error(`Failed to start devcontainer: ${error instanceof Error ? error.message : error}`);
|
|
167
|
-
}
|
|
168
|
-
// Get container ID
|
|
212
|
+
// Get container using the standard naming convention
|
|
213
|
+
const containerName = getAgentContainerName(agentName);
|
|
169
214
|
let containerId = null;
|
|
170
|
-
|
|
171
|
-
|
|
215
|
+
// Check if container is running
|
|
216
|
+
if (isContainerRunning(containerName)) {
|
|
217
|
+
containerId = getContainerId(containerName);
|
|
172
218
|
}
|
|
173
|
-
|
|
174
|
-
//
|
|
219
|
+
else {
|
|
220
|
+
// Try to start a stopped container
|
|
221
|
+
try {
|
|
222
|
+
execSync(`docker start ${containerName}`, { stdio: 'pipe' });
|
|
223
|
+
containerId = getContainerId(containerName);
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
// Container doesn't exist - user needs to run a work command first
|
|
227
|
+
this.log(colors.warning('Container is not running.'));
|
|
228
|
+
this.log('');
|
|
229
|
+
this.log('Start the container first by running a work command:');
|
|
230
|
+
this.log(' prlt work start <ticket-id>');
|
|
231
|
+
this.log('');
|
|
232
|
+
this.error('Container must exist. Run a work command first to create it.');
|
|
233
|
+
}
|
|
175
234
|
}
|
|
176
235
|
if (!containerId) {
|
|
177
236
|
this.error('Failed to find running container.');
|
|
@@ -3,7 +3,7 @@ import chalk from 'chalk';
|
|
|
3
3
|
import inquirer from 'inquirer';
|
|
4
4
|
import { getWorkspaceInfo, validateAgentNames, addAgentsToWorkspace } from '../../../lib/agents/commands.js';
|
|
5
5
|
import { ensureBuiltinThemes, BUILTIN_THEMES, isValidAgentName, normalizeAgentName } from '../../../lib/themes.js';
|
|
6
|
-
import { getTheme, getThemes, getAvailableThemeNames,
|
|
6
|
+
import { getTheme, getThemes, getAvailableThemeNames, getActiveTheme } from '../../../lib/database/index.js';
|
|
7
7
|
import { shouldOutputJson, outputPromptAsJson, outputErrorAsJson, createMetadata, buildPromptConfig, } from '../../../lib/prompt-json.js';
|
|
8
8
|
export default class Add extends Command {
|
|
9
9
|
static description = 'Add new agents to the workspace';
|
|
@@ -84,10 +84,6 @@ export default class Add extends Command {
|
|
|
84
84
|
validate: (input) => input.length > 0 || 'Please select at least one name'
|
|
85
85
|
}]);
|
|
86
86
|
agentNames = selected;
|
|
87
|
-
// Mark selected names as used
|
|
88
|
-
for (const name of agentNames) {
|
|
89
|
-
markThemeNameUsed(workspaceInfo.path, themeId, name);
|
|
90
|
-
}
|
|
91
87
|
}
|
|
92
88
|
// Interactive mode: show names from workspace's active theme
|
|
93
89
|
else if (agentNames.length === 0) {
|
|
@@ -153,10 +149,6 @@ export default class Add extends Command {
|
|
|
153
149
|
}
|
|
154
150
|
if (themedSelections.length > 0) {
|
|
155
151
|
agentNames.push(...themedSelections);
|
|
156
|
-
// Mark themed names as used
|
|
157
|
-
for (const name of themedSelections) {
|
|
158
|
-
markThemeNameUsed(workspaceInfo.path, activeTheme.id, name);
|
|
159
|
-
}
|
|
160
152
|
}
|
|
161
153
|
}
|
|
162
154
|
else {
|
|
@@ -233,9 +225,6 @@ export default class Add extends Command {
|
|
|
233
225
|
validate: (input) => input.length > 0 || 'Please select at least one name'
|
|
234
226
|
}]);
|
|
235
227
|
agentNames = selected;
|
|
236
|
-
for (const name of agentNames) {
|
|
237
|
-
markThemeNameUsed(workspaceInfo.path, selectedTheme, name);
|
|
238
|
-
}
|
|
239
228
|
}
|
|
240
229
|
}
|
|
241
230
|
if (agentNames.length === 0) {
|
|
@@ -40,12 +40,14 @@ export default class Remove extends PMOCommand {
|
|
|
40
40
|
};
|
|
41
41
|
// Get workspace information
|
|
42
42
|
const workspaceInfo = getWorkspaceInfo();
|
|
43
|
-
|
|
43
|
+
// Filter to staff (persistent) agents only
|
|
44
|
+
const staffAgents = workspaceInfo.agents.filter(a => a.type === 'persistent');
|
|
45
|
+
if (staffAgents.length === 0) {
|
|
44
46
|
if (jsonMode) {
|
|
45
|
-
outputErrorAsJson('NO_AGENTS', 'No agents to remove.', createMetadata('agent remove', flags));
|
|
47
|
+
outputErrorAsJson('NO_AGENTS', 'No staff agents to remove.', createMetadata('agent staff remove', flags));
|
|
46
48
|
return;
|
|
47
49
|
}
|
|
48
|
-
this.log(colors.warning('No agents to remove.'));
|
|
50
|
+
this.log(colors.warning('No staff agents to remove.'));
|
|
49
51
|
return;
|
|
50
52
|
}
|
|
51
53
|
let agentName = args.name;
|
|
@@ -53,7 +55,7 @@ export default class Remove extends PMOCommand {
|
|
|
53
55
|
if (!agentName) {
|
|
54
56
|
// Build choices once, use for both JSON and interactive modes
|
|
55
57
|
const agentChoices = [
|
|
56
|
-
...
|
|
58
|
+
...staffAgents.map((agent) => ({ name: agent.name, value: agent.name })),
|
|
57
59
|
{ name: 'Cancel', value: 'cancel' },
|
|
58
60
|
];
|
|
59
61
|
const selectMessage = 'Select agent to remove:';
|
|
@@ -80,10 +82,10 @@ export default class Remove extends PMOCommand {
|
|
|
80
82
|
}
|
|
81
83
|
agentName = selected;
|
|
82
84
|
}
|
|
83
|
-
// Validate agent exists
|
|
84
|
-
const agent =
|
|
85
|
+
// Validate agent exists and is a staff agent
|
|
86
|
+
const agent = staffAgents.find((a) => a.name === agentName);
|
|
85
87
|
if (!agent) {
|
|
86
|
-
return handleError('AGENT_NOT_FOUND', `
|
|
88
|
+
return handleError('AGENT_NOT_FOUND', `Staff agent "${agentName}" not found. Available staff agents: ${staffAgents.map((a) => a.name).join(', ')}`);
|
|
87
89
|
}
|
|
88
90
|
const agentsToRemove = [agentName];
|
|
89
91
|
// Build choices once, use for both JSON and interactive modes
|
|
@@ -49,15 +49,28 @@ export default class Status extends PMOCommand {
|
|
|
49
49
|
outputPromptAsJson(buildPromptConfig('list', 'name', 'Select agent to view status:', agentChoices), createMetadata('agent status', flags));
|
|
50
50
|
return;
|
|
51
51
|
}
|
|
52
|
+
// Group agents by type
|
|
53
|
+
const staffAgents = workspaceInfo.agents.filter(a => a.type === 'persistent');
|
|
54
|
+
const tempAgents = workspaceInfo.agents.filter(a => a.type === 'ephemeral');
|
|
55
|
+
const choices = [];
|
|
56
|
+
if (staffAgents.length > 0) {
|
|
57
|
+
choices.push(new inquirer.Separator('── Staff Agents ──'));
|
|
58
|
+
for (const agent of staffAgents) {
|
|
59
|
+
choices.push({ name: `👔 ${agent.name}`, value: agent.name });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (tempAgents.length > 0) {
|
|
63
|
+
choices.push(new inquirer.Separator('── Temp Agents ──'));
|
|
64
|
+
for (const agent of tempAgents) {
|
|
65
|
+
choices.push({ name: `⏱️ ${agent.name}`, value: agent.name });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
52
68
|
const { selected } = await inquirer.prompt([
|
|
53
69
|
{
|
|
54
70
|
type: 'list',
|
|
55
71
|
name: 'selected',
|
|
56
72
|
message: 'Select agent to view status:',
|
|
57
|
-
choices
|
|
58
|
-
name: agent.name,
|
|
59
|
-
value: agent.name
|
|
60
|
-
}))
|
|
73
|
+
choices
|
|
61
74
|
}
|
|
62
75
|
]);
|
|
63
76
|
agentName = selected;
|
|
@@ -225,9 +225,10 @@ export default class Cleanup extends PMOCommand {
|
|
|
225
225
|
if (!skipConfirm && !dryRun) {
|
|
226
226
|
// In JSON mode, output confirmation prompt with context
|
|
227
227
|
if (jsonMode) {
|
|
228
|
-
const promptConfig =
|
|
229
|
-
|
|
230
|
-
|
|
228
|
+
const promptConfig = {
|
|
229
|
+
...buildPromptConfig('list', 'confirmed', confirmMessage, confirmChoices),
|
|
230
|
+
context: { agentsToCleanup },
|
|
231
|
+
};
|
|
231
232
|
outputPromptAsJson(promptConfig, createMetadata('agent cleanup', flags));
|
|
232
233
|
return;
|
|
233
234
|
}
|
|
@@ -272,6 +273,7 @@ export default class Cleanup extends PMOCommand {
|
|
|
272
273
|
if (!jsonMode) {
|
|
273
274
|
this.log(colors.primary(`\nCleaning up: ${agentName}`));
|
|
274
275
|
}
|
|
276
|
+
// eslint-disable-next-line no-await-in-loop -- Sequential cleanup with user interaction
|
|
275
277
|
const result = await cleanupAgent(workspaceInfo, agentName, {
|
|
276
278
|
log: jsonMode ? undefined : (msg) => this.log(colors.textMuted(` ${msg}`)),
|
|
277
279
|
dryRun,
|
|
@@ -313,6 +315,7 @@ export default class Cleanup extends PMOCommand {
|
|
|
313
315
|
choices.push({ name: '🗑️ Force cleanup (discard all changes)', value: 'force' });
|
|
314
316
|
choices.push({ name: '⏭️ Skip this agent', value: 'skip' });
|
|
315
317
|
// In interactive mode, prompt for action
|
|
318
|
+
// eslint-disable-next-line no-await-in-loop -- User interaction per agent
|
|
316
319
|
const { action } = await inquirer.prompt([
|
|
317
320
|
{
|
|
318
321
|
type: 'list',
|
|
@@ -327,6 +330,7 @@ export default class Cleanup extends PMOCommand {
|
|
|
327
330
|
continue;
|
|
328
331
|
}
|
|
329
332
|
// Re-run cleanup with the selected option
|
|
333
|
+
// eslint-disable-next-line no-await-in-loop
|
|
330
334
|
const retryResult = await cleanupAgent(workspaceInfo, agentName, {
|
|
331
335
|
log: (msg) => this.log(colors.textMuted(` ${msg}`)),
|
|
332
336
|
dryRun,
|
|
@@ -3,7 +3,7 @@ import inquirer from 'inquirer';
|
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import { getWorkspaceInfo } from '../../../lib/agents/commands.js';
|
|
5
5
|
import { ensureBuiltinThemes } from '../../../lib/themes.js';
|
|
6
|
-
import { getThemes,
|
|
6
|
+
import { getThemes, getAvailableThemeNames } from '../../../lib/database/index.js';
|
|
7
7
|
import { shouldOutputJson, outputPromptAsJson, createMetadata, buildPromptConfig, } from '../../../lib/prompt-json.js';
|
|
8
8
|
export default class Themes extends Command {
|
|
9
9
|
static description = 'Manage agent naming themes';
|
|
@@ -119,15 +119,14 @@ export default class Themes extends Command {
|
|
|
119
119
|
}
|
|
120
120
|
// Build choices with theme info
|
|
121
121
|
const themeChoices = themes.map(t => {
|
|
122
|
-
const
|
|
123
|
-
const available = names.filter(n => !n.used).length;
|
|
122
|
+
const availableNames = getAvailableThemeNames(workspaceInfo.path, t.id);
|
|
124
123
|
const builtinTag = t.builtin ? chalk.dim(' [built-in]') : '';
|
|
125
124
|
return {
|
|
126
|
-
name: `${t.display_name}${builtinTag} ${chalk.dim(`(${
|
|
125
|
+
name: `${t.display_name}${builtinTag} ${chalk.dim(`(${availableNames.length} names available)`)}`,
|
|
127
126
|
value: t.id
|
|
128
127
|
};
|
|
129
128
|
});
|
|
130
|
-
// Add option to create new theme
|
|
129
|
+
// Add option to create new theme (using type assertion for mixed array)
|
|
131
130
|
themeChoices.push(new inquirer.Separator());
|
|
132
131
|
themeChoices.push({ name: chalk.green('+ Create new theme'), value: '__create_new__' });
|
|
133
132
|
const { selectedTheme } = await inquirer.prompt([{
|
|
@@ -2,7 +2,7 @@ import { Command } from '@oclif/core';
|
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import { getWorkspaceInfo } from '../../../lib/agents/commands.js';
|
|
4
4
|
import { ensureBuiltinThemes } from '../../../lib/themes.js';
|
|
5
|
-
import { getThemes, getThemeNames } from '../../../lib/database/index.js';
|
|
5
|
+
import { getThemes, getThemeNames, getAvailableThemeNames } from '../../../lib/database/index.js';
|
|
6
6
|
export default class ThemesList extends Command {
|
|
7
7
|
static description = 'List available agent themes';
|
|
8
8
|
static examples = [
|
|
@@ -20,16 +20,16 @@ export default class ThemesList extends Command {
|
|
|
20
20
|
}
|
|
21
21
|
this.log(chalk.bold('\nAgent Themes\n'));
|
|
22
22
|
for (const theme of themes) {
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
const
|
|
23
|
+
const allNames = getThemeNames(workspaceInfo.path, theme.id);
|
|
24
|
+
const availableNames = getAvailableThemeNames(workspaceInfo.path, theme.id);
|
|
25
|
+
const inUse = allNames.length - availableNames.length;
|
|
26
26
|
const builtinTag = theme.builtin ? chalk.gray(' [built-in]') : '';
|
|
27
27
|
this.log(` ${chalk.cyan(theme.display_name)}${builtinTag}`);
|
|
28
28
|
this.log(chalk.gray(` ID: ${theme.id}`));
|
|
29
29
|
if (theme.description) {
|
|
30
30
|
this.log(chalk.gray(` ${theme.description}`));
|
|
31
31
|
}
|
|
32
|
-
this.log(chalk.gray(` Names: ${chalk.green(
|
|
32
|
+
this.log(chalk.gray(` Names: ${chalk.green(availableNames.length + ' available')}, ${chalk.yellow(inUse + ' in use')}`));
|
|
33
33
|
this.log('');
|
|
34
34
|
}
|
|
35
35
|
this.log(chalk.blue('Use: prlt agent add --theme <theme-id>'));
|
|
@@ -58,15 +58,28 @@ export default class Visit extends PMOCommand {
|
|
|
58
58
|
outputPromptAsJson(buildPromptConfig('list', 'name', 'Select agent to visit:', agentChoices), createMetadata('agent visit', flags));
|
|
59
59
|
return;
|
|
60
60
|
}
|
|
61
|
+
// Group agents by type
|
|
62
|
+
const staffAgents = workspaceInfo.agents.filter(a => a.type === 'persistent');
|
|
63
|
+
const tempAgents = workspaceInfo.agents.filter(a => a.type === 'ephemeral');
|
|
64
|
+
const choices = [];
|
|
65
|
+
if (staffAgents.length > 0) {
|
|
66
|
+
choices.push(new inquirer.Separator('── Staff Agents ──'));
|
|
67
|
+
for (const agent of staffAgents) {
|
|
68
|
+
choices.push({ name: `👔 ${agent.name}`, value: agent.name });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (tempAgents.length > 0) {
|
|
72
|
+
choices.push(new inquirer.Separator('── Temp Agents ──'));
|
|
73
|
+
for (const agent of tempAgents) {
|
|
74
|
+
choices.push({ name: `⏱️ ${agent.name}`, value: agent.name });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
61
77
|
const { selected } = await inquirer.prompt([
|
|
62
78
|
{
|
|
63
79
|
type: 'list',
|
|
64
80
|
name: 'selected',
|
|
65
81
|
message: 'Select agent to visit:',
|
|
66
|
-
choices
|
|
67
|
-
name: agent.name,
|
|
68
|
-
value: agent.name
|
|
69
|
-
}))
|
|
82
|
+
choices
|
|
70
83
|
}
|
|
71
84
|
]);
|
|
72
85
|
agentName = selected;
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { PMOCommand } from '../../lib/pmo/index.js';
|
|
2
2
|
export default class BranchCreate extends PMOCommand {
|
|
3
|
+
private _selectedTicket?;
|
|
4
|
+
private _autoCommit?;
|
|
5
|
+
private _fromOrigin?;
|
|
6
|
+
private _customStartPoint?;
|
|
3
7
|
static description: string;
|
|
4
8
|
static examples: string[];
|
|
5
9
|
static args: {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Args, Flags } from '@oclif/core';
|
|
2
|
-
import * as fs from 'fs';
|
|
3
|
-
import * as path from 'path';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
4
|
import inquirer from 'inquirer';
|
|
5
5
|
import Database from 'better-sqlite3';
|
|
6
6
|
import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
|
|
@@ -11,6 +11,11 @@ import { getCoderName, getGitUserName, getGitHubUsername } from '../../lib/execu
|
|
|
11
11
|
import { getBranchType } from '../../lib/execution/types.js';
|
|
12
12
|
import { detectAgentName } from '../../lib/agents/index.js';
|
|
13
13
|
export default class BranchCreate extends PMOCommand {
|
|
14
|
+
// Internal state for passing data between methods
|
|
15
|
+
_selectedTicket;
|
|
16
|
+
_autoCommit;
|
|
17
|
+
_fromOrigin;
|
|
18
|
+
_customStartPoint;
|
|
14
19
|
static description = 'Create a new branch with conventional naming';
|
|
15
20
|
static examples = [
|
|
16
21
|
'<%= config.bin %> <%= command.id %>',
|
|
@@ -155,6 +160,7 @@ export default class BranchCreate extends PMOCommand {
|
|
|
155
160
|
if (!wizardResult)
|
|
156
161
|
return;
|
|
157
162
|
branchName = wizardResult.branchName;
|
|
163
|
+
// Store ticket info, autoCommit, fromOrigin, and customStartPoint flags for later
|
|
158
164
|
this._selectedTicket = wizardResult.ticket;
|
|
159
165
|
this._autoCommit = wizardResult.autoCommit;
|
|
160
166
|
this._fromOrigin = wizardResult.fromOrigin;
|
|
@@ -286,13 +292,14 @@ export default class BranchCreate extends PMOCommand {
|
|
|
286
292
|
/**
|
|
287
293
|
* Create branch from ticket ID with defaults (non-interactive).
|
|
288
294
|
*/
|
|
289
|
-
async createFromTicketId(ticketId, ownerOverride,
|
|
295
|
+
async createFromTicketId(ticketId, ownerOverride, _projectId) {
|
|
290
296
|
try {
|
|
291
297
|
// Search for ticket across all projects
|
|
292
298
|
const projects = await this.storage.listProjects();
|
|
293
299
|
let foundTicket = null;
|
|
294
|
-
for (const
|
|
295
|
-
|
|
300
|
+
for (const proj of projects) {
|
|
301
|
+
// eslint-disable-next-line no-await-in-loop -- Search with early exit
|
|
302
|
+
const tickets = await this.storage.listTickets(proj.id);
|
|
296
303
|
const match = tickets.find(t => t.id === ticketId);
|
|
297
304
|
if (match) {
|
|
298
305
|
foundTicket = match;
|
|
@@ -323,12 +330,13 @@ export default class BranchCreate extends PMOCommand {
|
|
|
323
330
|
// Get default owner name from config or GitHub
|
|
324
331
|
const defaultOwnerName = this.getDefaultOwnerName();
|
|
325
332
|
// Load tickets from PMO (across all projects)
|
|
326
|
-
|
|
333
|
+
const tickets = [];
|
|
327
334
|
try {
|
|
328
335
|
// Get all projects and their tickets
|
|
329
336
|
const projects = await this.storage.listProjects();
|
|
330
337
|
const projectId = flags.project;
|
|
331
338
|
for (const project of projects) {
|
|
339
|
+
// eslint-disable-next-line no-await-in-loop -- Collecting tickets from projects
|
|
332
340
|
const projectTickets = await this.storage.listTickets(projectId);
|
|
333
341
|
// Filter to actionable tickets (todo, in-progress, backlog)
|
|
334
342
|
const actionable = projectTickets.filter(t => !t.status || ['todo', 'in-progress', 'backlog', 'in_progress'].includes(t.status.toLowerCase()));
|
|
@@ -484,7 +492,7 @@ export default class BranchCreate extends PMOCommand {
|
|
|
484
492
|
default: 0,
|
|
485
493
|
},
|
|
486
494
|
]);
|
|
487
|
-
|
|
495
|
+
const fromOrigin = branchFrom === 'origin-main';
|
|
488
496
|
let customStartPoint;
|
|
489
497
|
if (branchFrom === 'other') {
|
|
490
498
|
customStartPoint = await this.promptForBranch();
|
|
@@ -586,7 +594,7 @@ export default class BranchCreate extends PMOCommand {
|
|
|
586
594
|
default: 0,
|
|
587
595
|
},
|
|
588
596
|
]);
|
|
589
|
-
|
|
597
|
+
const fromOrigin = branchFrom === 'origin-main';
|
|
590
598
|
let customStartPoint;
|
|
591
599
|
if (branchFrom === 'other') {
|
|
592
600
|
customStartPoint = await this.promptForBranch();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
2
|
import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
|
|
3
|
-
import { shouldOutputJson
|
|
3
|
+
import { shouldOutputJson } from '../../lib/prompt-json.js';
|
|
4
4
|
export default class Branch extends PMOCommand {
|
|
5
5
|
static description = 'Interactive menu for branch operations';
|
|
6
6
|
static examples = ['<%= config.bin %> <%= command.id %>'];
|
|
@@ -186,6 +186,7 @@ export default class BranchWhere extends PMOCommand {
|
|
|
186
186
|
while (current !== root) {
|
|
187
187
|
try {
|
|
188
188
|
const dbPath = path.join(current, '.proletariat', 'workspace.db');
|
|
189
|
+
// eslint-disable-next-line unicorn/prefer-module
|
|
189
190
|
const fs = require('node:fs');
|
|
190
191
|
if (fs.existsSync(dbPath)) {
|
|
191
192
|
return current;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class Claude extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
slug: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
'permission-mode': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
environment: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
'display-mode': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
prompt: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
directory: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
title: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
};
|
|
16
|
+
run(): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Run in "yolo mode" - outside any HQ
|
|
19
|
+
* No ticket creation, no tracking, just launch Claude
|
|
20
|
+
*/
|
|
21
|
+
private runOutsideHQ;
|
|
22
|
+
/**
|
|
23
|
+
* Run in "tracked mode" - inside an HQ
|
|
24
|
+
* Creates adhoc ticket, ephemeral agent, full tracking
|
|
25
|
+
*/
|
|
26
|
+
private runInsideHQ;
|
|
27
|
+
/**
|
|
28
|
+
* Set up catch-all devcontainer for directories without one
|
|
29
|
+
* Uses a temp directory to avoid polluting user's cwd
|
|
30
|
+
* Returns { configDir, workDir } where configDir contains .devcontainer
|
|
31
|
+
*/
|
|
32
|
+
private setupCatchallDevcontainer;
|
|
33
|
+
/**
|
|
34
|
+
* Check if catch-all container image is available
|
|
35
|
+
* Returns true if image exists locally or can be pulled
|
|
36
|
+
*/
|
|
37
|
+
private checkCatchallImage;
|
|
38
|
+
}
|