@proletariat/cli 0.3.35 → 0.3.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/agent/auth.d.ts +12 -2
- package/dist/commands/agent/auth.js +128 -4
- package/dist/commands/agent/list.js +16 -7
- package/dist/commands/agent/status.js +32 -4
- package/dist/commands/board/watch.js +6 -0
- package/dist/commands/branch/list.d.ts +1 -0
- package/dist/commands/branch/list.js +43 -12
- package/dist/commands/branch/where.js +3 -2
- package/dist/commands/category/list.d.ts +2 -1
- package/dist/commands/category/list.js +38 -13
- package/dist/commands/{claude.d.ts → claude/index.d.ts} +1 -1
- package/dist/commands/{claude.js → claude/index.js} +12 -12
- package/dist/commands/claude/open.d.ts +13 -0
- package/dist/commands/claude/open.js +175 -0
- package/dist/commands/diet.js +18 -2
- package/dist/commands/docker/logs.js +7 -3
- package/dist/commands/docker/shell.js +6 -0
- package/dist/commands/docker/start.js +20 -4
- package/dist/commands/docker/sync.d.ts +4 -0
- package/dist/commands/docker/sync.js +30 -2
- package/dist/commands/epic/show.d.ts +13 -0
- package/dist/commands/epic/show.js +16 -0
- package/dist/commands/epic/view.js +27 -0
- package/dist/commands/execution/config.d.ts +0 -4
- package/dist/commands/execution/config.js +10 -32
- package/dist/commands/execution/index.js +2 -1
- package/dist/commands/execution/logs.js +1 -1
- package/dist/commands/execution/stop.js +2 -1
- package/dist/commands/execution/view.js +22 -26
- package/dist/commands/init.js +2 -19
- package/dist/commands/label/create.js +2 -1
- package/dist/commands/label/delete.js +2 -1
- package/dist/commands/label/group/create.js +2 -1
- package/dist/commands/label/group/list.js +2 -1
- package/dist/commands/label/list.js +2 -1
- package/dist/commands/mcp-server.js +25 -0
- package/dist/commands/phase/template/list.js +2 -1
- package/dist/commands/project/create.js +3 -4
- package/dist/commands/project/update.js +5 -6
- package/dist/commands/pull.js +24 -0
- package/dist/commands/session/create.d.ts +19 -0
- package/dist/commands/session/create.js +102 -0
- package/dist/commands/session/health.js +1 -20
- package/dist/commands/session/index.js +14 -1
- package/dist/commands/session/list.js +26 -7
- package/dist/commands/session/peek.d.ts +38 -0
- package/dist/commands/session/peek.js +316 -0
- package/dist/commands/session/poke.d.ts +27 -0
- package/dist/commands/session/poke.js +219 -0
- package/dist/commands/spec/view.js +29 -0
- package/dist/commands/template/list.js +2 -1
- package/dist/commands/theme/add-names.d.ts +4 -0
- package/dist/commands/theme/add-names.js +11 -1
- package/dist/commands/theme/create.d.ts +2 -0
- package/dist/commands/theme/create.js +8 -0
- package/dist/commands/ticket/bulk.js +2 -2
- package/dist/commands/ticket/complete.js +2 -2
- package/dist/commands/ticket/create.js +21 -0
- package/dist/commands/ticket/delete.js +8 -0
- package/dist/commands/ticket/edit.js +25 -0
- package/dist/commands/ticket/index.js +2 -2
- package/dist/commands/ticket/move.js +25 -2
- package/dist/commands/ticket/resolve.js +3 -4
- package/dist/commands/ticket/show.d.ts +13 -0
- package/dist/commands/ticket/show.js +16 -0
- package/dist/commands/ticket/template/list.js +2 -1
- package/dist/commands/ticket/view.d.ts +0 -1
- package/dist/commands/ticket/view.js +30 -1
- package/dist/commands/work/index.js +4 -0
- package/dist/commands/work/start.js +169 -94
- package/dist/commands/work/status.d.ts +14 -0
- package/dist/commands/work/status.js +60 -0
- package/dist/commands/workflow/index.js +2 -1
- package/dist/commands/workflow/show.d.ts +13 -0
- package/dist/commands/workflow/show.js +16 -0
- package/dist/commands/workspace/add.js +15 -0
- package/dist/commands/workspace/list.js +2 -1
- package/dist/commands/workspace/prune.js +5 -5
- package/dist/lib/branch/index.d.ts +1 -0
- package/dist/lib/execution/config.d.ts +15 -1
- package/dist/lib/execution/config.js +28 -0
- package/dist/lib/execution/runners.d.ts +11 -0
- package/dist/lib/execution/runners.js +53 -19
- package/dist/lib/execution/session-utils.d.ts +11 -1
- package/dist/lib/execution/session-utils.js +26 -1
- package/dist/lib/execution/storage.d.ts +5 -0
- package/dist/lib/execution/storage.js +18 -3
- package/dist/lib/execution/types.d.ts +2 -0
- package/dist/lib/mcp/tools/board.js +4 -6
- package/dist/lib/mcp/tools/cli-passthrough.js +25 -6
- package/dist/lib/mcp/tools/epic.js +8 -3
- package/dist/lib/mcp/tools/spec.js +1 -1
- package/dist/lib/mcp/tools/ticket.js +11 -9
- package/dist/lib/mcp/tools/work.js +96 -6
- package/dist/lib/mcp/types.d.ts +10 -0
- package/dist/lib/multiline-input.js +2 -1
- package/dist/lib/pmo/base-command.js +4 -4
- package/dist/lib/pmo/storage/actions.js +1 -1
- package/dist/lib/pmo/storage/base.js +195 -50
- package/dist/lib/pmo/storage/types.d.ts +1 -0
- package/dist/lib/prompt-command.d.ts +20 -0
- package/dist/lib/prompt-command.js +38 -2
- package/dist/lib/prompt-json.d.ts +36 -4
- package/dist/lib/prompt-json.js +129 -7
- package/dist/lib/styles.d.ts +37 -0
- package/dist/lib/styles.js +73 -0
- package/oclif.manifest.json +3259 -2701
- package/package.json +1 -1
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Uses the workspace_settings table (not pmo_settings - execution is workspace-level).
|
|
6
6
|
*/
|
|
7
7
|
import Database from 'better-sqlite3';
|
|
8
|
-
import { ExecutionConfig, TerminalApp, Shell, DisplayMode, OutputMode, ExecutionEnvironment } from './types.js';
|
|
8
|
+
import { ExecutionConfig, TerminalApp, Shell, DisplayMode, OutputMode, ExecutionEnvironment, AuthMethod } from './types.js';
|
|
9
9
|
import { type JsonFlags } from '../prompt-json.js';
|
|
10
10
|
declare const CONFIG_KEYS: {
|
|
11
11
|
terminalApp: string;
|
|
@@ -28,6 +28,7 @@ declare const CONFIG_KEYS: {
|
|
|
28
28
|
vmKeyPath: string;
|
|
29
29
|
vmSyncMethod: string;
|
|
30
30
|
coderName: string;
|
|
31
|
+
authMethod: string;
|
|
31
32
|
};
|
|
32
33
|
/**
|
|
33
34
|
* Load execution config from database, merging with defaults
|
|
@@ -55,6 +56,19 @@ export declare function saveTmuxControlMode(db: Database.Database, enabled: bool
|
|
|
55
56
|
* When enabled, new terminal tabs open without stealing focus from current window.
|
|
56
57
|
*/
|
|
57
58
|
export declare function saveTerminalOpenInBackground(db: Database.Database, enabled: boolean): void;
|
|
59
|
+
/**
|
|
60
|
+
* Save auth method preference (oauth or apikey)
|
|
61
|
+
*/
|
|
62
|
+
export declare function saveAuthMethod(db: Database.Database, method: AuthMethod): void;
|
|
63
|
+
/**
|
|
64
|
+
* Get saved auth method preference.
|
|
65
|
+
* Returns null if no preference has been saved (user should be prompted).
|
|
66
|
+
*/
|
|
67
|
+
export declare function getAuthMethod(db: Database.Database): AuthMethod | null;
|
|
68
|
+
/**
|
|
69
|
+
* Clear saved auth method preference (will prompt again next time)
|
|
70
|
+
*/
|
|
71
|
+
export declare function clearAuthMethod(db: Database.Database): void;
|
|
58
72
|
/**
|
|
59
73
|
* Check if terminal app preference has been set
|
|
60
74
|
*/
|
|
@@ -32,6 +32,7 @@ const CONFIG_KEYS = {
|
|
|
32
32
|
vmKeyPath: 'execution.vm.key_path',
|
|
33
33
|
vmSyncMethod: 'execution.vm.sync_method',
|
|
34
34
|
coderName: 'coder.name',
|
|
35
|
+
authMethod: 'execution.auth_method',
|
|
35
36
|
};
|
|
36
37
|
/**
|
|
37
38
|
* Get a setting value from the database
|
|
@@ -97,6 +98,11 @@ export function loadExecutionConfig(db) {
|
|
|
97
98
|
if (sandboxed !== null) {
|
|
98
99
|
config.sandboxed = sandboxed === 'true';
|
|
99
100
|
}
|
|
101
|
+
// Load auth method preference
|
|
102
|
+
const authMethod = getSetting(db, CONFIG_KEYS.authMethod);
|
|
103
|
+
if (authMethod) {
|
|
104
|
+
config.authMethod = authMethod;
|
|
105
|
+
}
|
|
100
106
|
// Load tmux settings
|
|
101
107
|
const tmuxSession = getSetting(db, CONFIG_KEYS.tmuxSession);
|
|
102
108
|
if (tmuxSession) {
|
|
@@ -178,6 +184,28 @@ export function saveTmuxControlMode(db, enabled) {
|
|
|
178
184
|
export function saveTerminalOpenInBackground(db, enabled) {
|
|
179
185
|
setSetting(db, CONFIG_KEYS.terminalOpenInBackground, enabled.toString());
|
|
180
186
|
}
|
|
187
|
+
/**
|
|
188
|
+
* Save auth method preference (oauth or apikey)
|
|
189
|
+
*/
|
|
190
|
+
export function saveAuthMethod(db, method) {
|
|
191
|
+
setSetting(db, CONFIG_KEYS.authMethod, method);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Get saved auth method preference.
|
|
195
|
+
* Returns null if no preference has been saved (user should be prompted).
|
|
196
|
+
*/
|
|
197
|
+
export function getAuthMethod(db) {
|
|
198
|
+
const value = getSetting(db, CONFIG_KEYS.authMethod);
|
|
199
|
+
if (value === 'oauth' || value === 'apikey')
|
|
200
|
+
return value;
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Clear saved auth method preference (will prompt again next time)
|
|
205
|
+
*/
|
|
206
|
+
export function clearAuthMethod(db) {
|
|
207
|
+
db.prepare(`DELETE FROM ${SETTINGS_TABLE} WHERE key = ?`).run(CONFIG_KEYS.authMethod);
|
|
208
|
+
}
|
|
181
209
|
/**
|
|
182
210
|
* Check if terminal app preference has been set
|
|
183
211
|
*/
|
|
@@ -41,6 +41,17 @@ export declare function buildTmuxAttachCommand(useControlMode: boolean, includeU
|
|
|
41
41
|
*/
|
|
42
42
|
export declare function configureITermTmuxPreferences(mode: 'tab' | 'window'): void;
|
|
43
43
|
export declare function configureITermTmuxWindowMode(mode: 'tab' | 'window'): void;
|
|
44
|
+
/**
|
|
45
|
+
* Build the tmux script that runs inside the container.
|
|
46
|
+
* In background mode: kills PID 1 (sleep infinity) after Claude exits to stop/remove container.
|
|
47
|
+
* In terminal/foreground mode: drops into exec bash for user inspection.
|
|
48
|
+
*/
|
|
49
|
+
export declare function buildTmuxScript(sessionName: string, claudeCmd: string, displayMode: DisplayMode): string;
|
|
50
|
+
/**
|
|
51
|
+
* Get the auto-remove flags for docker run based on display mode.
|
|
52
|
+
* Background mode containers get --rm so Docker removes them when they stop.
|
|
53
|
+
*/
|
|
54
|
+
export declare function getDockerAutoRemoveFlags(displayMode: DisplayMode): string[];
|
|
44
55
|
/**
|
|
45
56
|
* Check if the claude-credentials Docker volume exists.
|
|
46
57
|
*/
|
|
@@ -87,6 +87,47 @@ export function configureITermTmuxWindowMode(mode) {
|
|
|
87
87
|
configureITermTmuxPreferences(mode);
|
|
88
88
|
}
|
|
89
89
|
// =============================================================================
|
|
90
|
+
// Background Mode Cleanup Helpers (TKT-988)
|
|
91
|
+
// =============================================================================
|
|
92
|
+
/**
|
|
93
|
+
* Build the tmux script that runs inside the container.
|
|
94
|
+
* In background mode: kills PID 1 (sleep infinity) after Claude exits to stop/remove container.
|
|
95
|
+
* In terminal/foreground mode: drops into exec bash for user inspection.
|
|
96
|
+
*/
|
|
97
|
+
export function buildTmuxScript(sessionName, claudeCmd, displayMode) {
|
|
98
|
+
if (displayMode === 'background') {
|
|
99
|
+
return `#!/bin/bash
|
|
100
|
+
export TERM=xterm-256color
|
|
101
|
+
export COLORTERM=truecolor
|
|
102
|
+
unset CI
|
|
103
|
+
echo "🚀 Starting: ${sessionName}"
|
|
104
|
+
echo ""
|
|
105
|
+
${claudeCmd}
|
|
106
|
+
echo ""
|
|
107
|
+
echo "✅ Agent work complete. Cleaning up container..."
|
|
108
|
+
kill 1
|
|
109
|
+
`;
|
|
110
|
+
}
|
|
111
|
+
return `#!/bin/bash
|
|
112
|
+
export TERM=xterm-256color
|
|
113
|
+
export COLORTERM=truecolor
|
|
114
|
+
unset CI
|
|
115
|
+
echo "🚀 Starting: ${sessionName}"
|
|
116
|
+
echo ""
|
|
117
|
+
${claudeCmd}
|
|
118
|
+
echo ""
|
|
119
|
+
echo "✅ Agent work complete. Press Enter to close or run more commands."
|
|
120
|
+
exec bash
|
|
121
|
+
`;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Get the auto-remove flags for docker run based on display mode.
|
|
125
|
+
* Background mode containers get --rm so Docker removes them when they stop.
|
|
126
|
+
*/
|
|
127
|
+
export function getDockerAutoRemoveFlags(displayMode) {
|
|
128
|
+
return displayMode === 'background' ? ['--rm'] : [];
|
|
129
|
+
}
|
|
130
|
+
// =============================================================================
|
|
90
131
|
// Docker Credential Helpers
|
|
91
132
|
// =============================================================================
|
|
92
133
|
const CLAUDE_CREDENTIALS_VOLUME = 'claude-credentials';
|
|
@@ -740,7 +781,7 @@ function imageExists(imageName) {
|
|
|
740
781
|
* Create and start a Docker container for an agent.
|
|
741
782
|
* Uses raw Docker commands instead of devcontainer CLI.
|
|
742
783
|
*/
|
|
743
|
-
function createDockerContainer(context, containerName, imageName, config) {
|
|
784
|
+
function createDockerContainer(context, containerName, imageName, config, displayMode = 'terminal') {
|
|
744
785
|
// Build mount flags
|
|
745
786
|
// KEY: Use a named Docker volume for Claude credentials - this is how devcontainer.json
|
|
746
787
|
// was handling it. The volume persists across containers, so login once = logged in everywhere.
|
|
@@ -794,12 +835,16 @@ function createDockerContainer(context, containerName, imageName, config) {
|
|
|
794
835
|
'--cap-add=NET_RAW', // For firewall setup
|
|
795
836
|
// Note: After firewall is set up, the container is network-restricted
|
|
796
837
|
];
|
|
838
|
+
// Auto-remove container on stop for background mode (R5)
|
|
839
|
+
// Background containers should be cleaned up after work completes — nobody will attach to inspect
|
|
840
|
+
const autoRemoveFlags = getDockerAutoRemoveFlags(displayMode);
|
|
797
841
|
try {
|
|
798
842
|
const createCmd = [
|
|
799
843
|
'docker run -d',
|
|
800
844
|
`--name ${containerName}`,
|
|
801
845
|
'--user node',
|
|
802
846
|
'-w /workspace',
|
|
847
|
+
...autoRemoveFlags,
|
|
803
848
|
...mounts,
|
|
804
849
|
...envVars,
|
|
805
850
|
...resourceFlags,
|
|
@@ -898,7 +943,7 @@ function runContainerSetup(containerId, sandboxed = true) {
|
|
|
898
943
|
* Builds image and creates container if needed.
|
|
899
944
|
* Returns the container ID if successful, null otherwise.
|
|
900
945
|
*/
|
|
901
|
-
function ensureDockerContainer(context, config) {
|
|
946
|
+
function ensureDockerContainer(context, config, displayMode = 'terminal') {
|
|
902
947
|
const containerName = getContainerName(context.agentName);
|
|
903
948
|
const imageName = getImageName(context.agentName);
|
|
904
949
|
// Always create fresh container to ensure mounts are up-to-date
|
|
@@ -927,7 +972,7 @@ function ensureDockerContainer(context, config) {
|
|
|
927
972
|
}
|
|
928
973
|
// Create and start container
|
|
929
974
|
console.debug(`[runners:docker] Creating container ${containerName}`);
|
|
930
|
-
if (!createDockerContainer(context, containerName, imageName, config)) {
|
|
975
|
+
if (!createDockerContainer(context, containerName, imageName, config, displayMode)) {
|
|
931
976
|
return null;
|
|
932
977
|
}
|
|
933
978
|
const containerId = getContainerId(containerName);
|
|
@@ -1105,7 +1150,7 @@ export async function runDevcontainer(context, executor, config, displayMode = '
|
|
|
1105
1150
|
copyClaudeCredentials(context.agentDir);
|
|
1106
1151
|
// Start or reuse container using raw Docker commands
|
|
1107
1152
|
// No devcontainer CLI required!
|
|
1108
|
-
const containerId = ensureDockerContainer(context, config);
|
|
1153
|
+
const containerId = ensureDockerContainer(context, config, displayMode);
|
|
1109
1154
|
if (!containerId) {
|
|
1110
1155
|
return {
|
|
1111
1156
|
success: false,
|
|
@@ -1456,21 +1501,10 @@ async function runDevcontainerInTmux(context, devcontainerCmd, config, displayMo
|
|
|
1456
1501
|
// Extract the claude command from the devcontainer command
|
|
1457
1502
|
const cmdMatch = devcontainerCmd.match(/bash -c '(.+)'$/);
|
|
1458
1503
|
const claudeCmd = cmdMatch ? cmdMatch[1] : devcontainerCmd;
|
|
1459
|
-
// Create a script inside the container that runs claude
|
|
1460
|
-
//
|
|
1461
|
-
//
|
|
1462
|
-
|
|
1463
|
-
const tmuxScript = `#!/bin/bash
|
|
1464
|
-
export TERM=xterm-256color
|
|
1465
|
-
export COLORTERM=truecolor
|
|
1466
|
-
unset CI
|
|
1467
|
-
echo "🚀 Starting: ${sessionName}"
|
|
1468
|
-
echo ""
|
|
1469
|
-
${claudeCmd}
|
|
1470
|
-
echo ""
|
|
1471
|
-
echo "✅ Agent work complete. Press Enter to close or run more commands."
|
|
1472
|
-
exec bash
|
|
1473
|
-
`;
|
|
1504
|
+
// Create a script inside the container that runs claude
|
|
1505
|
+
// Background mode (R1): kills PID 1 to stop container after completion
|
|
1506
|
+
// Terminal/foreground mode (R2): drops into exec bash for user inspection
|
|
1507
|
+
const tmuxScript = buildTmuxScript(sessionName, claudeCmd, displayMode);
|
|
1474
1508
|
const scriptPath = `/tmp/prlt-${sessionName}.sh`;
|
|
1475
1509
|
// Write script and start tmux session inside container
|
|
1476
1510
|
// IMPORTANT: We create the session with bash first, then send keys to run the script.
|
|
@@ -2,8 +2,18 @@
|
|
|
2
2
|
* Session Utilities
|
|
3
3
|
*
|
|
4
4
|
* Shared utilities for tmux session naming, parsing, and discovery.
|
|
5
|
-
* Used by session/list.ts and session/
|
|
5
|
+
* Used by session/list.ts, session/attach.ts, session/health.ts, and session/peek.ts commands.
|
|
6
6
|
*/
|
|
7
|
+
/**
|
|
8
|
+
* Capture the last N lines from a tmux pane.
|
|
9
|
+
* Supports both host tmux sessions and container tmux sessions (via docker exec).
|
|
10
|
+
*
|
|
11
|
+
* @param sessionId - The tmux session ID to capture from
|
|
12
|
+
* @param lines - Number of scrollback lines to capture
|
|
13
|
+
* @param containerId - Optional container ID for container-based sessions
|
|
14
|
+
* @returns The captured pane content, or null if capture fails
|
|
15
|
+
*/
|
|
16
|
+
export declare function captureTmuxPane(sessionId: string, lines: number, containerId?: string): string | null;
|
|
7
17
|
/**
|
|
8
18
|
* Known action names used in session naming.
|
|
9
19
|
* These are the actions defined in pmo/actions/ that may be used when spawning agents.
|
|
@@ -2,9 +2,34 @@
|
|
|
2
2
|
* Session Utilities
|
|
3
3
|
*
|
|
4
4
|
* Shared utilities for tmux session naming, parsing, and discovery.
|
|
5
|
-
* Used by session/list.ts and session/
|
|
5
|
+
* Used by session/list.ts, session/attach.ts, session/health.ts, and session/peek.ts commands.
|
|
6
6
|
*/
|
|
7
7
|
import { execSync } from 'node:child_process';
|
|
8
|
+
/**
|
|
9
|
+
* Capture the last N lines from a tmux pane.
|
|
10
|
+
* Supports both host tmux sessions and container tmux sessions (via docker exec).
|
|
11
|
+
*
|
|
12
|
+
* @param sessionId - The tmux session ID to capture from
|
|
13
|
+
* @param lines - Number of scrollback lines to capture
|
|
14
|
+
* @param containerId - Optional container ID for container-based sessions
|
|
15
|
+
* @returns The captured pane content, or null if capture fails
|
|
16
|
+
*/
|
|
17
|
+
export function captureTmuxPane(sessionId, lines, containerId) {
|
|
18
|
+
try {
|
|
19
|
+
const captureCmd = `tmux capture-pane -t "${sessionId}" -p -S -${lines}`;
|
|
20
|
+
if (containerId) {
|
|
21
|
+
return execSync(`docker exec ${containerId} bash -c '${captureCmd}'`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 10000 }).trim();
|
|
22
|
+
}
|
|
23
|
+
return execSync(captureCmd, {
|
|
24
|
+
encoding: 'utf-8',
|
|
25
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
26
|
+
timeout: 5000,
|
|
27
|
+
}).trim();
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
8
33
|
/**
|
|
9
34
|
* Known action names used in session naming.
|
|
10
35
|
* These are the actions defined in pmo/actions/ that may be used when spawning agents.
|
|
@@ -71,6 +71,11 @@ export declare class ExecutionStorage {
|
|
|
71
71
|
* Returns the number of stale executions cleaned up.
|
|
72
72
|
*/
|
|
73
73
|
cleanupStaleExecutions(): number;
|
|
74
|
+
/**
|
|
75
|
+
* Find container sessions using prefix matching.
|
|
76
|
+
* Handles cases where the stored containerId format differs from docker ps output.
|
|
77
|
+
*/
|
|
78
|
+
private findContainerSessionsByPrefix;
|
|
74
79
|
/**
|
|
75
80
|
* Get list of host tmux session names
|
|
76
81
|
*/
|
|
@@ -215,9 +215,9 @@ export class ExecutionStorage {
|
|
|
215
215
|
}
|
|
216
216
|
let sessionExists = false;
|
|
217
217
|
if (exec.environment === 'devcontainer' && exec.containerId) {
|
|
218
|
-
// Check if session exists in container
|
|
219
|
-
const containerSessions =
|
|
220
|
-
sessionExists = containerSessions
|
|
218
|
+
// Check if session exists in container (use prefix matching for ID format differences)
|
|
219
|
+
const containerSessions = this.findContainerSessionsByPrefix(containerTmuxSessions, exec.containerId);
|
|
220
|
+
sessionExists = containerSessions.includes(exec.sessionId);
|
|
221
221
|
}
|
|
222
222
|
else {
|
|
223
223
|
// Check if session exists on host
|
|
@@ -231,6 +231,21 @@ export class ExecutionStorage {
|
|
|
231
231
|
}
|
|
232
232
|
return cleanedCount;
|
|
233
233
|
}
|
|
234
|
+
/**
|
|
235
|
+
* Find container sessions using prefix matching.
|
|
236
|
+
* Handles cases where the stored containerId format differs from docker ps output.
|
|
237
|
+
*/
|
|
238
|
+
findContainerSessionsByPrefix(containerTmuxSessions, containerId) {
|
|
239
|
+
const exact = containerTmuxSessions.get(containerId);
|
|
240
|
+
if (exact)
|
|
241
|
+
return exact;
|
|
242
|
+
for (const [key, sessions] of containerTmuxSessions) {
|
|
243
|
+
if (key.startsWith(containerId) || containerId.startsWith(key)) {
|
|
244
|
+
return sessions;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return [];
|
|
248
|
+
}
|
|
234
249
|
/**
|
|
235
250
|
* Get list of host tmux session names
|
|
236
251
|
*/
|
|
@@ -112,6 +112,7 @@ export declare function getBranchType(category?: string): string;
|
|
|
112
112
|
* - agent: the AI agent doing the work
|
|
113
113
|
*/
|
|
114
114
|
export declare function generateBranchName(ticketId: string, ticketTitle: string, ownerName: string, agentName: string, category?: string): string;
|
|
115
|
+
export type AuthMethod = 'oauth' | 'apikey';
|
|
115
116
|
export interface ExecutionConfig {
|
|
116
117
|
defaultEnvironment: ExecutionEnvironment;
|
|
117
118
|
defaultExecutor: ExecutorType;
|
|
@@ -119,6 +120,7 @@ export interface ExecutionConfig {
|
|
|
119
120
|
shell: Shell;
|
|
120
121
|
outputMode: OutputMode;
|
|
121
122
|
sandboxed: boolean;
|
|
123
|
+
authMethod?: AuthMethod;
|
|
122
124
|
tmux: {
|
|
123
125
|
session: string;
|
|
124
126
|
layout: 'split' | 'window';
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* MCP Board Tools
|
|
3
3
|
*/
|
|
4
4
|
import { z } from 'zod';
|
|
5
|
-
import { errorResponse, strictTool } from '../helpers.js';
|
|
5
|
+
import { formatTicket, errorResponse, strictTool } from '../helpers.js';
|
|
6
6
|
export function registerBoardTools(server, ctx) {
|
|
7
|
-
strictTool(server, '
|
|
7
|
+
strictTool(server, 'board_view', 'Show the kanban board', { project: z.string().optional().describe('Project ID') }, async (params) => {
|
|
8
8
|
try {
|
|
9
9
|
let projectId = params.project;
|
|
10
10
|
if (!projectId) {
|
|
@@ -27,10 +27,8 @@ export function registerBoardTools(server, ctx) {
|
|
|
27
27
|
position: col.position,
|
|
28
28
|
ticketCount: col.tickets.length,
|
|
29
29
|
tickets: col.tickets.map((t) => ({
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
priority: t.priority,
|
|
33
|
-
assignee: t.assignee,
|
|
30
|
+
...formatTicket(t),
|
|
31
|
+
labels: t.labels,
|
|
34
32
|
})),
|
|
35
33
|
})),
|
|
36
34
|
updatedAt: board.updatedAt.toISOString(),
|
|
@@ -17,9 +17,26 @@ export function registerAgentTools(server, ctx) {
|
|
|
17
17
|
return errorResponse(error);
|
|
18
18
|
}
|
|
19
19
|
});
|
|
20
|
-
strictTool(server, 'agent_status', 'Check agent status', {}, async () => {
|
|
20
|
+
strictTool(server, 'agent_status', 'Check agent status. With agent param, returns that agent\'s status. Without, auto-detects current agent or returns all agent statuses.', { agent: z.string().optional().describe('Agent name. If omitted, auto-detects current agent or returns all.') }, async (params) => {
|
|
21
21
|
try {
|
|
22
|
-
|
|
22
|
+
if (params.agent) {
|
|
23
|
+
const output = ctx.runCommand(`prlt agent status ${params.agent} --json`);
|
|
24
|
+
return textResponse(output);
|
|
25
|
+
}
|
|
26
|
+
// Try auto-detecting current agent via whoami
|
|
27
|
+
try {
|
|
28
|
+
const whoamiOutput = ctx.runCommand('prlt whoami --json');
|
|
29
|
+
const whoami = JSON.parse(whoamiOutput);
|
|
30
|
+
if (whoami.agent) {
|
|
31
|
+
const output = ctx.runCommand(`prlt agent status ${whoami.agent} --json`);
|
|
32
|
+
return textResponse(output);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// whoami failed or no agent detected, fall through
|
|
37
|
+
}
|
|
38
|
+
// No agent detected - return all agent statuses
|
|
39
|
+
const output = ctx.runCommand('prlt agent status --json');
|
|
23
40
|
return textResponse(output);
|
|
24
41
|
}
|
|
25
42
|
catch (error) {
|
|
@@ -139,7 +156,7 @@ export function registerRepoTools(server, ctx) {
|
|
|
139
156
|
});
|
|
140
157
|
}
|
|
141
158
|
export function registerBranchTools(server, ctx) {
|
|
142
|
-
strictTool(server, 'branch_list', 'List branches', {}, async () => {
|
|
159
|
+
strictTool(server, 'branch_list', 'List branches (across all HQ repos if not in a git repo)', {}, async () => {
|
|
143
160
|
try {
|
|
144
161
|
const output = ctx.runCommand('prlt branch list');
|
|
145
162
|
return textResponse(output);
|
|
@@ -161,9 +178,11 @@ export function registerBranchTools(server, ctx) {
|
|
|
161
178
|
return errorResponse(error);
|
|
162
179
|
}
|
|
163
180
|
});
|
|
164
|
-
strictTool(server, 'branch_where', '
|
|
181
|
+
strictTool(server, 'branch_where', 'Find which directory a branch is checked out in', {
|
|
182
|
+
search: z.string().describe('Branch name or ticket ID to search for'),
|
|
183
|
+
}, async (params) => {
|
|
165
184
|
try {
|
|
166
|
-
const output = ctx.runCommand(
|
|
185
|
+
const output = ctx.runCommand(`prlt branch where ${params.search}`);
|
|
167
186
|
return textResponse(output);
|
|
168
187
|
}
|
|
169
188
|
catch (error) {
|
|
@@ -314,7 +333,7 @@ export function registerUtilityTools(server, ctx) {
|
|
|
314
333
|
});
|
|
315
334
|
strictTool(server, 'session_list', 'List tmux sessions', {}, async () => {
|
|
316
335
|
try {
|
|
317
|
-
const output = ctx.runCommand('prlt session list');
|
|
336
|
+
const output = ctx.runCommand('prlt session list --json');
|
|
318
337
|
return textResponse(output);
|
|
319
338
|
}
|
|
320
339
|
catch (error) {
|
|
@@ -71,7 +71,7 @@ export function registerEpicTools(server, ctx) {
|
|
|
71
71
|
return errorResponse(error);
|
|
72
72
|
}
|
|
73
73
|
});
|
|
74
|
-
strictTool(server, '
|
|
74
|
+
strictTool(server, 'epic_view', 'Get epic details with tickets', { id: z.string().describe('Epic ID') }, async (params) => {
|
|
75
75
|
try {
|
|
76
76
|
const epic = await ctx.storage.getEpic(params.id);
|
|
77
77
|
if (!epic)
|
|
@@ -83,9 +83,14 @@ export function registerEpicTools(server, ctx) {
|
|
|
83
83
|
text: JSON.stringify({
|
|
84
84
|
success: true,
|
|
85
85
|
epic: {
|
|
86
|
-
|
|
86
|
+
id: epic.id,
|
|
87
|
+
projectId: epic.projectId,
|
|
88
|
+
title: epic.title,
|
|
89
|
+
status: epic.status,
|
|
90
|
+
position: epic.position,
|
|
91
|
+
specId: epic.specId,
|
|
87
92
|
ticketCount: tickets.length,
|
|
88
|
-
tickets: tickets.map((t) => ({ id: t.id, title: t.title, statusName: t.statusName, priority: t.priority })),
|
|
93
|
+
tickets: tickets.map((t) => ({ id: t.id, title: t.title, statusName: t.statusName, priority: t.priority, category: t.category })),
|
|
89
94
|
createdAt: epic.createdAt.toISOString(),
|
|
90
95
|
updatedAt: epic.updatedAt.toISOString(),
|
|
91
96
|
},
|
|
@@ -67,7 +67,7 @@ export function registerSpecTools(server, ctx) {
|
|
|
67
67
|
return errorResponse(error);
|
|
68
68
|
}
|
|
69
69
|
});
|
|
70
|
-
strictTool(server, '
|
|
70
|
+
strictTool(server, 'spec_view', 'Get spec details', { id: z.string().describe('Spec ID') }, async (params) => {
|
|
71
71
|
try {
|
|
72
72
|
const spec = await ctx.storage.getSpec(params.id);
|
|
73
73
|
if (!spec)
|
|
@@ -5,7 +5,7 @@ import { z } from 'zod';
|
|
|
5
5
|
import { formatTicket, formatTicketFull, errorResponse, strictTool } from '../helpers.js';
|
|
6
6
|
import { getWorkspacePriorities, setWorkspacePriorities } from '../../pmo/utils.js';
|
|
7
7
|
export function registerTicketTools(server, ctx) {
|
|
8
|
-
strictTool(server, 'ticket_list', 'List tickets with optional filters', {
|
|
8
|
+
strictTool(server, 'ticket_list', 'List tickets with optional filters. Returns summary fields only (no descriptions). Use ticket_show for full details.', {
|
|
9
9
|
project: z.string().optional().describe('Project ID'),
|
|
10
10
|
column: z.string().optional().describe('Filter by column/status'),
|
|
11
11
|
priority: z.string().optional().describe('Filter by priority (uses workspace priority scale)'),
|
|
@@ -17,9 +17,11 @@ export function registerTicketTools(server, ctx) {
|
|
|
17
17
|
label: z.string().optional().describe('Filter by label name'),
|
|
18
18
|
label_group: z.string().optional().describe('Filter by label group name'),
|
|
19
19
|
all_projects: z.boolean().optional().describe('List from all projects'),
|
|
20
|
+
limit: z.number().min(1).optional().describe('Maximum number of tickets to return (default: 50)'),
|
|
21
|
+
offset: z.number().min(0).optional().describe('Number of tickets to skip for pagination (default: 0)'),
|
|
20
22
|
}, async (params) => {
|
|
21
23
|
try {
|
|
22
|
-
const
|
|
24
|
+
const allTickets = await ctx.storage.listTickets(params.all_projects ? undefined : params.project, {
|
|
23
25
|
column: params.column,
|
|
24
26
|
priority: params.priority,
|
|
25
27
|
category: params.category,
|
|
@@ -31,31 +33,31 @@ export function registerTicketTools(server, ctx) {
|
|
|
31
33
|
labelGroup: params.label_group,
|
|
32
34
|
allProjects: params.all_projects,
|
|
33
35
|
});
|
|
36
|
+
const total = allTickets.length;
|
|
37
|
+
const offset = params.offset ?? 0;
|
|
38
|
+
const limit = params.limit ?? 50;
|
|
39
|
+
const tickets = allTickets.slice(offset, offset + limit);
|
|
34
40
|
return {
|
|
35
41
|
content: [{
|
|
36
42
|
type: 'text',
|
|
37
43
|
text: JSON.stringify({
|
|
38
44
|
success: true,
|
|
45
|
+
total,
|
|
39
46
|
count: tickets.length,
|
|
47
|
+
offset,
|
|
48
|
+
limit,
|
|
40
49
|
tickets: await Promise.all(tickets.map(async (t) => {
|
|
41
50
|
const ticketLabels = await ctx.storage.getLabelsForTicket(t.id);
|
|
42
51
|
return {
|
|
43
52
|
id: t.id,
|
|
44
53
|
title: t.title,
|
|
45
|
-
description: t.description,
|
|
46
54
|
priority: t.priority,
|
|
47
55
|
category: t.category,
|
|
48
56
|
statusName: t.statusName,
|
|
49
57
|
statusCategory: t.statusCategory,
|
|
50
|
-
projectId: t.projectId,
|
|
51
58
|
assignee: t.assignee,
|
|
52
|
-
owner: t.owner,
|
|
53
|
-
epicId: t.epicId,
|
|
54
|
-
branch: t.branch,
|
|
55
59
|
position: t.position,
|
|
56
60
|
labels: ticketLabels.map(l => ({ id: l.id, name: l.name, groupName: l.groupName })),
|
|
57
|
-
createdAt: t.createdAt.toISOString(),
|
|
58
|
-
updatedAt: t.updatedAt.toISOString(),
|
|
59
61
|
};
|
|
60
62
|
})),
|
|
61
63
|
}, null, 2),
|