@cortexkit/aft-opencode 0.2.0 → 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/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +6 -0
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -3
- package/dist/index.js.map +1 -1
- package/dist/metadata-store.d.ts +29 -0
- package/dist/metadata-store.d.ts.map +1 -0
- package/dist/metadata-store.js +53 -0
- package/dist/metadata-store.js.map +1 -0
- package/dist/patch-parser.d.ts +33 -0
- package/dist/patch-parser.d.ts.map +1 -0
- package/dist/patch-parser.js +237 -0
- package/dist/patch-parser.js.map +1 -0
- package/dist/tools/ast.d.ts.map +1 -1
- package/dist/tools/ast.js +159 -62
- package/dist/tools/ast.js.map +1 -1
- package/dist/tools/editing.d.ts +3 -2
- package/dist/tools/editing.d.ts.map +1 -1
- package/dist/tools/editing.js +4 -146
- package/dist/tools/editing.js.map +1 -1
- package/dist/tools/hoisted.d.ts +26 -0
- package/dist/tools/hoisted.d.ts.map +1 -0
- package/dist/tools/hoisted.js +749 -0
- package/dist/tools/hoisted.js.map +1 -0
- package/dist/tools/imports.d.ts.map +1 -1
- package/dist/tools/imports.js +15 -5
- package/dist/tools/imports.js.map +1 -1
- package/dist/tools/lsp.d.ts.map +1 -1
- package/dist/tools/lsp.js +25 -108
- package/dist/tools/lsp.js.map +1 -1
- package/dist/tools/navigation.d.ts.map +1 -1
- package/dist/tools/navigation.js +9 -3
- package/dist/tools/navigation.js.map +1 -1
- package/dist/tools/reading.d.ts +2 -1
- package/dist/tools/reading.d.ts.map +1 -1
- package/dist/tools/reading.js +7 -70
- package/dist/tools/reading.js.map +1 -1
- package/dist/tools/refactoring.d.ts.map +1 -1
- package/dist/tools/refactoring.js +16 -5
- package/dist/tools/refactoring.js.map +1 -1
- package/dist/tools/safety.d.ts.map +1 -1
- package/dist/tools/safety.js +11 -6
- package/dist/tools/safety.js.map +1 -1
- package/dist/tools/structure.d.ts.map +1 -1
- package/dist/tools/structure.js +22 -7
- package/dist/tools/structure.js.map +1 -1
- package/package.json +6 -6
package/dist/tools/ast.js
CHANGED
|
@@ -5,6 +5,29 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { tool } from "@opencode-ai/plugin";
|
|
7
7
|
const z = tool.schema;
|
|
8
|
+
/** Show output in opencode UI via metadata callback. */
|
|
9
|
+
function showOutputToUser(context, output) {
|
|
10
|
+
const ctx = context;
|
|
11
|
+
ctx.metadata?.({ metadata: { output } });
|
|
12
|
+
}
|
|
13
|
+
/** Provide helpful hints when a pattern returns 0 matches. */
|
|
14
|
+
function getEmptyResultHint(pattern, lang) {
|
|
15
|
+
const src = pattern.trim();
|
|
16
|
+
if (lang === "python") {
|
|
17
|
+
if (src.startsWith("class ") && src.endsWith(":")) {
|
|
18
|
+
return `Hint: Python class patterns need a body. Try: "${src.slice(0, -1)}" or "${src}\n $$$"`;
|
|
19
|
+
}
|
|
20
|
+
if ((src.startsWith("def ") || src.startsWith("async def ")) && src.endsWith(":")) {
|
|
21
|
+
return `Hint: Python function patterns need a body. Try adding "\\n $$$" after the colon.`;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
if (["javascript", "typescript", "tsx"].includes(lang)) {
|
|
25
|
+
if (/^(export\s+)?(async\s+)?function\s+\$[A-Z_]+\s*$/i.test(src)) {
|
|
26
|
+
return `Hint: Function patterns need params and body. Try: "function $NAME($$$) { $$$ }"`;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
8
31
|
const SUPPORTED_LANGS = [
|
|
9
32
|
"bash",
|
|
10
33
|
"c",
|
|
@@ -33,70 +56,144 @@ const SUPPORTED_LANGS = [
|
|
|
33
56
|
"yaml",
|
|
34
57
|
];
|
|
35
58
|
export function astTools(ctx) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
59
|
+
const searchTool = {
|
|
60
|
+
description: "Search code patterns across filesystem using AST-aware matching. Supports 25 languages.\n\n" +
|
|
61
|
+
"Use meta-variables: $VAR matches a single AST node, $$$ matches multiple nodes (variadic).\n" +
|
|
62
|
+
"IMPORTANT: Patterns must be complete AST nodes (valid code fragments).\n" +
|
|
63
|
+
"For functions, include params and body: 'export async function $NAME($$$) { $$$ }' not just 'export async function $NAME'.\n\n" +
|
|
64
|
+
"Parameters:\n" +
|
|
65
|
+
"- pattern (string, required): AST pattern with meta-variables. Must be a complete AST node.\n" +
|
|
66
|
+
"- lang (enum, required): Target language — bash, c, cpp, csharp, css, elixir, go, haskell, html, java, javascript, json, kotlin, lua, nix, php, python, ruby, rust, scala, solidity, swift, typescript, tsx, yaml\n" +
|
|
67
|
+
"- paths (string[], optional): Directories or files to search (default: project root)\n" +
|
|
68
|
+
"- globs (string[], optional): Include/exclude glob patterns — prefix '!' to exclude (e.g. ['src/**', '!node_modules'])\n" +
|
|
69
|
+
"- context (number, optional): Number of context lines to show around each match\n\n" +
|
|
70
|
+
"Examples: pattern='console.log($MSG)' lang='typescript', pattern='async function $NAME($$$) { $$$ }' lang='javascript', pattern='def $FUNC($$$): $$$' lang='python'",
|
|
71
|
+
args: {
|
|
72
|
+
pattern: z
|
|
73
|
+
.string()
|
|
74
|
+
.describe("AST pattern with meta-variables ($VAR, $$$). Must be complete AST node."),
|
|
75
|
+
lang: z.enum(SUPPORTED_LANGS).describe("Target language"),
|
|
76
|
+
paths: z.array(z.string()).optional().describe("Paths to search (default: ['.'])"),
|
|
77
|
+
globs: z.array(z.string()).optional().describe("Include/exclude globs (prefix ! to exclude)"),
|
|
78
|
+
context: z.number().optional().describe("Context lines around match"),
|
|
79
|
+
},
|
|
80
|
+
execute: async (args, context) => {
|
|
81
|
+
const bridge = ctx.pool.getBridge(context.directory);
|
|
82
|
+
const params = {
|
|
83
|
+
pattern: args.pattern,
|
|
84
|
+
lang: args.lang,
|
|
85
|
+
};
|
|
86
|
+
if (args.paths)
|
|
87
|
+
params.paths = args.paths;
|
|
88
|
+
if (args.globs)
|
|
89
|
+
params.globs = args.globs;
|
|
90
|
+
if (args.context !== undefined)
|
|
91
|
+
params.context = Number(args.context);
|
|
92
|
+
const response = await bridge.send("ast_search", params);
|
|
93
|
+
// Format output for readability
|
|
94
|
+
const data = response;
|
|
95
|
+
const matchCount = data.total_matches ?? data.matches?.length ?? 0;
|
|
96
|
+
const filesSearched = data.files_searched ?? 0;
|
|
97
|
+
let output;
|
|
98
|
+
if (matchCount === 0) {
|
|
99
|
+
output = `No matches found (searched ${filesSearched} files)`;
|
|
100
|
+
// Add hints for common pattern mistakes
|
|
101
|
+
const hint = getEmptyResultHint(args.pattern, args.lang);
|
|
102
|
+
if (hint) {
|
|
103
|
+
output += `\n\n${hint}`;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
output = `Found ${matchCount} match(es) across ${filesSearched} file(s)\n\n`;
|
|
108
|
+
if (data.matches) {
|
|
109
|
+
for (const m of data.matches) {
|
|
110
|
+
const relFile = m.file ?? "unknown";
|
|
111
|
+
const line = m.line ?? 0;
|
|
112
|
+
output += `${relFile}:${line}\n`;
|
|
113
|
+
if (m.text) {
|
|
114
|
+
output += ` ${m.text.trim()}\n`;
|
|
115
|
+
}
|
|
116
|
+
if (m.meta_variables && Object.keys(m.meta_variables).length > 0) {
|
|
117
|
+
for (const [k, v] of Object.entries(m.meta_variables)) {
|
|
118
|
+
output += ` ${k}: ${v}\n`;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
output += "\n";
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// Show output in UI
|
|
126
|
+
showOutputToUser(context, output);
|
|
127
|
+
return output;
|
|
70
128
|
},
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
129
|
+
};
|
|
130
|
+
const replaceTool = {
|
|
131
|
+
description: "Replace code patterns across filesystem with AST-aware rewriting. Dry-run by default — set dryRun=false to apply.\n\n" +
|
|
132
|
+
"Use meta-variables in the rewrite pattern to preserve matched content from the pattern.\n\n" +
|
|
133
|
+
"Parameters:\n" +
|
|
134
|
+
"- pattern (string, required): AST pattern to match (same syntax as ast_grep_search)\n" +
|
|
135
|
+
"- rewrite (string, required): Replacement pattern — use $VAR from the match pattern to preserve captured content\n" +
|
|
136
|
+
"- lang (enum, required): Target language — typescript, javascript, tsx, python, rust, go, and 19 more\n" +
|
|
137
|
+
"- paths (string[], optional): Directories or files to search (default: project root)\n" +
|
|
138
|
+
"- globs (string[], optional): Include/exclude glob patterns — prefix '!' to exclude\n" +
|
|
139
|
+
"- dryRun (boolean, optional, default: true): Preview changes without applying. Set to false to apply.\n\n" +
|
|
140
|
+
"Example: pattern='console.log($MSG)' rewrite='logger.info($MSG)' lang='typescript' — replaces all console.log calls with logger.info across TypeScript files.",
|
|
141
|
+
args: {
|
|
142
|
+
pattern: z.string().describe("AST pattern to match"),
|
|
143
|
+
rewrite: z.string().describe("Replacement pattern (can use $VAR from pattern)"),
|
|
144
|
+
lang: z.enum(SUPPORTED_LANGS).describe("Target language"),
|
|
145
|
+
paths: z.array(z.string()).optional().describe("Paths to search"),
|
|
146
|
+
globs: z.array(z.string()).optional().describe("Include/exclude globs"),
|
|
147
|
+
dryRun: z.boolean().optional().describe("Preview changes without applying (default: true)"),
|
|
148
|
+
},
|
|
149
|
+
execute: async (args, context) => {
|
|
150
|
+
const bridge = ctx.pool.getBridge(context.directory);
|
|
151
|
+
const params = {
|
|
152
|
+
pattern: args.pattern,
|
|
153
|
+
rewrite: args.rewrite,
|
|
154
|
+
lang: args.lang,
|
|
155
|
+
};
|
|
156
|
+
if (args.paths)
|
|
157
|
+
params.paths = args.paths;
|
|
158
|
+
if (args.globs)
|
|
159
|
+
params.globs = args.globs;
|
|
160
|
+
params.dry_run = args.dryRun !== false;
|
|
161
|
+
const response = await bridge.send("ast_replace", params);
|
|
162
|
+
const data = response;
|
|
163
|
+
const isDryRun = args.dryRun !== false;
|
|
164
|
+
const matchCount = data.total_matches ?? data.matches?.length ?? 0;
|
|
165
|
+
const filesSearched = data.files_searched ?? 0;
|
|
166
|
+
let output;
|
|
167
|
+
if (matchCount === 0) {
|
|
168
|
+
output = `No matches found (searched ${filesSearched} files)`;
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
output = isDryRun
|
|
172
|
+
? `[DRY RUN] Would replace ${matchCount} match(es) across ${filesSearched} file(s)\n\n`
|
|
173
|
+
: `Replaced ${matchCount} match(es) across ${filesSearched} file(s)\n\n`;
|
|
174
|
+
if (data.matches) {
|
|
175
|
+
for (const m of data.matches) {
|
|
176
|
+
const relFile = m.file ?? "unknown";
|
|
177
|
+
const line = m.line ?? 0;
|
|
178
|
+
output += `${relFile}:${line}\n`;
|
|
179
|
+
if (m.text && m.replacement) {
|
|
180
|
+
output += ` - ${m.text.trim()}\n`;
|
|
181
|
+
output += ` + ${m.replacement.trim()}\n`;
|
|
182
|
+
}
|
|
183
|
+
output += "\n";
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
showOutputToUser(context, output);
|
|
188
|
+
return output;
|
|
99
189
|
},
|
|
100
190
|
};
|
|
191
|
+
// When hoisting: register as ast_grep_search/ast_grep_replace (override oh-my-opencode's)
|
|
192
|
+
// When not hoisting: register as aft_ast_search/aft_ast_replace
|
|
193
|
+
const hoisting = ctx.config.hoist_builtin_tools !== false;
|
|
194
|
+
return {
|
|
195
|
+
[hoisting ? "ast_grep_search" : "aft_ast_search"]: searchTool,
|
|
196
|
+
[hoisting ? "ast_grep_replace" : "aft_ast_replace"]: replaceTool,
|
|
197
|
+
};
|
|
101
198
|
}
|
|
102
199
|
//# sourceMappingURL=ast.js.map
|
package/dist/tools/ast.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ast.js","sourceRoot":"","sources":["../../src/tools/ast.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAE3C,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;AAKtB,MAAM,eAAe,GAAG;IACtB,MAAM;IACN,GAAG;IACH,KAAK;IACL,QAAQ;IACR,KAAK;IACL,QAAQ;IACR,IAAI;IACJ,SAAS;IACT,MAAM;IACN,MAAM;IACN,YAAY;IACZ,MAAM;IACN,QAAQ;IACR,KAAK;IACL,KAAK;IACL,KAAK;IACL,QAAQ;IACR,MAAM;IACN,MAAM;IACN,OAAO;IACP,UAAU;IACV,OAAO;IACP,YAAY;IACZ,KAAK;IACL,MAAM;CACE,CAAC;AAEX,MAAM,UAAU,QAAQ,CAAC,GAAkB;IACzC,
|
|
1
|
+
{"version":3,"file":"ast.js","sourceRoot":"","sources":["../../src/tools/ast.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAE3C,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;AAKtB,wDAAwD;AACxD,SAAS,gBAAgB,CAAC,OAAgB,EAAE,MAAc;IACxD,MAAM,GAAG,GAAG,OAEX,CAAC;IACF,GAAG,CAAC,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;AAC3C,CAAC;AAED,8DAA8D;AAC9D,SAAS,kBAAkB,CAAC,OAAe,EAAE,IAAY;IACvD,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAE3B,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtB,IAAI,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAClD,OAAO,kDAAkD,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,GAAG,YAAY,CAAC;QACpG,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAClF,OAAO,sFAAsF,CAAC;QAChG,CAAC;IACH,CAAC;IAED,IAAI,CAAC,YAAY,EAAE,YAAY,EAAE,KAAK,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACvD,IAAI,mDAAmD,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAClE,OAAO,kFAAkF,CAAC;QAC5F,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,eAAe,GAAG;IACtB,MAAM;IACN,GAAG;IACH,KAAK;IACL,QAAQ;IACR,KAAK;IACL,QAAQ;IACR,IAAI;IACJ,SAAS;IACT,MAAM;IACN,MAAM;IACN,YAAY;IACZ,MAAM;IACN,QAAQ;IACR,KAAK;IACL,KAAK;IACL,KAAK;IACL,QAAQ;IACR,MAAM;IACN,MAAM;IACN,OAAO;IACP,UAAU;IACV,OAAO;IACP,YAAY;IACZ,KAAK;IACL,MAAM;CACE,CAAC;AAEX,MAAM,UAAU,QAAQ,CAAC,GAAkB;IACzC,MAAM,UAAU,GAAmB;QACjC,WAAW,EACT,6FAA6F;YAC7F,8FAA8F;YAC9F,0EAA0E;YAC1E,gIAAgI;YAChI,eAAe;YACf,+FAA+F;YAC/F,qNAAqN;YACrN,wFAAwF;YACxF,0HAA0H;YAC1H,qFAAqF;YACrF,qKAAqK;QACvK,IAAI,EAAE;YACJ,OAAO,EAAE,CAAC;iBACP,MAAM,EAAE;iBACR,QAAQ,CAAC,yEAAyE,CAAC;YACtF,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,QAAQ,CAAC,iBAAiB,CAAC;YACzD,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,kCAAkC,CAAC;YAClF,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,6CAA6C,CAAC;YAC7F,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,4BAA4B,CAAC;SACtE;QACD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAmB,EAAE;YAChD,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YACrD,MAAM,MAAM,GAA4B;gBACtC,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,IAAI,EAAE,IAAI,CAAC,IAAI;aAChB,CAAC;YACF,IAAI,IAAI,CAAC,KAAK;gBAAE,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;YAC1C,IAAI,IAAI,CAAC,KAAK;gBAAE,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;YAC1C,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS;gBAAE,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACtE,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;YAEzD,gCAAgC;YAChC,MAAM,IAAI,GAAG,QAUZ,CAAC;YAEF,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC,CAAC;YACnE,MAAM,aAAa,GAAG,IAAI,CAAC,cAAc,IAAI,CAAC,CAAC;YAE/C,IAAI,MAAc,CAAC;YACnB,IAAI,UAAU,KAAK,CAAC,EAAE,CAAC;gBACrB,MAAM,GAAG,8BAA8B,aAAa,SAAS,CAAC;gBAC9D,wCAAwC;gBACxC,MAAM,IAAI,GAAG,kBAAkB,CAAC,IAAI,CAAC,OAAiB,EAAE,IAAI,CAAC,IAAc,CAAC,CAAC;gBAC7E,IAAI,IAAI,EAAE,CAAC;oBACT,MAAM,IAAI,OAAO,IAAI,EAAE,CAAC;gBAC1B,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,MAAM,GAAG,SAAS,UAAU,qBAAqB,aAAa,cAAc,CAAC;gBAC7E,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;oBACjB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;wBAC7B,MAAM,OAAO,GAAG,CAAC,CAAC,IAAI,IAAI,SAAS,CAAC;wBACpC,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC;wBACzB,MAAM,IAAI,GAAG,OAAO,IAAI,IAAI,IAAI,CAAC;wBACjC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;4BACX,MAAM,IAAI,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC;wBACnC,CAAC;wBACD,IAAI,CAAC,CAAC,cAAc,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;4BACjE,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC,EAAE,CAAC;gCACtD,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC;4BAC7B,CAAC;wBACH,CAAC;wBACD,MAAM,IAAI,IAAI,CAAC;oBACjB,CAAC;gBACH,CAAC;YACH,CAAC;YAED,oBAAoB;YACpB,gBAAgB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAClC,OAAO,MAAM,CAAC;QAChB,CAAC;KACF,CAAC;IAEF,MAAM,WAAW,GAAmB;QAClC,WAAW,EACT,uHAAuH;YACvH,6FAA6F;YAC7F,eAAe;YACf,uFAAuF;YACvF,oHAAoH;YACpH,yGAAyG;YACzG,wFAAwF;YACxF,uFAAuF;YACvF,2GAA2G;YAC3G,+JAA+J;QACjK,IAAI,EAAE;YACJ,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,sBAAsB,CAAC;YACpD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iDAAiD,CAAC;YAC/E,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,QAAQ,CAAC,iBAAiB,CAAC;YACzD,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,iBAAiB,CAAC;YACjE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,uBAAuB,CAAC;YACvE,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,kDAAkD,CAAC;SAC5F;QACD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAmB,EAAE;YAChD,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YACrD,MAAM,MAAM,GAA4B;gBACtC,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,IAAI,EAAE,IAAI,CAAC,IAAI;aAChB,CAAC;YACF,IAAI,IAAI,CAAC,KAAK;gBAAE,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;YAC1C,IAAI,IAAI,CAAC,KAAK;gBAAE,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;YAC1C,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC,MAAM,KAAK,KAAK,CAAC;YACvC,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;YAE1D,MAAM,IAAI,GAAG,QAKZ,CAAC;YAEF,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,KAAK,KAAK,CAAC;YACvC,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC,CAAC;YACnE,MAAM,aAAa,GAAG,IAAI,CAAC,cAAc,IAAI,CAAC,CAAC;YAE/C,IAAI,MAAc,CAAC;YACnB,IAAI,UAAU,KAAK,CAAC,EAAE,CAAC;gBACrB,MAAM,GAAG,8BAA8B,aAAa,SAAS,CAAC;YAChE,CAAC;iBAAM,CAAC;gBACN,MAAM,GAAG,QAAQ;oBACf,CAAC,CAAC,2BAA2B,UAAU,qBAAqB,aAAa,cAAc;oBACvF,CAAC,CAAC,YAAY,UAAU,qBAAqB,aAAa,cAAc,CAAC;gBAC3E,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;oBACjB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;wBAC7B,MAAM,OAAO,GAAG,CAAC,CAAC,IAAI,IAAI,SAAS,CAAC;wBACpC,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC;wBACzB,MAAM,IAAI,GAAG,OAAO,IAAI,IAAI,IAAI,CAAC;wBACjC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;4BAC5B,MAAM,IAAI,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC;4BACnC,MAAM,IAAI,OAAO,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC;wBAC5C,CAAC;wBACD,MAAM,IAAI,IAAI,CAAC;oBACjB,CAAC;gBACH,CAAC;YACH,CAAC;YAED,gBAAgB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAClC,OAAO,MAAM,CAAC;QAChB,CAAC;KACF,CAAC;IAEF,0FAA0F;IAC1F,gEAAgE;IAChE,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,mBAAmB,KAAK,KAAK,CAAC;IAC1D,OAAO;QACL,CAAC,QAAQ,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,gBAAgB,CAAC,EAAE,UAAU;QAC7D,CAAC,QAAQ,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,iBAAiB,CAAC,EAAE,WAAW;KACjE,CAAC;AACJ,CAAC"}
|
package/dist/tools/editing.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { ToolDefinition } from "@opencode-ai/plugin";
|
|
2
2
|
import type { PluginContext } from "../types.js";
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Editing tools (aft_edit) have been merged into the hoisted `edit` tool.
|
|
5
|
+
* This file is kept for future editing-specific tools that don't overlap.
|
|
5
6
|
*/
|
|
6
|
-
export declare function editingTools(
|
|
7
|
+
export declare function editingTools(_ctx: PluginContext): Record<string, ToolDefinition>;
|
|
7
8
|
//# sourceMappingURL=editing.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editing.d.ts","sourceRoot":"","sources":["../../src/tools/editing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"editing.d.ts","sourceRoot":"","sources":["../../src/tools/editing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAEhF"}
|
package/dist/tools/editing.js
CHANGED
|
@@ -1,150 +1,8 @@
|
|
|
1
|
-
import { tool } from "@opencode-ai/plugin";
|
|
2
|
-
import { queryLspHints } from "../lsp.js";
|
|
3
|
-
const z = tool.schema;
|
|
4
|
-
/** Valid operations for edit_symbol. */
|
|
5
|
-
const editOperationEnum = z
|
|
6
|
-
.enum(["replace", "delete", "insert_before", "insert_after"])
|
|
7
|
-
.describe("The edit operation to perform on the symbol");
|
|
8
|
-
/** Schema for a single batch edit item — either match-replace or line-range. */
|
|
9
|
-
const batchEditItem = z.union([
|
|
10
|
-
z.object({
|
|
11
|
-
match: z.string().describe("Text pattern to find and replace"),
|
|
12
|
-
replacement: z.string().describe("Replacement text"),
|
|
13
|
-
}),
|
|
14
|
-
z.object({
|
|
15
|
-
line_start: z.number().describe("Start line number (1-indexed)"),
|
|
16
|
-
line_end: z.number().describe("End line number (1-indexed, inclusive)"),
|
|
17
|
-
content: z.string().describe("Content to replace the line range with"),
|
|
18
|
-
}),
|
|
19
|
-
]);
|
|
20
1
|
/**
|
|
21
|
-
*
|
|
2
|
+
* Editing tools (aft_edit) have been merged into the hoisted `edit` tool.
|
|
3
|
+
* This file is kept for future editing-specific tools that don't overlap.
|
|
22
4
|
*/
|
|
23
|
-
export function editingTools(
|
|
24
|
-
return {
|
|
25
|
-
aft_edit: {
|
|
26
|
-
description: "Edit files with tree-sitter precision. All modes auto-backup before changes and support dry_run.\n" +
|
|
27
|
-
"Modes:\n" +
|
|
28
|
-
"- 'symbol': Edit a named symbol (function, class, type) — preferred for code changes. Needs 'symbol', 'operation' (replace/delete/insert_before/insert_after), and 'content'. Response includes context_before/context_after (3 lines each) to detect duplication.\n" +
|
|
29
|
-
"- 'match': Find and replace text by content match — use for config values, strings, unnamed code. Needs 'match', 'replacement'. Set replace_all=true to replace ALL occurrences at once. Returns ambiguous_match if multiple hits without occurrence or replace_all. For multi-line matching, use actual newlines in the JSON string value. Supports glob patterns in 'file' (e.g. '**/*.ts') to replace across multiple files at once — returns {files: [...], total_replacements, total_files}.\n" +
|
|
30
|
-
"- 'write': Write full file content — for new files or complete rewrites. Needs 'content'.\n" +
|
|
31
|
-
"- 'batch': Multiple edits in one file atomically — each edit is {match, replacement} or {line_start, line_end, content} (1-based, inclusive). Supports per-edit 'occurrence' for disambiguation. Set content to empty string to delete lines entirely. line_start == total_lines+1 appends at EOF. line_end is auto-clamped to last line (safe to overshoot).\n" +
|
|
32
|
-
"- 'transaction': Atomic multi-file edits with rollback — if any file fails, all revert. Needs 'operations' array of {file, command, ...}.\n" +
|
|
33
|
-
"Returns formatted, validation_errors, backup_id.",
|
|
34
|
-
args: {
|
|
35
|
-
mode: z.enum(["symbol", "match", "write", "batch", "transaction"]).describe("Editing mode"),
|
|
36
|
-
file: z
|
|
37
|
-
.string()
|
|
38
|
-
.optional()
|
|
39
|
-
.describe("Path to the file (required for all modes except transaction)"),
|
|
40
|
-
// symbol mode
|
|
41
|
-
symbol: z.string().optional().describe("Symbol name to edit (symbol mode)"),
|
|
42
|
-
operation: editOperationEnum.optional().describe("Edit operation (symbol mode)"),
|
|
43
|
-
scope: z
|
|
44
|
-
.string()
|
|
45
|
-
.optional()
|
|
46
|
-
.describe("Qualified scope for disambiguation (e.g. 'ClassName.method')"),
|
|
47
|
-
// match mode
|
|
48
|
-
match: z.string().optional().describe("Text to find (match mode)"),
|
|
49
|
-
replacement: z.string().optional().describe("Replacement text (match mode)"),
|
|
50
|
-
occurrence: z
|
|
51
|
-
.number()
|
|
52
|
-
.describe("Zero-based index selecting which occurrence to replace when multiple matches exist (0 = first, 1 = second, etc). Use with ambiguous_match response. (match mode)"),
|
|
53
|
-
replace_all: z
|
|
54
|
-
.boolean()
|
|
55
|
-
.optional()
|
|
56
|
-
.describe("Replace ALL occurrences instead of disambiguating (match mode, default: false)"),
|
|
57
|
-
// write + symbol content
|
|
58
|
-
content: z
|
|
59
|
-
.string()
|
|
60
|
-
.optional()
|
|
61
|
-
.describe("New content (write mode: full file, symbol mode: replacement body)"),
|
|
62
|
-
create_dirs: z
|
|
63
|
-
.boolean()
|
|
64
|
-
.optional()
|
|
65
|
-
.describe("Create parent directories (write mode, default: false)"),
|
|
66
|
-
// batch mode
|
|
67
|
-
edits: z
|
|
68
|
-
.array(batchEditItem)
|
|
69
|
-
.optional()
|
|
70
|
-
.describe("Array of edits to apply atomically (batch mode)"),
|
|
71
|
-
// transaction mode
|
|
72
|
-
operations: z
|
|
73
|
-
.array(z.object({
|
|
74
|
-
file: z.string().describe("File path"),
|
|
75
|
-
command: z.enum(["write", "edit_match"]).describe("Operation type"),
|
|
76
|
-
content: z.string().optional().describe("Full content for write"),
|
|
77
|
-
match: z.string().optional().describe("Text to find for edit_match"),
|
|
78
|
-
replacement: z.string().optional().describe("Replacement for edit_match"),
|
|
79
|
-
}))
|
|
80
|
-
.optional()
|
|
81
|
-
.describe("Array of file operations (transaction mode)"),
|
|
82
|
-
// common
|
|
83
|
-
validate: z
|
|
84
|
-
.enum(["syntax", "full"])
|
|
85
|
-
.optional()
|
|
86
|
-
.describe("Validation level: 'syntax' (default) or 'full'"),
|
|
87
|
-
dry_run: z.boolean().optional().describe("Preview as unified diff without modifying files"),
|
|
88
|
-
},
|
|
89
|
-
execute: async (args, context) => {
|
|
90
|
-
const bridge = ctx.pool.getBridge(context.directory);
|
|
91
|
-
const mode = args.mode;
|
|
92
|
-
const params = {};
|
|
93
|
-
if (args.file !== undefined)
|
|
94
|
-
params.file = args.file;
|
|
95
|
-
if (args.validate !== undefined)
|
|
96
|
-
params.validate = args.validate;
|
|
97
|
-
if (args.dry_run !== undefined)
|
|
98
|
-
params.dry_run = args.dry_run;
|
|
99
|
-
let command;
|
|
100
|
-
switch (mode) {
|
|
101
|
-
case "symbol": {
|
|
102
|
-
command = "edit_symbol";
|
|
103
|
-
params.symbol = args.symbol;
|
|
104
|
-
params.operation = args.operation;
|
|
105
|
-
if (args.content !== undefined)
|
|
106
|
-
params.content = args.content;
|
|
107
|
-
if (args.scope !== undefined)
|
|
108
|
-
params.scope = args.scope;
|
|
109
|
-
const hints = await queryLspHints(ctx.client, args.symbol);
|
|
110
|
-
if (hints)
|
|
111
|
-
params.lsp_hints = hints;
|
|
112
|
-
break;
|
|
113
|
-
}
|
|
114
|
-
case "match": {
|
|
115
|
-
command = "edit_match";
|
|
116
|
-
params.match = args.match;
|
|
117
|
-
params.replacement = args.replacement;
|
|
118
|
-
if (args.occurrence !== undefined)
|
|
119
|
-
params.occurrence = Number(args.occurrence);
|
|
120
|
-
if (args.replace_all !== undefined)
|
|
121
|
-
params.replace_all = args.replace_all;
|
|
122
|
-
break;
|
|
123
|
-
}
|
|
124
|
-
case "write": {
|
|
125
|
-
command = "write";
|
|
126
|
-
params.content = args.content;
|
|
127
|
-
if (args.create_dirs !== undefined)
|
|
128
|
-
params.create_dirs = args.create_dirs;
|
|
129
|
-
break;
|
|
130
|
-
}
|
|
131
|
-
case "batch": {
|
|
132
|
-
command = "batch";
|
|
133
|
-
params.edits = args.edits;
|
|
134
|
-
break;
|
|
135
|
-
}
|
|
136
|
-
case "transaction": {
|
|
137
|
-
command = "transaction";
|
|
138
|
-
params.operations = args.operations;
|
|
139
|
-
break;
|
|
140
|
-
}
|
|
141
|
-
default:
|
|
142
|
-
command = mode;
|
|
143
|
-
}
|
|
144
|
-
const response = await bridge.send(command, params);
|
|
145
|
-
return JSON.stringify(response);
|
|
146
|
-
},
|
|
147
|
-
},
|
|
148
|
-
};
|
|
5
|
+
export function editingTools(_ctx) {
|
|
6
|
+
return {};
|
|
149
7
|
}
|
|
150
8
|
//# sourceMappingURL=editing.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editing.js","sourceRoot":"","sources":["../../src/tools/editing.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"editing.js","sourceRoot":"","sources":["../../src/tools/editing.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,IAAmB;IAC9C,OAAO,EAAE,CAAC;AACZ,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hoisted tools that replace opencode's built-in tools (read, write, edit, apply_patch).
|
|
3
|
+
*
|
|
4
|
+
* When hoist_builtin_tools is enabled (default), these tools are registered with
|
|
5
|
+
* the SAME names as opencode's built-in tools, effectively overriding them.
|
|
6
|
+
* When disabled, they're registered with aft_ prefix (e.g., aft_read).
|
|
7
|
+
*
|
|
8
|
+
* All file operations go through AFT's Rust binary for better performance,
|
|
9
|
+
* backup tracking, formatting, and inline diagnostics.
|
|
10
|
+
*/
|
|
11
|
+
import type { ToolDefinition } from "@opencode-ai/plugin";
|
|
12
|
+
import type { PluginContext } from "../types.js";
|
|
13
|
+
/**
|
|
14
|
+
* Creates the unified read tool. Registers as "read" when hoisted, "aft_read" when not.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createReadTool(ctx: PluginContext): ToolDefinition;
|
|
17
|
+
/**
|
|
18
|
+
* Returns hoisted tools keyed by opencode's built-in names.
|
|
19
|
+
* Overrides: read, write, edit, apply_patch.
|
|
20
|
+
*/
|
|
21
|
+
export declare function hoistedTools(ctx: PluginContext): Record<string, ToolDefinition>;
|
|
22
|
+
/**
|
|
23
|
+
* Returns the same tools with aft_ prefix (for when hoisting is disabled).
|
|
24
|
+
*/
|
|
25
|
+
export declare function aftPrefixedTools(ctx: PluginContext): Record<string, ToolDefinition>;
|
|
26
|
+
//# sourceMappingURL=hoisted.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hoisted.d.ts","sourceRoot":"","sources":["../../src/tools/hoisted.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAI1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAwFjD;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,cAAc,CAsKjE;AAogBD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAS/E;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CASnF"}
|