@vuau/agent-memory 0.1.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.
- package/LICENSE +21 -0
- package/README.md +156 -0
- package/README.vi.md +145 -0
- package/bin/cli.ts +111 -0
- package/docs/ARCHITECTURE.md +285 -0
- package/docs/ARCHITECTURE.vi.md +285 -0
- package/docs/RESEARCH.md +216 -0
- package/docs/RESEARCH.vi.md +216 -0
- package/index.ts +11 -0
- package/package.json +45 -0
- package/src/core/doctor.ts +86 -0
- package/src/core/index.ts +5 -0
- package/src/core/memory.ts +86 -0
- package/src/core/scaffold.ts +124 -0
- package/src/core/tasks.ts +77 -0
- package/src/core/types.ts +30 -0
- package/src/opencode/plugin.ts +97 -0
- package/templates/AGENTS.md +44 -0
- package/templates/MEMORY.md +17 -0
- package/templates/TASKS.md +14 -0
- package/templates/copilot-instructions.md +30 -0
- package/templates/spec/.gitkeep +0 -0
package/docs/RESEARCH.md
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# Research: AI Memory Solutions — Tools Tried & Why File-Based Won
|
|
2
|
+
|
|
3
|
+
## The Problem
|
|
4
|
+
|
|
5
|
+
AI assistants (OpenCode, Copilot, Cursor, Windsurf) lose context between sessions. They need:
|
|
6
|
+
- ✅ Local-first (no API keys)
|
|
7
|
+
- ✅ Cross-IDE (OpenCode, VS Code, Windsurf, Antigravity)
|
|
8
|
+
- ✅ Cross-platform (Host + VM, Windows 11 + Linux)
|
|
9
|
+
- ✅ Persistent memory for decisions
|
|
10
|
+
- ✅ Low token overhead
|
|
11
|
+
- ✅ Reliable retrieval
|
|
12
|
+
|
|
13
|
+
## Tools Evaluated
|
|
14
|
+
|
|
15
|
+
| Tool | Auto-capture | VM compatible | Blocker | Status |
|
|
16
|
+
|------|--------------|---------------|---------|--------|
|
|
17
|
+
| **qmd** | ❌ (search only) | ❌ | better-sqlite3 needs Visual Studio Build Tools; HuggingFace blocked | ❌ Failed |
|
|
18
|
+
| **memsearch** | ✅ (daemon hooks) | ❌ | milvus-lite has no Windows wheels; HuggingFace blocked | ❌ Failed |
|
|
19
|
+
| **mem0** | ✅ (hooks) | ❌ | Requires OpenAI API key or HuggingFace models | ❌ Failed |
|
|
20
|
+
| **memories.sh** | ✅ (MCP) | ✅ | Auto-generates 10+ IDE config files (bloats repo) | ⚠️ Rejected |
|
|
21
|
+
| **codemem** | ❌ | ❌ | Flaky (unreliable save/recall) | ⚠️ Rejected |
|
|
22
|
+
| **OpenCode plugin** | ✅ (lifecycle hooks) | ✅ | None | ✅ **Viable** |
|
|
23
|
+
| **GitHub Copilot native** | Partial (skills) | ✅ | No lifecycle hooks; no direct session integration | ⚠️ Limited |
|
|
24
|
+
| **File-based + rules** | Manual (via rules) | ✅ | None | ✅ **CHOSEN** |
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Why Each Failed
|
|
29
|
+
|
|
30
|
+
### qmd
|
|
31
|
+
**Blocker**: `better-sqlite3` native module requires Visual Studio Build Tools to compile on Windows. HuggingFace is also blocked in many environments.
|
|
32
|
+
**Impact**: Cannot run on VM Windows 11 → breaks cross-platform requirement.
|
|
33
|
+
|
|
34
|
+
### memsearch
|
|
35
|
+
**Blockers**:
|
|
36
|
+
1. `milvus-lite` has no pre-built wheels for Windows → must compile from source
|
|
37
|
+
2. HuggingFace models blocked in isolated environments
|
|
38
|
+
3. **Context Blindness**: Auto-capture can't link user command ("remember this info") to 10-line analysis from previous turn → writes "No context provided" error
|
|
39
|
+
4. **Context Bloat**: Falls back to `memory_transcript`, pulling 19 old tool calls into context = **47,389 tokens (24% of context budget)**
|
|
40
|
+
|
|
41
|
+
**Impact**: Unreliable on Windows + VM. Token cost makes it unusable for real work.
|
|
42
|
+
|
|
43
|
+
### mem0
|
|
44
|
+
**Blocker**: Requires OpenAI API key or HuggingFace (both violate "local-first, no API key" requirement).
|
|
45
|
+
**Impact**: Not viable for local-first requirement.
|
|
46
|
+
|
|
47
|
+
### memories.sh
|
|
48
|
+
**Reevaluation**:
|
|
49
|
+
- ✅ CLI tool (good)
|
|
50
|
+
- ✅ Local-first (good)
|
|
51
|
+
- ✅ MCP support (cross-IDE capable)
|
|
52
|
+
- ✅ Clear memory fragmentation (Session, Semantic, Episodic, Procedural)
|
|
53
|
+
- ❌ **Auto-generates 10+ config files per IDE** (`.memories.sh` configs for Zsh, Bash, Fish, Zed, Helix, Neovim, etc.)
|
|
54
|
+
- ❌ Conflicts with "lightweight, centralized control" goal
|
|
55
|
+
|
|
56
|
+
**Decision**: Violates architecture principle of keeping configuration minimal and in one place. Repository becomes cluttered.
|
|
57
|
+
|
|
58
|
+
### codemem
|
|
59
|
+
**Issue**: Flaky (sometimes saves, sometimes doesn't). No consistent retrieval.
|
|
60
|
+
**Impact**: Cannot be relied upon for critical decisions.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Why File-Based Won
|
|
65
|
+
|
|
66
|
+
### 1. **No Environmental Blockers**
|
|
67
|
+
- Plain text files work everywhere (Host, VM, Windows 11, Linux)
|
|
68
|
+
- No native modules, no HuggingFace, no build tools required
|
|
69
|
+
- ✅ Cross-platform by default
|
|
70
|
+
|
|
71
|
+
### 2. **Handles Context Blindness**
|
|
72
|
+
- **Problem with auto-capture**: System can't reliably link user intent ("remember this") to prior technical analysis
|
|
73
|
+
- **Solution**: Agents write memory **when they understand context**
|
|
74
|
+
- Agent just finished exploring codebase → writes 1-line decision
|
|
75
|
+
- User approved decision → agent appends to MEMORY.md
|
|
76
|
+
- Agent reads MEMORY.md before implementing → follows pointer to spec file
|
|
77
|
+
- Result: Context is always linked because agent is in session when writing
|
|
78
|
+
|
|
79
|
+
### 3. **Solves Context Bloat**
|
|
80
|
+
- Auto-capture tools fail gracefully → pull raw transcripts (~47k tokens)
|
|
81
|
+
- File-based stores curated 1-liners (~200 tokens)
|
|
82
|
+
- **66x cheaper per session**
|
|
83
|
+
|
|
84
|
+
### 4. **IDE Portability**
|
|
85
|
+
| IDE | Integration | Works Now |
|
|
86
|
+
|-----|-------------|-----------|
|
|
87
|
+
| OpenCode | Plugin hooks | ✅ Yes |
|
|
88
|
+
| GitHub Copilot | Reads `.github/copilot-instructions.md` | ✅ Yes |
|
|
89
|
+
| Cursor | Reads `.cursorrules` | ✅ Yes |
|
|
90
|
+
| Windsurf | Reads `.windsurfrules` | ✅ Yes |
|
|
91
|
+
| VS Code | Reads markdown in workspace | ✅ Yes |
|
|
92
|
+
|
|
93
|
+
No custom driver needed per IDE. Markdown is portable.
|
|
94
|
+
|
|
95
|
+
### 5. **Sharing & Sync**
|
|
96
|
+
- Lives in git repo → automatically shared via Git/Rsync/Dropbox
|
|
97
|
+
- Developers see decision history in commits
|
|
98
|
+
- Can be backed up, versioned, audited
|
|
99
|
+
- No external database to sync across machines
|
|
100
|
+
|
|
101
|
+
### 6. **Transparent & Auditable**
|
|
102
|
+
- Human-readable: can review MEMORY.md directly
|
|
103
|
+
- No "locked in SQLite/Vector DB" problem
|
|
104
|
+
- No export/import needed
|
|
105
|
+
- Git history shows who decided what and when
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Architecture: 4-Layer Design
|
|
110
|
+
|
|
111
|
+
### Why 4 Layers?
|
|
112
|
+
|
|
113
|
+
**Problem**: Automatic memory systems fail when:
|
|
114
|
+
1. They can't link user intent to prior context (blindness)
|
|
115
|
+
2. They generate massive output when fallback fails (bloat)
|
|
116
|
+
3. They aren't portable across environments
|
|
117
|
+
|
|
118
|
+
**Solution**: Explicit layers that separate concerns:
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
Layer 1: Router (AGENTS.md, ~100 lines)
|
|
122
|
+
├─ Critical rules + pointers only
|
|
123
|
+
└─ Every IDE reads this first
|
|
124
|
+
|
|
125
|
+
Layer 2: Memory (MEMORY.md, ~150 lines)
|
|
126
|
+
├─ Curated 1-line decisions
|
|
127
|
+
├─ Category headers with spec pointers
|
|
128
|
+
└─ Agent reads before implementing
|
|
129
|
+
|
|
130
|
+
Layer 3: Tasks (TASKS.md)
|
|
131
|
+
├─ Current work, in-progress, next steps
|
|
132
|
+
└─ Enables session continuity
|
|
133
|
+
|
|
134
|
+
Layer 4: Specs (spec/*.md, on-demand)
|
|
135
|
+
├─ Detailed patterns, examples
|
|
136
|
+
├─ Referenced by Layer 2
|
|
137
|
+
└─ Agent loads only when needed
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**Progressive Disclosure**: Agents read ~200 tokens initially, follow pointers on-demand. Same token cost regardless of project size.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Token Cost Comparison
|
|
145
|
+
|
|
146
|
+
### Session 1: Find Storybook Rules (memsearch)
|
|
147
|
+
```
|
|
148
|
+
memory_search query: 100 tokens
|
|
149
|
+
memory_get fails (file lock)
|
|
150
|
+
↓ fallback to memory_transcript
|
|
151
|
+
memory_transcript (19 calls): 47,389 tokens
|
|
152
|
+
TOTAL: 47,489 tokens
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Session 2: Find Storybook Rules (file-based)
|
|
156
|
+
```
|
|
157
|
+
Read MEMORY.md: 200 tokens
|
|
158
|
+
Follow pointer → spec file: 500 tokens
|
|
159
|
+
TOTAL: 700 tokens
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Ratio**: 66x cheaper with file-based approach.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Why "Manual" (Agent Rules) > "Automatic"
|
|
167
|
+
|
|
168
|
+
### Automatic Capture (memsearch, qmd, mem0)
|
|
169
|
+
- ❌ Context Blindness: Can't link decision to prior context
|
|
170
|
+
- ❌ Context Bloat: Fallback pulls massive raw data
|
|
171
|
+
- ❌ Platform Bloat: Needs dependencies (sqlite, milvus, HF)
|
|
172
|
+
- ✅ Zero manual effort
|
|
173
|
+
|
|
174
|
+
### Agent Rules (File-based)
|
|
175
|
+
- ✅ Context aware: Agent writes when they understand
|
|
176
|
+
- ✅ Curated: Only important decisions survive
|
|
177
|
+
- ✅ Portable: Works everywhere (no dependencies)
|
|
178
|
+
- ❌ Needs agent discipline (mitigated by plugin reminders)
|
|
179
|
+
|
|
180
|
+
**Verdict**: Quality + Portability > Automation for teams of 1-10.
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## Cross-IDE Reality Check
|
|
185
|
+
|
|
186
|
+
### ✅ What Works Now
|
|
187
|
+
- OpenCode: Plugin hooks + session lifecycle
|
|
188
|
+
- Copilot: Reads `.github/copilot-instructions.md` natively
|
|
189
|
+
- Cursor: Reads `.cursorrules`
|
|
190
|
+
- Windsurf: Reads `.windsurfrules`
|
|
191
|
+
- VS Code: Reads markdown files
|
|
192
|
+
|
|
193
|
+
### ⚠️ Limitations
|
|
194
|
+
- Copilot doesn't have lifecycle hooks (can't auto-remind to update tasks)
|
|
195
|
+
- Cursor/Windsurf limited to reading rules, not injecting context
|
|
196
|
+
- **Solution**: VSCode extension (Phase 3) to provide unified sidebar
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## Conclusion
|
|
201
|
+
|
|
202
|
+
**For teams 1-10 working on focused projects:**
|
|
203
|
+
|
|
204
|
+
File-based memory + agent rules beats every alternative because it:
|
|
205
|
+
1. Works on VM Windows 11 (no build tools, no native modules)
|
|
206
|
+
2. Doesn't bloat token budget (700 vs 47k tokens)
|
|
207
|
+
3. Works with all IDEs without custom drivers
|
|
208
|
+
4. Shares naturally via git (Host ↔ VM ↔ Team)
|
|
209
|
+
5. Is transparent and auditable
|
|
210
|
+
|
|
211
|
+
**Automated memory capture fails** because:
|
|
212
|
+
- Context Blindness: Can't reliably link user intent to prior analysis
|
|
213
|
+
- Context Bloat: Fallback to raw transcripts costs 47k+ tokens
|
|
214
|
+
- Platform bloat: Requires dependencies that don't compile on Windows/VM
|
|
215
|
+
|
|
216
|
+
**The right trade-off**: Sacrifice full automation for reliability, portability, and cost.
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# Nghiên cứu: Giải pháp AI Memory — Các Tool Đã Thử & Tại Sao File-Based Thắng
|
|
2
|
+
|
|
3
|
+
## Bài toán
|
|
4
|
+
|
|
5
|
+
AI assistants (OpenCode, Copilot, Cursor, Windsurf) mất context giữa sessions. Chúng cần:
|
|
6
|
+
- ✅ Local-first (không API key)
|
|
7
|
+
- ✅ Cross-IDE (OpenCode, VS Code, Windsurf, Antigravity)
|
|
8
|
+
- ✅ Cross-platform (Host + VM, Windows 11 + Linux)
|
|
9
|
+
- ✅ Persistent memory cho decisions
|
|
10
|
+
- ✅ Token overhead thấp
|
|
11
|
+
- ✅ Retrieval đáng tin cậy
|
|
12
|
+
|
|
13
|
+
## Các Tool Đánh giá
|
|
14
|
+
|
|
15
|
+
| Tool | Auto-capture | VM compatible | Blocker | Trạng thái |
|
|
16
|
+
|------|--------------|---------------|---------|-----------|
|
|
17
|
+
| **qmd** | ❌ (search only) | ❌ | better-sqlite3 cần Visual Studio Build Tools; HuggingFace blocked | ❌ Failed |
|
|
18
|
+
| **memsearch** | ✅ (daemon) | ❌ | milvus-lite không có Windows wheels; HuggingFace blocked | ❌ Failed |
|
|
19
|
+
| **mem0** | ✅ (hooks) | ❌ | Cần OpenAI API key hoặc HuggingFace models | ❌ Failed |
|
|
20
|
+
| **memories.sh** | ✅ (MCP) | ✅ | Auto-generate 10+ IDE config files (bloat repo) | ⚠️ Rejected |
|
|
21
|
+
| **codemem** | ❌ | ❌ | Flaky (unreliable save/recall) | ⚠️ Rejected |
|
|
22
|
+
| **OpenCode plugin** | ✅ (lifecycle hooks) | ✅ | None | ✅ **Viable** |
|
|
23
|
+
| **GitHub Copilot native** | Partial (skills) | ✅ | Không có lifecycle hooks | ⚠️ Limited |
|
|
24
|
+
| **File-based + rules** | Manual (via rules) | ✅ | None | ✅ **CHOSEN** |
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Tại Sao Mỗi Cái Failed
|
|
29
|
+
|
|
30
|
+
### qmd
|
|
31
|
+
**Blocker**: `better-sqlite3` native module cần Visual Studio Build Tools để compile trên Windows. HuggingFace cũng bị blocked.
|
|
32
|
+
**Impact**: Không chạy được trên VM Windows 11 → phá vỡ cross-platform requirement.
|
|
33
|
+
|
|
34
|
+
### memsearch
|
|
35
|
+
**Blockers**:
|
|
36
|
+
1. `milvus-lite` không có pre-built wheels cho Windows → phải compile từ source
|
|
37
|
+
2. HuggingFace bị blocked trong isolated environments
|
|
38
|
+
3. **Context Blindness**: Auto-capture không thể liên kết lệnh user ("remember this info") với 10-line analysis từ turn trước → ghi "No context provided" error
|
|
39
|
+
4. **Context Bloat**: Fallback sang `memory_transcript`, kéo 19 old tool calls vào context = **47,389 tokens (24% of context budget)**
|
|
40
|
+
|
|
41
|
+
**Impact**: Unreliable trên Windows + VM. Token cost làm nó unusable.
|
|
42
|
+
|
|
43
|
+
### mem0
|
|
44
|
+
**Blocker**: Cần OpenAI API key hoặc HuggingFace (cả hai violate "local-first, no API key").
|
|
45
|
+
**Impact**: Không phù hợp với local-first requirement.
|
|
46
|
+
|
|
47
|
+
### memories.sh
|
|
48
|
+
**Đánh giá lại**:
|
|
49
|
+
- ✅ CLI tool (tốt)
|
|
50
|
+
- ✅ Local-first (tốt)
|
|
51
|
+
- ✅ MCP support (cross-IDE capable)
|
|
52
|
+
- ✅ Clear memory fragmentation (Session, Semantic, Episodic, Procedural)
|
|
53
|
+
- ❌ **Auto-generate 10+ config files per IDE** (`.memories.sh` cho Zsh, Bash, Fish, Zed, Helix, Neovim, etc.)
|
|
54
|
+
- ❌ Conflict với "lightweight, centralized control" goal
|
|
55
|
+
|
|
56
|
+
**Decision**: Violate architecture principle của keeping configuration minimal. Repo bị clutter.
|
|
57
|
+
|
|
58
|
+
### codemem
|
|
59
|
+
**Issue**: Flaky (thỉnh thoảng save được, thỉnh thoảng không). Retrieval không consistent.
|
|
60
|
+
**Impact**: Không thể rely on cho critical decisions.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Tại Sao File-Based Thắng
|
|
65
|
+
|
|
66
|
+
### 1. **Không Environmental Blockers**
|
|
67
|
+
- Plain text files hoạt động everywhere (Host, VM, Windows 11, Linux)
|
|
68
|
+
- Không native modules, không HuggingFace, không build tools
|
|
69
|
+
- ✅ Cross-platform by default
|
|
70
|
+
|
|
71
|
+
### 2. **Xử lý Context Blindness**
|
|
72
|
+
- **Vấn đề với auto-capture**: System không thể reliably link user intent ("remember this") tới prior technical analysis
|
|
73
|
+
- **Solution**: Agents ghi memory **khi họ hiểu context**
|
|
74
|
+
- Agent vừa explore xong codebase → ghi 1-line decision
|
|
75
|
+
- User approved decision → agent append vào MEMORY.md
|
|
76
|
+
- Agent read MEMORY.md trước implement → follow pointer tới spec file
|
|
77
|
+
- Result: Context always linked vì agent đang trong session khi ghi
|
|
78
|
+
|
|
79
|
+
### 3. **Giải quyết Context Bloat**
|
|
80
|
+
- Auto-capture tools fail gracefully → pull raw transcripts (~47k tokens)
|
|
81
|
+
- File-based store curated 1-liners (~200 tokens)
|
|
82
|
+
- **66x cheaper per session**
|
|
83
|
+
|
|
84
|
+
### 4. **IDE Portability**
|
|
85
|
+
| IDE | Integration | Works Now |
|
|
86
|
+
|-----|-------------|-----------|
|
|
87
|
+
| OpenCode | Plugin hooks | ✅ Yes |
|
|
88
|
+
| GitHub Copilot | Reads `.github/copilot-instructions.md` | ✅ Yes |
|
|
89
|
+
| Cursor | Reads `.cursorrules` | ✅ Yes |
|
|
90
|
+
| Windsurf | Reads `.windsurfrules` | ✅ Yes |
|
|
91
|
+
| VS Code | Reads markdown | ✅ Yes |
|
|
92
|
+
|
|
93
|
+
Không cần custom driver per IDE. Markdown portable.
|
|
94
|
+
|
|
95
|
+
### 5. **Sharing & Sync**
|
|
96
|
+
- Live trong git repo → auto-shared via Git/Rsync/Dropbox
|
|
97
|
+
- Developers thấy decision history trong commits
|
|
98
|
+
- Có thể backup, version, audit
|
|
99
|
+
- Không external database để sync qua machines
|
|
100
|
+
|
|
101
|
+
### 6. **Transparent & Auditable**
|
|
102
|
+
- Human-readable: có thể review MEMORY.md trực tiếp
|
|
103
|
+
- Không "locked in SQLite/Vector DB" problem
|
|
104
|
+
- Không cần export/import
|
|
105
|
+
- Git history show ai decided what và khi nào
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Kiến trúc: Thiết kế 4 Lớp
|
|
110
|
+
|
|
111
|
+
### Tại Sao 4 Lớp?
|
|
112
|
+
|
|
113
|
+
**Vấn đề**: Automatic memory systems fail khi:
|
|
114
|
+
1. Không thể link user intent tới prior context (blindness)
|
|
115
|
+
2. Generate massive output khi fallback fails (bloat)
|
|
116
|
+
3. Không portable qua environments
|
|
117
|
+
|
|
118
|
+
**Solution**: Explicit layers tách concerns:
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
Lớp 1: Router (AGENTS.md, ~100 dòng)
|
|
122
|
+
├─ Critical rules + pointers only
|
|
123
|
+
└─ Mọi IDE đọc first
|
|
124
|
+
|
|
125
|
+
Lớp 2: Memory (MEMORY.md, ~150 dòng)
|
|
126
|
+
├─ Curated 1-line decisions
|
|
127
|
+
├─ Category headers với spec pointers
|
|
128
|
+
└─ Agent đọc trước implement
|
|
129
|
+
|
|
130
|
+
Lớp 3: Tasks (TASKS.md)
|
|
131
|
+
├─ Current work, in-progress, next steps
|
|
132
|
+
└─ Enable session continuity
|
|
133
|
+
|
|
134
|
+
Lớp 4: Specs (spec/*.md, on-demand)
|
|
135
|
+
├─ Detailed patterns, examples
|
|
136
|
+
├─ Referenced bởi Lớp 2
|
|
137
|
+
└─ Agent load chỉ khi cần
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**Progressive Disclosure**: Agents đọc ~200 tokens initially, follow pointers on-demand. Same token cost regardless of project size.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## So sánh Token Cost
|
|
145
|
+
|
|
146
|
+
### Session 1: Tìm Storybook Rules (memsearch)
|
|
147
|
+
```
|
|
148
|
+
memory_search query: 100 tokens
|
|
149
|
+
memory_get fails (file lock)
|
|
150
|
+
↓ fallback to memory_transcript
|
|
151
|
+
memory_transcript (19 calls): 47,389 tokens
|
|
152
|
+
TOTAL: 47,489 tokens
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Session 2: Tìm Storybook Rules (file-based)
|
|
156
|
+
```
|
|
157
|
+
Read MEMORY.md: 200 tokens
|
|
158
|
+
Follow pointer → spec file: 500 tokens
|
|
159
|
+
TOTAL: 700 tokens
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Ratio**: 66x cheaper với file-based approach.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Tại Sao "Manual" (Agent Rules) > "Automatic"
|
|
167
|
+
|
|
168
|
+
### Automatic Capture (memsearch, qmd, mem0)
|
|
169
|
+
- ❌ Context Blindness: Không thể link decision tới prior context
|
|
170
|
+
- ❌ Context Bloat: Fallback kéo massive raw data
|
|
171
|
+
- ❌ Platform Bloat: Cần dependencies (sqlite, milvus, HF)
|
|
172
|
+
- ✅ Zero manual effort
|
|
173
|
+
|
|
174
|
+
### Agent Rules (File-based)
|
|
175
|
+
- ✅ Context aware: Agent ghi khi họ hiểu
|
|
176
|
+
- ✅ Curated: Chỉ important decisions sống sót
|
|
177
|
+
- ✅ Portable: Hoạt động everywhere (no dependencies)
|
|
178
|
+
- ❌ Cần agent discipline (mitigated bởi plugin reminders)
|
|
179
|
+
|
|
180
|
+
**Verdict**: Quality + Portability > Automation cho teams 1-10.
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## Cross-IDE Reality Check
|
|
185
|
+
|
|
186
|
+
### ✅ Gì hoạt động ngay
|
|
187
|
+
- OpenCode: Plugin hooks + session lifecycle
|
|
188
|
+
- Copilot: Read `.github/copilot-instructions.md` natively
|
|
189
|
+
- Cursor: Read `.cursorrules`
|
|
190
|
+
- Windsurf: Read `.windsurfrules`
|
|
191
|
+
- VS Code: Read markdown files
|
|
192
|
+
|
|
193
|
+
### ⚠️ Limitations
|
|
194
|
+
- Copilot không có lifecycle hooks (cannot auto-remind update tasks)
|
|
195
|
+
- Cursor/Windsurf limited tới read rules, không inject context
|
|
196
|
+
- **Solution**: VSCode extension (Phase 3) để provide unified sidebar
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## Kết luận
|
|
201
|
+
|
|
202
|
+
**Cho teams 1-10 làm việc trên focused projects:**
|
|
203
|
+
|
|
204
|
+
File-based memory + agent rules thắng mọi alternative vì nó:
|
|
205
|
+
1. Hoạt động trên VM Windows 11 (no build tools, no native modules)
|
|
206
|
+
2. Không bloat token budget (700 vs 47k tokens)
|
|
207
|
+
3. Hoạt động với all IDEs mà không cần custom drivers
|
|
208
|
+
4. Share naturally via git (Host ↔ VM ↔ Team)
|
|
209
|
+
5. Transparent và auditable
|
|
210
|
+
|
|
211
|
+
**Automated memory capture fails** vì:
|
|
212
|
+
- Context Blindness: Không thể reliably link user intent tới prior analysis
|
|
213
|
+
- Context Bloat: Fallback sang raw transcripts cost 47k+ tokens
|
|
214
|
+
- Platform bloat: Cần dependencies không compile trên Windows/VM
|
|
215
|
+
|
|
216
|
+
**Trade-off đúng**: Hy sinh full automation cho reliability, portability, và cost.
|
package/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @vuau/agent-memory
|
|
3
|
+
*
|
|
4
|
+
* Structured AI memory for codebases.
|
|
5
|
+
* OpenCode plugin entry point.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export { MemoryLifecyclePlugin as AgentMemoryPlugin } from "./src/opencode/plugin.ts"
|
|
9
|
+
|
|
10
|
+
// Re-export core for programmatic use
|
|
11
|
+
export * from "./src/core/index.ts"
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vuau/agent-memory",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Structured AI memory for codebases — OpenCode plugin + scaffolding CLI",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./index.ts"
|
|
9
|
+
},
|
|
10
|
+
"bin": {
|
|
11
|
+
"agent-memory": "./bin/cli.ts"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"index.ts",
|
|
15
|
+
"src/",
|
|
16
|
+
"templates/",
|
|
17
|
+
"bin/",
|
|
18
|
+
"docs/",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
21
|
+
"keywords": [
|
|
22
|
+
"opencode",
|
|
23
|
+
"opencode-plugin",
|
|
24
|
+
"agent-memory",
|
|
25
|
+
"ai-memory",
|
|
26
|
+
"agents",
|
|
27
|
+
"copilot",
|
|
28
|
+
"cursor",
|
|
29
|
+
"windsurf"
|
|
30
|
+
],
|
|
31
|
+
"author": "Au Pham <phamvuau@gmail.com>",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/vuau/agent-memory"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"@opencode-ai/plugin": ">=1.0.0"
|
|
39
|
+
},
|
|
40
|
+
"peerDependenciesMeta": {
|
|
41
|
+
"@opencode-ai/plugin": {
|
|
42
|
+
"optional": true
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Doctor — validate .agents/ structure integrity.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { existsSync, readFileSync } from "fs"
|
|
6
|
+
import { join } from "path"
|
|
7
|
+
import {
|
|
8
|
+
AGENTS_DIR,
|
|
9
|
+
SPEC_DIR,
|
|
10
|
+
MEMORY_FILE,
|
|
11
|
+
TASKS_FILE,
|
|
12
|
+
AGENTS_MD,
|
|
13
|
+
COPILOT_INSTRUCTIONS,
|
|
14
|
+
type DoctorResult,
|
|
15
|
+
type DoctorIssue,
|
|
16
|
+
} from "./types.ts"
|
|
17
|
+
|
|
18
|
+
export function doctor(projectDir: string): DoctorResult {
|
|
19
|
+
const issues: DoctorIssue[] = []
|
|
20
|
+
|
|
21
|
+
// Check required files
|
|
22
|
+
const required = [
|
|
23
|
+
{ file: AGENTS_MD, desc: "Root router file" },
|
|
24
|
+
{ file: MEMORY_FILE, desc: "Long-term memory" },
|
|
25
|
+
{ file: TASKS_FILE, desc: "Working memory" },
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
for (const { file, desc } of required) {
|
|
29
|
+
const filePath = join(projectDir, file)
|
|
30
|
+
if (!existsSync(filePath)) {
|
|
31
|
+
issues.push({ level: "error", file, message: `Missing ${desc}` })
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Check directories
|
|
36
|
+
for (const dir of [AGENTS_DIR, SPEC_DIR]) {
|
|
37
|
+
if (!existsSync(join(projectDir, dir))) {
|
|
38
|
+
issues.push({ level: "error", file: dir, message: "Directory missing" })
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Check optional files
|
|
43
|
+
const copilotPath = join(projectDir, COPILOT_INSTRUCTIONS)
|
|
44
|
+
if (!existsSync(copilotPath)) {
|
|
45
|
+
issues.push({
|
|
46
|
+
level: "warning",
|
|
47
|
+
file: COPILOT_INSTRUCTIONS,
|
|
48
|
+
message: "Copilot instructions missing — VSCode/GitHub Copilot won't have context",
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Validate AGENTS.md has documentation map
|
|
53
|
+
const agentsPath = join(projectDir, AGENTS_MD)
|
|
54
|
+
if (existsSync(agentsPath)) {
|
|
55
|
+
const content = readFileSync(agentsPath, "utf-8")
|
|
56
|
+
if (!content.includes(".agents/")) {
|
|
57
|
+
issues.push({
|
|
58
|
+
level: "warning",
|
|
59
|
+
file: AGENTS_MD,
|
|
60
|
+
message: "No references to .agents/ — agents may not find memory files",
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
if (content.split("\n").length > 150) {
|
|
64
|
+
issues.push({
|
|
65
|
+
level: "warning",
|
|
66
|
+
file: AGENTS_MD,
|
|
67
|
+
message: "Over 150 lines — consider keeping it concise as a router",
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Validate MEMORY.md line count
|
|
73
|
+
const memoryPath = join(projectDir, MEMORY_FILE)
|
|
74
|
+
if (existsSync(memoryPath)) {
|
|
75
|
+
const lines = readFileSync(memoryPath, "utf-8").split("\n").length
|
|
76
|
+
if (lines > 150) {
|
|
77
|
+
issues.push({
|
|
78
|
+
level: "warning",
|
|
79
|
+
file: MEMORY_FILE,
|
|
80
|
+
message: `${lines} lines — consider compressing or archiving old entries`,
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return { ok: issues.filter((i) => i.level === "error").length === 0, issues }
|
|
86
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { scaffold, type ScaffoldResult } from "./scaffold.ts"
|
|
2
|
+
export { appendMemory, readMemory, type MemoryEntry } from "./memory.ts"
|
|
3
|
+
export { readTasks, writeTasks, type TaskItem, type TaskStatus } from "./tasks.ts"
|
|
4
|
+
export { doctor } from "./doctor.ts"
|
|
5
|
+
export * from "./types.ts"
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory — read/append operations for MEMORY.md
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { existsSync, readFileSync, writeFileSync } from "fs"
|
|
6
|
+
import { join } from "path"
|
|
7
|
+
import { MEMORY_FILE } from "./types.ts"
|
|
8
|
+
|
|
9
|
+
export interface MemoryEntry {
|
|
10
|
+
date: string
|
|
11
|
+
content: string
|
|
12
|
+
category?: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Append a 1-line entry to MEMORY.md under a category.
|
|
17
|
+
* Creates category header if it doesn't exist.
|
|
18
|
+
*/
|
|
19
|
+
export function appendMemory(
|
|
20
|
+
projectDir: string,
|
|
21
|
+
entry: MemoryEntry
|
|
22
|
+
): void {
|
|
23
|
+
const filePath = join(projectDir, MEMORY_FILE)
|
|
24
|
+
if (!existsSync(filePath)) {
|
|
25
|
+
throw new Error(`${MEMORY_FILE} not found. Run 'agent-memory init' first.`)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const content = readFileSync(filePath, "utf-8")
|
|
29
|
+
const category = entry.category || "Decisions"
|
|
30
|
+
const line = `- ${entry.date}: ${entry.content}`
|
|
31
|
+
|
|
32
|
+
// Find category header
|
|
33
|
+
const categoryHeader = `## ${category}`
|
|
34
|
+
const headerIndex = content.indexOf(categoryHeader)
|
|
35
|
+
|
|
36
|
+
let updated: string
|
|
37
|
+
if (headerIndex === -1) {
|
|
38
|
+
// Append new category at end
|
|
39
|
+
updated = content.trimEnd() + `\n\n${categoryHeader}\n${line}\n`
|
|
40
|
+
} else {
|
|
41
|
+
// Find next ## header or end of file
|
|
42
|
+
const afterHeader = headerIndex + categoryHeader.length
|
|
43
|
+
const nextHeaderIndex = content.indexOf("\n## ", afterHeader)
|
|
44
|
+
const insertAt = nextHeaderIndex === -1 ? content.length : nextHeaderIndex
|
|
45
|
+
|
|
46
|
+
// Find last non-empty line in category
|
|
47
|
+
const categoryContent = content.slice(afterHeader, insertAt)
|
|
48
|
+
const lastLineEnd = afterHeader + categoryContent.trimEnd().length
|
|
49
|
+
|
|
50
|
+
updated =
|
|
51
|
+
content.slice(0, lastLineEnd) +
|
|
52
|
+
"\n" + line +
|
|
53
|
+
content.slice(lastLineEnd)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
writeFileSync(filePath, updated)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Read all entries from MEMORY.md, parsed by category.
|
|
61
|
+
*/
|
|
62
|
+
export function readMemory(
|
|
63
|
+
projectDir: string
|
|
64
|
+
): Record<string, string[]> {
|
|
65
|
+
const filePath = join(projectDir, MEMORY_FILE)
|
|
66
|
+
if (!existsSync(filePath)) return {}
|
|
67
|
+
|
|
68
|
+
const content = readFileSync(filePath, "utf-8")
|
|
69
|
+
const categories: Record<string, string[]> = {}
|
|
70
|
+
let currentCategory = "_uncategorized"
|
|
71
|
+
|
|
72
|
+
for (const line of content.split("\n")) {
|
|
73
|
+
const headerMatch = line.match(/^## (.+)/)
|
|
74
|
+
if (headerMatch) {
|
|
75
|
+
currentCategory = headerMatch[1]
|
|
76
|
+
categories[currentCategory] = categories[currentCategory] || []
|
|
77
|
+
continue
|
|
78
|
+
}
|
|
79
|
+
if (line.startsWith("- ") && currentCategory) {
|
|
80
|
+
categories[currentCategory] = categories[currentCategory] || []
|
|
81
|
+
categories[currentCategory].push(line.slice(2))
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return categories
|
|
86
|
+
}
|