opencode-plannotator-tui 0.2.2 → 0.3.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/CHANGELOG.md +11 -0
- package/README.md +8 -2
- package/package.json +34 -8
- package/review.mjs +24 -7
- package/tui.ts +12 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
- Select older completed replies with `/annotate 2`, `/annotate 3`, and so on.
|
|
6
|
+
Bare `/annotate` and `/annotate 1` still select the latest reply.
|
|
7
|
+
- Count only eligible completed text replies across history pages; report missing
|
|
8
|
+
replies and invalid numbers without opening a different source.
|
|
9
|
+
- `/annotate-last` now always selects the latest reply and ignores arguments.
|
|
10
|
+
**Migration:** replace `/annotate-last file.md` with `/annotate file.md`.
|
|
11
|
+
- Verify numbered feedback delivery and fixed latest-reply selection in the real
|
|
12
|
+
terminal smoke test, alongside 21 behavior tests.
|
|
13
|
+
|
|
3
14
|
## 0.2.2
|
|
4
15
|
|
|
5
16
|
- All confirmation dialogs, status notices, errors, and the feedback header sent
|
package/README.md
CHANGED
|
@@ -13,12 +13,14 @@ official OpenCode or Plannotator release.
|
|
|
13
13
|
|
|
14
14
|
```text
|
|
15
15
|
/annotate Review the last completed assistant reply
|
|
16
|
+
/annotate 2 Review the second most recent reply
|
|
17
|
+
/annotate 3 Review the third most recent reply
|
|
16
18
|
/annotate docs/plan.md Review a Markdown document
|
|
17
19
|
/annotate "docs/release plan.md" Review a path containing spaces
|
|
18
20
|
/annotate ~/Documents/notes.txt Review a plain-text document
|
|
19
21
|
```
|
|
20
22
|
|
|
21
|
-
`/annotate-last`
|
|
23
|
+
`/annotate-last` always reviews the latest completed reply; any supplied argument is ignored.
|
|
22
24
|
|
|
23
25
|
## Install
|
|
24
26
|
|
|
@@ -75,6 +77,9 @@ cancelled reviews submit nothing.
|
|
|
75
77
|
|
|
76
78
|
**Replies:** the latest completed text reply, with paginated history lookup.
|
|
77
79
|
Reasoning, in-progress messages and tool-call commentary are excluded.
|
|
80
|
+
Pass a positive integer to select the Nth most recent eligible reply: `1` is the
|
|
81
|
+
default, `2` is the previous reply, and so on. If there are too few replies, the
|
|
82
|
+
plugin reports that instead of opening another reply.
|
|
78
83
|
|
|
79
84
|
**Documents:** local UTF-8 `.md`, `.mdx`, `.markdown` and `.txt` files up to 2 MiB.
|
|
80
85
|
Relative paths resolve against the captured session's working directory, not the
|
|
@@ -160,8 +165,9 @@ See [release instructions](docs/releasing.md) and [changelog](CHANGELOG.md).
|
|
|
160
165
|
这是 **Plannotator TUI 的非官方 OpenCode 适配插件**。
|
|
161
166
|
|
|
162
167
|
- `/annotate`:批注上一条完整回复。
|
|
168
|
+
- `/annotate 2`:批注倒数第二条,`3`、`4` 依次往前;`1` 等同于默认。
|
|
163
169
|
- `/annotate 文档.md`:批注文档,支持中文、空格和相对路径。
|
|
164
|
-
- `/annotate-last
|
|
170
|
+
- `/annotate-last`:固定批注最新一条完整回复,忽略附带参数。
|
|
165
171
|
- 选中文字 → `c` 写批注 → Enter 保存 → `q` 返回 →「Send and continue」。
|
|
166
172
|
- 文档采用独立快照,反馈附原文件路径,确认后发回发起批注的会话。
|
|
167
173
|
- 更新:`opencode plugin update opencode-plannotator-tui@latest`,然后重新打开终端客户端。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-plannotator-tui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Unofficial OpenCode adapter for Plannotator TUI: annotate replies and documents without leaving your terminal.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -13,16 +13,42 @@
|
|
|
13
13
|
"check": "node --check review.mjs && npm test && npm pack --dry-run",
|
|
14
14
|
"prepublishOnly": "npm run check"
|
|
15
15
|
},
|
|
16
|
-
"files": [
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
"files": [
|
|
17
|
+
"tui.ts",
|
|
18
|
+
"review.mjs",
|
|
19
|
+
"README.md",
|
|
20
|
+
"LICENSE",
|
|
21
|
+
"CHANGELOG.md"
|
|
22
|
+
],
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=22"
|
|
25
|
+
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"@opencode/plugin": ">=2.0.22 <3"
|
|
28
|
+
},
|
|
29
|
+
"peerDependenciesMeta": {
|
|
30
|
+
"@opencode/plugin": {
|
|
31
|
+
"optional": true
|
|
32
|
+
}
|
|
33
|
+
},
|
|
20
34
|
"repository": {
|
|
21
35
|
"type": "git",
|
|
22
36
|
"url": "git+https://github.com/nxxxsooo/opencode-plannotator-tui.git"
|
|
23
37
|
},
|
|
24
38
|
"homepage": "https://github.com/nxxxsooo/opencode-plannotator-tui#readme",
|
|
25
|
-
"bugs": {
|
|
26
|
-
|
|
27
|
-
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/nxxxsooo/opencode-plannotator-tui/issues"
|
|
41
|
+
},
|
|
42
|
+
"keywords": [
|
|
43
|
+
"opencode",
|
|
44
|
+
"opencode-plugin",
|
|
45
|
+
"plannotator",
|
|
46
|
+
"tui",
|
|
47
|
+
"annotation",
|
|
48
|
+
"markdown",
|
|
49
|
+
"review"
|
|
50
|
+
],
|
|
51
|
+
"publishConfig": {
|
|
52
|
+
"access": "public"
|
|
53
|
+
}
|
|
28
54
|
}
|
package/review.mjs
CHANGED
|
@@ -48,6 +48,11 @@ async function documentSource(input, client, sessionID, signal) {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
export function selectReply(messages) {
|
|
51
|
+
return eligibleReplies(messages)[0]
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function eligibleReplies(messages) {
|
|
55
|
+
const replies = []
|
|
51
56
|
const newest = [...messages].sort((a, b) =>
|
|
52
57
|
b.time.created - a.time.created || b.id.localeCompare(a.id),
|
|
53
58
|
)
|
|
@@ -55,12 +60,14 @@ export function selectReply(messages) {
|
|
|
55
60
|
if (message.type !== "assistant" || message.time.completed === undefined || message.error) continue
|
|
56
61
|
if (["tool-calls", "error", "content-filter"].includes(message.finish)) continue
|
|
57
62
|
const text = message.content.filter(part => part.type === "text").map(part => part.text).join("\n\n")
|
|
58
|
-
if (text.trim())
|
|
63
|
+
if (text.trim()) replies.push({ id: message.id, text })
|
|
59
64
|
}
|
|
65
|
+
return replies
|
|
60
66
|
}
|
|
61
67
|
|
|
62
|
-
async function latestReply(client, sessionID, signal) {
|
|
68
|
+
async function latestReply(client, sessionID, signal, position = 1) {
|
|
63
69
|
const seen = new Set()
|
|
70
|
+
const messages = new Set()
|
|
64
71
|
let cursor
|
|
65
72
|
do {
|
|
66
73
|
signal.throwIfAborted()
|
|
@@ -68,8 +75,11 @@ async function latestReply(client, sessionID, signal) {
|
|
|
68
75
|
sessionID, type: "assistant", limit: 50,
|
|
69
76
|
...(cursor ? { cursor } : { order: "desc" }),
|
|
70
77
|
}, { signal })
|
|
71
|
-
const reply
|
|
72
|
-
|
|
78
|
+
for (const reply of eligibleReplies(page.data)) {
|
|
79
|
+
if (messages.has(reply.id)) continue
|
|
80
|
+
messages.add(reply.id)
|
|
81
|
+
if (--position === 0) return reply
|
|
82
|
+
}
|
|
73
83
|
cursor = page.cursor?.next
|
|
74
84
|
if (cursor && seen.has(cursor)) throw new Error("Session history returned a repeated page cursor.")
|
|
75
85
|
seen.add(cursor)
|
|
@@ -125,11 +135,18 @@ export function createReviewCommand(context, options = {}) {
|
|
|
125
135
|
// The UI cache contains only a recent page; a long tool run can push the last
|
|
126
136
|
// completed reply out of it. Read the session-scoped history, newest first.
|
|
127
137
|
const target = input.trim()
|
|
128
|
-
const
|
|
138
|
+
const numeric = target !== "" && /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i.test(target)
|
|
139
|
+
const position = numeric ? Number(target) : 1
|
|
140
|
+
if (numeric && (!/^\d+$/.test(target) || !Number.isSafeInteger(position) || position < 1)) {
|
|
141
|
+
throw new Error("Choose a positive integer reply number (1 = latest, 2 = previous).")
|
|
142
|
+
}
|
|
143
|
+
const source = target && !numeric
|
|
129
144
|
? await documentSource(target, context.client, sessionID, controller.signal)
|
|
130
|
-
: await latestReply(context.client, sessionID, controller.signal)
|
|
145
|
+
: await latestReply(context.client, sessionID, controller.signal, position)
|
|
131
146
|
if (controller.signal.aborted) return
|
|
132
|
-
if (!source) return notice(
|
|
147
|
+
if (!source) return notice(position === 1
|
|
148
|
+
? "This session has no completed assistant reply yet."
|
|
149
|
+
: `Can't find reply ${position}: this session has fewer completed assistant text replies.`)
|
|
133
150
|
|
|
134
151
|
directory = await mkdtemp(path.join(options.tempRoot ?? os.tmpdir(), "opencode-annotate-"))
|
|
135
152
|
keepDraft = true
|
package/tui.ts
CHANGED
|
@@ -14,11 +14,21 @@ export default Plugin.define({
|
|
|
14
14
|
commands: [{
|
|
15
15
|
id: "opencode-plannotator-tui.annotate",
|
|
16
16
|
title: "Annotate reply or document (TUI)",
|
|
17
|
-
description: "Annotate
|
|
17
|
+
description: "Annotate a reply (1 = latest, 2 = previous) or a file; press q, then confirm to send",
|
|
18
18
|
group: "Plannotator",
|
|
19
19
|
palette: true,
|
|
20
|
-
slash: { name: "annotate",
|
|
20
|
+
slash: { name: "annotate", arguments: true },
|
|
21
21
|
run: review.run,
|
|
22
|
+
}, {
|
|
23
|
+
id: "opencode-plannotator-tui.annotate-last",
|
|
24
|
+
title: "Annotate last reply (TUI)",
|
|
25
|
+
description: "Annotate the latest completed reply; press q, then confirm to send",
|
|
26
|
+
group: "Plannotator",
|
|
27
|
+
palette: true,
|
|
28
|
+
// Keep slash submission with a trailing space local to the command.
|
|
29
|
+
// Any supplied argument is deliberately ignored: this always means latest.
|
|
30
|
+
slash: { name: "annotate-last", arguments: true },
|
|
31
|
+
run: () => review.run(),
|
|
22
32
|
}],
|
|
23
33
|
}))
|
|
24
34
|
return null
|