opencode-readseek 0.8.17 → 0.8.19
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/README.md +48 -32
- package/index.ts +39 -37
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# opencode-readseek
|
|
2
2
|
|
|
3
|
-
<img alt="
|
|
3
|
+
<img alt="OpenCode running opencode-readseek" src="screenshot.png" width="800">
|
|
4
4
|
|
|
5
|
-
`opencode-readseek` adds
|
|
6
|
-
|
|
5
|
+
`opencode-readseek` adds anchored file operations, structural search, symbol
|
|
6
|
+
navigation, and PDF tools to OpenCode.
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## Install
|
|
9
9
|
|
|
10
|
-
Add the
|
|
10
|
+
Add the package to `opencode.json`:
|
|
11
11
|
|
|
12
12
|
```json
|
|
13
13
|
{
|
|
@@ -15,29 +15,34 @@ Add the plugin to `opencode.json`:
|
|
|
15
15
|
}
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
OpenCode installs the package and its platform-specific
|
|
19
|
-
|
|
18
|
+
OpenCode installs the package and its platform-specific
|
|
19
|
+
`@jarkkojs/readseek` dependency with Bun.
|
|
20
20
|
|
|
21
21
|
## Tools
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
and
|
|
23
|
+
| Tool | Purpose |
|
|
24
|
+
| --- | --- |
|
|
25
|
+
| `readseek_read` | Read text with `LINE:HASH` anchors; read images or one PDF page |
|
|
26
|
+
| `readseek_edit` | Apply hash-verified edits to an existing text file |
|
|
27
|
+
| `readseek_write` | Create or replace a complete text file |
|
|
28
|
+
| `readseek_grep` | Search text or regex and return anchors |
|
|
29
|
+
| `readseek_map` | Show a source file's symbol outline |
|
|
30
|
+
| `readseek_search` | Search AST patterns |
|
|
31
|
+
| `readseek_def` | Find symbol definitions |
|
|
32
|
+
| `readseek_refs` | Find identifier usages, optionally scoped to a cursor binding |
|
|
33
|
+
| `readseek_hover` | Identify the token and enclosing symbol at a cursor |
|
|
34
|
+
| `readseek_rename` | Apply a binding-aware rename; set `apply: false` for a dry run |
|
|
35
|
+
| `readseek_check` | Report parser errors and missing syntax |
|
|
36
|
+
| `readseek_view` | View or narrow a structural PDF index |
|
|
37
|
+
|
|
38
|
+
The plugin requests read, grep, external-directory, and edit permissions as
|
|
39
|
+
needed. Mutations invalidate remembered anchors; compaction preserves fresh
|
|
40
|
+
paths and pending rename plans. Text reads return at most 2,000 lines by
|
|
41
|
+
default.
|
|
42
|
+
|
|
43
|
+
OpenCode is instructed to prefer ReadSeek over the built-in `read`, `edit`,
|
|
44
|
+
`write`, `grep`, and `apply_patch` tools when ReadSeek can do the job. Built-ins
|
|
45
|
+
remain available as fallbacks.
|
|
41
46
|
|
|
42
47
|
## Configuration
|
|
43
48
|
|
|
@@ -49,13 +54,24 @@ Pass options with OpenCode's plugin tuple syntax:
|
|
|
49
54
|
}
|
|
50
55
|
```
|
|
51
56
|
|
|
52
|
-
`imageMode`
|
|
53
|
-
`objects`. `"on"` omits `none`; `"off"` skips images and PDFs. Omitting `image`
|
|
54
|
-
also skips visual files.
|
|
57
|
+
`imageMode` accepts:
|
|
55
58
|
|
|
56
|
-
|
|
59
|
+
- `"auto"` (default): expose `none`, `all`, `ocr`, `caption`, and `objects`.
|
|
60
|
+
- `"on"`: expose local analysis modes and omit `none`.
|
|
61
|
+
- `"off"`: skip image and PDF reads.
|
|
57
62
|
|
|
58
|
-
`
|
|
59
|
-
|
|
63
|
+
Omitting `image` from a read also skips visual files.
|
|
64
|
+
|
|
65
|
+
## Development
|
|
66
|
+
|
|
67
|
+
```sh
|
|
68
|
+
bun install
|
|
69
|
+
bun test
|
|
70
|
+
bun run typecheck
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## License
|
|
74
|
+
|
|
75
|
+
`opencode-readseek` is Apache-2.0; see [LICENSE](LICENSE).
|
|
60
76
|
`@jarkkojs/readseek` is licensed separately under
|
|
61
|
-
|
|
77
|
+
Apache-2.0 AND LGPL-2.1-or-later.
|
package/index.ts
CHANGED
|
@@ -16,33 +16,32 @@ const MAX_DOCUMENT_OUTPUT_BYTES = 256 * 1024;
|
|
|
16
16
|
const MAX_DOCUMENT_OUTPUT_LINES = 2000;
|
|
17
17
|
const TOOL_POLICY = [
|
|
18
18
|
"ReadSeek tool policy:",
|
|
19
|
-
"- Prefer readseek_* tools over
|
|
20
|
-
"-
|
|
21
|
-
"-
|
|
22
|
-
"-
|
|
23
|
-
"-
|
|
24
|
-
"-
|
|
25
|
-
"- Use readseek_check after source edits for a quick syntax check.",
|
|
19
|
+
"- Prefer readseek_* tools over built-ins when they can do the job.",
|
|
20
|
+
"- Read with readseek_read first; readseek_edit needs fresh LINE:HASH anchors.",
|
|
21
|
+
"- Use readseek_grep for text/regex, readseek_search for AST patterns, and readseek_map/def/refs/hover for symbol navigation.",
|
|
22
|
+
"- Use readseek_edit for existing files, readseek_write for whole-file creation or replacement, and readseek_rename for symbol renames.",
|
|
23
|
+
"- Do not use built-in read, edit, write, grep, or apply_patch when a ReadSeek tool can do the job.",
|
|
24
|
+
"- Run readseek_check after source edits for a quick syntax check.",
|
|
26
25
|
].join("\n");
|
|
27
26
|
const PREFERRED_TOOL_DESCRIPTIONS: Record<string, string> = {
|
|
28
|
-
readseek_read: "Preferred file reader; returns LINE:HASH anchors
|
|
29
|
-
readseek_edit: "Preferred tool for editing existing text files with
|
|
30
|
-
readseek_write: "Preferred tool for creating or replacing a complete text file.",
|
|
31
|
-
readseek_rename: "Preferred tool for renaming
|
|
27
|
+
readseek_read: "Preferred file reader; returns LINE:HASH anchors for durable references.",
|
|
28
|
+
readseek_edit: "Preferred tool for editing existing text files with fresh LINE:HASH anchors.",
|
|
29
|
+
readseek_write: "Preferred tool for creating or replacing a complete text file; returns LINE:HASH anchors.",
|
|
30
|
+
readseek_rename: "Preferred tool for renaming a symbol while preserving same-named bindings.",
|
|
32
31
|
readseek_grep: "Preferred text/regex search; returns edit-ready LINE:HASH anchors.",
|
|
33
|
-
readseek_search: "Preferred
|
|
34
|
-
readseek_map: "Preferred
|
|
32
|
+
readseek_search: "Preferred AST search for code shapes and call sites.",
|
|
33
|
+
readseek_map: "Preferred symbol outline for a source file.",
|
|
35
34
|
readseek_def: "Preferred symbol definition lookup.",
|
|
36
|
-
readseek_refs: "Preferred identifier usage finder before rename or delete.",
|
|
35
|
+
readseek_refs: "Preferred identifier usage finder; use before rename or delete.",
|
|
37
36
|
readseek_hover: "Preferred cursor token and enclosing-symbol inspector.",
|
|
38
37
|
readseek_view: "Preferred PDF structure and content viewer.",
|
|
39
38
|
readseek_check: "Preferred quick syntax check after source edits.",
|
|
40
39
|
};
|
|
41
40
|
const FALLBACK_TOOL_DESCRIPTIONS: Record<string, string> = {
|
|
42
|
-
read: "Fallback only. Prefer readseek_read
|
|
43
|
-
edit: "Fallback only. Prefer readseek_edit with
|
|
41
|
+
read: "Fallback only. Prefer readseek_read so edits can use LINE:HASH anchors.",
|
|
42
|
+
edit: "Fallback only. Prefer readseek_edit with fresh LINE:HASH anchors.",
|
|
44
43
|
write: "Fallback only. Prefer readseek_write so the result returns LINE:HASH anchors.",
|
|
45
|
-
grep: "Fallback only. Prefer readseek_grep for
|
|
44
|
+
grep: "Fallback only. Prefer readseek_grep for text/regex search with edit-ready anchors.",
|
|
46
45
|
apply_patch: "Fallback only. Prefer readseek_edit for existing text files.",
|
|
47
46
|
};
|
|
48
47
|
const READSEEK_PLATFORM_PACKAGES: Record<string, string> = {
|
|
@@ -1005,17 +1004,20 @@ export const ReadSeekPlugin: Plugin = async (_input, options) => {
|
|
|
1005
1004
|
: ["all", "ocr", "caption", "objects"];
|
|
1006
1005
|
const withSearchFlags = (args: string[], input: { cached?: boolean; others?: boolean; ignored?: boolean }) => {
|
|
1007
1006
|
if (input.ignored && !input.others) throw new Error("ignored requires others");
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1007
|
+
const selection = [
|
|
1008
|
+
input.cached && "cached",
|
|
1009
|
+
input.others && "others",
|
|
1010
|
+
input.ignored && "ignored",
|
|
1011
|
+
].filter((value): value is string => Boolean(value));
|
|
1012
|
+
if (selection.length > 0) args.push("--git", selection.join(","));
|
|
1011
1013
|
};
|
|
1012
1014
|
|
|
1013
1015
|
return {
|
|
1014
1016
|
tool: {
|
|
1015
1017
|
readseek_read: readseekTool(
|
|
1016
1018
|
imagePolicy === "off"
|
|
1017
|
-
? "Read text with LINE:HASH anchors
|
|
1018
|
-
: `Read text with LINE:HASH anchors
|
|
1019
|
+
? "Read text with LINE:HASH anchors. Image and PDF reads are disabled."
|
|
1020
|
+
: `Read text with LINE:HASH anchors. For an image or PDF, select image (${imageModes.join(", ")}); omit it to skip visual files.`,
|
|
1019
1021
|
{
|
|
1020
1022
|
path: tool.schema.string().describe("Path relative to the project directory"),
|
|
1021
1023
|
offset: tool.schema.number().int().positive().optional().describe("One-based starting line"),
|
|
@@ -1026,7 +1028,7 @@ export const ReadSeekPlugin: Plugin = async (_input, options) => {
|
|
|
1026
1028
|
? {}
|
|
1027
1029
|
: {
|
|
1028
1030
|
image: tool.schema.enum(imageModes as [ImageMode, ...ImageMode[]]).optional()
|
|
1029
|
-
.describe("Image
|
|
1031
|
+
.describe("Image/PDF processing mode")
|
|
1030
1032
|
}),
|
|
1031
1033
|
},
|
|
1032
1034
|
"read",
|
|
@@ -1062,7 +1064,7 @@ export const ReadSeekPlugin: Plugin = async (_input, options) => {
|
|
|
1062
1064
|
formatReadResult,
|
|
1063
1065
|
),
|
|
1064
1066
|
readseek_view: readseekTool(
|
|
1065
|
-
"View
|
|
1067
|
+
"View PDF structure or selected content. Start with the overview, then narrow by page or node.",
|
|
1066
1068
|
{
|
|
1067
1069
|
path: tool.schema.string().describe("PDF path relative to the project directory"),
|
|
1068
1070
|
node: tool.schema.string().min(1).optional().describe("Node ID to use as the view root"),
|
|
@@ -1087,7 +1089,7 @@ export const ReadSeekPlugin: Plugin = async (_input, options) => {
|
|
|
1087
1089
|
(result) => ({ output: boundDocumentOutput(String(result)) }),
|
|
1088
1090
|
),
|
|
1089
1091
|
readseek_map: readseekTool(
|
|
1090
|
-
"
|
|
1092
|
+
"Show a source file's symbol outline without reading the full file.",
|
|
1091
1093
|
{
|
|
1092
1094
|
path: tool.schema.string().describe("Path relative to the project directory"),
|
|
1093
1095
|
language: tool.schema.string().optional().describe("Language override when auto-detection is ambiguous"),
|
|
@@ -1102,7 +1104,7 @@ export const ReadSeekPlugin: Plugin = async (_input, options) => {
|
|
|
1102
1104
|
},
|
|
1103
1105
|
),
|
|
1104
1106
|
readseek_grep: readseekTool(
|
|
1105
|
-
"Search text or
|
|
1107
|
+
"Search text or regex and return matching LINE:HASH anchors. Set literal for exact text.",
|
|
1106
1108
|
{
|
|
1107
1109
|
pattern: tool.schema.string().describe("Text or regular expression to search for"),
|
|
1108
1110
|
path: tool.schema.string().optional().describe("File or directory, defaulting to the project directory"),
|
|
@@ -1164,9 +1166,9 @@ export const ReadSeekPlugin: Plugin = async (_input, options) => {
|
|
|
1164
1166
|
},
|
|
1165
1167
|
),
|
|
1166
1168
|
readseek_search: readseekTool(
|
|
1167
|
-
"Search
|
|
1169
|
+
"Search AST patterns (ast-grep style) and return LINE:HASH anchors. Use for code structure; use readseek_grep for plain text.",
|
|
1168
1170
|
{
|
|
1169
|
-
pattern: tool.schema.string().describe("ast-grep
|
|
1171
|
+
pattern: tool.schema.string().describe("AST pattern (ast-grep style), such as console.log($$$ARGS)"),
|
|
1170
1172
|
path: tool.schema.string().optional().describe("File or directory, defaulting to the project directory"),
|
|
1171
1173
|
language: tool.schema.string().optional().describe("Language override when auto-detection is ambiguous"),
|
|
1172
1174
|
cached: tool.schema.boolean().optional().describe("Search tracked or indexed files in a Git repository"),
|
|
@@ -1185,7 +1187,7 @@ export const ReadSeekPlugin: Plugin = async (_input, options) => {
|
|
|
1185
1187
|
},
|
|
1186
1188
|
),
|
|
1187
1189
|
readseek_def: readseekTool(
|
|
1188
|
-
"Find symbol declarations by name and return LINE:HASH anchors.
|
|
1190
|
+
"Find symbol declarations by name and return LINE:HASH anchors. Prefer this to text search for definitions.",
|
|
1189
1191
|
{
|
|
1190
1192
|
name: tool.schema.string().describe("Symbol name"),
|
|
1191
1193
|
path: tool.schema.string().optional().describe("File or directory, defaulting to the project directory"),
|
|
@@ -1205,7 +1207,7 @@ export const ReadSeekPlugin: Plugin = async (_input, options) => {
|
|
|
1205
1207
|
},
|
|
1206
1208
|
),
|
|
1207
1209
|
readseek_refs: readseekTool(
|
|
1208
|
-
"Find identifier usages with enclosing symbols. Use before
|
|
1210
|
+
"Find identifier usages with enclosing symbols. Use before renaming or deleting a symbol; add cursor scope to exclude same-named bindings.",
|
|
1209
1211
|
{
|
|
1210
1212
|
name: tool.schema.string().describe("Identifier name"),
|
|
1211
1213
|
path: tool.schema.string().optional().describe("File or directory, defaulting to the project directory"),
|
|
@@ -1235,7 +1237,7 @@ export const ReadSeekPlugin: Plugin = async (_input, options) => {
|
|
|
1235
1237
|
},
|
|
1236
1238
|
),
|
|
1237
1239
|
readseek_hover: readseekTool(
|
|
1238
|
-
"Identify the token and enclosing symbol at a cursor. Use before definition lookup or rename
|
|
1240
|
+
"Identify the token and enclosing symbol at a cursor. Use before definition lookup or rename.",
|
|
1239
1241
|
{
|
|
1240
1242
|
path: tool.schema.string().describe("Path relative to the project directory"),
|
|
1241
1243
|
line: tool.schema.number().int().positive().describe("One-based cursor line"),
|
|
@@ -1253,11 +1255,11 @@ export const ReadSeekPlugin: Plugin = async (_input, options) => {
|
|
|
1253
1255
|
},
|
|
1254
1256
|
),
|
|
1255
1257
|
readseek_rename: readseekTool(
|
|
1256
|
-
"Rename
|
|
1258
|
+
"Rename the symbol at a cursor while preserving same-named bindings. Applies verified edits atomically by default; set apply to false for a dry-run plan.",
|
|
1257
1259
|
{
|
|
1258
1260
|
path: tool.schema.string().describe("Path relative to the project directory"),
|
|
1259
|
-
line: tool.schema.number().int().positive().describe("One-based line of the symbol to rename"),
|
|
1260
|
-
column: tool.schema.number().int().positive().optional().describe("One-based byte column for disambiguation"),
|
|
1261
|
+
line: tool.schema.number().int().positive().describe("One-based cursor line of the symbol to rename"),
|
|
1262
|
+
column: tool.schema.number().int().positive().optional().describe("One-based cursor byte column for disambiguation"),
|
|
1261
1263
|
to: tool.schema.string().min(1).describe("New symbol name"),
|
|
1262
1264
|
workspace: tool.schema.boolean().optional().describe("Include project-wide occurrences"),
|
|
1263
1265
|
apply: tool.schema.boolean().optional().describe("Apply verified edits atomically; defaults to true"),
|
|
@@ -1288,7 +1290,7 @@ export const ReadSeekPlugin: Plugin = async (_input, options) => {
|
|
|
1288
1290
|
},
|
|
1289
1291
|
),
|
|
1290
1292
|
readseek_edit: readseekTool(
|
|
1291
|
-
"Edit an existing text file
|
|
1293
|
+
"Edit an existing text file with fresh LINE:HASH anchors. Stale hashes are rejected before writing.",
|
|
1292
1294
|
{
|
|
1293
1295
|
path: tool.schema.string().describe("Path relative to the project directory"),
|
|
1294
1296
|
edits: tool.schema.array(tool.schema.union([
|
|
@@ -1305,7 +1307,7 @@ export const ReadSeekPlugin: Plugin = async (_input, options) => {
|
|
|
1305
1307
|
tool.schema.object({
|
|
1306
1308
|
insert_after: tool.schema.object({ anchor: tool.schema.string(), new_text: tool.schema.string() }).strict(),
|
|
1307
1309
|
}).strict(),
|
|
1308
|
-
])).min(1).describe("
|
|
1310
|
+
])).min(1).describe("LINE:HASH-anchored edits to apply"),
|
|
1309
1311
|
},
|
|
1310
1312
|
"edit",
|
|
1311
1313
|
async (input, context) => {
|
|
@@ -1356,7 +1358,7 @@ export const ReadSeekPlugin: Plugin = async (_input, options) => {
|
|
|
1356
1358
|
},
|
|
1357
1359
|
),
|
|
1358
1360
|
readseek_check: readseekTool(
|
|
1359
|
-
"
|
|
1361
|
+
"Run a quick syntax check on a source file. Reports parser errors and missing syntax; not a compiler or linter.",
|
|
1360
1362
|
{
|
|
1361
1363
|
path: tool.schema.string().describe("Path relative to the project directory"),
|
|
1362
1364
|
language: tool.schema.string().optional().describe("Language override when auto-detection is ambiguous"),
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-readseek",
|
|
3
|
-
"version": "0.8.
|
|
4
|
-
"description": "OpenCode plugin for
|
|
3
|
+
"version": "0.8.19",
|
|
4
|
+
"description": "OpenCode plugin for anchored file operations and structural code navigation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.ts",
|
|
7
7
|
"exports": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"node": ">=20.0.0"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@jarkkojs/readseek": "^0.8.
|
|
34
|
+
"@jarkkojs/readseek": "^0.8.19",
|
|
35
35
|
"@opencode-ai/plugin": "^1.17.20"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|