@cortexkit/aft-opencode 0.3.0 → 0.4.1
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/dist/bridge.d.ts +8 -0
- package/dist/bridge.d.ts.map +1 -1
- package/dist/bridge.js +45 -2
- package/dist/bridge.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +11 -0
- package/dist/config.js.map +1 -1
- package/dist/downloader.d.ts.map +1 -1
- package/dist/downloader.js +51 -15
- package/dist/downloader.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +56 -16
- package/dist/index.js.map +1 -1
- package/dist/normalize-schemas.d.ts +16 -0
- package/dist/normalize-schemas.d.ts.map +1 -0
- package/dist/normalize-schemas.js +45 -0
- package/dist/normalize-schemas.js.map +1 -0
- package/dist/patch-parser.d.ts.map +1 -1
- package/dist/patch-parser.js +10 -0
- package/dist/patch-parser.js.map +1 -1
- package/dist/platform.d.ts +21 -0
- package/dist/platform.d.ts.map +1 -0
- package/dist/platform.js +31 -0
- package/dist/platform.js.map +1 -0
- package/dist/pool.d.ts.map +1 -1
- package/dist/pool.js +14 -5
- package/dist/pool.js.map +1 -1
- package/dist/resolver.d.ts.map +1 -1
- package/dist/resolver.js +6 -9
- package/dist/resolver.js.map +1 -1
- package/dist/tools/ast.d.ts.map +1 -1
- package/dist/tools/ast.js +52 -59
- package/dist/tools/ast.js.map +1 -1
- package/dist/tools/hoisted.d.ts +1 -1
- package/dist/tools/hoisted.d.ts.map +1 -1
- package/dist/tools/hoisted.js +333 -230
- package/dist/tools/hoisted.js.map +1 -1
- package/dist/tools/imports.d.ts.map +1 -1
- package/dist/tools/imports.js +40 -30
- package/dist/tools/imports.js.map +1 -1
- package/dist/tools/lsp.d.ts +1 -2
- package/dist/tools/lsp.d.ts.map +1 -1
- package/dist/tools/lsp.js +31 -13
- package/dist/tools/lsp.js.map +1 -1
- package/dist/tools/navigation.d.ts.map +1 -1
- package/dist/tools/navigation.js +23 -14
- package/dist/tools/navigation.js.map +1 -1
- package/dist/tools/permissions.d.ts +8 -0
- package/dist/tools/permissions.d.ts.map +1 -0
- package/dist/tools/permissions.js +50 -0
- package/dist/tools/permissions.js.map +1 -0
- package/dist/tools/reading.d.ts +1 -2
- package/dist/tools/reading.d.ts.map +1 -1
- package/dist/tools/reading.js +191 -12
- package/dist/tools/reading.js.map +1 -1
- package/dist/tools/refactoring.d.ts.map +1 -1
- package/dist/tools/refactoring.js +72 -34
- package/dist/tools/refactoring.js.map +1 -1
- package/dist/tools/safety.d.ts.map +1 -1
- package/dist/tools/safety.js +34 -12
- package/dist/tools/safety.js.map +1 -1
- package/dist/tools/structure.d.ts.map +1 -1
- package/dist/tools/structure.js +66 -31
- package/dist/tools/structure.js.map +1 -1
- package/package.json +7 -7
- package/dist/tools/editing.d.ts +0 -8
- package/dist/tools/editing.d.ts.map +0 -1
- package/dist/tools/editing.js +0 -8
- package/dist/tools/editing.js.map +0 -1
package/dist/tools/reading.js
CHANGED
|
@@ -1,40 +1,219 @@
|
|
|
1
|
+
import { readdir } from "node:fs/promises";
|
|
2
|
+
import { extname, join, resolve } from "node:path";
|
|
1
3
|
import { tool } from "@opencode-ai/plugin";
|
|
4
|
+
/** File extensions that aft_outline supports via tree-sitter or markdown parser */
|
|
5
|
+
const OUTLINE_EXTENSIONS = new Set([
|
|
6
|
+
".ts",
|
|
7
|
+
".tsx",
|
|
8
|
+
".js",
|
|
9
|
+
".jsx",
|
|
10
|
+
".mjs",
|
|
11
|
+
".cjs",
|
|
12
|
+
".rs",
|
|
13
|
+
".go",
|
|
14
|
+
".py",
|
|
15
|
+
".rb",
|
|
16
|
+
".c",
|
|
17
|
+
".cpp",
|
|
18
|
+
".h",
|
|
19
|
+
".hpp",
|
|
20
|
+
".cs",
|
|
21
|
+
".java",
|
|
22
|
+
".kt",
|
|
23
|
+
".scala",
|
|
24
|
+
".swift",
|
|
25
|
+
".lua",
|
|
26
|
+
".ex",
|
|
27
|
+
".exs",
|
|
28
|
+
".hs",
|
|
29
|
+
".sol",
|
|
30
|
+
".nix",
|
|
31
|
+
".md",
|
|
32
|
+
".mdx",
|
|
33
|
+
".css",
|
|
34
|
+
".html",
|
|
35
|
+
".json",
|
|
36
|
+
".yaml",
|
|
37
|
+
".yml",
|
|
38
|
+
".sh",
|
|
39
|
+
".bash",
|
|
40
|
+
]);
|
|
2
41
|
const z = tool.schema;
|
|
3
42
|
/**
|
|
4
|
-
* Tool definitions for code reading commands: outline
|
|
5
|
-
* The zoom/read functionality has been merged into the hoisted `read` tool.
|
|
43
|
+
* Tool definitions for code reading commands: outline + zoom.
|
|
6
44
|
*/
|
|
7
45
|
export function readingTools(ctx) {
|
|
8
46
|
return {
|
|
9
47
|
aft_outline: {
|
|
10
|
-
description: "Get a structural outline of a source file — lists all top-level symbols with their kind, name, line range, and visibility. Use this to understand file structure before editing
|
|
11
|
-
"Supports single file (via 'file') or multiple files in one call (via 'files' array).\n" +
|
|
48
|
+
description: "Get a structural outline of a source file, multiple files, or an entire directory — lists all top-level symbols with their kind, name, line range, and visibility. Use this to understand file structure before editing.\n" +
|
|
12
49
|
"Each entry includes 'name', 'kind' (function/class/struct/heading/etc), 'range', 'signature', and 'members' (nested children like methods in classes or sub-headings in markdown).\n" +
|
|
13
50
|
"For Markdown files (.md, .mdx): returns heading hierarchy — h1/h2/h3 as nested symbols with section ranges covering all content until the next same-level heading.\n\n" +
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
51
|
+
"Provide 'filePath', 'files', or 'directory'. Priority: directory > files > filePath. If multiple provided, highest-priority wins.\n" +
|
|
52
|
+
"Supported languages: TypeScript, JavaScript, TSX, Python, Rust, Go, Ruby, C, C++, C#, Java, Kotlin, Scala, Swift, Lua, Elixir, Haskell, Solidity, Nix, Markdown, CSS, HTML, JSON, YAML, Bash.\n" +
|
|
53
|
+
"Directory mode skips commonly ignored directories (node_modules, .git, dist, build, target, __pycache__, venv, vendor, coverage, etc.) and dot-prefixed directories.\n\n" +
|
|
54
|
+
"Returns: Single file { entries: [{ name, kind, range, signature?, exported, members }] }. Multi-file/directory { results: [{ file, ok, entries? }] }.",
|
|
18
55
|
args: {
|
|
19
|
-
|
|
56
|
+
filePath: z
|
|
20
57
|
.string()
|
|
21
58
|
.optional()
|
|
22
|
-
.describe("Path to a single
|
|
59
|
+
.describe("Path to a single file to outline (ignored if 'files' or 'directory' is also provided)"),
|
|
23
60
|
files: z
|
|
24
61
|
.array(z.string())
|
|
25
62
|
.optional()
|
|
26
|
-
.describe("Array of file paths to outline in one call
|
|
63
|
+
.describe("Array of file paths to outline in one call (ignored if 'directory' is also provided)"),
|
|
64
|
+
// The 200-file cap is intentionally only in .describe() — not in the main description —
|
|
65
|
+
// because it's a parameter-level detail. The cap is silent (no warning on truncation).
|
|
66
|
+
directory: z
|
|
67
|
+
.string()
|
|
68
|
+
.optional()
|
|
69
|
+
.describe("Path to a directory — outlines all source files under it recursively (capped at 200 files, takes priority over 'filePath' and 'files')"),
|
|
27
70
|
},
|
|
28
71
|
execute: async (args, context) => {
|
|
29
72
|
const bridge = ctx.pool.getBridge(context.directory);
|
|
73
|
+
const filesArg = Array.isArray(args.files) ? args.files : undefined;
|
|
74
|
+
if (!args.filePath && !filesArg?.length && !args.directory) {
|
|
75
|
+
throw new Error("Provide exactly one of 'filePath', 'files', or 'directory'");
|
|
76
|
+
}
|
|
77
|
+
// Directory mode: discover source files recursively and batch outline
|
|
78
|
+
if (typeof args.directory === "string") {
|
|
79
|
+
const dirPath = resolve(context.directory, args.directory);
|
|
80
|
+
const files = await discoverSourceFiles(dirPath);
|
|
81
|
+
if (files.length === 0) {
|
|
82
|
+
return JSON.stringify({
|
|
83
|
+
success: false,
|
|
84
|
+
message: `No source files found under ${args.directory}`,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
const response = await bridge.send("outline", { files });
|
|
88
|
+
return JSON.stringify(response);
|
|
89
|
+
}
|
|
30
90
|
if (Array.isArray(args.files) && args.files.length > 0) {
|
|
31
91
|
const response = await bridge.send("outline", { files: args.files });
|
|
32
92
|
return JSON.stringify(response);
|
|
33
93
|
}
|
|
34
|
-
const response = await bridge.send("outline", { file: args.
|
|
94
|
+
const response = await bridge.send("outline", { file: args.filePath });
|
|
35
95
|
return JSON.stringify(response);
|
|
36
96
|
},
|
|
37
97
|
},
|
|
98
|
+
aft_zoom: {
|
|
99
|
+
description: `Inspect code symbols with call-graph annotations. Returns the full source of named symbols with what they call and what calls them.
|
|
100
|
+
|
|
101
|
+
Use this when you need to understand a specific function, class, or type in detail — not for reading entire files (use read for that).
|
|
102
|
+
|
|
103
|
+
**Modes:**
|
|
104
|
+
|
|
105
|
+
1. **Inspect symbol** — pass filePath + symbol
|
|
106
|
+
Returns full source + call graph annotations.
|
|
107
|
+
Example: { "filePath": "src/app.ts", "symbol": "handleRequest" }
|
|
108
|
+
|
|
109
|
+
2. **Inspect multiple symbols** — pass filePath + symbols array
|
|
110
|
+
Returns multiple symbols in one call.
|
|
111
|
+
Example: { "filePath": "src/app.ts", "symbols": ["Config", "createApp"] }
|
|
112
|
+
|
|
113
|
+
3. **Read line range with context** — pass filePath + startLine + endLine
|
|
114
|
+
Returns lines with context_before and context_after.
|
|
115
|
+
Example: { "filePath": "src/app.ts", "startLine": 50, "endLine": 100 }
|
|
116
|
+
|
|
117
|
+
For Markdown files, use heading text as symbol name (e.g., symbol: "Architecture").
|
|
118
|
+
|
|
119
|
+
Mode priority: symbols array > single symbol > line range.
|
|
120
|
+
|
|
121
|
+
Returns: Symbol mode { name, kind, range, content, context_before, context_after, annotations: { calls_out, called_by } }. Multi-symbol mode returns an array of these. Line-range mode returns { content, context_before, context_after, start_line, end_line }.`,
|
|
122
|
+
args: {
|
|
123
|
+
filePath: z.string().describe("Path to file (absolute or relative to project root)"),
|
|
124
|
+
symbol: z.string().optional().describe("Name of a single symbol to inspect"),
|
|
125
|
+
symbols: z
|
|
126
|
+
.array(z.string())
|
|
127
|
+
.optional()
|
|
128
|
+
.describe("Array of symbol names to inspect in one call"),
|
|
129
|
+
startLine: z.number().optional().describe("1-based start line for line-range mode"),
|
|
130
|
+
endLine: z
|
|
131
|
+
.number()
|
|
132
|
+
.optional()
|
|
133
|
+
.describe("1-based end line for line-range mode (inclusive, required with startLine)"),
|
|
134
|
+
contextLines: z
|
|
135
|
+
.number()
|
|
136
|
+
.optional()
|
|
137
|
+
.describe("Lines of context before/after the requested range or symbol (default: 3)"),
|
|
138
|
+
},
|
|
139
|
+
execute: async (args, context) => {
|
|
140
|
+
const bridge = ctx.pool.getBridge(context.directory);
|
|
141
|
+
const file = args.filePath;
|
|
142
|
+
// Multi-symbol mode: make separate zoom calls in parallel and combine results
|
|
143
|
+
if (Array.isArray(args.symbols) && args.symbols.length > 0) {
|
|
144
|
+
const results = await Promise.all(args.symbols.map((sym) => {
|
|
145
|
+
const params = { file, symbol: sym };
|
|
146
|
+
if (args.contextLines !== undefined)
|
|
147
|
+
params.context_lines = args.contextLines;
|
|
148
|
+
return bridge.send("zoom", params);
|
|
149
|
+
}));
|
|
150
|
+
return JSON.stringify(results);
|
|
151
|
+
}
|
|
152
|
+
// Single symbol or line-range mode
|
|
153
|
+
const params = { file };
|
|
154
|
+
if (typeof args.symbol === "string")
|
|
155
|
+
params.symbol = args.symbol;
|
|
156
|
+
if (args.startLine !== undefined)
|
|
157
|
+
params.start_line = args.startLine;
|
|
158
|
+
if (args.endLine !== undefined)
|
|
159
|
+
params.end_line = args.endLine;
|
|
160
|
+
if (args.contextLines !== undefined)
|
|
161
|
+
params.context_lines = args.contextLines;
|
|
162
|
+
const data = await bridge.send("zoom", params);
|
|
163
|
+
return JSON.stringify(data);
|
|
164
|
+
},
|
|
165
|
+
},
|
|
38
166
|
};
|
|
39
167
|
}
|
|
168
|
+
/** Recursively discover source files under a directory, skipping common noise directories */
|
|
169
|
+
const SKIP_DIRS = new Set([
|
|
170
|
+
"node_modules",
|
|
171
|
+
".git",
|
|
172
|
+
"dist",
|
|
173
|
+
"build",
|
|
174
|
+
"out",
|
|
175
|
+
".next",
|
|
176
|
+
".nuxt",
|
|
177
|
+
"target",
|
|
178
|
+
"__pycache__",
|
|
179
|
+
".venv",
|
|
180
|
+
"venv",
|
|
181
|
+
"vendor",
|
|
182
|
+
".turbo",
|
|
183
|
+
"coverage",
|
|
184
|
+
".nyc_output",
|
|
185
|
+
".cache",
|
|
186
|
+
]);
|
|
187
|
+
async function discoverSourceFiles(dir, maxFiles = 200) {
|
|
188
|
+
const files = [];
|
|
189
|
+
async function walk(current) {
|
|
190
|
+
if (files.length >= maxFiles)
|
|
191
|
+
return;
|
|
192
|
+
let entries;
|
|
193
|
+
try {
|
|
194
|
+
entries = await readdir(current, { withFileTypes: true });
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
return; // permission denied, not a directory, etc.
|
|
198
|
+
}
|
|
199
|
+
for (const entry of entries) {
|
|
200
|
+
if (files.length >= maxFiles)
|
|
201
|
+
return;
|
|
202
|
+
if (entry.isDirectory()) {
|
|
203
|
+
if (!SKIP_DIRS.has(entry.name) && !entry.name.startsWith(".")) {
|
|
204
|
+
await walk(join(current, entry.name));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
else if (entry.isFile()) {
|
|
208
|
+
const ext = extname(entry.name).toLowerCase();
|
|
209
|
+
if (OUTLINE_EXTENSIONS.has(ext)) {
|
|
210
|
+
files.push(join(current, entry.name));
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
await walk(dir);
|
|
216
|
+
files.sort();
|
|
217
|
+
return files;
|
|
218
|
+
}
|
|
40
219
|
//# sourceMappingURL=reading.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reading.js","sourceRoot":"","sources":["../../src/tools/reading.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"reading.js","sourceRoot":"","sources":["../../src/tools/reading.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"refactoring.d.ts","sourceRoot":"","sources":["../../src/tools/refactoring.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAG1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"refactoring.d.ts","sourceRoot":"","sources":["../../src/tools/refactoring.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAG1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAYjD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CA4HnF"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { tool } from "@opencode-ai/plugin";
|
|
2
2
|
import { queryLspHints } from "../lsp.js";
|
|
3
|
+
import { askEditPermission, permissionDeniedResponse, resolveAbsolutePath, resolveRelativePattern, resolveRelativePatterns, workspacePattern, } from "./permissions.js";
|
|
3
4
|
const z = tool.schema;
|
|
4
5
|
/**
|
|
5
6
|
* Tool definitions for refactoring commands: move_symbol, extract_function, inline_symbol.
|
|
@@ -9,59 +10,96 @@ export function refactoringTools(ctx) {
|
|
|
9
10
|
aft_refactor: {
|
|
10
11
|
description: "Workspace-wide refactoring operations that update imports and references across files.\n\n" +
|
|
11
12
|
"Ops:\n" +
|
|
12
|
-
"- 'move': Move a symbol to another file, updating all imports workspace-wide. Requires 'symbol', 'destination'. Creates a checkpoint before mutating.\n" +
|
|
13
|
-
"
|
|
14
|
-
"- '
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"- name (string, optional): New function name for 'extract' op\n" +
|
|
22
|
-
"- start_line (number, optional): 1-based start line of the range to extract ('extract' op)\n" +
|
|
23
|
-
"- end_line (number, optional): 1-based end line of the range to extract, exclusive ('extract' op)\n" +
|
|
24
|
-
"- call_site_line (number, optional): 1-based line where the call expression is located ('inline' op)\n" +
|
|
25
|
-
"- dry_run (boolean, optional): Preview changes as diff without modifying files\n\n" +
|
|
26
|
-
"All ops need 'file'. Use dry_run to preview before applying.",
|
|
13
|
+
"- 'move': Move a top-level symbol to another file, updating all imports workspace-wide. Requires 'symbol', 'destination'. Creates a checkpoint before mutating. Only works on top-level exports (not nested functions or class methods).\n" +
|
|
14
|
+
" Note: This moves code symbols between files. To rename/move an entire file, use aft_move instead.\n" +
|
|
15
|
+
"- 'extract': Extract a line range into a new function with auto-detected parameters. Requires 'name', 'startLine', 'endLine' (1-based, both inclusive). Supports TS/JS/TSX and Python.\n" +
|
|
16
|
+
"- 'inline': Replace a function call with the function's body, substituting args for params. Requires 'symbol', 'callSiteLine' (1-based). Validates single-return constraint.\n\n" +
|
|
17
|
+
"Each op requires specific parameters — see parameter descriptions for requirements.\n\n" +
|
|
18
|
+
"All ops need 'filePath'. Use dryRun to preview before applying.\n\n" +
|
|
19
|
+
"Returns: move dry-run { ok, dry_run, diffs }; move apply { ok, files_modified, consumers_updated, checkpoint_name, results }. extract returns { file, name, parameters, return_type, syntax_valid, formatted, ... }. inline returns { file, symbol, call_context, substitutions, conflicts, syntax_valid, formatted, ... }.",
|
|
20
|
+
// Parameters are Zod-optional because different ops need different subsets.
|
|
21
|
+
// Runtime guards below validate per-op requirements and give clear errors.
|
|
27
22
|
args: {
|
|
28
23
|
op: z.enum(["move", "extract", "inline"]).describe("Refactoring operation"),
|
|
29
|
-
|
|
24
|
+
filePath: z
|
|
25
|
+
.string()
|
|
26
|
+
.describe("Path to the source file (absolute or relative to project root)"),
|
|
30
27
|
symbol: z
|
|
31
28
|
.string()
|
|
32
29
|
.optional()
|
|
33
|
-
.describe("Symbol name
|
|
30
|
+
.describe("Symbol name — required for 'move' and 'inline' ops"),
|
|
34
31
|
// move
|
|
35
|
-
destination: z
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
32
|
+
destination: z.string().optional().describe("Target file path — required for 'move' op"),
|
|
33
|
+
// scope disambiguates overloaded top-level names, NOT nested symbols.
|
|
34
|
+
// "Only works on top-level exports" in the description is correct — scope selects
|
|
35
|
+
// among multiple top-level symbols that share a name, not class methods.
|
|
39
36
|
scope: z
|
|
40
37
|
.string()
|
|
41
38
|
.optional()
|
|
42
|
-
.describe("Disambiguation scope when multiple symbols share the same name (
|
|
39
|
+
.describe("Disambiguation scope for 'move' op — when multiple top-level symbols share the same name, specify the containing scope to disambiguate (e.g. 'MyClass'). Does NOT enable access to nested symbols or class methods."),
|
|
43
40
|
// extract
|
|
44
|
-
name: z.string().optional().describe("
|
|
45
|
-
|
|
46
|
-
|
|
41
|
+
name: z.string().optional().describe("New function name — required for 'extract' op"),
|
|
42
|
+
startLine: z.number().optional().describe("1-based start line — required for 'extract' op"),
|
|
43
|
+
// endLine is inclusive from the agent's perspective; the execute function adds +1
|
|
44
|
+
// because the Rust backend expects exclusive end. This is intentional — do not document.
|
|
45
|
+
endLine: z
|
|
46
|
+
.number()
|
|
47
|
+
.optional()
|
|
48
|
+
.describe("1-based end line (inclusive) — required for 'extract' op"),
|
|
47
49
|
// inline
|
|
48
|
-
|
|
50
|
+
callSiteLine: z
|
|
49
51
|
.number()
|
|
50
|
-
.
|
|
52
|
+
.optional()
|
|
53
|
+
.describe("1-based call site line — required for 'inline' op"),
|
|
51
54
|
// common
|
|
52
|
-
|
|
55
|
+
dryRun: z
|
|
56
|
+
.boolean()
|
|
57
|
+
.optional()
|
|
58
|
+
.describe("Preview changes as diff without modifying files (default: false)"),
|
|
53
59
|
},
|
|
54
60
|
execute: async (args, context) => {
|
|
55
61
|
const bridge = ctx.pool.getBridge(context.directory);
|
|
56
62
|
const op = args.op;
|
|
63
|
+
const isDryRun = args.dryRun === true;
|
|
64
|
+
if ((op === "move" || op === "inline") && typeof args.symbol !== "string") {
|
|
65
|
+
throw new Error(`'symbol' is required for '${op}' op`);
|
|
66
|
+
}
|
|
67
|
+
if (op === "move" && typeof args.destination !== "string") {
|
|
68
|
+
throw new Error("'destination' is required for 'move' op");
|
|
69
|
+
}
|
|
70
|
+
if (op === "extract") {
|
|
71
|
+
if (typeof args.name !== "string")
|
|
72
|
+
throw new Error("'name' is required for 'extract' op");
|
|
73
|
+
if (args.startLine === undefined)
|
|
74
|
+
throw new Error("'startLine' is required for 'extract' op");
|
|
75
|
+
if (args.endLine === undefined)
|
|
76
|
+
throw new Error("'endLine' is required for 'extract' op");
|
|
77
|
+
}
|
|
78
|
+
if (op === "inline" && args.callSiteLine === undefined) {
|
|
79
|
+
throw new Error("'callSiteLine' is required for 'inline' op");
|
|
80
|
+
}
|
|
81
|
+
if (!isDryRun) {
|
|
82
|
+
const filePath = resolveAbsolutePath(context, args.filePath);
|
|
83
|
+
const patterns = op === "move"
|
|
84
|
+
? resolveRelativePatterns(context, [
|
|
85
|
+
workspacePattern(context),
|
|
86
|
+
args.filePath,
|
|
87
|
+
...(typeof args.destination === "string" ? [args.destination] : []),
|
|
88
|
+
])
|
|
89
|
+
: [resolveRelativePattern(context, args.filePath)];
|
|
90
|
+
const metadata = patterns.length === 1 ? { filepath: filePath } : {};
|
|
91
|
+
const permissionError = await askEditPermission(context, patterns, metadata);
|
|
92
|
+
if (permissionError)
|
|
93
|
+
return permissionDeniedResponse(permissionError);
|
|
94
|
+
}
|
|
57
95
|
const commandMap = {
|
|
58
96
|
move: "move_symbol",
|
|
59
97
|
extract: "extract_function",
|
|
60
98
|
inline: "inline_symbol",
|
|
61
99
|
};
|
|
62
|
-
const params = { file: args.
|
|
63
|
-
if (args.
|
|
64
|
-
params.dry_run = args.
|
|
100
|
+
const params = { file: args.filePath };
|
|
101
|
+
if (args.dryRun !== undefined)
|
|
102
|
+
params.dry_run = args.dryRun;
|
|
65
103
|
switch (op) {
|
|
66
104
|
case "move":
|
|
67
105
|
params.symbol = args.symbol;
|
|
@@ -71,12 +109,12 @@ export function refactoringTools(ctx) {
|
|
|
71
109
|
break;
|
|
72
110
|
case "extract":
|
|
73
111
|
params.name = args.name;
|
|
74
|
-
params.start_line = Number(args.
|
|
75
|
-
params.end_line = Number(args.
|
|
112
|
+
params.start_line = Number(args.startLine);
|
|
113
|
+
params.end_line = Number(args.endLine) + 1; // Agent uses inclusive, Rust expects exclusive
|
|
76
114
|
break;
|
|
77
115
|
case "inline":
|
|
78
116
|
params.symbol = args.symbol;
|
|
79
|
-
params.call_site_line = Number(args.
|
|
117
|
+
params.call_site_line = Number(args.callSiteLine);
|
|
80
118
|
break;
|
|
81
119
|
}
|
|
82
120
|
const hints = await queryLspHints(ctx.client, (args.symbol ?? args.name));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"refactoring.js","sourceRoot":"","sources":["../../src/tools/refactoring.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"refactoring.js","sourceRoot":"","sources":["../../src/tools/refactoring.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,EACL,iBAAiB,EACjB,wBAAwB,EACxB,mBAAmB,EACnB,sBAAsB,EACtB,uBAAuB,EACvB,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;AAEtB;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAkB;IACjD,OAAO;QACL,YAAY,EAAE;YACZ,WAAW,EACT,4FAA4F;gBAC5F,QAAQ;gBACR,4OAA4O;gBAC5O,wGAAwG;gBACxG,0LAA0L;gBAC1L,kLAAkL;gBAClL,yFAAyF;gBACzF,qEAAqE;gBACrE,6TAA6T;YAC/T,4EAA4E;YAC5E,2EAA2E;YAC3E,IAAI,EAAE;gBACJ,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,uBAAuB,CAAC;gBAC3E,QAAQ,EAAE,CAAC;qBACR,MAAM,EAAE;qBACR,QAAQ,CAAC,gEAAgE,CAAC;gBAC7E,MAAM,EAAE,CAAC;qBACN,MAAM,EAAE;qBACR,QAAQ,EAAE;qBACV,QAAQ,CAAC,oDAAoD,CAAC;gBACjE,OAAO;gBACP,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,2CAA2C,CAAC;gBACxF,sEAAsE;gBACtE,kFAAkF;gBAClF,yEAAyE;gBACzE,KAAK,EAAE,CAAC;qBACL,MAAM,EAAE;qBACR,QAAQ,EAAE;qBACV,QAAQ,CACP,qNAAqN,CACtN;gBACH,UAAU;gBACV,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,+CAA+C,CAAC;gBACrF,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,gDAAgD,CAAC;gBAC3F,kFAAkF;gBAClF,yFAAyF;gBACzF,OAAO,EAAE,CAAC;qBACP,MAAM,EAAE;qBACR,QAAQ,EAAE;qBACV,QAAQ,CAAC,0DAA0D,CAAC;gBACvE,SAAS;gBACT,YAAY,EAAE,CAAC;qBACZ,MAAM,EAAE;qBACR,QAAQ,EAAE;qBACV,QAAQ,CAAC,mDAAmD,CAAC;gBAChE,SAAS;gBACT,MAAM,EAAE,CAAC;qBACN,OAAO,EAAE;qBACT,QAAQ,EAAE;qBACV,QAAQ,CAAC,kEAAkE,CAAC;aAChF;YACD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAmB,EAAE;gBAChD,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;gBACrD,MAAM,EAAE,GAAG,IAAI,CAAC,EAAY,CAAC;gBAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC;gBAEtC,IAAI,CAAC,EAAE,KAAK,MAAM,IAAI,EAAE,KAAK,QAAQ,CAAC,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;oBAC1E,MAAM,IAAI,KAAK,CAAC,6BAA6B,EAAE,MAAM,CAAC,CAAC;gBACzD,CAAC;gBACD,IAAI,EAAE,KAAK,MAAM,IAAI,OAAO,IAAI,CAAC,WAAW,KAAK,QAAQ,EAAE,CAAC;oBAC1D,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;gBAC7D,CAAC;gBACD,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;oBACrB,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ;wBAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;oBAC1F,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS;wBAC9B,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;oBAC9D,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS;wBAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;gBAC5F,CAAC;gBACD,IAAI,EAAE,KAAK,QAAQ,IAAI,IAAI,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;oBACvD,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;gBAChE,CAAC;gBAED,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACd,MAAM,QAAQ,GAAG,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC,QAAkB,CAAC,CAAC;oBACvE,MAAM,QAAQ,GACZ,EAAE,KAAK,MAAM;wBACX,CAAC,CAAC,uBAAuB,CAAC,OAAO,EAAE;4BAC/B,gBAAgB,CAAC,OAAO,CAAC;4BACzB,IAAI,CAAC,QAAkB;4BACvB,GAAG,CAAC,OAAO,IAAI,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;yBACpE,CAAC;wBACJ,CAAC,CAAC,CAAC,sBAAsB,CAAC,OAAO,EAAE,IAAI,CAAC,QAAkB,CAAC,CAAC,CAAC;oBACjE,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBACrE,MAAM,eAAe,GAAG,MAAM,iBAAiB,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;oBAC7E,IAAI,eAAe;wBAAE,OAAO,wBAAwB,CAAC,eAAe,CAAC,CAAC;gBACxE,CAAC;gBAED,MAAM,UAAU,GAA2B;oBACzC,IAAI,EAAE,aAAa;oBACnB,OAAO,EAAE,kBAAkB;oBAC3B,MAAM,EAAE,eAAe;iBACxB,CAAC;gBACF,MAAM,MAAM,GAA4B,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAChE,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS;oBAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC;gBAE5D,QAAQ,EAAE,EAAE,CAAC;oBACX,KAAK,MAAM;wBACT,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;wBAC5B,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;wBACtC,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;4BAAE,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;wBACxD,MAAM;oBACR,KAAK,SAAS;wBACZ,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;wBACxB,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;wBAC3C,MAAM,CAAC,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,+CAA+C;wBAC3F,MAAM;oBACR,KAAK,QAAQ;wBACX,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;wBAC5B,MAAM,CAAC,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;wBAClD,MAAM;gBACV,CAAC;gBAED,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,CAAW,CAAC,CAAC;gBACpF,IAAI,KAAK;oBAAE,MAAM,CAAC,SAAS,GAAG,KAAK,CAAC;gBAEpC,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;gBAC3D,OAAO,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;YAClC,CAAC;SACF;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"safety.d.ts","sourceRoot":"","sources":["../../src/tools/safety.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"safety.d.ts","sourceRoot":"","sources":["../../src/tools/safety.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAWjD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CA6E9E"}
|
package/dist/tools/safety.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { tool } from "@opencode-ai/plugin";
|
|
2
|
+
import { askEditPermission, permissionDeniedResponse, resolveAbsolutePath, resolveRelativePattern, workspacePattern, } from "./permissions.js";
|
|
2
3
|
const z = tool.schema;
|
|
3
4
|
/**
|
|
4
5
|
* Tool definitions for safety & recovery commands: undo, edit_history,
|
|
@@ -8,24 +9,26 @@ export function safetyTools(ctx) {
|
|
|
8
9
|
return {
|
|
9
10
|
aft_safety: {
|
|
10
11
|
description: "File safety and recovery operations.\n\n" +
|
|
12
|
+
"IMPORTANT: All backups are in-memory only — lost if the AFT process restarts. Per-file undo stack is capped at 20 entries (oldest evicted).\n\n" +
|
|
11
13
|
"Ops:\n" +
|
|
12
|
-
"- 'undo': Undo the last edit to a file. Requires '
|
|
13
|
-
"- 'history': List all edit snapshots for a file. Requires '
|
|
14
|
+
"- 'undo': Undo the last edit to a file. Requires 'filePath'. Note: pops from the undo stack (irreversible, no redo). Use 'history' to inspect before undoing.\n" +
|
|
15
|
+
"- 'history': List all edit snapshots for a file. Requires 'filePath'.\n" +
|
|
14
16
|
"- 'checkpoint': Save a named snapshot of tracked files. Requires 'name'. Optional 'files' to snapshot specific files only.\n" +
|
|
15
17
|
"- 'restore': Restore files to a previously saved checkpoint. Requires 'name'.\n" +
|
|
16
18
|
"- 'list': List all available named checkpoints. No extra params needed.\n\n" +
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"Use checkpoint before risky multi-file changes. Use undo for quick single-file rollback.\n" +
|
|
23
|
-
"Note: backups are in-memory (lost on restart). Per-file undo stack is capped at 20 entries (oldest evicted).",
|
|
19
|
+
"Each op requires specific parameters — see parameter descriptions for requirements.\n\n" +
|
|
20
|
+
"Use checkpoint before risky multi-file changes. Use undo for quick single-file rollback.\n\n" +
|
|
21
|
+
"Returns: undo { path, backup_id }. history { file, entries }. checkpoint { ok, name }. restore { ok, name }. list { checkpoints }.",
|
|
22
|
+
// Parameters are Zod-optional because different ops need different subsets.
|
|
23
|
+
// Runtime guards below validate per-op requirements and give clear errors.
|
|
24
24
|
args: {
|
|
25
25
|
op: z
|
|
26
26
|
.enum(["undo", "history", "checkpoint", "restore", "list"])
|
|
27
27
|
.describe("Safety operation"),
|
|
28
|
-
|
|
28
|
+
filePath: z
|
|
29
|
+
.string()
|
|
30
|
+
.optional()
|
|
31
|
+
.describe("File path (required for undo, history). Absolute or relative to project root"),
|
|
29
32
|
name: z.string().optional().describe("Checkpoint name (required for checkpoint, restore)"),
|
|
30
33
|
files: z
|
|
31
34
|
.array(z.string())
|
|
@@ -35,6 +38,25 @@ export function safetyTools(ctx) {
|
|
|
35
38
|
execute: async (args, context) => {
|
|
36
39
|
const bridge = ctx.pool.getBridge(context.directory);
|
|
37
40
|
const op = args.op;
|
|
41
|
+
if ((op === "undo" || op === "history") && typeof args.filePath !== "string") {
|
|
42
|
+
throw new Error(`'filePath' is required for '${op}' op`);
|
|
43
|
+
}
|
|
44
|
+
if ((op === "checkpoint" || op === "restore") && typeof args.name !== "string") {
|
|
45
|
+
throw new Error(`'name' is required for '${op}' op`);
|
|
46
|
+
}
|
|
47
|
+
if (op === "undo" && typeof args.filePath === "string") {
|
|
48
|
+
const filePath = resolveAbsolutePath(context, args.filePath);
|
|
49
|
+
const permissionError = await askEditPermission(context, [resolveRelativePattern(context, args.filePath)], { filepath: filePath });
|
|
50
|
+
if (permissionError)
|
|
51
|
+
return permissionDeniedResponse(permissionError);
|
|
52
|
+
}
|
|
53
|
+
if (op === "restore") {
|
|
54
|
+
const permissionError = await askEditPermission(context, [workspacePattern(context)], {
|
|
55
|
+
checkpoint: args.name,
|
|
56
|
+
});
|
|
57
|
+
if (permissionError)
|
|
58
|
+
return permissionDeniedResponse(permissionError);
|
|
59
|
+
}
|
|
38
60
|
const commandMap = {
|
|
39
61
|
undo: "undo",
|
|
40
62
|
history: "edit_history",
|
|
@@ -43,8 +65,8 @@ export function safetyTools(ctx) {
|
|
|
43
65
|
list: "list_checkpoints",
|
|
44
66
|
};
|
|
45
67
|
const params = {};
|
|
46
|
-
if (args.
|
|
47
|
-
params.file = args.
|
|
68
|
+
if (args.filePath !== undefined)
|
|
69
|
+
params.file = args.filePath;
|
|
48
70
|
if (args.name !== undefined)
|
|
49
71
|
params.name = args.name;
|
|
50
72
|
if (args.files !== undefined)
|
package/dist/tools/safety.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"safety.js","sourceRoot":"","sources":["../../src/tools/safety.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"safety.js","sourceRoot":"","sources":["../../src/tools/safety.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"structure.d.ts","sourceRoot":"","sources":["../../src/tools/structure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"structure.d.ts","sourceRoot":"","sources":["../../src/tools/structure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAUjD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAkKjF"}
|