@nogataka/claw-memory 0.1.3 → 0.3.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-plugin/marketplace.json +5 -3
- package/.claude-plugin/plugin.json +11 -3
- package/README.ja.md +55 -11
- package/README.md +60 -11
- package/dist/cli.js +215 -1
- package/dist/core/db.js +132 -9
- package/dist/core/distill.js +97 -5
- package/dist/core/hooks.js +8 -7
- package/dist/core/installer/codex.js +19 -5
- package/dist/core/lesson-extract.js +233 -0
- package/dist/core/lesson-quality.js +79 -0
- package/dist/core/lesson-search.js +145 -0
- package/dist/core/lesson-share.js +80 -0
- package/dist/core/lessons.js +332 -0
- package/dist/core/logsearch/parse.js +0 -0
- package/dist/core/logsearch/paths.js +22 -0
- package/dist/core/logsearch/recent.js +46 -2
- package/dist/core/logsearch/search.js +57 -3
- package/dist/core/recall.js +61 -0
- package/dist/core/vector-memory.js +27 -10
- package/dist/mcp/server.js +177 -5
- package/dist/ui/page.js +125 -4
- package/dist/ui/server.js +65 -1
- package/hooks/claw-hook.sh +33 -9
- package/hooks/hooks.codex.json +0 -11
- package/hooks/hooks.json +0 -11
- package/package.json +4 -6
package/dist/ui/page.js
CHANGED
|
@@ -42,17 +42,37 @@ export const PAGE = /* html */ `<!doctype html>
|
|
|
42
42
|
.pref b { color:var(--accent); }
|
|
43
43
|
.empty { color:var(--muted); padding:30px; text-align:center; }
|
|
44
44
|
.cmeta { color:var(--muted); font-size:11px; margin:2px 0; word-break:break-all; }
|
|
45
|
-
header button#logsBtn { background:var(--chip); color:var(--fg);
|
|
46
|
-
border-radius:6px; padding:6px 10px; cursor:pointer; font-size:12px; }
|
|
47
|
-
header button#logsBtn.on { background:var(--accent); color:#0d1117; border-color:var(--accent); }
|
|
45
|
+
header button#logsBtn, header button#lessonsBtn { background:var(--chip); color:var(--fg);
|
|
46
|
+
border:1px solid var(--border); border-radius:6px; padding:6px 10px; cursor:pointer; font-size:12px; }
|
|
47
|
+
header button#logsBtn.on, header button#lessonsBtn.on { background:var(--accent); color:#0d1117; border-color:var(--accent); }
|
|
48
48
|
.src { color:var(--accent); font-size:10px; }
|
|
49
49
|
.hl { background:#9e6a03; color:#fff; border-radius:2px; padding:0 1px; }
|
|
50
|
+
.lstatus { border-radius:4px; padding:1px 6px; font-size:10px; font-weight:600; }
|
|
51
|
+
.lstatus.candidate { background:#3a2d00; color:#e3b341; }
|
|
52
|
+
.lstatus.approved { background:#03361a; color:#3fb950; }
|
|
53
|
+
.lstatus.rejected { background:#3a0d0d; color:#f85149; }
|
|
54
|
+
.lstatus.archived { background:#21262d; color:#8b949e; }
|
|
55
|
+
.lstatus.superseded{ background:#26203a; color:#a371f7; }
|
|
56
|
+
.lact { display:flex; flex-wrap:wrap; gap:6px; margin-top:10px; }
|
|
57
|
+
.lact button, .lact select { background:var(--chip); color:var(--fg); border:1px solid var(--border);
|
|
58
|
+
border-radius:6px; padding:4px 9px; cursor:pointer; font-size:11px; }
|
|
59
|
+
.lact button:hover { background:var(--accent); color:#0d1117; }
|
|
60
|
+
.lact button.danger:hover { background:#f85149; color:#fff; }
|
|
61
|
+
.lbody { white-space:pre-wrap; word-break:break-word; margin:6px 0; }
|
|
62
|
+
.lcond { font-size:12px; color:var(--muted); margin:3px 0; }
|
|
63
|
+
.lcond b { color:var(--fg); font-weight:600; }
|
|
64
|
+
.lhist { font-size:11px; color:var(--muted); margin-top:8px; border-top:1px solid var(--border); padding-top:6px; }
|
|
65
|
+
.lfilter { display:flex; gap:6px; margin-bottom:14px; flex-wrap:wrap; }
|
|
66
|
+
.lfilter button { background:var(--chip); color:var(--fg); border:1px solid var(--border);
|
|
67
|
+
border-radius:14px; padding:4px 12px; cursor:pointer; font-size:12px; }
|
|
68
|
+
.lfilter button.on { background:var(--accent); color:#0d1117; border-color:var(--accent); }
|
|
50
69
|
</style>
|
|
51
70
|
</head>
|
|
52
71
|
<body>
|
|
53
72
|
<header>
|
|
54
73
|
<h1>🧠 claw-memory</h1>
|
|
55
74
|
<span class="muted" id="stats"></span>
|
|
75
|
+
<button id="lessonsBtn" title="レッスン (再利用可能な知識のレビュー)">📚 Lessons</button>
|
|
56
76
|
<button id="logsBtn" title="生ログ全文検索 (Claude Code + Codex)">🔎 ログ検索</button>
|
|
57
77
|
<input id="search" placeholder="フィルタ (本文を絞り込み)" />
|
|
58
78
|
</header>
|
|
@@ -90,13 +110,14 @@ function renderNav() {
|
|
|
90
110
|
'<button data-id="'+p.id+'" class="'+(p.id===current?'active':'')+'">'
|
|
91
111
|
+ '<div class="nm">'+esc(p.name)+'</div>'
|
|
92
112
|
+ '<div class="pth">'+esc(p.path)+'</div>'
|
|
93
|
-
+ '<div class="pth">'+p.counts.summaries+' sum · '+p.counts.chunks+' chunk · '+p.counts.preferences+' pref</div>'
|
|
113
|
+
+ '<div class="pth">'+p.counts.summaries+' sum · '+p.counts.chunks+' chunk · '+p.counts.preferences+' pref · '+(p.counts.lessons||0)+' lesson</div>'
|
|
94
114
|
+ '</button>').join("");
|
|
95
115
|
el("nav").querySelectorAll("button").forEach(b =>
|
|
96
116
|
b.onclick = () => select(b.dataset.id));
|
|
97
117
|
}
|
|
98
118
|
async function select(id) {
|
|
99
119
|
current = id; renderNav();
|
|
120
|
+
if (lessonMode) { loadLessons(); return; }
|
|
100
121
|
const d = await (await fetch("/api/memory?project="+encodeURIComponent(id))).json();
|
|
101
122
|
render(d);
|
|
102
123
|
}
|
|
@@ -136,6 +157,7 @@ function currentPath() { const p = projects.find(x => x.id === current); return
|
|
|
136
157
|
el("logsBtn").addEventListener("click", () => {
|
|
137
158
|
logMode = !logMode;
|
|
138
159
|
el("logsBtn").classList.toggle("on", logMode);
|
|
160
|
+
if (logMode && lessonMode) { lessonMode = false; el("lessonsBtn").classList.remove("on"); }
|
|
139
161
|
el("search").value = "";
|
|
140
162
|
el("search").placeholder = logMode
|
|
141
163
|
? "生ログ全文検索 (このプロジェクトのCC+Codexログ)"
|
|
@@ -175,6 +197,105 @@ async function runLogSearch(q) {
|
|
|
175
197
|
el("main").innerHTML = '<div class="sect"><h2>Raw Log Search — ' + d.total + ' hits (showing ' + d.results.length + ')</h2>' + cards + '</div>';
|
|
176
198
|
}
|
|
177
199
|
|
|
200
|
+
// --- Lessons view ---------------------------------------------------------
|
|
201
|
+
let lessonMode = false, lessonStatus = "candidate";
|
|
202
|
+
const LSTATUSES = ["candidate","approved","conflicts","rejected","archived","superseded"];
|
|
203
|
+
|
|
204
|
+
el("lessonsBtn").addEventListener("click", () => {
|
|
205
|
+
lessonMode = !lessonMode;
|
|
206
|
+
el("lessonsBtn").classList.toggle("on", lessonMode);
|
|
207
|
+
if (lessonMode && logMode) { logMode = false; el("logsBtn").classList.remove("on"); }
|
|
208
|
+
el("search").value = ""; filter = "";
|
|
209
|
+
el("search").placeholder = lessonMode ? "レッスンを絞り込み (本文)" : "フィルタ (本文を絞り込み)";
|
|
210
|
+
if (current) select(current);
|
|
211
|
+
else el("main").innerHTML = '<div class="empty">プロジェクトを選択してください</div>';
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
async function loadLessons() {
|
|
215
|
+
if (!current) return;
|
|
216
|
+
el("main").innerHTML = '<div class="empty">読み込み中…</div>';
|
|
217
|
+
const url = "/api/lessons?project=" + encodeURIComponent(current) + "&status=" + encodeURIComponent(lessonStatus);
|
|
218
|
+
let d;
|
|
219
|
+
try { d = await (await fetch(url)).json(); }
|
|
220
|
+
catch { el("main").innerHTML = '<div class="empty">読み込みに失敗しました</div>'; return; }
|
|
221
|
+
renderLessons(d);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function lfilterBar(counts) {
|
|
225
|
+
return '<div class="lfilter">' + LSTATUSES.map(s =>
|
|
226
|
+
'<button class="' + (s===lessonStatus?'on':'') + '" data-st="' + s + '">'
|
|
227
|
+
+ s + ' (' + (counts[s]||0) + ')</button>').join("") + '</div>';
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function renderLessons(d) {
|
|
231
|
+
const f = filter.toLowerCase();
|
|
232
|
+
const match = (t) => !f || (t||"").toLowerCase().includes(f);
|
|
233
|
+
const lessons = (d.lessons||[]).filter(l => match(l.title + " " + l.lesson));
|
|
234
|
+
const heading = lessonStatus === "candidate" ? "Candidate Review" : (lessonStatus + " lessons");
|
|
235
|
+
let h = lfilterBar(d.counts||{});
|
|
236
|
+
h += '<div class="sect"><h2>' + esc(heading) + '</h2>';
|
|
237
|
+
h += lessons.length ? lessons.map(lessonCard).join("") : '<div class="empty">該当するレッスンがありません</div>';
|
|
238
|
+
h += '</div>';
|
|
239
|
+
el("main").innerHTML = h;
|
|
240
|
+
el("main").querySelectorAll(".lfilter button").forEach(b =>
|
|
241
|
+
b.onclick = () => { lessonStatus = b.dataset.st; loadLessons(); });
|
|
242
|
+
bindLessonActions();
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function lessonCard(l) {
|
|
246
|
+
const cond = (label, arr) => (arr && arr.length)
|
|
247
|
+
? '<div class="lcond"><b>' + label + ':</b> ' + arr.map(esc).join("; ") + '</div>' : "";
|
|
248
|
+
const tags = [];
|
|
249
|
+
if (l.concepts && l.concepts.length) tags.push('🏷 ' + l.concepts.map(esc).join(", "));
|
|
250
|
+
if (l.files && l.files.length) tags.push('📄 ' + l.files.map(esc).join(", "));
|
|
251
|
+
return '<div class="card" data-id="' + l.id + '">'
|
|
252
|
+
+ '<div class="meta">'
|
|
253
|
+
+ '<span class="lstatus ' + esc(l.status) + '">' + esc(l.status) + '</span>'
|
|
254
|
+
+ '<span class="tag">' + esc(l.scope) + '</span>'
|
|
255
|
+
+ '<span class="tag">conf ' + Number(l.confidence).toFixed(2) + '</span>'
|
|
256
|
+
+ (l.createdAt||"").split("T")[0] + '</div>'
|
|
257
|
+
+ '<div class="u" style="font-weight:600">' + esc(l.title) + '</div>'
|
|
258
|
+
+ '<div class="lbody">' + esc(l.lesson) + '</div>'
|
|
259
|
+
+ cond("Applies when", l.appliesWhen)
|
|
260
|
+
+ cond("Avoid when", l.avoidWhen)
|
|
261
|
+
+ (l.evidence ? '<div class="lcond"><b>Evidence:</b> ' + esc(l.evidence) + '</div>' : "")
|
|
262
|
+
+ (tags.length ? '<div class="cmeta">' + tags.join(" · ") + '</div>' : "")
|
|
263
|
+
+ lessonActions(l)
|
|
264
|
+
+ '</div>';
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function lessonActions(l) {
|
|
268
|
+
const scopeOpts = ["global","project","repo","file","task","user_preference","team"]
|
|
269
|
+
.map(s => '<option ' + (s===l.scope?'selected':'') + '>' + s + '</option>').join("");
|
|
270
|
+
let btns = "";
|
|
271
|
+
if (l.status !== "approved") btns += '<button data-act="approve">✓ Approve</button>';
|
|
272
|
+
if (l.status !== "rejected") btns += '<button class="danger" data-act="reject">✕ Reject</button>';
|
|
273
|
+
if (l.status !== "archived") btns += '<button data-act="archive">🗄 Archive</button>';
|
|
274
|
+
btns += '<select data-act="scope" title="scope変更">' + scopeOpts + '</select>';
|
|
275
|
+
return '<div class="lact">' + btns + '</div>';
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function bindLessonActions() {
|
|
279
|
+
el("main").querySelectorAll(".card[data-id]").forEach(card => {
|
|
280
|
+
const id = card.dataset.id;
|
|
281
|
+
card.querySelectorAll(".lact button").forEach(b =>
|
|
282
|
+
b.onclick = () => lessonAction(id, b.dataset.act));
|
|
283
|
+
const sel = card.querySelector('select[data-act="scope"]');
|
|
284
|
+
if (sel) sel.onchange = () => lessonAction(id, "scope", { scope: sel.value });
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
async function lessonAction(id, action, body) {
|
|
289
|
+
try {
|
|
290
|
+
await fetch("/api/lessons/" + encodeURIComponent(id) + "/" + action, {
|
|
291
|
+
method: "POST",
|
|
292
|
+
headers: { "content-type": "application/json" },
|
|
293
|
+
body: JSON.stringify(body||{}),
|
|
294
|
+
});
|
|
295
|
+
} catch { /* ignore */ }
|
|
296
|
+
loadLessons();
|
|
297
|
+
}
|
|
298
|
+
|
|
178
299
|
boot();
|
|
179
300
|
connectEvents();
|
|
180
301
|
</script>
|
package/dist/ui/server.js
CHANGED
|
@@ -10,6 +10,7 @@ import { listProjects } from "../core/projects.js";
|
|
|
10
10
|
import { getRecentSummaries, getPreferences } from "../core/memory.js";
|
|
11
11
|
import { listChunks, getChunkCount } from "../core/vector-memory.js";
|
|
12
12
|
import { searchLogs } from "../core/logsearch/search.js";
|
|
13
|
+
import { listLessons, listConflicts, getConflictCount, getLesson, getLessonCount, getEvents, getLinks, setStatus, supersede, updateLesson, } from "../core/lessons.js";
|
|
13
14
|
import { PAGE } from "./page.js";
|
|
14
15
|
function countSummaries(projectId) {
|
|
15
16
|
return sqlite
|
|
@@ -30,7 +31,7 @@ export function buildUiApp() {
|
|
|
30
31
|
// cheap, I/O-free change signal — no client-side polling required.
|
|
31
32
|
app.get("/api/events", (c) => {
|
|
32
33
|
return streamSSE(c, async (stream) => {
|
|
33
|
-
const dataVersion = () => sqlite.
|
|
34
|
+
const dataVersion = () => sqlite.prepare("PRAGMA data_version").get().data_version;
|
|
34
35
|
let last = dataVersion();
|
|
35
36
|
await stream.writeSSE({ event: "ready", data: String(last) });
|
|
36
37
|
while (!stream.closed && !stream.aborted) {
|
|
@@ -62,6 +63,7 @@ export function buildUiApp() {
|
|
|
62
63
|
summaries: countSummaries(p.id),
|
|
63
64
|
chunks: getChunkCount(p.id),
|
|
64
65
|
preferences: countPreferences(p.id),
|
|
66
|
+
lessons: getLessonCount({ projectId: p.id }),
|
|
65
67
|
},
|
|
66
68
|
}));
|
|
67
69
|
return c.json(out);
|
|
@@ -76,6 +78,68 @@ export function buildUiApp() {
|
|
|
76
78
|
chunks: listChunks(projectId, 300),
|
|
77
79
|
});
|
|
78
80
|
});
|
|
81
|
+
// --- Lesson layer -------------------------------------------------------
|
|
82
|
+
// The viewer is read-only for chunks/summaries, but lessons are reviewable:
|
|
83
|
+
// these POST routes are the only writes the UI performs, and only ever touch
|
|
84
|
+
// the lessons tables.
|
|
85
|
+
app.get("/api/lessons", (c) => {
|
|
86
|
+
const projectId = c.req.query("project") || undefined;
|
|
87
|
+
const status = c.req.query("status") || undefined;
|
|
88
|
+
// "conflicts" is a virtual view (lessons in a conflicts_with link), not a
|
|
89
|
+
// real status column value.
|
|
90
|
+
const lessons = status === "conflicts"
|
|
91
|
+
? listConflicts(projectId, 500)
|
|
92
|
+
: listLessons({ projectId, status }, 500);
|
|
93
|
+
return c.json({
|
|
94
|
+
lessons,
|
|
95
|
+
counts: {
|
|
96
|
+
candidate: getLessonCount({ projectId, status: "candidate" }),
|
|
97
|
+
approved: getLessonCount({ projectId, status: "approved" }),
|
|
98
|
+
rejected: getLessonCount({ projectId, status: "rejected" }),
|
|
99
|
+
archived: getLessonCount({ projectId, status: "archived" }),
|
|
100
|
+
superseded: getLessonCount({ projectId, status: "superseded" }),
|
|
101
|
+
conflicts: getConflictCount(projectId),
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
app.get("/api/lessons/:id", (c) => {
|
|
106
|
+
const lesson = getLesson(c.req.param("id"));
|
|
107
|
+
if (!lesson)
|
|
108
|
+
return c.json({ error: "not found" }, 404);
|
|
109
|
+
return c.json({ lesson, events: getEvents(lesson.id), links: getLinks(lesson.id) });
|
|
110
|
+
});
|
|
111
|
+
app.post("/api/lessons/:id/:action", async (c) => {
|
|
112
|
+
const id = c.req.param("id");
|
|
113
|
+
const action = c.req.param("action");
|
|
114
|
+
const body = await c.req.json().catch(() => ({}));
|
|
115
|
+
let ok = false;
|
|
116
|
+
switch (action) {
|
|
117
|
+
case "approve":
|
|
118
|
+
ok = setStatus(id, "approved");
|
|
119
|
+
break;
|
|
120
|
+
case "reject":
|
|
121
|
+
ok = setStatus(id, "rejected", body.reason);
|
|
122
|
+
break;
|
|
123
|
+
case "archive":
|
|
124
|
+
ok = setStatus(id, "archived", body.reason);
|
|
125
|
+
break;
|
|
126
|
+
case "status":
|
|
127
|
+
ok = setStatus(id, body.status, body.note);
|
|
128
|
+
break;
|
|
129
|
+
case "scope":
|
|
130
|
+
ok = updateLesson(id, { scope: String(body.scope) });
|
|
131
|
+
break;
|
|
132
|
+
case "confidence":
|
|
133
|
+
ok = updateLesson(id, { confidence: Number(body.confidence) });
|
|
134
|
+
break;
|
|
135
|
+
case "supersede":
|
|
136
|
+
ok = supersede(id, String(body.newId));
|
|
137
|
+
break;
|
|
138
|
+
default:
|
|
139
|
+
return c.json({ error: "unknown action" }, 400);
|
|
140
|
+
}
|
|
141
|
+
return c.json({ ok }, ok ? 200 : 404);
|
|
142
|
+
});
|
|
79
143
|
// Raw transcript search (cc-search port) — Claude Code + Codex logs.
|
|
80
144
|
app.get("/api/logs", async (c) => {
|
|
81
145
|
const query = c.req.query("q") ?? "";
|
package/hooks/claw-hook.sh
CHANGED
|
@@ -4,18 +4,42 @@
|
|
|
4
4
|
# mcp: claw-hook.sh mcp (stdio MCP server)
|
|
5
5
|
# Prefers a globally installed `claw-memory`; falls back to npx. stdin/stdout are
|
|
6
6
|
# inherited so hook input and MCP stdio pass through. Never blocks a session:
|
|
7
|
-
# hook failures
|
|
7
|
+
# hook failures don't propagate, but they are LOGGED (not swallowed) — a silent
|
|
8
|
+
# 2>/dev/null here once hid a 3-week total outage. `mcp` execs directly.
|
|
8
9
|
set -euo pipefail
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
HOOK_ERR_LOG="${CLAW_MEMORY_DIR:-$HOME/.claw-memory}/logs/hook-error.log"
|
|
12
|
+
|
|
13
|
+
# node:sqlite still carries an ExperimentalWarning on stderr; silence it so the
|
|
14
|
+
# error log only contains real failures.
|
|
15
|
+
export NODE_OPTIONS="${NODE_OPTIONS:+$NODE_OPTIONS }--disable-warning=ExperimentalWarning"
|
|
16
|
+
|
|
17
|
+
# Resolve the binary into $CMD (array, npx form needs multiple words).
|
|
18
|
+
if command -v claw-memory >/dev/null 2>&1; then
|
|
19
|
+
CMD=(claw-memory)
|
|
20
|
+
else
|
|
21
|
+
CMD=(npx -y @nogataka/claw-memory@latest)
|
|
22
|
+
fi
|
|
16
23
|
|
|
17
24
|
if [ "${1:-}" = "mcp" ]; then
|
|
18
|
-
|
|
25
|
+
exec "${CMD[@]}" "$@"
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
mkdir -p "$(dirname "$HOOK_ERR_LOG")" 2>/dev/null || true
|
|
29
|
+
# Capture stderr synchronously to a temp file, then append it timestamped.
|
|
30
|
+
# (An async process-substitution logger here raced script exit and dropped
|
|
31
|
+
# lines — exactly the silent-failure mode this log exists to prevent.)
|
|
32
|
+
ERR_TMP="$(mktemp "${TMPDIR:-/tmp}/claw-hook-err.XXXXXX" 2>/dev/null)" || ERR_TMP=""
|
|
33
|
+
if [ -n "$ERR_TMP" ]; then
|
|
34
|
+
"${CMD[@]}" "$@" 2>"$ERR_TMP" || true
|
|
35
|
+
if [ -s "$ERR_TMP" ]; then
|
|
36
|
+
TS="$(date '+%Y-%m-%dT%H:%M:%S%z')"
|
|
37
|
+
while IFS= read -r line || [ -n "$line" ]; do
|
|
38
|
+
printf '%s %s\n' "$TS" "$line"
|
|
39
|
+
done < "$ERR_TMP" >> "$HOOK_ERR_LOG" || true
|
|
40
|
+
fi
|
|
41
|
+
rm -f "$ERR_TMP"
|
|
19
42
|
else
|
|
20
|
-
|
|
43
|
+
# mktemp failed; still never silent — append raw stderr directly.
|
|
44
|
+
"${CMD[@]}" "$@" 2>>"$HOOK_ERR_LOG" || true
|
|
21
45
|
fi
|
package/hooks/hooks.codex.json
CHANGED
|
@@ -11,17 +11,6 @@
|
|
|
11
11
|
]
|
|
12
12
|
}
|
|
13
13
|
],
|
|
14
|
-
"UserPromptSubmit": [
|
|
15
|
-
{
|
|
16
|
-
"hooks": [
|
|
17
|
-
{
|
|
18
|
-
"type": "command",
|
|
19
|
-
"command": "\"$CLAUDE_PLUGIN_ROOT/hooks/run-hook.cmd\" claw-hook.sh hook recall",
|
|
20
|
-
"async": false
|
|
21
|
-
}
|
|
22
|
-
]
|
|
23
|
-
}
|
|
24
|
-
],
|
|
25
14
|
"Stop": [
|
|
26
15
|
{
|
|
27
16
|
"hooks": [
|
package/hooks/hooks.json
CHANGED
|
@@ -11,17 +11,6 @@
|
|
|
11
11
|
]
|
|
12
12
|
}
|
|
13
13
|
],
|
|
14
|
-
"UserPromptSubmit": [
|
|
15
|
-
{
|
|
16
|
-
"hooks": [
|
|
17
|
-
{
|
|
18
|
-
"type": "command",
|
|
19
|
-
"command": "\"$CLAUDE_PLUGIN_ROOT/hooks/run-hook.cmd\" claw-hook.sh hook recall",
|
|
20
|
-
"async": false
|
|
21
|
-
}
|
|
22
|
-
]
|
|
23
|
-
}
|
|
24
|
-
],
|
|
25
14
|
"Stop": [
|
|
26
15
|
{
|
|
27
16
|
"hooks": [
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nogataka/claw-memory",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "Independent, in-process semantic memory MCP server (sqlite-vec + local Xenova e5) with a lightweight web viewer. No daemon, no Python. Installable as a Claude Code plugin and a Codex MCP server.",
|
|
5
|
+
"description": "Independent, in-process semantic memory MCP server (node:sqlite + sqlite-vec + local Xenova e5) with a lightweight web viewer. No daemon, no Python, no native ABI. Installable as a Claude Code plugin and a Codex MCP server.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "nogataka",
|
|
8
8
|
"repository": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"README.ja.md"
|
|
33
33
|
],
|
|
34
34
|
"engines": {
|
|
35
|
-
"node": ">=
|
|
35
|
+
"node": ">=24.0.0"
|
|
36
36
|
},
|
|
37
37
|
"publishConfig": {
|
|
38
38
|
"access": "public"
|
|
@@ -53,14 +53,12 @@
|
|
|
53
53
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
54
54
|
"@openai/codex-sdk": "^0.135.0",
|
|
55
55
|
"@xenova/transformers": "^2.17.2",
|
|
56
|
-
"better-sqlite3": "12.8.0",
|
|
57
56
|
"hono": "^4.6.0",
|
|
58
57
|
"sqlite-vec": "^0.1.7-alpha.2",
|
|
59
58
|
"zod": "^3.24.0"
|
|
60
59
|
},
|
|
61
60
|
"devDependencies": {
|
|
62
|
-
"@types/
|
|
63
|
-
"@types/node": "^22.0.0",
|
|
61
|
+
"@types/node": "^24.0.0",
|
|
64
62
|
"tsx": "^4.19.0",
|
|
65
63
|
"typescript": "^5.6.0"
|
|
66
64
|
}
|