@comfanion/usethis_todo 0.1.16-dev.12 → 0.1.16-dev.13

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 +4 -1
  2. package/package.json +1 -1
  3. package/tools.ts +70 -6
package/index.ts CHANGED
@@ -2,7 +2,7 @@ import type { Plugin } from "@opencode-ai/plugin"
2
2
  import path from "path"
3
3
  import fs from "fs/promises"
4
4
 
5
- import { write, read, read_five, read_by_id, setNativeStorageBase, readTodos } from "./tools"
5
+ import { write, read, read_five, read_by_id, setNativeStorageBase, readTodos, setClientStorage } from "./tools"
6
6
 
7
7
  interface TodoPruneState {
8
8
  lastToolCallId: string | null
@@ -11,6 +11,9 @@ interface TodoPruneState {
11
11
  const pruneStates = new Map<string, TodoPruneState>()
12
12
 
13
13
  const UsethisTodoPlugin: Plugin = async ({ directory, client }) => {
14
+ // Set client storage API for native Storage operations
15
+ setClientStorage(client)
16
+
14
17
  // Resolve the authoritative state path from OpenCode server (non-blocking).
15
18
  // Must NOT await — server may block until plugin init completes → deadlock.
16
19
  client.path.get().then((pathInfo: any) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@comfanion/usethis_todo",
3
- "version": "0.1.16-dev.12",
3
+ "version": "0.1.16-dev.13",
4
4
  "description": "OpenCode plugin: enhanced TODO tools (dual storage + dependency graph)",
5
5
  "type": "module",
6
6
  "main": "./index.ts",
package/tools.ts CHANGED
@@ -8,8 +8,8 @@
8
8
  * usethis_todo_read_by_id({ id }) - read task by id with its blockers
9
9
  *
10
10
  * Storage:
11
- * Unified: Native OpenCode storage (TUI compatible)
12
- * Path resolved via client.path.get()
11
+ * Dual: Native OpenCode storage (via client.storage) + Local file backup (.opencode/session-todo/)
12
+ * Native storage uses await client.storage.write(["todo", sessionID], todos)
13
13
  *
14
14
  * Features:
15
15
  * - Stores FULL enhanced schema (blockedBy, priority, etc) in the native file
@@ -23,6 +23,44 @@ import path from "path"
23
23
  import os from "os"
24
24
  import fs from "fs/promises"
25
25
 
26
+ // ============================================================================
27
+ // Client Storage Wrapper
28
+ // ============================================================================
29
+
30
+ let _clientStorage: any = null
31
+
32
+ /**
33
+ * Set the OpenCode client storage API.
34
+ * Called during plugin initialization.
35
+ */
36
+ export function setClientStorage(client: any): void {
37
+ _clientStorage = client
38
+ }
39
+
40
+ /**
41
+ * Write to native OpenCode storage.
42
+ */
43
+ async function writeNativeStorage(key: string[], value: any): Promise<void> {
44
+ if (!_clientStorage?.write) return
45
+ try {
46
+ await _clientStorage.write(key, value)
47
+ } catch {
48
+ // ignore - native storage may not be available
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Read from native OpenCode storage.
54
+ */
55
+ async function readNativeStorage<T>(key: string[]): Promise<T | null> {
56
+ if (!_clientStorage?.read) return null
57
+ try {
58
+ return await _clientStorage.read(key)
59
+ } catch {
60
+ return null
61
+ }
62
+ }
63
+
26
64
  // ============================================================================
27
65
  // Types
28
66
  // ============================================================================
@@ -195,12 +233,30 @@ function toNativePriority(priority: string): string {
195
233
  // ============================================================================
196
234
 
197
235
  export async function readTodos(sid: string, directory?: string): Promise<Todo[]> {
236
+ // 1. Try Native OpenCode Storage first (primary)
237
+ try {
238
+ const nativeData = await readNativeStorage<Todo[]>(["todo", sid])
239
+ if (Array.isArray(nativeData) && nativeData.length > 0) {
240
+ await logAction(directory || "", "debug", `Reading from native storage: todo/${sid}`)
241
+ return nativeData.map((t: any) => {
242
+ // Restore "ready" status if it was saved
243
+ const realStatus = t.usethisStatus || t.status
244
+ const realPriority = t.usethisPriority || t.priority
245
+ return normalizeTodo({ ...t, status: realStatus, priority: realPriority })
246
+ })
247
+ }
248
+ } catch (e) {
249
+ await logAction(directory || "", "debug", `Native storage read failed, falling back to files: ${String(e)}`)
250
+ }
251
+
252
+ // 2. Fallback to file storage
198
253
  try {
199
254
  const storagePaths = await getStoragePaths(sid)
200
255
  for (const storagePath of storagePaths) {
201
256
  try {
202
257
  const raw = JSON.parse(await fs.readFile(storagePath, "utf-8"))
203
258
  if (Array.isArray(raw)) {
259
+ await logAction(directory || "", "debug", `Reading from global file: ${storagePath}`)
204
260
  return raw.map((t: any) => {
205
261
  // Restore "ready" status if it was saved
206
262
  const realStatus = t.usethisStatus || t.status
@@ -224,12 +280,12 @@ function getEnhancedPath(sid: string, directory?: string): string {
224
280
  async function writeTodos(todos: Todo[], sid: string, directory?: string): Promise<void> {
225
281
  const storagePaths = await getStoragePaths(sid)
226
282
  const enhancedPath = getEnhancedPath(sid, directory)
227
-
283
+
228
284
  const storageTodos = todos.map(t => {
229
285
  // Status is already native — map "ready" to "in_progress" for native storage
230
286
  const nativeStatus = t.status === "ready" ? "in_progress" : t.status
231
287
  const nativePriority = toNativePriority(t.priority)
232
-
288
+
233
289
  return {
234
290
  ...t,
235
291
  status: nativeStatus,
@@ -242,7 +298,15 @@ async function writeTodos(todos: Todo[], sid: string, directory?: string): Promi
242
298
 
243
299
  const json = JSON.stringify(storageTodos, null, 2)
244
300
 
245
- // 1. Write to Global Storages (for Sidebar)
301
+ // 1. Write to Native OpenCode Storage (primary)
302
+ try {
303
+ await writeNativeStorage(["todo", sid], storageTodos)
304
+ await logAction(directory || "", "debug", `Writing to native storage: todo/${sid}`)
305
+ } catch (e) {
306
+ await logAction(directory || "", "error", `Native storage write failed: ${String(e)}`)
307
+ }
308
+
309
+ // 2. Write to Global Storages (for Sidebar - fallback)
246
310
  await Promise.allSettled(
247
311
  storagePaths.map(async (p) => {
248
312
  try {
@@ -255,7 +319,7 @@ async function writeTodos(todos: Todo[], sid: string, directory?: string): Promi
255
319
  })
256
320
  )
257
321
 
258
- // 2. Write to Local Storage (for User visibility/Git)
322
+ // 3. Write to Local Storage (for User visibility/Git - backup)
259
323
  try {
260
324
  await fs.mkdir(path.dirname(enhancedPath), { recursive: true })
261
325
  await fs.writeFile(enhancedPath, json, "utf-8")