opencode-claude-memory 1.5.2 → 1.6.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/README.md +21 -0
- package/bin/opencode-memory +426 -9
- package/dist/index.d.ts +2 -0
- package/dist/index.js +239 -0
- package/dist/memory.d.ts +25 -0
- package/dist/memory.js +200 -0
- package/dist/memoryScan.d.ts +20 -0
- package/dist/memoryScan.js +106 -0
- package/dist/paths.d.ts +14 -0
- package/dist/paths.js +141 -0
- package/dist/prompt.d.ts +4 -0
- package/dist/prompt.js +199 -0
- package/dist/recall.d.ts +11 -0
- package/dist/recall.js +138 -0
- package/package.json +15 -6
- package/src/index.ts +0 -142
- package/src/memory.ts +0 -243
- package/src/paths.ts +0 -165
- package/src/prompt.ts +0 -147
- package/src/recall.ts +0 -126
package/src/recall.ts
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import { statSync } from "fs"
|
|
2
|
-
import { listMemories, type MemoryEntry } from "./memory.js"
|
|
3
|
-
|
|
4
|
-
const encoder = new TextEncoder()
|
|
5
|
-
|
|
6
|
-
export type RecalledMemory = {
|
|
7
|
-
fileName: string
|
|
8
|
-
name: string
|
|
9
|
-
type: string
|
|
10
|
-
description: string
|
|
11
|
-
content: string
|
|
12
|
-
ageInDays: number
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const MAX_RECALLED_MEMORIES = 5
|
|
16
|
-
const MAX_MEMORY_LINES = 200
|
|
17
|
-
const MAX_MEMORY_BYTES = 4096
|
|
18
|
-
|
|
19
|
-
function tokenizeQuery(query: string): string[] {
|
|
20
|
-
return [...new Set(query.toLowerCase().split(/\s+/).map((token) => token.trim()).filter((token) => token.length >= 2))]
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function getMemoryMtimeMs(entry: MemoryEntry): number {
|
|
24
|
-
try {
|
|
25
|
-
return statSync(entry.filePath).mtimeMs
|
|
26
|
-
} catch {
|
|
27
|
-
return 0
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function scoreMemory(entry: MemoryEntry, terms: string[]): number {
|
|
32
|
-
if (terms.length === 0) return 0
|
|
33
|
-
const haystack = `${entry.name}\n${entry.description}\n${entry.content}`.toLowerCase()
|
|
34
|
-
let score = 0
|
|
35
|
-
for (const term of terms) {
|
|
36
|
-
if (haystack.includes(term)) score += 1
|
|
37
|
-
}
|
|
38
|
-
return score
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function truncateMemoryContent(content: string): string {
|
|
42
|
-
const maxLines = content.split("\n").slice(0, MAX_MEMORY_LINES)
|
|
43
|
-
const lineTruncated = maxLines.join("\n")
|
|
44
|
-
if (encoder.encode(lineTruncated).length <= MAX_MEMORY_BYTES) {
|
|
45
|
-
return lineTruncated
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const lines = lineTruncated.split("\n")
|
|
49
|
-
const kept: string[] = []
|
|
50
|
-
let usedBytes = 0
|
|
51
|
-
|
|
52
|
-
for (const line of lines) {
|
|
53
|
-
const candidate = kept.length === 0 ? line : `\n${line}`
|
|
54
|
-
const candidateBytes = encoder.encode(candidate).length
|
|
55
|
-
if (usedBytes + candidateBytes > MAX_MEMORY_BYTES) break
|
|
56
|
-
kept.push(line)
|
|
57
|
-
usedBytes += candidateBytes
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return kept.join("\n")
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export function recallRelevantMemories(worktree: string, query?: string): RecalledMemory[] {
|
|
64
|
-
const memories = listMemories(worktree)
|
|
65
|
-
if (memories.length === 0) return []
|
|
66
|
-
|
|
67
|
-
const now = Date.now()
|
|
68
|
-
const memoriesWithMeta = memories.map((entry) => {
|
|
69
|
-
const mtimeMs = getMemoryMtimeMs(entry)
|
|
70
|
-
return {
|
|
71
|
-
entry,
|
|
72
|
-
mtimeMs,
|
|
73
|
-
}
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
const terms = query ? tokenizeQuery(query) : []
|
|
77
|
-
|
|
78
|
-
let selected = memoriesWithMeta
|
|
79
|
-
|
|
80
|
-
if (terms.length > 0) {
|
|
81
|
-
const withScores = memoriesWithMeta
|
|
82
|
-
.map((item) => ({
|
|
83
|
-
...item,
|
|
84
|
-
score: scoreMemory(item.entry, terms),
|
|
85
|
-
}))
|
|
86
|
-
.sort((a, b) => b.score - a.score || b.mtimeMs - a.mtimeMs)
|
|
87
|
-
|
|
88
|
-
if (withScores.some((item) => item.score > 0)) {
|
|
89
|
-
selected = withScores
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (selected === memoriesWithMeta) {
|
|
94
|
-
selected = [...memoriesWithMeta].sort((a, b) => b.mtimeMs - a.mtimeMs)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
return selected.slice(0, MAX_RECALLED_MEMORIES).map(({ entry, mtimeMs }) => ({
|
|
98
|
-
fileName: entry.fileName,
|
|
99
|
-
name: entry.name,
|
|
100
|
-
type: entry.type,
|
|
101
|
-
description: entry.description,
|
|
102
|
-
content: truncateMemoryContent(entry.content),
|
|
103
|
-
ageInDays: Math.max(0, Math.floor((now - mtimeMs) / (1000 * 60 * 60 * 24))),
|
|
104
|
-
}))
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function formatAgeWarning(ageInDays: number): string {
|
|
108
|
-
if (ageInDays <= 1) return ""
|
|
109
|
-
return `\n> ⚠️ This memory is ${ageInDays} days old. Memories are point-in-time observations, not live state — claims about code behavior or file:line citations may be outdated. Verify against current code before asserting as fact.\n`
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export function formatRecalledMemories(memories: RecalledMemory[]): string {
|
|
113
|
-
if (memories.length === 0) return ""
|
|
114
|
-
|
|
115
|
-
const sections = memories.map((memory) => {
|
|
116
|
-
const ageWarning = formatAgeWarning(memory.ageInDays)
|
|
117
|
-
return `### ${memory.name} (${memory.type})${ageWarning}\n${memory.content}`
|
|
118
|
-
})
|
|
119
|
-
return [
|
|
120
|
-
"## Recalled Memories",
|
|
121
|
-
"",
|
|
122
|
-
"The following memories were automatically selected as relevant to this conversation. They may be outdated — verify against current state before relying on them.",
|
|
123
|
-
"",
|
|
124
|
-
sections.join("\n\n"),
|
|
125
|
-
].join("\n")
|
|
126
|
-
}
|