echoes-vault-opencode 1.2.2 → 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/tui.tsx CHANGED
@@ -6,7 +6,8 @@ import * as path from "node:path"
6
6
 
7
7
  type EchoesState = {
8
8
  version: number
9
- pluginVersion: string
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
- const STATE_PATH = path.join(process.cwd(), ".opencode", "echoes-state.json")
29
+ type EchoesMarker = {
30
+ protocolVersion?: string
31
+ }
25
32
 
26
- const readState = (): EchoesState | null => {
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
- const raw = fs.readFileSync(STATE_PATH, "utf-8")
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 tui: TuiPlugin = async (api) => {
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 [state, setState] = createSignal<EchoesState | null>(readState())
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
- 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
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 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"
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
- const pages = state()?.stats?.totalPages ?? 0
64
- if (pages > 200) return theme.error
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 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
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 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
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>• </b></text>
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{state()?.pluginVersion ?? ""}</text>
109
+ <text fg={theme.textMuted}>{meta.version ? ` v${meta.version}` : ""}</text>
125
110
  </box>
126
111
  <text fg={statusColor()}>{statusLabel()}</text>
127
- {statusDesc() && (
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}>{statusCmd()}</text>
134
- <text fg={theme.textMuted}> {statusCmdHint()}</text>
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.