@aryaminus/controlkeel-opencode 0.2.45 → 0.2.47
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.
|
@@ -10,7 +10,7 @@ Recommended flow:
|
|
|
10
10
|
3. Run `controlkeel review plan submit --body-file .opencode/review-plan.md --submitted-by opencode --task-id <task_id> --json` (or use `--session-id <session_id>`)
|
|
11
11
|
4. Read the returned `review.id` and `browser_url`
|
|
12
12
|
5. If `browser_url` is available, wait with `controlkeel review plan wait --id <review_id> --timeout 30 --json`
|
|
13
|
-
6. If `browser_url` is missing/unreachable **or** wait times out while still `pending`, do **not** loop on wait; ask for explicit user approval in chat and record it with `controlkeel review plan respond --id <review_id> --decision approved --feedback-notes "User approved in chat; browser unavailable or timed out" --json` (or `ck_review_feedback`)
|
|
13
|
+
6. If `browser_url` is missing/unreachable, the browser does not actually open, **or** wait times out while still `pending`, do **not** loop on wait; ask for explicit user approval in chat and record it with `controlkeel review plan respond --id <review_id> --decision approved --feedback-notes "User approved in chat; browser unavailable or timed out" --json` (or `ck_review_feedback`)
|
|
14
14
|
7. Do not execute until the review is approved
|
|
15
15
|
|
|
16
16
|
Fallback when the `submit_plan` tool is stale in a long-running OpenCode session:
|
|
@@ -343,29 +343,45 @@ export const ControlKeelGovernance: Plugin = async ({ project, client, $, direct
|
|
|
343
343
|
submitPayload?.review?.browser_url ??
|
|
344
344
|
null
|
|
345
345
|
|
|
346
|
+
const buildPlanResult = (overrides = {}) => ({
|
|
347
|
+
reviewId,
|
|
348
|
+
browserUrl: overrides.browserUrl ?? browserUrl,
|
|
349
|
+
status: overrides.status ?? submitPayload?.review?.status ?? "pending",
|
|
350
|
+
feedbackNotes:
|
|
351
|
+
overrides.feedbackNotes ?? submitPayload?.review?.feedback_notes ?? null,
|
|
352
|
+
opened: overrides.opened ?? (openPayload?.opened === true),
|
|
353
|
+
timedOut: overrides.timedOut ?? false,
|
|
354
|
+
waitSkipped: overrides.waitSkipped ?? false,
|
|
355
|
+
manualApprovalRequired: overrides.manualApprovalRequired ?? false,
|
|
356
|
+
reason: overrides.reason ?? null,
|
|
357
|
+
guidance: overrides.guidance ?? null,
|
|
358
|
+
})
|
|
359
|
+
|
|
346
360
|
const openError = typeof openPayload?.open_error === "string" ? openPayload.open_error.trim() : ""
|
|
347
361
|
const openFailure = typeof openPayload?.error === "string" ? openPayload.error.trim() : ""
|
|
362
|
+
const browserNotOpened = openPayload?.opened !== true
|
|
363
|
+
const serverUnavailable = openPayload?.server_serving === false
|
|
348
364
|
|
|
349
365
|
const remoteLocalhostMismatch =
|
|
350
366
|
typeof browserUrl === "string" &&
|
|
351
367
|
browserUrl.includes("localhost") &&
|
|
352
368
|
openPayload?.remote === true
|
|
353
369
|
|
|
354
|
-
if (!browserUrl || openError || openFailure || remoteLocalhostMismatch) {
|
|
355
|
-
return {
|
|
356
|
-
reviewId,
|
|
357
|
-
submitPayload,
|
|
358
|
-
openPayload,
|
|
359
|
-
browserUrl,
|
|
360
|
-
status: submitPayload?.review?.status ?? "pending",
|
|
361
|
-
feedbackNotes: submitPayload?.review?.feedback_notes ?? null,
|
|
362
|
-
timedOut: false,
|
|
370
|
+
if (!browserUrl || serverUnavailable || openError || openFailure || remoteLocalhostMismatch || browserNotOpened) {
|
|
371
|
+
return buildPlanResult({
|
|
363
372
|
waitSkipped: true,
|
|
364
373
|
manualApprovalRequired: true,
|
|
365
|
-
reason:
|
|
374
|
+
reason:
|
|
375
|
+
!browserUrl
|
|
376
|
+
? "browser_url_unavailable"
|
|
377
|
+
: serverUnavailable
|
|
378
|
+
? "review_server_unavailable"
|
|
379
|
+
: browserNotOpened
|
|
380
|
+
? "browser_not_opened"
|
|
381
|
+
: "browser_unreachable",
|
|
366
382
|
guidance:
|
|
367
|
-
"Browser review is unavailable
|
|
368
|
-
}
|
|
383
|
+
"Browser review is unavailable, the CK review server is not reachable, or the browser did not actually open. Ask the user for explicit approval in chat, then record it with `controlkeel review plan respond --id <review_id> --decision approved --feedback-notes \"User approved in chat; browser/review server unavailable\" --json` or `ck_review_feedback`.",
|
|
384
|
+
})
|
|
369
385
|
}
|
|
370
386
|
|
|
371
387
|
const waitEnv = process.env.LOGGER_LEVEL
|
|
@@ -388,10 +404,7 @@ export const ControlKeelGovernance: Plugin = async ({ project, client, $, direct
|
|
|
388
404
|
|
|
389
405
|
if (waitExit !== 0) {
|
|
390
406
|
if (waitTimedOut && waitPending) {
|
|
391
|
-
return {
|
|
392
|
-
reviewId,
|
|
393
|
-
submitPayload,
|
|
394
|
-
waitPayload,
|
|
407
|
+
return buildPlanResult({
|
|
395
408
|
browserUrl: waitPayload?.browser_url ?? submitPayload?.browser_url,
|
|
396
409
|
status: "pending",
|
|
397
410
|
feedbackNotes: waitPayload?.review?.feedback_notes ?? null,
|
|
@@ -401,7 +414,7 @@ export const ControlKeelGovernance: Plugin = async ({ project, client, $, direct
|
|
|
401
414
|
reason: "review_timeout",
|
|
402
415
|
guidance:
|
|
403
416
|
"Plan review is still pending after timeout. Show the `browser_url` to the user if reachable. If browser review is unavailable or the user explicitly approves in chat, record it with `controlkeel review plan respond --id <review_id> --decision approved --feedback-notes \"User approved in chat after timeout/browser issue\" --json` (or `ck_review_feedback`) before proceeding.",
|
|
404
|
-
}
|
|
417
|
+
})
|
|
405
418
|
}
|
|
406
419
|
|
|
407
420
|
throw new Error(
|
|
@@ -409,17 +422,13 @@ export const ControlKeelGovernance: Plugin = async ({ project, client, $, direct
|
|
|
409
422
|
)
|
|
410
423
|
}
|
|
411
424
|
|
|
412
|
-
return {
|
|
413
|
-
reviewId,
|
|
414
|
-
submitPayload,
|
|
415
|
-
openPayload,
|
|
416
|
-
waitPayload,
|
|
425
|
+
return buildPlanResult({
|
|
417
426
|
browserUrl: waitPayload?.browser_url ?? browserUrl,
|
|
418
427
|
status: waitPayload?.review?.status,
|
|
419
428
|
feedbackNotes: waitPayload?.review?.feedback_notes ?? null,
|
|
420
429
|
waitSkipped: false,
|
|
421
430
|
manualApprovalRequired: false,
|
|
422
|
-
}
|
|
431
|
+
})
|
|
423
432
|
} finally {
|
|
424
433
|
// Clean up temp file
|
|
425
434
|
try { await Bun.file(tmpFile).unlink?.() ?? (await $`rm -f ${tmpFile}`.quiet()) } catch {}
|
|
@@ -479,7 +488,7 @@ export const ControlKeelGovernance: Plugin = async ({ project, client, $, direct
|
|
|
479
488
|
args.task_id,
|
|
480
489
|
args.session_id
|
|
481
490
|
)
|
|
482
|
-
return JSON.stringify(result
|
|
491
|
+
return JSON.stringify(result)
|
|
483
492
|
},
|
|
484
493
|
}),
|
|
485
494
|
},
|
package/AGENTS.md
CHANGED
|
@@ -11,8 +11,9 @@ Required workflow:
|
|
|
11
11
|
2. Call `ck_validate` before writing code, config, shell, or deploy content.
|
|
12
12
|
3. Submit plans or approval packets with `ck_review_submit` and check `ck_review_status` before execution.
|
|
13
13
|
4. Record any human-review issue with `ck_finding`.
|
|
14
|
-
5. Check `ck_budget` before expensive model or multi-agent work.
|
|
15
|
-
6.
|
|
14
|
+
5. Check `ck_budget` before expensive model or multi-agent work, and keep `ck_context` compact unless full raw context is needed.
|
|
15
|
+
6. Before AFK or delegated implementation, split large work into human-approved vertical slices with explicit dependencies; prefer durable behavior-first issues, stable deep-module interfaces, and branch-level automated review plus human QA before merge.
|
|
16
|
+
7. Use `ck_route`, `ck_skill_list`, and `ck_skill_load` to delegate or activate specialized CK workflows.
|
|
16
17
|
|
|
17
18
|
Install ControlKeel:
|
|
18
19
|
- Homebrew: `brew tap aryaminus/controlkeel && brew install controlkeel`
|
package/index.js
CHANGED
|
@@ -326,29 +326,45 @@ export const ControlKeelGovernance = async ({ $, directory }) => {
|
|
|
326
326
|
submitPayload?.review?.browser_url ??
|
|
327
327
|
null
|
|
328
328
|
|
|
329
|
+
const buildPlanResult = (overrides = {}) => ({
|
|
330
|
+
reviewId,
|
|
331
|
+
browserUrl: overrides.browserUrl ?? browserUrl,
|
|
332
|
+
status: overrides.status ?? submitPayload?.review?.status ?? "pending",
|
|
333
|
+
feedbackNotes:
|
|
334
|
+
overrides.feedbackNotes ?? submitPayload?.review?.feedback_notes ?? null,
|
|
335
|
+
opened: overrides.opened ?? (openPayload?.opened === true),
|
|
336
|
+
timedOut: overrides.timedOut ?? false,
|
|
337
|
+
waitSkipped: overrides.waitSkipped ?? false,
|
|
338
|
+
manualApprovalRequired: overrides.manualApprovalRequired ?? false,
|
|
339
|
+
reason: overrides.reason ?? null,
|
|
340
|
+
guidance: overrides.guidance ?? null,
|
|
341
|
+
})
|
|
342
|
+
|
|
329
343
|
const openError = typeof openPayload?.open_error === "string" ? openPayload.open_error.trim() : ""
|
|
330
344
|
const openFailure = typeof openPayload?.error === "string" ? openPayload.error.trim() : ""
|
|
345
|
+
const browserNotOpened = openPayload?.opened !== true
|
|
346
|
+
const serverUnavailable = openPayload?.server_serving === false
|
|
331
347
|
|
|
332
348
|
const remoteLocalhostMismatch =
|
|
333
349
|
typeof browserUrl === "string" &&
|
|
334
350
|
browserUrl.includes("localhost") &&
|
|
335
351
|
openPayload?.remote === true
|
|
336
352
|
|
|
337
|
-
if (!browserUrl || openError || openFailure || remoteLocalhostMismatch) {
|
|
338
|
-
return {
|
|
339
|
-
reviewId,
|
|
340
|
-
submitPayload,
|
|
341
|
-
openPayload,
|
|
342
|
-
browserUrl,
|
|
343
|
-
status: submitPayload?.review?.status ?? "pending",
|
|
344
|
-
feedbackNotes: submitPayload?.review?.feedback_notes ?? null,
|
|
345
|
-
timedOut: false,
|
|
353
|
+
if (!browserUrl || serverUnavailable || openError || openFailure || remoteLocalhostMismatch || browserNotOpened) {
|
|
354
|
+
return buildPlanResult({
|
|
346
355
|
waitSkipped: true,
|
|
347
356
|
manualApprovalRequired: true,
|
|
348
|
-
reason:
|
|
357
|
+
reason:
|
|
358
|
+
!browserUrl
|
|
359
|
+
? "browser_url_unavailable"
|
|
360
|
+
: serverUnavailable
|
|
361
|
+
? "review_server_unavailable"
|
|
362
|
+
: browserNotOpened
|
|
363
|
+
? "browser_not_opened"
|
|
364
|
+
: "browser_unreachable",
|
|
349
365
|
guidance:
|
|
350
|
-
"Browser review is unavailable
|
|
351
|
-
}
|
|
366
|
+
"Browser review is unavailable, the CK review server is not reachable, or the browser did not actually open. Ask the user for explicit approval in chat, then record it with `controlkeel review plan respond --id <review_id> --decision approved --feedback-notes \"User approved in chat; browser/review server unavailable\" --json` or `ck_review_feedback`.",
|
|
367
|
+
})
|
|
352
368
|
}
|
|
353
369
|
|
|
354
370
|
const waitEnv = process.env.LOGGER_LEVEL
|
|
@@ -371,10 +387,7 @@ export const ControlKeelGovernance = async ({ $, directory }) => {
|
|
|
371
387
|
|
|
372
388
|
if (waitExit !== 0) {
|
|
373
389
|
if (waitTimedOut && waitPending) {
|
|
374
|
-
return {
|
|
375
|
-
reviewId,
|
|
376
|
-
submitPayload,
|
|
377
|
-
waitPayload,
|
|
390
|
+
return buildPlanResult({
|
|
378
391
|
browserUrl: waitPayload?.browser_url ?? submitPayload?.browser_url,
|
|
379
392
|
status: "pending",
|
|
380
393
|
feedbackNotes: waitPayload?.review?.feedback_notes ?? null,
|
|
@@ -384,7 +397,7 @@ export const ControlKeelGovernance = async ({ $, directory }) => {
|
|
|
384
397
|
reason: "review_timeout",
|
|
385
398
|
guidance:
|
|
386
399
|
"Plan review is still pending after timeout. Show the `browser_url` to the user if reachable. If browser review is unavailable or the user explicitly approves in chat, record it with `controlkeel review plan respond --id <review_id> --decision approved --feedback-notes \"User approved in chat after timeout/browser issue\" --json` (or `ck_review_feedback`) before proceeding.",
|
|
387
|
-
}
|
|
400
|
+
})
|
|
388
401
|
}
|
|
389
402
|
|
|
390
403
|
throw new Error(
|
|
@@ -392,17 +405,13 @@ export const ControlKeelGovernance = async ({ $, directory }) => {
|
|
|
392
405
|
)
|
|
393
406
|
}
|
|
394
407
|
|
|
395
|
-
return {
|
|
396
|
-
reviewId,
|
|
397
|
-
submitPayload,
|
|
398
|
-
openPayload,
|
|
399
|
-
waitPayload,
|
|
408
|
+
return buildPlanResult({
|
|
400
409
|
browserUrl: waitPayload?.browser_url ?? browserUrl,
|
|
401
410
|
status: waitPayload?.review?.status,
|
|
402
411
|
feedbackNotes: waitPayload?.review?.feedback_notes ?? null,
|
|
403
412
|
waitSkipped: false,
|
|
404
413
|
manualApprovalRequired: false,
|
|
405
|
-
}
|
|
414
|
+
})
|
|
406
415
|
} finally {
|
|
407
416
|
// Clean up temp file
|
|
408
417
|
try { await Bun.file(tmpFile).unlink?.() ?? (await $`rm -f ${tmpFile}`.quiet()) } catch {}
|
|
@@ -462,7 +471,7 @@ export const ControlKeelGovernance = async ({ $, directory }) => {
|
|
|
462
471
|
args.task_id,
|
|
463
472
|
args.session_id
|
|
464
473
|
)
|
|
465
|
-
return JSON.stringify(result
|
|
474
|
+
return JSON.stringify(result)
|
|
466
475
|
},
|
|
467
476
|
}),
|
|
468
477
|
},
|
package/package.json
CHANGED