@aryaminus/controlkeel-opencode 0.2.25 → 0.2.26
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/.opencode/plugins/controlkeel-governance.ts +61 -12
- package/index.js +56 -12
- package/package.json +1 -1
|
@@ -180,7 +180,18 @@ export const ControlKeelGovernance: Plugin = async ({ project, client, $, direct
|
|
|
180
180
|
}
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
-
const resolveReviewScope = async (
|
|
183
|
+
const resolveReviewScope = async (
|
|
184
|
+
explicitTaskId?: string | number | null,
|
|
185
|
+
explicitSessionId?: string | number | null
|
|
186
|
+
) => {
|
|
187
|
+
if (explicitTaskId || explicitSessionId) {
|
|
188
|
+
return {
|
|
189
|
+
taskId: explicitTaskId != null ? String(explicitTaskId) : null,
|
|
190
|
+
sessionId: explicitSessionId != null ? String(explicitSessionId) : null,
|
|
191
|
+
source: "explicit",
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
184
195
|
const envTaskId = process.env.CONTROLKEEL_TASK_ID
|
|
185
196
|
const envSessionId = process.env.CONTROLKEEL_SESSION_ID
|
|
186
197
|
|
|
@@ -215,26 +226,44 @@ export const ControlKeelGovernance: Plugin = async ({ project, client, $, direct
|
|
|
215
226
|
const contextTaskId = contextPayload?.current_task?.id
|
|
216
227
|
const contextSessionId = contextPayload?.session_id
|
|
217
228
|
|
|
218
|
-
if (
|
|
219
|
-
|
|
229
|
+
if (contextTaskId || contextSessionId) {
|
|
230
|
+
return {
|
|
231
|
+
taskId: contextTaskId != null ? String(contextTaskId) : null,
|
|
232
|
+
sessionId: contextSessionId != null ? String(contextSessionId) : null,
|
|
233
|
+
source: "context",
|
|
234
|
+
}
|
|
220
235
|
}
|
|
221
236
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
237
|
+
try {
|
|
238
|
+
const bindingPayload = parseJson(await Bun.file(`${directory}/controlkeel/project.json`).text())
|
|
239
|
+
const bindingSessionId = bindingPayload?.session_id
|
|
240
|
+
|
|
241
|
+
if (bindingSessionId) {
|
|
242
|
+
return {
|
|
243
|
+
taskId: null,
|
|
244
|
+
sessionId: String(bindingSessionId),
|
|
245
|
+
source: "binding",
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
} catch (_error) {
|
|
226
249
|
}
|
|
250
|
+
|
|
251
|
+
throw new Error(
|
|
252
|
+
"ControlKeel could not infer review scope. Set CONTROLKEEL_TASK_ID or CONTROLKEEL_SESSION_ID, or pass task_id/session_id to submit_plan."
|
|
253
|
+
)
|
|
227
254
|
}
|
|
228
255
|
|
|
229
256
|
const submitPlan = async (
|
|
230
257
|
body: string,
|
|
231
258
|
submittedBy: string,
|
|
232
259
|
title?: string,
|
|
233
|
-
waitTimeoutSeconds?: number
|
|
260
|
+
waitTimeoutSeconds?: number,
|
|
261
|
+
taskId?: string | number | null,
|
|
262
|
+
sessionId?: string | number | null
|
|
234
263
|
) => {
|
|
235
264
|
await ensurePlanSubmitSupport()
|
|
236
265
|
|
|
237
|
-
const reviewScope = await resolveReviewScope()
|
|
266
|
+
const reviewScope = await resolveReviewScope(taskId, sessionId)
|
|
238
267
|
const waitTimeout = Number(waitTimeoutSeconds ?? process.env.CONTROLKEEL_REVIEW_WAIT_TIMEOUT ?? 30)
|
|
239
268
|
const waitTimeoutSecondsSafe = Number.isFinite(waitTimeout) && waitTimeout > 0 ? waitTimeout : 30
|
|
240
269
|
|
|
@@ -292,19 +321,35 @@ export const ControlKeelGovernance: Plugin = async ({ project, client, $, direct
|
|
|
292
321
|
const waitOut = await new Response(waitProc.stdout).text()
|
|
293
322
|
const waitErr = await new Response(waitProc.stderr).text()
|
|
294
323
|
const waitExit = await waitProc.exited
|
|
324
|
+
const waitPayload = parseJson([waitOut, waitErr].filter(Boolean).join("\n"))
|
|
325
|
+
const waitMessage = typeof waitPayload?.message === "string" ? waitPayload.message.toLowerCase() : ""
|
|
326
|
+
const waitError = typeof waitPayload?.error === "string" ? waitPayload.error.toLowerCase() : ""
|
|
327
|
+
const waitTimedOut = waitMessage.includes("timeout") || waitError.includes("timed out")
|
|
328
|
+
const waitPending = waitPayload?.review?.status === "pending"
|
|
295
329
|
|
|
296
330
|
if (waitExit !== 0) {
|
|
331
|
+
if (waitTimedOut && waitPending) {
|
|
332
|
+
return {
|
|
333
|
+
reviewId,
|
|
334
|
+
submitPayload,
|
|
335
|
+
waitPayload,
|
|
336
|
+
browserUrl: waitPayload?.browser_url ?? submitPayload?.browser_url,
|
|
337
|
+
status: "pending",
|
|
338
|
+
feedbackNotes: waitPayload?.review?.feedback_notes ?? null,
|
|
339
|
+
timedOut: true,
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
297
343
|
throw new Error(
|
|
298
344
|
`controlkeel review plan wait failed with exit code ${waitExit}${waitErr.trim() ? `: ${waitErr.trim()}` : ""}`
|
|
299
345
|
)
|
|
300
346
|
}
|
|
301
347
|
|
|
302
|
-
const waitPayload = parseJson([waitOut, waitErr].filter(Boolean).join("\n"))
|
|
303
348
|
return {
|
|
304
349
|
reviewId,
|
|
305
350
|
submitPayload,
|
|
306
351
|
waitPayload,
|
|
307
|
-
browserUrl: submitPayload?.browser_url,
|
|
352
|
+
browserUrl: waitPayload?.browser_url ?? submitPayload?.browser_url,
|
|
308
353
|
status: waitPayload?.review?.status,
|
|
309
354
|
feedbackNotes: waitPayload?.review?.feedback_notes ?? null,
|
|
310
355
|
}
|
|
@@ -355,13 +400,17 @@ export const ControlKeelGovernance: Plugin = async ({ project, client, $, direct
|
|
|
355
400
|
plan: tool.schema.string().describe("Markdown plan body to submit for review."),
|
|
356
401
|
title: tool.schema.string().optional(),
|
|
357
402
|
wait_timeout_seconds: tool.schema.number().int().positive().optional(),
|
|
403
|
+
task_id: tool.schema.number().int().positive().optional(),
|
|
404
|
+
session_id: tool.schema.number().int().positive().optional(),
|
|
358
405
|
},
|
|
359
406
|
async execute(args) {
|
|
360
407
|
const result = await submitPlan(
|
|
361
408
|
args.plan,
|
|
362
409
|
"opencode",
|
|
363
410
|
args.title,
|
|
364
|
-
args.wait_timeout_seconds
|
|
411
|
+
args.wait_timeout_seconds,
|
|
412
|
+
args.task_id,
|
|
413
|
+
args.session_id
|
|
365
414
|
)
|
|
366
415
|
return JSON.stringify(result, null, 2)
|
|
367
416
|
},
|
package/index.js
CHANGED
|
@@ -173,7 +173,15 @@ export const ControlKeelGovernance = async ({ $, directory }) => {
|
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
-
const resolveReviewScope = async () => {
|
|
176
|
+
const resolveReviewScope = async (explicitTaskId, explicitSessionId) => {
|
|
177
|
+
if (explicitTaskId || explicitSessionId) {
|
|
178
|
+
return {
|
|
179
|
+
taskId: explicitTaskId != null ? String(explicitTaskId) : null,
|
|
180
|
+
sessionId: explicitSessionId != null ? String(explicitSessionId) : null,
|
|
181
|
+
source: "explicit",
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
177
185
|
const envTaskId = process.env.CONTROLKEEL_TASK_ID
|
|
178
186
|
const envSessionId = process.env.CONTROLKEEL_SESSION_ID
|
|
179
187
|
|
|
@@ -208,21 +216,37 @@ export const ControlKeelGovernance = async ({ $, directory }) => {
|
|
|
208
216
|
const contextTaskId = contextPayload?.current_task?.id
|
|
209
217
|
const contextSessionId = contextPayload?.session_id
|
|
210
218
|
|
|
211
|
-
if (
|
|
212
|
-
|
|
219
|
+
if (contextTaskId || contextSessionId) {
|
|
220
|
+
return {
|
|
221
|
+
taskId: contextTaskId != null ? String(contextTaskId) : null,
|
|
222
|
+
sessionId: contextSessionId != null ? String(contextSessionId) : null,
|
|
223
|
+
source: "context",
|
|
224
|
+
}
|
|
213
225
|
}
|
|
214
226
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
227
|
+
try {
|
|
228
|
+
const bindingPayload = parseJson(await Bun.file(`${directory}/controlkeel/project.json`).text())
|
|
229
|
+
const bindingSessionId = bindingPayload?.session_id
|
|
230
|
+
|
|
231
|
+
if (bindingSessionId) {
|
|
232
|
+
return {
|
|
233
|
+
taskId: null,
|
|
234
|
+
sessionId: String(bindingSessionId),
|
|
235
|
+
source: "binding",
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
} catch (_error) {
|
|
219
239
|
}
|
|
240
|
+
|
|
241
|
+
throw new Error(
|
|
242
|
+
"ControlKeel could not infer review scope. Set CONTROLKEEL_TASK_ID or CONTROLKEEL_SESSION_ID, or pass task_id/session_id to submit_plan."
|
|
243
|
+
)
|
|
220
244
|
}
|
|
221
245
|
|
|
222
|
-
const submitPlan = async (body, submittedBy, title, waitTimeoutSeconds) => {
|
|
246
|
+
const submitPlan = async (body, submittedBy, title, waitTimeoutSeconds, taskId, sessionId) => {
|
|
223
247
|
await ensurePlanSubmitSupport()
|
|
224
248
|
|
|
225
|
-
const reviewScope = await resolveReviewScope()
|
|
249
|
+
const reviewScope = await resolveReviewScope(taskId, sessionId)
|
|
226
250
|
const waitTimeout = Number(waitTimeoutSeconds ?? process.env.CONTROLKEEL_REVIEW_WAIT_TIMEOUT ?? 30)
|
|
227
251
|
const waitTimeoutSecondsSafe = Number.isFinite(waitTimeout) && waitTimeout > 0 ? waitTimeout : 30
|
|
228
252
|
|
|
@@ -280,19 +304,35 @@ export const ControlKeelGovernance = async ({ $, directory }) => {
|
|
|
280
304
|
const waitOut = await new Response(waitProc.stdout).text()
|
|
281
305
|
const waitErr = await new Response(waitProc.stderr).text()
|
|
282
306
|
const waitExit = await waitProc.exited
|
|
307
|
+
const waitPayload = parseJson([waitOut, waitErr].filter(Boolean).join("\n"))
|
|
308
|
+
const waitMessage = typeof waitPayload?.message === "string" ? waitPayload.message.toLowerCase() : ""
|
|
309
|
+
const waitError = typeof waitPayload?.error === "string" ? waitPayload.error.toLowerCase() : ""
|
|
310
|
+
const waitTimedOut = waitMessage.includes("timeout") || waitError.includes("timed out")
|
|
311
|
+
const waitPending = waitPayload?.review?.status === "pending"
|
|
283
312
|
|
|
284
313
|
if (waitExit !== 0) {
|
|
314
|
+
if (waitTimedOut && waitPending) {
|
|
315
|
+
return {
|
|
316
|
+
reviewId,
|
|
317
|
+
submitPayload,
|
|
318
|
+
waitPayload,
|
|
319
|
+
browserUrl: waitPayload?.browser_url ?? submitPayload?.browser_url,
|
|
320
|
+
status: "pending",
|
|
321
|
+
feedbackNotes: waitPayload?.review?.feedback_notes ?? null,
|
|
322
|
+
timedOut: true,
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
285
326
|
throw new Error(
|
|
286
327
|
`controlkeel review plan wait failed with exit code ${waitExit}${waitErr.trim() ? `: ${waitErr.trim()}` : ""}`
|
|
287
328
|
)
|
|
288
329
|
}
|
|
289
330
|
|
|
290
|
-
const waitPayload = parseJson([waitOut, waitErr].filter(Boolean).join("\n"))
|
|
291
331
|
return {
|
|
292
332
|
reviewId,
|
|
293
333
|
submitPayload,
|
|
294
334
|
waitPayload,
|
|
295
|
-
browserUrl: submitPayload?.browser_url,
|
|
335
|
+
browserUrl: waitPayload?.browser_url ?? submitPayload?.browser_url,
|
|
296
336
|
status: waitPayload?.review?.status,
|
|
297
337
|
feedbackNotes: waitPayload?.review?.feedback_notes ?? null,
|
|
298
338
|
}
|
|
@@ -343,13 +383,17 @@ export const ControlKeelGovernance = async ({ $, directory }) => {
|
|
|
343
383
|
plan: tool.schema.string().describe("Markdown plan body to submit for review."),
|
|
344
384
|
title: tool.schema.string().optional(),
|
|
345
385
|
wait_timeout_seconds: tool.schema.number().int().positive().optional(),
|
|
386
|
+
task_id: tool.schema.number().int().positive().optional(),
|
|
387
|
+
session_id: tool.schema.number().int().positive().optional(),
|
|
346
388
|
},
|
|
347
389
|
async execute(args) {
|
|
348
390
|
const result = await submitPlan(
|
|
349
391
|
args.plan,
|
|
350
392
|
"opencode",
|
|
351
393
|
args.title,
|
|
352
|
-
args.wait_timeout_seconds
|
|
394
|
+
args.wait_timeout_seconds,
|
|
395
|
+
args.task_id,
|
|
396
|
+
args.session_id
|
|
353
397
|
)
|
|
354
398
|
return JSON.stringify(result, null, 2)
|
|
355
399
|
},
|
package/package.json
CHANGED