@aryaminus/controlkeel-opencode 0.2.20 → 0.2.21

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.
@@ -11,11 +11,41 @@ import { tool } from "@opencode-ai/plugin"
11
11
  * - routes plan submission and wait decisions through the CK CLI
12
12
  */
13
13
  export const ControlKeelGovernance: Plugin = async ({ project, client, $, directory }) => {
14
+ const extractJsonCandidate = (output: string) => {
15
+ const trimmed = output.trim()
16
+ if (!trimmed) {
17
+ return null
18
+ }
19
+
20
+ const lines = trimmed
21
+ .split(/\r?\n/)
22
+ .map((line) => line.trim())
23
+ .filter((line) => line.length > 0)
24
+
25
+ const jsonLine = [...lines].reverse().find((line) => line.startsWith("{") || line.startsWith("["))
26
+ return jsonLine ?? trimmed
27
+ }
28
+
14
29
  const parseJson = (output: string) => {
30
+ const trimmed = output.trim()
31
+ if (!trimmed) {
32
+ throw new Error("ControlKeel returned empty output")
33
+ }
34
+
15
35
  try {
16
- return JSON.parse(output)
36
+ return JSON.parse(trimmed)
17
37
  } catch (_error) {
18
- throw new Error(`ControlKeel returned invalid JSON: ${output}`)
38
+ const candidate = extractJsonCandidate(trimmed)
39
+
40
+ if (!candidate || candidate === trimmed) {
41
+ throw new Error(`ControlKeel returned invalid JSON: ${output}`)
42
+ }
43
+
44
+ try {
45
+ return JSON.parse(candidate)
46
+ } catch (_fallbackError) {
47
+ throw new Error(`ControlKeel returned invalid JSON: ${output}`)
48
+ }
19
49
  }
20
50
  }
21
51
 
@@ -156,9 +186,16 @@ export const ControlKeelGovernance: Plugin = async ({ project, client, $, direct
156
186
 
157
187
  const submitProc = Bun.spawn(submitArgs, { stdout: "pipe", stderr: "pipe" })
158
188
  const submitOut = await new Response(submitProc.stdout).text()
159
- await submitProc.exited
189
+ const submitErr = await new Response(submitProc.stderr).text()
190
+ const submitExit = await submitProc.exited
191
+
192
+ if (submitExit !== 0) {
193
+ throw new Error(
194
+ `controlkeel review plan submit failed with exit code ${submitExit}${submitErr.trim() ? `: ${submitErr.trim()}` : ""}`
195
+ )
196
+ }
160
197
 
161
- const submitPayload = parseJson(submitOut)
198
+ const submitPayload = parseJson(submitOut || submitErr)
162
199
 
163
200
  if (typeof submitPayload?.error === "string" && submitPayload.error.includes("session_id")) {
164
201
  throw new Error(
@@ -173,9 +210,16 @@ export const ControlKeelGovernance: Plugin = async ({ project, client, $, direct
173
210
 
174
211
  const waitProc = Bun.spawn(["controlkeel", "review", "plan", "wait", "--id", String(reviewId), "--timeout", String(waitTimeoutSecondsSafe), "--json"], { stdout: "pipe", stderr: "pipe" })
175
212
  const waitOut = await new Response(waitProc.stdout).text()
176
- await waitProc.exited
213
+ const waitErr = await new Response(waitProc.stderr).text()
214
+ const waitExit = await waitProc.exited
215
+
216
+ if (waitExit !== 0) {
217
+ throw new Error(
218
+ `controlkeel review plan wait failed with exit code ${waitExit}${waitErr.trim() ? `: ${waitErr.trim()}` : ""}`
219
+ )
220
+ }
177
221
 
178
- const waitPayload = parseJson(waitOut)
222
+ const waitPayload = parseJson(waitOut || waitErr)
179
223
  return {
180
224
  reviewId,
181
225
  submitPayload,
package/index.js CHANGED
@@ -7,11 +7,41 @@ import { tool } from "@opencode-ai/plugin"
7
7
  * but ships as plain JavaScript for npm-based installs.
8
8
  */
9
9
  export const ControlKeelGovernance = async ({ $, directory }) => {
10
+ const extractJsonCandidate = (output) => {
11
+ const trimmed = output.trim()
12
+ if (!trimmed) {
13
+ return null
14
+ }
15
+
16
+ const lines = trimmed
17
+ .split(/\r?\n/)
18
+ .map((line) => line.trim())
19
+ .filter((line) => line.length > 0)
20
+
21
+ const jsonLine = [...lines].reverse().find((line) => line.startsWith("{") || line.startsWith("["))
22
+ return jsonLine ?? trimmed
23
+ }
24
+
10
25
  const parseJson = (output) => {
26
+ const trimmed = output.trim()
27
+ if (!trimmed) {
28
+ throw new Error("ControlKeel returned empty output")
29
+ }
30
+
11
31
  try {
12
- return JSON.parse(output)
32
+ return JSON.parse(trimmed)
13
33
  } catch (_error) {
14
- throw new Error(`ControlKeel returned invalid JSON: ${output}`)
34
+ const candidate = extractJsonCandidate(trimmed)
35
+
36
+ if (!candidate || candidate === trimmed) {
37
+ throw new Error(`ControlKeel returned invalid JSON: ${output}`)
38
+ }
39
+
40
+ try {
41
+ return JSON.parse(candidate)
42
+ } catch (_fallbackError) {
43
+ throw new Error(`ControlKeel returned invalid JSON: ${output}`)
44
+ }
15
45
  }
16
46
  }
17
47
 
@@ -144,9 +174,16 @@ export const ControlKeelGovernance = async ({ $, directory }) => {
144
174
 
145
175
  const submitProc = Bun.spawn(submitArgs, { stdout: "pipe", stderr: "pipe" })
146
176
  const submitOut = await new Response(submitProc.stdout).text()
147
- await submitProc.exited
177
+ const submitErr = await new Response(submitProc.stderr).text()
178
+ const submitExit = await submitProc.exited
179
+
180
+ if (submitExit !== 0) {
181
+ throw new Error(
182
+ `controlkeel review plan submit failed with exit code ${submitExit}${submitErr.trim() ? `: ${submitErr.trim()}` : ""}`
183
+ )
184
+ }
148
185
 
149
- const submitPayload = parseJson(submitOut)
186
+ const submitPayload = parseJson(submitOut || submitErr)
150
187
 
151
188
  if (typeof submitPayload?.error === "string" && submitPayload.error.includes("session_id")) {
152
189
  throw new Error(
@@ -161,9 +198,16 @@ export const ControlKeelGovernance = async ({ $, directory }) => {
161
198
 
162
199
  const waitProc = Bun.spawn(["controlkeel", "review", "plan", "wait", "--id", String(reviewId), "--timeout", String(waitTimeoutSecondsSafe), "--json"], { stdout: "pipe", stderr: "pipe" })
163
200
  const waitOut = await new Response(waitProc.stdout).text()
164
- await waitProc.exited
201
+ const waitErr = await new Response(waitProc.stderr).text()
202
+ const waitExit = await waitProc.exited
203
+
204
+ if (waitExit !== 0) {
205
+ throw new Error(
206
+ `controlkeel review plan wait failed with exit code ${waitExit}${waitErr.trim() ? `: ${waitErr.trim()}` : ""}`
207
+ )
208
+ }
165
209
 
166
- const waitPayload = parseJson(waitOut)
210
+ const waitPayload = parseJson(waitOut || waitErr)
167
211
  return {
168
212
  reviewId,
169
213
  submitPayload,
package/package.json CHANGED
@@ -35,5 +35,5 @@
35
35
  "url": "git+https://github.com/aryaminus/controlkeel.git"
36
36
  },
37
37
  "type": "module",
38
- "version": "0.2.20"
38
+ "version": "0.2.21"
39
39
  }