forked from letta-ai/letta-code
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathregistry.ts
More file actions
656 lines (633 loc) · 17.7 KB
/
Copy pathregistry.ts
File metadata and controls
656 lines (633 loc) · 17.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
// src/cli/commands/registry.ts
// Registry of available CLI commands
import { CAVEMAN_MODE_HINT, normalizeCavemanMode } from "./caveman";
import { handleSecretCommand } from "./secret";
type CommandResult = { success: boolean; output: string };
type CommandHandler = (
args: string[],
) => Promise<string | CommandResult> | string | CommandResult;
interface Command {
desc: string;
handler: CommandHandler;
args?: string; // Optional argument syntax hint (e.g., "[conversation_id]", "<name>")
hidden?: boolean; // Hidden commands don't show in autocomplete but still work
order?: number; // Lower numbers appear first in autocomplete (default: 100)
noArgs?: boolean; // If true, reject any arguments passed to this command
}
export const commands: Record<string, Command> = {
// === Page 1: Most commonly used (order 10-19) ===
"/agents": {
desc: "Browse agents (pinned, Letta Code, all)",
order: 10,
noArgs: true,
handler: () => {
// Handled specially in App.tsx to open agent browser
return "Opening agent browser...";
},
},
"/model": {
desc: "Switch model",
order: 11,
noArgs: true,
handler: () => {
return "Opening model selector...";
},
},
"/init": {
desc: "Initialize (or re-init) your agent's memory",
order: 12,
noArgs: true,
handler: () => {
// Handled specially in App.tsx to send initialization prompt
return "Initializing memory...";
},
},
"/doctor": {
desc: "Audit and refine your memory structure",
order: 12.1,
noArgs: true,
handler: () => {
// Handled specially in App.tsx to send doctor prompt
return "Running memory doctor...";
},
},
"/remember": {
desc: "Remember something from the conversation (/remember [instructions])",
order: 13,
handler: () => {
// Handled specially in App.tsx to trigger memory update
return "Processing memory request...";
},
},
"/reflect": {
desc: "Launch reflection (/reflect [transcript_file])",
args: "[transcript_file]",
order: 50,
handler: () => {
// Handled specially in App.tsx
return "Launching reflection agent...";
},
},
"/reflection": {
desc: "Alias for /reflect",
args: "[transcript_file]",
handler: () => {
// Handled specially in App.tsx
return "Launching reflection agent...";
},
},
"/skills": {
desc: "Browse available skills",
order: 28,
noArgs: true,
handler: () => {
// Handled specially in App.tsx to open skills browser overlay
return "Opening skills browser...";
},
},
"/skill-creator": {
desc: "Enter skill creation mode (/skill-creator [description])",
order: 28.5,
handler: () => {
// Handled specially in App.tsx to trigger skill-creation workflow
return "Starting skill creation...";
},
},
"/caveman": {
desc: "Switch cave-code mode",
args: CAVEMAN_MODE_HINT,
order: 29,
handler: (args) => {
const mode = normalizeCavemanMode(args.join(" "));
if (!mode) {
return {
success: false,
output: `Usage: /caveman ${CAVEMAN_MODE_HINT}`,
};
}
return {
success: false,
output: `/caveman ${mode} must be used inside the interactive CLI; mode was not applied.`,
};
},
},
"/memory": {
desc: "View your agent's memory",
order: 15,
noArgs: true,
handler: () => {
// Handled specially in App.tsx to open memory viewer
return "Opening memory viewer...";
},
},
"/palace": {
desc: "Open the Memory Palace in your browser",
order: 16,
noArgs: true,
handler: () => {
// Handled specially in App.tsx - opens browser directly
return "Opening Memory Palace...";
},
},
"/sleeptime": {
desc: "Configure reflection reminder trigger settings",
order: 15.5,
noArgs: true,
handler: () => {
// Handled specially in App.tsx to open sleeptime settings
return "Opening sleeptime settings...";
},
},
"/compaction": {
desc: "Configure compaction mode settings",
order: 15.6,
noArgs: true,
handler: () => {
// Handled specially in App.tsx to open compaction settings
return "Opening compaction settings...";
},
},
"/memfs": {
desc: "Manage filesystem-backed memory (/memfs [enable|disable|sync|reset])",
args: "[enable|disable|sync|reset]",
order: 27.5, // Advanced feature, near /toolset
handler: () => {
// Handled specially in App.tsx
return "Managing memory filesystem...";
},
},
"/search": {
desc: "Search messages across all agents (/search [query])",
order: 15.1,
handler: () => {
// Handled specially in App.tsx to show message search
return "Opening message search...";
},
},
"/connect": {
desc: "Connect your LLM API keys (OpenAI, Anthropic, etc.)",
order: 17,
noArgs: true,
handler: () => {
// Handled specially in App.tsx - opens ProviderSelector
return "Opening provider connection...";
},
},
// "/remote": {
// desc: "Connect to Letta Cloud (device connect mode)",
// args: "[--env-name <name>]",
// order: 17.5,
// handler: () => {
// // Handled specially in App.tsx
// return "Starting listener...";
// },
// },
"/clear": {
desc: "Clear in-context messages",
order: 18,
noArgs: true,
handler: () => {
// Handled specially in App.tsx to reset agent messages
return "Clearing in-context messages...";
},
},
// === Page 2: Agent management (order 20-29) ===
"/new": {
desc: "Start a new conversation (keep agent memory)",
order: 20,
noArgs: true,
handler: () => {
// Handled specially in App.tsx to create new conversation
return "Starting new conversation...";
},
},
"/fork": {
desc: "Fork the current conversation",
order: 20.5,
noArgs: true,
handler: () => {
// Handled specially in App.tsx to fork current conversation
return "Forking conversation...";
},
},
"/btw": {
desc: "Fork conversation and ask a side question (/btw <question>)",
order: 20.6,
handler: () => {
// Handled specially in App.tsx to fork and ask in background
return "Forking conversation...";
},
},
"/pin": {
desc: "Pin current agent globally, or use -l for local only",
order: 22,
handler: () => {
// Handled specially in App.tsx
return "Pinning agent...";
},
},
"/unpin": {
desc: "Unpin current agent globally, or use -l for local only",
order: 23,
handler: () => {
// Handled specially in App.tsx
return "Unpinning agent...";
},
},
"/rename": {
desc: "Rename agent or conversation (/rename agent|convo <name>)",
order: 24,
handler: () => {
// Handled specially in App.tsx to access agent ID and client
return "Renaming...";
},
},
"/description": {
desc: "Update the current agent's description (/description <text>)",
order: 25,
handler: () => {
// Handled specially in App.tsx to access agent ID and client
return "Updating description...";
},
},
"/export": {
desc: "Export AgentFile (.af)",
order: 26,
noArgs: true,
handler: () => {
// Handled specially in App.tsx to access agent ID and client
return "Exporting agent file...";
},
},
"/toolset": {
desc: "Switch toolset (replaces /link and /unlink)",
order: 27,
noArgs: true,
handler: () => {
// Handled specially in App.tsx to access agent ID and client
return "Opening toolset selector...";
},
},
"/ade": {
desc: "Open agent in ADE (browser)",
order: 28,
noArgs: true,
handler: () => {
// Handled specially in App.tsx to access agent ID and open browser
return "Opening ADE...";
},
},
// === Page 3: Advanced features (order 30-39) ===
"/system": {
desc: "Switch system prompt",
order: 30,
noArgs: true,
handler: () => {
// Handled specially in App.tsx to open system prompt selector
return "Opening system prompt selector...";
},
},
"/personality": {
desc: "Switch personality",
order: 30.5,
noArgs: true,
handler: () => {
// Handled specially in App.tsx to open personality selector
return "Opening personality selector...";
},
},
"/subagents": {
desc: "Manage custom subagents",
order: 31,
noArgs: true,
handler: () => {
// Handled specially in App.tsx to open SubagentManager component
return "Opening subagent manager...";
},
},
"/mcp": {
desc: "Manage MCP servers (add, connect with OAuth)",
order: 32,
handler: () => {
// Handled specially in App.tsx to show MCP server selector
return "Opening MCP server manager...";
},
},
"/secret": {
desc: "Manage secrets for shell commands",
order: 33,
args: "<set|list|unset> [key] [value]",
handler: async (args: string[]) => {
const result = await handleSecretCommand(args);
return result.output;
},
},
"/usage": {
desc: "Show session usage statistics and balance",
order: 33,
noArgs: true,
handler: () => {
// Handled specially in App.tsx to display usage stats
return "Fetching usage statistics...";
},
},
"/context": {
desc: "Show context window usage",
order: 33.5,
noArgs: true,
handler: () => {
// Handled specially in App.tsx to display context usage
return "Fetching context usage...";
},
},
"/recompile": {
desc: "Recompile current agent + conversation (warning: this will evict the cache and increase costs)",
order: 33.6,
noArgs: true,
handler: () => {
// Handled specially in App.tsx
return "Recompiling agent and conversation...";
},
},
"/feedback": {
desc: "Send feedback to the Letta team",
order: 34,
handler: () => {
// Handled specially in App.tsx to send feedback request
return "Sending feedback...";
},
},
"/help": {
desc: "Show available commands",
order: 35,
hidden: true, // Redundant with improved autocomplete, but still works if typed
noArgs: true,
handler: () => {
// Handled specially in App.tsx to open help dialog
return "Opening help...";
},
},
"/hooks": {
desc: "Manage hooks configuration",
order: 36,
noArgs: true,
handler: () => {
// Handled specially in App.tsx to open hooks manager
return "Opening hooks manager...";
},
},
"/statusline": {
desc: "Configure status line (help|show|set|clear|test|enable|disable)",
args: "[subcommand]",
order: 36.5,
handler: () => {
// Handled specially in App.tsx
return "Managing status line...";
},
},
"/reasoning-tab": {
desc: "Toggle Tab shortcut for reasoning tiers (/reasoning-tab on|off|status)",
args: "[on|off|status]",
order: 36.6,
handler: () => {
// Handled specially in App.tsx
return "Managing reasoning Tab shortcut...";
},
},
"/terminal": {
desc: "Setup terminal shortcuts [--revert]",
order: 37,
handler: async (args: string[]) => {
if (args.includes("help")) {
return [
"/terminal help",
"",
"Setup terminal keyboard shortcuts.",
"",
"USAGE",
" /terminal — install Shift+Enter keybinding",
" /terminal --revert — remove keybinding",
" /terminal help — show this help",
].join("\n");
}
const {
detectTerminalType,
getKeybindingsPath,
installKeybinding,
removeKeybinding,
} = await import("../utils/terminalKeybindingInstaller");
const { updateSettings } = await import("../../settings");
const isRevert = args.includes("--revert") || args.includes("--remove");
const terminal = detectTerminalType();
if (!terminal) {
return "Not running in a VS Code-like terminal. Shift+Enter keybinding is not needed.";
}
const terminalName = {
vscode: "VS Code",
cursor: "Cursor",
windsurf: "Windsurf",
}[terminal];
const keybindingsPath = getKeybindingsPath(terminal);
if (!keybindingsPath) {
return `Could not determine keybindings.json path for ${terminalName}`;
}
if (isRevert) {
const result = removeKeybinding(keybindingsPath);
if (!result.success) {
return `Failed to remove keybinding: ${result.error}`;
}
await updateSettings({ shiftEnterKeybindingInstalled: false });
return `Removed Shift+Enter keybinding from ${terminalName}`;
}
const result = installKeybinding(keybindingsPath);
if (!result.success) {
return `Failed to install keybinding: ${result.error}`;
}
if (result.alreadyExists) {
return `Shift+Enter keybinding already exists in ${terminalName}`;
}
await updateSettings({ shiftEnterKeybindingInstalled: true });
return `Installed Shift+Enter keybinding for ${terminalName}\nLocation: ${keybindingsPath}`;
},
},
"/install-github-app": {
desc: "Setup Letta Code GitHub Action in this repo",
order: 38,
noArgs: true,
handler: () => {
// Handled specially in App.tsx
return "Opening GitHub App installer...";
},
},
// === Session management (order 40-49) ===
"/plan": {
desc: "Enter plan mode",
order: 40,
noArgs: true,
handler: () => {
// Handled specially in App.tsx
return "Entering plan mode...";
},
},
"/disconnect": {
desc: "Disconnect an existing account (/disconnect codex|claude|zai)",
order: 41,
handler: () => {
// Handled specially in App.tsx
return "Disconnecting...";
},
},
"/bg": {
desc: "Show background shell processes",
order: 42,
noArgs: true,
handler: () => {
// Handled specially in App.tsx to show background processes
return "Showing background processes...";
},
},
"/exit": {
desc: "Exit this session",
order: 43,
noArgs: true,
handler: () => {
// Handled specially in App.tsx
return "Exiting...";
},
},
"/logout": {
desc: "Clear credentials and exit",
order: 44,
noArgs: true,
handler: () => {
// Handled specially in App.tsx to access settings manager
return "Clearing credentials...";
},
},
// === Ralph Wiggum mode (order 45-46) ===
"/ralph": {
desc: 'Start Ralph Wiggum loop (/ralph [prompt] [--completion-promise "X"] [--max-iterations N])',
order: 45,
handler: () => {
// Handled specially in App.tsx
return "Activating ralph mode...";
},
},
"/yolo-ralph": {
desc: "Start Ralph loop with bypass permissions (yolo + ralph)",
order: 46,
handler: () => {
// Handled specially in App.tsx
return "Activating yolo-ralph mode...";
},
},
// === Hidden commands (not shown in autocomplete) ===
"/stream": {
desc: "Toggle token streaming on/off",
hidden: true,
noArgs: true,
handler: () => {
// Handled specially in App.tsx for live toggling
return "Toggling token streaming...";
},
},
"/compact": {
desc: "Summarize conversation history (compaction) with optional mode",
args: "[all|sliding_window|self_compact_all|self_compact_sliding_window]",
handler: () => {
// Handled specially in App.tsx to access client and agent ID
return "Compacting conversation...";
},
},
"/link": {
desc: "Attach all Letta Code tools to agent (deprecated, use /toolset instead)",
hidden: true,
noArgs: true,
handler: () => {
// Handled specially in App.tsx to access agent ID and client
return "Linking tools...";
},
},
"/unlink": {
desc: "Remove all Letta Code tools from agent (deprecated, use /toolset instead)",
hidden: true,
noArgs: true,
handler: () => {
// Handled specially in App.tsx to access agent ID and client
return "Unlinking tools...";
},
},
"/resume": {
desc: "Resume a previous conversation",
args: "[conversation_id]",
order: 19,
handler: () => {
// Handled specially in App.tsx to show conversation selector or switch directly
return "Opening conversation selector...";
},
},
"/pinned": {
desc: "Browse pinned agents",
hidden: true, // Alias for /agents (opens to Pinned tab)
noArgs: true,
handler: () => {
return "Opening agent browser...";
},
},
"/profiles": {
desc: "Browse pinned agents",
hidden: true, // Alias for /agents (opens to Pinned tab)
noArgs: true,
handler: () => {
return "Opening agent browser...";
},
},
"/download": {
desc: "Export AgentFile (.af)",
hidden: true, // Legacy alias for /export
noArgs: true,
handler: () => {
return "Exporting agent file...";
},
},
};
/**
* Execute a command and return the result
*/
export async function executeCommand(
input: string,
): Promise<{ success: boolean; output: string; notFound?: boolean }> {
const [command, ...args] = input.trim().split(/\s+/);
if (!command) {
return {
success: false,
output: "No command found",
};
}
const handler = commands[command];
if (!handler) {
return {
success: false,
output: `Unknown command: ${command}`,
notFound: true,
};
}
if (handler.noArgs && args.length > 0) {
return {
success: false,
output: `${command} does not accept arguments.`,
};
}
try {
const result = await handler.handler(args);
if (typeof result === "string") {
return { success: true, output: result };
}
return result;
} catch (error) {
return {
success: false,
output: `Error executing ${command}: ${error instanceof Error ? error.message : String(error)}`,
};
}
}