@ephemushroom/opencode-claude-mem 0.4.3 → 0.5.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/README.md +179 -45
- package/dist/cli.d.ts +17 -0
- package/dist/cli.js +694 -0
- package/dist/index.js +2126 -2119
- package/dist/package-server.d.ts +7 -0
- package/dist/package-server.js +10 -0
- package/dist/package-tui.d.ts +6 -0
- package/dist/package-tui.js +8 -0
- package/dist/shared.d.ts +14 -0
- package/dist/sidebar-model.d.ts +50 -0
- package/dist/tui-registration.d.ts +7 -0
- package/dist/tui.d.ts +0 -36
- package/dist/tui.js +30 -32
- package/dist/v2.d.ts +22 -0
- package/dist/v2.js +992 -0
- package/dist/worker-client.d.ts +6 -6
- package/package.json +28 -13
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { ClaudeMemPlugin } from './index';
|
|
2
|
+
declare const _default: {
|
|
3
|
+
id: string;
|
|
4
|
+
server: import("@opencode-ai/plugin").Plugin;
|
|
5
|
+
setup: (context: import("@opencode-ai/plugin-v2/promise/plugin").Context) => Promise<import("@opencode-ai/plugin-v2/promise/plugin").Cleanup | void> | import("@opencode-ai/plugin-v2/promise/plugin").Cleanup | void;
|
|
6
|
+
};
|
|
7
|
+
export default _default;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/package-server.ts
|
|
3
|
+
import v1 from "./index";
|
|
4
|
+
import v2 from "./v2";
|
|
5
|
+
import { ClaudeMemPlugin } from "./index";
|
|
6
|
+
var package_server_default = { id: v2.id, server: v1.server, setup: v2.setup };
|
|
7
|
+
export {
|
|
8
|
+
ClaudeMemPlugin,
|
|
9
|
+
package_server_default as default
|
|
10
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
id: string;
|
|
3
|
+
tui: (api: any) => Promise<void>;
|
|
4
|
+
setup: (context: import("@opencode-ai/plugin-v2/tui/context").Context) => Promise<import("@opencode-ai/plugin-v2/tui/plugin").Cleanup | void> | import("@opencode-ai/plugin-v2/tui/plugin").Cleanup | void;
|
|
5
|
+
};
|
|
6
|
+
export default _default;
|
package/dist/shared.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const MAX_OBSERVATION_BYTES: number;
|
|
2
|
+
export declare const MAX_TAG_REPLACEMENTS = 100;
|
|
3
|
+
export declare const META_TOOLS: Set<string>;
|
|
4
|
+
export declare function stripTaggedContent(text: string): string;
|
|
5
|
+
export declare function sanitizeObservationValue(value: unknown): unknown;
|
|
6
|
+
export declare function truncateUtf8Bytes(text: string, maxBytes: number): string;
|
|
7
|
+
export declare function shouldSkipObservationTool(toolName: string): boolean;
|
|
8
|
+
export declare function normalizeToolOutput(output: unknown): string;
|
|
9
|
+
/**
|
|
10
|
+
* Extract text content from message parts.
|
|
11
|
+
* Parts can be TextPart, ToolCallPart, etc. We only want text.
|
|
12
|
+
* Skips synthetic/ignored parts to match what is shown to the user.
|
|
13
|
+
*/
|
|
14
|
+
export declare function extractTextFromParts(parts: any[]): string;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export declare const POLL_INTERVAL_MS = 5000;
|
|
2
|
+
export declare const OBSERVATION_TYPE_ICONS: Record<string, string>;
|
|
3
|
+
export interface MemSidebarView {
|
|
4
|
+
readonly healthy: boolean;
|
|
5
|
+
readonly project: string;
|
|
6
|
+
readonly observations: number | null;
|
|
7
|
+
readonly sessions: number | null;
|
|
8
|
+
readonly summaries: number | null;
|
|
9
|
+
readonly queueDepth: number | null;
|
|
10
|
+
readonly isProcessing: boolean;
|
|
11
|
+
readonly recentSummaries: readonly {
|
|
12
|
+
id: number;
|
|
13
|
+
request: string;
|
|
14
|
+
}[];
|
|
15
|
+
readonly recentObservations: readonly {
|
|
16
|
+
id: number;
|
|
17
|
+
type: string;
|
|
18
|
+
title: string;
|
|
19
|
+
}[];
|
|
20
|
+
}
|
|
21
|
+
export interface ViewNode {
|
|
22
|
+
readonly kind: 'box' | 'text';
|
|
23
|
+
readonly props: Record<string, unknown>;
|
|
24
|
+
readonly children?: readonly ViewNode[];
|
|
25
|
+
readonly text?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface Theme {
|
|
28
|
+
readonly text: unknown;
|
|
29
|
+
readonly textMuted: unknown;
|
|
30
|
+
readonly info: unknown;
|
|
31
|
+
readonly success: unknown;
|
|
32
|
+
readonly warning: unknown;
|
|
33
|
+
readonly error: unknown;
|
|
34
|
+
readonly borderSubtle: unknown;
|
|
35
|
+
}
|
|
36
|
+
export declare function box(props: Record<string, unknown>, children: readonly ViewNode[]): ViewNode;
|
|
37
|
+
export declare function text(props: Record<string, unknown>, value: string): ViewNode;
|
|
38
|
+
export declare function decodeEntities(value: string): string;
|
|
39
|
+
export declare function truncate(value: string): string;
|
|
40
|
+
export declare function formatCount(value: number | null): string;
|
|
41
|
+
export declare function summaryLine(view: MemSidebarView): {
|
|
42
|
+
label: string;
|
|
43
|
+
tone: 'ok' | 'warn' | 'err';
|
|
44
|
+
};
|
|
45
|
+
export declare function toneColor(tone: 'ok' | 'warn' | 'err', theme: Theme): unknown;
|
|
46
|
+
export declare function bulletRow(icon: string, iconColor: unknown, label: string, theme: Theme): ViewNode;
|
|
47
|
+
export declare function expandedRows(view: MemSidebarView, theme: Theme): ViewNode[];
|
|
48
|
+
export declare function buildMemNodes(view: MemSidebarView, theme: Theme, collapsed: boolean, onToggle: () => void): ViewNode[];
|
|
49
|
+
export declare function viewKey(view: MemSidebarView): string;
|
|
50
|
+
export declare function readMemView(project: string, collapsed: boolean): Promise<MemSidebarView>;
|
|
@@ -7,3 +7,10 @@ export type TuiRegistrationResult = 'added' | 'already-present' | 'no-server-ent
|
|
|
7
7
|
* target instead of being replaced by a regular file.
|
|
8
8
|
*/
|
|
9
9
|
export declare function ensureTuiPluginEntry(): TuiRegistrationResult;
|
|
10
|
+
/**
|
|
11
|
+
* V2 self-heal: register the V2 CLI entry in `~/.config/opencode/cli.json`
|
|
12
|
+
* (the V2 CLI/TUI config file) so the Memory sidebar loads in the V2 TUI
|
|
13
|
+
* without manual configuration. The CLI writes strict JSON (2-space indent),
|
|
14
|
+
* so a comment-bearing file is left untouched ('malformed').
|
|
15
|
+
*/
|
|
16
|
+
export declare function ensureCliPluginEntry(): TuiRegistrationResult;
|
package/dist/tui.d.ts
CHANGED
|
@@ -1,39 +1,3 @@
|
|
|
1
|
-
export interface MemSidebarView {
|
|
2
|
-
readonly healthy: boolean;
|
|
3
|
-
readonly project: string;
|
|
4
|
-
readonly observations: number | null;
|
|
5
|
-
readonly sessions: number | null;
|
|
6
|
-
readonly summaries: number | null;
|
|
7
|
-
readonly queueDepth: number | null;
|
|
8
|
-
readonly isProcessing: boolean;
|
|
9
|
-
readonly recentSummaries: readonly {
|
|
10
|
-
id: number;
|
|
11
|
-
request: string;
|
|
12
|
-
}[];
|
|
13
|
-
readonly recentObservations: readonly {
|
|
14
|
-
id: number;
|
|
15
|
-
type: string;
|
|
16
|
-
title: string;
|
|
17
|
-
}[];
|
|
18
|
-
}
|
|
19
|
-
interface ViewNode {
|
|
20
|
-
readonly kind: 'box' | 'text';
|
|
21
|
-
readonly props: Record<string, unknown>;
|
|
22
|
-
readonly children?: readonly ViewNode[];
|
|
23
|
-
readonly text?: string;
|
|
24
|
-
}
|
|
25
|
-
interface Theme {
|
|
26
|
-
readonly text: string;
|
|
27
|
-
readonly textMuted: string;
|
|
28
|
-
readonly info: string;
|
|
29
|
-
readonly success: string;
|
|
30
|
-
readonly warning: string;
|
|
31
|
-
readonly error: string;
|
|
32
|
-
readonly borderSubtle: string;
|
|
33
|
-
}
|
|
34
|
-
export declare function buildMemNodes(view: MemSidebarView, theme: Theme, collapsed: boolean, onToggle: () => void): ViewNode[];
|
|
35
|
-
export declare function viewKey(view: MemSidebarView): string;
|
|
36
|
-
export declare function readMemView(project: string, collapsed: boolean): Promise<MemSidebarView>;
|
|
37
1
|
declare const tuiModule: {
|
|
38
2
|
id: string;
|
|
39
3
|
tui: (api: any) => Promise<void>;
|
package/dist/tui.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var __require = import.meta.require;
|
|
3
|
-
|
|
4
2
|
// src/worker-client.ts
|
|
5
3
|
import { existsSync, readFileSync } from "fs";
|
|
6
4
|
import { homedir } from "os";
|
|
7
5
|
import { join } from "path";
|
|
8
6
|
import { spawn } from "child_process";
|
|
9
7
|
var HEALTH_TIMEOUT_MS = 1000;
|
|
8
|
+
var WRITE_TIMEOUT_MS = 1e4;
|
|
10
9
|
var AUTOSTART_POLL_INTERVAL_MS = 500;
|
|
11
10
|
var AUTOSTART_POLL_MAX_ATTEMPTS = 16;
|
|
12
11
|
var DEFAULT_WORKER_HOST = "127.0.0.1";
|
|
@@ -92,14 +91,14 @@ function extractTextContent(value) {
|
|
|
92
91
|
return content.trim() || null;
|
|
93
92
|
}
|
|
94
93
|
if (Array.isArray(content)) {
|
|
95
|
-
const
|
|
94
|
+
const text = content.map((part) => {
|
|
96
95
|
if (!isRecord(part) || part["type"] !== "text" || typeof part["text"] !== "string") {
|
|
97
96
|
return "";
|
|
98
97
|
}
|
|
99
98
|
return part["text"];
|
|
100
99
|
}).filter(Boolean).join(`
|
|
101
100
|
`).trim();
|
|
102
|
-
return
|
|
101
|
+
return text || null;
|
|
103
102
|
}
|
|
104
103
|
const text = JSON.stringify(value, null, 2);
|
|
105
104
|
return text === "{}" || text === "null" ? null : text;
|
|
@@ -110,20 +109,17 @@ class WorkerClient {
|
|
|
110
109
|
static autoStartPromise = null;
|
|
111
110
|
static async isHealthy() {
|
|
112
111
|
try {
|
|
113
|
-
const controller = new AbortController;
|
|
114
|
-
const timeoutId = setTimeout(() => controller.abort(), HEALTH_TIMEOUT_MS);
|
|
115
112
|
const response = await fetch(`${this.BASE_URL}/api/health`, {
|
|
116
|
-
signal:
|
|
113
|
+
signal: AbortSignal.timeout(HEALTH_TIMEOUT_MS)
|
|
117
114
|
});
|
|
118
|
-
clearTimeout(timeoutId);
|
|
119
115
|
return response.ok;
|
|
120
116
|
} catch {
|
|
121
117
|
return false;
|
|
122
118
|
}
|
|
123
119
|
}
|
|
124
120
|
static async ensureRunning() {
|
|
125
|
-
|
|
126
|
-
return
|
|
121
|
+
await this.tryAutoStart();
|
|
122
|
+
return this.isHealthy();
|
|
127
123
|
}
|
|
128
124
|
static tryAutoStart() {
|
|
129
125
|
if (!this.autoStartPromise) {
|
|
@@ -191,6 +187,7 @@ class WorkerClient {
|
|
|
191
187
|
try {
|
|
192
188
|
const response = await fetch(`${this.BASE_URL}/api/sessions/init`, {
|
|
193
189
|
method: "POST",
|
|
190
|
+
signal: AbortSignal.timeout(WRITE_TIMEOUT_MS),
|
|
194
191
|
headers: { "Content-Type": "application/json" },
|
|
195
192
|
body: JSON.stringify({
|
|
196
193
|
contentSessionId,
|
|
@@ -207,10 +204,11 @@ class WorkerClient {
|
|
|
207
204
|
return null;
|
|
208
205
|
}
|
|
209
206
|
}
|
|
210
|
-
static async sendObservation(contentSessionId, toolName, toolInput, toolResponse, cwd) {
|
|
207
|
+
static async sendObservation(contentSessionId, toolName, toolInput, toolResponse, cwd, toolUseId) {
|
|
211
208
|
try {
|
|
212
|
-
await fetch(`${this.BASE_URL}/api/sessions/observations`, {
|
|
209
|
+
const response = await fetch(`${this.BASE_URL}/api/sessions/observations`, {
|
|
213
210
|
method: "POST",
|
|
211
|
+
signal: AbortSignal.timeout(WRITE_TIMEOUT_MS),
|
|
214
212
|
headers: { "Content-Type": "application/json" },
|
|
215
213
|
body: JSON.stringify({
|
|
216
214
|
contentSessionId,
|
|
@@ -218,33 +216,34 @@ class WorkerClient {
|
|
|
218
216
|
tool_input: toolInput,
|
|
219
217
|
tool_response: toolResponse,
|
|
220
218
|
cwd,
|
|
221
|
-
platformSource: PLATFORM_SOURCE
|
|
219
|
+
platformSource: PLATFORM_SOURCE,
|
|
220
|
+
tool_use_id: toolUseId
|
|
222
221
|
})
|
|
223
222
|
});
|
|
224
|
-
|
|
223
|
+
return response.ok;
|
|
224
|
+
} catch {
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
225
227
|
}
|
|
226
|
-
static async summarize(contentSessionId, lastUserMessage, lastAssistantMessage) {
|
|
228
|
+
static async summarize(contentSessionId, lastUserMessage, lastAssistantMessage, metadata = {}) {
|
|
227
229
|
try {
|
|
228
|
-
await fetch(`${this.BASE_URL}/api/sessions/summarize`, {
|
|
230
|
+
const response = await fetch(`${this.BASE_URL}/api/sessions/summarize`, {
|
|
229
231
|
method: "POST",
|
|
232
|
+
signal: AbortSignal.timeout(WRITE_TIMEOUT_MS),
|
|
230
233
|
headers: { "Content-Type": "application/json" },
|
|
231
234
|
body: JSON.stringify({
|
|
232
235
|
contentSessionId,
|
|
233
236
|
last_user_message: lastUserMessage,
|
|
234
237
|
last_assistant_message: lastAssistantMessage,
|
|
235
|
-
platformSource: PLATFORM_SOURCE
|
|
238
|
+
platformSource: PLATFORM_SOURCE,
|
|
239
|
+
observedModel: metadata.observedModel,
|
|
240
|
+
observedBilling: metadata.observedBilling
|
|
236
241
|
})
|
|
237
242
|
});
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
await fetch(`${this.BASE_URL}/api/sessions/complete`, {
|
|
243
|
-
method: "POST",
|
|
244
|
-
headers: { "Content-Type": "application/json" },
|
|
245
|
-
body: JSON.stringify({ contentSessionId, platformSource: PLATFORM_SOURCE })
|
|
246
|
-
});
|
|
247
|
-
} catch {}
|
|
243
|
+
return response.ok;
|
|
244
|
+
} catch {
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
248
247
|
}
|
|
249
248
|
static async getContext(project) {
|
|
250
249
|
try {
|
|
@@ -399,7 +398,7 @@ class WorkerClient {
|
|
|
399
398
|
}
|
|
400
399
|
}
|
|
401
400
|
|
|
402
|
-
// src/
|
|
401
|
+
// src/sidebar-model.ts
|
|
403
402
|
var POLL_INTERVAL_MS = 5000;
|
|
404
403
|
var LABEL_MAX = 40;
|
|
405
404
|
var RECENT_SUMMARIES = 3;
|
|
@@ -537,6 +536,8 @@ async function readMemView(project, collapsed) {
|
|
|
537
536
|
recentObservations
|
|
538
537
|
};
|
|
539
538
|
}
|
|
539
|
+
|
|
540
|
+
// src/tui.ts
|
|
540
541
|
async function loadSolidCore() {
|
|
541
542
|
try {
|
|
542
543
|
const mod = await import("solid-js");
|
|
@@ -652,8 +653,5 @@ var tuiModule = {
|
|
|
652
653
|
};
|
|
653
654
|
var tui_default = tuiModule;
|
|
654
655
|
export {
|
|
655
|
-
|
|
656
|
-
readMemView,
|
|
657
|
-
tui_default as default,
|
|
658
|
-
buildMemNodes
|
|
656
|
+
tui_default as default
|
|
659
657
|
};
|
package/dist/v2.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode V2 plugin for Claude-Mem.
|
|
3
|
+
*
|
|
4
|
+
* V2 is a breaking API change from V1 — plugins are `Plugin.define({ id,
|
|
5
|
+
* setup })` modules with a server-like context instead of a `{ server }`
|
|
6
|
+
* module of hook handlers. This entrypoint is published under the package's
|
|
7
|
+
* `./v2` export; V1 users keep loading the default export (see src/index.ts).
|
|
8
|
+
* Both entrypoints are thin adapters over the same WorkerClient + shared
|
|
9
|
+
* helpers, so V1 and V2 can coexist and talk to the same Claude-Mem worker.
|
|
10
|
+
*
|
|
11
|
+
* V2 API mapping (vs V1):
|
|
12
|
+
* - `experimental.chat.system.transform` → `ctx.session.hook('context')` (push
|
|
13
|
+
* a SystemPart into `event.system`)
|
|
14
|
+
* - `tool.execute.after` → `ctx.tool.hook('execute.after')`
|
|
15
|
+
* - `tool` definitions → `ctx.tool.transform((tools) => tools.add(...))`
|
|
16
|
+
* - `event` lifecycle → `ctx.event.subscribe()` stream
|
|
17
|
+
* - Assistant text capture uses `session.text.ended` events (the event carries
|
|
18
|
+
* the complete text — no debounced re-fetch needed like V1's message.updated)
|
|
19
|
+
* - Summarization fires on `session.execution.succeeded` (V1's session.idle)
|
|
20
|
+
*/
|
|
21
|
+
declare const _default: import("@opencode-ai/plugin-v2/promise/plugin").Plugin;
|
|
22
|
+
export default _default;
|