@letta-ai/letta-code 0.19.6 → 0.19.7

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/letta.js CHANGED
@@ -3240,7 +3240,7 @@ var package_default;
3240
3240
  var init_package = __esm(() => {
3241
3241
  package_default = {
3242
3242
  name: "@letta-ai/letta-code",
3243
- version: "0.19.6",
3243
+ version: "0.19.7",
3244
3244
  description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
3245
3245
  type: "module",
3246
3246
  bin: {
@@ -45001,11 +45001,17 @@ var execFile6, GIT_MEMORY_ENABLED_TAG = "git-memory-enabled", PRE_COMMIT_HOOK_SC
45001
45001
  # Validate frontmatter in staged memory .md files
45002
45002
  # Installed by Letta Code CLI
45003
45003
 
45004
- AGENT_EDITABLE_KEYS="description limit"
45004
+ AGENT_EDITABLE_KEYS="description"
45005
45005
  PROTECTED_KEYS="read_only"
45006
- ALL_KNOWN_KEYS="description limit read_only"
45006
+ ALL_KNOWN_KEYS="description read_only limit"
45007
45007
  errors=""
45008
45008
 
45009
+ # Skills must always be directories: skills/<name>/SKILL.md
45010
+ # Reject legacy flat skill files (both current and legacy repo layouts).
45011
+ for file in $(git diff --cached --name-only --diff-filter=ACMR | grep -E '^(memory/)?skills/[^/]+\\.md$' || true); do
45012
+ errors="$errors\\n $file: invalid skill path (skills must be folders). Use skills/<name>/SKILL.md"
45013
+ done
45014
+
45009
45015
  # Helper: extract a frontmatter value from content
45010
45016
  get_fm_value() {
45011
45017
  local content="$1" key="$2"
@@ -45015,7 +45021,9 @@ get_fm_value() {
45015
45021
  echo "$content" | tail -n +2 | head -n $((closing_line - 1)) | grep "^$key:" | cut -d: -f2- | sed 's/^ *//;s/ *$//'
45016
45022
  }
45017
45023
 
45018
- for file in $(git diff --cached --name-only --diff-filter=ACM | grep '^memory/.*\\.md$'); do
45024
+ # Match .md files under system/ or reference/ (with optional memory/ prefix).
45025
+ # Skip skill SKILL.md files — they use a different frontmatter format.
45026
+ for file in $(git diff --cached --name-only --diff-filter=ACM | grep -E '^(memory/)?(system|reference)/.*\\.md$'); do
45019
45027
  staged=$(git show ":$file")
45020
45028
 
45021
45029
  # Frontmatter is required
@@ -45047,7 +45055,6 @@ for file in $(git diff --cached --name-only --diff-filter=ACM | grep '^memory/.*
45047
45055
 
45048
45056
  # Track required fields
45049
45057
  has_description=false
45050
- has_limit=false
45051
45058
 
45052
45059
  # Validate each line
45053
45060
  while IFS= read -r line; do
@@ -45088,10 +45095,7 @@ for file in $(git diff --cached --name-only --diff-filter=ACM | grep '^memory/.*
45088
45095
  # Validate value types
45089
45096
  case "$key" in
45090
45097
  limit)
45091
- has_limit=true
45092
- if ! echo "$value" | grep -qE '^[0-9]+$' || [ "$value" = "0" ]; then
45093
- errors="$errors\\n $file: 'limit' must be a positive integer, got '$value'"
45094
- fi
45098
+ # Legacy field accepted for backward compatibility.
45095
45099
  ;;
45096
45100
  description)
45097
45101
  has_description=true
@@ -45106,9 +45110,6 @@ for file in $(git diff --cached --name-only --diff-filter=ACM | grep '^memory/.*
45106
45110
  if [ "$has_description" = "false" ]; then
45107
45111
  errors="$errors\\n $file: missing required field 'description'"
45108
45112
  fi
45109
- if [ "$has_limit" = "false" ]; then
45110
- errors="$errors\\n $file: missing required field 'limit'"
45111
- fi
45112
45113
 
45113
45114
  # Check if protected keys were removed (existed in HEAD but not in staged)
45114
45115
  if [ -n "$head_content" ]; then
@@ -47642,8 +47643,7 @@ async function memory(args) {
47642
47643
  }
47643
47644
  const body = args.file_text ?? "";
47644
47645
  const rendered = renderMemoryFile({
47645
- description,
47646
- limit: DEFAULT_LIMIT3
47646
+ description
47647
47647
  }, body);
47648
47648
  await mkdir2(dirname5(filePath), { recursive: true });
47649
47649
  await writeFile2(filePath, rendered, "utf8");
@@ -47862,7 +47862,6 @@ function parseMemoryFile(content) {
47862
47862
  const frontmatterText = match[1] ?? "";
47863
47863
  const body = match[2] ?? "";
47864
47864
  let description;
47865
- let limit2;
47866
47865
  let readOnly;
47867
47866
  for (const line of frontmatterText.split(/\r?\n/)) {
47868
47867
  const idx = line.indexOf(":");
@@ -47872,11 +47871,6 @@ function parseMemoryFile(content) {
47872
47871
  const value = line.slice(idx + 1).trim();
47873
47872
  if (key === "description") {
47874
47873
  description = value;
47875
- } else if (key === "limit") {
47876
- const parsedLimit = Number.parseInt(value, 10);
47877
- if (!Number.isNaN(parsedLimit)) {
47878
- limit2 = parsedLimit;
47879
- }
47880
47874
  } else if (key === "read_only") {
47881
47875
  readOnly = value;
47882
47876
  }
@@ -47884,13 +47878,9 @@ function parseMemoryFile(content) {
47884
47878
  if (!description || !description.trim()) {
47885
47879
  throw new Error("memory: target file frontmatter is missing 'description'");
47886
47880
  }
47887
- if (!limit2 || !Number.isInteger(limit2) || limit2 <= 0) {
47888
- throw new Error("memory: target file frontmatter is missing a valid positive 'limit'");
47889
- }
47890
47881
  return {
47891
47882
  frontmatter: {
47892
47883
  description,
47893
- limit: limit2,
47894
47884
  ...readOnly !== undefined ? { read_only: readOnly } : {}
47895
47885
  },
47896
47886
  body
@@ -47901,13 +47891,9 @@ function renderMemoryFile(frontmatter, body) {
47901
47891
  if (!description) {
47902
47892
  throw new Error("memory: 'description' must not be empty");
47903
47893
  }
47904
- if (!Number.isInteger(frontmatter.limit) || frontmatter.limit <= 0) {
47905
- throw new Error("memory: 'limit' must be a positive integer");
47906
- }
47907
47894
  const lines = [
47908
47895
  "---",
47909
- `description: ${sanitizeFrontmatterValue(description)}`,
47910
- `limit: ${frontmatter.limit}`
47896
+ `description: ${sanitizeFrontmatterValue(description)}`
47911
47897
  ];
47912
47898
  if (frontmatter.read_only !== undefined) {
47913
47899
  lines.push(`read_only: ${frontmatter.read_only}`);
@@ -47989,7 +47975,7 @@ function requireString(value, field, command) {
47989
47975
  }
47990
47976
  return value;
47991
47977
  }
47992
- var execFile9, DEFAULT_LIMIT3 = 2000;
47978
+ var execFile9;
47993
47979
  var init_Memory2 = __esm(async () => {
47994
47980
  init_context();
47995
47981
  await init_client2();
@@ -72751,7 +72737,8 @@ async function sendMessageStream(conversationId, messages, opts = { streamTokens
72751
72737
  await waitForToolsetReady();
72752
72738
  const { clientTools, contextId } = captureToolExecutionContext(opts.workingDirectory, opts.permissionModeState);
72753
72739
  const { clientSkills, errors: clientSkillDiscoveryErrors } = await buildClientSkillsPayload({
72754
- agentId: opts.agentId
72740
+ agentId: opts.agentId,
72741
+ skillSources: ALL_SKILL_SOURCES
72755
72742
  });
72756
72743
  const resolvedConversationId = conversationId;
72757
72744
  const requestBody = buildConversationMessagesCreateRequestBody(conversationId, messages, opts, clientTools, clientSkills);
@@ -72815,6 +72802,7 @@ var init_message = __esm(async () => {
72815
72802
  init_timing();
72816
72803
  init_approval_result_normalization();
72817
72804
  init_clientSkills();
72805
+ init_skillSources();
72818
72806
  await __promiseAll([
72819
72807
  init_manager3(),
72820
72808
  init_client2()
@@ -77749,7 +77737,7 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
77749
77737
  return;
77750
77738
  }
77751
77739
  if (parsed.type === "terminal_spawn") {
77752
- handleTerminalSpawn(parsed, socket, runtime.bootWorkingDirectory);
77740
+ handleTerminalSpawn(parsed, socket, parsed.cwd ?? runtime.bootWorkingDirectory);
77753
77741
  return;
77754
77742
  }
77755
77743
  if (parsed.type === "terminal_input") {
@@ -123696,22 +123684,75 @@ function buildParentMemoryTree(files) {
123696
123684
  }
123697
123685
  return nameA.localeCompare(nameB);
123698
123686
  });
123699
- const lines = ["/memory/"];
123687
+ const limits = getDirectoryLimits();
123688
+ const maxLines = Math.max(2, limits.memfsTreeMaxLines);
123689
+ const maxChars = Math.max(128, limits.memfsTreeMaxChars);
123690
+ const maxChildrenPerDir = Math.max(1, limits.memfsTreeMaxChildrenPerDir);
123691
+ const rootLine = "/memory/";
123692
+ const lines = [rootLine];
123693
+ let totalChars = rootLine.length;
123694
+ const countTreeEntries = (node) => {
123695
+ let total = 0;
123696
+ for (const [, child] of node.children) {
123697
+ total += 1;
123698
+ if (child.children.size > 0) {
123699
+ total += countTreeEntries(child);
123700
+ }
123701
+ }
123702
+ return total;
123703
+ };
123704
+ const canAppendLine = (line) => {
123705
+ const nextLineCount = lines.length + 1;
123706
+ const nextCharCount = totalChars + 1 + line.length;
123707
+ return nextLineCount <= maxLines && nextCharCount <= maxChars;
123708
+ };
123700
123709
  const render2 = (node, prefix) => {
123701
123710
  const entries = sortedEntries(node);
123702
- for (const [index, [name, child]] of entries.entries()) {
123703
- const isLast = index === entries.length - 1;
123711
+ const visibleEntries = entries.slice(0, maxChildrenPerDir);
123712
+ const omittedEntries = Math.max(0, entries.length - visibleEntries.length);
123713
+ const renderItems = visibleEntries.map(([name, child]) => ({
123714
+ kind: "entry",
123715
+ name,
123716
+ child
123717
+ }));
123718
+ if (omittedEntries > 0) {
123719
+ renderItems.push({ kind: "omitted", omittedCount: omittedEntries });
123720
+ }
123721
+ for (const [index, item] of renderItems.entries()) {
123722
+ const isLast = index === renderItems.length - 1;
123704
123723
  const branch = isLast ? "└──" : "├──";
123705
- const suffix = child.isFile ? "" : "/";
123706
- const description = child.description ? ` (${child.description})` : "";
123707
- lines.push(`${prefix}${branch} ${name}${suffix}${description}`);
123708
- if (child.children.size > 0) {
123724
+ const line = item.kind === "entry" ? `${prefix}${branch} ${item.name}${item.child.isFile ? "" : "/"}${item.child.description ? ` (${item.child.description})` : ""}` : `${prefix}${branch} … (${item.omittedCount.toLocaleString()} more entries)`;
123725
+ if (!canAppendLine(line)) {
123726
+ return false;
123727
+ }
123728
+ lines.push(line);
123729
+ totalChars += 1 + line.length;
123730
+ if (item.kind === "entry" && item.child.children.size > 0) {
123709
123731
  const nextPrefix = `${prefix}${isLast ? " " : "│ "}`;
123710
- render2(child, nextPrefix);
123732
+ if (!render2(item.child, nextPrefix)) {
123733
+ return false;
123734
+ }
123711
123735
  }
123712
123736
  }
123737
+ return true;
123713
123738
  };
123714
- render2(root, "");
123739
+ const totalEntries = countTreeEntries(root);
123740
+ const fullyRendered = render2(root, "");
123741
+ if (!fullyRendered) {
123742
+ while (lines.length > 1) {
123743
+ const shownEntries = Math.max(0, lines.length - 1);
123744
+ const omittedEntries = Math.max(1, totalEntries - shownEntries);
123745
+ const notice = `[Tree truncated: showing ${shownEntries.toLocaleString()} of ${totalEntries.toLocaleString()} entries. ${omittedEntries.toLocaleString()} omitted.]`;
123746
+ if (canAppendLine(notice)) {
123747
+ lines.push(notice);
123748
+ break;
123749
+ }
123750
+ const removed = lines.pop();
123751
+ if (removed) {
123752
+ totalChars -= 1 + removed.length;
123753
+ }
123754
+ }
123755
+ }
123715
123756
  return lines.join(`
123716
123757
  `);
123717
123758
  }
@@ -123927,6 +123968,7 @@ async function finalizeAutoReflectionPayload(agentId, conversationId, _payloadPa
123927
123968
  var TRANSCRIPT_ROOT_ENV = "LETTA_TRANSCRIPT_ROOT", DEFAULT_TRANSCRIPT_DIR = "transcripts";
123928
123969
  var init_reflectionTranscript = __esm(async () => {
123929
123970
  init_memoryFilesystem();
123971
+ init_directoryLimits();
123930
123972
  await init_accumulator();
123931
123973
  });
123932
123974
 
@@ -143301,4 +143343,4 @@ Error during initialization: ${message}`);
143301
143343
  }
143302
143344
  main();
143303
143345
 
143304
- //# debugId=370E7E3EE35E2CB664756E2164756E21
143346
+ //# debugId=671451F09469FC0A64756E2164756E21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letta-ai/letta-code",
3
- "version": "0.19.6",
3
+ "version": "0.19.7",
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": {
@@ -16,23 +16,23 @@ If you are running as a background subagent (you cannot use AskUserQuestion):
16
16
  - Use reasonable defaults for all preferences
17
17
  - Any specific overrides will be provided in your initial prompt
18
18
 
19
- ## Your Goal: Explode Into 15-25 Hierarchical Files
19
+ ## Your Goal: Organize Into a Hierarchical Memory Structure
20
20
 
21
- Your goal is to **explode** memory into a **deeply hierarchical structure of 15-25 small, focused files**.
21
+ Your goal is to **organize** memory into a **deeply hierarchical structure of small, focused files**.
22
22
 
23
23
  ### Target Output
24
24
 
25
25
  | Metric | Target |
26
26
  |--------|--------|
27
- | **Total files** | 15-25 (aim for ~20) |
28
- | **Max lines per file** | ~40 lines (split if larger) |
29
- | **Hierarchy depth** | 2-3 levels using `/` naming (e.g., `project/tooling/bun.md`) |
27
+ | **Total files** | Enough focused files to cover distinct concepts without bloating any single file |
28
+ | **File size** | Keep files concise and split when they become unwieldy |
29
+ | **Hierarchy depth** | Use nested `/` paths whenever they improve clarity (e.g., `project/tooling/bun.md`) |
30
30
  | **Nesting requirement** | Every new file MUST be nested under a parent using `/` |
31
31
 
32
32
  **Anti-patterns to avoid:**
33
- - ❌ Ending with only 3-5 large files
33
+ - ❌ Ending with only a few large files
34
34
  - ❌ Flat naming (all files at top level)
35
- - ❌ Mega-files with 10+ sections
35
+ - ❌ Mega-files with many unrelated sections
36
36
 
37
37
  ## Memory Filesystem Integration
38
38
 
@@ -45,27 +45,27 @@ Your memory is a git-backed filesystem at `~/.letta/agents/<agent-id>/`. The act
45
45
  - The filesystem tree (all file paths + metadata) is always visible regardless of location
46
46
  - You can use bash commands (`ls`, `mkdir`, `mv`, `git`) to organize files
47
47
  - You MUST create a **deeply hierarchical file structure** — flat naming is NOT acceptable
48
- - **Target: 15-25 files in system/**, with additional reference files outside as needed
48
+ - Create as many files as needed for clarity in `system/`, with additional reference files outside as needed
49
49
 
50
50
  **MANDATORY principles for hierarchical organization:**
51
51
 
52
52
  | Requirement | Target |
53
53
  |-------------|--------|
54
- | **Total files** | 15-25 files (aim for ~20) |
55
- | **Max lines per file** | ~40 lines (split if larger) |
56
- | **Hierarchy depth** | 2-3 levels using `/` naming |
54
+ | **Total files** | Enough focused files to avoid monoliths while staying maintainable |
55
+ | **File size** | Keep files concise and split when they become unwieldy |
56
+ | **Hierarchy depth** | Use meaningful nesting with `/` naming where it helps organization |
57
57
  | **Nesting requirement** | EVERY new file MUST use `/` naming (no flat files) |
58
58
 
59
59
  **Anti-patterns to avoid:**
60
- - ❌ Creating only 3-5 large files
60
+ - ❌ Creating only a few large files
61
61
  - ❌ Flat naming (all files at top level like `project-commands.md`)
62
- - ❌ Mega-files with 10+ sections
62
+ - ❌ Mega-files with many unrelated sections
63
63
 
64
64
  **Rules:**
65
- - Use **2-3 levels of nesting** for ALL files (e.g., `project/tooling/bun.md`)
66
- - Keep files **focused and small** (~40 lines max per file)
65
+ - Use clear nested paths for files whenever hierarchy improves discoverability (e.g., `project/tooling/bun.md`)
66
+ - Keep files **focused and concise**
67
67
  - Use **descriptive paths** that make sense when you see just the filename
68
- - Split when a file has **2+ concepts** (be aggressive)
68
+ - Split when a file starts mixing multiple concepts (be aggressive)
69
69
 
70
70
  **Example target structure (what success looks like):**
71
71
 
@@ -93,7 +93,7 @@ system/
93
93
  └── behavior.md # How to behave
94
94
  ```
95
95
 
96
- This example has **~20 files** with **3 levels of hierarchy**. Your output should look similar.
96
+ This example is illustrative. Your output should match the project’s actual complexity and the user’s needs.
97
97
 
98
98
  This approach makes memory more **scannable**, **maintainable**, and **shareable** with other agents.
99
99
 
@@ -319,7 +319,7 @@ You should ask these questions at the start (bundle them together in one AskUser
319
319
  2. **Identity**: "Which contributor are you?" (You can often infer this from git logs - e.g., if git shows "cpacker" as a top contributor, ask "Are you cpacker?")
320
320
  3. **Related repos**: "Are there other repositories I should know about and consider in my research?" (e.g., backend monorepo, shared libraries)
321
321
  4. **Historical sessions** (include this question if history data was found in step 2): "I found Claude Code / Codex history on your machine. Should I analyze it to learn your preferences, coding patterns, and project context? This significantly improves how I work with you but uses additional time and tokens." Options: "Yes, analyze history" / "Skip for now". Use "History" as the header.
322
- 5. **Memory updates**: "How often should I check if I should update my memory?" with options "Frequent (every 3-5 turns)" and "Occasional (every 8-10 turns)". This should be a binary question with "Memory" as the header.
322
+ 5. **Memory updates**: "How often should I check whether to update memory?" with options "Frequent" and "Occasional". This should be a binary question with "Memory" as the header.
323
323
  6. **Communication style**: "Terse or detailed responses?"
324
324
  7. **Any specific rules**: "Rules I should always follow?"
325
325
 
@@ -364,11 +364,11 @@ mkdir -p ~/.letta/agents/<agent-id>/memory/system/human/prefs
364
364
  - **Every new file MUST use `/` naming** - no flat files allowed
365
365
  - Use `/` for hierarchy: `project/tooling/testing` (not `project-tooling-testing`)
366
366
  - File path determines the memory label: `system/project/overview.md` → label `project/overview`
367
- - Keep files small and focused (~40 lines max)
367
+ - Keep files small and focused
368
368
  - Use **descriptive frontmatter** — the `description` field helps your future self understand each file's purpose
369
369
 
370
370
  **Checkpoint before proceeding:**
371
- Count your proposed files. **If you have fewer than 15 files, go back and split more aggressively.**
371
+ Review your proposed files and split further if the structure still feels too flat or monolithic.
372
372
 
373
373
  **Benefits:**
374
374
  - More scannable and maintainable
@@ -376,17 +376,17 @@ Count your proposed files. **If you have fewer than 15 files, go back and split
376
376
  - Natural progressive disclosure (load parent, then drill into children)
377
377
  - Works like a file system you're familiar with
378
378
 
379
- ### Split Aggressively - Target 15-25 Files
379
+ ### Split Aggressively
380
380
 
381
- **Don't create monolithic files.** Your goal is **15-25 total files**. Be aggressive about splitting:
381
+ **Don't create monolithic files.** Be aggressive about splitting when it improves clarity:
382
382
 
383
383
  **Split when:**
384
- - A file has **40+ lines** (lower threshold than typical)
385
- - A file has **2+ distinct concepts** (not 3+, be aggressive)
384
+ - A file becomes long enough that scanning it slows you down
385
+ - A file mixes distinct concepts that would be clearer if separated
386
386
  - A section could stand alone as its own file
387
387
  - You can name the extracted content with a clear `/` path
388
388
 
389
- If a file is getting long (>40 lines), split it:
389
+ If a file is getting long or conceptually mixed, split it:
390
390
 
391
391
  **Without memory filesystem** (flat naming - acceptable but not ideal):
392
392
  - `project-overview`: High-level description, tech stack, repo links
@@ -402,7 +402,7 @@ If a file is getting long (>40 lines), split it:
402
402
  - `project/architecture`: Directory structure, key modules
403
403
  - `project/gotchas`: Footguns, things to watch out for
404
404
  - **Must further nest**: `project/tooling/testing`, `project/tooling/linting`, `project/tooling/bun`
405
- - **Target 15-25 files total** - if commands is long, split into `project/commands/dev`, `project/commands/build`, etc.
405
+ - If commands are broad, split into focused files like `project/commands/dev`, `project/commands/build`, etc.
406
406
 
407
407
  This makes memory more scannable and easier to update and share with other agents.
408
408
 
@@ -456,9 +456,9 @@ And add memory files that you think make sense to add (e.g., `project/architectu
456
456
 
457
457
  9. **Create/update memory structure** (can happen incrementally alongside steps 7-8):
458
458
  - **With memfs enabled**: Create a deeply hierarchical file structure using bash commands
459
- - Use `mkdir -p` to create subdirectories (2-3 levels deep)
459
+ - Use `mkdir -p` to create subdirectories as needed
460
460
  - Create `.md` files for memory files using `/` naming
461
- - **Target 15-25 total files** - be aggressive about splitting
461
+ - Be aggressive about splitting when it improves clarity
462
462
  - Use nested paths like `project/tooling/testing.md` (never flat like `project-testing.md`)
463
463
  - **Every new file MUST be nested** under a parent using `/`
464
464
  - **Every new file MUST be nested** under a parent using `/`
@@ -466,10 +466,9 @@ And add memory files that you think make sense to add (e.g., `project/architectu
466
466
  - **Don't wait until the end** - write findings as you go
467
467
 
468
468
  **Checkpoint verification:**
469
- - After creating files, count them: `ls ~/.letta/agents/<agent-id>/memory/system/ | wc -l`
470
- - **If count < 15, you haven't split enough** - go back and split more
471
- - Check maximum depth: `find ~/.letta/agents/<agent-id>/memory/system/ -type f | awk -F/ '{print NF}' | sort -n | tail -1`
472
- - **Should be 2-3 levels deep** minimum
469
+ - Review file count and shape: `find ~/.letta/agents/<agent-id>/memory/system/ -type f | wc -l`
470
+ - Review hierarchy depth: `find ~/.letta/agents/<agent-id>/memory/system/ -type f | awk -F/ '{print NF}' | sort -n | tail -1`
471
+ - Verify the structure feels appropriately granular and discoverable for this project
473
472
 
474
473
  10. **Organize incrementally**:
475
474
  - Start with a basic structure
@@ -487,14 +486,13 @@ And add memory files that you think make sense to add (e.g., `project/architectu
487
486
 
488
487
  Before finishing, you MUST do a reflection step. **Your memory files are visible to you in your system prompt right now.** Look at them carefully and ask yourself:
489
488
 
490
- 1. **File count check**:
491
- - Count your memory files: `ls ~/.letta/agents/<agent-id>/memory/system/ | wc -l`
492
- - **Do you have 15-25 files?** If not, you haven't split enough
493
- - Too few files means they're too large - split more aggressively
489
+ 1. **File granularity check**:
490
+ - Review your memory file set: `find ~/.letta/agents/<agent-id>/memory/system/ -type f | wc -l`
491
+ - Ask whether any files are still too broad and should be split
494
492
 
495
493
  2. **Hierarchy check**:
496
494
  - Are ALL new files using `/` naming? (e.g., `project/tooling/bun.md`)
497
- - Do you have 2-3 levels of nesting minimum?
495
+ - Is the nesting meaningful for this project?
498
496
  - Are there any flat files like `project-commands.md`? **These should be nested**
499
497
 
500
498
  3. **Redundancy check**: Are there files with overlapping content? Either literally overlapping (due to errors while editing), or semantically/conceptually overlapping?
@@ -545,7 +543,7 @@ cat ~/.letta/agents/<agent-id>/memory/system/persona.md
545
543
  ❌ `project_testing.md` (underscore instead of `/`)
546
544
 
547
545
  ```bash
548
- # Create deeply nested directory structure (2-3 levels)
546
+ # Create a nested directory structure suited to the content
549
547
  mkdir -p ~/.letta/agents/<agent-id>/memory/system/project/{tooling,architecture,conventions}
550
548
  mkdir -p ~/.letta/agents/<agent-id>/memory/system/human/prefs
551
549
  mkdir -p ~/.letta/agents/<agent-id>/memory/system/persona/behavior
@@ -584,22 +582,22 @@ mv ~/.letta/agents/<agent-id>/memory/system/project/tooling.md \
584
582
 
585
583
  Before you tell the user you're done, confirm:
586
584
 
587
- - [ ] **File count is 15-25** — Count your files with `ls ~/.letta/agents/<agent-id>/memory/system/ | wc -l`. If < 15, split more.
585
+ - [ ] **File granularity is appropriate** — verify no files are overly broad and split where useful.
588
586
  - [ ] **All new files use `/` naming** — No flat files like `my_notes.md` or `project-commands.md`
589
- - [ ] **Hierarchy is 2-3 levels deep** — e.g., `project/tooling/bun.md`, not just `project.md`
590
- - [ ] **No file exceeds ~40 lines** — Split larger files
591
- - [ ] **Each file has one concept** — If 2+ topics, split into 2+ files
587
+ - [ ] **Hierarchy is meaningful** — nested paths should improve discoverability and organization.
588
+ - [ ] **No file is bloated** — split files that are hard to scan quickly.
589
+ - [ ] **Each file stays concept-focused** — split files that combine unrelated topics.
592
590
  - [ ] **Every file has real content** — No empty or pointer-only files
593
591
  - [ ] **Verify sync**: After creating files, check they appear in your memory files
594
592
 
595
- **If you have fewer than 15 files, you haven't split enough. Go back and split more.**
593
+ **If the structure still feels flat or monolithic, split further until it is clear and maintainable.**
596
594
 
597
595
  ### Best Practices
598
596
 
599
597
  1. **Check memfs status first**: Look for `memory_filesystem` section in your system prompt
600
598
  2. **Start with directories**: Create the directory structure before populating files
601
- 3. **Use short paths**: Aim for 2-3 levels (e.g., `project/tooling/testing`, not `project/dev/tools/testing/setup`)
602
- 4. **Keep files focused**: Each file should cover one concept (~40 lines max)
599
+ 3. **Use practical paths**: prefer clear, readable nesting (e.g., `project/tooling/testing`) over unnecessarily deep paths.
600
+ 4. **Keep files focused**: each file should cover a coherent concept and remain easy to scan.
603
601
  5. **Every file should have real content** — no empty or pointer-only files
604
602
  6. **Be aggressive about splitting**: If in doubt, split. Too many small files is better than too few large ones.
605
603
 
@@ -636,15 +634,11 @@ LINES=$(wc -l < ~/.claude/history.jsonl)
636
634
  CHUNK_SIZE=$(( LINES / NUM_WORKERS + 1 ))
637
635
  split -l $CHUNK_SIZE ~/.claude/history.jsonl "$SPLIT_DIR/claude-"
638
636
 
639
- # Split Codex history (if it exists and is large enough to warrant splitting)
637
+ # Split Codex history if it exists
640
638
  if [ -f ~/.codex/history.jsonl ]; then
641
639
  LINES=$(wc -l < ~/.codex/history.jsonl)
642
- if [ "$LINES" -gt 100 ]; then
643
- CHUNK_SIZE=$(( LINES / NUM_WORKERS + 1 ))
644
- split -l $CHUNK_SIZE ~/.codex/history.jsonl "$SPLIT_DIR/codex-"
645
- else
646
- cp ~/.codex/history.jsonl "$SPLIT_DIR/codex-aa"
647
- fi
640
+ CHUNK_SIZE=$(( LINES / NUM_WORKERS + 1 ))
641
+ split -l $CHUNK_SIZE ~/.codex/history.jsonl "$SPLIT_DIR/codex-"
648
642
  fi
649
643
 
650
644
  # Rename to .jsonl for clarity
@@ -702,12 +696,12 @@ After merging, **read every file in `system/`** and apply editorial judgment:
702
696
 
703
697
  Workers may have created files that don't fit the ideal hierarchy, or put too much into `system/`. Fix this:
704
698
 
705
- - Split oversized files (>40 lines) into focused sub-files
699
+ - Split oversized or conceptually mixed files into focused sub-files
706
700
  - Move reference-quality content (detailed history, background context, evidence trails) to `reference/`
707
701
  - Ensure `system/` contains only what you genuinely need in-context: identity, active preferences, current project context, behavioral rules, gotchas
708
702
  - Merge near-duplicate files that cover the same topic
709
703
 
710
- **Rule of thumb**: If removing a file from `system/` wouldn't hurt your next 10 responses, it belongs in `reference/`.
704
+ **Rule of thumb**: If removing a file from `system/` wouldn't materially affect near-term responses, it belongs in `reference/`.
711
705
 
712
706
  **3d. Clean up worktrees and branches:**
713
707