echoes-vault-opencode 1.1.0 → 1.1.1

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.
Files changed (3) hide show
  1. package/index.ts +40 -0
  2. package/package.json +1 -1
  3. package/tui.tsx +35 -0
package/index.ts CHANGED
@@ -19,10 +19,17 @@ type VaultPaths = {
19
19
  assets: string
20
20
  }
21
21
 
22
+ type VaultStats = {
23
+ totalPages: number
24
+ totalDailyLogs: number
25
+ deprecatedPages: number
26
+ }
27
+
22
28
  type EchoesState = {
23
29
  version: number
24
30
  pluginVersion: string
25
31
  initialized: boolean
32
+ stats: VaultStats
26
33
  session: {
27
34
  started: boolean
28
35
  saved: boolean
@@ -47,6 +54,7 @@ const defaultState = (): EchoesState => ({
47
54
  version: 1,
48
55
  pluginVersion: "0.0.0",
49
56
  initialized: false,
57
+ stats: { totalPages: 0, totalDailyLogs: 0, deprecatedPages: 0 },
50
58
  session: {
51
59
  started: false,
52
60
  saved: false,
@@ -55,6 +63,32 @@ const defaultState = (): EchoesState => ({
55
63
  },
56
64
  })
57
65
 
66
+ const collectStats = async (vaultPaths: VaultPaths): Promise<VaultStats> => {
67
+ let totalPages = 0
68
+ let deprecatedPages = 0
69
+ let totalDailyLogs = 0
70
+ try {
71
+ const pageFiles = (await fs.readdir(vaultPaths.pages)).filter((f) => f.endsWith(".md"))
72
+ totalPages = pageFiles.length
73
+ for (const file of pageFiles) {
74
+ const content = await fs.readFile(path.join(vaultPaths.pages, file), "utf-8")
75
+ if (content.includes("> [!warning] DEPRECATED")) {
76
+ deprecatedPages++
77
+ }
78
+ }
79
+ } catch { /* pages dir may not exist */ }
80
+ try {
81
+ totalDailyLogs = (await fs.readdir(vaultPaths.daily)).filter((f) => f.endsWith(".md")).length
82
+ } catch { /* daily dir may not exist */ }
83
+ return { totalPages, totalDailyLogs, deprecatedPages }
84
+ }
85
+
86
+ const updateStats = async (directory: string, vaultPaths: VaultPaths): Promise<void> => {
87
+ const st = await readState(directory)
88
+ st.stats = await collectStats(vaultPaths)
89
+ await writeState(directory, st)
90
+ }
91
+
58
92
  const readState = async (directory: string): Promise<EchoesState> => {
59
93
  try {
60
94
  const raw = await fs.readFile(path.join(directory, STATE_FILENAME), "utf-8")
@@ -373,6 +407,7 @@ const OpenCodeEchoes: Plugin = async ({ directory }) => {
373
407
 
374
408
  const state = await readState(directory)
375
409
  state.pluginVersion = await getPluginVersion()
410
+ state.stats = await collectStats(paths)
376
411
  state.session.started = false
377
412
  state.session.saved = false
378
413
  await writeState(directory, state)
@@ -479,6 +514,7 @@ const OpenCodeEchoes: Plugin = async ({ directory }) => {
479
514
  const st = await readState(directory)
480
515
  st.session.saved = true
481
516
  st.session.lastSave = new Date().toISOString()
517
+ st.stats = await collectStats(paths)
482
518
  await writeState(directory, st)
483
519
 
484
520
  return [
@@ -506,6 +542,7 @@ const OpenCodeEchoes: Plugin = async ({ directory }) => {
506
542
  const timestamp = new Date().toISOString()
507
543
  const entry = `### Scratchpad — ${timestamp}\n\n${args.logEntry}\n\n`
508
544
  await fs.appendFile(dailyFile, entry)
545
+ await updateStats(directory, paths)
509
546
  return `✅ Scratchpad note saved to EchoesVault/daily/${today}.md`
510
547
  },
511
548
  }),
@@ -587,6 +624,8 @@ const OpenCodeEchoes: Plugin = async ({ directory }) => {
587
624
  }
588
625
  }
589
626
 
627
+ await updateStats(directory, paths)
628
+
590
629
  const action = existed ? "updated" : "created"
591
630
  const parts = [`✅ Page ${action}: EchoesVault/pages/${fileName}`]
592
631
  if (!existed && args.indexDescription) {
@@ -602,6 +641,7 @@ const OpenCodeEchoes: Plugin = async ({ directory }) => {
602
641
  async execute(_args, _ctx) {
603
642
  const st = await readState(directory)
604
643
  st.initialized = true
644
+ st.stats = await collectStats(paths)
605
645
  await writeState(directory, st)
606
646
  return "EchoesVault activated."
607
647
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "echoes-vault-opencode",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
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",
package/tui.tsx CHANGED
@@ -4,10 +4,17 @@ 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
+ type VaultStats = {
8
+ totalPages: number
9
+ totalDailyLogs: number
10
+ deprecatedPages: number
11
+ }
12
+
7
13
  type EchoesState = {
8
14
  version: number
9
15
  pluginVersion: string
10
16
  initialized: boolean
17
+ stats: VaultStats
11
18
  session: {
12
19
  started: boolean
13
20
  saved: boolean
@@ -16,6 +23,9 @@ type EchoesState = {
16
23
  }
17
24
  }
18
25
 
26
+ const CAPACITY_LIMIT = 200
27
+ const CAPACITY_WARNING = 170
28
+
19
29
  const STATE_PATH = path.join(process.cwd(), ".opencode", "echoes-state.json")
20
30
 
21
31
  const readState = (): EchoesState | null => {
@@ -78,6 +88,17 @@ const tui: TuiPlugin = async (api) => {
78
88
  return "to load context"
79
89
  }
80
90
 
91
+ const pages = () => state()?.stats?.totalPages ?? 0
92
+
93
+ const capacityColor = () => {
94
+ const p = pages()
95
+ if (p > CAPACITY_LIMIT) return theme.error
96
+ if (p > CAPACITY_WARNING) return theme.warning
97
+ return theme.success
98
+ }
99
+
100
+ const capacityPercent = () => Math.round((pages() / CAPACITY_LIMIT) * 100)
101
+
81
102
  return (
82
103
  <box flexDirection="column">
83
104
  <box flexDirection="row">
@@ -97,6 +118,20 @@ const tui: TuiPlugin = async (api) => {
97
118
  <text fg={theme.textMuted}> {statusCmdHint()}</text>
98
119
  </box>
99
120
  )}
121
+ {state()?.initialized && (
122
+ <box flexDirection="column">
123
+ <text> </text>
124
+ <box flexDirection="row">
125
+ <text fg={capacityColor()}><b>• </b></text>
126
+ <text fg={theme.text}><b>Vault Capacity</b></text>
127
+ </box>
128
+ <text fg={theme.textMuted}>Pages: {pages()}</text>
129
+ <text fg={theme.textMuted}>Usage: {capacityPercent()}%</text>
130
+ {pages() > CAPACITY_LIMIT && (
131
+ <text fg={theme.textMuted}>Consider migrating to RAG</text>
132
+ )}
133
+ </box>
134
+ )}
100
135
  </box>
101
136
  )
102
137
  },