swarm_memory 2.0.0 → 2.1.1
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.
- checksums.yaml +4 -4
- data/lib/claude_swarm.rb +4 -2
- data/lib/swarm_cli/version.rb +1 -1
- data/lib/swarm_cli.rb +1 -2
- data/lib/swarm_memory/adapters/base.rb +2 -1
- data/lib/swarm_memory/adapters/filesystem_adapter.rb +102 -46
- data/lib/swarm_memory/core/storage.rb +3 -1
- data/lib/swarm_memory/prompts/memory_assistant.md.erb +47 -5
- data/lib/swarm_memory/prompts/memory_researcher.md.erb +203 -123
- data/lib/swarm_memory/prompts/memory_retrieval.md.erb +2 -0
- data/lib/swarm_memory/search/text_search.rb +3 -1
- data/lib/swarm_memory/tools/memory_glob.rb +25 -10
- data/lib/swarm_memory/tools/memory_grep.rb +59 -21
- data/lib/swarm_memory/tools/memory_write.rb +16 -0
- data/lib/swarm_memory/version.rb +1 -1
- data/lib/swarm_sdk/agent/chat.rb +15 -0
- data/lib/swarm_sdk/swarm/tool_configurator.rb +4 -1
- data/lib/swarm_sdk/tools/think.rb +3 -3
- data/lib/swarm_sdk/version.rb +1 -1
- data/lib/swarm_sdk.rb +1 -11
- metadata +5 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 38d427a70039bc09a7a1bbad5a0f2c9f28e46d80a954587ab27f04ad33c66e2f
|
|
4
|
+
data.tar.gz: 3942488b1873520a984493c6270085b0f6f2e0e01560bea349903463d01bda11
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 945735c0d60be12edc1452ea84059f6a3d6aa68966e687a81d2c8ccbd2492c5b2bdd9099fc4a14c40aaabd1d275cec0a80b35be8c9f5354d217467136493ec57
|
|
7
|
+
data.tar.gz: f5b0680131c7d38ff229da49031628356c44c4edcb90685b7489e2bc2b30a6d361babf55d3e995d5f28acc62f56b28deaa03829d512f95550dd198b192179df0
|
data/lib/claude_swarm.rb
CHANGED
|
@@ -30,13 +30,12 @@ require "thor"
|
|
|
30
30
|
require "zeitwerk"
|
|
31
31
|
loader = Zeitwerk::Loader.new
|
|
32
32
|
loader.tag = "claude_swarm"
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
loader.ignore("#{__dir__}/claude_swarm/templates")
|
|
35
35
|
loader.inflector.inflect(
|
|
36
36
|
"cli" => "CLI",
|
|
37
37
|
"openai" => "OpenAI",
|
|
38
38
|
)
|
|
39
|
-
loader.setup
|
|
40
39
|
|
|
41
40
|
module ClaudeSwarm
|
|
42
41
|
class Error < StandardError; end
|
|
@@ -67,3 +66,6 @@ module ClaudeSwarm
|
|
|
67
66
|
end
|
|
68
67
|
end
|
|
69
68
|
end
|
|
69
|
+
|
|
70
|
+
loader.push_dir("#{__dir__}/claude_swarm", namespace: ClaudeSwarm)
|
|
71
|
+
loader.setup
|
data/lib/swarm_cli/version.rb
CHANGED
data/lib/swarm_cli.rb
CHANGED
|
@@ -74,8 +74,9 @@ module SwarmMemory
|
|
|
74
74
|
# @param pattern [String] Regular expression pattern to search for
|
|
75
75
|
# @param case_insensitive [Boolean] Whether to perform case-insensitive search
|
|
76
76
|
# @param output_mode [String] Output mode: "files_with_matches" (default), "content", or "count"
|
|
77
|
+
# @param path [String, nil] Optional path prefix filter (e.g., "concept/", "fact/api-design")
|
|
77
78
|
# @return [Array<Hash>, String] Results based on output_mode
|
|
78
|
-
def grep(pattern:, case_insensitive: false, output_mode: "files_with_matches")
|
|
79
|
+
def grep(pattern:, case_insensitive: false, output_mode: "files_with_matches", path: nil)
|
|
79
80
|
raise NotImplementedError, "Subclass must implement #grep"
|
|
80
81
|
end
|
|
81
82
|
|
|
@@ -276,9 +276,9 @@ module SwarmMemory
|
|
|
276
276
|
.reject { |f| stub_file?(f) }
|
|
277
277
|
|
|
278
278
|
entries = md_files.map do |md_file|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
279
|
+
# Calculate logical path relative to @directory
|
|
280
|
+
logical_path = md_file.sub("#{@directory}/", "")
|
|
281
|
+
base_logical_path = logical_path.sub(/\.md\z/, "")
|
|
282
282
|
|
|
283
283
|
# Filter by prefix if provided (strip .md for comparison)
|
|
284
284
|
next if prefix && !base_logical_path.start_with?(prefix.sub(/\.md\z/, ""))
|
|
@@ -287,7 +287,7 @@ module SwarmMemory
|
|
|
287
287
|
yaml_data = File.exist?(yaml_file) ? YAML.load_file(yaml_file, permitted_classes: [Time, Date, Symbol]) : {}
|
|
288
288
|
|
|
289
289
|
{
|
|
290
|
-
path: logical_path,
|
|
290
|
+
path: logical_path,
|
|
291
291
|
title: yaml_data["title"] || "Untitled",
|
|
292
292
|
size: yaml_data["size"] || File.size(md_file),
|
|
293
293
|
updated_at: parse_time(yaml_data["updated_at"]) || File.mtime(md_file),
|
|
@@ -304,24 +304,35 @@ module SwarmMemory
|
|
|
304
304
|
def glob(pattern:)
|
|
305
305
|
raise ArgumentError, "pattern is required" if pattern.nil? || pattern.to_s.strip.empty?
|
|
306
306
|
|
|
307
|
-
#
|
|
308
|
-
|
|
309
|
-
|
|
307
|
+
# Normalize pattern to ensure we only match .md files
|
|
308
|
+
# Standard glob behavior - just add .md extension intelligently
|
|
309
|
+
normalized_pattern = if pattern.end_with?("**")
|
|
310
|
+
# fact/** → fact/**/*.md (recursive match of all .md files)
|
|
311
|
+
"#{pattern}/*.md"
|
|
312
|
+
elsif pattern.end_with?("*")
|
|
313
|
+
# fact/* → fact/*.md (direct children .md files only)
|
|
314
|
+
"#{pattern}.md"
|
|
315
|
+
elsif pattern.end_with?(".md")
|
|
316
|
+
# Already has .md, use as-is
|
|
317
|
+
pattern
|
|
318
|
+
else
|
|
319
|
+
# No wildcard or extension, add .md
|
|
320
|
+
"#{pattern}.md"
|
|
321
|
+
end
|
|
310
322
|
|
|
311
|
-
#
|
|
312
|
-
|
|
313
|
-
|
|
323
|
+
# Use native Dir.glob with hierarchical paths - efficient!
|
|
324
|
+
glob_pattern = File.join(@directory, normalized_pattern)
|
|
325
|
+
md_files = Dir.glob(glob_pattern).reject { |f| stub_file?(f) }
|
|
314
326
|
|
|
315
327
|
results = md_files.map do |md_file|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
logical_path = "#{base_logical_path}.md" # Add .md extension
|
|
328
|
+
# Calculate logical path relative to @directory
|
|
329
|
+
relative_path = md_file.sub("#{@directory}/", "")
|
|
319
330
|
|
|
320
331
|
yaml_file = md_file.sub(".md", ".yml")
|
|
321
332
|
yaml_data = File.exist?(yaml_file) ? YAML.load_file(yaml_file, permitted_classes: [Time, Date, Symbol]) : {}
|
|
322
333
|
|
|
323
334
|
{
|
|
324
|
-
path:
|
|
335
|
+
path: relative_path,
|
|
325
336
|
title: yaml_data["title"] || "Untitled",
|
|
326
337
|
size: File.size(md_file),
|
|
327
338
|
updated_at: parse_time(yaml_data["updated_at"]) || File.mtime(md_file),
|
|
@@ -340,7 +351,7 @@ module SwarmMemory
|
|
|
340
351
|
# @param case_insensitive [Boolean] Case-insensitive search
|
|
341
352
|
# @param output_mode [String] Output mode
|
|
342
353
|
# @return [Array<Hash>] Results
|
|
343
|
-
def grep(pattern:, case_insensitive: false, output_mode: "files_with_matches")
|
|
354
|
+
def grep(pattern:, case_insensitive: false, output_mode: "files_with_matches", path: nil)
|
|
344
355
|
raise ArgumentError, "pattern is required" if pattern.nil? || pattern.to_s.strip.empty?
|
|
345
356
|
|
|
346
357
|
flags = case_insensitive ? Regexp::IGNORECASE : 0
|
|
@@ -348,11 +359,11 @@ module SwarmMemory
|
|
|
348
359
|
|
|
349
360
|
case output_mode
|
|
350
361
|
when "files_with_matches"
|
|
351
|
-
grep_files_with_matches(regex)
|
|
362
|
+
grep_files_with_matches(regex, path)
|
|
352
363
|
when "content"
|
|
353
|
-
grep_with_content(regex)
|
|
364
|
+
grep_with_content(regex, path)
|
|
354
365
|
when "count"
|
|
355
|
-
grep_with_count(regex)
|
|
366
|
+
grep_with_count(regex, path)
|
|
356
367
|
else
|
|
357
368
|
raise ArgumentError, "Invalid output_mode: #{output_mode}"
|
|
358
369
|
end
|
|
@@ -506,17 +517,22 @@ module SwarmMemory
|
|
|
506
517
|
#
|
|
507
518
|
# @param logical_path [String] Logical path with slashes
|
|
508
519
|
# @return [String] Flattened path with --
|
|
520
|
+
# Identity function - paths are now stored hierarchically
|
|
521
|
+
# Kept for backward compatibility during transition
|
|
522
|
+
#
|
|
523
|
+
# @param logical_path [String] Logical path
|
|
524
|
+
# @return [String] Same path (no flattening)
|
|
509
525
|
def flatten_path(logical_path)
|
|
510
|
-
logical_path
|
|
526
|
+
logical_path
|
|
511
527
|
end
|
|
512
528
|
|
|
513
|
-
#
|
|
514
|
-
#
|
|
529
|
+
# Identity function - paths are now stored hierarchically
|
|
530
|
+
# Kept for backward compatibility during transition
|
|
515
531
|
#
|
|
516
|
-
# @param disk_path [String]
|
|
517
|
-
# @return [String]
|
|
532
|
+
# @param disk_path [String] Disk path
|
|
533
|
+
# @return [String] Same path (no unflattening)
|
|
518
534
|
def unflatten_path(disk_path)
|
|
519
|
-
disk_path
|
|
535
|
+
disk_path
|
|
520
536
|
end
|
|
521
537
|
|
|
522
538
|
# Check if content is a stub (redirect)
|
|
@@ -622,9 +638,12 @@ module SwarmMemory
|
|
|
622
638
|
Dir.glob(File.join(@directory, "**/*.md")).each do |md_file|
|
|
623
639
|
next if stub_file?(md_file)
|
|
624
640
|
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
641
|
+
# Calculate logical path relative to @directory
|
|
642
|
+
logical_path = md_file.sub("#{@directory}/", "")
|
|
643
|
+
base_logical_path = logical_path.sub(/\.md\z/, "")
|
|
644
|
+
|
|
645
|
+
# disk_path is now the same as base_logical_path (no flattening)
|
|
646
|
+
disk_path = base_logical_path
|
|
628
647
|
|
|
629
648
|
yaml_file = md_file.sub(".md", ".yml")
|
|
630
649
|
yaml_data = File.exist?(yaml_file) ? YAML.load_file(yaml_file, permitted_classes: [Time, Date, Symbol]) : {}
|
|
@@ -648,19 +667,21 @@ module SwarmMemory
|
|
|
648
667
|
#
|
|
649
668
|
# @param regex [Regexp] Pattern to match
|
|
650
669
|
# @return [Array<String>] Matching logical paths with .md extension
|
|
651
|
-
def grep_files_with_matches(regex)
|
|
670
|
+
def grep_files_with_matches(regex, path_filter = nil)
|
|
652
671
|
results = []
|
|
653
672
|
|
|
654
673
|
# Fast path: Search .yml files (metadata)
|
|
655
674
|
Dir.glob(File.join(@directory, "**/*.yml")).each do |yaml_file|
|
|
656
675
|
next if yaml_file.include?("_stubs/")
|
|
657
676
|
|
|
677
|
+
# Calculate logical path relative to @directory
|
|
678
|
+
logical_path = yaml_file.sub("#{@directory}/", "").sub(".yml", ".md")
|
|
679
|
+
next unless matches_path_filter?(logical_path, path_filter)
|
|
680
|
+
|
|
658
681
|
content = File.read(yaml_file)
|
|
659
682
|
next unless regex.match?(content)
|
|
660
683
|
|
|
661
|
-
|
|
662
|
-
base_path = unflatten_path(disk_path)
|
|
663
|
-
results << "#{base_path}.md" # Add .md extension
|
|
684
|
+
results << logical_path
|
|
664
685
|
end
|
|
665
686
|
|
|
666
687
|
# If found in metadata, return quickly
|
|
@@ -670,12 +691,14 @@ module SwarmMemory
|
|
|
670
691
|
Dir.glob(File.join(@directory, "**/*.md")).each do |md_file|
|
|
671
692
|
next if stub_file?(md_file)
|
|
672
693
|
|
|
694
|
+
# Calculate logical path relative to @directory
|
|
695
|
+
logical_path = md_file.sub("#{@directory}/", "")
|
|
696
|
+
next unless matches_path_filter?(logical_path, path_filter)
|
|
697
|
+
|
|
673
698
|
content = File.read(md_file)
|
|
674
699
|
next unless regex.match?(content)
|
|
675
700
|
|
|
676
|
-
|
|
677
|
-
base_path = unflatten_path(disk_path)
|
|
678
|
-
results << "#{base_path}.md" # Add .md extension
|
|
701
|
+
results << logical_path
|
|
679
702
|
end
|
|
680
703
|
|
|
681
704
|
results.uniq.sort
|
|
@@ -684,13 +707,18 @@ module SwarmMemory
|
|
|
684
707
|
# Grep with content and line numbers
|
|
685
708
|
#
|
|
686
709
|
# @param regex [Regexp] Pattern to match
|
|
710
|
+
# @param path_filter [String, nil] Optional path prefix filter
|
|
687
711
|
# @return [Array<Hash>] Results with matches
|
|
688
|
-
def grep_with_content(regex)
|
|
712
|
+
def grep_with_content(regex, path_filter = nil)
|
|
689
713
|
results = []
|
|
690
714
|
|
|
691
715
|
Dir.glob(File.join(@directory, "**/*.md")).each do |md_file|
|
|
692
716
|
next if stub_file?(md_file)
|
|
693
717
|
|
|
718
|
+
# Calculate logical path relative to @directory
|
|
719
|
+
logical_path = md_file.sub("#{@directory}/", "")
|
|
720
|
+
next unless matches_path_filter?(logical_path, path_filter)
|
|
721
|
+
|
|
694
722
|
content = File.read(md_file)
|
|
695
723
|
matching_lines = []
|
|
696
724
|
|
|
@@ -700,12 +728,8 @@ module SwarmMemory
|
|
|
700
728
|
|
|
701
729
|
next if matching_lines.empty?
|
|
702
730
|
|
|
703
|
-
disk_path = File.basename(md_file, ".md")
|
|
704
|
-
base_path = unflatten_path(disk_path)
|
|
705
|
-
logical_path = "#{base_path}.md" # Add .md extension
|
|
706
|
-
|
|
707
731
|
results << {
|
|
708
|
-
path: logical_path,
|
|
732
|
+
path: logical_path,
|
|
709
733
|
matches: matching_lines,
|
|
710
734
|
}
|
|
711
735
|
end
|
|
@@ -716,24 +740,25 @@ module SwarmMemory
|
|
|
716
740
|
# Grep with match counts
|
|
717
741
|
#
|
|
718
742
|
# @param regex [Regexp] Pattern to match
|
|
743
|
+
# @param path_filter [String, nil] Optional path prefix filter
|
|
719
744
|
# @return [Array<Hash>] Results with counts
|
|
720
|
-
def grep_with_count(regex)
|
|
745
|
+
def grep_with_count(regex, path_filter = nil)
|
|
721
746
|
results = []
|
|
722
747
|
|
|
723
748
|
Dir.glob(File.join(@directory, "**/*.md")).each do |md_file|
|
|
724
749
|
next if stub_file?(md_file)
|
|
725
750
|
|
|
751
|
+
# Calculate logical path relative to @directory
|
|
752
|
+
logical_path = md_file.sub("#{@directory}/", "")
|
|
753
|
+
next unless matches_path_filter?(logical_path, path_filter)
|
|
754
|
+
|
|
726
755
|
content = File.read(md_file)
|
|
727
756
|
count = content.scan(regex).size
|
|
728
757
|
|
|
729
758
|
next if count <= 0
|
|
730
759
|
|
|
731
|
-
disk_path = File.basename(md_file, ".md")
|
|
732
|
-
base_path = unflatten_path(disk_path)
|
|
733
|
-
logical_path = "#{base_path}.md" # Add .md extension
|
|
734
|
-
|
|
735
760
|
results << {
|
|
736
|
-
path: logical_path,
|
|
761
|
+
path: logical_path,
|
|
737
762
|
count: count,
|
|
738
763
|
}
|
|
739
764
|
end
|
|
@@ -741,6 +766,37 @@ module SwarmMemory
|
|
|
741
766
|
results
|
|
742
767
|
end
|
|
743
768
|
|
|
769
|
+
# Check if a logical path matches the filter
|
|
770
|
+
#
|
|
771
|
+
# Behaves like directory/file filtering even though paths are logical.
|
|
772
|
+
#
|
|
773
|
+
# @param logical_path [String] The logical path to check (e.g., "concept/ruby/blocks.md")
|
|
774
|
+
# @param path_filter [String, nil] Optional path prefix filter (e.g., "concept/", "fact/api-design", "skill/ruby/lambdas.md")
|
|
775
|
+
# @return [Boolean] True if path matches or no filter specified
|
|
776
|
+
#
|
|
777
|
+
# @example Directory-style filtering
|
|
778
|
+
# matches_path_filter?("concept/ruby/blocks.md", "concept/") #=> true
|
|
779
|
+
# matches_path_filter?("concept/ruby/blocks.md", "concept") #=> true
|
|
780
|
+
# matches_path_filter?("fact/api-design/rest.md", "fact/api") #=> false (requires "fact/api/")
|
|
781
|
+
# matches_path_filter?("fact/api/rest-basics.md", "fact/api") #=> true
|
|
782
|
+
#
|
|
783
|
+
# @example File-specific filtering
|
|
784
|
+
# matches_path_filter?("concept/ruby/blocks.md", "concept/ruby/blocks.md") #=> true (exact match)
|
|
785
|
+
# matches_path_filter?("concept/ruby/lambdas.md", "concept/ruby/blocks.md") #=> false
|
|
786
|
+
def matches_path_filter?(logical_path, path_filter)
|
|
787
|
+
return true if path_filter.nil? || path_filter.empty?
|
|
788
|
+
|
|
789
|
+
# If filter specifies a file (ends with .md), do exact match
|
|
790
|
+
return logical_path == path_filter if path_filter.end_with?(".md")
|
|
791
|
+
|
|
792
|
+
# Otherwise, treat as directory path
|
|
793
|
+
# Normalize: ensure filter ends with "/" for proper directory matching
|
|
794
|
+
# This prevents "fact/api" from matching "fact/api-design/"
|
|
795
|
+
dir_filter = path_filter.end_with?("/") ? path_filter : "#{path_filter}/"
|
|
796
|
+
|
|
797
|
+
logical_path.start_with?(dir_filter)
|
|
798
|
+
end
|
|
799
|
+
|
|
744
800
|
# Calculate checksum for embedding
|
|
745
801
|
#
|
|
746
802
|
# @param embedding [Array<Float>] Embedding vector
|
|
@@ -143,12 +143,14 @@ module SwarmMemory
|
|
|
143
143
|
# @param pattern [String] Regex pattern
|
|
144
144
|
# @param case_insensitive [Boolean] Case-insensitive search
|
|
145
145
|
# @param output_mode [String] Output mode
|
|
146
|
+
# @param path [String, nil] Optional path prefix filter
|
|
146
147
|
# @return [Array<Hash>] Search results
|
|
147
|
-
def grep(pattern:, case_insensitive: false, output_mode: "files_with_matches")
|
|
148
|
+
def grep(pattern:, case_insensitive: false, output_mode: "files_with_matches", path: nil)
|
|
148
149
|
@adapter.grep(
|
|
149
150
|
pattern: pattern,
|
|
150
151
|
case_insensitive: case_insensitive,
|
|
151
152
|
output_mode: output_mode,
|
|
153
|
+
path: path,
|
|
152
154
|
)
|
|
153
155
|
end
|
|
154
156
|
|
|
@@ -7,7 +7,9 @@ You have persistent memory that learns from conversations and helps you answer q
|
|
|
7
7
|
**When user says "learn about X" or "research X":**
|
|
8
8
|
1. Gather information (read docs, ask questions, etc.)
|
|
9
9
|
2. **STORE your findings in memory** using MemoryWrite
|
|
10
|
-
3.
|
|
10
|
+
3. **Be THOROUGH** - Capture all important details, don't summarize away key information
|
|
11
|
+
4. **Split if needed** - If content is large, create multiple focused, linked memories
|
|
12
|
+
5. Categorize as fact/concept/skill/experience
|
|
11
13
|
|
|
12
14
|
**"Learning" is NOT complete until you've stored it in memory.**
|
|
13
15
|
|
|
@@ -16,7 +18,7 @@ You have persistent memory that learns from conversations and helps you answer q
|
|
|
16
18
|
- "Find out who's the commander" → Discover it → MemoryWrite(type: "fact", ...)
|
|
17
19
|
- "Learn this procedure" → Understand it → MemoryWrite(type: "skill", ...)
|
|
18
20
|
|
|
19
|
-
**Learning = Understanding +
|
|
21
|
+
**Learning = Understanding + Thorough Storage. Always do both.**
|
|
20
22
|
|
|
21
23
|
## Your Memory Tools (Use ONLY These)
|
|
22
24
|
|
|
@@ -79,6 +81,42 @@ User says: "Save a skill called 'Eclipse power prep' with these steps..."
|
|
|
79
81
|
|
|
80
82
|
**Don't consolidate.** Separate memories are more searchable.
|
|
81
83
|
|
|
84
|
+
## CRITICAL: Be Thorough But Split Large Content
|
|
85
|
+
|
|
86
|
+
**IMPORTANT: Memories are NOT summaries - they are FULL, DETAILED records.**
|
|
87
|
+
|
|
88
|
+
**When storing information, you MUST:**
|
|
89
|
+
|
|
90
|
+
1. **Be THOROUGH** - Don't miss any details, facts, or nuances
|
|
91
|
+
2. **Store COMPLETE information** - Not just bullet points or summaries
|
|
92
|
+
3. **Include ALL relevant details** - Code examples, specific values, exact procedures
|
|
93
|
+
4. **Keep each memory FOCUSED** - If content is getting long, split it
|
|
94
|
+
5. **Link related memories** - Use the `related` metadata field
|
|
95
|
+
|
|
96
|
+
**What this means:**
|
|
97
|
+
- ❌ "The payment system has several validation steps" (too vague)
|
|
98
|
+
- ✅ "The payment system validates: 1) Card number format (Luhn algorithm), 2) CVV length (3-4 digits depending on card type), 3) Expiration date (must be future date), 4) Billing address match via AVS..." (complete details)
|
|
99
|
+
|
|
100
|
+
**If content is too large:**
|
|
101
|
+
- ✅ Split into multiple focused memories
|
|
102
|
+
- ✅ Each memory covers one specific aspect IN DETAIL
|
|
103
|
+
- ✅ Link them together using `related` field
|
|
104
|
+
- ❌ Don't create one huge memory that's hard to search
|
|
105
|
+
- ❌ Don't summarize to make it fit - split instead
|
|
106
|
+
|
|
107
|
+
**Example - Learning about a complex system:**
|
|
108
|
+
|
|
109
|
+
Instead of one giant memory:
|
|
110
|
+
❌ `concept/payment-system.md` (1000 words covering everything)
|
|
111
|
+
|
|
112
|
+
Create multiple linked memories with FULL details in each:
|
|
113
|
+
✅ `concept/payment/processing-flow.md` (250 words) (complete flow with all steps) → related: ["concept/payment/validation.md"]
|
|
114
|
+
✅ `concept/payment/validation.md` (250 words) (all validation rules with specifics) → related: ["concept/payment/processing-flow.md", "concept/payment/error-handling.md"]
|
|
115
|
+
✅ `concept/payment/error-handling.md` (250 words) (all error codes and responses) → related: ["concept/payment/validation.md"]
|
|
116
|
+
✅ `concept/payment/security.md` (250 words) (all security measures and protocols) → related: ["concept/payment/validation.md"]
|
|
117
|
+
|
|
118
|
+
**The goal: Capture EVERYTHING with full details, but keep each memory focused and searchable.**
|
|
119
|
+
|
|
82
120
|
## When to Use LoadSkill vs MemoryRead
|
|
83
121
|
|
|
84
122
|
**CRITICAL - LoadSkill is for DOING, not for explaining:**
|
|
@@ -110,8 +148,10 @@ User says: "Save a skill called 'Eclipse power prep' with these steps..."
|
|
|
110
148
|
**When user teaches you:**
|
|
111
149
|
1. Listen to what they're saying
|
|
112
150
|
2. Identify the type (fact/concept/skill/experience)
|
|
113
|
-
3.
|
|
114
|
-
4.
|
|
151
|
+
3. **Capture ALL details** - Don't skip anything important
|
|
152
|
+
4. If content is large, split into multiple related memories
|
|
153
|
+
5. MemoryWrite with proper type, metadata, and `related` links
|
|
154
|
+
6. Continue conversation naturally
|
|
115
155
|
|
|
116
156
|
**When user asks a question:**
|
|
117
157
|
1. Check auto-surfaced memories (including skills)
|
|
@@ -132,8 +172,10 @@ User says: "Save a skill called 'Eclipse power prep' with these steps..."
|
|
|
132
172
|
- `title` - Brief description
|
|
133
173
|
- `tags` - Searchable keywords
|
|
134
174
|
- `domain` - Category (e.g., "people", "thermal/systems")
|
|
135
|
-
- `related` - Empty array `[]` if
|
|
175
|
+
- `related` - **IMPORTANT**: Link related memories (e.g., ["concept/payment/validation.md"]). Use this to connect split memories and related topics. Empty array `[]` only if truly isolated.
|
|
136
176
|
- `confidence` - Defaults to "medium" if omitted
|
|
137
177
|
- `source` - Defaults to "user" if omitted
|
|
138
178
|
|
|
139
179
|
**Be natural in conversation. Store knowledge efficiently. Create skills when user describes procedures.**
|
|
180
|
+
|
|
181
|
+
IMPORTANT: For optimal performance, make all tool calls in parallel when you can.
|