@lmaksym/agent-mem 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/.claude/commands/context.md +24 -0
- package/.claude/skills/agent-mem/SKILL.md +66 -0
- package/.claude/skills/agent-mem/references/branching-merging.md +34 -0
- package/.claude/skills/agent-mem/references/coexistence.md +19 -0
- package/.claude/skills/agent-mem/references/collaboration.md +33 -0
- package/.claude/skills/agent-mem/references/reflection-compaction.md +104 -0
- package/.claude/skills/agent-mem/references/sub-agent-patterns.md +60 -0
- package/LICENSE +21 -0
- package/README.md +235 -0
- package/bin/agent-context.js +95 -0
- package/bin/parse-args.js +85 -0
- package/package.json +58 -0
- package/src/commands/branch.js +57 -0
- package/src/commands/branch.test.js +91 -0
- package/src/commands/branches.js +34 -0
- package/src/commands/commit.js +55 -0
- package/src/commands/compact.js +307 -0
- package/src/commands/compact.test.js +110 -0
- package/src/commands/config.js +47 -0
- package/src/commands/core.test.js +166 -0
- package/src/commands/diff.js +157 -0
- package/src/commands/diff.test.js +64 -0
- package/src/commands/forget.js +77 -0
- package/src/commands/forget.test.js +68 -0
- package/src/commands/help.js +99 -0
- package/src/commands/import.js +83 -0
- package/src/commands/init.js +269 -0
- package/src/commands/init.test.js +80 -0
- package/src/commands/lesson.js +95 -0
- package/src/commands/lesson.test.js +93 -0
- package/src/commands/merge.js +105 -0
- package/src/commands/pin.js +34 -0
- package/src/commands/pull.js +80 -0
- package/src/commands/push.js +80 -0
- package/src/commands/read.js +62 -0
- package/src/commands/reflect.js +328 -0
- package/src/commands/remember.js +95 -0
- package/src/commands/resolve.js +230 -0
- package/src/commands/resolve.test.js +167 -0
- package/src/commands/search.js +70 -0
- package/src/commands/share.js +65 -0
- package/src/commands/snapshot.js +106 -0
- package/src/commands/status.js +37 -0
- package/src/commands/switch.js +31 -0
- package/src/commands/sync.js +328 -0
- package/src/commands/track.js +61 -0
- package/src/commands/unpin.js +28 -0
- package/src/commands/write.js +58 -0
- package/src/core/auto-commit.js +22 -0
- package/src/core/config.js +93 -0
- package/src/core/context-root.js +28 -0
- package/src/core/fs.js +137 -0
- package/src/core/git.js +182 -0
- package/src/core/importers.js +210 -0
- package/src/core/lock.js +62 -0
- package/src/core/reflect-defrag.js +287 -0
- package/src/core/reflect-gather.js +360 -0
- package/src/core/reflect-parse.js +168 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse agent's structured reflection output into sections.
|
|
3
|
+
* Fuzzy section-header matching with graceful fallback.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const SECTION_MAP = [
|
|
7
|
+
{ key: 'patterns', match: ['pattern'] },
|
|
8
|
+
{ key: 'decisions', match: ['decision', 'validated'] },
|
|
9
|
+
{ key: 'lessons', match: ['lesson'] },
|
|
10
|
+
{ key: 'contradictions', match: ['contradict'] },
|
|
11
|
+
{ key: 'stale', match: ['stale'] },
|
|
12
|
+
{ key: 'gaps', match: ['gap', 'new entr'] },
|
|
13
|
+
{ key: 'themes', match: ['theme'] },
|
|
14
|
+
{ key: 'summary', match: ['summar'] },
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Normalize a section header to a known key.
|
|
19
|
+
*/
|
|
20
|
+
function normalizeSection(header) {
|
|
21
|
+
const lower = header.toLowerCase();
|
|
22
|
+
for (const { key, match } of SECTION_MAP) {
|
|
23
|
+
if (match.some((m) => lower.includes(m))) return key;
|
|
24
|
+
}
|
|
25
|
+
return lower.replace(/[^a-z0-9]+/g, '_').replace(/^_|_$/g, '');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Parse reflection text into { sections, raw, recognized }.
|
|
30
|
+
* sections: { key: string[] } — lines under each section
|
|
31
|
+
* raw: original text
|
|
32
|
+
* recognized: whether any known sections were found
|
|
33
|
+
*/
|
|
34
|
+
export function parseReflection(text) {
|
|
35
|
+
const sections = {};
|
|
36
|
+
let currentSection = null;
|
|
37
|
+
let recognizedCount = 0;
|
|
38
|
+
|
|
39
|
+
for (const line of text.split('\n')) {
|
|
40
|
+
const headerMatch = line.match(/^##\s+(.+)/);
|
|
41
|
+
if (headerMatch) {
|
|
42
|
+
currentSection = normalizeSection(headerMatch[1].trim());
|
|
43
|
+
if (!sections[currentSection]) sections[currentSection] = [];
|
|
44
|
+
// Check if this is a known section
|
|
45
|
+
const known = SECTION_MAP.some((s) => s.key === currentSection);
|
|
46
|
+
if (known) recognizedCount++;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (currentSection && line.trim()) {
|
|
51
|
+
sections[currentSection].push(line);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
sections,
|
|
57
|
+
raw: text,
|
|
58
|
+
recognized: recognizedCount > 0,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Build a gap entry object. For lessons, includes problem/resolution fields.
|
|
64
|
+
*/
|
|
65
|
+
function buildGapEntry(type, text, problem, resolution) {
|
|
66
|
+
const entry = { type: type.toLowerCase(), text };
|
|
67
|
+
if (type.toLowerCase() === 'lesson' && problem) entry.problem = problem;
|
|
68
|
+
if (type.toLowerCase() === 'lesson' && resolution) entry.resolution = resolution;
|
|
69
|
+
return entry;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Extract structured "Gaps Filled" entries.
|
|
74
|
+
* Looks for "type: <category>" and "text: <content>" patterns.
|
|
75
|
+
* For lessons, also extracts "problem:" and "resolution:" fields.
|
|
76
|
+
* Returns array of { type, text, problem?, resolution? }.
|
|
77
|
+
*/
|
|
78
|
+
export function extractGaps(lines) {
|
|
79
|
+
if (!lines || !lines.length) return [];
|
|
80
|
+
|
|
81
|
+
const entries = [];
|
|
82
|
+
let currentType = null;
|
|
83
|
+
let currentText = null;
|
|
84
|
+
let currentProblem = null;
|
|
85
|
+
let currentResolution = null;
|
|
86
|
+
|
|
87
|
+
for (const line of lines) {
|
|
88
|
+
const trimmed = line.trim().replace(/^-\s*/, '');
|
|
89
|
+
|
|
90
|
+
const typeMatch = trimmed.match(/^type:\s*(decision|pattern|mistake|note|lesson)/i);
|
|
91
|
+
if (typeMatch) {
|
|
92
|
+
// If we had a previous entry, push it
|
|
93
|
+
if (currentType && currentText) {
|
|
94
|
+
entries.push(buildGapEntry(currentType, currentText, currentProblem, currentResolution));
|
|
95
|
+
}
|
|
96
|
+
currentType = typeMatch[1];
|
|
97
|
+
currentText = null;
|
|
98
|
+
currentProblem = null;
|
|
99
|
+
currentResolution = null;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const textMatch = trimmed.match(/^text:\s*(.+)/);
|
|
104
|
+
if (textMatch) {
|
|
105
|
+
currentText = textMatch[1].trim();
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const problemMatch = trimmed.match(/^problem:\s*(.+)/);
|
|
110
|
+
if (problemMatch) {
|
|
111
|
+
currentProblem = problemMatch[1].trim();
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const resolutionMatch = trimmed.match(/^resolution:\s*(.+)/);
|
|
116
|
+
if (resolutionMatch) {
|
|
117
|
+
currentResolution = resolutionMatch[1].trim();
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// If line doesn't match type/text pattern, treat as inline entry
|
|
122
|
+
// e.g. "- type: pattern, text: Always do X"
|
|
123
|
+
const inlineMatch = trimmed.match(
|
|
124
|
+
/type:\s*(decision|pattern|mistake|note|lesson)\s*[,;]\s*text:\s*(.+)/i,
|
|
125
|
+
);
|
|
126
|
+
if (inlineMatch) {
|
|
127
|
+
entries.push({ type: inlineMatch[1].toLowerCase(), text: inlineMatch[2].trim() });
|
|
128
|
+
currentType = null;
|
|
129
|
+
currentText = null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Push last entry
|
|
134
|
+
if (currentType && currentText) {
|
|
135
|
+
entries.push(buildGapEntry(currentType, currentText, currentProblem, currentResolution));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return entries;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Extract stale entry references from the Stale Entries section.
|
|
143
|
+
* Looks for patterns like "memory/file.md: entry text" or "memory/file.md line N: text"
|
|
144
|
+
* Returns array of { file, text }.
|
|
145
|
+
*/
|
|
146
|
+
export function extractStaleEntries(lines) {
|
|
147
|
+
if (!lines || !lines.length) return [];
|
|
148
|
+
|
|
149
|
+
const entries = [];
|
|
150
|
+
for (const line of lines) {
|
|
151
|
+
const trimmed = line.trim().replace(/^-\s*/, '');
|
|
152
|
+
// Pattern: "memory/file.md: some entry text"
|
|
153
|
+
const match = trimmed.match(/^(memory\/[\w-]+\.md)(?:\s*(?:line\s*\d+)?)\s*[:—-]\s*(.+)/i);
|
|
154
|
+
if (match) {
|
|
155
|
+
entries.push({ file: match[1], text: match[2].trim() });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return entries;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Get the summary text from parsed sections.
|
|
164
|
+
*/
|
|
165
|
+
export function extractSummary(sections) {
|
|
166
|
+
if (!sections.summary || !sections.summary.length) return null;
|
|
167
|
+
return sections.summary.join(' ').trim();
|
|
168
|
+
}
|