teleportation-cli 1.0.0
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/.claude/hooks/config-loader.mjs +93 -0
- package/.claude/hooks/heartbeat.mjs +331 -0
- package/.claude/hooks/notification.mjs +35 -0
- package/.claude/hooks/permission_request.mjs +307 -0
- package/.claude/hooks/post_tool_use.mjs +137 -0
- package/.claude/hooks/pre_tool_use.mjs +451 -0
- package/.claude/hooks/session-register.mjs +274 -0
- package/.claude/hooks/session_end.mjs +256 -0
- package/.claude/hooks/session_start.mjs +308 -0
- package/.claude/hooks/stop.mjs +277 -0
- package/.claude/hooks/user_prompt_submit.mjs +91 -0
- package/LICENSE +21 -0
- package/README.md +243 -0
- package/lib/auth/api-key.js +110 -0
- package/lib/auth/credentials.js +341 -0
- package/lib/backup/manager.js +461 -0
- package/lib/cli/daemon-commands.js +299 -0
- package/lib/cli/index.js +303 -0
- package/lib/cli/session-commands.js +294 -0
- package/lib/cli/snapshot-commands.js +223 -0
- package/lib/cli/worktree-commands.js +291 -0
- package/lib/config/manager.js +306 -0
- package/lib/daemon/lifecycle.js +336 -0
- package/lib/daemon/pid-manager.js +160 -0
- package/lib/daemon/teleportation-daemon.js +2009 -0
- package/lib/handoff/config.js +102 -0
- package/lib/handoff/example.js +152 -0
- package/lib/handoff/git-handoff.js +351 -0
- package/lib/handoff/handoff.js +277 -0
- package/lib/handoff/index.js +25 -0
- package/lib/handoff/session-state.js +238 -0
- package/lib/install/installer.js +555 -0
- package/lib/machine-coders/claude-code-adapter.js +329 -0
- package/lib/machine-coders/example.js +239 -0
- package/lib/machine-coders/gemini-cli-adapter.js +406 -0
- package/lib/machine-coders/index.js +103 -0
- package/lib/machine-coders/interface.js +168 -0
- package/lib/router/classifier.js +251 -0
- package/lib/router/example.js +92 -0
- package/lib/router/index.js +69 -0
- package/lib/router/mech-llms-client.js +277 -0
- package/lib/router/models.js +188 -0
- package/lib/router/router.js +382 -0
- package/lib/session/cleanup.js +100 -0
- package/lib/session/metadata.js +258 -0
- package/lib/session/mute-checker.js +114 -0
- package/lib/session-registry/manager.js +302 -0
- package/lib/snapshot/manager.js +390 -0
- package/lib/utils/errors.js +166 -0
- package/lib/utils/logger.js +148 -0
- package/lib/utils/retry.js +155 -0
- package/lib/worktree/manager.js +301 -0
- package/package.json +66 -0
- package/teleportation-cli.cjs +2987 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Daemon Management Commands
|
|
3
|
+
* Handles away mode, back mode, and daemon status commands
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { checkDaemonStatus, startDaemon, stopDaemon } from '../daemon/pid-manager.js';
|
|
7
|
+
|
|
8
|
+
// Color helpers
|
|
9
|
+
const c = {
|
|
10
|
+
red: (text) => '\x1b[0;31m' + text + '\x1b[0m',
|
|
11
|
+
green: (text) => '\x1b[0;32m' + text + '\x1b[0m',
|
|
12
|
+
yellow: (text) => '\x1b[1;33m' + text + '\x1b[0m',
|
|
13
|
+
blue: (text) => '\x1b[0;34m' + text + '\x1b[0m',
|
|
14
|
+
cyan: (text) => '\x1b[0;36m' + text + '\x1b[0m',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Get relay API configuration from environment
|
|
19
|
+
*/
|
|
20
|
+
function getRelayConfig() {
|
|
21
|
+
return {
|
|
22
|
+
url: process.env.RELAY_API_URL || '',
|
|
23
|
+
key: process.env.RELAY_API_KEY || '',
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Update session daemon state via Relay API
|
|
29
|
+
*/
|
|
30
|
+
async function updateSessionDaemonState(sessionId, updates) {
|
|
31
|
+
const { url, key } = getRelayConfig();
|
|
32
|
+
|
|
33
|
+
if (!sessionId || !url || !key) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const res = await fetch(`${url}/api/sessions/${encodeURIComponent(sessionId)}/daemon-state`, {
|
|
39
|
+
method: 'PATCH',
|
|
40
|
+
headers: {
|
|
41
|
+
'Content-Type': 'application/json',
|
|
42
|
+
'Authorization': `Bearer ${key}`,
|
|
43
|
+
},
|
|
44
|
+
body: JSON.stringify(updates),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
if (!res.ok) {
|
|
48
|
+
console.error(`[daemon-commands] Failed to update daemon_state: HTTP ${res.status}`);
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return true;
|
|
53
|
+
} catch (err) {
|
|
54
|
+
console.error(`[daemon-commands] Error updating daemon_state:`, err.message);
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Get current session ID from environment or config
|
|
61
|
+
*/
|
|
62
|
+
function getSessionId() {
|
|
63
|
+
// Try environment variable first
|
|
64
|
+
if (process.env.TELEPORTATION_SESSION_ID) {
|
|
65
|
+
return process.env.TELEPORTATION_SESSION_ID;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Could also check config file or other sources
|
|
69
|
+
// For now, we'll require it to be set
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Command: teleportation away
|
|
75
|
+
* Mark session as away and start daemon
|
|
76
|
+
*/
|
|
77
|
+
export async function commandAway() {
|
|
78
|
+
console.log(c.yellow('🚀 Marking session as away and starting daemon...\n'));
|
|
79
|
+
|
|
80
|
+
const sessionId = getSessionId();
|
|
81
|
+
if (!sessionId) {
|
|
82
|
+
console.log(c.red('❌ Error: TELEPORTATION_SESSION_ID not set\n'));
|
|
83
|
+
console.log(c.cyan('Set the environment variable or use: teleportation away --session <id>\n'));
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
// Update session daemon state
|
|
89
|
+
const updated = await updateSessionDaemonState(sessionId, {
|
|
90
|
+
is_away: true,
|
|
91
|
+
status: 'running',
|
|
92
|
+
started_reason: 'cli_away',
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
if (!updated) {
|
|
96
|
+
console.log(c.yellow('⚠️ Warning: Could not update session state via Relay API\n'));
|
|
97
|
+
console.log(c.cyan('Continuing with local daemon start...\n'));
|
|
98
|
+
} else {
|
|
99
|
+
console.log(c.green('✅ Session marked as away in Relay API\n'));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Start daemon
|
|
103
|
+
const status = await checkDaemonStatus();
|
|
104
|
+
if (status.running) {
|
|
105
|
+
console.log(c.yellow('⚠️ Daemon already running (PID: ' + status.pid + ')\n'));
|
|
106
|
+
} else {
|
|
107
|
+
const result = await startDaemon();
|
|
108
|
+
if (result.success) {
|
|
109
|
+
console.log(c.green('✅ Daemon started (PID: ' + result.pid + ')\n'));
|
|
110
|
+
} else {
|
|
111
|
+
console.log(c.red('❌ Failed to start daemon: ' + result.error + '\n'));
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
console.log(c.green('✅ Session marked as away. Daemon is running.\n'));
|
|
117
|
+
console.log(c.cyan('When you return, run: teleportation back\n'));
|
|
118
|
+
} catch (error) {
|
|
119
|
+
console.log(c.red('❌ Error: ' + error.message + '\n'));
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Command: teleportation back
|
|
126
|
+
* Mark session as back and stop daemon if no other sessions
|
|
127
|
+
*/
|
|
128
|
+
export async function commandBack() {
|
|
129
|
+
console.log(c.yellow('🔙 Marking session as back and stopping daemon...\n'));
|
|
130
|
+
|
|
131
|
+
const sessionId = getSessionId();
|
|
132
|
+
if (!sessionId) {
|
|
133
|
+
console.log(c.red('❌ Error: TELEPORTATION_SESSION_ID not set\n'));
|
|
134
|
+
console.log(c.cyan('Set the environment variable or use: teleportation back --session <id>\n'));
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
// Update session daemon state
|
|
140
|
+
const updated = await updateSessionDaemonState(sessionId, {
|
|
141
|
+
is_away: false,
|
|
142
|
+
status: 'stopped',
|
|
143
|
+
started_reason: null,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
if (!updated) {
|
|
147
|
+
console.log(c.yellow('⚠️ Warning: Could not update session state via Relay API\n'));
|
|
148
|
+
console.log(c.cyan('Continuing with local daemon stop...\n'));
|
|
149
|
+
} else {
|
|
150
|
+
console.log(c.green('✅ Session marked as back in Relay API\n'));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Check if other sessions still need daemon
|
|
154
|
+
const { url, key } = getRelayConfig();
|
|
155
|
+
let shouldStop = true;
|
|
156
|
+
|
|
157
|
+
if (url && key) {
|
|
158
|
+
try {
|
|
159
|
+
const res = await fetch(`${url}/api/sessions`, {
|
|
160
|
+
headers: { 'Authorization': `Bearer ${key}` },
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
if (res.ok) {
|
|
164
|
+
const sessions = await res.json();
|
|
165
|
+
const otherRunning = sessions.some(
|
|
166
|
+
(s) =>
|
|
167
|
+
s.session_id !== sessionId &&
|
|
168
|
+
s.daemon_state &&
|
|
169
|
+
s.daemon_state.status === 'running'
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
if (otherRunning) {
|
|
173
|
+
console.log(c.yellow('⚠️ Other sessions still have daemon running\n'));
|
|
174
|
+
shouldStop = false;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
} catch (err) {
|
|
178
|
+
console.log(c.yellow('⚠️ Could not check other sessions\n'));
|
|
179
|
+
// Continue with stop anyway
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (shouldStop) {
|
|
184
|
+
const status = await checkDaemonStatus();
|
|
185
|
+
if (!status.running) {
|
|
186
|
+
console.log(c.yellow('⚠️ Daemon not running\n'));
|
|
187
|
+
} else {
|
|
188
|
+
const result = await stopDaemon();
|
|
189
|
+
if (result.success) {
|
|
190
|
+
console.log(c.green('✅ Daemon stopped\n'));
|
|
191
|
+
} else {
|
|
192
|
+
console.log(c.red('❌ Failed to stop daemon: ' + result.error + '\n'));
|
|
193
|
+
process.exit(1);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
console.log(c.green('✅ Session marked as back.\n'));
|
|
199
|
+
} catch (error) {
|
|
200
|
+
console.log(c.red('❌ Error: ' + error.message + '\n'));
|
|
201
|
+
process.exit(1);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Command: teleportation daemon-status
|
|
207
|
+
* Show detailed daemon status
|
|
208
|
+
*/
|
|
209
|
+
export async function commandDaemonStatus() {
|
|
210
|
+
console.log(c.cyan('\n📊 Daemon Status\n'));
|
|
211
|
+
|
|
212
|
+
const sessionId = getSessionId();
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
// Check daemon process
|
|
216
|
+
const status = await checkDaemonStatus();
|
|
217
|
+
|
|
218
|
+
console.log(c.yellow('Process:'));
|
|
219
|
+
if (status.running) {
|
|
220
|
+
console.log(c.green(` Status: Running`));
|
|
221
|
+
console.log(` PID: ${status.pid}`);
|
|
222
|
+
console.log(` Uptime: ${formatUptime(status.uptime)}`);
|
|
223
|
+
} else {
|
|
224
|
+
console.log(c.red(` Status: Stopped`));
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Get session daemon state from Relay API
|
|
228
|
+
if (sessionId) {
|
|
229
|
+
const { url, key } = getRelayConfig();
|
|
230
|
+
|
|
231
|
+
if (url && key) {
|
|
232
|
+
try {
|
|
233
|
+
const res = await fetch(`${url}/api/sessions/${encodeURIComponent(sessionId)}`, {
|
|
234
|
+
headers: { 'Authorization': `Bearer ${key}` },
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
if (res.ok) {
|
|
238
|
+
const session = await res.json();
|
|
239
|
+
const daemonState = session.daemon_state;
|
|
240
|
+
|
|
241
|
+
if (daemonState) {
|
|
242
|
+
console.log(c.yellow('\nSession State:'));
|
|
243
|
+
console.log(` Status: ${daemonState.status === 'running' ? c.green('Running') : c.red('Stopped')}`);
|
|
244
|
+
console.log(` Away: ${daemonState.is_away ? c.yellow('Yes') : c.green('No')}`);
|
|
245
|
+
|
|
246
|
+
if (daemonState.started_at) {
|
|
247
|
+
const startedDate = new Date(daemonState.started_at);
|
|
248
|
+
console.log(` Started: ${startedDate.toLocaleString()}`);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (daemonState.started_reason) {
|
|
252
|
+
console.log(` Started Reason: ${daemonState.started_reason}`);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (daemonState.last_approval_location) {
|
|
256
|
+
console.log(` Last Approval: ${daemonState.last_approval_location}`);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (daemonState.stopped_reason) {
|
|
260
|
+
console.log(` Stopped Reason: ${daemonState.stopped_reason}`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
} catch (err) {
|
|
265
|
+
console.log(c.yellow('\n⚠️ Could not fetch session state from Relay API\n'));
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
console.log();
|
|
271
|
+
} catch (error) {
|
|
272
|
+
console.log(c.red('❌ Error: ' + error.message + '\n'));
|
|
273
|
+
process.exit(1);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Format uptime in human-readable format
|
|
279
|
+
*/
|
|
280
|
+
function formatUptime(seconds) {
|
|
281
|
+
if (!seconds) return 'unknown';
|
|
282
|
+
|
|
283
|
+
const hours = Math.floor(seconds / 3600);
|
|
284
|
+
const minutes = Math.floor((seconds % 3600) / 60);
|
|
285
|
+
const secs = Math.floor(seconds % 60);
|
|
286
|
+
|
|
287
|
+
const parts = [];
|
|
288
|
+
if (hours > 0) parts.push(`${hours}h`);
|
|
289
|
+
if (minutes > 0) parts.push(`${minutes}m`);
|
|
290
|
+
if (secs > 0 || parts.length === 0) parts.push(`${secs}s`);
|
|
291
|
+
|
|
292
|
+
return parts.join(' ');
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export default {
|
|
296
|
+
commandAway,
|
|
297
|
+
commandBack,
|
|
298
|
+
commandDaemonStatus,
|
|
299
|
+
};
|
package/lib/cli/index.js
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CLI Commands Index
|
|
4
|
+
* Exports all snapshot/worktree/session commands for integration with teleportation-cli.cjs
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Worktree commands - import for local use
|
|
8
|
+
import {
|
|
9
|
+
commandWorktreeCreate,
|
|
10
|
+
commandWorktreeList,
|
|
11
|
+
commandWorktreeRemove,
|
|
12
|
+
commandWorktreeInfo,
|
|
13
|
+
commandWorktreeMerge,
|
|
14
|
+
commandWorktreePrune,
|
|
15
|
+
commandWorktreeUse
|
|
16
|
+
} from './worktree-commands.js';
|
|
17
|
+
|
|
18
|
+
// Snapshot commands - import for local use
|
|
19
|
+
import {
|
|
20
|
+
commandSnapshotCreate,
|
|
21
|
+
commandSnapshotList,
|
|
22
|
+
commandSnapshotRestore,
|
|
23
|
+
commandSnapshotDiff,
|
|
24
|
+
commandSnapshotDelete,
|
|
25
|
+
commandSnapshotDeleteAll
|
|
26
|
+
} from './snapshot-commands.js';
|
|
27
|
+
|
|
28
|
+
// Session commands - import for local use
|
|
29
|
+
import {
|
|
30
|
+
commandSessionList,
|
|
31
|
+
commandSessionInfo,
|
|
32
|
+
commandCheckConflicts,
|
|
33
|
+
commandSessionStats,
|
|
34
|
+
commandSessionPause,
|
|
35
|
+
commandSessionResume,
|
|
36
|
+
commandSessionComplete,
|
|
37
|
+
commandSessionCleanup
|
|
38
|
+
} from './session-commands.js';
|
|
39
|
+
|
|
40
|
+
// Re-export all commands
|
|
41
|
+
export {
|
|
42
|
+
commandWorktreeCreate,
|
|
43
|
+
commandWorktreeList,
|
|
44
|
+
commandWorktreeRemove,
|
|
45
|
+
commandWorktreeInfo,
|
|
46
|
+
commandWorktreeMerge,
|
|
47
|
+
commandWorktreePrune,
|
|
48
|
+
commandWorktreeUse,
|
|
49
|
+
commandSnapshotCreate,
|
|
50
|
+
commandSnapshotList,
|
|
51
|
+
commandSnapshotRestore,
|
|
52
|
+
commandSnapshotDiff,
|
|
53
|
+
commandSnapshotDelete,
|
|
54
|
+
commandSnapshotDeleteAll,
|
|
55
|
+
commandSessionList,
|
|
56
|
+
commandSessionInfo,
|
|
57
|
+
commandCheckConflicts,
|
|
58
|
+
commandSessionStats,
|
|
59
|
+
commandSessionPause,
|
|
60
|
+
commandSessionResume,
|
|
61
|
+
commandSessionComplete,
|
|
62
|
+
commandSessionCleanup
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Re-export types
|
|
66
|
+
export { SnapshotType } from '../snapshot/manager.js';
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Parse CLI arguments for worktree/snapshot commands
|
|
70
|
+
* @param {Array<string>} args - Command line arguments
|
|
71
|
+
* @returns {Object} Parsed arguments
|
|
72
|
+
*/
|
|
73
|
+
export function parseArgs(args) {
|
|
74
|
+
const parsed = {
|
|
75
|
+
command: null,
|
|
76
|
+
subcommand: null,
|
|
77
|
+
sessionId: null,
|
|
78
|
+
snapshotId: null,
|
|
79
|
+
branch: null,
|
|
80
|
+
agent: 'claude-code',
|
|
81
|
+
base: 'main',
|
|
82
|
+
target: 'main',
|
|
83
|
+
type: 'checkpoint',
|
|
84
|
+
message: '',
|
|
85
|
+
force: false,
|
|
86
|
+
keepSnapshot: true,
|
|
87
|
+
repoOnly: false,
|
|
88
|
+
status: null,
|
|
89
|
+
deleteAfter: false
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
let i = 0;
|
|
93
|
+
while (i < args.length) {
|
|
94
|
+
const arg = args[i];
|
|
95
|
+
|
|
96
|
+
if (arg === 'worktree' || arg === 'snapshot' || arg === 'session') {
|
|
97
|
+
parsed.command = arg;
|
|
98
|
+
} else if (arg === 'create' || arg === 'list' || arg === 'remove' ||
|
|
99
|
+
arg === 'info' || arg === 'merge' || arg === 'prune' ||
|
|
100
|
+
arg === 'use' || arg === 'restore' || arg === 'diff' ||
|
|
101
|
+
arg === 'delete' || arg === 'delete-all' || arg === 'stats' ||
|
|
102
|
+
arg === 'pause' || arg === 'resume' || arg === 'complete' ||
|
|
103
|
+
arg === 'cleanup' || arg === 'check-conflicts') {
|
|
104
|
+
parsed.subcommand = arg;
|
|
105
|
+
} else if (arg === '--session-id' || arg === '-s') {
|
|
106
|
+
parsed.sessionId = args[++i];
|
|
107
|
+
} else if (arg === '--snapshot-id') {
|
|
108
|
+
parsed.snapshotId = args[++i];
|
|
109
|
+
} else if (arg === '--branch' || arg === '-b') {
|
|
110
|
+
parsed.branch = args[++i];
|
|
111
|
+
} else if (arg === '--agent' || arg === '-a') {
|
|
112
|
+
parsed.agent = args[++i];
|
|
113
|
+
} else if (arg === '--base') {
|
|
114
|
+
parsed.base = args[++i];
|
|
115
|
+
} else if (arg === '--target') {
|
|
116
|
+
parsed.target = args[++i];
|
|
117
|
+
} else if (arg === '--type' || arg === '-t') {
|
|
118
|
+
parsed.type = args[++i];
|
|
119
|
+
} else if (arg === '--message' || arg === '-m') {
|
|
120
|
+
parsed.message = args[++i];
|
|
121
|
+
} else if (arg === '--force' || arg === '-f') {
|
|
122
|
+
parsed.force = true;
|
|
123
|
+
} else if (arg === '--no-snapshot') {
|
|
124
|
+
parsed.keepSnapshot = false;
|
|
125
|
+
} else if (arg === '--repo-only') {
|
|
126
|
+
parsed.repoOnly = true;
|
|
127
|
+
} else if (arg === '--status') {
|
|
128
|
+
parsed.status = args[++i];
|
|
129
|
+
} else if (arg === '--delete-after') {
|
|
130
|
+
parsed.deleteAfter = true;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
i++;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return parsed;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Route to the appropriate command handler
|
|
141
|
+
* @param {Object} parsed - Parsed arguments
|
|
142
|
+
* @returns {Promise<any>}
|
|
143
|
+
*/
|
|
144
|
+
export async function routeCommand(parsed) {
|
|
145
|
+
const { command, subcommand } = parsed;
|
|
146
|
+
|
|
147
|
+
if (command === 'worktree') {
|
|
148
|
+
switch (subcommand) {
|
|
149
|
+
case 'create':
|
|
150
|
+
return commandWorktreeCreate(parsed);
|
|
151
|
+
case 'list':
|
|
152
|
+
return commandWorktreeList();
|
|
153
|
+
case 'remove':
|
|
154
|
+
return commandWorktreeRemove(parsed);
|
|
155
|
+
case 'info':
|
|
156
|
+
return commandWorktreeInfo(parsed);
|
|
157
|
+
case 'merge':
|
|
158
|
+
return commandWorktreeMerge(parsed);
|
|
159
|
+
case 'prune':
|
|
160
|
+
return commandWorktreePrune();
|
|
161
|
+
case 'use':
|
|
162
|
+
return commandWorktreeUse(parsed);
|
|
163
|
+
default:
|
|
164
|
+
console.log('Unknown worktree subcommand. Available: create, list, remove, info, merge, prune, use');
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (command === 'snapshot') {
|
|
170
|
+
switch (subcommand) {
|
|
171
|
+
case 'create':
|
|
172
|
+
return commandSnapshotCreate(parsed);
|
|
173
|
+
case 'list':
|
|
174
|
+
return commandSnapshotList(parsed);
|
|
175
|
+
case 'restore':
|
|
176
|
+
return commandSnapshotRestore(parsed);
|
|
177
|
+
case 'diff':
|
|
178
|
+
return commandSnapshotDiff(parsed);
|
|
179
|
+
case 'delete':
|
|
180
|
+
return commandSnapshotDelete(parsed);
|
|
181
|
+
case 'delete-all':
|
|
182
|
+
return commandSnapshotDeleteAll(parsed);
|
|
183
|
+
default:
|
|
184
|
+
console.log('Unknown snapshot subcommand. Available: create, list, restore, diff, delete, delete-all');
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (command === 'session') {
|
|
190
|
+
switch (subcommand) {
|
|
191
|
+
case 'list':
|
|
192
|
+
return commandSessionList(parsed);
|
|
193
|
+
case 'info':
|
|
194
|
+
return commandSessionInfo(parsed);
|
|
195
|
+
case 'check-conflicts':
|
|
196
|
+
return commandCheckConflicts(parsed);
|
|
197
|
+
case 'stats':
|
|
198
|
+
return commandSessionStats();
|
|
199
|
+
case 'pause':
|
|
200
|
+
return commandSessionPause(parsed);
|
|
201
|
+
case 'resume':
|
|
202
|
+
return commandSessionResume(parsed);
|
|
203
|
+
case 'complete':
|
|
204
|
+
return commandSessionComplete(parsed);
|
|
205
|
+
case 'cleanup':
|
|
206
|
+
return commandSessionCleanup();
|
|
207
|
+
default:
|
|
208
|
+
console.log('Unknown session subcommand. Available: list, info, check-conflicts, stats, pause, resume, complete, cleanup');
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
console.log('Unknown command. Available: worktree, snapshot, session');
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Print help for worktree/snapshot/session commands
|
|
218
|
+
*/
|
|
219
|
+
export function printHelp() {
|
|
220
|
+
console.log(`
|
|
221
|
+
Teleportation Worktree, Snapshot & Session Commands
|
|
222
|
+
|
|
223
|
+
WORKTREE COMMANDS:
|
|
224
|
+
worktree create -s <session-id> -b <branch> [--agent <agent>] [--base <base-branch>]
|
|
225
|
+
Create a new worktree for isolated session development
|
|
226
|
+
|
|
227
|
+
worktree list
|
|
228
|
+
List all session worktrees
|
|
229
|
+
|
|
230
|
+
worktree remove -s <session-id> [--force] [--no-snapshot]
|
|
231
|
+
Remove a worktree (creates pre-destroy snapshot by default)
|
|
232
|
+
|
|
233
|
+
worktree info [-s <session-id>]
|
|
234
|
+
Show information about a worktree
|
|
235
|
+
|
|
236
|
+
worktree use -s <session-id>
|
|
237
|
+
Show path to switch to a worktree
|
|
238
|
+
|
|
239
|
+
worktree merge -s <session-id> [--target <branch>] [--delete-after]
|
|
240
|
+
Merge a worktree branch (manual process guidance)
|
|
241
|
+
|
|
242
|
+
worktree prune
|
|
243
|
+
Clean up stale worktree references
|
|
244
|
+
|
|
245
|
+
SNAPSHOT COMMANDS:
|
|
246
|
+
snapshot create [-s <session-id>] [-t <type>] [-m <message>]
|
|
247
|
+
Create a snapshot (types: baseline, checkpoint, pre-merge, pre-commit, auto, pre-destroy)
|
|
248
|
+
|
|
249
|
+
snapshot list [-s <session-id>]
|
|
250
|
+
List all snapshots for a session
|
|
251
|
+
|
|
252
|
+
snapshot restore --snapshot-id <id> [--force]
|
|
253
|
+
Restore a previous snapshot
|
|
254
|
+
|
|
255
|
+
snapshot diff --snapshot-id <id>
|
|
256
|
+
Show diff between current state and snapshot
|
|
257
|
+
|
|
258
|
+
snapshot delete --snapshot-id <id> [--force]
|
|
259
|
+
Delete a snapshot
|
|
260
|
+
|
|
261
|
+
snapshot delete-all [-s <session-id>] [--force]
|
|
262
|
+
Delete all snapshots for a session
|
|
263
|
+
|
|
264
|
+
SESSION COMMANDS:
|
|
265
|
+
session list [--status <status>] [--repo-only]
|
|
266
|
+
List all registered sessions
|
|
267
|
+
|
|
268
|
+
session info [-s <session-id>]
|
|
269
|
+
Show detailed session information
|
|
270
|
+
|
|
271
|
+
session check-conflicts [-s <session-id>]
|
|
272
|
+
Check for file conflicts with other active sessions
|
|
273
|
+
|
|
274
|
+
session stats
|
|
275
|
+
Show session statistics
|
|
276
|
+
|
|
277
|
+
session pause [-s <session-id>]
|
|
278
|
+
Pause a session
|
|
279
|
+
|
|
280
|
+
session resume [-s <session-id>]
|
|
281
|
+
Resume a paused session
|
|
282
|
+
|
|
283
|
+
session complete [-s <session-id>]
|
|
284
|
+
Mark a session as completed
|
|
285
|
+
|
|
286
|
+
session cleanup
|
|
287
|
+
Clean up stale sessions (inactive > 24h)
|
|
288
|
+
|
|
289
|
+
OPTIONS:
|
|
290
|
+
-s, --session-id <id> Session identifier
|
|
291
|
+
-b, --branch <name> Branch name
|
|
292
|
+
-a, --agent <type> Agent type (default: claude-code)
|
|
293
|
+
-t, --type <type> Snapshot type
|
|
294
|
+
-m, --message <msg> Snapshot message
|
|
295
|
+
-f, --force Force operation
|
|
296
|
+
--base <branch> Base branch for new worktree (default: main)
|
|
297
|
+
--target <branch> Target branch for merge (default: main)
|
|
298
|
+
--no-snapshot Skip creating snapshot before destructive ops
|
|
299
|
+
--repo-only Only show sessions for current repository
|
|
300
|
+
--status <status> Filter by status (active, paused, completed)
|
|
301
|
+
--delete-after Delete worktree after merge
|
|
302
|
+
`);
|
|
303
|
+
}
|