echoes-vault-opencode 1.2.3 → 2.0.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/EchoesProtocol.md +1097 -0
- package/README.md +150 -99
- package/index.ts +176 -429
- package/package.json +17 -6
- package/prompts/commands/echoes-end.md +9 -16
- package/prompts/commands/echoes-init.md +7 -31
- package/prompts/commands/echoes-start.md +6 -25
- package/prompts/commands/echoes-status.md +4 -32
- package/runtime.ts +157 -0
- package/scripts/echoes_vault.py +2454 -0
- package/tui.tsx +75 -75
- package/prompts/skills/echoes-append-to-daily-log.md +0 -22
- package/prompts/skills/echoes-create-or-update-page.md +0 -22
- package/prompts/skills/echoes-search-vault-pages.md +0 -19
package/tui.tsx
CHANGED
|
@@ -6,7 +6,8 @@ import * as path from "node:path"
|
|
|
6
6
|
|
|
7
7
|
type EchoesState = {
|
|
8
8
|
version: number
|
|
9
|
-
|
|
9
|
+
protocolVersion: string
|
|
10
|
+
engineVersion: string
|
|
10
11
|
initialized: boolean
|
|
11
12
|
session: {
|
|
12
13
|
started: boolean
|
|
@@ -19,122 +20,121 @@ type EchoesState = {
|
|
|
19
20
|
totalDailyLogs: number
|
|
20
21
|
deprecatedPages: number
|
|
21
22
|
}
|
|
23
|
+
lastWriter?: {
|
|
24
|
+
agent: string | null
|
|
25
|
+
adapterVersion: string | null
|
|
26
|
+
}
|
|
22
27
|
}
|
|
23
28
|
|
|
24
|
-
|
|
29
|
+
type EchoesMarker = {
|
|
30
|
+
protocolVersion?: string
|
|
31
|
+
}
|
|
25
32
|
|
|
26
|
-
|
|
33
|
+
type VaultSnapshot = {
|
|
34
|
+
marker: EchoesMarker | null
|
|
35
|
+
state: EchoesState | null
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const readJson = <Value,>(file: string): Value | null => {
|
|
27
39
|
try {
|
|
28
|
-
|
|
29
|
-
return JSON.parse(raw) as EchoesState
|
|
40
|
+
return JSON.parse(fs.readFileSync(file, "utf-8")) as Value
|
|
30
41
|
} catch {
|
|
31
42
|
return null
|
|
32
43
|
}
|
|
33
44
|
}
|
|
34
45
|
|
|
35
|
-
const
|
|
46
|
+
const readSnapshot = (workspace: string): VaultSnapshot => ({
|
|
47
|
+
marker: readJson<EchoesMarker>(path.join(workspace, "EchoesVault", ".echoes-vault.json")),
|
|
48
|
+
state: readJson<EchoesState>(path.join(workspace, ".echoes-vault", "state.json")),
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
const tui: TuiPlugin = async (api, _options, meta) => {
|
|
36
52
|
const theme = api.theme.current
|
|
53
|
+
const workspace = api.state.path.worktree || api.state.path.directory || process.cwd()
|
|
37
54
|
|
|
38
55
|
api.slots.register({
|
|
39
56
|
slots: {
|
|
40
57
|
sidebar_content() {
|
|
41
|
-
const [
|
|
42
|
-
|
|
43
|
-
const interval = setInterval(() => setState(readState()), 3000)
|
|
58
|
+
const [snapshot, setSnapshot] = createSignal<VaultSnapshot>(readSnapshot(workspace))
|
|
59
|
+
const interval = setInterval(() => setSnapshot(readSnapshot(workspace)), 3000)
|
|
44
60
|
onCleanup(() => clearInterval(interval))
|
|
45
61
|
|
|
62
|
+
const markerReady = () => snapshot().marker?.protocolVersion === "1.0.0"
|
|
63
|
+
const state = () => snapshot().state
|
|
64
|
+
|
|
46
65
|
const statusColor = () => {
|
|
47
|
-
|
|
48
|
-
if (
|
|
49
|
-
if (
|
|
50
|
-
if (s.session.started) return theme.success
|
|
66
|
+
if (!markerReady()) return theme.error
|
|
67
|
+
if (state()?.session.saved) return theme.accent
|
|
68
|
+
if (state()?.session.started) return theme.success
|
|
51
69
|
return theme.warning
|
|
52
70
|
}
|
|
53
71
|
|
|
54
72
|
const statusLabel = () => {
|
|
55
|
-
const
|
|
56
|
-
if (!
|
|
57
|
-
if (
|
|
58
|
-
if (
|
|
73
|
+
const marker = snapshot().marker
|
|
74
|
+
if (!marker) return "Not Initialized"
|
|
75
|
+
if (!markerReady()) return `Unsupported Protocol ${marker.protocolVersion ?? "?"}`
|
|
76
|
+
if (!state()) return "Ready — Local State Missing"
|
|
77
|
+
if (state()?.session.saved) return "Memory Saved"
|
|
78
|
+
if (state()?.session.started) return "Active"
|
|
59
79
|
return "Session Not Started"
|
|
60
80
|
}
|
|
61
81
|
|
|
82
|
+
const pages = () => state()?.stats?.totalPages ?? 0
|
|
62
83
|
const vaultHealthColor = () => {
|
|
63
|
-
|
|
64
|
-
if (pages
|
|
65
|
-
if (pages >= 170) return theme.warning
|
|
84
|
+
if (pages() > 200) return theme.error
|
|
85
|
+
if (pages() >= 170) return theme.warning
|
|
66
86
|
return theme.success
|
|
67
87
|
}
|
|
68
88
|
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const s = state()
|
|
76
|
-
if (!s || !s.initialized) return null
|
|
77
|
-
if (s.session.saved) return "Final memory saved by /echoes-end"
|
|
78
|
-
if (s.session.started) return null
|
|
79
|
-
return null
|
|
89
|
+
const statusCommand = () => {
|
|
90
|
+
if (!snapshot().marker) return ["/echoes-init", "to initialize or migrate"] as const
|
|
91
|
+
if (!markerReady()) return ["/echoes-status", "to inspect compatibility"] as const
|
|
92
|
+
if (state()?.session.saved) return null
|
|
93
|
+
if (state()?.session.started) return ["/echoes-end", "before closing"] as const
|
|
94
|
+
return ["/echoes-start", "to restore context"] as const
|
|
80
95
|
}
|
|
81
96
|
|
|
82
|
-
const
|
|
83
|
-
const
|
|
84
|
-
if (!
|
|
85
|
-
|
|
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
|
|
102
|
-
return (
|
|
103
|
-
<box flexDirection="column">
|
|
104
|
-
<text>{""}</text>
|
|
105
|
-
<box flexDirection="row">
|
|
106
|
-
<text fg={vaultHealthColor()}>{"• "}</text>
|
|
107
|
-
<text fg={theme.text}><b>Vault Health</b></text>
|
|
108
|
-
</box>
|
|
109
|
-
<text fg={theme.textMuted}>{"Pages: "}{pages}</text>
|
|
110
|
-
<text fg={theme.textMuted}>{"Usage: "}{vaultUsagePercent()}{"%"}</text>
|
|
111
|
-
{pages > 200 ? (
|
|
112
|
-
<text fg={theme.textMuted}>{"Consider migrating to RAG"}</text>
|
|
113
|
-
) : null}
|
|
114
|
-
</box>
|
|
115
|
-
)
|
|
97
|
+
const lastWriter = () => {
|
|
98
|
+
const writer = state()?.lastWriter
|
|
99
|
+
if (!writer?.agent) return null
|
|
100
|
+
return `${writer.agent}${writer.adapterVersion ? ` ${writer.adapterVersion}` : ""}`
|
|
116
101
|
}
|
|
117
102
|
|
|
118
103
|
return (
|
|
119
104
|
<box flexDirection="column">
|
|
120
105
|
<box flexDirection="row">
|
|
121
|
-
<text fg={statusColor()}><b
|
|
106
|
+
<text fg={statusColor()}><b>{"• "}</b></text>
|
|
122
107
|
<text fg={theme.textMuted}><b>Echoes</b></text>
|
|
123
108
|
<text fg={theme.text}><b>Vault</b></text>
|
|
124
|
-
<text fg={theme.textMuted}> v{
|
|
109
|
+
<text fg={theme.textMuted}>{meta.version ? ` v${meta.version}` : ""}</text>
|
|
125
110
|
</box>
|
|
126
111
|
<text fg={statusColor()}>{statusLabel()}</text>
|
|
127
|
-
{
|
|
128
|
-
<text fg={theme.textMuted}>{statusDesc()}</text>
|
|
129
|
-
)}
|
|
130
|
-
{statusCmd() && (
|
|
112
|
+
{statusCommand() ? (
|
|
131
113
|
<box flexDirection="row">
|
|
132
114
|
<text fg={theme.textMuted}>Run </text>
|
|
133
|
-
<text fg={theme.text}>{
|
|
134
|
-
<text fg={theme.textMuted}> {
|
|
115
|
+
<text fg={theme.text}>{statusCommand()?.[0]}</text>
|
|
116
|
+
<text fg={theme.textMuted}> {statusCommand()?.[1]}</text>
|
|
117
|
+
</box>
|
|
118
|
+
) : null}
|
|
119
|
+
{markerReady() ? (
|
|
120
|
+
<box flexDirection="column">
|
|
121
|
+
<text>{""}</text>
|
|
122
|
+
<box flexDirection="row">
|
|
123
|
+
<text fg={vaultHealthColor()}>{"• "}</text>
|
|
124
|
+
<text fg={theme.text}><b>Vault Health</b></text>
|
|
125
|
+
</box>
|
|
126
|
+
<text fg={theme.textMuted}>Pages: {pages()}</text>
|
|
127
|
+
<text fg={theme.textMuted}>Daily entries: {state()?.stats?.totalDailyLogs ?? 0}</text>
|
|
128
|
+
<text fg={theme.textMuted}>Protocol: {snapshot().marker?.protocolVersion}</text>
|
|
129
|
+
{state()?.engineVersion ? (
|
|
130
|
+
<text fg={theme.textMuted}>Engine: {state()?.engineVersion}</text>
|
|
131
|
+
) : null}
|
|
132
|
+
{lastWriter() ? <text fg={theme.textMuted}>Last writer: {lastWriter()}</text> : null}
|
|
133
|
+
{pages() > 200 ? (
|
|
134
|
+
<text fg={theme.textMuted}>Prefer targeted search over loading all pages</text>
|
|
135
|
+
) : null}
|
|
135
136
|
</box>
|
|
136
|
-
)}
|
|
137
|
-
{vaultHealthSection()}
|
|
137
|
+
) : null}
|
|
138
138
|
</box>
|
|
139
139
|
)
|
|
140
140
|
},
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: echoes_append_to_daily_log
|
|
3
|
-
description: Append an intermediate technical note or decision to today's daily log immediately after completing a sub-task.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# TOOL USAGE: echoes_append_to_daily_log
|
|
7
|
-
You are equipped with a scratchpad tool to manage your cognitive load. You MUST use this tool to offload important context into `EchoesVault/daily/YYYY-MM-DD.md`.
|
|
8
|
-
|
|
9
|
-
## 🎯 EXACT TRIGGER CONDITIONS (WHEN TO CALL THIS TOOL)
|
|
10
|
-
Do NOT use this tool randomly. You MUST invoke this tool IMMEDIATELY in the current response if ANY of the following specific events occur:
|
|
11
|
-
1. **Task Completion:** We successfully finish a logical unit of work (e.g., a script works, a bug is verified as fixed, tests pass) BEFORE starting the next user request.
|
|
12
|
-
2. **Context Switch:** The user asks to change focus (e.g., "Now let's work on the frontend" after we just worked on the backend).
|
|
13
|
-
3. **Architectural Agreement:** We just agreed on a core rule, library choice, database schema, or API contract.
|
|
14
|
-
4. **Explicit User Command:** The user explicitly tells you to "take a note", "remember this", "save our progress", or "log this".
|
|
15
|
-
|
|
16
|
-
## ⚠️ RULES
|
|
17
|
-
1. **Be Concise:** Write ONLY dry facts and bullet points (e.g., "Refactored AuthGuard to use JWT refresh tokens"). No conversational filler.
|
|
18
|
-
2. **Do Not Interrupt Flow:** Make the tool call silently or add a brief confirmation in your response like: *"Logged the AuthGuard update to the daily vault. Ready for the frontend."*
|
|
19
|
-
3. **No File Overwrites:** This tool ONLY appends to the end of today's file.
|
|
20
|
-
|
|
21
|
-
## 📥 PAYLOAD PARAMETERS
|
|
22
|
-
- `logEntry`: (String) The markdown-formatted bullet points to append.
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: echoes_create_or_update_page
|
|
3
|
-
description: Atomically create a new markdown page or update an existing one in EchoesVault/pages/, automatically updating the index.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# TOOL USAGE: echoes_create_or_update_page
|
|
7
|
-
Use this tool when a new global concept has been defined or an existing component's architecture has fundamentally changed during our session. This allows you to update the encyclopedia immediately.
|
|
8
|
-
|
|
9
|
-
## 🎯 WHEN TO USE
|
|
10
|
-
- We finalized a new database schema or API contract.
|
|
11
|
-
- A major refactoring occurred, rendering previous documentation inaccurate.
|
|
12
|
-
- You need to document a newly integrated library or hardware component.
|
|
13
|
-
|
|
14
|
-
## ⚠️ RULES
|
|
15
|
-
1. **Strict YAML Frontmatter:** Every page MUST include a YAML metadata block at the top (type, stack, status).
|
|
16
|
-
2. **Index Sync:** When you create a new file, you must provide a one-sentence description for the index. The system will automatically append it to `index.md`.
|
|
17
|
-
3. **Deprecate, Don't Delete:** If you are rewriting an existing page completely because the logic changed, consider if you should instead create a new page (e.g., `api-v2.md`) and update the old one with a `> [!warning] DEPRECATED` callout via this tool.
|
|
18
|
-
|
|
19
|
-
## 📥 PAYLOAD PARAMETERS
|
|
20
|
-
- `filename`: (String) The exact filename without paths (e.g., `auth-architecture.md`).
|
|
21
|
-
- `content`: (String) The full markdown content of the page, starting with the YAML frontmatter.
|
|
22
|
-
- `indexDescription`: (String) A one-sentence description of the file. Required if this is a newly created file. Format: "- [[filename]]: description".
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: echoes_search_vault_pages
|
|
3
|
-
description: Search the EchoesVault for specific concepts, keywords, or implementation details.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# TOOL USAGE: echoes_search_vault_pages
|
|
7
|
-
You are the EchoesVault Keeper. If you encounter a concept, API, or architectural pattern in our conversation that you suspect is documented but you lack the full context, use this tool BEFORE generating code.
|
|
8
|
-
|
|
9
|
-
## 🎯 WHEN TO USE
|
|
10
|
-
- The user asks to modify an existing component, but its structure is not in your current context window.
|
|
11
|
-
- You need to verify if an Architectural Decision Record (ADR) exists for a specific technology.
|
|
12
|
-
- You want to fulfill the "Read-Before-Write" core rule.
|
|
13
|
-
|
|
14
|
-
## ⚠️ RULES
|
|
15
|
-
1. **Targeted Queries:** Use specific technical keywords (e.g., "AuthGuard", "esp32 pinout", "database schema") rather than natural language questions.
|
|
16
|
-
2. **Handle Deprecations:** If the search returns a file marked with `> [!warning] DEPRECATED`, look for the link to the new relevant file and read that instead.
|
|
17
|
-
|
|
18
|
-
## 📥 PAYLOAD PARAMETERS
|
|
19
|
-
- `query`: (String) The specific keyword or short phrase to search for across the `pages/` directory.
|