opencodekit 0.18.0 → 0.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -1
- package/dist/template/.opencode/AGENTS.md +57 -0
- package/dist/template/.opencode/agent/scout.md +0 -37
- package/dist/template/.opencode/command/resume.md +1 -1
- package/dist/template/.opencode/command/status.md +7 -14
- package/dist/template/.opencode/memory.db +0 -0
- package/dist/template/.opencode/memory.db-shm +0 -0
- package/dist/template/.opencode/memory.db-wal +0 -0
- package/dist/template/.opencode/plugin/README.md +37 -25
- package/dist/template/.opencode/skill/memory-system/SKILL.md +103 -60
- package/dist/template/.opencode/skill/session-management/SKILL.md +22 -35
- package/package.json +1 -1
- package/dist/template/.opencode/plugin/compaction.ts +0 -190
package/dist/index.js
CHANGED
|
@@ -220,3 +220,60 @@ Files over ~500 lines become hard to maintain and review. Extract helpers, split
|
|
|
220
220
|
- Cite concrete file paths and line numbers for non-trivial claims
|
|
221
221
|
|
|
222
222
|
_Complexity is the enemy. Minimize moving parts._
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## Memory System
|
|
227
|
+
|
|
228
|
+
4-tier automated knowledge pipeline backed by SQLite + FTS5 (porter stemming).
|
|
229
|
+
|
|
230
|
+
**Pipeline:** messages → capture → distillations (TF-IDF) → observations (curator) → LTM injection (system.transform)
|
|
231
|
+
|
|
232
|
+
### Memory Tools
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
# Search observations (FTS5)
|
|
236
|
+
memory-search({ query: "auth" })
|
|
237
|
+
|
|
238
|
+
# Get full observation details
|
|
239
|
+
memory-get({ ids: "42,45" })
|
|
240
|
+
|
|
241
|
+
# Create observation
|
|
242
|
+
observation({ type: "decision", title: "Use JWT", narrative: "..." })
|
|
243
|
+
|
|
244
|
+
# Update memory file
|
|
245
|
+
memory-update({ file: "research/findings", content: "..." })
|
|
246
|
+
|
|
247
|
+
# Read memory file
|
|
248
|
+
memory-read({ file: "research/findings" })
|
|
249
|
+
|
|
250
|
+
# Admin operations
|
|
251
|
+
memory-admin({ operation: "status" })
|
|
252
|
+
memory-admin({ operation: "capture-stats" })
|
|
253
|
+
memory-admin({ operation: "distill-now" })
|
|
254
|
+
memory-admin({ operation: "curate-now" })
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Session Tools
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
# Search sessions by keyword
|
|
261
|
+
find_sessions({ query: "auth", limit: 5 })
|
|
262
|
+
|
|
263
|
+
# Read session messages
|
|
264
|
+
read_session({ session_id: "abc123" })
|
|
265
|
+
read_session({ session_id: "abc123", focus: "auth" })
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Directory Structure
|
|
269
|
+
|
|
270
|
+
```
|
|
271
|
+
.opencode/memory/
|
|
272
|
+
├── project/ # Tacit knowledge (auto-injected)
|
|
273
|
+
│ ├── user.md # User preferences
|
|
274
|
+
│ ├── tech-stack.md # Framework, constraints
|
|
275
|
+
│ └── gotchas.md # Footguns, warnings
|
|
276
|
+
├── research/ # Research notes
|
|
277
|
+
├── handoffs/ # Session handoffs
|
|
278
|
+
└── _templates/ # Document templates
|
|
279
|
+
```
|
|
@@ -47,43 +47,6 @@ Find trustworthy external references quickly and return concise, cited guidance.
|
|
|
47
47
|
- Cite every non-trivial claim
|
|
48
48
|
- Prefer high-signal synthesis over long dumps
|
|
49
49
|
|
|
50
|
-
## Source Quality Hierarchy
|
|
51
|
-
|
|
52
|
-
| Rank | Source Type | Tiebreaker |
|
|
53
|
-
| ---- | ----------------------------------------------------- | ---------------------------------------------- |
|
|
54
|
-
| 1 | Official docs/specifications/release notes | Use unless clearly outdated |
|
|
55
|
-
| 2 | Library/framework source code and maintained examples | Prefer recent commits |
|
|
56
|
-
| 3 | Maintainer-authored technical articles | Check date, prefer <1 year |
|
|
57
|
-
| 4 | Community blogs/posts | Use only when higher-ranked sources are absent |
|
|
58
|
-
|
|
59
|
-
## Output
|
|
60
|
-
|
|
61
|
-
- Summary (2-5 bullets)
|
|
62
|
-
- Recommended approach
|
|
63
|
-
- Sources
|
|
64
|
-
- Risks/tradeoffs
|
|
65
|
-
|
|
66
|
-
# Scout Agent
|
|
67
|
-
|
|
68
|
-
**Purpose**: Knowledge seeker — you find the signal in the noise of external information.
|
|
69
|
-
|
|
70
|
-
> _"Good research doesn't dump facts; it creates actionable clarity."_
|
|
71
|
-
|
|
72
|
-
## Identity
|
|
73
|
-
|
|
74
|
-
You are a read-only research agent. You output concise recommendations backed by verifiable sources only.
|
|
75
|
-
|
|
76
|
-
## Task
|
|
77
|
-
|
|
78
|
-
Find trustworthy external references quickly and return concise, cited guidance.
|
|
79
|
-
|
|
80
|
-
## Rules
|
|
81
|
-
|
|
82
|
-
- Never modify project files
|
|
83
|
-
- Never invent URLs; only use verified links
|
|
84
|
-
- Cite every non-trivial claim
|
|
85
|
-
- Prefer high-signal synthesis over long dumps
|
|
86
|
-
|
|
87
50
|
## Before You Scout
|
|
88
51
|
|
|
89
52
|
- **Verify memory first**: Always check memory-search before external research
|
|
@@ -40,12 +40,11 @@ skill({ name: "beads" });
|
|
|
40
40
|
|
|
41
41
|
## Available Tools
|
|
42
42
|
|
|
43
|
-
| Tool | Use When
|
|
44
|
-
| --------------- |
|
|
45
|
-
| `br` | Task status and stats
|
|
46
|
-
| `git` | Git state and history
|
|
47
|
-
| `
|
|
48
|
-
| `action-queue` | Pending approvals and ready tasks |
|
|
43
|
+
| Tool | Use When |
|
|
44
|
+
| --------------- | --------------------- |
|
|
45
|
+
| `br` | Task status and stats |
|
|
46
|
+
| `git` | Git state and history |
|
|
47
|
+
| `find_sessions` | Recent sessions |
|
|
49
48
|
|
|
50
49
|
## Phase 1: Gather State (Parallel)
|
|
51
50
|
|
|
@@ -64,8 +63,7 @@ git log --oneline -5
|
|
|
64
63
|
```
|
|
65
64
|
|
|
66
65
|
```typescript
|
|
67
|
-
|
|
68
|
-
action - queue({ op: "status" });
|
|
66
|
+
find_sessions({ query: "today", limit: 5 });
|
|
69
67
|
```
|
|
70
68
|
|
|
71
69
|
---
|
|
@@ -89,12 +87,7 @@ GIT
|
|
|
89
87
|
Recent: [from git log]
|
|
90
88
|
|
|
91
89
|
SESSIONS TODAY
|
|
92
|
-
[from
|
|
93
|
-
|
|
94
|
-
ACTION QUEUE
|
|
95
|
-
Pending approvals: [from action-queue pending_approvals]
|
|
96
|
-
Ready tasks: [from action-queue ready_tasks]
|
|
97
|
-
Idle workers: [from action-queue idle_workers]
|
|
90
|
+
[from find_sessions]
|
|
98
91
|
```
|
|
99
92
|
|
|
100
93
|
---
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -6,36 +6,48 @@ Plugins in this directory extend OpenCode with project-specific behavior and too
|
|
|
6
6
|
|
|
7
7
|
```text
|
|
8
8
|
plugin/
|
|
9
|
-
├── memory.ts #
|
|
10
|
-
├── sessions.ts # Session tools (
|
|
11
|
-
├── compaction.ts # Compaction-time context recovery injection
|
|
12
|
-
├── swarm-enforcer.ts # Beads workflow enforcement and reminders
|
|
13
|
-
├── skill-mcp.ts # Skill-scoped MCP bridge (skill_mcp tools)
|
|
9
|
+
├── memory.ts # 4-tier automated memory system (capture → distill → curate → inject)
|
|
10
|
+
├── sessions.ts # Session search tools (find/read)
|
|
14
11
|
├── copilot-auth.ts # GitHub Copilot provider/auth integration
|
|
15
|
-
├──
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
├── skill-mcp.ts # Skill-scoped MCP bridge (skill_mcp tools)
|
|
13
|
+
└── lib/
|
|
14
|
+
├── memory-tools.ts # 6 core memory tools (observation, search, get, read, update, timeline)
|
|
15
|
+
├── memory-admin-tools.ts # Admin tool (memory-admin: 9 operations)
|
|
16
|
+
├── memory-hooks.ts # All hooks (event, idle, transforms, compaction)
|
|
17
|
+
├── memory-helpers.ts # Constants, compaction utilities, formatting
|
|
18
|
+
├── memory-db.ts # Barrel re-export for db/ modules
|
|
19
|
+
├── capture.ts # message.part.updated → temporal_messages
|
|
20
|
+
├── distill.ts # TF-IDF extraction, key sentence selection
|
|
21
|
+
├── curator.ts # Pattern-based knowledge extraction
|
|
22
|
+
├── context.ts # Token budget enforcement via messages.transform
|
|
23
|
+
├── inject.ts # Relevance-scored LTM injection via system.transform
|
|
24
|
+
├── notify.ts # Cross-platform notification helpers
|
|
25
|
+
└── db/
|
|
26
|
+
├── types.ts # All types + MEMORY_CONFIG
|
|
27
|
+
├── schema.ts # SQL schema, migrations, DB singleton
|
|
28
|
+
├── observations.ts # Observation CRUD + FTS5 search
|
|
29
|
+
├── pipeline.ts # Temporal messages + distillations + relevance scoring
|
|
30
|
+
└── maintenance.ts # Memory files, FTS5, archiving, vacuum
|
|
19
31
|
```
|
|
20
32
|
|
|
21
33
|
## Plugin Responsibilities
|
|
22
34
|
|
|
23
35
|
- `memory.ts`
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
36
|
+
- 4-tier automated knowledge system: temporal_messages → distillations → observations → memory_files
|
|
37
|
+
- Captures messages automatically via `message.part.updated` events
|
|
38
|
+
- Distills sessions on idle (TF-IDF, key sentence extraction)
|
|
39
|
+
- Curates observations from distillations via pattern matching
|
|
40
|
+
- Injects relevant knowledge into system prompt (BM25 _ recency _ confidence scoring)
|
|
41
|
+
- Manages context window via messages.transform (token budget enforcement)
|
|
42
|
+
- Merges compaction logic (beads, handoffs, project memory, knowledge)
|
|
43
|
+
- Provides 7 tools: observation, memory-search, memory-get, memory-read, memory-update, memory-timeline, memory-admin
|
|
27
44
|
|
|
28
45
|
- `sessions.ts`
|
|
29
|
-
- Provides
|
|
30
|
-
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
|
|
35
|
-
- `swarm-enforcer.ts`
|
|
36
|
-
- Injects bead state and stage labels into system context
|
|
37
|
-
- Warns when implementation starts without a properly started bead
|
|
38
|
-
- Reminds to close/sync in-progress work on session idle
|
|
46
|
+
- Provides tools: `find_sessions`, `read_session`
|
|
47
|
+
- Direct SQLite access to OpenCode's session DB
|
|
48
|
+
- Multi-word AND search with relevance ranking
|
|
49
|
+
- 180-day time-bounded search
|
|
50
|
+
- Agentic `nextStep` guidance in results
|
|
39
51
|
|
|
40
52
|
- `skill-mcp.ts`
|
|
41
53
|
- Loads MCP configs from skills
|
|
@@ -48,9 +60,9 @@ plugin/
|
|
|
48
60
|
|
|
49
61
|
## Notes
|
|
50
62
|
|
|
51
|
-
-
|
|
52
|
-
- Keep plugin documentation aligned with actual files in this directory
|
|
53
|
-
- Prefer shared helpers in `lib/` over duplicated utilities across plugins
|
|
63
|
+
- OpenCode auto-discovers every `.ts` file in `plugin/` as a plugin — keep helper modules in `lib/`
|
|
64
|
+
- Keep plugin documentation aligned with actual files in this directory
|
|
65
|
+
- Prefer shared helpers in `lib/` over duplicated utilities across plugins
|
|
54
66
|
|
|
55
67
|
## References
|
|
56
68
|
|
|
@@ -5,7 +5,29 @@ description: Use when persisting learnings, loading previous context, or searchi
|
|
|
5
5
|
|
|
6
6
|
# Memory System Best Practices
|
|
7
7
|
|
|
8
|
-
Persistent context that survives across sessions. Uses **
|
|
8
|
+
Persistent context that survives across sessions. Uses **4-tier automated knowledge pipeline** backed by SQLite + FTS5.
|
|
9
|
+
|
|
10
|
+
## Architecture
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
message.part.updated → capture.ts → temporal_messages
|
|
14
|
+
↓ (session.idle, 10+ messages)
|
|
15
|
+
distill.ts → distillations (TF-IDF + key sentences)
|
|
16
|
+
↓ (session.idle)
|
|
17
|
+
curator.ts → observations (pattern-matched)
|
|
18
|
+
↓
|
|
19
|
+
system.transform ← inject.ts ← FTS5 search → scored + packed → system prompt
|
|
20
|
+
messages.transform ← context.ts → token budget enforcement
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### 4 Tiers
|
|
24
|
+
|
|
25
|
+
| Tier | Storage | Populated By | Purpose |
|
|
26
|
+
| ----------------- | ------------- | ------------------- | ------------------------------- |
|
|
27
|
+
| temporal_messages | SQLite | Automatic (capture) | Raw message text, 180-day TTL |
|
|
28
|
+
| distillations | SQLite + FTS5 | Automatic (idle) | TF-IDF compressed sessions |
|
|
29
|
+
| observations | SQLite + FTS5 | Manual + curator | Decisions, bugs, patterns, etc. |
|
|
30
|
+
| memory_files | SQLite | Manual | Static docs, handoffs, research |
|
|
9
31
|
|
|
10
32
|
## Core Principle
|
|
11
33
|
|
|
@@ -23,11 +45,11 @@ Always search memory first.
|
|
|
23
45
|
|
|
24
46
|
```typescript
|
|
25
47
|
// Search for relevant past work
|
|
26
|
-
|
|
27
|
-
|
|
48
|
+
memory - search({ query: "<task keywords>", limit: 5 });
|
|
49
|
+
memory - search({ query: "bugfix <component>", type: "observations" });
|
|
28
50
|
|
|
29
51
|
// Check recent handoffs
|
|
30
|
-
|
|
52
|
+
memory - search({ query: "handoff", type: "handoffs", limit: 3 });
|
|
31
53
|
```
|
|
32
54
|
|
|
33
55
|
**Why:** Past you already solved this. Don't rediscover.
|
|
@@ -38,14 +60,14 @@ Don't fetch full content until you know you need it.
|
|
|
38
60
|
|
|
39
61
|
```typescript
|
|
40
62
|
// 1. Search returns compact index (50-100 tokens per result)
|
|
41
|
-
const results =
|
|
63
|
+
const results = memory - search({ query: "auth patterns" });
|
|
42
64
|
// Returns: [{id: 42, title: "Auth bug fixed", ...}]
|
|
43
65
|
|
|
44
66
|
// 2. Fetch full details ONLY for relevant IDs
|
|
45
|
-
|
|
67
|
+
memory - get({ ids: "42,45" });
|
|
46
68
|
|
|
47
69
|
// 3. See what led to this decision
|
|
48
|
-
|
|
70
|
+
memory - timeline({ anchor_id: 42, depth_before: 3 });
|
|
49
71
|
```
|
|
50
72
|
|
|
51
73
|
**Why:** Prevents context bloat. High signal, low noise.
|
|
@@ -56,12 +78,13 @@ Create observations for anything non-obvious. Don't wait until the end.
|
|
|
56
78
|
|
|
57
79
|
```typescript
|
|
58
80
|
observation({
|
|
59
|
-
type: "pattern", // decision | bugfix | pattern | discovery | warning
|
|
81
|
+
type: "pattern", // decision | bugfix | pattern | discovery | warning | learning
|
|
60
82
|
title: "Brief description",
|
|
61
83
|
narrative: "Context and reasoning...",
|
|
62
84
|
facts: "key, facts, here",
|
|
63
85
|
concepts: "searchable, keywords",
|
|
64
86
|
files_modified: "src/file.ts",
|
|
87
|
+
source: "manual", // manual (default) | curator | imported
|
|
65
88
|
});
|
|
66
89
|
```
|
|
67
90
|
|
|
@@ -79,9 +102,10 @@ observation({
|
|
|
79
102
|
Document completion state for future you.
|
|
80
103
|
|
|
81
104
|
```typescript
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
105
|
+
memory -
|
|
106
|
+
update({
|
|
107
|
+
file: "handoffs/YYYY-MM-DD-task",
|
|
108
|
+
content: `## Completed
|
|
85
109
|
- X
|
|
86
110
|
|
|
87
111
|
## Blockers
|
|
@@ -89,8 +113,8 @@ memory_update({
|
|
|
89
113
|
|
|
90
114
|
## Next
|
|
91
115
|
- Z`,
|
|
92
|
-
|
|
93
|
-
});
|
|
116
|
+
mode: "append",
|
|
117
|
+
});
|
|
94
118
|
```
|
|
95
119
|
|
|
96
120
|
---
|
|
@@ -99,18 +123,18 @@ memory_update({
|
|
|
99
123
|
|
|
100
124
|
### memory-search (Start Here)
|
|
101
125
|
|
|
102
|
-
Fast FTS5 full-text search. Returns **compact index** for progressive disclosure.
|
|
126
|
+
Fast FTS5 full-text search with porter stemming. Returns **compact index** for progressive disclosure.
|
|
103
127
|
|
|
104
128
|
```typescript
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
129
|
+
memory - search({ query: "authentication" });
|
|
130
|
+
memory - search({ query: "bugfix", type: "observations", limit: 5 });
|
|
131
|
+
memory - search({ query: "session", type: "handoffs" });
|
|
132
|
+
memory - search({ query: "patterns", type: "all" }); // Search everything
|
|
109
133
|
```
|
|
110
134
|
|
|
111
135
|
**Search modes:**
|
|
112
136
|
|
|
113
|
-
- `observations` (default): Search SQLite with FTS5 ranking
|
|
137
|
+
- `observations` (default): Search SQLite with FTS5 BM25 ranking
|
|
114
138
|
- `handoffs`, `research`, `templates`: Search specific directories
|
|
115
139
|
- `beads`: Search .beads/artifacts
|
|
116
140
|
- `all`: Search everything
|
|
@@ -120,8 +144,8 @@ memory_search({ query: "patterns", type: "all" }); // Search everything
|
|
|
120
144
|
Fetch full observation details after identifying relevant IDs:
|
|
121
145
|
|
|
122
146
|
```typescript
|
|
123
|
-
|
|
124
|
-
|
|
147
|
+
memory - get({ ids: "42" }); // Single observation
|
|
148
|
+
memory - get({ ids: "1,5,10" }); // Multiple observations
|
|
125
149
|
```
|
|
126
150
|
|
|
127
151
|
### memory-timeline (Chronological Context)
|
|
@@ -129,7 +153,7 @@ memory_get({ ids: "1,5,10" }); // Multiple observations
|
|
|
129
153
|
See what happened before/after a specific observation:
|
|
130
154
|
|
|
131
155
|
```typescript
|
|
132
|
-
|
|
156
|
+
memory - timeline({ anchor_id: 42, depth_before: 5, depth_after: 5 });
|
|
133
157
|
```
|
|
134
158
|
|
|
135
159
|
### memory-read (Files)
|
|
@@ -137,9 +161,9 @@ memory_timeline({ anchor_id: 42, depth_before: 5, depth_after: 5 });
|
|
|
137
161
|
Load project files, handoffs, or templates:
|
|
138
162
|
|
|
139
163
|
```typescript
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
164
|
+
memory - read({ file: "project/gotchas" });
|
|
165
|
+
memory - read({ file: "handoffs/2024-01-20-phase-1" });
|
|
166
|
+
memory - read({ file: "research/auth-patterns" });
|
|
143
167
|
```
|
|
144
168
|
|
|
145
169
|
### memory-update (Files)
|
|
@@ -147,13 +171,40 @@ memory_read({ file: "research/auth-patterns" });
|
|
|
147
171
|
Save to project files or handoffs:
|
|
148
172
|
|
|
149
173
|
```typescript
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
174
|
+
memory -
|
|
175
|
+
update({
|
|
176
|
+
file: "project/gotchas",
|
|
177
|
+
content: "### New Gotcha\n\nDescription...",
|
|
178
|
+
mode: "append", // or "replace"
|
|
179
|
+
});
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### memory-admin (Maintenance)
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
// Check current status (schema, FTS5, counts, DB size)
|
|
186
|
+
memory - admin({ operation: "status" });
|
|
187
|
+
|
|
188
|
+
// Full maintenance (archive >90 days, checkpoint WAL, vacuum)
|
|
189
|
+
memory - admin({ operation: "full" });
|
|
190
|
+
|
|
191
|
+
// Preview what would be archived
|
|
192
|
+
memory - admin({ operation: "archive", older_than_days: 60, dry_run: true });
|
|
193
|
+
|
|
194
|
+
// Capture pipeline stats (temporal messages, distillations, compression)
|
|
195
|
+
memory - admin({ operation: "capture-stats" });
|
|
196
|
+
|
|
197
|
+
// Force distillation for current session
|
|
198
|
+
memory - admin({ operation: "distill-now" });
|
|
199
|
+
|
|
200
|
+
// Force curator run (extract observations from distillations)
|
|
201
|
+
memory - admin({ operation: "curate-now" });
|
|
155
202
|
```
|
|
156
203
|
|
|
204
|
+
**Automatic:** On session idle — distillation, curation, FTS5 optimize, WAL checkpoint.
|
|
205
|
+
|
|
206
|
+
**Manual:** Run `memory-admin({ operation: "status" })` to check health.
|
|
207
|
+
|
|
157
208
|
---
|
|
158
209
|
|
|
159
210
|
## What Goes Where
|
|
@@ -161,24 +212,34 @@ memory_update({
|
|
|
161
212
|
### SQLite (observations)
|
|
162
213
|
|
|
163
214
|
- Events: decisions, bugfixes, patterns discovered
|
|
164
|
-
- Searchable via FTS5
|
|
215
|
+
- Searchable via FTS5 with porter stemming
|
|
216
|
+
- Created manually via `observation()` or automatically by curator
|
|
165
217
|
- Use `observation()` to create
|
|
166
218
|
|
|
219
|
+
### SQLite (distillations — automatic)
|
|
220
|
+
|
|
221
|
+
- Compressed session summaries with TF-IDF terms
|
|
222
|
+
- Created automatically when 10+ messages accumulate
|
|
223
|
+
- Searchable via FTS5
|
|
224
|
+
- Used for relevance-scored LTM injection
|
|
225
|
+
|
|
167
226
|
### Markdown Files
|
|
168
227
|
|
|
169
228
|
- Static knowledge: user preferences, tech stack
|
|
170
229
|
- Handoffs: session summaries
|
|
171
230
|
- Research: deep-dive documents
|
|
172
|
-
- Use `
|
|
173
|
-
|
|
174
|
-
| Location | Content | Tool
|
|
175
|
-
| -------------------------- | -------------------------- |
|
|
176
|
-
| `project/user.md` | User identity, preferences | `
|
|
177
|
-
| `project/tech-stack.md` | Frameworks, constraints | `
|
|
178
|
-
| `project/gotchas.md` | Footguns, warnings | `
|
|
179
|
-
| `handoffs/YYYY-MM-DD-*.md` | Session summaries | `
|
|
180
|
-
| `research/*.md` | Deep-dive analysis | `
|
|
181
|
-
| SQLite
|
|
231
|
+
- Use `memory-read()` / `memory-update()`
|
|
232
|
+
|
|
233
|
+
| Location | Content | Tool |
|
|
234
|
+
| -------------------------- | -------------------------- | ----------------------------------- |
|
|
235
|
+
| `project/user.md` | User identity, preferences | `memory-read()` |
|
|
236
|
+
| `project/tech-stack.md` | Frameworks, constraints | `memory-read()` |
|
|
237
|
+
| `project/gotchas.md` | Footguns, warnings | `memory-update({ mode: "append" })` |
|
|
238
|
+
| `handoffs/YYYY-MM-DD-*.md` | Session summaries | `memory-update()` |
|
|
239
|
+
| `research/*.md` | Deep-dive analysis | `memory-update()` |
|
|
240
|
+
| SQLite observations | Events, decisions | `observation()` |
|
|
241
|
+
| SQLite distillations | Session summaries | Automatic (idle) or `distill-now` |
|
|
242
|
+
| SQLite temporal_messages | Raw captured text | Automatic (message events) |
|
|
182
243
|
|
|
183
244
|
---
|
|
184
245
|
|
|
@@ -195,6 +256,7 @@ observation({
|
|
|
195
256
|
files_read: "src/auth.ts, src/middleware.ts",
|
|
196
257
|
files_modified: "src/auth.ts",
|
|
197
258
|
bead_id: "br-abc123", // Link to task (optional)
|
|
259
|
+
source: "manual", // manual (default), curator, imported
|
|
198
260
|
});
|
|
199
261
|
```
|
|
200
262
|
|
|
@@ -213,25 +275,6 @@ observation({
|
|
|
213
275
|
|
|
214
276
|
---
|
|
215
277
|
|
|
216
|
-
## Maintenance
|
|
217
|
-
|
|
218
|
-
```typescript
|
|
219
|
-
// Check current status
|
|
220
|
-
memory_admin({ operation: "status" });
|
|
221
|
-
|
|
222
|
-
// Full maintenance (archive >90 days, checkpoint WAL, vacuum)
|
|
223
|
-
memory_admin({ operation: "full" });
|
|
224
|
-
|
|
225
|
-
// Preview what would be archived
|
|
226
|
-
memory_admin({ operation: "archive", older_than_days: 60, dry_run: true });
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
**Automatic:** Runs at session end (FTS5 optimize, WAL checkpoint if >1MB)
|
|
230
|
-
|
|
231
|
-
**Manual:** Run monthly or when storage grows
|
|
232
|
-
|
|
233
|
-
---
|
|
234
|
-
|
|
235
278
|
## Philosophy
|
|
236
279
|
|
|
237
280
|
**Memory is not a dumping ground. It's curated signal.**
|
|
@@ -19,48 +19,35 @@ The environment monitors context usage and warns at these thresholds:
|
|
|
19
19
|
|
|
20
20
|
## Session Tools
|
|
21
21
|
|
|
22
|
-
###
|
|
22
|
+
### find_sessions
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
Search and discover sessions by keyword. Returns ranked results with match counts and snippets.
|
|
25
25
|
|
|
26
26
|
```typescript
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
list_sessions({ project: "all", since: "yesterday" }); // Cross-project
|
|
27
|
+
find_sessions({ query: "auth bug", limit: 5 }); // Search by keyword
|
|
28
|
+
find_sessions({ query: "refactor" }); // Default limit
|
|
30
29
|
```
|
|
31
30
|
|
|
32
|
-
|
|
31
|
+
**Parameters:**
|
|
33
32
|
|
|
34
|
-
|
|
33
|
+
- `query` (required): Search keywords — multi-word queries use AND matching
|
|
34
|
+
- `limit` (optional): Max results to return
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
read_session("last"); // Most recent
|
|
38
|
-
read_session("2 ago", { project: "current" }); // 2nd most recent
|
|
39
|
-
read_session("today"); // Today's first session
|
|
40
|
-
read_session("ses_abc123", { focus: "file changes" }); // Specific aspect
|
|
41
|
-
```
|
|
36
|
+
**Returns:** Ranked sessions with match count, snippets, and suggested next steps.
|
|
42
37
|
|
|
43
|
-
###
|
|
38
|
+
### read_session
|
|
44
39
|
|
|
45
|
-
|
|
40
|
+
Read messages from a specific session. Supports optional focus filtering.
|
|
46
41
|
|
|
47
42
|
```typescript
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
search_session({ query: "error", limit: 10 }); // Limit results
|
|
43
|
+
read_session({ session_id: "ses_abc123" }); // Full session
|
|
44
|
+
read_session({ session_id: "ses_abc123", focus: "auth" }); // Filter to relevant messages
|
|
51
45
|
```
|
|
52
46
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
### summarize_session
|
|
56
|
-
|
|
57
|
-
Generate AI summary of a session:
|
|
58
|
-
|
|
59
|
-
```typescript
|
|
60
|
-
summarize_session("ses_abc123"); // Trigger AI summarization
|
|
61
|
-
```
|
|
47
|
+
**Parameters:**
|
|
62
48
|
|
|
63
|
-
|
|
49
|
+
- `session_id` (required): Session ID from find_sessions results
|
|
50
|
+
- `focus` (optional): Keyword to filter messages within the session
|
|
64
51
|
|
|
65
52
|
## When to Start New Session
|
|
66
53
|
|
|
@@ -74,11 +61,11 @@ Use before `read_session` to get a quick overview of what happened in a past ses
|
|
|
74
61
|
```
|
|
75
62
|
Session 1: Implement feature X (80k tokens)
|
|
76
63
|
↓ close, update memory
|
|
77
|
-
Session 2:
|
|
64
|
+
Session 2: find_sessions({ query: "feature X" }) → read_session(...) → Refactor (60k tokens)
|
|
78
65
|
↓
|
|
79
|
-
Session 3:
|
|
66
|
+
Session 3: find_sessions({ query: "feature X" }) → Add tests (90k tokens)
|
|
80
67
|
↓
|
|
81
|
-
Session 4: read_session
|
|
68
|
+
Session 4: read_session(...) → Final review (100k tokens)
|
|
82
69
|
```
|
|
83
70
|
|
|
84
71
|
**Result**: 4 fresh contexts vs 1 degraded 330k context. Better performance, lower cost.
|
|
@@ -87,10 +74,10 @@ Session 4: read_session refs → Final review (100k tokens)
|
|
|
87
74
|
|
|
88
75
|
Use all available sources:
|
|
89
76
|
|
|
90
|
-
1. `read_session
|
|
91
|
-
2. Git state
|
|
92
|
-
3. Memory files
|
|
93
|
-
4. Beads
|
|
77
|
+
1. `find_sessions` + `read_session` — Previous session work
|
|
78
|
+
2. Git state — `git diff`, `git log` — Code changes
|
|
79
|
+
3. Memory files — `.opencode/memory/*` — Persistent context
|
|
80
|
+
4. Beads — `br show <id>` — Task specs
|
|
94
81
|
|
|
95
82
|
**Don't**: Carry everything forward. Extract what's needed, discard the rest.
|
|
96
83
|
|
package/package.json
CHANGED
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Session Continuity Plugin (compaction-time context injection)
|
|
3
|
-
*
|
|
4
|
-
* Purpose:
|
|
5
|
-
* - Provide compact, bounded state needed to resume work after compaction.
|
|
6
|
-
* - Keep injected prompt guidance minimal and deterministic.
|
|
7
|
-
*
|
|
8
|
-
* Non-goals:
|
|
9
|
-
* - Replace DCP policy/rules management.
|
|
10
|
-
* - Inject large free-form manuals into the prompt.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { Database } from "bun:sqlite";
|
|
14
|
-
import { readFile, readdir, stat } from "node:fs/promises";
|
|
15
|
-
import path from "node:path";
|
|
16
|
-
import type { Plugin } from "@opencode-ai/plugin";
|
|
17
|
-
|
|
18
|
-
const MAX_SESSION_CONTEXT_CHARS = 3000;
|
|
19
|
-
const MAX_PROJECT_FILES = 3;
|
|
20
|
-
const MAX_PROJECT_FILE_CHARS = 900;
|
|
21
|
-
const MAX_HANDOFF_CHARS = 2500;
|
|
22
|
-
const MAX_BEADS = 8;
|
|
23
|
-
const MAX_COMBINED_CONTEXT_CHARS = 9000;
|
|
24
|
-
|
|
25
|
-
interface BeadRow {
|
|
26
|
-
id: string;
|
|
27
|
-
title: string;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function truncate(text: string, maxChars: number): string {
|
|
31
|
-
if (text.length <= maxChars) return text;
|
|
32
|
-
return `${text.slice(0, maxChars)}\n...[truncated]`;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async function safeReadFile(filePath: string): Promise<string> {
|
|
36
|
-
try {
|
|
37
|
-
return await readFile(filePath, "utf-8");
|
|
38
|
-
} catch {
|
|
39
|
-
return "";
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function renderSection(title: string, body: string): string {
|
|
44
|
-
if (!body.trim()) return "";
|
|
45
|
-
return `## ${title}\n${body.trim()}`;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async function readProjectMemoryContext(memoryDir: string): Promise<string> {
|
|
49
|
-
const projectDir = path.join(memoryDir, "project");
|
|
50
|
-
let names: string[] = [];
|
|
51
|
-
try {
|
|
52
|
-
names = (await readdir(projectDir))
|
|
53
|
-
.filter((name) => name.endsWith(".md"))
|
|
54
|
-
.sort()
|
|
55
|
-
.slice(0, MAX_PROJECT_FILES);
|
|
56
|
-
} catch {
|
|
57
|
-
return "";
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const chunks: string[] = [];
|
|
61
|
-
for (const name of names) {
|
|
62
|
-
const fullPath = path.join(projectDir, name);
|
|
63
|
-
const content = (await safeReadFile(fullPath)).trim();
|
|
64
|
-
if (!content) continue;
|
|
65
|
-
chunks.push(
|
|
66
|
-
`### ${name.replace(/\.md$/, "")}\n${truncate(content, MAX_PROJECT_FILE_CHARS)}`,
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return chunks.join("\n\n");
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
async function readLatestHandoff(handoffDir: string): Promise<string> {
|
|
74
|
-
let names: string[] = [];
|
|
75
|
-
try {
|
|
76
|
-
names = (await readdir(handoffDir)).filter((name) => name.endsWith(".md"));
|
|
77
|
-
} catch {
|
|
78
|
-
return "";
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (names.length === 0) return "";
|
|
82
|
-
|
|
83
|
-
const withMtime = await Promise.all(
|
|
84
|
-
names.map(async (name) => {
|
|
85
|
-
const fullPath = path.join(handoffDir, name);
|
|
86
|
-
try {
|
|
87
|
-
const info = await stat(fullPath);
|
|
88
|
-
return { name, fullPath, mtimeMs: info.mtimeMs };
|
|
89
|
-
} catch {
|
|
90
|
-
return { name, fullPath, mtimeMs: 0 };
|
|
91
|
-
}
|
|
92
|
-
}),
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
withMtime.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
96
|
-
const latest = withMtime[0];
|
|
97
|
-
const content = (await safeReadFile(latest.fullPath)).trim();
|
|
98
|
-
if (!content) return "";
|
|
99
|
-
|
|
100
|
-
return `Source: ${latest.name}\n${truncate(content, MAX_HANDOFF_CHARS)}`;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Read in-progress beads directly from SQLite database.
|
|
105
|
-
* Cross-platform alternative to shell command execution.
|
|
106
|
-
*/
|
|
107
|
-
function readInProgressBeads(directory: string): string {
|
|
108
|
-
const dbPath = path.join(directory, ".beads", "beads.db");
|
|
109
|
-
let db: Database | undefined;
|
|
110
|
-
|
|
111
|
-
try {
|
|
112
|
-
db = new Database(dbPath, { readonly: true });
|
|
113
|
-
|
|
114
|
-
const rows = db
|
|
115
|
-
.query<BeadRow, [number]>(
|
|
116
|
-
"SELECT id, title FROM issues WHERE status = 'in_progress' ORDER BY updated_at DESC LIMIT ?",
|
|
117
|
-
)
|
|
118
|
-
.all(MAX_BEADS);
|
|
119
|
-
|
|
120
|
-
if (rows.length === 0) return "";
|
|
121
|
-
|
|
122
|
-
return rows.map((row) => `- ${row.id}: ${row.title}`).join("\n");
|
|
123
|
-
} catch {
|
|
124
|
-
// Database may not exist, be locked, or have different schema
|
|
125
|
-
// Return empty string to allow graceful degradation
|
|
126
|
-
return "";
|
|
127
|
-
} finally {
|
|
128
|
-
db?.close();
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
export const CompactionPlugin: Plugin = async ({ directory }) => {
|
|
133
|
-
const memoryDir = path.join(directory, ".opencode", "memory");
|
|
134
|
-
const handoffDir = path.join(memoryDir, "handoffs");
|
|
135
|
-
|
|
136
|
-
return {
|
|
137
|
-
"experimental.session.compacting": async (_input, output) => {
|
|
138
|
-
const sessionContext = truncate(
|
|
139
|
-
(await safeReadFile(path.join(memoryDir, "session-context.md"))).trim(),
|
|
140
|
-
MAX_SESSION_CONTEXT_CHARS,
|
|
141
|
-
);
|
|
142
|
-
|
|
143
|
-
const [projectContext, handoffContext] = await Promise.all([
|
|
144
|
-
readProjectMemoryContext(memoryDir),
|
|
145
|
-
readLatestHandoff(handoffDir),
|
|
146
|
-
]);
|
|
147
|
-
|
|
148
|
-
// Synchronous SQLite query - no async/await needed
|
|
149
|
-
const beadsContext = readInProgressBeads(directory);
|
|
150
|
-
|
|
151
|
-
const combined = [
|
|
152
|
-
renderSection("Session Continuity", sessionContext),
|
|
153
|
-
renderSection("Active Beads", beadsContext),
|
|
154
|
-
renderSection("Previous Handoff", handoffContext),
|
|
155
|
-
renderSection("Project Memory", projectContext),
|
|
156
|
-
]
|
|
157
|
-
.filter(Boolean)
|
|
158
|
-
.join("\n\n");
|
|
159
|
-
|
|
160
|
-
if (combined) {
|
|
161
|
-
output.context.push(
|
|
162
|
-
`## Session Context\n${truncate(combined, MAX_COMBINED_CONTEXT_CHARS)}\n`,
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
output.prompt = `${output.prompt}
|
|
167
|
-
|
|
168
|
-
<compaction_task>
|
|
169
|
-
Summarize conversation state for reliable continuation after compaction.
|
|
170
|
-
</compaction_task>
|
|
171
|
-
|
|
172
|
-
<compaction_rules>
|
|
173
|
-
- Preserve exact IDs, file paths, and unresolved constraints.
|
|
174
|
-
- Distinguish completed work from current in-progress work.
|
|
175
|
-
- Keep summary concise and execution-focused.
|
|
176
|
-
- If critical context is missing, state uncertainty explicitly.
|
|
177
|
-
</compaction_rules>
|
|
178
|
-
|
|
179
|
-
<compaction_output>
|
|
180
|
-
Include:
|
|
181
|
-
- What was done
|
|
182
|
-
- What is being worked on now
|
|
183
|
-
- Files currently in play
|
|
184
|
-
- Next actions
|
|
185
|
-
- Persistent user constraints/preferences
|
|
186
|
-
</compaction_output>
|
|
187
|
-
`;
|
|
188
|
-
},
|
|
189
|
-
};
|
|
190
|
-
};
|