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.
Files changed (54) hide show
  1. package/.claude/hooks/config-loader.mjs +93 -0
  2. package/.claude/hooks/heartbeat.mjs +331 -0
  3. package/.claude/hooks/notification.mjs +35 -0
  4. package/.claude/hooks/permission_request.mjs +307 -0
  5. package/.claude/hooks/post_tool_use.mjs +137 -0
  6. package/.claude/hooks/pre_tool_use.mjs +451 -0
  7. package/.claude/hooks/session-register.mjs +274 -0
  8. package/.claude/hooks/session_end.mjs +256 -0
  9. package/.claude/hooks/session_start.mjs +308 -0
  10. package/.claude/hooks/stop.mjs +277 -0
  11. package/.claude/hooks/user_prompt_submit.mjs +91 -0
  12. package/LICENSE +21 -0
  13. package/README.md +243 -0
  14. package/lib/auth/api-key.js +110 -0
  15. package/lib/auth/credentials.js +341 -0
  16. package/lib/backup/manager.js +461 -0
  17. package/lib/cli/daemon-commands.js +299 -0
  18. package/lib/cli/index.js +303 -0
  19. package/lib/cli/session-commands.js +294 -0
  20. package/lib/cli/snapshot-commands.js +223 -0
  21. package/lib/cli/worktree-commands.js +291 -0
  22. package/lib/config/manager.js +306 -0
  23. package/lib/daemon/lifecycle.js +336 -0
  24. package/lib/daemon/pid-manager.js +160 -0
  25. package/lib/daemon/teleportation-daemon.js +2009 -0
  26. package/lib/handoff/config.js +102 -0
  27. package/lib/handoff/example.js +152 -0
  28. package/lib/handoff/git-handoff.js +351 -0
  29. package/lib/handoff/handoff.js +277 -0
  30. package/lib/handoff/index.js +25 -0
  31. package/lib/handoff/session-state.js +238 -0
  32. package/lib/install/installer.js +555 -0
  33. package/lib/machine-coders/claude-code-adapter.js +329 -0
  34. package/lib/machine-coders/example.js +239 -0
  35. package/lib/machine-coders/gemini-cli-adapter.js +406 -0
  36. package/lib/machine-coders/index.js +103 -0
  37. package/lib/machine-coders/interface.js +168 -0
  38. package/lib/router/classifier.js +251 -0
  39. package/lib/router/example.js +92 -0
  40. package/lib/router/index.js +69 -0
  41. package/lib/router/mech-llms-client.js +277 -0
  42. package/lib/router/models.js +188 -0
  43. package/lib/router/router.js +382 -0
  44. package/lib/session/cleanup.js +100 -0
  45. package/lib/session/metadata.js +258 -0
  46. package/lib/session/mute-checker.js +114 -0
  47. package/lib/session-registry/manager.js +302 -0
  48. package/lib/snapshot/manager.js +390 -0
  49. package/lib/utils/errors.js +166 -0
  50. package/lib/utils/logger.js +148 -0
  51. package/lib/utils/retry.js +155 -0
  52. package/lib/worktree/manager.js +301 -0
  53. package/package.json +66 -0
  54. 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
+ };
@@ -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
+ }