@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 +84 -42
- package/package.json +1 -1
- package/skills/initializing-memory/SKILL.md +48 -54
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.
|
|
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
|
|
45004
|
+
AGENT_EDITABLE_KEYS="description"
|
|
45005
45005
|
PROTECTED_KEYS="read_only"
|
|
45006
|
-
ALL_KNOWN_KEYS="description limit
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
123703
|
-
|
|
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
|
|
123706
|
-
|
|
123707
|
-
|
|
123708
|
-
|
|
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
|
-
|
|
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=
|
|
143346
|
+
//# debugId=671451F09469FC0A64756E2164756E21
|
package/package.json
CHANGED
|
@@ -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:
|
|
19
|
+
## Your Goal: Organize Into a Hierarchical Memory Structure
|
|
20
20
|
|
|
21
|
-
Your goal is to **
|
|
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** |
|
|
28
|
-
| **
|
|
29
|
-
| **Hierarchy depth** |
|
|
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
|
|
33
|
+
- ❌ Ending with only a few large files
|
|
34
34
|
- ❌ Flat naming (all files at top level)
|
|
35
|
-
- ❌ Mega-files with
|
|
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
|
-
-
|
|
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** |
|
|
55
|
-
| **
|
|
56
|
-
| **Hierarchy depth** |
|
|
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
|
|
60
|
+
- ❌ Creating only a few large files
|
|
61
61
|
- ❌ Flat naming (all files at top level like `project-commands.md`)
|
|
62
|
-
- ❌ Mega-files with
|
|
62
|
+
- ❌ Mega-files with many unrelated sections
|
|
63
63
|
|
|
64
64
|
**Rules:**
|
|
65
|
-
- Use
|
|
66
|
-
- Keep files **focused and
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
379
|
+
### Split Aggressively
|
|
380
380
|
|
|
381
|
-
**Don't create monolithic files.**
|
|
381
|
+
**Don't create monolithic files.** Be aggressive about splitting when it improves clarity:
|
|
382
382
|
|
|
383
383
|
**Split when:**
|
|
384
|
-
- A file
|
|
385
|
-
- A file
|
|
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
|
|
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
|
-
-
|
|
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
|
|
459
|
+
- Use `mkdir -p` to create subdirectories as needed
|
|
460
460
|
- Create `.md` files for memory files using `/` naming
|
|
461
|
-
-
|
|
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
|
-
-
|
|
470
|
-
-
|
|
471
|
-
-
|
|
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
|
|
491
|
-
-
|
|
492
|
-
-
|
|
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
|
-
-
|
|
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
|
|
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
|
|
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
|
|
590
|
-
- [ ] **No file
|
|
591
|
-
- [ ] **Each file
|
|
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
|
|
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
|
|
602
|
-
4. **Keep files focused**:
|
|
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
|
|
637
|
+
# Split Codex history if it exists
|
|
640
638
|
if [ -f ~/.codex/history.jsonl ]; then
|
|
641
639
|
LINES=$(wc -l < ~/.codex/history.jsonl)
|
|
642
|
-
|
|
643
|
-
|
|
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
|
|
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
|
|
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
|
|