@comfanion/usethis_todo 0.1.15-dev.12 → 0.1.15-dev.14
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/index.ts +4 -1
- package/package.json +1 -1
- package/tools.ts +44 -41
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, update, setNativeStorageBase } from "./tools"
|
|
5
|
+
import { write, read, read_five, read_by_id, update, setNativeStorageBase, setClient } 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
|
+
// Pass client to tools for event emitting
|
|
15
|
+
setClient(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
package/tools.ts
CHANGED
|
@@ -103,10 +103,11 @@ interface TodoGraph {
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
// ============================================================================
|
|
106
|
-
// Storage —
|
|
106
|
+
// Storage — Dual Write
|
|
107
107
|
// ============================================================================
|
|
108
108
|
|
|
109
109
|
let _nativeStorageBase: string | null = null
|
|
110
|
+
let _client: any = null
|
|
110
111
|
|
|
111
112
|
/**
|
|
112
113
|
* Set the native storage base path (OpenCode's state directory).
|
|
@@ -116,6 +117,10 @@ export function setNativeStorageBase(statePath: string): void {
|
|
|
116
117
|
_nativeStorageBase = statePath
|
|
117
118
|
}
|
|
118
119
|
|
|
120
|
+
export function setClient(client: any): void {
|
|
121
|
+
_client = client
|
|
122
|
+
}
|
|
123
|
+
|
|
119
124
|
// Resolve project directory (context.directory may be undefined via MCP)
|
|
120
125
|
function dir(directory?: string): string {
|
|
121
126
|
return directory || process.env.OPENCODE_PROJECT_DIR || process.cwd()
|
|
@@ -150,26 +155,22 @@ async function getNativeDataDirs(): Promise<string[]> {
|
|
|
150
155
|
return [...dirs]
|
|
151
156
|
}
|
|
152
157
|
|
|
153
|
-
async function
|
|
158
|
+
async function getStoragePaths(sid: string): Promise<string[]> {
|
|
154
159
|
const file = `${sid || "current"}.json`
|
|
160
|
+
const paths: string[] = []
|
|
155
161
|
|
|
156
|
-
// 1.
|
|
162
|
+
// 1. Authoritative path from OpenCode server
|
|
157
163
|
if (_nativeStorageBase) {
|
|
158
|
-
|
|
164
|
+
paths.push(path.join(_nativeStorageBase, "storage", "todo", file))
|
|
159
165
|
}
|
|
160
166
|
|
|
161
|
-
// 2.
|
|
167
|
+
// 2. Well-known data dirs (fallbacks)
|
|
162
168
|
const baseDirs = await getNativeDataDirs()
|
|
163
|
-
// Try to find one that exists
|
|
164
169
|
for (const base of baseDirs) {
|
|
165
|
-
|
|
166
|
-
await fs.access(base)
|
|
167
|
-
return path.join(base, "opencode", "storage", "todo", file)
|
|
168
|
-
} catch {}
|
|
170
|
+
paths.push(path.join(base, "opencode", "storage", "todo", file))
|
|
169
171
|
}
|
|
170
172
|
|
|
171
|
-
|
|
172
|
-
return path.join(baseDirs[0], "opencode", "storage", "todo", file)
|
|
173
|
+
return [...new Set(paths)] // Unique paths
|
|
173
174
|
}
|
|
174
175
|
|
|
175
176
|
// ============================================================================
|
|
@@ -217,21 +218,21 @@ function toNativePriority(priority: string): string {
|
|
|
217
218
|
|
|
218
219
|
async function readTodos(sid: string, directory?: string): Promise<Todo[]> {
|
|
219
220
|
try {
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
221
|
+
const storagePaths = await getStoragePaths(sid)
|
|
222
|
+
// Try to read from any available path, starting with authoritative
|
|
223
|
+
for (const storagePath of storagePaths) {
|
|
224
|
+
try {
|
|
225
|
+
const raw = JSON.parse(await fs.readFile(storagePath, "utf-8"))
|
|
226
|
+
if (Array.isArray(raw)) {
|
|
227
|
+
return raw.map((t: any) => {
|
|
228
|
+
const realStatus = fromNativeStatus(t.status, t.usethisStatus)
|
|
229
|
+
const realPriority = t.usethisPriority || t.priority
|
|
230
|
+
return normalizeTodo({ ...t, status: realStatus, priority: realPriority })
|
|
231
|
+
})
|
|
232
|
+
}
|
|
233
|
+
} catch { continue }
|
|
234
|
+
}
|
|
235
|
+
return []
|
|
235
236
|
} catch {
|
|
236
237
|
return []
|
|
237
238
|
}
|
|
@@ -243,25 +244,17 @@ function getEnhancedPath(sid: string, directory?: string): string {
|
|
|
243
244
|
}
|
|
244
245
|
|
|
245
246
|
async function writeTodos(todos: Todo[], sid: string, directory?: string): Promise<void> {
|
|
246
|
-
const
|
|
247
|
+
const storagePaths = await getStoragePaths(sid)
|
|
247
248
|
const enhancedPath = getEnhancedPath(sid, directory)
|
|
248
249
|
|
|
249
|
-
// Prepare for storage:
|
|
250
|
-
// 1. Set native-compatible fields (status, priority)
|
|
251
|
-
// 2. Store real values in shadow fields (usethisStatus, usethisPriority)
|
|
252
|
-
// 3. Keep all other fields (blockedBy, etc)
|
|
253
|
-
|
|
254
250
|
const storageTodos = todos.map(t => {
|
|
255
251
|
const nativeStatus = toNativeStatus(t.status)
|
|
256
252
|
const nativePriority = toNativePriority(t.priority)
|
|
257
253
|
|
|
258
254
|
return {
|
|
259
255
|
...t,
|
|
260
|
-
// Native fields for Sidebar compatibility
|
|
261
256
|
status: nativeStatus,
|
|
262
257
|
priority: nativePriority,
|
|
263
|
-
|
|
264
|
-
// Shadow fields (our source of truth)
|
|
265
258
|
usethisStatus: t.status,
|
|
266
259
|
usethisPriority: t.priority
|
|
267
260
|
}
|
|
@@ -269,16 +262,26 @@ async function writeTodos(todos: Todo[], sid: string, directory?: string): Promi
|
|
|
269
262
|
|
|
270
263
|
const json = JSON.stringify(storageTodos, null, 2)
|
|
271
264
|
|
|
272
|
-
// 1. Write to Global
|
|
273
|
-
await
|
|
274
|
-
|
|
265
|
+
// 1. Write to Global Storages (for Sidebar)
|
|
266
|
+
await Promise.allSettled(
|
|
267
|
+
storagePaths.map(async (p) => {
|
|
268
|
+
try {
|
|
269
|
+
await fs.mkdir(path.dirname(p), { recursive: true })
|
|
270
|
+
await fs.writeFile(p, json, "utf-8")
|
|
271
|
+
await logAction(directory || "", "debug", `Writing to global: ${p}`)
|
|
272
|
+
} catch (e) {
|
|
273
|
+
await logAction(directory || "", "error", `Global write failed for ${p}: ${String(e)}`)
|
|
274
|
+
}
|
|
275
|
+
})
|
|
276
|
+
)
|
|
275
277
|
|
|
276
278
|
// 2. Write to Local Storage (for User visibility/Git)
|
|
277
279
|
try {
|
|
278
280
|
await fs.mkdir(path.dirname(enhancedPath), { recursive: true })
|
|
279
281
|
await fs.writeFile(enhancedPath, json, "utf-8")
|
|
280
|
-
|
|
281
|
-
|
|
282
|
+
await logAction(directory || "", "debug", `Writing to local: ${enhancedPath}`)
|
|
283
|
+
} catch (e) {
|
|
284
|
+
await logAction(directory || "", "error", `Local write failed: ${String(e)}`)
|
|
282
285
|
}
|
|
283
286
|
}
|
|
284
287
|
|