@comfanion/usethis_todo 0.1.7-dev.0 → 0.1.7-dev.2
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 +78 -1
- package/package.json +1 -1
package/index.ts
CHANGED
|
@@ -2,6 +2,12 @@ import type { Plugin } from "@opencode-ai/plugin"
|
|
|
2
2
|
|
|
3
3
|
import { write, read, read_five, read_by_id, update } from "./tools"
|
|
4
4
|
|
|
5
|
+
interface TodoPruneState {
|
|
6
|
+
lastToolCallId: string | null
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const pruneStates = new Map<string, TodoPruneState>()
|
|
10
|
+
|
|
5
11
|
const UsethisTodoPlugin: Plugin = async ({ directory, client }) => {
|
|
6
12
|
// Ensure storage directory exists on init
|
|
7
13
|
try {
|
|
@@ -12,6 +18,69 @@ const UsethisTodoPlugin: Plugin = async ({ directory, client }) => {
|
|
|
12
18
|
}
|
|
13
19
|
|
|
14
20
|
return {
|
|
21
|
+
"experimental.chat.messages.transform": async (_input: unknown, output: { messages: any[] }) => {
|
|
22
|
+
const messages = output.messages || []
|
|
23
|
+
const sessionID = messages[0]?.info?.sessionID
|
|
24
|
+
|
|
25
|
+
if (!sessionID) return
|
|
26
|
+
|
|
27
|
+
const state = pruneStates.get(sessionID)
|
|
28
|
+
if (!state?.lastToolCallId) return
|
|
29
|
+
|
|
30
|
+
const prunedToolNames = new Set([
|
|
31
|
+
"usethis_todo_write",
|
|
32
|
+
"usethis_todo_update",
|
|
33
|
+
"usethis_todo_read",
|
|
34
|
+
"usethis_todo_read_five",
|
|
35
|
+
"usethis_todo_read_by_id",
|
|
36
|
+
"todowrite",
|
|
37
|
+
"todoread",
|
|
38
|
+
])
|
|
39
|
+
|
|
40
|
+
// 1. Collect all "## TODO" user messages and all tool outputs
|
|
41
|
+
const todoTextParts: { part: any }[] = []
|
|
42
|
+
const todoToolParts: { part: any; isLast: boolean }[] = []
|
|
43
|
+
|
|
44
|
+
for (const msg of messages) {
|
|
45
|
+
const parts = Array.isArray(msg.parts) ? msg.parts : []
|
|
46
|
+
for (const part of parts) {
|
|
47
|
+
// Tool outputs
|
|
48
|
+
if (part.type === "tool" && prunedToolNames.has(part.tool) && part.state?.status === "completed") {
|
|
49
|
+
todoToolParts.push({ part, isLast: part.callID === state.lastToolCallId })
|
|
50
|
+
}
|
|
51
|
+
// User "## TODO" snapshots
|
|
52
|
+
if (part.type === "text" && typeof part.text === "string" && part.text.startsWith("## TODO")) {
|
|
53
|
+
todoTextParts.push({ part })
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// 2. Check if all tasks are done by parsing the latest snapshot
|
|
59
|
+
const lastSnapshot = todoTextParts[todoTextParts.length - 1]?.part.text || ""
|
|
60
|
+
const doneMatch = lastSnapshot.match(/\[(\d+)\/(\d+) done/)
|
|
61
|
+
const allDone = doneMatch && doneMatch[1] === doneMatch[2] && parseInt(doneMatch[1]) > 0
|
|
62
|
+
|
|
63
|
+
// 3. Prune tool outputs
|
|
64
|
+
for (const { part, isLast } of todoToolParts) {
|
|
65
|
+
if (allDone || !isLast) {
|
|
66
|
+
part.state.output = "[TODO output pruned - see latest]"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// 4. Prune "## TODO" user messages
|
|
71
|
+
if (allDone) {
|
|
72
|
+
// All done → prune everything, no need to keep any snapshot
|
|
73
|
+
for (const { part } of todoTextParts) {
|
|
74
|
+
part.text = "[TODO completed - all tasks done]"
|
|
75
|
+
}
|
|
76
|
+
} else if (todoTextParts.length > 1) {
|
|
77
|
+
// Active tasks → keep only the last snapshot
|
|
78
|
+
for (let i = 0; i < todoTextParts.length - 1; i++) {
|
|
79
|
+
todoTextParts[i].part.text = "[TODO snapshot pruned - see latest]"
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
|
|
15
84
|
tool: {
|
|
16
85
|
usethis_todo_write: write,
|
|
17
86
|
usethis_todo_read: read,
|
|
@@ -22,7 +91,15 @@ const UsethisTodoPlugin: Plugin = async ({ directory, client }) => {
|
|
|
22
91
|
|
|
23
92
|
// UI niceties + publish snapshot into the chat
|
|
24
93
|
"tool.execute.after": async (input, output) => {
|
|
25
|
-
if (!input.tool.startsWith("usethis_todo_")) return
|
|
94
|
+
if (!input.tool.startsWith("usethis_todo_") && input.tool !== "todowrite" && input.tool !== "todoread") return
|
|
95
|
+
|
|
96
|
+
// Update prune state with latest call ID
|
|
97
|
+
const sessionID = input.sessionID
|
|
98
|
+
if (sessionID) {
|
|
99
|
+
const state = pruneStates.get(sessionID) || { lastToolCallId: null }
|
|
100
|
+
state.lastToolCallId = input.callID
|
|
101
|
+
pruneStates.set(sessionID, state)
|
|
102
|
+
}
|
|
26
103
|
|
|
27
104
|
const out = output.output || ""
|
|
28
105
|
|