Skip to content

Commit 4954bbb

Browse files
authored
Merge pull request #72 from rohitjavvadi/fix/opencode-json-diagnostics
Improve opencode malformed JSON diagnostics
2 parents 89b9de9 + 44727fc commit 4954bbb

3 files changed

Lines changed: 53 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## 0.3.1 - Unreleased
44

5+
- Improved OpenCode malformed JSON diagnostics with output length, event kinds, and a bounded preview, thanks @rohitjavvadi.
56
- Fixed Express route mapping for aliased Router imports that follow block comment banners, thanks @rohitjavvadi.
67
- Fixed Bun package-manager detection to recognize the text `bun.lock` lockfile, thanks @austinm911.
78

src/provider.test.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { afterEach, describe, expect, it } from "vitest";
22
import { ClawpatchError } from "./errors.js";
33
import { __testing, extractJson, providerByName } from "./provider.js";
4+
import { safeProviderPreview } from "./provider-json.js";
45
import { revalidateOutputSchema, reviewOutputSchema } from "./types.js";
56

67
// eslint-disable-next-line no-underscore-dangle
@@ -55,6 +56,10 @@ function expectMalformed(fn: () => unknown, message: RegExp): void {
5556
throw new Error("expected malformed-output");
5657
}
5758

59+
function escapeRegExp(value: string): string {
60+
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
61+
}
62+
5863
describe("extractJson", () => {
5964
it("parses strict JSON directly", () => {
6065
const input = '{"findings":[],"inspected":{"files":[],"symbols":[],"notes":[]}}';
@@ -484,6 +489,46 @@ describe("extractOpencodeJson", () => {
484489
expectMalformed(() => extractOpencodeJson(stdout), /no extractable text.*step_finish/u);
485490
});
486491

492+
it("treats whitespace-only opencode text as no extractable text", () => {
493+
const stdout = [
494+
JSON.stringify({ type: "text", part: { text: " \n\t " } }),
495+
JSON.stringify({ type: "step_finish", part: { reason: "stop" } }),
496+
].join("\n");
497+
498+
expectMalformed(() => extractOpencodeJson(stdout), /no extractable text.*text, step_finish/u);
499+
});
500+
501+
it("throws malformed-output with a preview when opencode text is unparsable", () => {
502+
const stdout = [
503+
JSON.stringify({
504+
type: "text",
505+
part: { text: '{"findings": [' },
506+
}),
507+
JSON.stringify({ type: "step_finish", part: { reason: "stop" } }),
508+
].join("\n");
509+
510+
expectMalformed(
511+
() => extractOpencodeJson(stdout),
512+
/unparsable JSON.*text chars=14.*observed event kinds: \[text, step_finish\].*output preview: \{"findings": \[/u,
513+
);
514+
});
515+
516+
it("bounds the opencode unparsable text preview", () => {
517+
const text = `{"findings":["${"x".repeat(300)}`;
518+
const stdout = JSON.stringify({
519+
type: "text",
520+
part: { text },
521+
});
522+
const preview = safeProviderPreview(text);
523+
524+
expect(preview.length).toBe(200);
525+
526+
expectMalformed(
527+
() => extractOpencodeJson(stdout),
528+
new RegExp(`output preview: ${escapeRegExp(preview)}\\)`, "u"),
529+
);
530+
});
531+
487532
it("throws provider-failure for opencode error events", () => {
488533
const stdout = JSON.stringify({
489534
type: "error",

src/provider.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,13 @@ export function extractOpencodeJson(stdout: string): unknown {
630630
}
631631
const parsed = extractJson(combined);
632632
if (parsed === null) {
633-
throw new ClawpatchError("opencode provider produced unparsable JSON", 8, "malformed-output");
633+
throw new ClawpatchError(
634+
`opencode provider produced unparsable JSON ` +
635+
`(text chars=${combined.length}, observed event kinds: ` +
636+
`[${[...observedKinds].join(", ")}], output preview: ${safeProviderPreview(combined)})`,
637+
8,
638+
"malformed-output",
639+
);
634640
}
635641
return parsed;
636642
}

0 commit comments

Comments
 (0)