echoes-vault-opencode 1.1.6 → 1.1.8
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/package.json +4 -2
- package/tui.tsx +103 -114
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "echoes-vault-opencode",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.8",
|
|
4
4
|
"description": "EchoesVault — persistent memory plugin for OpenCode. Obsidian-style knowledge base with daily logs, encyclopedia pages, and session resumption.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -28,7 +28,9 @@
|
|
|
28
28
|
],
|
|
29
29
|
"license": "MIT",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@opencode-ai/plugin": ">=1.16.0"
|
|
31
|
+
"@opencode-ai/plugin": ">=1.16.0",
|
|
32
|
+
"@opentui/solid": ">=0.4.3",
|
|
33
|
+
"solid-js": ">=1.9.0"
|
|
32
34
|
},
|
|
33
35
|
"devDependencies": {
|
|
34
36
|
"@types/node": "^22.0.0",
|
package/tui.tsx
CHANGED
|
@@ -4,14 +4,6 @@ import { createSignal, onCleanup } from "solid-js"
|
|
|
4
4
|
import * as fs from "node:fs"
|
|
5
5
|
import * as path from "node:path"
|
|
6
6
|
|
|
7
|
-
console.log("[EchoesVault TUI] Module loaded")
|
|
8
|
-
|
|
9
|
-
type VaultStats = {
|
|
10
|
-
totalPages: number
|
|
11
|
-
totalDailyLogs: number
|
|
12
|
-
deprecatedPages: number
|
|
13
|
-
}
|
|
14
|
-
|
|
15
7
|
type EchoesState = {
|
|
16
8
|
version: number
|
|
17
9
|
pluginVersion: string
|
|
@@ -22,134 +14,131 @@ type EchoesState = {
|
|
|
22
14
|
lastStart: string | null
|
|
23
15
|
lastSave: string | null
|
|
24
16
|
}
|
|
25
|
-
stats?:
|
|
17
|
+
stats?: {
|
|
18
|
+
totalPages: number
|
|
19
|
+
totalDailyLogs: number
|
|
20
|
+
deprecatedPages: number
|
|
21
|
+
}
|
|
26
22
|
}
|
|
27
23
|
|
|
28
24
|
const STATE_PATH = path.join(process.cwd(), ".opencode", "echoes-state.json")
|
|
29
|
-
console.log("[EchoesVault TUI] STATE_PATH:", STATE_PATH)
|
|
30
25
|
|
|
31
26
|
const readState = (): EchoesState | null => {
|
|
32
27
|
try {
|
|
33
28
|
const raw = fs.readFileSync(STATE_PATH, "utf-8")
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return parsed
|
|
37
|
-
} catch (e) {
|
|
38
|
-
console.log("[EchoesVault TUI] State read failed:", e)
|
|
29
|
+
return JSON.parse(raw) as EchoesState
|
|
30
|
+
} catch {
|
|
39
31
|
return null
|
|
40
32
|
}
|
|
41
33
|
}
|
|
42
34
|
|
|
43
35
|
const tui: TuiPlugin = async (api) => {
|
|
44
|
-
console.log("[EchoesVault TUI] tui() called, api.theme available:", !!api.theme)
|
|
45
36
|
const theme = api.theme.current
|
|
46
|
-
console.log("[EchoesVault TUI] theme.current loaded")
|
|
47
|
-
|
|
48
|
-
try {
|
|
49
|
-
api.slots.register({
|
|
50
|
-
slots: {
|
|
51
|
-
sidebar_content() {
|
|
52
|
-
console.log("[EchoesVault TUI] sidebar_content() rendering")
|
|
53
|
-
const [state, setState] = createSignal<EchoesState | null>(readState())
|
|
54
|
-
|
|
55
|
-
const interval = setInterval(() => setState(readState()), 3000)
|
|
56
|
-
onCleanup(() => clearInterval(interval))
|
|
57
|
-
|
|
58
|
-
const statusColor = () => {
|
|
59
|
-
const s = state()
|
|
60
|
-
if (!s || !s.initialized) return theme.error
|
|
61
|
-
if (s.session.saved) return theme.accent
|
|
62
|
-
if (s.session.started) return theme.success
|
|
63
|
-
return theme.warning
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const statusLabel = () => {
|
|
67
|
-
const s = state()
|
|
68
|
-
if (!s || !s.initialized) return "Not Activated"
|
|
69
|
-
if (s.session.saved) return "Memory Saved"
|
|
70
|
-
if (s.session.started) return "Active"
|
|
71
|
-
return "Session Not Started"
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const vaultHealthColor = () => {
|
|
75
|
-
const pages = state()?.stats?.totalPages ?? 0
|
|
76
|
-
if (pages > 200) return theme.error
|
|
77
|
-
if (pages >= 170) return theme.warning
|
|
78
|
-
return theme.success
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const vaultUsagePercent = () => {
|
|
82
|
-
const pages = state()?.stats?.totalPages ?? 0
|
|
83
|
-
return Math.round((pages / 200) * 100)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const statusDesc = () => {
|
|
87
|
-
const s = state()
|
|
88
|
-
if (!s || !s.initialized) return null
|
|
89
|
-
if (s.session.saved) return "Vault memorized session data"
|
|
90
|
-
if (s.session.started) return null
|
|
91
|
-
return null
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const statusCmd = () => {
|
|
95
|
-
const s = state()
|
|
96
|
-
if (!s || !s.initialized) return "/echoes-init"
|
|
97
|
-
if (s.session.saved) return null
|
|
98
|
-
if (s.session.started) return "/echoes-end"
|
|
99
|
-
return "/echoes-start"
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const statusCmdHint = () => {
|
|
103
|
-
const s = state()
|
|
104
|
-
if (!s || !s.initialized) return "to activate vault"
|
|
105
|
-
if (s.session.saved) return null
|
|
106
|
-
if (s.session.started) return "before closing"
|
|
107
|
-
return "to load context"
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
console.log("[EchoesVault TUI] About to return JSX")
|
|
111
37
|
|
|
38
|
+
api.slots.register({
|
|
39
|
+
slots: {
|
|
40
|
+
sidebar_content() {
|
|
41
|
+
const [state, setState] = createSignal<EchoesState | null>(readState())
|
|
42
|
+
|
|
43
|
+
const interval = setInterval(() => setState(readState()), 3000)
|
|
44
|
+
onCleanup(() => clearInterval(interval))
|
|
45
|
+
|
|
46
|
+
const statusColor = () => {
|
|
47
|
+
const s = state()
|
|
48
|
+
if (!s || !s.initialized) return theme.error
|
|
49
|
+
if (s.session.saved) return theme.accent
|
|
50
|
+
if (s.session.started) return theme.success
|
|
51
|
+
return theme.warning
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const statusLabel = () => {
|
|
55
|
+
const s = state()
|
|
56
|
+
if (!s || !s.initialized) return "Not Activated"
|
|
57
|
+
if (s.session.saved) return "Memory Saved"
|
|
58
|
+
if (s.session.started) return "Active"
|
|
59
|
+
return "Session Not Started"
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const vaultHealthColor = () => {
|
|
63
|
+
const pages = state()?.stats?.totalPages ?? 0
|
|
64
|
+
if (pages > 200) return theme.error
|
|
65
|
+
if (pages >= 170) return theme.warning
|
|
66
|
+
return theme.success
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const vaultUsagePercent = () => {
|
|
70
|
+
const pages = state()?.stats?.totalPages ?? 0
|
|
71
|
+
return Math.round((pages / 200) * 100)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const statusDesc = () => {
|
|
75
|
+
const s = state()
|
|
76
|
+
if (!s || !s.initialized) return null
|
|
77
|
+
if (s.session.saved) return "Vault memorized session data"
|
|
78
|
+
if (s.session.started) return null
|
|
79
|
+
return null
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const statusCmd = () => {
|
|
83
|
+
const s = state()
|
|
84
|
+
if (!s || !s.initialized) return "/echoes-init"
|
|
85
|
+
if (s.session.saved) return null
|
|
86
|
+
if (s.session.started) return "/echoes-end"
|
|
87
|
+
return "/echoes-start"
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const statusCmdHint = () => {
|
|
91
|
+
const s = state()
|
|
92
|
+
if (!s || !s.initialized) return "to activate vault"
|
|
93
|
+
if (s.session.saved) return null
|
|
94
|
+
if (s.session.started) return "before closing"
|
|
95
|
+
return "to load context"
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const vaultHealthSection = () => {
|
|
99
|
+
const s = state()
|
|
100
|
+
if (!s || !s.initialized) return null
|
|
101
|
+
const pages = s.stats?.totalPages ?? 0
|
|
112
102
|
return (
|
|
113
103
|
<box flexDirection="column">
|
|
114
104
|
<box flexDirection="row">
|
|
115
|
-
<text fg={
|
|
116
|
-
<text fg={theme.
|
|
117
|
-
<text fg={theme.text}><b>Vault</b></text>
|
|
118
|
-
<text fg={theme.textMuted}> v{state()?.pluginVersion ?? ""}</text>
|
|
105
|
+
<text fg={vaultHealthColor()}>{"• "}</text>
|
|
106
|
+
<text fg={theme.text}><b>Vault Health</b></text>
|
|
119
107
|
</box>
|
|
120
|
-
<text fg={
|
|
121
|
-
{
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
{statusCmd() && (
|
|
125
|
-
<box flexDirection="row">
|
|
126
|
-
<text fg={theme.textMuted}>Run </text>
|
|
127
|
-
<text fg={theme.text}>{statusCmd()}</text>
|
|
128
|
-
<text fg={theme.textMuted}> {statusCmdHint()}</text>
|
|
129
|
-
</box>
|
|
130
|
-
)}
|
|
131
|
-
{state()?.initialized ? (
|
|
132
|
-
<box flexDirection="column">
|
|
133
|
-
<box flexDirection="row">
|
|
134
|
-
<text fg={vaultHealthColor()}>{"• "}</text>
|
|
135
|
-
<text fg={theme.text}><b>Vault Health</b></text>
|
|
136
|
-
</box>
|
|
137
|
-
<text fg={theme.textMuted}>{" Pages: "}{state()?.stats?.totalPages ?? 0}</text>
|
|
138
|
-
<text fg={theme.textMuted}>{" Usage: "}{vaultUsagePercent()}{"%"}</text>
|
|
139
|
-
{(state()?.stats?.totalPages ?? 0) > 200 ? (
|
|
140
|
-
<text fg={theme.textMuted}>{" Consider migrating to RAG"}</text>
|
|
141
|
-
) : null}
|
|
142
|
-
</box>
|
|
108
|
+
<text fg={theme.textMuted}>{" Pages: "}{pages}</text>
|
|
109
|
+
<text fg={theme.textMuted}>{" Usage: "}{vaultUsagePercent()}{"%"}</text>
|
|
110
|
+
{pages > 200 ? (
|
|
111
|
+
<text fg={theme.textMuted}>{" Consider migrating to RAG"}</text>
|
|
143
112
|
) : null}
|
|
144
113
|
</box>
|
|
145
114
|
)
|
|
146
|
-
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return (
|
|
118
|
+
<box flexDirection="column">
|
|
119
|
+
<box flexDirection="row">
|
|
120
|
+
<text fg={statusColor()}><b>• </b></text>
|
|
121
|
+
<text fg={theme.textMuted}><b>Echoes</b></text>
|
|
122
|
+
<text fg={theme.text}><b>Vault</b></text>
|
|
123
|
+
<text fg={theme.textMuted}> v{state()?.pluginVersion ?? ""}</text>
|
|
124
|
+
</box>
|
|
125
|
+
<text fg={statusColor()}>{statusLabel()}</text>
|
|
126
|
+
{statusDesc() && (
|
|
127
|
+
<text fg={theme.textMuted}>{statusDesc()}</text>
|
|
128
|
+
)}
|
|
129
|
+
{statusCmd() && (
|
|
130
|
+
<box flexDirection="row">
|
|
131
|
+
<text fg={theme.textMuted}>Run </text>
|
|
132
|
+
<text fg={theme.text}>{statusCmd()}</text>
|
|
133
|
+
<text fg={theme.textMuted}> {statusCmdHint()}</text>
|
|
134
|
+
</box>
|
|
135
|
+
)}
|
|
136
|
+
{vaultHealthSection()}
|
|
137
|
+
</box>
|
|
138
|
+
)
|
|
147
139
|
},
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
} catch (e) {
|
|
151
|
-
console.error("[EchoesVault TUI] slots.register() FAILED:", e)
|
|
152
|
-
}
|
|
140
|
+
},
|
|
141
|
+
})
|
|
153
142
|
}
|
|
154
143
|
|
|
155
144
|
export default { id: "echoes-vault-ui", tui }
|