@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.
@@ -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
+ }