@letta-ai/letta-code 0.11.1 → 0.11.2-next.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letta-ai/letta-code",
3
- "version": "0.11.1",
3
+ "version": "0.11.2-next.2",
4
4
  "description": "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -30,7 +30,7 @@
30
30
  "access": "public"
31
31
  },
32
32
  "dependencies": {
33
- "@letta-ai/letta-client": "1.6.1",
33
+ "@letta-ai/letta-client": "1.6.4",
34
34
  "glob": "^13.0.0",
35
35
  "ink-link": "^5.0.0",
36
36
  "open": "^10.2.0"
@@ -0,0 +1,115 @@
1
+ ---
2
+ name: finding-agents
3
+ description: Find other agents on the same server. Use when the user asks about other agents, wants to migrate memory from another agent, or needs to find an agent by name or tags.
4
+ ---
5
+
6
+ # Finding Agents
7
+
8
+ This skill helps you find other agents on the same Letta server.
9
+
10
+ ## When to Use This Skill
11
+
12
+ - User asks about other agents they have
13
+ - User wants to find a specific agent by name
14
+ - User wants to list agents with certain tags
15
+ - You need to find an agent ID for memory migration
16
+ - You found an agent_id via message search and need details about that agent
17
+
18
+ ## Script Usage
19
+
20
+ ```bash
21
+ npx ts-node scripts/find-agents.ts [options]
22
+ ```
23
+
24
+ ### Options
25
+
26
+ | Option | Description |
27
+ |--------|-------------|
28
+ | `--name <name>` | Exact name match |
29
+ | `--query <text>` | Fuzzy search by name |
30
+ | `--tags <tag1,tag2>` | Filter by tags (comma-separated) |
31
+ | `--match-all-tags` | Require ALL tags (default: ANY) |
32
+ | `--include-blocks` | Include agent.blocks in response |
33
+ | `--limit <n>` | Max results (default: 20) |
34
+
35
+ ## Common Patterns
36
+
37
+ ### Finding Letta Code Agents
38
+
39
+ Agents created by Letta Code are tagged with `origin:letta-code`. To find only Letta Code agents:
40
+
41
+ ```bash
42
+ npx ts-node scripts/find-agents.ts --tags "origin:letta-code"
43
+ ```
44
+
45
+ This is useful when the user is looking for agents they've worked with in Letta Code CLI sessions.
46
+
47
+ ### Finding All Agents
48
+
49
+ If the user has agents created outside Letta Code (via ADE, SDK, etc.), search without the tag filter:
50
+
51
+ ```bash
52
+ npx ts-node scripts/find-agents.ts
53
+ ```
54
+
55
+ ## Examples
56
+
57
+ **List all agents (up to 20):**
58
+ ```bash
59
+ npx ts-node scripts/find-agents.ts
60
+ ```
61
+
62
+ **Find agent by exact name:**
63
+ ```bash
64
+ npx ts-node scripts/find-agents.ts --name "ProjectX-v1"
65
+ ```
66
+
67
+ **Search agents by name (fuzzy):**
68
+ ```bash
69
+ npx ts-node scripts/find-agents.ts --query "project"
70
+ ```
71
+
72
+ **Find only Letta Code agents:**
73
+ ```bash
74
+ npx ts-node scripts/find-agents.ts --tags "origin:letta-code"
75
+ ```
76
+
77
+ **Find agents with multiple tags:**
78
+ ```bash
79
+ npx ts-node scripts/find-agents.ts --tags "frontend,production" --match-all-tags
80
+ ```
81
+
82
+ **Include memory blocks in results:**
83
+ ```bash
84
+ npx ts-node scripts/find-agents.ts --query "project" --include-blocks
85
+ ```
86
+
87
+ ## Output
88
+
89
+ Returns the raw API response with full agent details. Key fields:
90
+ - `id` - Agent ID (e.g., `agent-abc123`)
91
+ - `name` - Agent name
92
+ - `description` - Agent description
93
+ - `tags` - Agent tags
94
+ - `blocks` - Memory blocks (if `--include-blocks` used)
95
+
96
+ ## Related Skills
97
+
98
+ - **migrating-memory** - Once you find an agent, use this skill to copy/share memory blocks
99
+ - **searching-messages** - Search messages across all agents to find which agent discussed a topic. Use `--all-agents` to get `agent_id` values, then use this skill to get full agent details.
100
+
101
+ ### Finding Agents by Topic
102
+
103
+ If you need to find which agent worked on a specific topic:
104
+
105
+ 1. Load both skills: `searching-messages` and `finding-agents`
106
+ 2. Search messages across all agents:
107
+ ```bash
108
+ search-messages.ts --query "topic" --all-agents --limit 10
109
+ ```
110
+ 3. Note the `agent_id` values from matching messages
111
+ 4. Get agent details:
112
+ ```bash
113
+ find-agents.ts --query "partial-name"
114
+ ```
115
+ Or use the agent_id directly in the Letta API
@@ -0,0 +1,177 @@
1
+ #!/usr/bin/env npx tsx
2
+ /**
3
+ * Find Agents - Search for agents with various filters
4
+ *
5
+ * This script is standalone and can be run outside the CLI process.
6
+ * It reads auth from LETTA_API_KEY env var or ~/.letta/settings.json.
7
+ *
8
+ * Usage:
9
+ * npx tsx find-agents.ts [options]
10
+ *
11
+ * Options:
12
+ * --name <name> Exact name match
13
+ * --query <text> Fuzzy search by name
14
+ * --tags <tag1,tag2> Filter by tags (comma-separated)
15
+ * --match-all-tags Require ALL tags (default: ANY)
16
+ * --include-blocks Include agent.blocks in response
17
+ * --limit <n> Max results (default: 20)
18
+ *
19
+ * Output:
20
+ * Raw API response from GET /v1/agents
21
+ */
22
+
23
+ import { readFileSync } from "node:fs";
24
+ import { createRequire } from "node:module";
25
+ import { homedir } from "node:os";
26
+ import { join } from "node:path";
27
+
28
+ // Use createRequire for @letta-ai/letta-client so NODE_PATH is respected
29
+ // (ES module imports don't respect NODE_PATH, but require does)
30
+ const require = createRequire(import.meta.url);
31
+ const Letta = require("@letta-ai/letta-client")
32
+ .default as typeof import("@letta-ai/letta-client").default;
33
+ type LettaClient = InstanceType<typeof Letta>;
34
+
35
+ interface FindAgentsOptions {
36
+ name?: string;
37
+ query?: string;
38
+ tags?: string[];
39
+ matchAllTags?: boolean;
40
+ includeBlocks?: boolean;
41
+ limit?: number;
42
+ }
43
+
44
+ /**
45
+ * Get API key from env var or settings file
46
+ */
47
+ function getApiKey(): string {
48
+ // First check env var (set by CLI's getShellEnv)
49
+ if (process.env.LETTA_API_KEY) {
50
+ return process.env.LETTA_API_KEY;
51
+ }
52
+
53
+ // Fall back to settings file
54
+ const settingsPath = join(homedir(), ".letta", "settings.json");
55
+ try {
56
+ const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
57
+ if (settings.env?.LETTA_API_KEY) {
58
+ return settings.env.LETTA_API_KEY;
59
+ }
60
+ } catch {
61
+ // Settings file doesn't exist or is invalid
62
+ }
63
+
64
+ throw new Error(
65
+ "No LETTA_API_KEY found. Set the env var or run the Letta CLI to authenticate.",
66
+ );
67
+ }
68
+
69
+ /**
70
+ * Create a Letta client with auth from env/settings
71
+ */
72
+ function createClient(): LettaClient {
73
+ return new Letta({ apiKey: getApiKey() });
74
+ }
75
+
76
+ /**
77
+ * Find agents matching the given criteria
78
+ * @param client - Letta client instance
79
+ * @param options - Search options
80
+ * @returns Array of agent objects from the API
81
+ */
82
+ export async function findAgents(
83
+ client: LettaClient,
84
+ options: FindAgentsOptions = {},
85
+ ): Promise<Awaited<ReturnType<typeof client.agents.list>>> {
86
+ const params: Parameters<typeof client.agents.list>[0] = {
87
+ limit: options.limit ?? 20,
88
+ };
89
+
90
+ if (options.name) {
91
+ params.name = options.name;
92
+ }
93
+
94
+ if (options.query) {
95
+ params.query_text = options.query;
96
+ }
97
+
98
+ if (options.tags && options.tags.length > 0) {
99
+ params.tags = options.tags;
100
+ if (options.matchAllTags) {
101
+ params.match_all_tags = true;
102
+ }
103
+ }
104
+
105
+ if (options.includeBlocks) {
106
+ params.include = ["agent.blocks"];
107
+ }
108
+
109
+ return await client.agents.list(params);
110
+ }
111
+
112
+ function parseArgs(args: string[]): FindAgentsOptions {
113
+ const options: FindAgentsOptions = {};
114
+
115
+ const nameIndex = args.indexOf("--name");
116
+ if (nameIndex !== -1 && nameIndex + 1 < args.length) {
117
+ options.name = args[nameIndex + 1];
118
+ }
119
+
120
+ const queryIndex = args.indexOf("--query");
121
+ if (queryIndex !== -1 && queryIndex + 1 < args.length) {
122
+ options.query = args[queryIndex + 1];
123
+ }
124
+
125
+ const tagsIndex = args.indexOf("--tags");
126
+ if (tagsIndex !== -1 && tagsIndex + 1 < args.length) {
127
+ options.tags = args[tagsIndex + 1]?.split(",").map((t) => t.trim());
128
+ }
129
+
130
+ if (args.includes("--match-all-tags")) {
131
+ options.matchAllTags = true;
132
+ }
133
+
134
+ if (args.includes("--include-blocks")) {
135
+ options.includeBlocks = true;
136
+ }
137
+
138
+ const limitIndex = args.indexOf("--limit");
139
+ if (limitIndex !== -1 && limitIndex + 1 < args.length) {
140
+ const limit = Number.parseInt(args[limitIndex + 1] as string, 10);
141
+ if (!Number.isNaN(limit)) {
142
+ options.limit = limit;
143
+ }
144
+ }
145
+
146
+ return options;
147
+ }
148
+
149
+ // CLI entry point - check if this file is being run directly
150
+ const isMainModule = import.meta.url === `file://${process.argv[1]}`;
151
+ if (isMainModule) {
152
+ (async () => {
153
+ try {
154
+ const options = parseArgs(process.argv.slice(2));
155
+ const client = createClient();
156
+ const result = await findAgents(client, options);
157
+ console.log(JSON.stringify(result, null, 2));
158
+ } catch (error) {
159
+ console.error(
160
+ "Error:",
161
+ error instanceof Error ? error.message : String(error),
162
+ );
163
+ console.error(`
164
+ Usage: npx tsx find-agents.ts [options]
165
+
166
+ Options:
167
+ --name <name> Exact name match
168
+ --query <text> Fuzzy search by name
169
+ --tags <tag1,tag2> Filter by tags (comma-separated)
170
+ --match-all-tags Require ALL tags (default: ANY)
171
+ --include-blocks Include agent.blocks in response
172
+ --limit <n> Max results (default: 20)
173
+ `);
174
+ process.exit(1);
175
+ }
176
+ })();
177
+ }
@@ -18,6 +18,18 @@ This command may be run in different scenarios:
18
18
 
19
19
  Before making changes, use the `memory` tool to inspect your current memory blocks and understand what already exists.
20
20
 
21
+ ## Memory Migration Option
22
+
23
+ If you're setting up a new agent that should inherit memory from an existing agent, consider using the `migrating-memory` skill:
24
+
25
+ 1. Load the skill: `Skill({ command: "load", skills: ["migrating-memory"] })`
26
+ 2. Follow its workflow to copy or share blocks from another agent
27
+
28
+ **When to suggest migration**:
29
+ - User mentions they have an existing agent with useful memory
30
+ - User is replacing an old agent with a new one
31
+ - User wants to share memory blocks across multiple agents
32
+
21
33
  ## What Coding Agents Should Remember
22
34
 
23
35
  ### 1. Procedures (Rules & Workflows)
@@ -0,0 +1,132 @@
1
+ ---
2
+ name: migrating-memory
3
+ description: Migrate memory blocks from an existing agent to the current agent. Use when the user wants to copy or share memory from another agent, or during /init when setting up a new agent that should inherit memory from an existing one.
4
+ ---
5
+
6
+ # Migrating Memory
7
+
8
+ This skill helps migrate memory blocks from an existing agent to a new agent, similar to macOS Migration Assistant for AI agents.
9
+
10
+ ## When to Use This Skill
11
+
12
+ - User is setting up a new agent that should inherit memory from an existing one
13
+ - User wants to share memory blocks across multiple agents
14
+ - User is replacing an old agent with a new one
15
+ - User mentions they have an existing agent with useful memory
16
+
17
+ ## Migration Methods
18
+
19
+ ### 1. Manual Copy (Recommended for partial content)
20
+
21
+ If you only need **part** of a source block, or the source is messy and needs cleanup:
22
+ 1. Use `get-agent-blocks.ts` to view the source block's content
23
+ 2. Use the `memory` tool to create a new block with just the content you want
24
+ 3. No scripts needed - you have full control over what gets copied
25
+
26
+ Best for: Extracting sections, cleaning up messy content, selective migration.
27
+
28
+ ### 2. Script Copy (Full block duplication)
29
+
30
+ Creates new blocks with the same content using `copy-block.ts`. After copying:
31
+ - You own the copy - changes don't sync
32
+ - Use `--label` flag if you already have a block with that label
33
+ - Best for: One-time migration, forking an agent
34
+
35
+ ### 3. Share (Linked Blocks)
36
+
37
+ Attaches the same block to multiple agents using `attach-block.ts`. After sharing:
38
+ - All agents see the same block content
39
+ - Changes by any agent are visible to all others
40
+ - Can be read-only (target can read but not modify)
41
+ - Best for: Shared knowledge bases, synchronized state
42
+
43
+ **Note:** You cannot have two blocks with the same label. When copying, use `--label` to rename if needed.
44
+
45
+ ## Workflow
46
+
47
+ ### Step 1: Identify Source Agent
48
+
49
+ Ask the user for the source agent's ID (e.g., `agent-abc123`).
50
+
51
+ If they don't know the ID, load the **finding-agents** skill to search:
52
+ ```
53
+ Skill({ command: "load", skills: ["finding-agents"] })
54
+ ```
55
+
56
+ Example: "What's the ID of the agent you want to migrate memory from?"
57
+
58
+ ### Step 2: View Source Agent's Blocks
59
+
60
+ Inspect what memory blocks the source agent has:
61
+
62
+ ```bash
63
+ npx ts-node scripts/get-agent-blocks.ts --agent-id <source-agent-id>
64
+ ```
65
+
66
+ This shows each block's ID, label, description, and value.
67
+
68
+ ### Step 3: Migrate Blocks
69
+
70
+ For each block you want to migrate, choose copy or share:
71
+
72
+ **To Copy (create independent block):**
73
+ ```bash
74
+ npx ts-node scripts/copy-block.ts --block-id <block-id> [--label <new-label>]
75
+ ```
76
+
77
+ Use `--label` if you already have a block with that label (e.g., `--label project-imported`).
78
+
79
+ **To Share (attach existing block):**
80
+ ```bash
81
+ npx ts-node scripts/attach-block.ts --block-id <block-id>
82
+ ```
83
+
84
+ Add `--read-only` flag to share to make this agent unable to modify the block.
85
+
86
+ Note: These scripts automatically target the current agent (you) for safety.
87
+
88
+ ## Script Reference
89
+
90
+ All scripts are located in the `scripts/` directory and output raw API responses (JSON).
91
+
92
+ | Script | Purpose | Args |
93
+ |--------|---------|------|
94
+ | `get-agent-blocks.ts` | Get blocks from an agent | `--agent-id` |
95
+ | `copy-block.ts` | Copy block to current agent | `--block-id`, optional `--label` |
96
+ | `attach-block.ts` | Attach existing block to current agent | `--block-id`, optional `--read-only` |
97
+
98
+ ## Authentication
99
+
100
+ The bundled scripts automatically use the same authentication as Letta Code:
101
+ - Keychain/secrets storage
102
+ - `~/.config/letta/settings.json` fallback
103
+ - `LETTA_API_KEY` environment variable
104
+
105
+ You can also make direct API calls using the Letta SDK if you have the API key available.
106
+
107
+ ## Example: Migrating Project Memory
108
+
109
+ Scenario: You're a new agent and want to inherit memory from an existing agent "ProjectX-v1".
110
+
111
+ 1. **Get source agent ID from user:**
112
+ User provides: `agent-abc123`
113
+
114
+ 2. **List its blocks:**
115
+ ```bash
116
+ npx ts-node scripts/get-agent-blocks.ts --agent-id agent-abc123
117
+ # Shows: project (block-def456), human (block-ghi789), persona (block-jkl012)
118
+ ```
119
+
120
+ 3. **Copy project knowledge to yourself:**
121
+ ```bash
122
+ # If you don't have a 'project' block yet:
123
+ npx ts-node scripts/copy-block.ts --block-id block-def456
124
+
125
+ # If you already have 'project', use --label to rename:
126
+ npx ts-node scripts/copy-block.ts --block-id block-def456 --label project-v1
127
+ ```
128
+
129
+ 4. **Optionally share human preferences (read-only):**
130
+ ```bash
131
+ npx ts-node scripts/attach-block.ts --block-id block-ghi789 --read-only
132
+ ```
@@ -0,0 +1,161 @@
1
+ #!/usr/bin/env npx tsx
2
+ /**
3
+ * Attach Block - Attaches an existing memory block to an agent (sharing)
4
+ *
5
+ * This script is standalone and can be run outside the CLI process.
6
+ * It reads auth from LETTA_API_KEY env var or ~/.letta/settings.json.
7
+ * It reads agent ID from LETTA_AGENT_ID env var or --agent-id arg.
8
+ *
9
+ * Usage:
10
+ * npx tsx attach-block.ts --block-id <block-id> [--agent-id <agent-id>] [--read-only]
11
+ *
12
+ * This attaches an existing block to another agent, making it shared.
13
+ * Changes to the block will be visible to all agents that have it attached.
14
+ *
15
+ * Options:
16
+ * --agent-id Target agent ID (overrides LETTA_AGENT_ID env var)
17
+ * --read-only Target agent can read but not modify the block
18
+ *
19
+ * Output:
20
+ * Raw API response from the attach operation
21
+ */
22
+
23
+ import { readFileSync } from "node:fs";
24
+ import { createRequire } from "node:module";
25
+ import { homedir } from "node:os";
26
+ import { join } from "node:path";
27
+
28
+ // Use createRequire for @letta-ai/letta-client so NODE_PATH is respected
29
+ // (ES module imports don't respect NODE_PATH, but require does)
30
+ const require = createRequire(import.meta.url);
31
+ const Letta = require("@letta-ai/letta-client")
32
+ .default as typeof import("@letta-ai/letta-client").default;
33
+ type LettaClient = InstanceType<typeof Letta>;
34
+
35
+ /**
36
+ * Get API key from env var or settings file
37
+ */
38
+ function getApiKey(): string {
39
+ if (process.env.LETTA_API_KEY) {
40
+ return process.env.LETTA_API_KEY;
41
+ }
42
+
43
+ const settingsPath = join(homedir(), ".letta", "settings.json");
44
+ try {
45
+ const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
46
+ if (settings.env?.LETTA_API_KEY) {
47
+ return settings.env.LETTA_API_KEY;
48
+ }
49
+ } catch {
50
+ // Settings file doesn't exist or is invalid
51
+ }
52
+
53
+ throw new Error(
54
+ "No LETTA_API_KEY found. Set the env var or run the Letta CLI to authenticate.",
55
+ );
56
+ }
57
+
58
+ /**
59
+ * Get agent ID from CLI arg, env var, or throw
60
+ */
61
+ function getAgentId(cliArg?: string): string {
62
+ if (cliArg) return cliArg;
63
+ if (process.env.LETTA_AGENT_ID) {
64
+ return process.env.LETTA_AGENT_ID;
65
+ }
66
+ throw new Error(
67
+ "No agent ID provided. Use --agent-id or ensure LETTA_AGENT_ID env var is set.",
68
+ );
69
+ }
70
+
71
+ /**
72
+ * Create a Letta client with auth from env/settings
73
+ */
74
+ function createClient(): LettaClient {
75
+ return new Letta({ apiKey: getApiKey() });
76
+ }
77
+
78
+ /**
79
+ * Attach an existing block to the current agent (sharing it)
80
+ * @param client - Letta client instance
81
+ * @param blockId - The block ID to attach
82
+ * @param readOnly - Whether this agent should have read-only access
83
+ * @param targetAgentId - Optional target agent ID (defaults to current agent)
84
+ * @returns API response from the attach operation
85
+ */
86
+ export async function attachBlock(
87
+ client: LettaClient,
88
+ blockId: string,
89
+ readOnly = false,
90
+ targetAgentId?: string,
91
+ ): Promise<Awaited<ReturnType<typeof client.agents.blocks.attach>>> {
92
+ // Get current agent ID (the agent calling this script) or use provided ID
93
+ const currentAgentId = getAgentId(targetAgentId);
94
+
95
+ const result = await client.agents.blocks.attach(blockId, {
96
+ agent_id: currentAgentId,
97
+ });
98
+
99
+ // If read-only is requested, update the block's read_only flag for this agent
100
+ // Note: This may require a separate API call depending on how read_only works
101
+ if (readOnly) {
102
+ // The read_only flag is per-block, not per-agent attachment
103
+ // For now, we'll note this in the output
104
+ console.warn(
105
+ "Note: read_only flag is set on the block itself, not per-agent. " +
106
+ "Use the block update API to set read_only if needed.",
107
+ );
108
+ }
109
+
110
+ return result;
111
+ }
112
+
113
+ function parseArgs(args: string[]): {
114
+ blockId: string;
115
+ readOnly: boolean;
116
+ agentId?: string;
117
+ } {
118
+ const blockIdIndex = args.indexOf("--block-id");
119
+ const agentIdIndex = args.indexOf("--agent-id");
120
+ const readOnly = args.includes("--read-only");
121
+
122
+ if (blockIdIndex === -1 || blockIdIndex + 1 >= args.length) {
123
+ throw new Error("Missing required argument: --block-id <block-id>");
124
+ }
125
+
126
+ return {
127
+ blockId: args[blockIdIndex + 1] as string,
128
+ readOnly,
129
+ agentId:
130
+ agentIdIndex !== -1 && agentIdIndex + 1 < args.length
131
+ ? (args[agentIdIndex + 1] as string)
132
+ : undefined,
133
+ };
134
+ }
135
+
136
+ // CLI entry point - check if this file is being run directly
137
+ const isMainModule = import.meta.url === `file://${process.argv[1]}`;
138
+ if (isMainModule) {
139
+ (async () => {
140
+ try {
141
+ const { blockId, readOnly, agentId } = parseArgs(process.argv.slice(2));
142
+ const client = createClient();
143
+ const result = await attachBlock(client, blockId, readOnly, agentId);
144
+ console.log(JSON.stringify(result, null, 2));
145
+ } catch (error) {
146
+ console.error(
147
+ "Error:",
148
+ error instanceof Error ? error.message : String(error),
149
+ );
150
+ if (
151
+ error instanceof Error &&
152
+ error.message.includes("Missing required argument")
153
+ ) {
154
+ console.error(
155
+ "\nUsage: npx tsx attach-block.ts --block-id <block-id> [--agent-id <agent-id>] [--read-only]",
156
+ );
157
+ }
158
+ process.exit(1);
159
+ }
160
+ })();
161
+ }