alchemylab 1.4.13 → 1.4.14
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/dist/cli.js +42 -30
- package/dist/http-server.js +191 -150
- package/dist/index.js +270 -229
- package/dist/mcp-direct.js +291 -250
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -946,7 +946,17 @@ module.exports=require("ws")},5124(module){"use strict"
|
|
|
946
946
|
module.exports=require("winston")},5317(module){"use strict"
|
|
947
947
|
module.exports=require("child_process")},5419(e,exports,t){"use strict"
|
|
948
948
|
function r(e){return(0,o.join)((0,s.homedir)(),n.ALCHEMY_DIR_NAME,e?"opencode-dev":"opencode")}Object.defineProperty(exports,"__esModule",{value:!0}),exports.getGlobalConfigDir=r,exports.getOpencodeSpawnEnv=function(e,t){return{OPENCODE_CLIENT:"alchemy",OPENCODE_DATA_DIR:(0,o.join)(e,n.ALCHEMY_DIR_NAME,"opencode"),OPENCODE_CONFIG_DIR:r(t)}}
|
|
949
|
-
const s=t(857),o=t(6928),n=t(4139)},
|
|
949
|
+
const s=t(857),o=t(6928),n=t(4139)},5454(e,exports){"use strict"
|
|
950
|
+
function t(e,t){let r=0
|
|
951
|
+
for(let t=0;t<e.length;t++)r=(r<<5)-r+e.charCodeAt(t)|0
|
|
952
|
+
return`${t+1}${String.fromCharCode(97+Math.abs(r)%26)}`}function r(e){const r=new Map
|
|
953
|
+
for(let s=0;s<e.length;s++){const o=t(e[s].id,s)
|
|
954
|
+
r.set(o,e[s])}return r}function s(e){if(0===e.length)return"No acceptance criteria defined."
|
|
955
|
+
const t=r(e),s=[]
|
|
956
|
+
for(const[e,r]of t){const t=(r.status||"unverified").toUpperCase()
|
|
957
|
+
s.push(`[${e}] [${t}] ${r.description}`)}return s.join("\n")}function o(e){const t=r(e),o=[]
|
|
958
|
+
for(const[e,r]of t)o.push(` {"key": "${e}", "evidence": "<your evidence for: ${r.description}>"}`)
|
|
959
|
+
return`Use the merge operation with evidence for each criterion:\n\n${s(e)}\n\nExample call:\n{\n "operation": "merge",\n "requestId": "<requestId>",\n "merge": {\n "verifications": [\n${o.join(",\n")}\n ]\n }\n}`}Object.defineProperty(exports,"__esModule",{value:!0}),exports.formatEvidenceInstructions=exports.formatCriteriaWithHashes=exports.buildCriteriaHashMap=exports.criterionHash=void 0,exports.criterionShortKey=t,exports.buildCriteriaKeyMap=r,exports.formatCriteriaWithKeys=s,exports.formatVerifyInstructions=o,exports.criterionHash=t,exports.buildCriteriaHashMap=r,exports.formatCriteriaWithHashes=s,exports.formatEvidenceInstructions=o},5607(e,exports,t){"use strict"
|
|
950
960
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.executeAction=async function(e,t,r){const s=y[e]
|
|
951
961
|
if(!s)return{success:!1,message:"Unknown action type: "+e,error:"No handler registered for action type: "+e}
|
|
952
962
|
try{return await s(t,r)}catch(t){const s=(0,m.getErrorMessage)(t)
|
|
@@ -1036,35 +1046,33 @@ return"idle"===t||"completed"===t}))
|
|
|
1036
1046
|
return{sessions:s,statusMap:o,running:n,idle:i}}catch(e){return this.logger.debug("Failed to fetch OpenCode session state",{error:e+""}),null}}}},6540(e,exports){"use strict"
|
|
1037
1047
|
var t
|
|
1038
1048
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.LogLevel=void 0,function(e){e.ERROR="error",e.WARN="warn",e.INFO="info",e.DEBUG="debug"}(t||(exports.LogLevel=t={}))},6555(e,exports,t){"use strict"
|
|
1039
|
-
function r(e,process,t,r,s=!1,o,n){const i=process.config||{},
|
|
1040
|
-
let
|
|
1041
|
-
for(let e=0;e<
|
|
1042
|
-
|
|
1043
|
-
for(const t of e.acceptanceCriteria)g+=`- ${t}\n`
|
|
1044
|
-
g+='\nFor each criterion, report in the `acceptanceCriteria` array of your results:\n- **criterion**: The criterion text\n- **status**: "verified" (confirmed in code), "unverified" (not found/not implemented), or "unclear" (ambiguous)\n- **evidence**: How you verified it, or why you couldn\'t\n'}return g}Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleRunTestsAction=async function(e,l){const{requestId:d,testProcessIds:u,parentSessionId:h,mode:g}=e,p="interactive"===g,{logger:m,apiClient:f,projectId:y,projectPath:v,trackChildPid:w}=l
|
|
1049
|
+
function r(e,process,t,r,s=!1,o,n){const i=process.config||{},c=(i.setup,i.commands||[]),l=o&&o.length>0?o.map((e=>c.find((t=>t.name===e)))).filter((e=>e)):c,d=new Set(n||[]),u=d.size>0?l.filter((e=>!d.has(e.name))):l,h=o&&o.length>0&&u.length<c.length||d.size>0,g=d.size>0?c.filter((e=>d.has(e.name))).map((e=>e.name)):[]
|
|
1050
|
+
let p=`You are a test runner agent. Your job is to verify that request ${e.localId||e.id} ("${e.title}") passes all required tests.\n\n## Test Process: ${process.name}\n${process.description?`\n${process.description}\n`:""}\n${process.scope?`**Scope:** Files under \`${process.scope}\`\n`:""}\n## Project ID: ${r}\n${g.length>0?`\n_The coder opted out of these command(s) for this completion: **${g.join(", ")}**. Do NOT run them. They've been excluded from the list below._\n`:""}\n## Test Commands${h?" (partial run \u2014 only the commands listed below)":""}\nExecute these in order:\n`
|
|
1051
|
+
for(let e=0;e<u.length;e++){const t=u[e]
|
|
1052
|
+
p+=`\n### ${e+1}. ${t.name}${!1!==t.required?" (REQUIRED)":" (optional)"}\n- **Command:** \`${t.command}\`\n- **Timeout:** ${t.timeout||300}s\n`}return p+=s?`\n## Instructions\n\nYou are in interactive mode. Present the test plan to the user and **wait for their direction** before running anything.\n\n1. Greet the user and list the test commands available for this process.\n2. Wait for the user to tell you to proceed (they may want to run specific commands, skip some, or run all).\n3. Use \`execute_test_command\` with processId="${process.id}" and the command name to run each command.\n4. After each command, explain the result and wait for the user before continuing.\n5. When the user is satisfied, call \`report_test_results\` with the overall status.\n\nYou are read-only \u2014 do NOT modify any source code. Your job is to verify, not fix.`:`\n## Instructions\n\nSetup commands have already been executed. Use the \`execute_test_command\` tool to run each test command:\n\n1. For each command above, call \`execute_test_command\` with processId="${process.id}" and the command name.\n2. Check if it passed or failed.\n3. If it fails: try to determine why \u2014 is it a real bug, a flaky test, or an environment issue? Retry once if it looks flaky.\n4. When all commands are done, call \`report_test_results\` with the overall status and per-command results.\n - If all REQUIRED commands passed \u2192 status "passed"\n - If any REQUIRED command failed \u2192 status "failed" with diagnosis of each failure\n\nYou are read-only \u2014 do NOT modify any source code. Your job is to verify, not fix.`,i.verifyAcceptanceCriteria&&e.acceptanceCriteria?.length>0&&(p+="\n\n## Acceptance Criteria Verification\n\nAfter running all commands, verify each acceptance criterion by running the relevant tests/commands (and reading code as needed). Criteria you confirm by execution are recorded as the test-dimension verdict.\n\nCriteria to verify (each is [key] [STATUS] text \u2014 cite the key):\n",p+=(0,a.formatCriteriaWithKeys)(e.acceptanceCriteria)+"\n",p+='\nFor each criterion, report in the `acceptanceCriteria` array of your results:\n- **key**: the criterion\'s [key] shown above\n- **status**: "verified" (proven by running it), "unverified" (not satisfied), or "unclear" (couldn\'t determine)\n- **evidence**: the command/output that proves it, or why you couldn\'t\n'),p}Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleRunTestsAction=async function(e,a){const{requestId:d,testProcessIds:u,parentSessionId:h,mode:g}=e,p="interactive"===g,{logger:m,apiClient:f,projectId:y,projectPath:v,trackChildPid:w}=a
|
|
1045
1053
|
if(!d)return{success:!1,message:"requestId is required",error:"Missing requestId in payload"}
|
|
1046
1054
|
m.info("Starting run-tests action",{requestId:d,testProcessIds:u})
|
|
1047
|
-
try{const
|
|
1048
|
-
if(!
|
|
1049
|
-
const g=
|
|
1055
|
+
try{const a=await f.get(`/api/projects/${y}/requests/${d}`)
|
|
1056
|
+
if(!a.success||!a.data)return{success:!1,message:`Request ${d} not found`,error:"Request not found"}
|
|
1057
|
+
const g=a.data,I=g.branchName||"requests/"+d
|
|
1050
1058
|
let E
|
|
1051
1059
|
if(u?.length)E=(await Promise.all(u.map((e=>f.get(`/api/projects/${y}/test-processes/${e}`))))).filter((e=>e.success&&e.data)).map((e=>e.data))
|
|
1052
1060
|
else{const e=await f.get(`/api/projects/${y}/test-processes/active`)
|
|
1053
1061
|
E=e.success&&e.data?Array.isArray(e.data)?e.data:[e.data]:[]}if(0===E.length)return{success:!1,message:"No test processes configured for this project. Set up testing first.",error:"No test processes"}
|
|
1054
1062
|
const b=Math.max(1,Math.min(8,E[0]?.config?.poolSize??2)),S=E[0]?.config?.fingerprint||[]
|
|
1055
1063
|
let C
|
|
1056
|
-
try{C=await(0,
|
|
1064
|
+
try{C=await(0,l.acquireSlot)(v,b)}catch(e){return{success:!1,message:"Could not acquire test worktree slot: "+(0,i.getErrorMessage)(e),error:(0,i.getErrorMessage)(e)}}const k=C.path,T=(0,n.simpleGit)(v)
|
|
1057
1065
|
try{if((0,s.existsSync)(k))try{const e=(0,n.simpleGit)(k)
|
|
1058
1066
|
await e.raw(["reset","--hard",I]),m.info("Reset pool slot to latest branch HEAD",{worktreePath:k,branchName:I,slotIndex:C.index})}catch(e){m.warn("Could not reset slot, recreating",{error:(0,i.getErrorMessage)(e)})
|
|
1059
1067
|
try{await T.raw(["worktree","remove","--force",k])}catch{}}if(!(0,s.existsSync)(k)){(0,s.mkdirSync)((0,o.join)(v,".alchemy_lab","test-worktrees"),{recursive:!0})
|
|
1060
|
-
try{if(!(await T.branch()).all.includes(I))return(0,
|
|
1061
|
-
await T.raw(["worktree","add","--detach",k,I]),m.info("Created pool slot worktree (detached)",{worktreePath:k,branchName:I,slotIndex:C.index})}catch(e){if(!(e+"").includes("already exists"))return(0,
|
|
1062
|
-
m.info("Pool slot worktree already exists, using as-is",{worktreePath:k})}}}catch(e){throw(0,
|
|
1068
|
+
try{if(!(await T.branch()).all.includes(I))return(0,l.releaseSlot)(k),{success:!1,message:`Branch ${I} not found`,error:"Branch not found"}
|
|
1069
|
+
await T.raw(["worktree","add","--detach",k,I]),m.info("Created pool slot worktree (detached)",{worktreePath:k,branchName:I,slotIndex:C.index})}catch(e){if(!(e+"").includes("already exists"))return(0,l.releaseSlot)(k),{success:!1,message:"Failed to create pool slot worktree: "+(0,i.getErrorMessage)(e),error:(0,i.getErrorMessage)(e)}
|
|
1070
|
+
m.info("Pool slot worktree already exists, using as-is",{worktreePath:k})}}}catch(e){throw(0,l.releaseSlot)(k),e}const _=await(0,l.computeFingerprint)(k,S),j=0===S.length||_!==C.storedFingerprint,A=0===S.length?null:_===C.storedFingerprint
|
|
1063
1071
|
j||m.info("Pool slot fingerprint matches; skipping setup",{worktreePath:k,slotIndex:C.index,fingerprintFiles:S})
|
|
1064
1072
|
const{execSync:P}=await Promise.resolve().then(t.t.bind(t,5317,23))
|
|
1065
1073
|
if(j){for(const e of E){const t=e.config?.setup||[]
|
|
1066
1074
|
if(0!==t.length){m.info("Running setup commands for test process",{processName:e.name,commandCount:t.length,slotIndex:C.index})
|
|
1067
|
-
for(const r of t)try{m.info("Setup command",{command:r,worktreePath:k}),P(r,{cwd:k,timeout:12e4,encoding:"utf-8",stdio:["pipe","pipe","pipe"],shell:"/bin/sh",env:{...process.env,FORCE_COLOR:"0"}})}catch(t){m.warn("Setup command failed",{command:r,processName:e.name,exitCode:t.status,stderr:(t.stderr||"").slice(-500)})}}}S.length>0&&await(0,
|
|
1075
|
+
for(const r of t)try{m.info("Setup command",{command:r,worktreePath:k}),P(r,{cwd:k,timeout:12e4,encoding:"utf-8",stdio:["pipe","pipe","pipe"],shell:"/bin/sh",env:{...process.env,FORCE_COLOR:"0"}})}catch(t){m.warn("Setup command failed",{command:r,processName:e.name,exitCode:t.status,stderr:(t.stderr||"").slice(-500)})}}}S.length>0&&await(0,l.writeSlotFingerprint)(k,_),await(0,l.writeWarmSnapshot)(k)}else{const{removed:e}=await(0,l.restoreToWarmSnapshot)(k)
|
|
1068
1076
|
e>0&&m.info("Pruned drift from warm slot",{worktreePath:k,slotIndex:C.index,removed:e})}let M
|
|
1069
1077
|
try{const e=(0,n.simpleGit)(k)
|
|
1070
1078
|
M=(await e.revparse(["HEAD"])).trim()}catch(e){m.warn("Failed to capture worktree HEAD; commit_sha will be NULL on this run",{worktreePath:k,error:(0,i.getErrorMessage)(e)})}const O=[],R=[],D=[]
|
|
@@ -1074,13 +1082,13 @@ for(const process of E){let t
|
|
|
1074
1082
|
const s=e.testRunIds?.[O.length]
|
|
1075
1083
|
try{s?(await f.patch(`/api/projects/${y}/test-runs/${s}`,$),t=(await f.get(`/api/projects/${y}/test-runs/${s}`)).data,O.push(t.id)):(t=(await f.post(`/api/projects/${y}/test-runs`,{requestId:d,testProcessId:process.id,parentSessionId:h,...$})).data,O.push(t.id))}catch(e){m.error("Failed to get/create test run record",{requestId:d,processId:process.id,preCreatedId:s,error:(0,i.getErrorMessage)(e)})
|
|
1076
1084
|
continue}const o=r(g,process,0,y,p,e.commandNames,e.skipCommandNames),n=p?"tester":"auto-tester"
|
|
1077
|
-
try{const e=await(0,
|
|
1085
|
+
try{const e=await(0,c.spawnActionAgent)({agent:n,prompt:o,cwd:k,projectPath:v,projectId:y,title:`[${d}] Test: ${process.name}`,requestId:d,parentSessionId:h,allowParallel:!0,apiClient:f,logger:m,trackChildPid:w})
|
|
1078
1086
|
if(!e.success)throw Error(e.error||"Spawn failed")
|
|
1079
1087
|
R.push(t.id),e.sessionId&&D.push(e.sessionId),e.pid&&(x=e.pid),e.sessionId&&(f.patch(`/api/projects/${y}/test-runs/${t.id}`,{testerSessionId:e.sessionId}).catch((e=>m.warn("Failed to set testerSessionId on test run",{error:(0,i.getErrorMessage)(e)}))),f.post(`/api/projects/${y}/session-test-process-associations`,{opencodeSessionId:e.sessionId,testProcessId:process.id}).catch((t=>m.warn("Failed to create session\u2194test-process association",{sessionId:e.sessionId,testProcessId:process.id,error:(0,i.getErrorMessage)(t)})))),m.info("Spawned test-runner agent",{testRunId:t.id,processName:process.name,worktreePath:k,pid:e.pid,sessionId:e.sessionId})}catch(e){m.error("Failed to spawn test-runner agent",{testRunId:t.id,error:(0,i.getErrorMessage)(e)})
|
|
1080
|
-
try{await f.patch(`/api/projects/${y}/test-runs/${t.id}`,{status:"error",results:{steps:[],summary:"Failed to spawn test agent: "+(0,i.getErrorMessage)(e)}})}catch{}}}if(0===R.length)return(0,
|
|
1081
|
-
for(const e of D);return(0,
|
|
1088
|
+
try{await f.patch(`/api/projects/${y}/test-runs/${t.id}`,{status:"error",results:{steps:[],summary:"Failed to spawn test agent: "+(0,i.getErrorMessage)(e)}})}catch{}}}if(0===R.length)return(0,l.releaseSlot)(k),{success:!1,message:"Failed to spawn any test agents",error:"All agent spawns failed"}
|
|
1089
|
+
for(const e of D);return(0,l.watchPidAndRelease)(x,k,12e5),{success:!0,message:`Started ${R.length} test run(s) for request ${d}`,data:{testRunIds:O,agentsSpawned:R.length,processCount:E.length,worktreePath:k,spawnedSessionIds:D}}}catch(e){const t=(0,i.getErrorMessage)(e)
|
|
1082
1090
|
return m.error("run-tests action failed",{requestId:d,error:t}),{success:!1,message:"Failed to run tests: "+t,error:t}}}
|
|
1083
|
-
const s=t(9896),o=t(6928),n=t(3807),i=t(8824),a=t(
|
|
1091
|
+
const s=t(9896),o=t(6928),n=t(3807),i=t(8824),a=t(5454),c=t(8148),l=t(489)},6563(e,exports,t){"use strict"
|
|
1084
1092
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.TimerHookScheduler=void 0
|
|
1085
1093
|
const r=t(6175),s=t(4627),o=t(4769)
|
|
1086
1094
|
class n{constructor(e){this.tracking=new Map,this.inFlight=new Set,this.paused=new Set,this.stopped=new Set,this.cachedHooks=[],this.lastHookFetch=0,this.lastActivityTime=0,this.agentTypeCache=new Map,this.logger=e.logger,this.hookExecutor=e.hookExecutor,this.apiClient=e.apiClient,this.getOpenCodeUrl=e.getOpenCodeUrl,this.getDefaultModel=e.getDefaultModel,this.projectId=e.projectId,this.projectPath=e.projectPath,this.conditionEvaluator=new r.TimerConditionEvaluator(e.logger)}start(){this.tickTimer||(this.logger.info("Timer hook scheduler starting",{tickInterval:1e4}),setTimeout((()=>this.tick().catch((()=>{}))),3e3),this.tickTimer=setInterval((()=>{this.tick().catch((()=>{}))}),1e4))}stop(){this.tickTimer&&(clearInterval(this.tickTimer),this.tickTimer=void 0),this.tracking.clear(),this.inFlight.clear(),this.paused.clear(),this.stopped.clear(),this.cachedHooks=[],this.logger.info("Timer hook scheduler stopped")}pauseHook(e){return!this.stopped.has(e)&&(this.paused.add(e),this.logger.info("Timer hook paused",{hookId:e}),!0)}resumeHook(e){const t=this.paused.delete(e)
|
|
@@ -1395,16 +1403,20 @@ i.ok?n.info("Pushed notification to CLI session",{sessionId:f}):n.warn("Failed t
|
|
|
1395
1403
|
m=e.success,m?n.info("Spawned work agent for request",{requestId:o,sessionId:e.sessionId}):n.warn("Failed to spawn work agent",{requestId:o,error:e.error})}catch(e){n.warn("Failed to spawn work agent",{requestId:o,error:(0,r.getErrorMessage)(e)})}return n.info("Approve-and-work-request action completed",{requestId:o,notifiedSession:p,agentSpawned:m}),{success:!0,message:`Successfully approved request ${o}: ${d.title}${p?" \u2014 creating session notified":m?" \u2014 work agent spawned":""}`,data:{operationType:"approve-and-work",previousStatus:"pending_approval",newStatus:"open",notifiedSession:p,agentSpawned:m}}}catch(e){const t=(0,r.getErrorMessage)(e)
|
|
1396
1404
|
return n.error("approve-and-work-request action failed",{requestId:o,error:t}),{success:!1,message:"Failed to approve request: "+t,error:t}}}
|
|
1397
1405
|
const r=t(8824),s=t(8148)},7775(e,exports,t){"use strict"
|
|
1398
|
-
Object.defineProperty(exports,"__esModule",{value:!0}),exports.REVIEWER_FOCUSES=void 0,exports.handleSpawnReviewerAction=async function(e,t){const{requestId:
|
|
1399
|
-
if(!
|
|
1400
|
-
if("string"!=typeof(
|
|
1401
|
-
var
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1406
|
+
Object.defineProperty(exports,"__esModule",{value:!0}),exports.REVIEWER_FOCUSES=void 0,exports.handleSpawnReviewerAction=async function(e,t){const{requestId:n,parentSessionId:i,focus:a}=e,{logger:c,apiClient:l,projectId:d,projectPath:u,trackChildPid:h}=t
|
|
1407
|
+
if(!n||!i||!a)return{success:!1,message:"spawn-reviewer requires requestId, parentSessionId, and focus",error:"Missing payload fields"}
|
|
1408
|
+
if("string"!=typeof(g=a)||!exports.REVIEWER_FOCUSES.includes(g))return{success:!1,message:"Invalid focus: "+a,error:"Invalid focus"}
|
|
1409
|
+
var g
|
|
1410
|
+
c.info("Starting spawn-reviewer action",{requestId:n,focus:a,parentSessionId:i})
|
|
1411
|
+
let p=""
|
|
1412
|
+
if("CRITERIA"===a)try{const e=await l.get(`/api/projects/${d}/requests/${n}`)
|
|
1413
|
+
if(e.success&&e.data){const t=e.data,r=Array.isArray(t.acceptanceCriteria)?t.acceptanceCriteria:[],s=r.length>0?(0,o.formatCriteriaWithKeys)(r):"",n="string"==typeof t.completionSummary?t.completionSummary.trim():"",i=[]
|
|
1414
|
+
i.push(s?"Acceptance criteria to verify (shown as [key] [STATUS] text \u2014 record the ones you confirm satisfied via record_criteria_verification, citing the key):\n"+s:"This request has no acceptance criteria defined \u2014 report (none) for must_fix."),n&&i.push("Coder's completion summary (what they say they did):\n"+n),p=`\n${i.join("\n\n")}\n`}}catch(e){c.warn("spawn-reviewer: could not fetch request for CRITERIA context; proceeding without",{requestId:n,error:(0,s.getErrorMessage)(e)})}try{const e=function(e,t,r,s){const o=`[Session Context]\n- Request id: ${e}\n- Your focus: ${t}\n- Coder session to report to: ${r}`
|
|
1415
|
+
return"CRITERIA"===t?`${o}\n\nYou are the CRITERIA reviewer. You verify the diff actually delivers each acceptance criterion, RECORD your per-criterion verdict, then report. The other reviewers cover code quality; you cover whether the work is complete and correct against the spec.\n${s}\n1. Call \`request_diff\` to read the diff.\n2. Use \`read_files\` and \`search\` to confirm whether each criterion is satisfied by the change (cite file:line).\n3. For every criterion you confirm is satisfied, record it with \`record_criteria_verification\` { requestId: "${e}", verifications: [{ key, status, evidence }] } \u2014 record a verdict for EVERY criterion in a SINGLE call: status is "met" (evidence = file:line justification) or "unmet" (evidence = what is missing and how to fix it). Use the [key] shown next to each criterion.\n4. Any criterion you mark "unmet" is also a must_fix in your report. The server opens a rework task from the unmet criteria for the coder to address.\n5. Send ONE report via \`session_message_operations send\` with type "report", toSessionId "${r}", finish true, and a structured markdown summary (a per-criterion table + a must_fix section).\n6. Exit. Do not send follow-ups.`:`${o}\n\nYou are reviewing this request's diff with a single focus: **${t}**. Other dimensions are being reviewed in parallel by your peers \u2014 ignore them silently.\n\n1. Call \`request_diff\` to read the diff.\n2. Use \`read_files\` and \`search\` to gather context for any finding you intend to flag.\n3. Categorize each finding into must_fix / nice_to_have / out_of_scope per your agent instructions.\n4. Send ONE report via \`session_message_operations send\` with type "report", toSessionId "${r}", finish true, and the structured markdown summary.\n5. Exit. Do not send follow-ups.`}(n,a,i,p),t=`Reviewer (${a}) for ${n}`,s=await(0,r.spawnActionAgent)({agent:"CRITERIA"===a?"criteria-reviewer":"reviewer",prompt:e,cwd:u,projectPath:u,projectId:d,title:t,requestId:n,parentSessionId:i,allowParallel:!0,apiClient:l,logger:c,trackChildPid:h})
|
|
1416
|
+
return s.success&&s.sessionId?{success:!0,message:`Reviewer spawned: ${a} for ${n}`,data:{sessionId:s.sessionId,pid:s.pid,focus:a}}:{success:!1,message:`Failed to spawn reviewer (${a}): ${s.error}`,error:s.error||"Spawn failed"}}catch(e){const t=(0,s.getErrorMessage)(e)
|
|
1417
|
+
return c.error("spawn-reviewer action failed",{requestId:n,focus:a,error:t}),{success:!1,message:`Failed to spawn reviewer (${a}): ${t}`,error:t}}}
|
|
1418
|
+
const r=t(8148),s=t(8824),o=t(5454)
|
|
1419
|
+
exports.REVIEWER_FOCUSES=["REUSE","QUALITY","EFFICIENCY","CRITERIA"]},7904(e,exports){"use strict"
|
|
1408
1420
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.REPORT_STATUS_EMOJI=void 0,exports.formatReportNotification=function(e){const{messageCode:t,status:r,summary:s,details:o,fromSessionId:n,senderAgent:i}=e,a=[`${exports.REPORT_STATUS_EMOJI[r]||"\u{1f4cb}"} **Report \u2014 ${r}**`]
|
|
1409
1421
|
return t&&a.push("**Report Code:** "+t),i&&a.push(n?`from: ${i} (${n})`:"from: "+i),a.push("",s||"Task completed"),o&&a.push("",o),t&&a.push("",`Use \`session_message_operations\` with \`reply\` operation and message code \`${t}\` to acknowledge.`),a.join("\n")},exports.REPORT_STATUS_EMOJI={success:"\u2705",partial:"\u26a0\ufe0f",failure:"\u274c",blocked:"\u{1f6ab}"}},7960(e,exports){"use strict"
|
|
1410
1422
|
function t(){const e=process.argv[1]||""
|
|
@@ -1592,7 +1604,7 @@ for(const e of T){if(!e.enabled)continue
|
|
|
1592
1604
|
const t={}
|
|
1593
1605
|
for(const[r,s]of Object.entries(e.tools||{})){const e=s,n=r.replace(/-/g,"_")
|
|
1594
1606
|
t[o.OPENCODE_BUILTIN_TOOLS.has(r)?r:`${d}_${n}`]=e}const r=_?.metadata?.bashToolAgents||{}
|
|
1595
|
-
e.name in r&&(t.bash=!!r[e.name]),t.skill=!1,("subagent"===e.mode||"tester"===e.name||"auto-tester"===e.name)&&void 0===t.task&&(t.task=!1)
|
|
1607
|
+
e.name in r&&(t.bash=!!r[e.name]),t.skill=!1,("subagent"===e.mode||"tester"===e.name||"auto-tester"===e.name||"reviewer"===e.name||"criteria-reviewer"===e.name)&&void 0===t.task&&(t.task=!1)
|
|
1596
1608
|
let s=""
|
|
1597
1609
|
k&&(s+=k.trim()+"\n\n"),e.prompt&&(s+=e.prompt),e.promptAddition&&(s+="\n\n"+e.promptAddition),j.agent[e.name]={tools:t,...s?{prompt:s}:{},...e.description?{description:e.description}:{},mode:"utility"===e.mode?"primary":e.mode||"primary",...e.color?{color:e.color}:{}}}j.agent.general={...j.agent.general,disable:!0},j.agent.explore={...j.agent.explore,tools:{grep:!1,glob:!1,list:!1,read:!1,bash:!1,[d+"_search"]:!0,[d+"_read_files"]:!0,[d+"_view_asset"]:!0,[d+"_query_history"]:!0,webfetch:!0,websearch:!0,codesearch:!0}}
|
|
1598
1610
|
const e=`---\ndescription: Fast codebase exploration and research subagent\nmode: subagent\n---\n\n${k?k.trim()+"\n\n":""}You are a codebase exploration agent. Your job is to find specific information and return concise, targeted answers.\n\n## Response Guidelines\n\n- **Be concise.** Return only what was asked for. A typical answer is 100-500 words.\n- **Lead with the answer.** State findings first, then supporting details.\n- **Reference specific files and lines.** Use \`file_path:line_number\` format.\n- **Don't exhaustively document everything you find.** Focus on what's relevant to the question.\n- **Use structured output** when listing multiple items (bullet points, not prose).\n\n## Strategy\n\n1. Start with \`search\` to find relevant files by name or content patterns\n2. Use \`read_files\` to read specific sections (use line ranges, don't read entire large files)\n3. Use \`query_history\` for understanding why code changed or tracing decisions\n\n## Anti-patterns to Avoid\n\n- Don't produce multi-thousand-word reports for simple questions\n- Don't read every file in a directory when a targeted search would suffice\n- Don't explain how tools work \u2014 just use them and report findings\n`
|