@skastr0/quartz-opencode-plugin 0.1.0 → 0.2.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/README.md +2 -1
- package/dist/server.js +66 -36
- package/package.json +3 -4
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ The bundle is written to `dist/server.js` and exported as `@skastr0/quartz-openc
|
|
|
26
26
|
|
|
27
27
|
## Runtime Behavior
|
|
28
28
|
|
|
29
|
-
The plugin roots Quartz at the OpenCode workspace directory
|
|
29
|
+
The plugin roots one persistent native Quartz analyzer at the OpenCode workspace directory and reuses it across tool calls. It marks the analyzer cache dirty from OpenCode's `file.edited` and `file.watcher.updated` events, with tool-hook fallbacks for `apply_patch`, `edit`, `write`, and `morph-mcp_edit_file`. The analyzer is disposed when OpenCode emits `server.instance.disposed` for that workspace directory; deleting a session does not close it.
|
|
30
30
|
|
|
31
31
|
Use the optional `package` argument when the workspace has multiple discovered TypeScript packages.
|
|
32
32
|
|
|
@@ -58,6 +58,7 @@ Relationships and refactors:
|
|
|
58
58
|
- `type_why_error`
|
|
59
59
|
- `type_explain`
|
|
60
60
|
- `type_transform_search`
|
|
61
|
+
- `type_verify_contract`
|
|
61
62
|
|
|
62
63
|
## Examples
|
|
63
64
|
|
package/dist/server.js
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// src/server.ts
|
|
3
3
|
import { tool } from "@opencode-ai/plugin";
|
|
4
|
-
import {
|
|
5
|
-
import { CoreLayer, TypeAnalyzerService } from "@skastr0/quartz-core";
|
|
6
|
-
var run = (effect) => Effect.runPromise(effect.pipe(Effect.mapError((error) => error instanceof Error ? error : new Error(String(error)))));
|
|
4
|
+
import { createTypeAnalyzer } from "@skastr0/quartz-engine";
|
|
7
5
|
var optionalPackageArg = tool.schema.string().optional().describe("Package/directory to analyze. Omit for root or single-package projects.");
|
|
8
6
|
var limitArg = tool.schema.number().optional().describe("Maximum number of results");
|
|
9
7
|
var json = (value) => JSON.stringify(value, null, 2);
|
|
10
|
-
var FILE_MODIFYING_TOOLS = new Set(["edit", "morph-mcp_edit_file", "write"]);
|
|
8
|
+
var FILE_MODIFYING_TOOLS = new Set(["apply_patch", "edit", "morph-mcp_edit_file", "write"]);
|
|
11
9
|
var packageOption = (packageName) => packageName === undefined ? {} : { packageName };
|
|
12
10
|
var optionalOption = (key, value) => value === undefined ? {} : { [key]: value };
|
|
13
11
|
var createDiscoveryTools = (analyzer) => ({
|
|
@@ -15,7 +13,7 @@ var createDiscoveryTools = (analyzer) => ({
|
|
|
15
13
|
description: "List TypeScript packages discovered from tsconfig.json files.",
|
|
16
14
|
args: {},
|
|
17
15
|
async execute() {
|
|
18
|
-
return json(await
|
|
16
|
+
return json(await analyzer.getPackages());
|
|
19
17
|
}
|
|
20
18
|
}),
|
|
21
19
|
type_symbols: tool({
|
|
@@ -27,12 +25,12 @@ var createDiscoveryTools = (analyzer) => ({
|
|
|
27
25
|
limit: limitArg
|
|
28
26
|
},
|
|
29
27
|
async execute(args) {
|
|
30
|
-
return json(await
|
|
28
|
+
return json(await analyzer.listSymbols({
|
|
31
29
|
...packageOption(args.package),
|
|
32
30
|
...optionalOption("pattern", args.pattern),
|
|
33
31
|
...optionalOption("kind", args.kind),
|
|
34
32
|
...optionalOption("limit", args.limit)
|
|
35
|
-
}))
|
|
33
|
+
}));
|
|
36
34
|
}
|
|
37
35
|
}),
|
|
38
36
|
type_info: tool({
|
|
@@ -42,7 +40,7 @@ var createDiscoveryTools = (analyzer) => ({
|
|
|
42
40
|
package: optionalPackageArg
|
|
43
41
|
},
|
|
44
42
|
async execute(args) {
|
|
45
|
-
return json(await
|
|
43
|
+
return json(await analyzer.getTypeInfo(args.symbol, args.package));
|
|
46
44
|
}
|
|
47
45
|
}),
|
|
48
46
|
type_expand: tool({
|
|
@@ -52,7 +50,7 @@ var createDiscoveryTools = (analyzer) => ({
|
|
|
52
50
|
package: optionalPackageArg
|
|
53
51
|
},
|
|
54
52
|
async execute(args) {
|
|
55
|
-
return json(await
|
|
53
|
+
return json(await analyzer.expandType(args.symbol, args.package));
|
|
56
54
|
}
|
|
57
55
|
}),
|
|
58
56
|
type_related: tool({
|
|
@@ -62,7 +60,7 @@ var createDiscoveryTools = (analyzer) => ({
|
|
|
62
60
|
package: optionalPackageArg
|
|
63
61
|
},
|
|
64
62
|
async execute(args) {
|
|
65
|
-
return json(await
|
|
63
|
+
return json(await analyzer.findRelated(args.symbol, args.package));
|
|
66
64
|
}
|
|
67
65
|
}),
|
|
68
66
|
type_search: tool({
|
|
@@ -76,14 +74,14 @@ var createDiscoveryTools = (analyzer) => ({
|
|
|
76
74
|
limit: limitArg
|
|
77
75
|
},
|
|
78
76
|
async execute(args) {
|
|
79
|
-
return json(await
|
|
77
|
+
return json(await analyzer.searchTypes({
|
|
80
78
|
query: args.query,
|
|
81
79
|
...optionalOption("pattern", args.pattern),
|
|
82
80
|
...optionalOption("hasProperty", args.hasProperty),
|
|
83
81
|
...optionalOption("extends", args.extends),
|
|
84
82
|
...packageOption(args.package),
|
|
85
83
|
...optionalOption("limit", args.limit)
|
|
86
|
-
}))
|
|
84
|
+
}));
|
|
87
85
|
}
|
|
88
86
|
})
|
|
89
87
|
});
|
|
@@ -95,7 +93,7 @@ var createAnalysisTools = (analyzer) => ({
|
|
|
95
93
|
package: optionalPackageArg
|
|
96
94
|
},
|
|
97
95
|
async execute(args) {
|
|
98
|
-
return json(await
|
|
96
|
+
return json(await analyzer.evalType(args.expression, args.package));
|
|
99
97
|
}
|
|
100
98
|
}),
|
|
101
99
|
type_diagnostics: tool({
|
|
@@ -105,10 +103,10 @@ var createAnalysisTools = (analyzer) => ({
|
|
|
105
103
|
explain: tool.schema.boolean().optional().describe("Include explanations for diagnostics")
|
|
106
104
|
},
|
|
107
105
|
async execute(args) {
|
|
108
|
-
return json(await
|
|
106
|
+
return json(await analyzer.getDiagnostics({
|
|
109
107
|
...packageOption(args.package),
|
|
110
108
|
...optionalOption("explain", args.explain)
|
|
111
|
-
}))
|
|
109
|
+
}));
|
|
112
110
|
}
|
|
113
111
|
}),
|
|
114
112
|
type_check_snippet: tool({
|
|
@@ -118,7 +116,7 @@ var createAnalysisTools = (analyzer) => ({
|
|
|
118
116
|
package: optionalPackageArg
|
|
119
117
|
},
|
|
120
118
|
async execute(args) {
|
|
121
|
-
return json(await
|
|
119
|
+
return json(await analyzer.checkSnippet(args.code, args.package));
|
|
122
120
|
}
|
|
123
121
|
}),
|
|
124
122
|
type_at_position: tool({
|
|
@@ -130,7 +128,7 @@ var createAnalysisTools = (analyzer) => ({
|
|
|
130
128
|
package: optionalPackageArg
|
|
131
129
|
},
|
|
132
130
|
async execute(args) {
|
|
133
|
-
return json(await
|
|
131
|
+
return json(await analyzer.getTypeAtPosition(args.file, args.line, args.column, args.package));
|
|
134
132
|
}
|
|
135
133
|
}),
|
|
136
134
|
type_file: tool({
|
|
@@ -142,11 +140,11 @@ var createAnalysisTools = (analyzer) => ({
|
|
|
142
140
|
package: optionalPackageArg
|
|
143
141
|
},
|
|
144
142
|
async execute(args) {
|
|
145
|
-
return json(await
|
|
143
|
+
return json(await analyzer.getFileDeclarations(args.file, {
|
|
146
144
|
...packageOption(args.package),
|
|
147
145
|
...optionalOption("symbol", args.symbol),
|
|
148
146
|
...optionalOption("includePrivate", args.includePrivate)
|
|
149
|
-
}))
|
|
147
|
+
}));
|
|
150
148
|
}
|
|
151
149
|
}),
|
|
152
150
|
type_refresh: tool({
|
|
@@ -155,7 +153,7 @@ var createAnalysisTools = (analyzer) => ({
|
|
|
155
153
|
package: optionalPackageArg
|
|
156
154
|
},
|
|
157
155
|
async execute(args) {
|
|
158
|
-
return await
|
|
156
|
+
return await analyzer.refresh(args.package);
|
|
159
157
|
}
|
|
160
158
|
})
|
|
161
159
|
});
|
|
@@ -176,7 +174,7 @@ var createCompatibilityTools = (analyzer) => ({
|
|
|
176
174
|
package: optionalPackageArg
|
|
177
175
|
},
|
|
178
176
|
async execute(args) {
|
|
179
|
-
return json(await
|
|
177
|
+
return json(await analyzer.checkCompatibility(args.from, args.to, args.package));
|
|
180
178
|
}
|
|
181
179
|
})
|
|
182
180
|
});
|
|
@@ -190,11 +188,11 @@ var createGraphTools = (analyzer) => ({
|
|
|
190
188
|
package: optionalPackageArg
|
|
191
189
|
},
|
|
192
190
|
async execute(args) {
|
|
193
|
-
return json(await
|
|
191
|
+
return json(await analyzer.generateGraph(args.symbol, {
|
|
194
192
|
...packageOption(args.package),
|
|
195
193
|
...optionalOption("depth", args.depth),
|
|
196
194
|
...optionalOption("format", args.format)
|
|
197
|
-
}))
|
|
195
|
+
}));
|
|
198
196
|
}
|
|
199
197
|
})
|
|
200
198
|
});
|
|
@@ -207,12 +205,12 @@ var createRefactorTools = (analyzer) => ({
|
|
|
207
205
|
package: optionalPackageArg
|
|
208
206
|
},
|
|
209
207
|
async execute(args) {
|
|
210
|
-
return json(await
|
|
208
|
+
return json(await analyzer.previewRefactor({
|
|
211
209
|
action: "rename",
|
|
212
210
|
symbol: args.symbol,
|
|
213
211
|
to: args.to,
|
|
214
212
|
...packageOption(args.package)
|
|
215
|
-
}))
|
|
213
|
+
}));
|
|
216
214
|
}
|
|
217
215
|
})
|
|
218
216
|
});
|
|
@@ -227,13 +225,13 @@ var createExplanationTools = (analyzer) => ({
|
|
|
227
225
|
package: optionalPackageArg
|
|
228
226
|
},
|
|
229
227
|
async execute(args) {
|
|
230
|
-
return json(await
|
|
228
|
+
return json(await analyzer.explainError({
|
|
231
229
|
...packageOption(args.package),
|
|
232
230
|
...optionalOption("code", args.code),
|
|
233
231
|
...optionalOption("message", args.message),
|
|
234
232
|
...optionalOption("file", args.file),
|
|
235
233
|
...optionalOption("line", args.line)
|
|
236
|
-
}))
|
|
234
|
+
}));
|
|
237
235
|
}
|
|
238
236
|
}),
|
|
239
237
|
type_explain: tool({
|
|
@@ -243,7 +241,7 @@ var createExplanationTools = (analyzer) => ({
|
|
|
243
241
|
package: optionalPackageArg
|
|
244
242
|
},
|
|
245
243
|
async execute(args) {
|
|
246
|
-
return json(await
|
|
244
|
+
return json(await analyzer.explainType(args.expression, args.package));
|
|
247
245
|
}
|
|
248
246
|
})
|
|
249
247
|
});
|
|
@@ -266,7 +264,7 @@ var createTransformSearchTools = (analyzer) => ({
|
|
|
266
264
|
package: optionalPackageArg
|
|
267
265
|
},
|
|
268
266
|
async execute(args) {
|
|
269
|
-
return await
|
|
267
|
+
return json(await analyzer.transformSearch({
|
|
270
268
|
...packageOption(args.package),
|
|
271
269
|
...optionalOption("from", args.from),
|
|
272
270
|
...optionalOption("to", args.to),
|
|
@@ -298,7 +296,7 @@ var createVerifyContractTools = (analyzer) => ({
|
|
|
298
296
|
package: optionalPackageArg
|
|
299
297
|
},
|
|
300
298
|
async execute(args) {
|
|
301
|
-
return json(await
|
|
299
|
+
return json(await analyzer.verifyContract({
|
|
302
300
|
...packageOption(args.package),
|
|
303
301
|
...optionalOption("from", args.from),
|
|
304
302
|
...optionalOption("to", args.to),
|
|
@@ -307,7 +305,7 @@ var createVerifyContractTools = (analyzer) => ({
|
|
|
307
305
|
...optionalOption("includeDiagnostics", args.includeDiagnostics),
|
|
308
306
|
...optionalOption("includeTransformEvidence", args.includeTransformEvidence),
|
|
309
307
|
...optionalOption("transformLimit", args.transformLimit)
|
|
310
|
-
}))
|
|
308
|
+
}));
|
|
311
309
|
}
|
|
312
310
|
})
|
|
313
311
|
});
|
|
@@ -317,9 +315,34 @@ var createToolDefinitions = (analyzer) => ({
|
|
|
317
315
|
...createRelationshipTools(analyzer)
|
|
318
316
|
});
|
|
319
317
|
var QuartzPlugin = async (ctx) => {
|
|
320
|
-
const
|
|
321
|
-
const analyzer = runtime.runSync(TypeAnalyzerService);
|
|
318
|
+
const analyzer = await createTypeAnalyzer(ctx.directory, { collectTiming: true });
|
|
322
319
|
const client = ctx.client;
|
|
320
|
+
let disposePromise = null;
|
|
321
|
+
const disposeAnalyzer = (reason) => {
|
|
322
|
+
disposePromise ??= (async () => {
|
|
323
|
+
try {
|
|
324
|
+
await analyzer.dispose();
|
|
325
|
+
await client.app?.log?.({
|
|
326
|
+
body: {
|
|
327
|
+
service: "quartz",
|
|
328
|
+
level: "debug",
|
|
329
|
+
message: "quartz analyzer disposed",
|
|
330
|
+
extra: { reason }
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
} catch (cause) {
|
|
334
|
+
await client.app?.log?.({
|
|
335
|
+
body: {
|
|
336
|
+
service: "quartz",
|
|
337
|
+
level: "warn",
|
|
338
|
+
message: "quartz analyzer dispose failed",
|
|
339
|
+
extra: { reason, cause: cause instanceof Error ? cause.message : String(cause) }
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
})();
|
|
344
|
+
return disposePromise;
|
|
345
|
+
};
|
|
323
346
|
return {
|
|
324
347
|
event: async ({ event }) => {
|
|
325
348
|
if (event.type === "session.idle") {
|
|
@@ -328,17 +351,24 @@ var QuartzPlugin = async (ctx) => {
|
|
|
328
351
|
service: "quartz",
|
|
329
352
|
level: "debug",
|
|
330
353
|
message: "session idle observed by quartz plugin",
|
|
331
|
-
extra: { sessionID: event.properties
|
|
354
|
+
extra: { sessionID: event.properties?.sessionID }
|
|
332
355
|
}
|
|
333
356
|
});
|
|
334
357
|
}
|
|
358
|
+
if (event.type === "file.edited" || event.type === "file.watcher.updated") {
|
|
359
|
+
await analyzer.markDirty();
|
|
360
|
+
}
|
|
361
|
+
if (event.type === "server.instance.disposed" && event.properties.directory === ctx.directory) {
|
|
362
|
+
await disposeAnalyzer(event.type);
|
|
363
|
+
}
|
|
335
364
|
},
|
|
336
365
|
"tool.execute.after": async (input) => {
|
|
337
366
|
if (FILE_MODIFYING_TOOLS.has(input.tool)) {
|
|
338
|
-
await
|
|
367
|
+
await analyzer.markDirty();
|
|
339
368
|
}
|
|
340
369
|
},
|
|
341
|
-
tool: createToolDefinitions(analyzer)
|
|
370
|
+
tool: createToolDefinitions(analyzer),
|
|
371
|
+
dispose: () => disposeAnalyzer("explicit")
|
|
342
372
|
};
|
|
343
373
|
};
|
|
344
374
|
var server_default = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skastr0/quartz-opencode-plugin",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/server.js",
|
|
6
6
|
"types": "./dist/server.d.ts",
|
|
@@ -17,12 +17,11 @@
|
|
|
17
17
|
"README.md"
|
|
18
18
|
],
|
|
19
19
|
"scripts": {
|
|
20
|
-
"build": "
|
|
20
|
+
"build": "bun ../../scripts/clean-build-output.ts opencode-plugin && bun build ./src/server.ts --outdir ./dist --target bun --format esm --external @opencode-ai/plugin --external @skastr0/quartz-engine && tsc -p tsconfig.build.json",
|
|
21
21
|
"prepack": "bun run build"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@skastr0/quartz-
|
|
25
|
-
"effect": "^3.21.2"
|
|
24
|
+
"@skastr0/quartz-engine": "0.2.1"
|
|
26
25
|
},
|
|
27
26
|
"peerDependencies": {
|
|
28
27
|
"@opencode-ai/plugin": ">=1.4.0"
|