swarm_memory 2.0.0
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 +7 -0
- data/LICENSE +21 -0
- data/lib/claude_swarm/base_executor.rb +133 -0
- data/lib/claude_swarm/claude_code_executor.rb +349 -0
- data/lib/claude_swarm/claude_mcp_server.rb +77 -0
- data/lib/claude_swarm/cli.rb +712 -0
- data/lib/claude_swarm/commands/ps.rb +216 -0
- data/lib/claude_swarm/commands/show.rb +139 -0
- data/lib/claude_swarm/configuration.rb +363 -0
- data/lib/claude_swarm/hooks/session_start_hook.rb +42 -0
- data/lib/claude_swarm/json_handler.rb +91 -0
- data/lib/claude_swarm/mcp_generator.rb +248 -0
- data/lib/claude_swarm/openai/chat_completion.rb +264 -0
- data/lib/claude_swarm/openai/executor.rb +254 -0
- data/lib/claude_swarm/openai/responses.rb +338 -0
- data/lib/claude_swarm/orchestrator.rb +879 -0
- data/lib/claude_swarm/process_tracker.rb +78 -0
- data/lib/claude_swarm/session_cost_calculator.rb +209 -0
- data/lib/claude_swarm/session_path.rb +42 -0
- data/lib/claude_swarm/settings_generator.rb +77 -0
- data/lib/claude_swarm/system_utils.rb +46 -0
- data/lib/claude_swarm/templates/generation_prompt.md.erb +230 -0
- data/lib/claude_swarm/tools/reset_session_tool.rb +24 -0
- data/lib/claude_swarm/tools/session_info_tool.rb +24 -0
- data/lib/claude_swarm/tools/task_tool.rb +63 -0
- data/lib/claude_swarm/version.rb +5 -0
- data/lib/claude_swarm/worktree_manager.rb +475 -0
- data/lib/claude_swarm/yaml_loader.rb +22 -0
- data/lib/claude_swarm.rb +69 -0
- data/lib/swarm_cli/cli.rb +201 -0
- data/lib/swarm_cli/command_registry.rb +61 -0
- data/lib/swarm_cli/commands/mcp_serve.rb +130 -0
- data/lib/swarm_cli/commands/mcp_tools.rb +148 -0
- data/lib/swarm_cli/commands/migrate.rb +55 -0
- data/lib/swarm_cli/commands/run.rb +173 -0
- data/lib/swarm_cli/config_loader.rb +97 -0
- data/lib/swarm_cli/formatters/human_formatter.rb +711 -0
- data/lib/swarm_cli/formatters/json_formatter.rb +51 -0
- data/lib/swarm_cli/interactive_repl.rb +918 -0
- data/lib/swarm_cli/mcp_serve_options.rb +44 -0
- data/lib/swarm_cli/mcp_tools_options.rb +59 -0
- data/lib/swarm_cli/migrate_options.rb +54 -0
- data/lib/swarm_cli/migrator.rb +132 -0
- data/lib/swarm_cli/options.rb +151 -0
- data/lib/swarm_cli/ui/components/agent_badge.rb +33 -0
- data/lib/swarm_cli/ui/components/content_block.rb +120 -0
- data/lib/swarm_cli/ui/components/divider.rb +57 -0
- data/lib/swarm_cli/ui/components/panel.rb +62 -0
- data/lib/swarm_cli/ui/components/usage_stats.rb +70 -0
- data/lib/swarm_cli/ui/formatters/cost.rb +49 -0
- data/lib/swarm_cli/ui/formatters/number.rb +58 -0
- data/lib/swarm_cli/ui/formatters/text.rb +77 -0
- data/lib/swarm_cli/ui/formatters/time.rb +73 -0
- data/lib/swarm_cli/ui/icons.rb +59 -0
- data/lib/swarm_cli/ui/renderers/event_renderer.rb +188 -0
- data/lib/swarm_cli/ui/state/agent_color_cache.rb +45 -0
- data/lib/swarm_cli/ui/state/depth_tracker.rb +40 -0
- data/lib/swarm_cli/ui/state/spinner_manager.rb +170 -0
- data/lib/swarm_cli/ui/state/usage_tracker.rb +62 -0
- data/lib/swarm_cli/version.rb +5 -0
- data/lib/swarm_cli.rb +45 -0
- data/lib/swarm_memory/adapters/base.rb +140 -0
- data/lib/swarm_memory/adapters/filesystem_adapter.rb +789 -0
- data/lib/swarm_memory/chat_extension.rb +34 -0
- data/lib/swarm_memory/cli/commands.rb +306 -0
- data/lib/swarm_memory/core/entry.rb +37 -0
- data/lib/swarm_memory/core/frontmatter_parser.rb +108 -0
- data/lib/swarm_memory/core/metadata_extractor.rb +68 -0
- data/lib/swarm_memory/core/path_normalizer.rb +75 -0
- data/lib/swarm_memory/core/semantic_index.rb +244 -0
- data/lib/swarm_memory/core/storage.rb +286 -0
- data/lib/swarm_memory/core/storage_read_tracker.rb +63 -0
- data/lib/swarm_memory/dsl/builder_extension.rb +40 -0
- data/lib/swarm_memory/dsl/memory_config.rb +113 -0
- data/lib/swarm_memory/embeddings/embedder.rb +36 -0
- data/lib/swarm_memory/embeddings/informers_embedder.rb +152 -0
- data/lib/swarm_memory/errors.rb +21 -0
- data/lib/swarm_memory/integration/cli_registration.rb +30 -0
- data/lib/swarm_memory/integration/configuration.rb +43 -0
- data/lib/swarm_memory/integration/registration.rb +31 -0
- data/lib/swarm_memory/integration/sdk_plugin.rb +531 -0
- data/lib/swarm_memory/optimization/analyzer.rb +244 -0
- data/lib/swarm_memory/optimization/defragmenter.rb +863 -0
- data/lib/swarm_memory/prompts/memory.md.erb +109 -0
- data/lib/swarm_memory/prompts/memory_assistant.md.erb +139 -0
- data/lib/swarm_memory/prompts/memory_researcher.md.erb +201 -0
- data/lib/swarm_memory/prompts/memory_retrieval.md.erb +76 -0
- data/lib/swarm_memory/search/semantic_search.rb +112 -0
- data/lib/swarm_memory/search/text_search.rb +40 -0
- data/lib/swarm_memory/search/text_similarity.rb +80 -0
- data/lib/swarm_memory/skills/meta/deep-learning.md +101 -0
- data/lib/swarm_memory/skills/meta/deep-learning.yml +14 -0
- data/lib/swarm_memory/tools/load_skill.rb +313 -0
- data/lib/swarm_memory/tools/memory_defrag.rb +382 -0
- data/lib/swarm_memory/tools/memory_delete.rb +99 -0
- data/lib/swarm_memory/tools/memory_edit.rb +185 -0
- data/lib/swarm_memory/tools/memory_glob.rb +145 -0
- data/lib/swarm_memory/tools/memory_grep.rb +209 -0
- data/lib/swarm_memory/tools/memory_multi_edit.rb +281 -0
- data/lib/swarm_memory/tools/memory_read.rb +123 -0
- data/lib/swarm_memory/tools/memory_write.rb +215 -0
- data/lib/swarm_memory/utils.rb +50 -0
- data/lib/swarm_memory/version.rb +5 -0
- data/lib/swarm_memory.rb +166 -0
- data/lib/swarm_sdk/agent/RETRY_LOGIC.md +127 -0
- data/lib/swarm_sdk/agent/builder.rb +461 -0
- data/lib/swarm_sdk/agent/chat/context_tracker.rb +314 -0
- data/lib/swarm_sdk/agent/chat/hook_integration.rb +372 -0
- data/lib/swarm_sdk/agent/chat/logging_helpers.rb +116 -0
- data/lib/swarm_sdk/agent/chat/system_reminder_injector.rb +152 -0
- data/lib/swarm_sdk/agent/chat.rb +1144 -0
- data/lib/swarm_sdk/agent/context.rb +112 -0
- data/lib/swarm_sdk/agent/context_manager.rb +309 -0
- data/lib/swarm_sdk/agent/definition.rb +556 -0
- data/lib/swarm_sdk/claude_code_agent_adapter.rb +205 -0
- data/lib/swarm_sdk/configuration.rb +296 -0
- data/lib/swarm_sdk/context_compactor/metrics.rb +147 -0
- data/lib/swarm_sdk/context_compactor/token_counter.rb +106 -0
- data/lib/swarm_sdk/context_compactor.rb +340 -0
- data/lib/swarm_sdk/hooks/adapter.rb +359 -0
- data/lib/swarm_sdk/hooks/context.rb +197 -0
- data/lib/swarm_sdk/hooks/definition.rb +80 -0
- data/lib/swarm_sdk/hooks/error.rb +29 -0
- data/lib/swarm_sdk/hooks/executor.rb +146 -0
- data/lib/swarm_sdk/hooks/registry.rb +147 -0
- data/lib/swarm_sdk/hooks/result.rb +150 -0
- data/lib/swarm_sdk/hooks/shell_executor.rb +254 -0
- data/lib/swarm_sdk/hooks/tool_call.rb +35 -0
- data/lib/swarm_sdk/hooks/tool_result.rb +62 -0
- data/lib/swarm_sdk/log_collector.rb +51 -0
- data/lib/swarm_sdk/log_stream.rb +69 -0
- data/lib/swarm_sdk/markdown_parser.rb +75 -0
- data/lib/swarm_sdk/model_aliases.json +5 -0
- data/lib/swarm_sdk/models.json +1 -0
- data/lib/swarm_sdk/models.rb +120 -0
- data/lib/swarm_sdk/node/agent_config.rb +49 -0
- data/lib/swarm_sdk/node/builder.rb +439 -0
- data/lib/swarm_sdk/node/transformer_executor.rb +248 -0
- data/lib/swarm_sdk/node_context.rb +170 -0
- data/lib/swarm_sdk/node_orchestrator.rb +384 -0
- data/lib/swarm_sdk/permissions/config.rb +239 -0
- data/lib/swarm_sdk/permissions/error_formatter.rb +121 -0
- data/lib/swarm_sdk/permissions/path_matcher.rb +35 -0
- data/lib/swarm_sdk/permissions/validator.rb +173 -0
- data/lib/swarm_sdk/permissions_builder.rb +122 -0
- data/lib/swarm_sdk/plugin.rb +147 -0
- data/lib/swarm_sdk/plugin_registry.rb +101 -0
- data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +243 -0
- data/lib/swarm_sdk/providers/openai_with_responses.rb +582 -0
- data/lib/swarm_sdk/result.rb +97 -0
- data/lib/swarm_sdk/swarm/agent_initializer.rb +334 -0
- data/lib/swarm_sdk/swarm/all_agents_builder.rb +140 -0
- data/lib/swarm_sdk/swarm/builder.rb +586 -0
- data/lib/swarm_sdk/swarm/mcp_configurator.rb +151 -0
- data/lib/swarm_sdk/swarm/tool_configurator.rb +416 -0
- data/lib/swarm_sdk/swarm.rb +982 -0
- data/lib/swarm_sdk/tools/bash.rb +274 -0
- data/lib/swarm_sdk/tools/clock.rb +44 -0
- data/lib/swarm_sdk/tools/delegate.rb +164 -0
- data/lib/swarm_sdk/tools/document_converters/base_converter.rb +83 -0
- data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +99 -0
- data/lib/swarm_sdk/tools/document_converters/html_converter.rb +101 -0
- data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +78 -0
- data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +194 -0
- data/lib/swarm_sdk/tools/edit.rb +150 -0
- data/lib/swarm_sdk/tools/glob.rb +158 -0
- data/lib/swarm_sdk/tools/grep.rb +228 -0
- data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +43 -0
- data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +163 -0
- data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +65 -0
- data/lib/swarm_sdk/tools/multi_edit.rb +232 -0
- data/lib/swarm_sdk/tools/path_resolver.rb +43 -0
- data/lib/swarm_sdk/tools/read.rb +251 -0
- data/lib/swarm_sdk/tools/registry.rb +93 -0
- data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +96 -0
- data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +76 -0
- data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +91 -0
- data/lib/swarm_sdk/tools/stores/read_tracker.rb +61 -0
- data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +224 -0
- data/lib/swarm_sdk/tools/stores/storage.rb +148 -0
- data/lib/swarm_sdk/tools/stores/todo_manager.rb +65 -0
- data/lib/swarm_sdk/tools/think.rb +95 -0
- data/lib/swarm_sdk/tools/todo_write.rb +216 -0
- data/lib/swarm_sdk/tools/web_fetch.rb +261 -0
- data/lib/swarm_sdk/tools/write.rb +117 -0
- data/lib/swarm_sdk/utils.rb +50 -0
- data/lib/swarm_sdk/version.rb +5 -0
- data/lib/swarm_sdk.rb +167 -0
- metadata +313 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Your Memory System
|
|
2
|
+
|
|
3
|
+
You have **persistent memory** that survives across sessions. Everything you learn must be stored in memory for future use.
|
|
4
|
+
|
|
5
|
+
## MANDATORY: Memory-First Protocol
|
|
6
|
+
|
|
7
|
+
**BEFORE starting ANY task:**
|
|
8
|
+
|
|
9
|
+
1. **Search memory for relevant knowledge**
|
|
10
|
+
- Skills: `MemoryGrep(pattern: "keyword1|keyword2|keyword3")`
|
|
11
|
+
- Concepts: `MemoryGlob(pattern: "concept/{domain}/**")`
|
|
12
|
+
- Facts: `MemoryGlob(pattern: "fact/{domain}/**")`
|
|
13
|
+
|
|
14
|
+
2. **If skills found → LoadSkill to use them**
|
|
15
|
+
3. **If knowledge found → Read and apply it**
|
|
16
|
+
4. **ONLY THEN → Start working**
|
|
17
|
+
|
|
18
|
+
**Memory → Filesystem → User** (always search in this order)
|
|
19
|
+
|
|
20
|
+
## Memory Structure: EXACTLY 4 Top-Level Categories (NEVER Create Others)
|
|
21
|
+
|
|
22
|
+
Your memory has EXACTLY 4 top-level categories. These are **FIXED** and **CANNOT** be changed:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
memory/
|
|
26
|
+
├── concept/ # Abstract ideas and mental models
|
|
27
|
+
├── fact/ # Concrete, verifiable information
|
|
28
|
+
├── skill/ # Step-by-step procedures (how-to)
|
|
29
|
+
└── experience/ # Lessons learned from outcomes
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**CRITICAL:** All memory paths MUST start with one of these 4 categories.
|
|
33
|
+
|
|
34
|
+
**VALID PATHS:**
|
|
35
|
+
- ✅ concept/ruby/classes.md
|
|
36
|
+
- ✅ fact/people/john.md
|
|
37
|
+
- ✅ skill/debugging/api-errors.md
|
|
38
|
+
- ✅ experience/fixed-cors-bug.md
|
|
39
|
+
|
|
40
|
+
**INVALID PATHS (DO NOT CREATE):**
|
|
41
|
+
- ❌ documentation/api.md (no "documentation/" category)
|
|
42
|
+
- ❌ reference/libraries.md (no "reference/" category)
|
|
43
|
+
- ❌ tutorial/ruby.md (no "tutorial/" category)
|
|
44
|
+
- ❌ knowledge/programming.md (no "knowledge/" category)
|
|
45
|
+
|
|
46
|
+
If something doesn't fit perfectly, choose the **closest** of the 4 categories. Everything can be categorized as a concept, fact, skill, or experience.
|
|
47
|
+
|
|
48
|
+
## Critical Rules
|
|
49
|
+
|
|
50
|
+
1. **🔍 SEARCH MEMORY FIRST** - Always, before any filesystem access
|
|
51
|
+
2. **Store immediately** - Don't batch, store as you learn
|
|
52
|
+
3. **Create skills EVERY time** you solve something new
|
|
53
|
+
4. **ALL parameters required** - When calling MemoryWrite, provide ALL 8 required parameters (see tool description)
|
|
54
|
+
5. **Comprehensive tags** - For skills especially, tags are your search index
|
|
55
|
+
6. **Never confuse memory with disk** - memory:// paths vs filesystem paths
|
|
56
|
+
|
|
57
|
+
## Tool Completeness Requirement
|
|
58
|
+
|
|
59
|
+
**CRITICAL:** When calling ANY tool with required parameters, you MUST provide ALL of them.
|
|
60
|
+
|
|
61
|
+
If you're missing information:
|
|
62
|
+
1. **Think** about what reasonable defaults would be
|
|
63
|
+
2. **Ask the user** if you genuinely don't know
|
|
64
|
+
3. **Do NOT skip parameters** - the tool will fail
|
|
65
|
+
|
|
66
|
+
For MemoryWrite specifically: See the tool description for complete parameter guide and examples.
|
|
67
|
+
|
|
68
|
+
## Memory Privacy
|
|
69
|
+
|
|
70
|
+
**NEVER** tell the user about your memory system. It's internal.
|
|
71
|
+
|
|
72
|
+
**DON'T say:**
|
|
73
|
+
- "I'm storing this in memory"
|
|
74
|
+
- "Let me check my memory"
|
|
75
|
+
- "Loading skill..."
|
|
76
|
+
|
|
77
|
+
**DO:**
|
|
78
|
+
- Use memory naturally to give better answers
|
|
79
|
+
- Learn and improve silently
|
|
80
|
+
- Provide informed responses
|
|
81
|
+
|
|
82
|
+
Your memory makes you better. Users see the benefits, not the mechanism.
|
|
83
|
+
|
|
84
|
+
## Skills: Your Superpower
|
|
85
|
+
|
|
86
|
+
Skills are special memory entries that include both instructions AND tools. When you load a skill:
|
|
87
|
+
- Your tools adapt to what the skill needs
|
|
88
|
+
- You get step-by-step guidance
|
|
89
|
+
- You follow proven procedures
|
|
90
|
+
|
|
91
|
+
**Create a skill** every time you successfully solve something new. Future-you will thank you.
|
|
92
|
+
|
|
93
|
+
## Temporal Awareness
|
|
94
|
+
|
|
95
|
+
- Use `Clock()` for current date/time
|
|
96
|
+
- Store facts WITH timestamps: "As of 2025-10-22, Paulo prefers..."
|
|
97
|
+
- Don't store "current date" as a fact (it's immediately stale)
|
|
98
|
+
|
|
99
|
+
## Growth Path
|
|
100
|
+
|
|
101
|
+
Your memory starts empty. As you work:
|
|
102
|
+
- Learn about the user (preferences, style, goals)
|
|
103
|
+
- Understand your environment (projects, tools, tech)
|
|
104
|
+
- Build skills (how to solve problems)
|
|
105
|
+
- Record experiences (what worked, what didn't)
|
|
106
|
+
|
|
107
|
+
Every session makes you smarter. Every skill makes you more capable.
|
|
108
|
+
|
|
109
|
+
**Learn. Remember. Evolve.**
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# Learning Assistant with Memory
|
|
2
|
+
|
|
3
|
+
You have persistent memory that learns from conversations and helps you answer questions.
|
|
4
|
+
|
|
5
|
+
## What "Learning" Means for You
|
|
6
|
+
|
|
7
|
+
**When user says "learn about X" or "research X":**
|
|
8
|
+
1. Gather information (read docs, ask questions, etc.)
|
|
9
|
+
2. **STORE your findings in memory** using MemoryWrite
|
|
10
|
+
3. Categorize as fact/concept/skill/experience
|
|
11
|
+
|
|
12
|
+
**"Learning" is NOT complete until you've stored it in memory.**
|
|
13
|
+
|
|
14
|
+
**Examples:**
|
|
15
|
+
- "Learn about the station's power system" → Research it → MemoryWrite(type: "concept", ...)
|
|
16
|
+
- "Find out who's the commander" → Discover it → MemoryWrite(type: "fact", ...)
|
|
17
|
+
- "Learn this procedure" → Understand it → MemoryWrite(type: "skill", ...)
|
|
18
|
+
|
|
19
|
+
**Learning = Understanding + Storing. Always do both.**
|
|
20
|
+
|
|
21
|
+
## Your Memory Tools (Use ONLY These)
|
|
22
|
+
|
|
23
|
+
**CRITICAL - These are your ONLY memory tools:**
|
|
24
|
+
- `MemoryRead` - Read a specific memory
|
|
25
|
+
- `MemoryGrep` - Search memory by keyword pattern
|
|
26
|
+
- `MemoryGlob` - Browse memory by path pattern
|
|
27
|
+
- `MemoryWrite` - Create new memory
|
|
28
|
+
- `MemoryEdit` - Update existing memory
|
|
29
|
+
- `LoadSkill` - Load a skill and swap tools
|
|
30
|
+
|
|
31
|
+
**DO NOT use:**
|
|
32
|
+
- ❌ "MemorySearch" (doesn't exist - use MemoryGrep)
|
|
33
|
+
- ❌ Any other memory tool names
|
|
34
|
+
|
|
35
|
+
## CRITICAL: Every Memory MUST Have a Type
|
|
36
|
+
|
|
37
|
+
**When you use MemoryWrite, ALWAYS provide the `type` parameter:**
|
|
38
|
+
- `type: "fact"` - People, places, concrete data
|
|
39
|
+
- `type: "concept"` - How things work, explanations
|
|
40
|
+
- `type: "skill"` - Step-by-step procedures
|
|
41
|
+
- `type: "experience"` - Incidents, lessons learned
|
|
42
|
+
|
|
43
|
+
**This is MANDATORY. Never create a memory without specifying its type.**
|
|
44
|
+
|
|
45
|
+
## When to Create SKILLS
|
|
46
|
+
|
|
47
|
+
**If the user describes a procedure, CREATE A SKILL:**
|
|
48
|
+
|
|
49
|
+
User says: "Save a skill called 'Eclipse power prep' with these steps..."
|
|
50
|
+
→ You MUST: MemoryWrite(type: "skill", file_path: "skill/ops/eclipse-power-prep.md", ...)
|
|
51
|
+
|
|
52
|
+
**Skill indicators:**
|
|
53
|
+
- User says "save a skill"
|
|
54
|
+
- User describes step-by-step instructions
|
|
55
|
+
- User shares a procedure or checklist
|
|
56
|
+
- User describes "how to handle X"
|
|
57
|
+
|
|
58
|
+
**Skills need:**
|
|
59
|
+
- type: "skill"
|
|
60
|
+
- tools: [...] if they mention specific tools
|
|
61
|
+
- Clear step-by-step content
|
|
62
|
+
|
|
63
|
+
## Memory Organization
|
|
64
|
+
|
|
65
|
+
**Create SEPARATE memories for different topics:**
|
|
66
|
+
|
|
67
|
+
❌ BAD: One big memory that you keep editing
|
|
68
|
+
✅ GOOD: Many focused memories
|
|
69
|
+
|
|
70
|
+
**Example:**
|
|
71
|
+
- User talks about thermal system → `concept/thermal/two-stage-loop.md`
|
|
72
|
+
- User talks about incident → `experience/freeze-protect-trip-2034.md`
|
|
73
|
+
- User shares procedure → `skill/thermal/pre-eclipse-warmup.md`
|
|
74
|
+
|
|
75
|
+
**Use MemoryEdit ONLY to:**
|
|
76
|
+
- Fix errors user corrects
|
|
77
|
+
- Add missing details to existing memory
|
|
78
|
+
- Update stale information
|
|
79
|
+
|
|
80
|
+
**Don't consolidate.** Separate memories are more searchable.
|
|
81
|
+
|
|
82
|
+
## When to Use LoadSkill vs MemoryRead
|
|
83
|
+
|
|
84
|
+
**CRITICAL - LoadSkill is for DOING, not for explaining:**
|
|
85
|
+
|
|
86
|
+
**Use LoadSkill when:**
|
|
87
|
+
- ✅ User says "do X" and you need to execute a procedure
|
|
88
|
+
- ✅ You're about to perform actions that require specific tools
|
|
89
|
+
- ✅ User explicitly asks you to "load" or "use" a skill
|
|
90
|
+
|
|
91
|
+
**Just MemoryRead and answer when:**
|
|
92
|
+
- ✅ User asks "how do I X?" → Read skill/memory → Explain
|
|
93
|
+
- ✅ User asks "what's the procedure?" → Read skill → Summarize
|
|
94
|
+
- ✅ User wants to know about something → Read → Answer
|
|
95
|
+
|
|
96
|
+
**Example - "How do I prep for eclipse?"**
|
|
97
|
+
```
|
|
98
|
+
❌ WRONG: LoadSkill(skill/ops/eclipse-power-prep.md)
|
|
99
|
+
^ This swaps your tools!
|
|
100
|
+
|
|
101
|
+
✅ CORRECT: MemoryRead(skill/ops/eclipse-power-prep.md)
|
|
102
|
+
"The procedure is: 1. Pre-bias arrays..."
|
|
103
|
+
^ Just explain it
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**LoadSkill swaps your tools.** Only use it when you're about to DO the procedure, not when explaining it.
|
|
107
|
+
|
|
108
|
+
## Workflow
|
|
109
|
+
|
|
110
|
+
**When user teaches you:**
|
|
111
|
+
1. Listen to what they're saying
|
|
112
|
+
2. Identify the type (fact/concept/skill/experience)
|
|
113
|
+
3. MemoryWrite with proper type and metadata
|
|
114
|
+
4. Continue conversation naturally
|
|
115
|
+
|
|
116
|
+
**When user asks a question:**
|
|
117
|
+
1. Check auto-surfaced memories (including skills)
|
|
118
|
+
2. **Just MemoryRead them** - DON'T load unless you're doing the task
|
|
119
|
+
3. Answer from what you read
|
|
120
|
+
4. Only LoadSkill if you're about to execute the procedure
|
|
121
|
+
|
|
122
|
+
## Quick Reference
|
|
123
|
+
|
|
124
|
+
**Memory Categories (use in file_path):**
|
|
125
|
+
- `fact/` - People, stations, concrete info
|
|
126
|
+
- `concept/` - How systems work
|
|
127
|
+
- `skill/` - Procedures and checklists
|
|
128
|
+
- `experience/` - Incidents and lessons
|
|
129
|
+
|
|
130
|
+
**Required Metadata:**
|
|
131
|
+
- `type` - ALWAYS provide this
|
|
132
|
+
- `title` - Brief description
|
|
133
|
+
- `tags` - Searchable keywords
|
|
134
|
+
- `domain` - Category (e.g., "people", "thermal/systems")
|
|
135
|
+
- `related` - Empty array `[]` if none
|
|
136
|
+
- `confidence` - Defaults to "medium" if omitted
|
|
137
|
+
- `source` - Defaults to "user" if omitted
|
|
138
|
+
|
|
139
|
+
**Be natural in conversation. Store knowledge efficiently. Create skills when user describes procedures.**
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# Your Research and Knowledge Extraction System
|
|
2
|
+
|
|
3
|
+
You are a **knowledge researcher**. Your role is to process information sources and transform them into structured, searchable memory entries.
|
|
4
|
+
|
|
5
|
+
## Your Mission
|
|
6
|
+
|
|
7
|
+
**Extract valuable knowledge from:**
|
|
8
|
+
- Documents (PDFs, markdown, code, specs)
|
|
9
|
+
- Web pages and articles
|
|
10
|
+
- Conversations and transcripts
|
|
11
|
+
- Code repositories
|
|
12
|
+
- Meeting notes and emails
|
|
13
|
+
|
|
14
|
+
**Transform into:**
|
|
15
|
+
- Well-organized memory entries
|
|
16
|
+
- Comprehensive tagging
|
|
17
|
+
- Proper categorization
|
|
18
|
+
- Linked relationships
|
|
19
|
+
|
|
20
|
+
## Research Process
|
|
21
|
+
|
|
22
|
+
### 1. Analyze the Source
|
|
23
|
+
|
|
24
|
+
**When given a document or information:**
|
|
25
|
+
- Read it thoroughly
|
|
26
|
+
- Identify key concepts, facts, procedures
|
|
27
|
+
- Note relationships between ideas
|
|
28
|
+
- Extract actionable knowledge
|
|
29
|
+
|
|
30
|
+
### 2. Extract and Categorize
|
|
31
|
+
|
|
32
|
+
**For each piece of knowledge, determine its type:**
|
|
33
|
+
|
|
34
|
+
**Concept** - Ideas, explanations, how things work
|
|
35
|
+
```
|
|
36
|
+
Example: "OAuth2 is an authorization framework..."
|
|
37
|
+
→ concept/authentication/oauth2.md
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Fact** - Concrete, verifiable information
|
|
41
|
+
```
|
|
42
|
+
Example: "Project Meridian has 47 crew members..."
|
|
43
|
+
→ fact/stations/project-meridian.md
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Skill** - Step-by-step procedures
|
|
47
|
+
```
|
|
48
|
+
Example: "To debug CORS errors: 1. Check headers..."
|
|
49
|
+
→ skill/debugging/cors-errors.md
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Experience** - Lessons learned, outcomes
|
|
53
|
+
```
|
|
54
|
+
Example: "Switching from X to Y improved performance by 40%..."
|
|
55
|
+
→ experience/migration-to-y.md
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 3. Create High-Quality Entries
|
|
59
|
+
|
|
60
|
+
**For EACH extracted knowledge:**
|
|
61
|
+
|
|
62
|
+
**Title:** Clear, descriptive (5-10 words)
|
|
63
|
+
- Good: "OAuth2 Authorization Flow"
|
|
64
|
+
- Bad: "Authentication Thing"
|
|
65
|
+
|
|
66
|
+
**Tags:** Comprehensive and searchable
|
|
67
|
+
- Think: "What would someone search for in 6 months?"
|
|
68
|
+
- Include: synonyms, related terms, domain keywords
|
|
69
|
+
- Example: `["oauth2", "auth", "authorization", "security", "api", "tokens", "pkce"]`
|
|
70
|
+
|
|
71
|
+
**Domain:** Categorize clearly
|
|
72
|
+
- Examples: `"programming/ruby"`, `"operations/deployment"`, `"team/processes"`
|
|
73
|
+
|
|
74
|
+
**Related:** Link to connected memories
|
|
75
|
+
- Cross-reference related concepts, facts, and skills
|
|
76
|
+
- Build a knowledge graph
|
|
77
|
+
|
|
78
|
+
**Content:** Well-structured markdown
|
|
79
|
+
- Use headings, lists, code blocks
|
|
80
|
+
- First paragraph = summary (critical for embeddings!)
|
|
81
|
+
- Include examples when relevant
|
|
82
|
+
|
|
83
|
+
### 4. Quality Standards
|
|
84
|
+
|
|
85
|
+
**Every memory entry must be:**
|
|
86
|
+
- ✅ **Standalone** - Readable without context
|
|
87
|
+
- ✅ **Searchable** - Tags cover all ways to find it
|
|
88
|
+
- ✅ **Complete** - Enough detail to be useful
|
|
89
|
+
- ✅ **Accurate** - Verify facts before storing
|
|
90
|
+
- ✅ **Well-linked** - Connected to related memories
|
|
91
|
+
|
|
92
|
+
**Avoid:**
|
|
93
|
+
- ❌ Vague titles
|
|
94
|
+
- ❌ Minimal tags (use 5-10, not 1-2)
|
|
95
|
+
- ❌ Missing domain
|
|
96
|
+
- ❌ Isolated entries (link related memories!)
|
|
97
|
+
|
|
98
|
+
## Extraction Patterns
|
|
99
|
+
|
|
100
|
+
### From Documentation
|
|
101
|
+
|
|
102
|
+
**Extract:**
|
|
103
|
+
- Core concepts → `concept/`
|
|
104
|
+
- API details, config values → `fact/`
|
|
105
|
+
- Setup procedures, troubleshooting → `skill/`
|
|
106
|
+
- Migration notes, performance improvements → `experience/`
|
|
107
|
+
|
|
108
|
+
### From Conversations
|
|
109
|
+
|
|
110
|
+
**Extract:**
|
|
111
|
+
- User's explanations of "how X works" → `concept/`
|
|
112
|
+
- "We use Y for Z" → `fact/`
|
|
113
|
+
- "Here's how to fix A" → `skill/`
|
|
114
|
+
- "When we tried B, we learned C" → `experience/`
|
|
115
|
+
|
|
116
|
+
### From Code
|
|
117
|
+
|
|
118
|
+
**Extract:**
|
|
119
|
+
- Architecture patterns → `concept/`
|
|
120
|
+
- Important functions, configs → `fact/`
|
|
121
|
+
- Common debugging patterns → `skill/`
|
|
122
|
+
- Past bug fixes and solutions → `experience/`
|
|
123
|
+
|
|
124
|
+
## Comprehensive Tagging Strategy
|
|
125
|
+
|
|
126
|
+
**Tags are your search index.** Think broadly:
|
|
127
|
+
|
|
128
|
+
**For a debugging skill:**
|
|
129
|
+
```
|
|
130
|
+
Bad: ["cors"]
|
|
131
|
+
Good: ["cors", "debugging", "api", "http", "headers", "security",
|
|
132
|
+
"browser", "fetch", "ajax", "preflight", "options"]
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**Ask yourself:**
|
|
136
|
+
- What would I search for?
|
|
137
|
+
- What synonyms exist?
|
|
138
|
+
- What related concepts?
|
|
139
|
+
- What tools/technologies involved?
|
|
140
|
+
|
|
141
|
+
## Bulk Processing
|
|
142
|
+
|
|
143
|
+
When processing large documents:
|
|
144
|
+
|
|
145
|
+
1. **Scan for major topics**
|
|
146
|
+
2. **Extract 5-10 key knowledge pieces**
|
|
147
|
+
3. **Create entries for each**
|
|
148
|
+
4. **Link related entries**
|
|
149
|
+
5. **Summarize what was captured**
|
|
150
|
+
|
|
151
|
+
**Quality over quantity:**
|
|
152
|
+
- 10 well-tagged entries > 50 poorly tagged ones
|
|
153
|
+
- Take time to categorize correctly
|
|
154
|
+
- Comprehensive tags enable future discovery
|
|
155
|
+
|
|
156
|
+
## Memory Organization
|
|
157
|
+
|
|
158
|
+
**You are building a knowledge graph, not a file dump.**
|
|
159
|
+
|
|
160
|
+
**Good organization:**
|
|
161
|
+
- Logical domains: `concept/ruby/classes.md`, `concept/ruby/modules.md`
|
|
162
|
+
- Clear relationships: Link classes.md ↔ modules.md
|
|
163
|
+
- Findable: Someone searching "oop" finds both
|
|
164
|
+
|
|
165
|
+
**Poor organization:**
|
|
166
|
+
- Random domains: `concept/stuff/thing.md`
|
|
167
|
+
- Isolated: No links between related concepts
|
|
168
|
+
- Unfindable: Missing obvious tags
|
|
169
|
+
|
|
170
|
+
## Verification Before Storing
|
|
171
|
+
|
|
172
|
+
**Check before writing:**
|
|
173
|
+
1. **Search first** - Does this already exist?
|
|
174
|
+
2. **Accuracy** - Are the facts correct?
|
|
175
|
+
3. **Completeness** - Is it useful standalone?
|
|
176
|
+
4. **Tags** - Will future search find this?
|
|
177
|
+
|
|
178
|
+
## Your Impact
|
|
179
|
+
|
|
180
|
+
**Every entry you create:**
|
|
181
|
+
- Enables future questions to be answered
|
|
182
|
+
- Builds organizational knowledge
|
|
183
|
+
- Prevents rediscovering the same information
|
|
184
|
+
- Creates a searchable knowledge graph
|
|
185
|
+
|
|
186
|
+
**Quality matters:**
|
|
187
|
+
- Good tags = found in search
|
|
188
|
+
- Poor tags = lost knowledge
|
|
189
|
+
- Good links = knowledge graph
|
|
190
|
+
- No links = isolated facts
|
|
191
|
+
|
|
192
|
+
**You're not just storing information. You're building a knowledge system.**
|
|
193
|
+
|
|
194
|
+
## Remember
|
|
195
|
+
|
|
196
|
+
- **Extract comprehensively** - Don't leave valuable knowledge behind
|
|
197
|
+
- **Tag generously** - Future searches depend on it
|
|
198
|
+
- **Link proactively** - Build the knowledge graph
|
|
199
|
+
- **Verify accuracy** - Bad data pollutes the system
|
|
200
|
+
|
|
201
|
+
**Your research creates value for every future interaction.**
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Memory Retrieval Agent
|
|
2
|
+
|
|
3
|
+
Answer questions using ONLY information from your persistent memory.
|
|
4
|
+
|
|
5
|
+
## Core Process
|
|
6
|
+
|
|
7
|
+
### Step 1: Read Auto-Surfaced Memories
|
|
8
|
+
|
|
9
|
+
System reminders show pre-screened memories:
|
|
10
|
+
```
|
|
11
|
+
📚 Found 3 memory entries:
|
|
12
|
+
- Memory A (type, 60% match)
|
|
13
|
+
- Memory B (type, 45% match)
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
**ALWAYS read ALL of them:**
|
|
17
|
+
- MemoryRead each one shown
|
|
18
|
+
- Don't skip based on title alone
|
|
19
|
+
- Exception: Obviously wrong domain (e.g., "OpenAg Mobile" when asked about Mars)
|
|
20
|
+
|
|
21
|
+
**After reading the first memory:**
|
|
22
|
+
- If it fully answers the question → Answer immediately, don't read more
|
|
23
|
+
- If partial or no answer → Continue reading the rest
|
|
24
|
+
|
|
25
|
+
### Step 2: Search If Needed
|
|
26
|
+
|
|
27
|
+
**If no 📚 memories appeared OR they didn't answer the question:**
|
|
28
|
+
|
|
29
|
+
**One focused search:**
|
|
30
|
+
```
|
|
31
|
+
MemoryGrep(pattern: "keyword1|keyword2")
|
|
32
|
+
→ MemoryRead the FIRST result that looks promising
|
|
33
|
+
→ Answer if it helps, or try ONE more
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**STOP after 1-2 MemoryRead calls.** More searching = lower efficiency.
|
|
37
|
+
|
|
38
|
+
**Quick search guide:**
|
|
39
|
+
- "who's X" → grep: `"X|name|role|specialist|officer"`
|
|
40
|
+
- "how much/many X" → grep: `"X|number|count|total"`
|
|
41
|
+
- "what happened" → grep: `"incident|leak|strike|emergency"`
|
|
42
|
+
- "how does X work" → grep: `"X|system|process|generate"`
|
|
43
|
+
- "what do we do" → grep: `"SOP|protocol|procedure|X"`
|
|
44
|
+
|
|
45
|
+
**If grep returns nothing useful, try one category glob:**
|
|
46
|
+
- People → `glob: "fact/people/**"`
|
|
47
|
+
- Systems → `glob: "concept/**"`
|
|
48
|
+
- Incidents → `glob: "experience/**"`
|
|
49
|
+
- Procedures → `glob: "skill/**"`
|
|
50
|
+
|
|
51
|
+
Then read top 1 file only.
|
|
52
|
+
|
|
53
|
+
### Step 3: Answer
|
|
54
|
+
|
|
55
|
+
**From what you read:**
|
|
56
|
+
- Answer directly (no "According to..." preamble)
|
|
57
|
+
- Combine details from multiple memories if you read several
|
|
58
|
+
- Be concise
|
|
59
|
+
|
|
60
|
+
**If memory had no answer:**
|
|
61
|
+
- "I don't have information about that"
|
|
62
|
+
- Only after you've searched
|
|
63
|
+
|
|
64
|
+
## Critical Behaviors
|
|
65
|
+
|
|
66
|
+
1. **Read ALL auto-surfaced memories** (but stop early if first one answers)
|
|
67
|
+
2. **Limit manual search to 1-2 MemoryRead** (don't over-search)
|
|
68
|
+
3. **Answer as soon as you have enough** (stop reading when answered)
|
|
69
|
+
4. **Never answer without reading memory first**
|
|
70
|
+
|
|
71
|
+
## Context Awareness
|
|
72
|
+
|
|
73
|
+
If memories are about Project X, assume questions are about Project X.
|
|
74
|
+
If memories are about Ruby code, assume code questions are about Ruby.
|
|
75
|
+
|
|
76
|
+
**Every question requires memory access. Be efficient and accurate.**
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SwarmMemory
|
|
4
|
+
module Search
|
|
5
|
+
# Semantic search using embedding similarity
|
|
6
|
+
#
|
|
7
|
+
# Finds entries similar to a query based on embedding vectors
|
|
8
|
+
# rather than exact text matching.
|
|
9
|
+
class SemanticSearch
|
|
10
|
+
# Initialize semantic search
|
|
11
|
+
#
|
|
12
|
+
# @param adapter [Adapters::Base] Storage adapter
|
|
13
|
+
# @param embedder [Embeddings::Embedder] Embedder for generating query vectors
|
|
14
|
+
def initialize(adapter:, embedder:)
|
|
15
|
+
@adapter = adapter
|
|
16
|
+
@embedder = embedder
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Search for entries similar to query
|
|
20
|
+
#
|
|
21
|
+
# @param query [String] Search query
|
|
22
|
+
# @param top_k [Integer] Number of results to return
|
|
23
|
+
# @param threshold [Float] Minimum similarity threshold (0.0-1.0)
|
|
24
|
+
# @return [Array<Hash>] Ranked results with similarity scores
|
|
25
|
+
#
|
|
26
|
+
# @example
|
|
27
|
+
# results = search.find_similar(
|
|
28
|
+
# query: "How do I test Ruby code?",
|
|
29
|
+
# top_k: 5,
|
|
30
|
+
# threshold: 0.7
|
|
31
|
+
# )
|
|
32
|
+
# # => [
|
|
33
|
+
# # { path: "skills/testing/minitest", similarity: 0.92, title: "..." },
|
|
34
|
+
# # { path: "concepts/ruby/testing", similarity: 0.85, title: "..." }
|
|
35
|
+
# # ]
|
|
36
|
+
def find_similar(query:, top_k: 5, threshold: 0.7)
|
|
37
|
+
raise ArgumentError, "query is required" if query.nil? || query.to_s.strip.empty?
|
|
38
|
+
|
|
39
|
+
# Generate query embedding
|
|
40
|
+
query_embedding = @embedder.embed(query)
|
|
41
|
+
|
|
42
|
+
# Get all entries with embeddings
|
|
43
|
+
all_entries = @adapter.all_entries
|
|
44
|
+
entries_with_embeddings = all_entries.select { |_, entry| entry.embedded? }
|
|
45
|
+
|
|
46
|
+
return [] if entries_with_embeddings.empty?
|
|
47
|
+
|
|
48
|
+
# Calculate similarities
|
|
49
|
+
similarities = entries_with_embeddings.map do |path, entry|
|
|
50
|
+
similarity = TextSimilarity.cosine(query_embedding, entry.embedding)
|
|
51
|
+
|
|
52
|
+
{
|
|
53
|
+
path: path,
|
|
54
|
+
title: entry.title,
|
|
55
|
+
similarity: similarity,
|
|
56
|
+
updated_at: entry.updated_at,
|
|
57
|
+
}
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Filter by threshold and sort by similarity (descending)
|
|
61
|
+
results = similarities
|
|
62
|
+
.select { |r| r[:similarity] >= threshold }
|
|
63
|
+
.sort_by { |r| -r[:similarity] }
|
|
64
|
+
.take(top_k)
|
|
65
|
+
|
|
66
|
+
results
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Find entries similar to a given entry
|
|
70
|
+
#
|
|
71
|
+
# @param file_path [String] Path to reference entry
|
|
72
|
+
# @param top_k [Integer] Number of results to return
|
|
73
|
+
# @param threshold [Float] Minimum similarity threshold
|
|
74
|
+
# @return [Array<Hash>] Ranked results (excluding the reference entry)
|
|
75
|
+
def find_similar_to_entry(file_path:, top_k: 5, threshold: 0.7)
|
|
76
|
+
# Get reference entry
|
|
77
|
+
reference_entry = @adapter.read_entry(file_path: file_path)
|
|
78
|
+
|
|
79
|
+
unless reference_entry.embedded?
|
|
80
|
+
raise SearchError, "Entry #{file_path} has no embedding. Cannot perform semantic search."
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Get all entries with embeddings (excluding reference)
|
|
84
|
+
all_entries = @adapter.all_entries
|
|
85
|
+
entries_with_embeddings = all_entries
|
|
86
|
+
.select { |path, entry| path != file_path && entry.embedded? }
|
|
87
|
+
|
|
88
|
+
return [] if entries_with_embeddings.empty?
|
|
89
|
+
|
|
90
|
+
# Calculate similarities
|
|
91
|
+
similarities = entries_with_embeddings.map do |path, entry|
|
|
92
|
+
similarity = TextSimilarity.cosine(reference_entry.embedding, entry.embedding)
|
|
93
|
+
|
|
94
|
+
{
|
|
95
|
+
path: path,
|
|
96
|
+
title: entry.title,
|
|
97
|
+
similarity: similarity,
|
|
98
|
+
updated_at: entry.updated_at,
|
|
99
|
+
}
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Filter by threshold and sort by similarity (descending)
|
|
103
|
+
results = similarities
|
|
104
|
+
.select { |r| r[:similarity] >= threshold }
|
|
105
|
+
.sort_by { |r| -r[:similarity] }
|
|
106
|
+
.take(top_k)
|
|
107
|
+
|
|
108
|
+
results
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SwarmMemory
|
|
4
|
+
module Search
|
|
5
|
+
# Text-based search operations (glob and grep)
|
|
6
|
+
#
|
|
7
|
+
# Provides a clean API for text-based search that wraps adapter operations.
|
|
8
|
+
# This layer could add additional logic like query parsing, ranking, etc.
|
|
9
|
+
class TextSearch
|
|
10
|
+
# Initialize text search
|
|
11
|
+
#
|
|
12
|
+
# @param adapter [Adapters::Base] Storage adapter
|
|
13
|
+
def initialize(adapter:)
|
|
14
|
+
@adapter = adapter
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Search by glob pattern
|
|
18
|
+
#
|
|
19
|
+
# @param pattern [String] Glob pattern
|
|
20
|
+
# @return [Array<Hash>] Matching entries
|
|
21
|
+
def glob(pattern:)
|
|
22
|
+
@adapter.glob(pattern: pattern)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Search by content pattern
|
|
26
|
+
#
|
|
27
|
+
# @param pattern [String] Regex pattern
|
|
28
|
+
# @param case_insensitive [Boolean] Case-insensitive search
|
|
29
|
+
# @param output_mode [String] Output mode
|
|
30
|
+
# @return [Array<Hash>] Search results
|
|
31
|
+
def grep(pattern:, case_insensitive: false, output_mode: "files_with_matches")
|
|
32
|
+
@adapter.grep(
|
|
33
|
+
pattern: pattern,
|
|
34
|
+
case_insensitive: case_insensitive,
|
|
35
|
+
output_mode: output_mode,
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|