alchemylab 1.4.12 → 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.
@@ -334,15 +334,15 @@ const $=new k.TaskClient(T,n.child({component:"task-client"}),o,M,R,j)
334
334
  x.registerClient($)
335
335
  const O=new v.FileOperationsClient(T,n.child({component:"file-client"}),o,M,j,R)
336
336
  x.registerClient(O)
337
- const D=new C.ProxyClient(T,n.child({component:"proxy-client"}),o,M,j,R)
338
- x.registerClient(D)
339
- const A=new _.RequestClient(T,n.child({component:"request-client"}),o,M,j,R)
340
- A.setAuthToken(j),(0,d.isDebugToolsEnabled)()&&n.debug("Explicitly set token on RequestClient",{tokenPresent:!!j}),x.registerClient(A)
337
+ const A=new C.ProxyClient(T,n.child({component:"proxy-client"}),o,M,j,R)
338
+ x.registerClient(A)
339
+ const D=new _.RequestClient(T,n.child({component:"request-client"}),o,M,j,R)
340
+ D.setAuthToken(j),(0,d.isDebugToolsEnabled)()&&n.debug("Explicitly set token on RequestClient",{tokenPresent:!!j}),x.registerClient(D)
341
341
  const F=new E.DocumentClient(T,n.child({component:"document-client"}),o,M,j,R)
342
342
  x.registerClient(F)
343
343
  const N=new S.ToolRecordClient(T,n.child({component:"tool-record-client"}),o,M,j,R)
344
344
  x.registerClient(N)
345
- const z=new q.ApiClient(P,M,$,O,A,F,N,D,n.child({component:"api-client"})),L=a.default.Router();(0,h.registerAuthRoutes)(L,{projectClient:M,taskClient:$,fileClient:O,authManager:P,apiClient:z},i,n)
345
+ const z=new q.ApiClient(P,M,$,O,D,F,N,A,n.child({component:"api-client"})),L=a.default.Router();(0,h.registerAuthRoutes)(L,{projectClient:M,taskClient:$,fileClient:O,authManager:P,apiClient:z},i,n)
346
346
  const H=r.getContext();(0,p.registerConfigRoutes)(L,n,H.basePath),e.use("/api",L)
347
347
  const B=e=>{const t=o.startTimer(e)
348
348
  return e=>t(e)},U=(e,t,n)=>{const o=e.query.path||e.body.path
@@ -377,11 +377,11 @@ return r({endpoint:"files/content",method:"GET",success:!0,encoding:"binary"}),t
377
377
  r({endpoint:"files/content",method:"GET",success:!0,encoding:o}),t.json({success:!0,data:{path:e.relativePath,content:s,size:n.size,modified:n.mtime}})}catch(t){throw"ENOENT"===t.code?(r({endpoint:"files/content",method:"GET",success:!1,error:"not_found"}),(0,u.createNotFoundError)("File not found: "+e.relativePath)):t}}catch(e){r(e)}})),e.post("/api/files/write",U,W,(async(e,t,s)=>{try{o.incrementCounter("api_requests_total",1,{endpoint:"files/write",method:"POST"})
378
378
  const s=B("api_request_duration"),{content:i,taskId:a,format:c="text"}=e.body
379
379
  if(void 0===i)throw(0,u.createValidationError)("Content parameter is required")
380
- const l=await(0,g.handleWriteFile)({taskId:a,path:e.relativePath,content:i,format:c},r.getContext(),O,$,N,A,n,o)
380
+ const l=await(0,g.handleWriteFile)({taskId:a,path:e.relativePath,content:i,format:c},r.getContext(),O,$,N,D,n,o)
381
381
  s({endpoint:"files/write",method:"POST",success:!0}),t.json({success:!0,data:l})}catch(e){s(e)}})),e.post("/api/files/replace",U,W,(async(e,t,s)=>{try{o.incrementCounter("api_requests_total",1,{endpoint:"files/replace",method:"POST"})
382
382
  const s=B("api_request_duration"),{replacements:i,mode:a="unique",taskId:c}=e.body
383
383
  if(!i||!Array.isArray(i))throw(0,u.createValidationError)("Replacements array is required")
384
- const l=await(0,m.handleReplaceStrings)({taskId:c,operations:[{path:e.relativePath,replacements:i,mode:a}],comment:"String replacement via API"},r.getContext(),$,N,A,n,o)
384
+ const l=await(0,m.handleReplaceStrings)({taskId:c,operations:[{path:e.relativePath,replacements:i,mode:a}],comment:"String replacement via API"},r.getContext(),$,N,D,n,o)
385
385
  s({endpoint:"files/replace",method:"POST",success:!0}),t.json({success:!0,data:l})}catch(e){s(e)}})),e.delete("/api/files/delete",U,W,(async(e,t,r)=>{try{o.incrementCounter("api_requests_total",1,{endpoint:"files/delete",method:"DELETE"})
386
386
  const r=B("api_request_duration"),{taskId:n}=e.body
387
387
  try{await l.default.access(e.fullPath)}catch(t){throw r({endpoint:"files/delete",method:"DELETE",success:!1,error:"not_found"}),(0,u.createNotFoundError)("File not found: "+e.relativePath)}await l.default.unlink(e.fullPath),r({endpoint:"files/delete",method:"DELETE",success:!0}),t.json({success:!0,data:{path:e.relativePath,deleted:!0}})}catch(e){r(e)}})),e.post("/api/directories/create",U,W,(async(e,t,r)=>{try{o.incrementCounter("api_requests_total",1,{endpoint:"directories/create",method:"POST"})
@@ -557,7 +557,7 @@ v.initialScan={totalEntries:t.length,directories:r,files:n,firstFewFiles:n.slice
557
557
  v.errors.push(t),l.error(t)}const $=E?"file":"directory"
558
558
  l.info(`Searching for "${n}" in ${$} ${_} (mode: ${x})`,{needle:n,searchTarget:$,path:_,options:{maxResults:S,caseSensitive:q,fileExtension:M,searchIn:x}}),k=Date.now(),v.phases.initializationTime=k-u
559
559
  const O=Date.now()
560
- let D=[],A=0,F=0
560
+ let A=[],D=0,F=0
561
561
  try{const e=await async function(e,t,n,o,s,i){const{maxResults:a,fileExtension:c,searchIn:l,isFile:d,contextLines:u}=n,m=[]
562
562
  if(s.info("performLocalSearch started",{searchPath:e,pattern:t.toString(),options:n}),d)return await async function(e,t,r,n,o,s=0){const i=[],a=h.default.basename(e)
563
563
  let c
@@ -574,16 +574,16 @@ const g={path:e,filesProcessed:0,filesMatched:0,subdirectories:0,errors:[]}
574
574
  try{const{filesCount:n,fileMatchesCount:c,subdirCount:d,subdirStats:h}=await r(e,t,m,a,o,s,0,g,l,u)
575
575
  return g.filesProcessed=n,g.filesMatched=c,g.subdirectories=d,i.directoryStats.push(g),h&&h.length>0&&i.directoryStats.push(...h),s.info("performLocalSearch completed",{totalFiles:n,matchesFound:m.length,directories:d+1}),{matches:m,totalFiles:n}}catch(t){const r="Error in performLocalSearch: "+(0,b.getErrorMessage)(t)
576
576
  return g.errors.push(r),i.directoryStats.push(g),i.errors.push(r),s.error(r,{error:t instanceof Error?t.stack:t+"",searchPath:e}),{matches:m,totalFiles:g.filesProcessed}}}(_,R,{maxResults:S,fileExtension:M,searchIn:x,isFile:E,contextLines:j},M,l,v)
577
- D=e.matches,A=e.totalFiles
577
+ A=e.matches,D=e.totalFiles
578
578
  const t=1e3
579
- D.length>0&&D.length<=t?(D.sort(((e,t)=>void 0!==e.modifiedTime&&void 0!==t.modifiedTime?t.modifiedTime-e.modifiedTime:void 0!==e.modifiedTime&&void 0===t.modifiedTime?-1:void 0===e.modifiedTime&&void 0!==t.modifiedTime?1:0)),l.info(`Sorted ${D.length} matches by modification time (newest first)`)):D.length>t&&l.info(`Skipped sorting ${D.length} matches (exceeds hard cap of ${t} for performance)`),F=D.length,D.length>S&&(D=D.slice(0,S),l.info(`Limited results to ${S} (from ${F} total matches)`)),D.forEach((e=>{e.relativePath=function(e,t){const r=h.default.relative(e,t)
580
- return r?r===h.default.basename(t)?r:r.startsWith("./")?r.substring(2):r:h.default.basename(t)}(_,e.file)})),l.info(`Local search completed: found ${F||D.length} total matches in ${A} files, returning ${D.length} results`),v.phases.searchTime=Date.now()-O}catch(e){const t="Error during search: "+(0,b.getErrorMessage)(e)
581
- v.errors.push(t),l.error(t),v.phases.searchTime=Date.now()-O}v.totalFiles=A,v.totalMatches=D.length,v.endTime=Date.now()
579
+ A.length>0&&A.length<=t?(A.sort(((e,t)=>void 0!==e.modifiedTime&&void 0!==t.modifiedTime?t.modifiedTime-e.modifiedTime:void 0!==e.modifiedTime&&void 0===t.modifiedTime?-1:void 0===e.modifiedTime&&void 0!==t.modifiedTime?1:0)),l.info(`Sorted ${A.length} matches by modification time (newest first)`)):A.length>t&&l.info(`Skipped sorting ${A.length} matches (exceeds hard cap of ${t} for performance)`),F=A.length,A.length>S&&(A=A.slice(0,S),l.info(`Limited results to ${S} (from ${F} total matches)`)),A.forEach((e=>{e.relativePath=function(e,t){const r=h.default.relative(e,t)
580
+ return r?r===h.default.basename(t)?r:r.startsWith("./")?r.substring(2):r:h.default.basename(t)}(_,e.file)})),l.info(`Local search completed: found ${F||A.length} total matches in ${D} files, returning ${A.length} results`),v.phases.searchTime=Date.now()-O}catch(e){const t="Error during search: "+(0,b.getErrorMessage)(e)
581
+ v.errors.push(t),l.error(t),v.phases.searchTime=Date.now()-O}v.totalFiles=D,v.totalMatches=A.length,v.endTime=Date.now()
582
582
  let N,z=""
583
583
  try{if(t.workspaceManager){const e=t.workspaceManager.getCurrentContext(t.currentSessionId)
584
584
  e.branchName&&!e.permanentBranch&&(z=e.isMainBranch?` (searched on main branch: ${e.branchName})`:` (searched on request branch: ${e.branchName}${e.requestId?" for "+e.requestId:""})`)}}catch(e){l.debug("Could not get branch context",{error:e}),z=" (branch context unavailable)"}l.debug("Search diagnostic information",{diagnostic:v})
585
585
  const L="content"===x?"content":"filename"===x?"filenames":"content and filenames"
586
- if(F>D.length?(N=`Found ${F} results for "${n}" (searching ${L})`,m&&(N+=" in "+m),P&&(N+=" with extension "+P),N+=`${z}. Showing first ${D.length}:\n\n`):(N=`Found ${D.length} results for "${n}" (searching ${L})`,m&&(N+=" in "+m),P&&(N+=" with extension "+P),N+=z+":\n\n"),D.length>0)if(j>0){const{filenameMatches:e,mergedGroups:t}=function(e,t){const r=e.filter((e=>"filename"===e.matchType)),n=e.filter((e=>"content"===e.matchType))
586
+ if(F>A.length?(N=`Found ${F} results for "${n}" (searching ${L})`,m&&(N+=" in "+m),P&&(N+=" with extension "+P),N+=`${z}. Showing first ${A.length}:\n\n`):(N=`Found ${A.length} results for "${n}" (searching ${L})`,m&&(N+=" in "+m),P&&(N+=" with extension "+P),N+=z+":\n\n"),A.length>0)if(j>0){const{filenameMatches:e,mergedGroups:t}=function(e,t){const r=e.filter((e=>"filename"===e.matchType)),n=e.filter((e=>"content"===e.matchType))
587
587
  if(0===n.length)return{filenameMatches:r,mergedGroups:[]}
588
588
  const o=new Map
589
589
  for(const e of n){const t=o.get(e.file)||[]
@@ -591,7 +591,7 @@ t.push(e),o.set(e.file,t)}const i=[]
591
591
  for(const[e,r]of o.entries()){r.sort(((e,t)=>e.line-t.line))
592
592
  let e=[r[0]]
593
593
  for(let n=1;n<r.length;n++){const o=e[e.length-1],a=r[n],c=o.line+t
594
- a.line-t>c+1?(i.push(s(e,t)),e=[a]):e.push(a)}e.length>0&&i.push(s(e,t))}return{filenameMatches:r,mergedGroups:i}}(D,j),r=[]
594
+ a.line-t>c+1?(i.push(s(e,t)),e=[a]):e.push(a)}e.length>0&&i.push(s(e,t))}return{filenameMatches:r,mergedGroups:i}}(A,j),r=[]
595
595
  e.forEach((e=>{r.push(e.relativePath+" [filename match]")})),t.forEach((e=>{const t=[]
596
596
  e.lines.forEach((e=>{e.isMatch?t.push(`> ${e.lineNumber}: ${e.content}`):t.push(` ${e.lineNumber}: ${e.content}`)})),r.push(`${e.relativePath}:\n${t.join("\n")}`)})),N+=r.join("\n\n")}else N+=await async function(e){const t=e.filter((e=>"filename"===e.matchType)),r=e.filter((e=>"content"===e.matchType)),n=[]
597
597
  t.length>0&&t.forEach((e=>{n.push(e.relativePath+" [filename match]")}))
@@ -602,7 +602,7 @@ const r=t[0].relativePath||e,o=t.length,s=await i(e)
602
602
  let a=r
603
603
  void 0!==s&&(a+=` (${s} lines)`),a+=` - ${o} ${1===o?"match":"matches"}:`
604
604
  const c=t.map((e=>` :${e.line} - ${e.text}`))
605
- n.push(a+"\n"+c.join("\n"))}return n.join("\n\n")}(D)
605
+ n.push(a+"\n"+c.join("\n"))}return n.join("\n\n")}(A)
606
606
  else N+="No matches found."
607
607
  return d({success:!0}),{content:[{type:"text",text:N}],...(0,w.alchemyMeta)({kind:"search"})}}catch(t){l.error("Search failed",{args:e,error:t instanceof Error?t.stack:t+""}),v.errors.push((0,b.getErrorMessage)(t)),v.endTime=Date.now(),l.debug("Search diagnostic information on error",{diagnostic:v})
608
608
  let r="Search failed: "+(0,b.getErrorMessage)(t)
@@ -1342,7 +1342,8 @@ Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleMergeReques
1342
1342
  try{if(!h)throw(0,n.createValidationError)("requestId is required")
1343
1343
  d.info("Starting merge request",{requestId:h,verificationCount:p.length})
1344
1344
  const e=a.getRequestClient(),s=await e.getRequest(h)
1345
- if("ready_for_review"===s?.status)return u({success:!1,reason:"testing_in_progress"}),{content:[{type:"text",text:`STOP: Cannot merge request ${h} \u2014 testing is in progress. Wait for tests to complete. The request will be moved to ready_for_merge when tests pass.`}],isError:!0}
1345
+ if("running"===s?.testStatus)return u({success:!1,reason:"testing_in_progress"}),{content:[{type:"text",text:`STOP: Cannot merge request ${h} \u2014 tests are still running. Wait for the test report; you'll be cleared to merge when they pass.`}],isError:!0}
1346
+ if("ready_for_review"===s?.status)return u({success:!1,reason:"awaiting_human"}),{content:[{type:"text",text:`STOP: Cannot merge request ${h} \u2014 it is awaiting human action (sign-off or review). A maintainer finalizes it from the UI.`}],isError:!0}
1346
1347
  if(p.length>0)try{await a.proxyPost(`/api/projects/${t.projectId}/requests/${h}/verify-criteria`,{verifications:p}),d.info("Criteria verified",{requestId:h,count:p.length})}catch(e){const t=(0,i.getErrorMessage)(e)
1347
1348
  d.warn("Criteria verification failed, continuing with merge",{requestId:h,error:t})}let c,l,m
1348
1349
  try{const e=await a.proxyPost(`/api/projects/${t.projectId}/requests/${h}/validate-completion`,{})
@@ -1385,23 +1386,24 @@ const o=process.config||{},s=o.commands||[]
1385
1386
  let a=`## ${process.name}\n`
1386
1387
  process.description&&(a+=process.description+"\n"),process.scope&&(a+=`**Scope:** \`${process.scope}\`\n`),a+=`**Status:** ${process.isActive?"Active":"Inactive"}\n\n`,o.setup?.length&&(a+=`### Setup\n${o.setup.map((e=>`\`\`\`\n${e}\n\`\`\``)).join("\n")}\n\n`),a+=`### Test Commands (${s.length})\n`
1387
1388
  for(let e=0;e<s.length;e++){const t=s[e]
1388
- a+=`\n**${e+1}. ${t.name}**${t.required?" \u2014 REQUIRED":" \u2014 optional"}\n`,a+=` Command: \`${t.command}\`\n`,t.timeout&&(a+=` Timeout: ${t.timeout}s\n`)}return Array.isArray(o.fingerprint)&&o.fingerprint.length>0&&(a+=`\n### Fingerprint Files\n${o.fingerprint.map((e=>`- \`${e}\``)).join("\n")}\n`),"number"==typeof o.poolSize&&(a+=`\n**Pool size:** ${o.poolSize}\n`),i({success:!0}),{content:[{type:"text",text:a}]}}catch(e){return i({success:!1,error:"api_error"}),{content:[{type:"text",text:"Failed to get test process: "+(0,r.getErrorMessage)(e)}],isError:!0}}},exports.handleTestingProcessCreate=async function(e,t,n,o,s){const i=s.startTimer("tool_execution_time")
1389
- try{const r={setup:e.setup||[],commands:e.commands.map((e=>({name:e.name,command:e.command,required:!1!==e.required,timeout:e.timeout||300})))}
1390
- void 0!==e.fingerprint&&(r.fingerprint=e.fingerprint),void 0!==e.poolSize&&(r.poolSize=e.poolSize)
1391
- const o=await n.proxyPost(`/api/projects/${t.projectId}/test-processes`,{name:e.name,description:e.description,scope:e.scope,config:r,createdBy:"test-architect"}),process=o?.data||o
1392
- return i({success:!0}),{content:[{type:"text",text:`\u2705 Created test process **${process.name}** (${process.id}) with ${r.commands.length} test commands.${e.scope?`\nScope: \`${e.scope}\``:""}\n\nThe process is now active and will run when requests are completed.`}]}}catch(e){return i({success:!1,error:"api_error"}),{content:[{type:"text",text:"Failed to create test process: "+(0,r.getErrorMessage)(e)}],isError:!0}}},exports.handleTestingProcessUpdate=async function(e,t,n,o,s){const i=s.startTimer("tool_execution_time")
1389
+ a+=`\n**${e+1}. ${t.name}**${t.required?" \u2014 REQUIRED":" \u2014 optional"}\n`,a+=` Command: \`${t.command}\`\n`,t.timeout&&(a+=` Timeout: ${t.timeout}s\n`)}return Array.isArray(o.fingerprint)&&o.fingerprint.length>0&&(a+=`\n### Fingerprint Files\n${o.fingerprint.map((e=>`- \`${e}\``)).join("\n")}\n`),"number"==typeof o.poolSize&&(a+=`\n**Pool size:** ${o.poolSize}\n`),i({success:!0}),{content:[{type:"text",text:a}]}}catch(e){return i({success:!1,error:"api_error"}),{content:[{type:"text",text:"Failed to get test process: "+(0,r.getErrorMessage)(e)}],isError:!0}}},exports.handleTestingProcessCreate=async function(e,n,o,s,i){const a=i.startTimer("tool_execution_time")
1390
+ try{const i={setup:e.setup||[],commands:e.commands.map((e=>({name:e.name,command:e.command,required:!1!==e.required,timeout:e.timeout||300})))}
1391
+ void 0!==e.fingerprint&&(i.fingerprint=e.fingerprint),void 0!==e.poolSize&&(i.poolSize=e.poolSize)
1392
+ const c=await o.proxyPost(`/api/projects/${n.projectId}/test-processes`,{name:e.name,description:e.description,scope:e.scope,config:i,createdBy:"test-architect"}),process=c?.data||c
1393
+ try{const{sessionIdentity:e}=await Promise.resolve().then(t.bind(t,6688)),r=e.getIdentity()
1394
+ r?.sessionId&&process?.id&&await o.proxyPost(`/api/projects/${n.projectId}/session-test-process-associations`,{opencodeSessionId:r.sessionId,testProcessId:process.id})}catch(e){s.warn("Failed to write session-test-process association",{error:(0,r.getErrorMessage)(e)})}return a({success:!0}),{content:[{type:"text",text:`\u2705 Created test process **${process.name}** (${process.id}) with ${i.commands.length} test commands.${e.scope?`\nScope: \`${e.scope}\``:""}\n\nThe process is now active and will run when requests are completed.`}]}}catch(e){return a({success:!1,error:"api_error"}),{content:[{type:"text",text:"Failed to create test process: "+(0,r.getErrorMessage)(e)}],isError:!0}}},exports.handleTestingProcessUpdate=async function(e,t,n,o,s){const i=s.startTimer("tool_execution_time")
1393
1395
  try{const r={}
1394
1396
  if(void 0!==e.name&&(r.name=e.name),void 0!==e.description&&(r.description=e.description),void 0!==e.scope&&(r.scope=e.scope),void 0!==e.isActive&&(r.isActive=e.isActive),void 0!==e.commands||void 0!==e.setup||void 0!==e.fingerprint||void 0!==e.poolSize){const o=await n.proxyGet(`/api/projects/${t.projectId}/test-processes/${e.processId}`),s=o?.data||o,i=s?.config||{},a={setup:e.setup??i.setup??[],commands:e.commands?e.commands.map((e=>({name:e.name,command:e.command,required:!1!==e.required,timeout:e.timeout||300}))):i.commands??[]}
1395
1397
  void 0!==e.fingerprint?a.fingerprint=e.fingerprint:void 0!==i.fingerprint&&(a.fingerprint=i.fingerprint),void 0!==e.poolSize?a.poolSize=e.poolSize:void 0!==i.poolSize&&(a.poolSize=i.poolSize),void 0!==i.verifyAcceptanceCriteria&&(a.verifyAcceptanceCriteria=i.verifyAcceptanceCriteria),r.config=a}const o=await n.proxyPatch(`/api/projects/${t.projectId}/test-processes/${e.processId}`,r),process=o?.data||o
1396
- return i({success:!0}),{content:[{type:"text",text:`\u2705 Updated test process **${process.name}** (${process.id}).`}]}}catch(e){return i({success:!1,error:"api_error"}),{content:[{type:"text",text:"Failed to update test process: "+(0,r.getErrorMessage)(e)}],isError:!0}}},exports.handleExecuteTestCommand=async function(e,n,o,s,a,c,l){const d=a.startTimer("tool_execution_time"),u=s.child({tool:"execute_test_command"})
1397
- try{const a=await o.proxyGet(`/api/projects/${n.projectId}/test-processes/${e.processId}`),process=a?.data||a
1398
+ return i({success:!0}),{content:[{type:"text",text:`\u2705 Updated test process **${process.name}** (${process.id}).`}]}}catch(e){return i({success:!1,error:"api_error"}),{content:[{type:"text",text:"Failed to update test process: "+(0,r.getErrorMessage)(e)}],isError:!0}}},exports.handleExecuteTestCommand=async function(e,n,o,s,i,a,l){const d=i.startTimer("tool_execution_time"),u=s.child({tool:"execute_test_command"})
1399
+ try{const i=await o.proxyGet(`/api/projects/${n.projectId}/test-processes/${e.processId}`),process=i?.data||i
1398
1400
  if(!process?.config)return d({success:!1,error:"not_found"}),{content:[{type:"text",text:`Test process ${e.processId} not found or has no config.`}],isError:!0}
1399
- let c
1401
+ let a
1400
1402
  if("setup"===e.commandName){const e=process.config.setup||[]
1401
1403
  if(0===e.length)return d({success:!0}),{content:[{type:"text",text:"No setup commands configured for this test process."}]}
1402
- c=e.map(((e,t)=>({name:"setup-"+(t+1),command:e,timeout:120})))}else{const t=process.config.commands||[],r=t.find((t=>t.name===e.commandName))
1404
+ a=e.map(((e,t)=>({name:"setup-"+(t+1),command:e,timeout:120})))}else{const t=process.config.commands||[],r=t.find((t=>t.name===e.commandName))
1403
1405
  if(!r){const r=t.map((e=>e.name)).join(", ")
1404
- return d({success:!1,error:"command_not_found"}),{content:[{type:"text",text:`Command "${e.commandName}" not found in test process "${process.name}".\nAvailable commands: ${r}\nUse "setup" to run setup commands.`}],isError:!0}}c=[{name:r.name,command:r.command,timeout:r.timeout||300}]}const{runShellCommand:h}=await Promise.resolve().then(t.bind(t,4600)),p=globalThis.process
1406
+ return d({success:!1,error:"command_not_found"}),{content:[{type:"text",text:`Command "${e.commandName}" not found in test process "${process.name}".\nAvailable commands: ${r}\nUse "setup" to run setup commands.`}],isError:!0}}a=[{name:r.name,command:r.command,timeout:r.timeout||300}]}const{runShellCommand:h}=await Promise.resolve().then(t.bind(t,4600)),p=globalThis.process
1405
1407
  let m=null
1406
1408
  const g=[]
1407
1409
  try{const{sessionIdentity:e}=await Promise.resolve().then(t.bind(t,6688)),r=e.getIdentity()
@@ -1410,53 +1412,60 @@ if(g.push("reqId: "+(t||"NONE")),t){const e=await o.proxyGet(`/api/projects/${n.
1410
1412
  g.push(`runs: ${Array.isArray(r)?r.length:0}, withWorktree: ${s?.id||"NONE"}, path: ${s?.worktreePath||s?.worktree_path||"NONE"}`),s&&(m=s.worktreePath||s.worktree_path,u.info("Using test worktree from request test run",{worktreePath:m,reqId:t}))}}}catch(e){u.warn("Failed to resolve test worktree",{error:(0,r.getErrorMessage)(e)})}if(!m)return d({success:!1,error:"no_worktree"}),{content:[{type:"text",text:`No test worktree found.\n\nDiagnostics:\n${g.length>0?g.map((e=>"\u2022 "+e)).join("\n"):"\u2022 No session identity found"}\n\nEnsure this session is linked to a test process and a test run has been created.`}],isError:!0}
1411
1413
  const f=[],{default:y}=await Promise.resolve().then(t.t.bind(t,6928,23)),{default:w}=await Promise.resolve().then(t.t.bind(t,9896,23)),{ALCHEMY_DIR_NAME:I}=await Promise.resolve().then(t.bind(t,4139)),b=y.join(m,I,"command-logs")
1412
1414
  try{w.mkdirSync(b,{recursive:!0})}catch{}const{runTrackedShellCommand:k}=await Promise.resolve().then(t.bind(t,4600)),{formatCombinedOutput:v}=await Promise.resolve().then(t.bind(t,1816)),{sessionIdentity:C}=await Promise.resolve().then(t.bind(t,6688)),_=C.getIdentity()?.sessionId
1413
- for(const t of c){u.info("Executing test command",{name:t.name,command:t.command,timeout:t.timeout,cwd:m})
1415
+ for(const t of a){u.info("Executing test command",{name:t.name,command:t.command,timeout:t.timeout,cwd:m})
1414
1416
  const r=`${t.name.replace(/[^a-zA-Z0-9._-]/g,"_")}-${Date.now()}`,n=y.join(b,r+".log")
1415
1417
  let o=null
1416
- try{o=w.createWriteStream(n,{flags:"w"}),o.write(`# ${(new Date).toISOString()} starting: ${t.command}\n# cwd: ${m}\n# timeout: ${t.timeout}s\n\n`)}catch(e){u.warn("Failed to open command log file",{logPath:n,error:e+""})}const s=await(0,i.runTrackedShellWithHeartbeat)(t.command,{cwd:m,timeoutMs:1e3*t.timeout,env:{...p.env,FORCE_COLOR:"0"},onChunk:o?(e,t)=>{o?.write(t)}:void 0},{id:r,name:t.name,sessionId:_,logPath:n},l)
1418
+ try{o=w.createWriteStream(n,{flags:"w"}),o.write(`# ${(new Date).toISOString()} starting: ${t.command}\n# cwd: ${m}\n# timeout: ${t.timeout}s\n\n`)}catch(e){u.warn("Failed to open command log file",{logPath:n,error:e+""})}const s=await(0,c.runTrackedShellWithHeartbeat)(t.command,{cwd:m,timeoutMs:1e3*t.timeout,env:{...p.env,FORCE_COLOR:"0"},onChunk:o?(e,t)=>{o?.write(t)}:void 0},{id:r,name:t.name,sessionId:_,logPath:n},l)
1417
1419
  o&&(o.write(`\n# exit: ${s.exitCode}${s.timedOut?" (timed out)":""}\n`),o.end())
1418
- const a=(s.stdout+(s.stderr?"\n"+s.stderr:"")).trim(),{output:c,truncatedNote:d,logFooter:h}=v(a,{invocationId:r})
1419
- if(f.push({name:t.name,command:t.command,exitCode:s.exitCode,output:c,timedOut:s.timedOut,invocationId:r,truncatedNote:d,logFooter:h}),"setup"===e.commandName&&0!==s.exitCode)break}const E=f.every((e=>0===e.exitCode)),S=f.map((e=>{const t=e.timedOut?"\u23f1 TIMEOUT":0===e.exitCode?"\u2705 PASS":`\u274c FAIL (exit ${e.exitCode})`
1420
+ const i=(s.stdout+(s.stderr?"\n"+s.stderr:"")).trim(),{output:a,truncatedNote:d,logFooter:h}=v(i,{invocationId:r})
1421
+ if(f.push({name:t.name,command:t.command,exitCode:s.exitCode,output:a,timedOut:s.timedOut,invocationId:r,truncatedNote:d,logFooter:h}),"setup"===e.commandName&&0!==s.exitCode)break}const E=f.every((e=>0===e.exitCode)),S=f.map((e=>{const t=e.timedOut?"\u23f1 TIMEOUT":0===e.exitCode?"\u2705 PASS":`\u274c FAIL (exit ${e.exitCode})`
1420
1422
  return`### ${e.name}: ${t}\n\`\`\`\n$ ${e.command}\n${e.output}\n\`\`\`${e.truncatedNote}\n${e.logFooter}`})).join("\n\n")
1421
- return d({success:!0}),{content:[{type:"text",text:`## ${"setup"===e.commandName?"Setup":e.commandName} \u2014 ${E?"PASSED":"FAILED"}\n\n${S}`}]}}catch(e){return d({success:!1,error:"execution_error"}),{content:[{type:"text",text:"Failed to execute test command: "+(0,r.getErrorMessage)(e)}],isError:!0}}},exports.handleReportTestResults=async function(e,s,i,a,c,l){const d=c.startTimer("tool_execution_time")
1422
- try{const{resolveRequestIdOrError:u}=await Promise.resolve().then(t.bind(t,9032)),h=await u(i,a,l?.sessionId,"Cannot report test results: this session is not associated with a request.")
1423
- if(!h.ok)return d({success:!1,error:"no_request_association"}),h.errorResult
1424
- const p=h.requestId,m=await i.proxyGet(`/api/projects/${s.projectId}/requests/${p}/test-runs`),g=m?.data||m||[],f=Array.isArray(g)?g:[],y=f.find((e=>"pending"===e.status||"running"===e.status))||f[0]
1425
- if(!y)return d({success:!1,error:"no_test_run"}),{content:[{type:"text",text:`No test run found for request ${p}.`}],isError:!0}
1426
- const w=y.id
1427
- await i.proxyPatch(`/api/projects/${s.projectId}/test-runs/${w}`,{status:e.status,results:e.results}),y.worktreePath&&fetch((0,o.getActionServerBaseUrl)()+"/api/release-pool-slot",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({worktreePath:y.worktreePath}),signal:AbortSignal.timeout(5e3)}).catch((e=>a.debug("release-pool-slot fire-and-forget failed (non-fatal)",{error:(0,r.getErrorMessage)(e)})))
1428
- const I="passed"===e.status?"\u2705":"failed"===e.status?"\u274c":"\u26a0\ufe0f"
1429
- try{const t=i.getRequestClient()
1430
- "passed"===e.status?(await t.updateRequest(p,{status:"ready_for_merge"}),a.info("Tests passed, set request to ready_for_merge",{requestId:p})):(await t.updateRequest(p,{status:"in_progress"}),a.info("Tests failed, set request back to in_progress",{requestId:p}))}catch(e){a.warn("Failed to update request status after test results",{requestId:p,error:(0,r.getErrorMessage)(e)})}const b="passed"===e.status?"\n\nAll tests passed \u2014 the coder can now call `merge` to merge this request.":"\n\nTests failed \u2014 the coder should fix the issues and call `complete` again.",k=`${I} Test run ${e.status} for request ${p}\n\n${e.results.summary}${b}`
1431
- try{const t=await(0,n.handleSessionMessageOperations)({operation:"send",type:"report",status:"passed"===e.status?"success":"failure",summary:k,details:e.results.commands?.map((e=>`${"passed"===e.status?"\u2705":"\u274c"} ${e.name}: ${e.status}${e.diagnosis?" - "+e.diagnosis:""}`)).join("\n"),finish:!0},s,a,c,i,l),r=t?.content?.[0]?.text||"unknown",o=t?.isError
1432
- return d({success:!0}),{content:[{type:"text",text:`Test run for request ${p} reported as **${e.status}**.${o?" Parent notification FAILED: "+r:" Parent session notified."}\n\n${e.results.summary}`}]}}catch(t){return d({success:!0}),{content:[{type:"text",text:`Test run for request ${p} reported as **${e.status}** but failed to notify parent: ${(0,r.getErrorMessage)(t)}\n\n${e.results.summary}`}]}}}catch(e){return d({success:!1,error:"api_error"}),{content:[{type:"text",text:"Failed to report test results: "+(0,r.getErrorMessage)(e)}],isError:!0}}},exports.handleTestingReference=async function(e,t,r,n,o){return o.startTimer("tool_execution_time")({success:!0}),{content:[{type:"text",text:'# Test Process Design Guide\n\n## Overview\n\nA test process defines how to verify code changes for a project. It consists of:\n- **Setup**: Commands to prepare the environment (install deps, link node_modules, etc.) \u2014 runs once per worktree\n- **Commands**: The actual test commands (build, lint, test, etc.) \u2014 the verification steps\n\n## Command Properties\n\n- **name**: Human-readable name (e.g., "Build", "Unit Tests")\n- **command**: Shell command to run (e.g., `npm test`)\n- **required** (default: true): If a required command fails, the request won\'t be merged\n- **timeout** (default: 300s): Maximum execution time. Exit code 0 = pass, non-zero = fail.\n\n## Scope (Monorepo Support)\n\nSet `scope` to a path prefix to limit when a process runs:\n- `scope: "packages/api/"` \u2192 only runs when files under packages/api/ change\n- omit `scope` \u2192 runs for all changes (global process)\n\nMultiple active processes per project are supported.\n\n## Common Patterns\n\n### Node.js / TypeScript\n```json\n{\n "setup": ["ln -s ../../node_modules node_modules"],\n "commands": [\n { "name": "Build", "command": "npm run build" },\n { "name": "Lint", "command": "npm run lint", "required": false },\n { "name": "Unit Tests", "command": "npm test" }\n ]\n}\n```\n\n### Python\n```json\n{\n "setup": ["pip install -e \'.[dev]\'"],\n "commands": [\n { "name": "Type Check", "command": "mypy src/" },\n { "name": "Lint", "command": "ruff check .", "required": false },\n { "name": "Tests", "command": "pytest" }\n ]\n}\n```\n\n### Go\n```json\n{\n "setup": ["go mod download"],\n "commands": [\n { "name": "Build", "command": "go build ./..." },\n { "name": "Vet", "command": "go vet ./...", "required": false },\n { "name": "Tests", "command": "go test ./..." }\n ]\n}\n```\n\n### Rust\n```json\n{\n "setup": ["cargo fetch"],\n "commands": [\n { "name": "Build", "command": "cargo build" },\n { "name": "Clippy", "command": "cargo clippy -- -D warnings", "required": false },\n { "name": "Tests", "command": "cargo test" }\n ]\n}\n```\n\n## Project Analysis Checklist\n\nWhen setting up testing for a new project, check for:\n1. **package.json** \u2192 Node.js/TypeScript (check "scripts" for test/build/lint commands)\n2. **Cargo.toml** \u2192 Rust\n3. **go.mod** \u2192 Go\n4. **pyproject.toml** / **requirements.txt** \u2192 Python\n5. **Makefile** \u2192 Custom build system (check for test/build targets)\n6. **.github/workflows/** \u2192 Existing CI configuration (great source of test commands)\n7. **.gitlab-ci.yml** \u2192 GitLab CI\n8. **tsconfig.json** \u2192 TypeScript type checking\n9. **.eslintrc** / **biome.json** \u2192 Linting configuration\n10. **vitest.config.ts** / **jest.config.ts** / **pytest.ini** \u2192 Test framework configuration'}]}},exports.handleTestingProcessDelete=async function(e,t,n,o,s){const i=s.startTimer("tool_execution_time")
1433
- try{return await n.proxyDelete(`/api/projects/${t.projectId}/test-processes/${e.processId}`),o.info("Deleted test process",{processId:e.processId}),i({success:!0}),{content:[{type:"text",text:`Deleted test process ${e.processId}.`}]}}catch(e){return i({success:!1,error:"api_error"}),{content:[{type:"text",text:"Failed to delete test process: "+(0,r.getErrorMessage)(e)}],isError:!0}}},exports.handleTests=async function(e,n,i,a,c,l){const d=c.startTimer("tool_execution_time")
1434
- try{if("list"===e.operation){const e=await i.proxyGet(`/api/projects/${n.projectId}/test-processes`),t=e?.data||e||[],r=Array.isArray(t)?t:[]
1423
+ return d({success:!0}),{content:[{type:"text",text:`## ${"setup"===e.commandName?"Setup":e.commandName} \u2014 ${E?"PASSED":"FAILED"}\n\n${S}`}]}}catch(e){return d({success:!1,error:"execution_error"}),{content:[{type:"text",text:"Failed to execute test command: "+(0,r.getErrorMessage)(e)}],isError:!0}}},exports.handleReportTestResults=async function(e,a,c,l,d,u){const h=d.startTimer("tool_execution_time")
1424
+ try{const{resolveRequestIdOrError:p}=await Promise.resolve().then(t.bind(t,9032)),m=await p(c,l,u?.sessionId,"Cannot report test results: this session is not associated with a request.")
1425
+ if(!m.ok)return h({success:!1,error:"no_request_association"}),m.errorResult
1426
+ const g=m.requestId,f=await c.proxyGet(`/api/projects/${a.projectId}/requests/${g}/test-runs`),y=f?.data||f||[],w=Array.isArray(y)?y:[],I=w.find((e=>"pending"===e.status||"running"===e.status))||w[0]
1427
+ if(!I)return h({success:!1,error:"no_test_run"}),{content:[{type:"text",text:`No test run found for request ${g}.`}],isError:!0}
1428
+ const b=I.id
1429
+ await c.proxyPatch(`/api/projects/${a.projectId}/test-runs/${b}`,{status:e.status,results:e.results})
1430
+ const k=e.results.acceptanceCriteria
1431
+ if(k&&k.length>0)try{const e=c.getRequestClient(),t=await e.getRequest(g),r=t?.acceptanceCriteria||[]
1432
+ if(r.length>0){const e=(0,n.buildCriteriaKeyMap)(r),t=k.filter((t=>"verified"===t.status&&!!t.key&&e.has(t.key))).map((t=>({criterionId:e.get(t.key).id,evidence:t.evidence,testRunId:b})))
1433
+ t.length>0&&await c.proxyPost(`/api/projects/${a.projectId}/requests/${g}/verify-criteria`,{verifications:t,dimension:"test"})}}catch(e){l.warn("Failed to persist test-dimension criteria verdicts",{error:(0,r.getErrorMessage)(e)})}I.worktreePath&&fetch((0,i.getActionServerBaseUrl)()+"/api/release-pool-slot",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({worktreePath:I.worktreePath}),signal:AbortSignal.timeout(5e3)}).catch((e=>l.debug("release-pool-slot fire-and-forget failed (non-fatal)",{error:(0,r.getErrorMessage)(e)})))
1434
+ const v="passed"===e.status?"\u2705":"failed"===e.status?"\u274c":"\u26a0\ufe0f"
1435
+ let C=!1,_=!1
1436
+ try{const t=c.getRequestClient()
1437
+ if("passed"===e.status){const e=await t.updateRequest(g,{status:"ready_for_merge"})
1438
+ _="ready_for_review"===e?.status,l.info("Tests passed",{requestId:g,status:e?.status})}else{const{escalate:e,failedRounds:r}=(0,o.decideTestRoundCap)(w,b,o.TEST_MAX_ROUNDS)
1439
+ e?(await t.updateRequest(g,{status:"ready_for_review"}),C=!0,l.warn("Test gate cap reached; escalating to human review",{requestId:g,failedRounds:r,cap:o.TEST_MAX_ROUNDS})):(await t.updateRequest(g,{status:"in_progress"}),l.info("Tests failed, set request back to in_progress",{requestId:g,failedRounds:r}))}}catch(e){l.warn("Failed to update request status after test results",{requestId:g,error:(0,r.getErrorMessage)(e)})}const E="passed"===e.status?"\n\nAll tests passed \u2014 you can now call `merge` to finalize this request.":"\n\nTests failed \u2014 fix the issues and call `complete` again.",S=`${v} Test run ${e.status} for request ${g}\n\n${e.results.summary}${C?"\n\nTests failed repeatedly; escalated for human review. Stop here; do not call complete again.":_?"\n\nAll tests passed. Human sign-off is required, so the request now awaits a maintainer to finalize in the UI. Stop here; do not call merge.":E}`
1440
+ try{const t=await(0,s.handleSessionMessageOperations)({operation:"send",type:"report",status:"passed"===e.status?"success":"failure",summary:S,details:e.results.commands?.map((e=>`${"passed"===e.status?"\u2705":"\u274c"} ${e.name}: ${e.status}${e.diagnosis?" - "+e.diagnosis:""}`)).join("\n"),finish:!0},a,l,d,c,u),r=t?.content?.[0]?.text||"unknown",n=t?.isError
1441
+ return h({success:!0}),{content:[{type:"text",text:`Test run for request ${g} reported as **${e.status}**.${n?" Parent notification FAILED: "+r:" Parent session notified."}\n\n${e.results.summary}`}]}}catch(t){return h({success:!0}),{content:[{type:"text",text:`Test run for request ${g} reported as **${e.status}** but failed to notify parent: ${(0,r.getErrorMessage)(t)}\n\n${e.results.summary}`}]}}}catch(e){return h({success:!1,error:"api_error"}),{content:[{type:"text",text:"Failed to report test results: "+(0,r.getErrorMessage)(e)}],isError:!0}}},exports.handleTestingReference=async function(e,t,r,n,o){return o.startTimer("tool_execution_time")({success:!0}),{content:[{type:"text",text:'# Test Process Design Guide\n\n## Overview\n\nA test process defines how to verify code changes for a project. It consists of:\n- **Setup**: Commands to prepare the environment (install deps, link node_modules, etc.) \u2014 runs once per worktree\n- **Commands**: The actual test commands (build, lint, test, etc.) \u2014 the verification steps\n\n## Command Properties\n\n- **name**: Human-readable name (e.g., "Build", "Unit Tests")\n- **command**: Shell command to run (e.g., `npm test`)\n- **required** (default: true): If a required command fails, the request won\'t be merged\n- **timeout** (default: 300s): Maximum execution time. Exit code 0 = pass, non-zero = fail.\n\n## Scope (Monorepo Support)\n\nSet `scope` to a path prefix to limit when a process runs:\n- `scope: "packages/api/"` \u2192 only runs when files under packages/api/ change\n- omit `scope` \u2192 runs for all changes (global process)\n\nMultiple active processes per project are supported.\n\n## Common Patterns\n\n### Node.js / TypeScript\n```json\n{\n "setup": ["ln -s ../../node_modules node_modules"],\n "commands": [\n { "name": "Build", "command": "npm run build" },\n { "name": "Lint", "command": "npm run lint", "required": false },\n { "name": "Unit Tests", "command": "npm test" }\n ]\n}\n```\n\n### Python\n```json\n{\n "setup": ["pip install -e \'.[dev]\'"],\n "commands": [\n { "name": "Type Check", "command": "mypy src/" },\n { "name": "Lint", "command": "ruff check .", "required": false },\n { "name": "Tests", "command": "pytest" }\n ]\n}\n```\n\n### Go\n```json\n{\n "setup": ["go mod download"],\n "commands": [\n { "name": "Build", "command": "go build ./..." },\n { "name": "Vet", "command": "go vet ./...", "required": false },\n { "name": "Tests", "command": "go test ./..." }\n ]\n}\n```\n\n### Rust\n```json\n{\n "setup": ["cargo fetch"],\n "commands": [\n { "name": "Build", "command": "cargo build" },\n { "name": "Clippy", "command": "cargo clippy -- -D warnings", "required": false },\n { "name": "Tests", "command": "cargo test" }\n ]\n}\n```\n\n## Project Analysis Checklist\n\nWhen setting up testing for a new project, check for:\n1. **package.json** \u2192 Node.js/TypeScript (check "scripts" for test/build/lint commands)\n2. **Cargo.toml** \u2192 Rust\n3. **go.mod** \u2192 Go\n4. **pyproject.toml** / **requirements.txt** \u2192 Python\n5. **Makefile** \u2192 Custom build system (check for test/build targets)\n6. **.github/workflows/** \u2192 Existing CI configuration (great source of test commands)\n7. **.gitlab-ci.yml** \u2192 GitLab CI\n8. **tsconfig.json** \u2192 TypeScript type checking\n9. **.eslintrc** / **biome.json** \u2192 Linting configuration\n10. **vitest.config.ts** / **jest.config.ts** / **pytest.ini** \u2192 Test framework configuration'}]}},exports.handleTestingProcessDelete=async function(e,t,n,o,s){const i=s.startTimer("tool_execution_time")
1442
+ try{return await n.proxyDelete(`/api/projects/${t.projectId}/test-processes/${e.processId}`),o.info("Deleted test process",{processId:e.processId}),i({success:!0}),{content:[{type:"text",text:`Deleted test process ${e.processId}.`}]}}catch(e){return i({success:!1,error:"api_error"}),{content:[{type:"text",text:"Failed to delete test process: "+(0,r.getErrorMessage)(e)}],isError:!0}}},exports.handleTests=async function(e,n,o,s,c,l){const d=c.startTimer("tool_execution_time")
1443
+ try{if("list"===e.operation){const e=await o.proxyGet(`/api/projects/${n.projectId}/test-processes`),t=e?.data||e||[],r=Array.isArray(t)?t:[]
1435
1444
  if(0===r.length)return d({success:!0}),{content:[{type:"text",text:'No test processes registered for this project. Use `tests` with `operation: "setup"` to spawn an interactive setup session that helps configure one.'}]}
1436
- const o=r.map((e=>`**${e.name}** (${e.id})${e.scope?` [scope: ${e.scope}]`:""}\n ${e.isActive?"\u2705 Active":"\u23f8 Inactive"} \u2014 ${e.config?.commands?.length||0} commands${e.description?"\n "+e.description:""}`)).join("\n\n")
1437
- return d({success:!0}),{content:[{type:"text",text:`## Test Processes (${r.length})\n\n${o}`}]}}if("run"===e.operation){let c,u=[]
1438
- try{const e=await i.proxyGet(`/api/projects/${n.projectId}/test-processes`),t=e?.data||e||[]
1439
- u=Array.isArray(t)?t:[]}catch(e){a.warn("tests run: existence check failed; proceeding optimistically",{error:(0,r.getErrorMessage)(e)})}if(0===u.length)return d({success:!1,error:"no_process"}),{content:[{type:"text",text:'No test process registered for this project. Run `tests` with `operation: "setup"` first to bootstrap one.'}],isError:!0}
1445
+ const s=r.map((e=>`**${e.name}** (${e.id})${e.scope?` [scope: ${e.scope}]`:""}\n ${e.isActive?"\u2705 Active":"\u23f8 Inactive"} \u2014 ${e.config?.commands?.length||0} commands${e.description?"\n "+e.description:""}`)).join("\n\n")
1446
+ return d({success:!0}),{content:[{type:"text",text:`## Test Processes (${r.length})\n\n${s}`}]}}if("run"===e.operation){let c,u=[]
1447
+ try{const e=await o.proxyGet(`/api/projects/${n.projectId}/test-processes`),t=e?.data||e||[]
1448
+ u=Array.isArray(t)?t:[]}catch(e){s.warn("tests run: existence check failed; proceeding optimistically",{error:(0,r.getErrorMessage)(e)})}if(0===u.length)return d({success:!1,error:"no_process"}),{content:[{type:"text",text:'No test process registered for this project. Run `tests` with `operation: "setup"` first to bootstrap one.'}],isError:!0}
1440
1449
  const h=e.process
1441
1450
  if(h){const e=h.toLowerCase(),t=u.find((t=>t.id===h||"string"==typeof t.name&&t.name.toLowerCase()===e))
1442
1451
  if(!t){const e=u.map((e=>`- ${e.name} (${e.id})`)).join("\n")
1443
- return d({success:!1,error:"process_not_found"}),{content:[{type:"text",text:`No test process matched "${h}". Available:\n${e}`}],isError:!0}}c=[t.id]}const p=l?.sessionId,{resolveRequestIdOrError:m}=await Promise.resolve().then(t.bind(t,9032)),g=await m(i,a,p,"tests run requires an associated request \u2014 your session is not currently linked to one. Open or create a request, then try again.")
1452
+ return d({success:!1,error:"process_not_found"}),{content:[{type:"text",text:`No test process matched "${h}". Available:\n${e}`}],isError:!0}}c=[t.id]}const p=l?.sessionId,{resolveRequestIdOrError:m}=await Promise.resolve().then(t.bind(t,9032)),g=await m(o,s,p,"tests run requires an associated request \u2014 your session is not currently linked to one. Open or create a request, then try again.")
1444
1453
  if(!g.ok)return d({success:!1,error:"no_request"}),g.errorResult
1445
1454
  const f=g.requestId,y=e.name?[e.name]:void 0
1446
- try{const e=await fetch((0,o.getActionServerBaseUrl)()+"/api/run-tests-now",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({requestId:f,parentSessionId:p,testProcessIds:c,commandNames:y}),signal:AbortSignal.timeout(15e3)})
1455
+ try{const e=await fetch((0,i.getActionServerBaseUrl)()+"/api/run-tests-now",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({requestId:f,parentSessionId:p,testProcessIds:c,commandNames:y}),signal:AbortSignal.timeout(15e3)})
1447
1456
  if(!e.ok){const t=await e.text().catch((()=>""))
1448
1457
  return d({success:!1,error:"run_failed"}),{content:[{type:"text",text:`tests run failed: action server returned ${e.status}: ${t.slice(0,300)}`}],isError:!0}}const t=await e.json()
1449
1458
  if(!t.success||!t.data)return d({success:!1,error:"run_failed"}),{content:[{type:"text",text:"tests run failed: "+(t.error||"unknown error")}],isError:!0}
1450
1459
  const r=t.data.spawnedSessionIds?.[0],n=y?` (filtered to: ${y.join(", ")})`:""
1451
- return d({success:!0}),{content:[{type:"text",text:`Test run started${n}.\nSpawned ${t.data.agentsSpawned} runner(s) for ${t.data.processCount} process(es). Test runs: ${t.data.testRunIds.join(", ")}.\n\nThe auto-tester is executing the registered commands. You will receive a notification on your next turn when results are reported \u2014 continue with other work in the meantime.`}],...(0,s.alchemyMeta)({kind:"spawn-subagent",spawnedSessionId:r,agentType:"auto-tester",requestId:f,description:y?"Test run: "+y.join(", "):"Test run"})}}catch(e){return d({success:!1,error:"fetch_error"}),{content:[{type:"text",text:"tests run failed: could not reach action server: "+(0,r.getErrorMessage)(e)}],isError:!0}}}if("setup"===e.operation){let e
1452
- try{const t=await i.proxyGet(`/api/projects/${n.projectId}/test-processes`),r=t?.data||t||[],o=Array.isArray(r)?r:[]
1453
- if(o.length>0){const t=o.map((e=>`- ${e.name}${e.scope?` (scope: ${e.scope})`:""} \u2014 ${e.config?.commands?.length||0} command(s), ${e.isActive?"active":"inactive"}`)).join("\n")
1454
- e=`${o.length} test process(es) already registered for this project:\n${t}\n\nPick a distinct name AND a non-overlapping scope for the new process. Confirm with the user what they want this new process to cover before registering.`}}catch(e){a.warn("tests setup: existing-processes lookup failed; proceeding without hint",{error:(0,r.getErrorMessage)(e)})}const{sessionIdentity:c}=await Promise.resolve().then(t.bind(t,6688)),u=l?.sessionId,h=u?await c.getSessionRequestId(i,a,u):void 0
1455
- try{const t=await fetch((0,o.getActionServerBaseUrl)()+"/api/setup-tests",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({requestId:h,parentSessionId:u,context:e}),signal:AbortSignal.timeout(15e3)})
1460
+ return d({success:!0}),{content:[{type:"text",text:`Test run started${n}.\nSpawned ${t.data.agentsSpawned} runner(s) for ${t.data.processCount} process(es). Test runs: ${t.data.testRunIds.join(", ")}.\n\nThe auto-tester is executing the registered commands. You will receive a notification on your next turn when results are reported \u2014 continue with other work in the meantime.`}],...(0,a.alchemyMeta)({kind:"spawn-subagent",spawnedSessionId:r,agentType:"auto-tester",requestId:f,description:y?"Test run: "+y.join(", "):"Test run"})}}catch(e){return d({success:!1,error:"fetch_error"}),{content:[{type:"text",text:"tests run failed: could not reach action server: "+(0,r.getErrorMessage)(e)}],isError:!0}}}if("setup"===e.operation){let e
1461
+ try{const t=await o.proxyGet(`/api/projects/${n.projectId}/test-processes`),r=t?.data||t||[],s=Array.isArray(r)?r:[]
1462
+ if(s.length>0){const t=s.map((e=>`- ${e.name}${e.scope?` (scope: ${e.scope})`:""} \u2014 ${e.config?.commands?.length||0} command(s), ${e.isActive?"active":"inactive"}`)).join("\n")
1463
+ e=`${s.length} test process(es) already registered for this project:\n${t}\n\nPick a distinct name AND a non-overlapping scope for the new process. Confirm with the user what they want this new process to cover before registering.`}}catch(e){s.warn("tests setup: existing-processes lookup failed; proceeding without hint",{error:(0,r.getErrorMessage)(e)})}const{sessionIdentity:c}=await Promise.resolve().then(t.bind(t,6688)),u=l?.sessionId,h=u?await c.getSessionRequestId(o,s,u):void 0
1464
+ try{const t=await fetch((0,i.getActionServerBaseUrl)()+"/api/setup-tests",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({requestId:h,parentSessionId:u,context:e}),signal:AbortSignal.timeout(15e3)})
1456
1465
  if(!t.ok){const e=await t.text().catch((()=>""))
1457
1466
  return d({success:!1,error:"spawn_failed"}),{content:[{type:"text",text:`tests setup failed: action server returned ${t.status}: ${e.slice(0,300)}`}],isError:!0}}const r=await t.json()
1458
- return r.success&&r.data?(d({success:!0}),{content:[{type:"text",text:`Test setup session started.\nWorktree: ${r.data.worktreePath} (branch: ${r.data.branch})\n\nThe test-architect agent is now interviewing the user. You will receive a notification on your next turn when configuration completes \u2014 continue with other work in the meantime.`}],...(0,s.alchemyMeta)({kind:"spawn-subagent",spawnedSessionId:r.data.sessionId,agentType:"test-architect",requestId:h??void 0,description:"Test setup"})}):(d({success:!1,error:"spawn_failed"}),{content:[{type:"text",text:"tests setup failed: "+(r.error||"unknown error")}],isError:!0})}catch(e){return d({success:!1,error:"fetch_error"}),{content:[{type:"text",text:"tests setup failed: could not reach action server: "+(0,r.getErrorMessage)(e)}],isError:!0}}}return d({success:!1,error:"unknown_operation"}),{content:[{type:"text",text:"Unknown operation: "+e.operation}],isError:!0}}catch(e){return d({success:!1,error:"api_error"}),{content:[{type:"text",text:"tests failed: "+(0,r.getErrorMessage)(e)}],isError:!0}}}
1459
- const r=t(8824),n=t(9072),o=t(9414),s=t(2272),i=t(1375)},2085(e,exports,t){"use strict"
1467
+ return r.success&&r.data?(d({success:!0}),{content:[{type:"text",text:`Test setup session started.\nWorktree: ${r.data.worktreePath} (branch: ${r.data.branch})\n\nThe test-architect agent is now interviewing the user. You will receive a notification on your next turn when configuration completes \u2014 continue with other work in the meantime.`}],...(0,a.alchemyMeta)({kind:"spawn-subagent",spawnedSessionId:r.data.sessionId,agentType:"test-architect",requestId:h??void 0,description:"Test setup"})}):(d({success:!1,error:"spawn_failed"}),{content:[{type:"text",text:"tests setup failed: "+(r.error||"unknown error")}],isError:!0})}catch(e){return d({success:!1,error:"fetch_error"}),{content:[{type:"text",text:"tests setup failed: could not reach action server: "+(0,r.getErrorMessage)(e)}],isError:!0}}}return d({success:!1,error:"unknown_operation"}),{content:[{type:"text",text:"Unknown operation: "+e.operation}],isError:!0}}catch(e){return d({success:!1,error:"api_error"}),{content:[{type:"text",text:"tests failed: "+(0,r.getErrorMessage)(e)}],isError:!0}}}
1468
+ const r=t(8824),n=t(5454),o=t(7499),s=t(9072),i=t(9414),a=t(2272),c=t(1375)},2085(e,exports,t){"use strict"
1460
1469
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.getRequestTimelineForFile=async function(e,t,s,i){let a=`# File History: ${t}\n\n`
1461
1470
  a+="**Context**: Main branch (request-level view)\n\n"
1462
1471
  try{const o=await e.log({file:t,n:100})
@@ -1620,26 +1629,26 @@ const e=await(0,h.commitUserChangesBeforeToolOperation)(r.git,"move-files",{requ
1620
1629
  e.userChangesCommitted&&(f.debug("[MOVE-FILES] User changes committed before move operations",{commitHash:e.commitHash?.substring(0,8),filesCommitted:e.filesCommitted}),f.info("User changes committed before move-files operation",{taskId:E,moveOperationsCount:q.length,userCommitHash:e.commitHash?.substring(0,8),userFilesCommitted:e.filesCommitted}),M={hadUserMods:!0,userModsCommitHash:e.commitHash})}catch(e){f.debug("[MOVE-FILES] Pre-tool commit failed, continuing with move operations",{error:e instanceof Error?e.message:e+""}),f.warn("Pre-tool user changes commit failed, continuing with move operation",{taskId:E,moveOperationsCount:q.length,error:e instanceof Error?e.message:e+""})}f.debug("[MOVE-FILES] Proceeding with move operations",{operationCount:q.length}),b=l.FileOperationPhase.RECORD_CREATION,m.incrementCounter("move_files_phase",1,{phase:b})
1621
1630
  const{toolRecordId:$,localToolRecordId:O}=await l.createFileOperationRecords(E,P,r.projectId,k.recordType,q[0].source,{operationCount:u.length,operations:q.map((e=>({source:e.source,destination:e.destination}))),userComment:v,hadUserMods:M.hadUserMods,userModsCommitHash:M.userModsCommitHash},n,o,f,m,k.metricsPrefix,g)
1622
1631
  w=$,I=$,b=k.fileOperationPhase,m.incrementCounter("move_files_phase",1,{phase:b})
1623
- const D=[]
1624
- let A=0
1632
+ const A=[]
1633
+ let D=0
1625
1634
  for(const e of q){const t={operation:{source:e.source,destination:e.destination},success:!1}
1626
1635
  try{try{await a.default.access(e.fullSource)}catch(t){throw Error("Source path not accessible: "+e.source)}const r=(await a.default.stat(e.fullSource)).isDirectory()
1627
1636
  t.isDirectory=r
1628
1637
  const n=c.default.dirname(e.fullDestination)
1629
- try{await a.default.mkdir(n,{recursive:!0})}catch(e){throw Error("Failed to create destination directory: "+n)}try{throw await a.default.access(e.fullDestination),Error("Destination already exists: "+e.destination)}catch(e){if(!(e instanceof Error&&e.message.includes("ENOENT")))throw e}await a.default.rename(e.fullSource,e.fullDestination),t.success=!0,A++,f.info("Successfully moved "+(r?"directory":"file"),{source:e.source,destination:e.destination,isDirectory:r})}catch(r){t.error=(0,p.getErrorMessage)(r),f.warn(`Failed to move ${e.source} to ${e.destination}`,{error:t.error})}D.push(t)}const F=Date.now()-y
1630
- m.recordValue("move_files_time",F),m.incrementCounter("move_files_operations_total",u.length),m.incrementCounter("move_files_operations_success",A),m.incrementCounter("move_files_operations_failed",u.length-A),A===u.length?m.incrementCounter("move_files_success",1):A>0?m.incrementCounter("move_files_partial_success",1):m.incrementCounter("move_files_total_failure",1)
1638
+ try{await a.default.mkdir(n,{recursive:!0})}catch(e){throw Error("Failed to create destination directory: "+n)}try{throw await a.default.access(e.fullDestination),Error("Destination already exists: "+e.destination)}catch(e){if(!(e instanceof Error&&e.message.includes("ENOENT")))throw e}await a.default.rename(e.fullSource,e.fullDestination),t.success=!0,D++,f.info("Successfully moved "+(r?"directory":"file"),{source:e.source,destination:e.destination,isDirectory:r})}catch(r){t.error=(0,p.getErrorMessage)(r),f.warn(`Failed to move ${e.source} to ${e.destination}`,{error:t.error})}A.push(t)}const F=Date.now()-y
1639
+ m.recordValue("move_files_time",F),m.incrementCounter("move_files_operations_total",u.length),m.incrementCounter("move_files_operations_success",D),m.incrementCounter("move_files_operations_failed",u.length-D),D===u.length?m.incrementCounter("move_files_success",1):D>0?m.incrementCounter("move_files_partial_success",1):m.incrementCounter("move_files_total_failure",1)
1631
1640
  let N=null
1632
- if(A>0){b=l.FileOperationPhase.GIT_COMMIT,m.incrementCounter("move_files_phase",1,{phase:b})
1633
- const e=D.filter((e=>e.success)),t=1===e.length?`Move ${e[0].isDirectory?"directory":"file"} from ${e[0].operation.source} to ${e[0].operation.destination} - ${v}`:`Move ${e.length} ${1===e.length?"item":"items"} - ${v}`
1641
+ if(D>0){b=l.FileOperationPhase.GIT_COMMIT,m.incrementCounter("move_files_phase",1,{phase:b})
1642
+ const e=A.filter((e=>e.success)),t=1===e.length?`Move ${e[0].isDirectory?"directory":"file"} from ${e[0].operation.source} to ${e[0].operation.destination} - ${v}`:`Move ${e.length} ${1===e.length?"item":"items"} - ${v}`
1634
1643
  N=await l.commitToGit(E,q[0].destination,$,O,t,r,f,m,k.metricsPrefix)}b=l.FileOperationPhase.RECORD_UPDATE_SUCCESS,m.incrementCounter("move_files_phase",1,{phase:b})
1635
- const z=D.filter((e=>e.success)),L=D.filter((e=>!e.success))
1636
- await l.updateFileOperationRecords(E,w,I,q[0].source,k.recordType,A===u.length?`Successfully moved all ${u.length} items`:`Moved ${A}/${u.length} items successfully`,{status:A===u.length?"completed":"partial_success",commitHash:N?.commitHash,previousCommitHash:N?.previousCommitHash,operationCount:u.length,successCount:A,failureCount:u.length-A,successfulOperations:z.map((e=>({source:e.operation.source,destination:e.operation.destination,isDirectory:e.isDirectory}))),failedOperations:L.map((e=>({source:e.operation.source,destination:e.operation.destination,error:e.error}))),userComment:v,hadUserMods:M.hadUserMods,userModsCommitHash:M.userModsCommitHash},A===u.length?`Successfully moved all ${u.length} items`:`Moved ${A}/${u.length} items successfully`,n,o,f,m,k.metricsPrefix)
1644
+ const z=A.filter((e=>e.success)),L=A.filter((e=>!e.success))
1645
+ await l.updateFileOperationRecords(E,w,I,q[0].source,k.recordType,D===u.length?`Successfully moved all ${u.length} items`:`Moved ${D}/${u.length} items successfully`,{status:D===u.length?"completed":"partial_success",commitHash:N?.commitHash,previousCommitHash:N?.previousCommitHash,operationCount:u.length,successCount:D,failureCount:u.length-D,successfulOperations:z.map((e=>({source:e.operation.source,destination:e.operation.destination,isDirectory:e.isDirectory}))),failedOperations:L.map((e=>({source:e.operation.source,destination:e.operation.destination,error:e.error}))),userComment:v,hadUserMods:M.hadUserMods,userModsCommitHash:M.userModsCommitHash},D===u.length?`Successfully moved all ${u.length} items`:`Moved ${D}/${u.length} items successfully`,n,o,f,m,k.metricsPrefix)
1637
1646
  const H=r.getCurrentBranchContext?await r.getCurrentBranchContext():{branch:null,requestId:null,isMain:!0}
1638
1647
  let B=""
1639
- if(A===u.length)B=`Successfully moved all ${u.length} ${1===u.length?"item":"items"}`
1640
- else if(A>0){const e=D.filter((e=>!e.success))
1641
- B=`Successfully moved ${A}/${u.length} items.\n\nFailed operations:\n`+e.map((e=>`\u2022 ${e.operation.source} \u2192 ${e.operation.destination}: ${e.error}`)).join("\n")}else B="All move operations failed:\n"+D.map((e=>`\u2022 ${e.operation.source} \u2192 ${e.operation.destination}: ${e.error}`)).join("\n")
1642
- return N?.commitHash&&(B+="\nCommit: "+N.commitHash.substring(0,8)),H.requestId&&(B+="\nBranch: "+H.requestId),{content:[{type:"text",text:B}],isError:0===A}}catch(t){if(e.taskId&&o)try{const n=t instanceof d.NotFoundError||t instanceof d.ValidationError||t instanceof u.ZodError
1648
+ if(D===u.length)B=`Successfully moved all ${u.length} ${1===u.length?"item":"items"}`
1649
+ else if(D>0){const e=A.filter((e=>!e.success))
1650
+ B=`Successfully moved ${D}/${u.length} items.\n\nFailed operations:\n`+e.map((e=>`\u2022 ${e.operation.source} \u2192 ${e.operation.destination}: ${e.error}`)).join("\n")}else B="All move operations failed:\n"+A.map((e=>`\u2022 ${e.operation.source} \u2192 ${e.operation.destination}: ${e.error}`)).join("\n")
1651
+ return N?.commitHash&&(B+="\nCommit: "+N.commitHash.substring(0,8)),H.requestId&&(B+="\nBranch: "+H.requestId),{content:[{type:"text",text:B}],isError:0===D}}catch(t){if(e.taskId&&o)try{const n=t instanceof d.NotFoundError||t instanceof d.ValidationError||t instanceof u.ZodError
1643
1652
  await o.createToolRecord({...n?{}:{taskId:e.taskId},projectId:r.projectId,type:"move-files",metadata:{status:"failed",timestamp:(new Date).toISOString(),operationCount:e.operations?.length||0,operations:e.operations||[],userComment:e.comment,error:(0,p.getErrorMessage)(t),errorDetails:t instanceof Error?t.stack:t+"",phase:b,attemptedTaskId:e.taskId,taskIdValidationFailed:n},note:"Failed to move files - "+(0,p.getErrorMessage)(t)})}catch(t){f.error("Failed to create failure record for move-files operation",{error:t instanceof Error?t.message:t+"",taskId:e.taskId,operationCount:e.operations?.length||0})}const s=Date.now()-y
1644
1653
  if(m.recordValue("move_files_time",s),m.incrementCounter("move_files_errors",1),f.error("Error in move-files operation:",{error:(0,p.getErrorMessage)(t),time:s}),(w||I)&&e.taskId){b=l.FileOperationPhase.RECORD_UPDATE_FAILURE
1645
1654
  try{await l.updateFileOperationRecords(e.taskId,w,I,e.operations?.[0]?.source||"unknown",k.recordType,"Failed to move files",{status:"failed",error:(0,p.getErrorMessage)(t),operationCount:e.operations?.length||0,operations:e.operations||[],userComment:e.comment},"Failed to move files",n,o,f,m,k.metricsPrefix)}catch(t){f.error("Failed to update record with failure status",{taskId:e.taskId,recordId:w,error:t instanceof Error?t.message:t+""})}}return l.handleFileOperationError(t,b,f,m,e,k)}}
@@ -1651,7 +1660,7 @@ exports.def={name:"quick_manage",description:"WHEN TO USE: trivial single-file e
1651
1660
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.buildTaskEndpoint=function(e,t,r){const n=r?"/"+r.replace(/^\//,""):""
1652
1661
  return`/api/projects/${e}/tasks/${encodeURIComponent(t)}${n}`}},2462(e,exports,t){"use strict"
1653
1662
  Object.defineProperty(exports,"__esModule",{value:!0})
1654
- const r=t(2245),n=t(9080),o=t(5692),s=t(9030),i=t(2903),a=t(4607),c=t(9499),l=t(6770),d=t(8045),u=t(9697),h=t(9196),p=t(5459),m=t(4805),g=t(260),f=t(2283),y=t(4165),w=t(7834),I=t(9373),b=t(6111),k=t(700),v=t(1535),C=t(7257),_=t(9230),E=t(4150),S=t(6473),q=t(6309),T=t(4921),P=t(8850),x=t(3556),j=t(1391),R=t(6315),M=t(7713),$=t(6252),O=t(577),D=t(4206),A=t(213),F=t(7250),N=t(2425),z=t(1540),L=t(5767),H=t(4973),B=t(509),U=t(7613),W=t(8456),G=t(3322),V=t(5247),Q=t(8849),K=t(542);(0,r.registerTool)(n.def),(0,r.registerTool)(o.def),(0,r.registerTool)(s.def),(0,r.registerTool)(i.def),(0,r.registerTool)(a.def),(0,r.registerTool)(c.def),(0,r.registerTool)(l.def),(0,r.registerTool)(d.def),(0,r.registerTool)(u.def),(0,r.registerTool)(h.def),(0,r.registerTool)(p.def),(0,r.registerTool)(m.def),(0,r.registerTool)(g.def),(0,r.registerTool)(f.def),(0,r.registerTool)(y.def),(0,r.registerTool)(w.def),(0,r.registerTool)(I.def),(0,r.registerTool)(b.def),(0,r.registerTool)(k.def),(0,r.registerTool)(v.def),(0,r.registerTool)(E.def),(0,r.registerTool)(C.def),(0,r.registerTool)(_.def),(0,r.registerTool)(S.def),(0,r.registerTool)(q.def),(0,r.registerTool)(T.def),(0,r.registerTool)(P.def),(0,r.registerTool)(x.def),(0,r.registerTool)(j.def),(0,r.registerTool)(R.def),(0,r.registerTool)(M.def),(0,r.registerTool)($.def),(0,r.registerTool)(O.def),(0,r.registerTool)(D.def),(0,r.registerTool)(A.def),(0,r.registerTool)(F.def),(0,r.registerTool)(N.def),(0,r.registerTool)(z.def),(0,r.registerTool)(L.def),(0,r.registerTool)(H.def),(0,r.registerTool)(B.def),(0,r.registerTool)(U.def),(0,r.registerTool)(W.def),(0,r.registerTools)(G.metaDefs),(0,r.registerTools)(V.testingDefs),(0,r.registerTools)(Q.checkingDefs),(0,r.registerTools)(K.debugDefs),(0,t(2245).freezeRegistry)()},2478(e,exports){"use strict"
1663
+ const r=t(2245),n=t(9080),o=t(5692),s=t(9030),i=t(2903),a=t(4607),c=t(9499),l=t(6770),d=t(8045),u=t(9697),h=t(9196),p=t(5459),m=t(4805),g=t(260),f=t(2283),y=t(4165),w=t(7834),I=t(9373),b=t(6111),k=t(700),v=t(5837),C=t(1535),_=t(7257),E=t(9230),S=t(4150),q=t(6473),T=t(6309),P=t(4921),x=t(8850),j=t(3556),R=t(1391),M=t(6315),$=t(7713),O=t(6252),A=t(577),D=t(4206),F=t(213),N=t(7250),z=t(2425),L=t(1540),H=t(5767),B=t(4973),U=t(509),W=t(7613),G=t(8456),V=t(3322),Q=t(5247),K=t(8849),J=t(542);(0,r.registerTool)(n.def),(0,r.registerTool)(o.def),(0,r.registerTool)(s.def),(0,r.registerTool)(i.def),(0,r.registerTool)(a.def),(0,r.registerTool)(c.def),(0,r.registerTool)(l.def),(0,r.registerTool)(d.def),(0,r.registerTool)(u.def),(0,r.registerTool)(h.def),(0,r.registerTool)(p.def),(0,r.registerTool)(m.def),(0,r.registerTool)(g.def),(0,r.registerTool)(f.def),(0,r.registerTool)(y.def),(0,r.registerTool)(w.def),(0,r.registerTool)(I.def),(0,r.registerTool)(b.def),(0,r.registerTool)(k.def),(0,r.registerTool)(v.def),(0,r.registerTool)(C.def),(0,r.registerTool)(S.def),(0,r.registerTool)(_.def),(0,r.registerTool)(E.def),(0,r.registerTool)(q.def),(0,r.registerTool)(T.def),(0,r.registerTool)(P.def),(0,r.registerTool)(x.def),(0,r.registerTool)(j.def),(0,r.registerTool)(R.def),(0,r.registerTool)(M.def),(0,r.registerTool)($.def),(0,r.registerTool)(O.def),(0,r.registerTool)(A.def),(0,r.registerTool)(D.def),(0,r.registerTool)(F.def),(0,r.registerTool)(N.def),(0,r.registerTool)(z.def),(0,r.registerTool)(L.def),(0,r.registerTool)(H.def),(0,r.registerTool)(B.def),(0,r.registerTool)(U.def),(0,r.registerTool)(W.def),(0,r.registerTool)(G.def),(0,r.registerTools)(V.metaDefs),(0,r.registerTools)(Q.testingDefs),(0,r.registerTools)(K.checkingDefs),(0,r.registerTools)(J.debugDefs),(0,t(2245).freezeRegistry)()},2478(e,exports){"use strict"
1655
1664
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.requestConfig=void 0,exports.areTestsRequiredForCompletion=function(){return exports.requestConfig.requireTestsForCompletion},exports.setTestsRequiredForCompletion=function(e){exports.requestConfig.requireTestsForCompletion=e},exports.getCompletionMode=function(e){return e&&["auto-merge","ask-me","test-by-default","always-test"].includes(e)?e:"test-by-default"},exports.getReviewMode=function(e){return e&&["auto-finalize","manual-review"].includes(e)?e:"auto-finalize"},exports.getApprovalMode=function(e){return e&&["none","require-approval"].includes(e)?e:"none"},exports.requestConfig={requireTestsForCompletion:!1}},2537(e,exports,t){"use strict"
1656
1665
  async function r(e,t,r){if(v.has(r)&&C>=t){if(0===t)return 0
1657
1666
  try{return(await y.default.readFile(e,"utf-8")).split("\n").length}catch{return}}}function n(e,t,r){const n="depth"in e,o=n?e:r,s=n?e.isDirectory:e.isDirectory(),a=e.name,c=s?"[\u{1f4c1} DIR]":"[\u{1f4c4} FILE]"
@@ -2210,11 +2219,11 @@ if(R.length>0&&r.apiClient){const e=R.map((e=>e.documentId))
2210
2219
  try{await(0,m.ensureDocumentsNotLocked)(e,r.apiClient,y,c,"replace-strings-documents")}catch(e){if(e instanceof u.ValidationError)return{content:[{type:"text",text:e.message}],isError:!0}
2211
2220
  throw e}}k=d.FileOperationPhase.TASK_VALIDATION,c.incrementCounter("replace_strings_phase",1,{phase:k})
2212
2221
  const M=j.length>0?j[0].path:"document-operation",$=await d.validateTaskId(T,M,o,y,c,v.metricsPrefix),O=$?.requestId
2213
- let D=null
2214
- j.length>0&&O&&(D=await d.validateWorkItemStates(T,j[0].path,o,i,r,y,c,v.metricsPrefix))
2215
- let A={taskPromoted:!1,requestPromoted:!1}
2216
- j.length>0&&O&&D&&(A=await d.autoPromoteTaskStatus(T,$.status,O,D.request?.status,j[0].path,o,i,y,c,v.metricsPrefix),A.taskPromoted&&y.info("Auto-promoted task to in_progress for replace-strings operation",{taskId:T,requestId:O,previousStatus:$.status,newStatus:A.newTaskStatus,triggeringOperation:"replace-strings"}))
2217
- const F=await d.ensureCorrectWorkspaceContext({taskPromoted:A.taskPromoted,requestId:O,context:r,operation:"replace-strings",logger:y,metrics:c,metricsPrefix:v.metricsPrefix})
2222
+ let A=null
2223
+ j.length>0&&O&&(A=await d.validateWorkItemStates(T,j[0].path,o,i,r,y,c,v.metricsPrefix))
2224
+ let D={taskPromoted:!1,requestPromoted:!1}
2225
+ j.length>0&&O&&A&&(D=await d.autoPromoteTaskStatus(T,$.status,O,A.request?.status,j[0].path,o,i,y,c,v.metricsPrefix),D.taskPromoted&&y.info("Auto-promoted task to in_progress for replace-strings operation",{taskId:T,requestId:O,previousStatus:$.status,newStatus:D.newTaskStatus,triggeringOperation:"replace-strings"}))
2226
+ const F=await d.ensureCorrectWorkspaceContext({taskPromoted:D.taskPromoted,requestId:O,context:r,operation:"replace-strings",logger:y,metrics:c,metricsPrefix:v.metricsPrefix})
2218
2227
  if(!F.success)throw Error(F.error)
2219
2228
  if(j.length>0){y.debug("Re-validating file paths with correct workspace context",{operationCount:j.length,workspaceBasePath:r.workspaceManager?.getCurrentContext(r.currentSessionId).basePath})
2220
2229
  for(let e=0;e<j.length;e++){const t=j[e],n=P[e],{normalizedPath:o,fullPath:s}=d.validateAndNormalizePath(n.path,r)
@@ -2351,7 +2360,8 @@ let o=null,s=null},3545(e,exports){"use strict"
2351
2360
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.asInternal=exports.asExternal=void 0,exports.asExternal=e=>e,exports.asInternal=e=>e},3556(e,exports,t){"use strict"
2352
2361
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.def=void 0
2353
2362
  const r=t(6183),n=t(8317)
2354
- exports.def={name:"request_file_history",description:"Get file change history for an entire request. Supports summary mode (net diffs) or detailed mode (chronological operations). Can be filtered to specific task. Use query_history tool instead.",schema:r.QueryHistorySchema,annotations:{title:"Request File History",readOnlyHint:!0},hidden:!0,readOnly:!0,handler(e,t){return(0,n.handleRequestFileHistory)(e,t.context,t.apiClient,t.logger,t.metrics)}}},3611(e,exports,t){"use strict"
2363
+ exports.def={name:"request_file_history",description:"Get file change history for an entire request. Supports summary mode (net diffs) or detailed mode (chronological operations). Can be filtered to specific task. Use query_history tool instead.",schema:r.QueryHistorySchema,annotations:{title:"Request File History",readOnlyHint:!0},hidden:!0,readOnly:!0,handler(e,t){return(0,n.handleRequestFileHistory)(e,t.context,t.apiClient,t.logger,t.metrics)}}},3580(e,exports){"use strict"
2364
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.isAgentReviewMode=function(e){return"agent-review"===e||"agent-review-then-signoff"===e}},3611(e,exports,t){"use strict"
2355
2365
  var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
2356
2366
  Object.defineProperty(exports,"__esModule",{value:!0})
2357
2367
  const n=r(t(6928)),o=r(t(7252)),s=r(t(8577)),i=t(184),a=t(1213),c=t(3444),l=t(9955),d=t(2568),u=t(3102),h=t(1152),p=t(3884),m=t(7888),g=t(4139),f=t(6800),y=t(6539),w=t(8824),I=t(6975),b=t(1253),k=process.argv.slice(2),v=k[0]||process.cwd(),C=n.default.resolve(v),_=parseInt(process.env.LOCAL_SERVER_PORT||k[1]||"3002",10),E=(0,c.createEnvLogger)({serviceName:"mcp-http-server"});(0,c.isDebugToolsEnabled)()?E.info("HTTP server initializing",{baseDir:C,preferredPort:_,debugLogging:process.env.DEBUG_LOGGING,debugLevel:process.env.DEBUG_LEVEL}):E.info("HTTP server starting",{port:_})
@@ -2504,8 +2514,8 @@ if(e.isError)f.error("Request completion failed, attempting direct completion",{
2504
2514
  else{try{await w.updateRequest(T,{status:"completed",completionSummary:j}),f.info("Request finalized to completed",{requestId:T})}catch(e){f.warn("Failed to finalize request to completed, may be stuck at ready_for_merge",{requestId:T,error:(0,d.getErrorMessage)(e)})}M=!0,f.info("Request completed successfully",{requestId:T})}}catch(e){f.error("Request completion threw exception, attempting direct completion",{requestId:T,error:e instanceof Error?e.message:e+""})}if(!M)try{f.info("Attempting direct request completion via API",{requestId:T}),await w.updateRequest(T,{status:"completed",completionSummary:j}),M=!0,f.info("Request completed successfully via direct API call",{requestId:T})}catch(e){return f.error("Direct request completion also failed",{requestId:T,error:e instanceof Error?e.message:e+""}),{content:[{type:"text",text:`\u26a0\ufe0f Quick change operation succeeded but request completion failed.\n\n**Request:** ${T}\n**Status:** Still in 'in_progress' - please complete manually\n\nThe file operation was successful, but we couldn't automatically complete the request. Please use the complete-request tool manually.`}],isError:!0}}const $=Date.now()-y
2505
2515
  f.info("Quick fix completed",{requestId:T,duration:$,hasPartialFailure:x})
2506
2516
  const O=(0,n.readAlchemyMeta)(C)?.toolRecordId
2507
- let D
2508
- return D=x?`\u26a0\ufe0f Quick change completed with partial success\n\n**Request:** ${T}\n**Title:** ${v}\n**Operation:** ${t}\n**Duration:** ${$}ms\n**Status:** Some operations succeeded, some failed\n\nThe successful changes have been applied and automatically merged to main.\n**Active branch:** main\n\n**\u26a0\ufe0f Note:** This quick-fix had partial failures. See operation details below:\n\n`+q:`\u2705 Quick change completed successfully!\n\n**Request:** ${T}\n**Title:** ${v}\n**Operation:** ${t}\n**Duration:** ${$}ms\n\nThe change has been applied and automatically merged to main.\n**Active branch:** main`+(O?`\n[RECORD:${O}]`:""),S&&(D+="\n\n\u{1f4a1} **Recommendation:** You've created several quick-edits in a row. For better traceability and organization, consider creating a proper feature or bugfix request with multiple tasks for your next change."),{content:[{type:"text",text:D}],...(0,n.alchemyMeta)({kind:"quick-"+t,toolRecordId:O})}}catch(e){if(f.error("Quick change workflow failed after request creation, cleaning up",{requestId:T,error:(0,d.getErrorMessage)(e)}),P)try{await I.updateTaskStatus(P,"canceled"),f.info("Canceled task after workflow failure",{taskId:P})}catch(e){f.warn("Failed to cancel task during cleanup",{taskId:P,error:e instanceof Error?e.message:e+""})}try{await w.updateRequest(T,{status:"canceled"}),f.info("Canceled request after workflow failure",{requestId:T}),u.workspaceManager&&(await u.workspaceManager.cancelRequest(T),f.info("Cleaned up branch/worktree after workflow failure",{requestId:T}))}catch(e){f.warn("Failed to cancel request during cleanup",{requestId:T,error:e instanceof Error?e.message:e+""})}throw e}}catch(e){const t=Date.now()-y
2517
+ let A
2518
+ return A=x?`\u26a0\ufe0f Quick change completed with partial success\n\n**Request:** ${T}\n**Title:** ${v}\n**Operation:** ${t}\n**Duration:** ${$}ms\n**Status:** Some operations succeeded, some failed\n\nThe successful changes have been applied and automatically merged to main.\n**Active branch:** main\n\n**\u26a0\ufe0f Note:** This quick-fix had partial failures. See operation details below:\n\n`+q:`\u2705 Quick change completed successfully!\n\n**Request:** ${T}\n**Title:** ${v}\n**Operation:** ${t}\n**Duration:** ${$}ms\n\nThe change has been applied and automatically merged to main.\n**Active branch:** main`+(O?`\n[RECORD:${O}]`:""),S&&(A+="\n\n\u{1f4a1} **Recommendation:** You've created several quick-edits in a row. For better traceability and organization, consider creating a proper feature or bugfix request with multiple tasks for your next change."),{content:[{type:"text",text:A}],...(0,n.alchemyMeta)({kind:"quick-"+t,toolRecordId:O})}}catch(e){if(f.error("Quick change workflow failed after request creation, cleaning up",{requestId:T,error:(0,d.getErrorMessage)(e)}),P)try{await I.updateTaskStatus(P,"canceled"),f.info("Canceled task after workflow failure",{taskId:P})}catch(e){f.warn("Failed to cancel task during cleanup",{taskId:P,error:e instanceof Error?e.message:e+""})}try{await w.updateRequest(T,{status:"canceled"}),f.info("Canceled request after workflow failure",{requestId:T}),u.workspaceManager&&(await u.workspaceManager.cancelRequest(T),f.info("Cleaned up branch/worktree after workflow failure",{requestId:T}))}catch(e){f.warn("Failed to cancel request during cleanup",{requestId:T,error:e instanceof Error?e.message:e+""})}throw e}}catch(e){const t=Date.now()-y
2509
2519
  return f.error("Quick change failed",{error:(0,d.getErrorMessage)(e),duration:t}),e instanceof r.ValidationError?{content:[{type:"text",text:"\u274c "+e.message}],isError:!0}:{content:[{type:"text",text:"\u274c Quick change failed: "+(0,d.getErrorMessage)(e)}],isError:!0}}}
2510
2520
  const r=t(9860),n=t(2272),o=t(4079),s=t(9442),i=t(7684),a=t(2736),c=t(5715),l=t(7478),d=t(8824)},3807(module){"use strict"
2511
2521
  module.exports=require("simple-git")},3884(e,exports,t){"use strict"
@@ -2599,7 +2609,7 @@ const{enforceActiveTask:j}=await Promise.resolve().then(t.bind(t,3451)),R=await
2599
2609
  if("errorResult"in R)return R.errorResult
2600
2610
  const{taskId:M}=R,$=!!S,O=!!g
2601
2611
  I.info("Writing content to "+($?"document":"file"),{taskId:M,filePath:O?g:void 0,documentId:$?S:void 0,contentLength:"string"==typeof q?q.length:JSON.stringify(q).length,format:T,append:P,comment:x})
2602
- let D,A,F,N="",z=!1,L=!1
2612
+ let A,D,F,N="",z=!1,L=!1
2603
2613
  const H=O?g:void 0
2604
2614
  if($){if(r.apiClient)try{await(0,m.ensureDocumentNotLocked)(S,r.apiClient,I,y,"write_file-document")}catch(e){if(e instanceof u.ValidationError)return{content:[{type:"text",text:e.message}],isError:!0}
2605
2615
  throw e}if(!r.documentFileManager)throw Error("Document file manager not available")
@@ -2614,26 +2624,26 @@ O&&W&&G&&(V=await d.autoPromoteTaskStatus(M,U.status,W,G.request?.status,B,o,i,I
2614
2624
  const Q=await d.ensureCorrectWorkspaceContext({taskPromoted:V.taskPromoted,requestId:W,context:r,operation:"write_file",logger:I,metrics:y,metricsPrefix:E.metricsPrefix,requireRequestContext:O})
2615
2625
  if(!Q.success)throw Error(Q.error)
2616
2626
  if(Q.switched&&I.info("Workspace context successfully established",{requestId:W,branchName:Q.branchName,operation:"write_file"}),O&&H){const e=d.validateAndNormalizePath(H,r)
2617
- D=e.normalizedPath,A=e.fullPath,N=A
2618
- try{await c.default.access(A),L=!0}catch{L=!1,z=!0}I.debug("Path validated with correct workspace context",{rawPath:H,normalizedPath:D,fullPath:A,workspaceBasePath:r.workspaceManager?.getCurrentContext(r.currentSessionId).basePath})}if($&&W)try{await(0,h.validateRequestStatusForDocumentOperation)(W,i,I,y,"write_file-document")}catch(e){if(e instanceof u.ValidationError)return{content:[{type:"text",text:e.message}],isError:!0}
2627
+ A=e.normalizedPath,D=e.fullPath,N=D
2628
+ try{await c.default.access(D),L=!0}catch{L=!1,z=!0}I.debug("Path validated with correct workspace context",{rawPath:H,normalizedPath:A,fullPath:D,workspaceBasePath:r.workspaceManager?.getCurrentContext(r.currentSessionId).basePath})}if($&&W)try{await(0,h.validateRequestStatusForDocumentOperation)(W,i,I,y,"write_file-document")}catch(e){if(e instanceof u.ValidationError)return{content:[{type:"text",text:e.message}],isError:!0}
2619
2629
  throw e}_=d.FileOperationPhase.RECORD_CREATION,y.incrementCounter("write_file_phase",1,{phase:_})
2620
- const{toolRecordId:K}=await d.createFileOperationRecords(M,W,r.projectId,E.recordType,B,{operationType:$?"document":"file",documentId:$?S:void 0,documentFilename:F,filePath:O?D:void 0,contentLength:"string"==typeof q?q.length:JSON.stringify(q).length,format:T||"text",append:P||!1,wasFileCreated:!L,userComment:x},o,s,I,y,E.metricsPrefix,w)
2630
+ const{toolRecordId:K}=await d.createFileOperationRecords(M,W,r.projectId,E.recordType,B,{operationType:$?"document":"file",documentId:$?S:void 0,documentFilename:F,filePath:O?A:void 0,contentLength:"string"==typeof q?q.length:JSON.stringify(q).length,format:T||"text",append:P||!1,wasFileCreated:!L,userComment:x},o,s,I,y,E.metricsPrefix,w)
2621
2631
  let J
2622
2632
  if(v=K,C=K,_=E.fileOperationPhase,y.incrementCounter("write_file_phase",1,{phase:_}),"json"===T)if("string"==typeof q)try{JSON.parse(q),J=q}catch(e){throw new u.ValidationError("Content is not valid JSON")}else J=JSON.stringify(q,null,2)
2623
2633
  else J="string"==typeof q?q:JSON.stringify(q,null,2)
2624
2634
  let Y=J
2625
2635
  if(O&&r.git)try{I.debug("[WRITE-FILE] Checking for user changes before write operation")
2626
2636
  const e=await(0,p.commitUserChangesBeforeToolOperation)(r.git,"write_file",{requestId:r.workspaceManager?.getCurrentContext(r.currentSessionId)?.requestId,taskId:M},I,y)
2627
- e.userChangesCommitted&&(I.debug("[WRITE-FILE] User changes committed before write operation",{commitHash:e.commitHash?.substring(0,8),filesCommitted:e.filesCommitted}),I.info("User changes committed before write-file operation",{taskId:M,path:D,userCommitHash:e.commitHash?.substring(0,8),userFilesCommitted:e.filesCommitted}))}catch(e){I.debug("[WRITE-FILE] Pre-tool commit failed, continuing with write",{error:e instanceof Error?e.message:e+""}),I.warn("Pre-tool user changes commit failed, continuing with write operation",{taskId:M,path:D,error:e instanceof Error?e.message:e+""})}if(!N)throw Error("Target path not properly initialized")
2637
+ e.userChangesCommitted&&(I.debug("[WRITE-FILE] User changes committed before write operation",{commitHash:e.commitHash?.substring(0,8),filesCommitted:e.filesCommitted}),I.info("User changes committed before write-file operation",{taskId:M,path:A,userCommitHash:e.commitHash?.substring(0,8),userFilesCommitted:e.filesCommitted}))}catch(e){I.debug("[WRITE-FILE] Pre-tool commit failed, continuing with write",{error:e instanceof Error?e.message:e+""}),I.warn("Pre-tool user changes commit failed, continuing with write operation",{taskId:M,path:A,error:e instanceof Error?e.message:e+""})}if(!N)throw Error("Target path not properly initialized")
2628
2638
  if(I.debug("[WRITE-FILE] Proceeding with file write operation",{targetPath:N}),P&&L)try{Y=await c.default.readFile(N,"utf8")+J}catch(e){I.warn("Failed to read existing content for append",{error:e})}const Z=a.default.dirname(N)
2629
- if(await c.default.mkdir(Z,{recursive:!0}),await c.default.writeFile(N,Y,"utf8"),I.info("Content written successfully",{path:O?D:"document:"+S,finalLength:Y.length,append:P,wasCreated:z}),$){r.documentFileManager.markForSync(S)
2639
+ if(await c.default.mkdir(Z,{recursive:!0}),await c.default.writeFile(N,Y,"utf8"),I.info("Content written successfully",{path:O?A:"document:"+S,finalLength:Y.length,append:P,wasCreated:z}),$){r.documentFileManager.markForSync(S)
2630
2640
  try{await r.documentFileManager.syncDocument(S),I.info("Document synced successfully",{documentId:S})}catch(e){I.warn("Failed to sync document immediately",{documentId:S,error:e})}}let X,ee,te=null,re=!1
2631
2641
  if(O){if(_=d.FileOperationPhase.GIT_COMMIT,y.incrementCounter("write_file_phase",1,{phase:_}),r.workspaceManager){const e=r.workspaceManager.getCurrentContext(r.currentSessionId)
2632
- ee=e.branchName,re=e.permanentBranch,I.debug("Retrieved branch name from workspace manager",{branchName:ee,requestId:e.requestId,mode:e.mode,isMainBranch:e.isMainBranch})}try{const e=`${z?"Create":P?"Append to":"Update"} ${D} (${C})`,t=await d.commitToGit(M,D,C,C,e,r,I,y,E.metricsPrefix)
2633
- X=t.commitHash,te=t.previousCommitHash,I.info("Changes committed to git via shared utility",{path:D,commitMessage:e,commitHash:X?.substring(0,8),previousCommitHash:te?.substring(0,8),branchName:ee,toolRecordId:C})}catch(e){I.warn("Git operations failed via shared utility",{error:e})}}try{await s.updateToolRecord(C,{metadata:{path:O?D:"document:"+S,status:"completed",finalContentLength:Y.length,wasFileCreated:z,wasAppended:P&&L,operationType:$?"document":"file",processingTime:Date.now()-k,userComment:x,...O&&X&&{commitHash:X,previousCommitHash:te||void 0}},note:"Write operation completed successfully"+($?" (document synced)":" (committed to git)")})}catch(e){I.warn("Failed to update tool record",{recordError:e})}let ne=`**${z?"Created":P?"Appended to":"Updated"} ${$?`document ${S}${F?` (${F})`:""}`:"file "+D}**\n`
2642
+ ee=e.branchName,re=e.permanentBranch,I.debug("Retrieved branch name from workspace manager",{branchName:ee,requestId:e.requestId,mode:e.mode,isMainBranch:e.isMainBranch})}try{const e=`${z?"Create":P?"Append to":"Update"} ${A} (${C})`,t=await d.commitToGit(M,A,C,C,e,r,I,y,E.metricsPrefix)
2643
+ X=t.commitHash,te=t.previousCommitHash,I.info("Changes committed to git via shared utility",{path:A,commitMessage:e,commitHash:X?.substring(0,8),previousCommitHash:te?.substring(0,8),branchName:ee,toolRecordId:C})}catch(e){I.warn("Git operations failed via shared utility",{error:e})}}try{await s.updateToolRecord(C,{metadata:{path:O?A:"document:"+S,status:"completed",finalContentLength:Y.length,wasFileCreated:z,wasAppended:P&&L,operationType:$?"document":"file",processingTime:Date.now()-k,userComment:x,...O&&X&&{commitHash:X,previousCommitHash:te||void 0}},note:"Write operation completed successfully"+($?" (document synced)":" (committed to git)")})}catch(e){I.warn("Failed to update tool record",{recordError:e})}let ne=`**${z?"Created":P?"Appended to":"Updated"} ${$?`document ${S}${F?` (${F})`:""}`:"file "+A}**\n`
2634
2644
  ne+=`**Content Length:** ${Y.length} characters\n`,O&&ee&&(re||(ne+=`**Branch:** ${ee}\n`)),O&&X&&(ne+=`**Commit Hash:** ${X.substring(0,8)}\n`,te&&(ne+=`**Previous Commit:** ${te.substring(0,8)}\n`)),$&&(ne+=`\n**Document Update:** The document content has been updated and synced back to the tracking system. You can continue editing using other file modification tools with documentId \`${S}\`.`)
2635
2645
  const oe=Date.now()
2636
- I.info("Write operation completed successfully",{duration:oe-k,path:O?D:"document:"+S,contentLength:Y.length,wasCreated:z}),b({tool:"write_file",success:!0}),y.incrementCounter("write_file_total",1,{success:!0})
2646
+ I.info("Write operation completed successfully",{duration:oe-k,path:O?A:"document:"+S,contentLength:Y.length,wasCreated:z}),b({tool:"write_file",success:!0}),y.incrementCounter("write_file_total",1,{success:!0})
2637
2647
  const se=r.workspaceManager?.consumeLargeFileWarning(r.currentSessionId)
2638
2648
  return se&&(ne+=se),{content:[{type:"text",text:ne}],...(0,l.alchemyMeta)({kind:"write-file",toolRecordId:C})}}catch(t){const r=Date.now()-k
2639
2649
  if(I.error("Write operation failed",{error:t instanceof Error?t.stack:t+"",duration:r,phase:_}),C)try{await s.updateToolRecord(C,{metadata:{path:e?.path,status:"failed",error:(0,g.getErrorMessage)(t),phase:_,duration:r,userComment:e?.comment},note:"Write operation failed: "+(0,g.getErrorMessage)(t)})}catch(e){}return b({tool:"write_file",success:!1}),y.incrementCounter("write_file_errors",1,{phase:_}),{content:[{type:"text",text:"\u274c **Write Operation Failed:** "+(0,g.getErrorMessage)(t)}],isError:!0}}}
@@ -2765,10 +2775,10 @@ let P=null
2765
2775
  try{i.default.mkdirSync(S,{recursive:!0}),P=i.default.createWriteStream(T,{flags:"w"}),P.write(`# ${(new Date).toISOString()} starting: ${C}\n# cwd: ${E}\n# timeout: ${v.timeout||60}s\n\n`)}catch(e){h.warn("Failed to open command log file",{logPath:T,error:(0,l.getErrorMessage)(e)})}h.info("Running command",{name:v.name,command:C,cwd:E,configuredCwd:v.cwd,logPath:T})
2766
2776
  const x=await(0,n.runTrackedShellWithHeartbeat)(C,{cwd:E,timeoutMs:1e3*(v.timeout||60),env:{...process.env,FORCE_COLOR:"0"},onChunk:P?(e,t)=>{P?.write(t)}:void 0},{id:q,name:v.name,sessionId:g?.sessionId,logPath:T},f)
2767
2777
  P&&(P.write(`\n# exit: ${x.exitCode}${x.timedOut?" (timed out)":""}\n`),P.end())
2768
- const{stdout:j,stderr:R,exitCode:M,timedOut:$}=x,{output:O,truncatedNote:D,logFooter:A}=(0,o.formatCommandOutput)(j,R,{invocationId:q}),F=$?"TIMEOUT":null
2778
+ const{stdout:j,stderr:R,exitCode:M,timedOut:$}=x,{output:O,truncatedNote:A,logFooter:D}=(0,o.formatCommandOutput)(j,R,{invocationId:q}),F=$?"TIMEOUT":null
2769
2779
  let N
2770
2780
  if(m)try{const e=await m.createToolRecord({projectId:t.projectId,type:"run-command",metadata:{name:v.name,command:C,exitCode:M,timedOut:$,status:$?"timeout":0===M?"success":"failed",timestamp:(new Date).toISOString()}})
2771
- N=e?.id}catch(e){h.debug("Failed to create tool record for command run",{error:(0,l.getErrorMessage)(e)})}return y({success:!0}),{content:[{type:"text",text:`## ${v.name}${F?` \u2014 ${F} (killed after ${v.timeout}s)`:""}\n\`\`\`\n$ ${C}\n${O}\n\`\`\`${D}\n\nExit code: ${M}\n${A} (pass to read_files or search)`}],...(0,c.alchemyMeta)({kind:"run-command",toolRecordId:N})}}catch(e){return y({success:!1,error:"api_error"}),{content:[{type:"text",text:"Failed to run command: "+(0,l.getErrorMessage)(e)}],isError:!0}}}
2781
+ N=e?.id}catch(e){h.debug("Failed to create tool record for command run",{error:(0,l.getErrorMessage)(e)})}return y({success:!0}),{content:[{type:"text",text:`## ${v.name}${F?` \u2014 ${F} (killed after ${v.timeout}s)`:""}\n\`\`\`\n$ ${C}\n${O}\n\`\`\`${A}\n\nExit code: ${M}\n${D} (pass to read_files or search)`}],...(0,c.alchemyMeta)({kind:"run-command",toolRecordId:N})}}catch(e){return y({success:!1,error:"api_error"}),{content:[{type:"text",text:"Failed to run command: "+(0,l.getErrorMessage)(e)}],isError:!0}}}
2772
2782
  const n=t(1375),o=t(1816),s=r(t(6928)),i=r(t(9896)),a=t(4139),c=t(2272),l=t(8824),d=t(6688),u=t(9414)},4466(e,exports,t){"use strict"
2773
2783
  function r(e,t){return`${e}#${t.toString().padStart(2,"0")}`}async function n(e,t,n,s){if(exports.TASK_REF_PATTERN.test(e))return e
2774
2784
  const i=e.match(exports.TASK_REF_SHORT_PATTERN)
@@ -2875,7 +2885,7 @@ const{operation:b}=w
2875
2885
  if("list"!==b&&"search"!==b&&!I){const e=await(0,a.resolveRequestIdOrError)(p,y,t.currentSessionId,`Error: requestId is required for '${b}' and could not be resolved from session`)
2876
2886
  if(!e.ok)return e.errorResult
2877
2887
  I=e.requestId}switch(y.info("Processing request operation",{requestId:I,operation:b}),b){case"complete":{y.info("Routing to complete-request handler")
2878
- const e={requestId:I,summary:w.complete.summary,skipTestsReason:w.complete.skipTestsReason,skipTests:w.complete.skipTests}
2888
+ const e={requestId:I,summary:w.complete.summary,skipTestsReason:w.complete.skipTestsReason,skipTests:w.complete.skipTests,criteriaComments:w.complete.criteriaComments}
2879
2889
  return(0,u.withAlchemyKind)(await(0,r.handleCompleteRequest)(e,t,p,m,g,f),h.complete)}case"merge":return y.info("Routing to merge handler"),(0,u.withAlchemyKind)(await(0,l.handleMergeRequest)({requestId:I,verifications:w.merge?.verifications||[]},t,p,m,g,f),h.merge)
2880
2890
  case"approve":{y.info("Routing to approve-request handler")
2881
2891
  const e={requestId:I}
@@ -3312,48 +3322,59 @@ return{isOffline:this.isInOfflineMode(),consecutiveFailures:e.consecutiveFailure
3312
3322
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.def=void 0
3313
3323
  const r=t(6183),n=t(2965)
3314
3324
  exports.def={name:"read_files",description:'Read the contents of multiple files and documents with flexible line selection.\n\nLINE SELECTION (string format):\n- Single line: "lines": "5"\n- Multiple lines: "lines": "1,3,7,10"\n- Range: "lines": "5-15"\n- Entire file: omit "lines" parameter\n\nEXAMPLES:\n- Read first 10 lines: {"path": "file.txt", "lines": "1-10"}\n- Read specific lines: {"path": "file.txt", "lines": "1,5,25"}\n- Read line 42 only: {"path": "file.txt", "lines": "42"}\n- Read entire file: {"path": "file.txt"}\n\nSupports both filesystem files (using \'path\') and tracked documents (using \'documentId\'). More efficient than reading files one by one. Failed reads won\'t stop the entire operation.',schema:r.ReadFilesSchema,annotations:{title:"Read Files/Documents",readOnlyHint:!0},readOnly:!0,handler(e,t){return(0,n.handleReadFiles)(e,t.context,t.logger,t.metrics)}}},5715(e,exports,t){"use strict"
3315
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleCompleteRequest=async function(e,t,c,l,d,u,h){const p=l.child({tool:"complete_request"}),m=d.startTimer("tool_execution_time")
3316
- try{const{requestId:n,summary:o,skipTestsReason:l,skipTests:d}=e
3325
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleCompleteRequest=async function(e,t,l,d,u,h,p){const m=d.child({tool:"complete_request"}),g=u.startTimer("tool_execution_time")
3326
+ try{const{requestId:n,summary:o,skipTestsReason:d,skipTests:u,criteriaComments:h}=e
3317
3327
  if(!n)throw(0,r.createValidationError)("Request ID is required")
3318
3328
  if(!o)throw(0,r.createValidationError)("Summary is required")
3319
- if(l&&d&&d.length>0)throw(0,r.createValidationError)("Provide either skipTestsReason (skip all) or skipTests (per-command), not both.")
3320
- p.info("Starting request completion",{requestId:n,skipAllRequested:!!l,perCommandSkipCount:d?.length||0})
3321
- const u=h?.sessionId
3322
- let g
3323
- try{const e=await c.proxyPost(`/api/projects/${t.projectId}/requests/${n}/validate-completion`,{sessionId:u,skipTestsReason:l,skipTests:d})
3324
- g=e?.data||e}catch(e){const t=(0,s.getErrorMessage)(e)
3325
- return p.error("Server validation failed",{requestId:n,error:t}),m({success:!1,error:"validation_request_failed"}),{content:[{type:"text",text:"Failed to validate completion: "+t}],isError:!0}}if(!g.canComplete){let e=`Cannot complete request ${n}: ${g.reason}\n`
3326
- return g.blockedByTasks&&g.blockedByTasks.length>0&&(e+="\n**Tasks that must be completed or canceled first:**\n",g.blockedByTasks.forEach(((t,r)=>{e+=`${r+1}. **${t.taskId}** - ${t.title} (Status: ${t.status})\n`}))),g.lifoViolation&&(e+="\n**Requests that must be re-completed first (LIFO order):**\n",g.lifoViolation.blockingRequests.forEach(((t,r)=>{e+=`${r+1}. **${t.requestId}** - ${t.title} (Status: ${t.status})\n`}))),m({success:!1,reason:"server_blocked"}),{content:[{type:"text",text:e}],isError:!0}}if(g.testsSkipped&&(l||d&&d.length>0))try{const e=c.getToolRecordClient()
3327
- e&&await e.createToolRecord({projectId:t.projectId,requestId:n,type:"tests-skipped",metadata:{...l?{reason:l}:{},...d&&d.length>0?{skipTests:d}:{},completionMode:g.completionMode,timestamp:(new Date).toISOString()}})}catch(e){p.warn("Failed to persist tests-skipped tool_record",{error:(0,s.getErrorMessage)(e)})}const f=c.getRequestClient(),y="auto-merge"!==g.completionMode&&!g.testsSkipped&&!1===g.hasActiveTestProcesses&&("test-by-default"===g.completionMode||"always-test"===g.completionMode)
3328
- let w="auto-merge"!==g.completionMode&&!g.testsSkipped&&!y
3329
- const I="manual-review"===g.reviewMode
3330
- let b,k=null
3331
- if(w)try{const[e,r]=await Promise.all([c.proxyGet(`/api/projects/${t.projectId}/requests/${n}/test-runs`),fetch(`${(0,a.getActionServerBaseUrl)()}/api/branch-head?requestId=${encodeURIComponent(n)}`,{signal:AbortSignal.timeout(5e3)}).catch((e=>(p.debug("branch-head fetch failed; proceeding with ready_for_review",{error:(0,s.getErrorMessage)(e)}),null)))]),o=e?.data||e||[],i=Array.isArray(o)&&o.length>0?o[0]:null
3332
- if(i&&"passed"===i.status&&i.commitSha){let e=null
3333
- if(r&&r.ok){const t=await r.json()
3334
- e=t?.data?.sha??null}e&&e===i.commitSha&&(w=!1,k=`latest test run ${i.id} passed against current branch HEAD ${e.slice(0,7)}`,b=i.id,p.info("Skip-if-fresh: SHA match, bypassing ready_for_review",{requestId:n,runId:i.id,sha:e}))}}catch(e){p.debug("Skip-if-fresh lookup failed; proceeding with ready_for_review",{error:(0,s.getErrorMessage)(e)})}const v=w||I?"ready_for_review":"ready_for_merge"
3335
- try{await f.updateRequest(n,{status:v,completionSummary:o,...b?{skippedTestRunId:b}:{}})}catch(e){const t=(0,s.getErrorMessage)(e)
3336
- return m({success:!1,error:"status_transition_failed"}),{content:[{type:"text",text:"Failed to transition request: "+t}],isError:!0}}let C
3337
- try{C=await f.getRequest(n)}catch(e){const t=(0,s.getErrorMessage)(e)
3338
- return m({success:!1,error:"fetch_request_failed"}),{content:[{type:"text",text:"Failed to fetch request criteria: "+t}],isError:!0}}const _=C?.acceptanceCriteria||[]
3339
- if(m({success:!0}),w){const e=g.completionMode
3340
- return{content:[{type:"text",text:`Request ${n} moved to ready_for_review. ${"ask-me"===e?"A notification has been created asking the user to choose how to handle testing (auto-test, interactive-test, or skip).":`Tests are running automatically in the background (${e} mode). When they pass and the project is set to auto-finalize, the request will be merged without further input; otherwise the user will click Finalize after reviewing.`} Do NOT call merge or any other tool. Your work is done. Stop here and wait.`}]}}if(I)return{content:[{type:"text",text:`Request ${n} submitted for human review.${g.testsSkipped&&l?`\n\n_Tests skipped: ${l}_`:""}\n\nThe project is set to manual-review \u2014 a person will approve and merge from the UI. Do NOT call merge yourself.`}]}
3341
- const E=k?`\n\n_Skipped re-running tests: ${k}._\n`:"",S=y?`\n\n_No test processes are configured for this project; ${g.completionMode} mode is being treated as auto-merge._\n`:""
3342
- return 0===_.length?{content:[{type:"text",text:`Request ${n} is ready. ${E}${S}No acceptance criteria defined \u2014 call merge directly.`}]}:{content:[{type:"text",text:`Request ${n} completion started (${v}).${E}${S}\n\n## Next Step: Verify & Merge\n\n${(0,i.formatVerifyInstructions)(_)}\n\nUse the merge operation with your verification evidence to merge to main.`}]}}catch(r){let s="An unexpected error occurred",i=""
3343
- r instanceof n.ZodError?(s="Validation error",i=r.errors.map((e=>`${e.path.join(".")}: ${e.message}`)).join(", ")):r instanceof Error?(s=r.message,i=r.stack||""):i=r+"",p.error("Complete request failed",{error:s,details:i})
3344
- try{await(0,o.createFailureToolRecord)("complete-request",r,{error:s,details:i,requestId:e?.requestId},c.getToolRecordClient(),p,d,{requestId:e?.requestId,projectId:t.projectId,note:"Failed to complete request"})}catch{}return d.startTimer("tool_execution_time")({success:!1,error:r instanceof Error?r.name:"unknown"}),{content:[{type:"text",text:"Error: "+s}],isError:!0}}}
3345
- const r=t(9860),n=t(1569),o=t(8616),s=t(8824),i=t(5454),a=t(9414)},5767(e,exports,t){"use strict"
3329
+ if(d&&u&&u.length>0)throw(0,r.createValidationError)("Provide either skipTestsReason (skip all) or skipTests (per-command), not both.")
3330
+ m.info("Starting request completion",{requestId:n,skipAllRequested:!!d,perCommandSkipCount:u?.length||0})
3331
+ const f=p?.sessionId??t.currentSessionId,y=p??(f?{providerId:"unknown",sessionId:f,createdAt:(new Date).toISOString()}:void 0)
3332
+ let w,I=null
3333
+ try{const e=await fetch(`${(0,a.getActionServerBaseUrl)()}/api/branch-head?requestId=${encodeURIComponent(n)}`,{signal:AbortSignal.timeout(5e3)})
3334
+ if(e.ok){const t=await e.json()
3335
+ I=t?.data?.sha??null}}catch(e){m.debug("branch-head fetch failed; server will fall back",{error:(0,s.getErrorMessage)(e)})}try{const e=await l.proxyPost(`/api/projects/${t.projectId}/requests/${n}/validate-completion`,{sessionId:f,skipTestsReason:d,skipTests:u,headSha:I})
3336
+ w=e?.data||e}catch(e){const t=(0,s.getErrorMessage)(e)
3337
+ return m.error("Server validation failed",{requestId:n,error:t}),g({success:!1,error:"validation_request_failed"}),{content:[{type:"text",text:"Failed to validate completion: "+t}],isError:!0}}if(!w.canComplete){let e=`Cannot complete request ${n}: ${w.reason}\n`
3338
+ return w.blockedByTasks&&w.blockedByTasks.length>0&&(e+="\n**Tasks that must be completed or canceled first:**\n",w.blockedByTasks.forEach(((t,r)=>{e+=`${r+1}. **${t.taskId}** - ${t.title} (Status: ${t.status})\n`}))),w.lifoViolation&&(e+="\n**Requests that must be re-completed first (LIFO order):**\n",w.lifoViolation.blockingRequests.forEach(((t,r)=>{e+=`${r+1}. **${t.requestId}** - ${t.title} (Status: ${t.status})\n`}))),g({success:!1,reason:"server_blocked"}),{content:[{type:"text",text:e}],isError:!0}}if(h&&h.length>0)try{const e=l.getRequestClient(),r=await e.getRequest(n),o=r?.acceptanceCriteria||[]
3339
+ if(o.length>0){const e=(0,i.buildCriteriaKeyMap)(o),r=[]
3340
+ for(const t of h){const n=e.get(t.key)
3341
+ n&&r.push({criterionId:n.id,evidence:t.comment})}r.length>0&&await l.proxyPost(`/api/projects/${t.projectId}/requests/${n}/verify-criteria`,{verifications:r,dimension:"coder"})}}catch(e){m.warn("Failed to persist coder criteria comments",{error:(0,s.getErrorMessage)(e)})}if((0,c.isAgentReviewMode)(w.reviewMode)&&"escalated"===w.reviewStatus)return g({success:!0}),{content:[{type:"text",text:`Request ${n}: agent review hit its pass cap and was escalated for human sign-off. No further action from you; a human finalizes or sends back must_fix items.`}]}
3342
+ if((0,c.isAgentReviewMode)(w.reviewMode)&&w.reviewFresh&&"pending"!==w.reviewStatus&&"completed"!==w.reviewStatus&&"escalated"!==w.reviewStatus)try{await l.getRequestClient().updateRequest(n,{reviewStatus:"completed",completionSummary:o})}catch(e){return g({success:!1,error:"review_mark_failed"}),{content:[{type:"text",text:"Failed to record review: "+(0,s.getErrorMessage)(e)}],isError:!0}}else if((0,c.isAgentReviewMode)(w.reviewMode)&&"pending"!==w.reviewStatus&&"completed"!==w.reviewStatus){const e=l.getRequestClient()
3343
+ try{await e.updateRequest(n,{reviewStatus:"pending",completionSummary:o,cliOrigin:y})}catch(e){return g({success:!1,error:"review_start_failed"}),{content:[{type:"text",text:"Failed to start review: "+(0,s.getErrorMessage)(e)}],isError:!0}}return g({success:!0}),{content:[{type:"text",text:`Request ${n}: review started. Read-only reviewers are reading your diff. STOP here and wait \u2014 do NOT poll, sleep, or call tools to pass the time; each reviewer's report is delivered to you automatically and wakes you, so just end your turn between reports. When their reports arrive (a detailed handoff follows with the specifics), act on the findings, finish your tasks, and call complete again. After review passes, the server runs any remaining gates and messages you the exact next step (run tests, await human sign-off, or merge) -- wait for that and don't merge until it tells you to.`}]}}if((0,c.isAgentReviewMode)(w.reviewMode)&&"pending"===w.reviewStatus){const e=l.getRequestClient()
3344
+ try{await e.updateRequest(n,{reviewStatus:"completed",completionSummary:o,cliOrigin:y})}catch(e){return g({success:!1,error:"review_finish_failed"}),{content:[{type:"text",text:"Failed to finish review: "+(0,s.getErrorMessage)(e)}],isError:!0}}return g({success:!0}),{content:[{type:"text",text:`Request ${n}: review submitted. The server is evaluating it and will message you the exact next step (run tests, await human sign-off, or merge) -- wait for that before acting. If a criterion is still unmet, the criteria reviewer takes another pass and reports back; address any must_fix and call complete again.`}]}}if((0,c.isAgentReviewMode)(w.reviewMode)&&"completed"===w.reviewStatus&&("auto-merge"===w.completionMode||w.testsSkipped||!0!==w.hasActiveTestProcesses))return g({success:!0}),{content:[{type:"text",text:`Request ${n}: review is complete and all gates are clear. Call merge to finalize (do not call complete again).`}]}
3345
+ if(w.testsSkipped&&(d||u&&u.length>0))try{const e=l.getToolRecordClient()
3346
+ e&&await e.createToolRecord({projectId:t.projectId,requestId:n,type:"tests-skipped",metadata:{...d?{reason:d}:{},...u&&u.length>0?{skipTests:u}:{},completionMode:w.completionMode,timestamp:(new Date).toISOString()}})}catch(e){m.warn("Failed to persist tests-skipped tool_record",{error:(0,s.getErrorMessage)(e)})}const b=l.getRequestClient(),k=!w.testsSkipped&&!1===w.hasActiveTestProcesses&&("test-by-default"===w.completionMode||"always-test"===w.completionMode)
3347
+ let v="auto-merge"!==w.completionMode&&!w.testsSkipped&&!k
3348
+ const C="manual-review"===w.reviewMode
3349
+ let _,E=null
3350
+ if(v)try{const e=await l.proxyGet(`/api/projects/${t.projectId}/requests/${n}/test-runs`),r=e?.data||e||[],o=Array.isArray(r)&&r.length>0?r[0]:null
3351
+ if(o&&"passed"===o.status&&o.commitSha){const e=I
3352
+ e&&e===o.commitSha&&(v=!1,E=`latest test run ${o.id} passed against current branch HEAD ${e.slice(0,7)}`,_=o.id,m.info("Skip-if-fresh: SHA match, bypassing ready_for_review",{requestId:n,runId:o.id,sha:e}))}}catch(e){m.debug("Skip-if-fresh lookup failed; proceeding with ready_for_review",{error:(0,s.getErrorMessage)(e)})}const S=v?"in_progress":C?"ready_for_review":"ready_for_merge",q=v?"running":void 0
3353
+ try{await b.updateRequest(n,{status:S,...q?{testStatus:q}:{},completionSummary:o,cliOrigin:y,..._?{skippedTestRunId:_}:{}})}catch(e){const t=(0,s.getErrorMessage)(e)
3354
+ return g({success:!1,error:"status_transition_failed"}),{content:[{type:"text",text:"Failed to transition request: "+t}],isError:!0}}let T
3355
+ try{T=await b.getRequest(n)}catch(e){const t=(0,s.getErrorMessage)(e)
3356
+ return g({success:!1,error:"fetch_request_failed"}),{content:[{type:"text",text:"Failed to fetch request criteria: "+t}],isError:!0}}const P=T?.acceptanceCriteria||[]
3357
+ if(g({success:!0}),v){const e=w.completionMode
3358
+ return{content:[{type:"text",text:`Request ${n}: tests are now running. ${"ask-me"===e?"A notification has been created asking the user to choose how to handle testing (auto-test, interactive-test, or skip). Stop here; you will receive a report when testing finishes.":`Tests are running automatically in the background (${e} mode). Stop here; a tester subagent will run the test commands and post a report back to you when done.`}\n\n## When the report arrives\n- **Passed** \u2014 call \`merge\` with verification evidence for each acceptance criterion to finalize the request.\n- **Failed** \u2014 fix the issues and call \`complete\` again.\n\nDo NOT call merge, finalize, or any other tool BEFORE the report arrives \u2014 stop here and wait.`}]}}if(C)return{content:[{type:"text",text:`Request ${n} submitted for human review.${w.testsSkipped&&d?`\n\n_Tests skipped: ${d}_`:""}\n\nThe project is set to manual-review \u2014 a person will approve and merge from the UI. Do NOT call merge yourself.`}]}
3359
+ const x=E?`\n\n_Skipped re-running tests: ${E}._\n`:"",j=k?`\n\n_No test processes are configured for this project; ${w.completionMode} mode is being treated as auto-merge._\n`:""
3360
+ return 0===P.length?{content:[{type:"text",text:`Request ${n} is ready. ${x}${j}No acceptance criteria defined \u2014 call merge directly.`}]}:{content:[{type:"text",text:`Request ${n}: all gates are clear.${x}${j}\n\n## Next Step: Verify & Merge\n\n${(0,i.formatVerifyInstructions)(P)}\n\nUse the merge operation with your verification evidence to merge to main.`}]}}catch(r){let s="An unexpected error occurred",i=""
3361
+ r instanceof n.ZodError?(s="Validation error",i=r.errors.map((e=>`${e.path.join(".")}: ${e.message}`)).join(", ")):r instanceof Error?(s=r.message,i=r.stack||""):i=r+"",m.error("Complete request failed",{error:s,details:i})
3362
+ try{await(0,o.createFailureToolRecord)("complete-request",r,{error:s,details:i,requestId:e?.requestId},l.getToolRecordClient(),m,u,{requestId:e?.requestId,projectId:t.projectId,note:"Failed to complete request"})}catch{}return u.startTimer("tool_execution_time")({success:!1,error:r instanceof Error?r.name:"unknown"}),{content:[{type:"text",text:"Error: "+s}],isError:!0}}}
3363
+ const r=t(9860),n=t(1569),o=t(8616),s=t(8824),i=t(5454),a=t(9414),c=t(3580)},5767(e,exports,t){"use strict"
3346
3364
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.def=void 0
3347
3365
  const r=t(6183),n=t(6285)
3348
3366
  exports.def={name:"specialist_ops",description:"Manage your pool of up to 10 specialists. Each has a specialization describing their domain. Reuse specialists via follow-up before spawning new ones.\n\nAll params are flat (no nested objects). The operation field determines which params apply.\n\nOperations:\n\u2022 delegate \u2014 prompt + specialization required. Optional: description, specializationHeadline, requestId.\n\u2022 follow-up \u2014 prompt + sessionId required\n\u2022 check-status \u2014 sessionId required\n\u2022 list \u2014 all params optional: activeOnly, requestId (filter), limit\n\u2022 abort \u2014 sessionId required",schema:r.SpecialistOpsSchema,annotations:{title:"Specialist Operations",readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1},handler(e,t){return(0,n.handleSpecialistOps)(e,t.context,t.logger,t.metrics,t.apiClient??void 0,t.cliOrigin)}}},5802(module){"use strict"
3349
3367
  module.exports=require("@modelcontextprotocol/sdk/server/stdio.js")},5806(e,exports,t){"use strict"
3350
3368
  function r(){const e=(0,o.areTestsRequiredForCompletion)(),t=e?s:i
3351
3369
  if(t)return t
3352
- const r=n.z.object({requestId:n.z.string(),summary:n.z.string(),skipTestsReason:n.z.string().min(20).optional(),skipTests:n.z.array(n.z.object({commandName:n.z.string().min(1),reason:n.z.string().min(20)})).optional(),acceptanceEvidence:n.z.array(n.z.object({evidence:n.z.string()}))}),a=n.z.object({testRecords:n.z.array(n.z.string()),coverage:n.z.object({criteriaTestedCount:n.z.number(),totalTestCases:n.z.number()}),criterionCoverage:n.z.array(n.z.object({coverage:n.z.enum(["none","partial","full"]),testRecords:n.z.array(n.z.string())}))}),c=e?r.extend({testVerification:a}):r
3370
+ const r=n.z.object({requestId:n.z.string(),summary:n.z.string(),skipTestsReason:n.z.string().min(20).optional(),skipTests:n.z.array(n.z.object({commandName:n.z.string().min(1),reason:n.z.string().min(20)})).optional(),criteriaComments:n.z.array(n.z.object({key:n.z.string().min(1),comment:n.z.string().min(1)})).optional()}),a=n.z.object({testRecords:n.z.array(n.z.string()),coverage:n.z.object({criteriaTestedCount:n.z.number(),totalTestCases:n.z.number()}),criterionCoverage:n.z.array(n.z.object({coverage:n.z.enum(["none","partial","full"]),testRecords:n.z.array(n.z.string())}))}),c=e?r.extend({testVerification:a}):r
3353
3371
  return e?s=c:i=c,c}Object.defineProperty(exports,"__esModule",{value:!0}),exports.DynamicCompleteRequestSchema=void 0,exports.getDynamicCompleteRequestSchema=r
3354
3372
  const n=t(1569),o=t(2478)
3355
3373
  let s=null,i=null
3356
- exports.DynamicCompleteRequestSchema={get schema(){return r()}}},5964(e,exports){"use strict"
3374
+ exports.DynamicCompleteRequestSchema={get schema(){return r()}}},5837(e,exports,t){"use strict"
3375
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.def=void 0
3376
+ const r=t(1569),n=t(7648),o=r.z.object({requestId:r.z.string().describe("The request whose acceptance criteria are being recorded."),verifications:r.z.array(r.z.object({key:r.z.string().min(1).describe('Short criterion key (e.g. "1a") as shown next to the criterion.'),status:r.z.enum(["met","unmet"]).default("met").describe('Verdict for this criterion: "met" if the diff delivers it, "unmet" if missing or only partial.'),evidence:r.z.string().min(1).describe("For met: how the diff satisfies it (cite file:line). For unmet: what is missing and how to fix it.")})).min(1).describe("One entry per acceptance criterion you reviewed: record a verdict (met or unmet) for EVERY criterion, not only the satisfied ones.")})
3377
+ exports.def={name:"record_criteria_verification",description:"Record the per-criterion acceptance-criteria verdict (met or unmet + reason) for every criterion; record-only, does NOT merge. For the criteria reviewer to persist its full verdict.",schema:o,annotations:{title:"Record Criteria Verification",readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0},handler(e,t){return(0,n.handleRecordCriteriaVerification)(e,t.context,t.apiClient,t.logger,t.metrics)}}},5964(e,exports){"use strict"
3357
3378
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.ConflictDetectionError=void 0,exports.createConflictDetectionError=function(e,r){return new t(e,r)}
3358
3379
  class t extends Error{constructor(e,t){super(e),this.isConflictDetection=!0,this.preserveWorktree=!0,this.name="ConflictDetectionError",this.conflictInfo=t}static isConflictDetectionError(e){return e instanceof t||e instanceof Error&&(!0===e.isConflictDetection||void 0!==e.conflictInfo)}static shouldPreserveWorktree(e){return t.isConflictDetectionError(e)||e instanceof Error&&!0===e.preserveWorktree}}exports.ConflictDetectionError=t},5967(e,exports,t){"use strict"
3359
3380
  var r=this&&this.__createBinding||(Object.create?function(e,t,r,n){void 0===n&&(n=r)
@@ -3387,15 +3408,15 @@ g.info("Initial branch context",{initialBranch:$.branch,initialRequestId:$.reque
3387
3408
  const O=[]
3388
3409
  for(const e of j){const{normalizedPath:t,fullPath:n}=c.validateAndNormalizePath(e.target,r)
3389
3410
  O.push({normalized:t,full:n})}I=c.FileOperationPhase.TASK_VALIDATION,s.incrementCounter("delete_files_phase",1,{phase:I})
3390
- const D=O.length>0?O[0].normalized:"document_operations",A=await c.validateTaskId(P,D,d,g,s,b.metricsPrefix),F=A?.requestId,N=await c.validateWorkItemStates(P,D,d,v,r,g,s,b.metricsPrefix),z=await c.autoPromoteTaskStatus(P,A.status,F,N.request?.status,D,d,v,g,s,b.metricsPrefix)
3391
- z.taskPromoted&&g.info(`Auto-promoted task ${P} (${A.title||"Untitled"}) to in_progress for mixed operations`,{taskId:P,taskTitle:A.title,previousStatus:A.status,newStatus:z.newTaskStatus,totalOperations:x.length,fileOperations:j.length,documentOperations:R.length}),z.requestPromoted&&g.info(`Auto-promoted request ${F} to in_progress for file operations`,{requestId:F,previousStatus:N.request?.status,newStatus:z.newRequestStatus,triggeringTaskId:P})
3411
+ const A=O.length>0?O[0].normalized:"document_operations",D=await c.validateTaskId(P,A,d,g,s,b.metricsPrefix),F=D?.requestId,N=await c.validateWorkItemStates(P,A,d,v,r,g,s,b.metricsPrefix),z=await c.autoPromoteTaskStatus(P,D.status,F,N.request?.status,A,d,v,g,s,b.metricsPrefix)
3412
+ z.taskPromoted&&g.info(`Auto-promoted task ${P} (${D.title||"Untitled"}) to in_progress for mixed operations`,{taskId:P,taskTitle:D.title,previousStatus:D.status,newStatus:z.newTaskStatus,totalOperations:x.length,fileOperations:j.length,documentOperations:R.length}),z.requestPromoted&&g.info(`Auto-promoted request ${F} to in_progress for file operations`,{requestId:F,previousStatus:N.request?.status,newStatus:z.newRequestStatus,triggeringTaskId:P})
3392
3413
  const L=await c.ensureCorrectWorkspaceContext({taskPromoted:z.taskPromoted,requestId:F,context:r,operation:"delete-files",logger:g,metrics:s,metricsPrefix:b.metricsPrefix})
3393
3414
  if(!L.success)throw Error(L.error)
3394
3415
  let H={hadUserMods:!1,userModsCommitHash:void 0}
3395
3416
  if(r.git)try{g.debug("[DELETE-FILES] Checking for user changes before mixed delete operations")
3396
3417
  const e=await(0,u.commitUserChangesBeforeToolOperation)(r.git,"delete-files",{requestId:F,taskId:P,workspaceManager:r.workspaceManager},g,s)
3397
3418
  e.userChangesCommitted&&(g.debug("[DELETE-FILES] User changes committed before mixed delete operations",{commitHash:e.commitHash?.substring(0,8),filesCommitted:e.filesCommitted}),g.info("User changes committed before mixed delete-files operation",{taskId:P,totalOperations:x.length,fileOperations:j.length,documentOperations:R.length,userCommitHash:e.commitHash?.substring(0,8),userFilesCommitted:e.filesCommitted}),H={hadUserMods:!0,userModsCommitHash:e.commitHash})}catch(e){g.debug("[DELETE-FILES] Pre-tool commit failed, continuing with mixed delete operations",{error:e instanceof Error?e.message:e+""}),g.warn("Pre-tool user changes commit failed, continuing with mixed delete operation",{taskId:P,totalOperations:x.length,fileOperations:j.length,documentOperations:R.length,error:e instanceof Error?e.message:e+""})}g.debug("[DELETE-FILES] Proceeding with mixed delete operations",{totalOperations:x.length,fileDeletes:j.length,documentDeletes:R.length}),I=c.FileOperationPhase.RECORD_CREATION,s.incrementCounter("delete_files_phase",1,{phase:I})
3398
- const{toolRecordId:B,localToolRecordId:U}=await c.createFileOperationRecords(P,F,r.projectId,b.recordType,D,{totalOperations:x.length,fileOperations:j.length,documentOperations:R.length,filePaths:O.map((e=>e.normalized)),documentIds:R.map((e=>e.target)),userComment:S,hadUserMods:H.hadUserMods,userModsCommitHash:H.userModsCommitHash},d,k,g,s,b.metricsPrefix,i)
3419
+ const{toolRecordId:B,localToolRecordId:U}=await c.createFileOperationRecords(P,F,r.projectId,b.recordType,A,{totalOperations:x.length,fileOperations:j.length,documentOperations:R.length,filePaths:O.map((e=>e.normalized)),documentIds:R.map((e=>e.target)),userComment:S,hadUserMods:H.hadUserMods,userModsCommitHash:H.userModsCommitHash},d,k,g,s,b.metricsPrefix,i)
3399
3420
  y=B,w=B,I=b.fileOperationPhase,s.incrementCounter("delete_files_phase",1,{phase:I})
3400
3421
  const W=[]
3401
3422
  let G=0
@@ -3414,9 +3435,9 @@ let K=null
3414
3435
  if(G>0){I=c.FileOperationPhase.GIT_COMMIT,s.incrementCounter("delete_files_phase",1,{phase:I})
3415
3436
  const e=W.filter((e=>e.success)),t=e.filter((e=>"file"===e.type)).length,n=e.filter((e=>"document"===e.type)).length
3416
3437
  let o
3417
- o=t>0&&n>0?`Delete ${t} files and ${n} documents - ${S}`:t>0?1===t?"Delete file - "+S:`Delete ${t} files - ${S}`:1===n?"Delete document - "+S:`Delete ${n} documents - ${S}`,K=await c.commitToGit(P,D,B,U,o,r,g,s,b.metricsPrefix)}I=c.FileOperationPhase.RECORD_UPDATE_SUCCESS,s.incrementCounter("delete_files_phase",1,{phase:I})
3438
+ o=t>0&&n>0?`Delete ${t} files and ${n} documents - ${S}`:t>0?1===t?"Delete file - "+S:`Delete ${t} files - ${S}`:1===n?"Delete document - "+S:`Delete ${n} documents - ${S}`,K=await c.commitToGit(P,A,B,U,o,r,g,s,b.metricsPrefix)}I=c.FileOperationPhase.RECORD_UPDATE_SUCCESS,s.incrementCounter("delete_files_phase",1,{phase:I})
3418
3439
  const J=W.filter((e=>e.success)),Y=W.filter((e=>!e.success))
3419
- await c.updateFileOperationRecords(P,y,w,D,b.recordType,G===M?`Successfully deleted all ${M} operations`:`Deleted ${G}/${M} operations successfully`,{status:G===M?"completed":"partial_success",commitHash:K?.commitHash,previousCommitHash:K?.previousCommitHash,totalOperations:M,fileOperations:j.length,documentOperations:R.length,successCount:G,failureCount:M-G,successfulOperations:J.map((e=>({type:e.type,path:e.path,documentId:e.documentId,filename:e.filename}))),failedOperations:Y.map((e=>({type:e.type,path:e.path,documentId:e.documentId,filename:e.filename,error:e.error}))),userComment:S,hadUserMods:H.hadUserMods,userModsCommitHash:H.userModsCommitHash},G===M?`Successfully deleted all ${M} operations`:`Deleted ${G}/${M} operations successfully`,d,k,g,s,b.metricsPrefix)
3440
+ await c.updateFileOperationRecords(P,y,w,A,b.recordType,G===M?`Successfully deleted all ${M} operations`:`Deleted ${G}/${M} operations successfully`,{status:G===M?"completed":"partial_success",commitHash:K?.commitHash,previousCommitHash:K?.previousCommitHash,totalOperations:M,fileOperations:j.length,documentOperations:R.length,successCount:G,failureCount:M-G,successfulOperations:J.map((e=>({type:e.type,path:e.path,documentId:e.documentId,filename:e.filename}))),failedOperations:Y.map((e=>({type:e.type,path:e.path,documentId:e.documentId,filename:e.filename,error:e.error}))),userComment:S,hadUserMods:H.hadUserMods,userModsCommitHash:H.userModsCommitHash},G===M?`Successfully deleted all ${M} operations`:`Deleted ${G}/${M} operations successfully`,d,k,g,s,b.metricsPrefix)
3420
3441
  const Z=r.getCurrentBranchContext?await r.getCurrentBranchContext():{branch:null,requestId:null,isMain:!0}
3421
3442
  let X=""
3422
3443
  if(G===M){const e=J.filter((e=>"file"===e.type)).length,t=J.filter((e=>"document"===e.type)).length
@@ -3501,7 +3522,7 @@ if("create"===r){e.name||t.addIssue({code:"custom",message:"name is required for
3501
3522
  const r=s.QUEST_METADATA_KEYS.filter((t=>void 0!==e[t]))
3502
3523
  r.length>0&&t.addIssue({code:"custom",message:`Quest metadata fields (${r.join(", ")}) are not allowed on plan_write create. Quests are created via the UI flow at /quests/new. plan_write update still works on existing Quest documents.`})}else e.planId||t.addIssue({code:"custom",message:`planId is required for '${r}'.`})
3503
3524
  if("append"!==r||e.content||t.addIssue({code:"custom",message:'content is required for \'append\'. Example: { "operation": "append", "planId": "plan000001", "content": "..." }'}),"update"===r){const r=void 0!==e.content,n=void 0!==e.criteria||void 0!==e.tickIntervalSeconds||void 0!==e.maxInFlight||void 0!==e.maxPerDay||void 0!==e.minIterations||void 0!==e.status||void 0!==e.completionMode
3504
- r||n||t.addIssue({code:"custom",message:"update requires either 'content' (plan or quest body) or at least one metadata field (criteria, tickIntervalSeconds, maxInFlight, maxPerDay, minIterations, status, completionMode \u2014 quest only)."})}"replace"===r&&(e.old||t.addIssue({code:"custom",message:'old is required for replace. Example: { "operation": "replace", "planId": "plan000001", "old": "find this", "new": "replace with" }'}),void 0===e.new&&t.addIssue({code:"custom",message:"new is required for replace"})),"restore"!==r||e.versionNumber||t.addIssue({code:"custom",message:'versionNumber is required for restore. Example: { "operation": "restore", "planId": "plan000001", "versionNumber": 1 }'})})),exports.QuestSetCriteriaStatusesSchema=r.z.object({questId:r.z.string().min(1).describe("The quest documentId whose criteria to update."),updates:r.z.array(r.z.object({id:r.z.string().min(1).describe("Criterion id (must match an existing item in metadata.criteria)."),status:r.z.enum(["unverified","verified"]).describe("New status for this criterion."),evidence:r.z.string().optional().describe("Optional evidence note (file path, request id, test name, command output, etc.) describing why the status changed.")})).min(1).describe("One or more per-criterion updates. Each id must exist in the quest's metadata.criteria; unknown ids return an error listing the valid ids. Updates merge into the existing item (other fields preserved); only status and evidence change.")}),exports.QuestAuditCompleteSchema=r.z.object({questId:r.z.string().min(1).describe("The quest documentId to audit (the long uuid form returned by plan_write create, or the project-scoped doc000NN id)."),summary:r.z.string().min(1).describe("Short prose summary of what evidence the audit found. Stored on the quest if this call reaches the threshold and flips status \u2014 visible to the user when they review."),evidence:r.z.array(r.z.string()).optional().describe("Optional list of evidence references (file paths, request IDs, test names, command outputs) that support the audit conclusion. Stored on the quest alongside the summary if status flips.")}),exports.SwitchToMainBranchSchema=r.z.object({}),exports.SwitchToBranchSchema=r.z.object({branch:r.z.string().optional()}),exports.RevertRequestSchema=r.z.object({requestId:c,reason:r.z.string(),confirmDestructive:r.z.boolean().optional()}),exports.ModifyFileSchema=r.z.object({operations:r.z.array(r.z.object({path:r.z.string().optional(),documentId:l.optional(),operationType:r.z.enum(["replace-string","insert-lines","replace-lines","delete-string","delete-lines"]),replaceString:r.z.object({replacements:r.z.array(r.z.object({old_str:r.z.string(),new_str:r.z.string()})),mode:r.z.enum(["unique","all"]).optional()}).optional(),insertLines:r.z.object({insertions:r.z.array(r.z.object({type:r.z.enum(["before","after","between"]),line:r.z.number().optional(),afterLine:r.z.number().optional(),beforeLine:r.z.number().optional(),content:r.z.union([r.z.string(),r.z.array(r.z.string())])}).refine((e=>"between"===e.type?void 0!==e.afterLine&&void 0!==e.beforeLine&&void 0===e.line:void 0!==e.line&&void 0===e.afterLine&&void 0===e.beforeLine),{message:"Invalid line parameters"}))}).optional(),replaceLines:r.z.object({replacements:r.z.array(r.z.object({lines:r.z.string(),content:r.z.union([r.z.string(),r.z.array(r.z.string())])}))}).optional(),deleteString:r.z.object({patterns:r.z.array(r.z.string()),mode:r.z.enum(["all","unique"]).optional()}).optional(),deleteLines:r.z.object({lines:r.z.string()}).optional()}).transform(d).refine(u,{message:"path or documentId required"}).refine((e=>"replace-string"===e.operationType?e.replaceString&&!e.insertLines&&!e.replaceLines&&!e.deleteString&&!e.deleteLines:"insert-lines"===e.operationType?e.insertLines&&!e.replaceString&&!e.replaceLines&&!e.deleteString&&!e.deleteLines:"replace-lines"===e.operationType?e.replaceLines&&!e.replaceString&&!e.insertLines&&!e.deleteString&&!e.deleteLines:"delete-string"===e.operationType?e.deleteString&&!e.replaceString&&!e.insertLines&&!e.replaceLines&&!e.deleteLines:"delete-lines"===e.operationType&&e.deleteLines&&!e.replaceString&&!e.insertLines&&!e.replaceLines&&!e.deleteString),{message:"Invalid operation data"})),comment:r.z.string()}),exports.ManageFilesSchema=r.z.object({operations:r.z.array(r.z.object({operationType:r.z.enum(["create-directory","move-files","delete-files","get-file-info"]),createDirectory:r.z.object({path:r.z.string()}).optional(),moveFiles:r.z.object({moves:r.z.array(r.z.object({source:r.z.string(),destination:r.z.string()}))}).optional(),deleteFiles:r.z.object({targets:r.z.array(r.z.object({path:r.z.string().optional(),documentId:l.optional()}).transform(d).refine(u,{message:"path or documentId required"}))}).optional(),getFileInfo:r.z.object({path:r.z.string()}).optional()}).refine((e=>"create-directory"===e.operationType?e.createDirectory&&!e.moveFiles&&!e.deleteFiles&&!e.getFileInfo:"move-files"===e.operationType?e.moveFiles&&!e.createDirectory&&!e.deleteFiles&&!e.getFileInfo:"delete-files"===e.operationType?e.deleteFiles&&!e.createDirectory&&!e.moveFiles&&!e.getFileInfo:"get-file-info"===e.operationType&&e.getFileInfo&&!e.createDirectory&&!e.moveFiles&&!e.deleteFiles),{message:"Invalid operation data"})),comment:r.z.string()}),exports.RequestOperationsSchema=r.z.object({requestId:c.optional(),operation:r.z.enum(["complete","merge","approve","revert","get-info","list","search","delete-empty"]),complete:r.z.preprocess((e=>"string"==typeof e?(()=>{try{return JSON.parse(e)}catch{return e}})():e),r.z.object({summary:r.z.string(),skipTestsReason:exports.skipTestsReasonSchema,skipTests:exports.skipTestsSchema})).optional(),merge:r.z.preprocess((e=>"string"==typeof e?(()=>{try{return JSON.parse(e)}catch{return e}})():e),r.z.object({verifications:r.z.array(r.z.object({key:r.z.string().min(2).max(4),evidence:r.z.string().min(1)})).min(1)})).optional(),revert:r.z.object({reason:r.z.string(),confirmDestructive:r.z.boolean().optional()}).optional(),list:r.z.object({filters:r.z.object({type:r.z.enum(["feature","bugfix"]).optional(),status:r.z.enum(["pending_approval","open","in_progress","ready_for_review","ready_for_merge","resolving_conflicts","completed","canceled"]).optional(),planDocumentId:r.z.string().optional().describe("Narrow to requests linked to this Plan document (use the plan's id or local id)."),questDocumentId:r.z.string().optional().describe("Narrow to requests spawned under this Quest document (use the quest's id or local id)."),dateFrom:r.z.string().optional(),dateTo:r.z.string().optional(),limit:r.z.number().min(1).max(100).default(20),offset:r.z.number().min(0).default(0),sortBy:r.z.enum(["created","updated"]).default("updated"),sortDirection:r.z.enum(["asc","desc"]).default("desc")}).optional()}).optional(),search:r.z.object({query:r.z.string().min(1),searchIn:r.z.enum(["title","description","criteria","all"]).default("all"),filters:r.z.object({type:r.z.enum(["feature","bugfix"]).optional(),status:r.z.enum(["pending_approval","open","in_progress","ready_for_review","ready_for_merge","resolving_conflicts","completed","canceled"]).optional(),planDocumentId:r.z.string().optional().describe("Narrow to requests linked to this Plan document."),questDocumentId:r.z.string().optional().describe("Narrow to requests spawned under this Quest document."),dateFrom:r.z.string().optional(),dateTo:r.z.string().optional(),limit:r.z.number().min(1).max(100).default(20),offset:r.z.number().min(0).default(0)}).optional()}).optional(),approve:r.z.object({}).optional()}).superRefine(((e,t)=>{e.requestId
3525
+ r||n||t.addIssue({code:"custom",message:"update requires either 'content' (plan or quest body) or at least one metadata field (criteria, tickIntervalSeconds, maxInFlight, maxPerDay, minIterations, status, completionMode \u2014 quest only)."})}"replace"===r&&(e.old||t.addIssue({code:"custom",message:'old is required for replace. Example: { "operation": "replace", "planId": "plan000001", "old": "find this", "new": "replace with" }'}),void 0===e.new&&t.addIssue({code:"custom",message:"new is required for replace"})),"restore"!==r||e.versionNumber||t.addIssue({code:"custom",message:'versionNumber is required for restore. Example: { "operation": "restore", "planId": "plan000001", "versionNumber": 1 }'})})),exports.QuestSetCriteriaStatusesSchema=r.z.object({questId:r.z.string().min(1).describe("The quest documentId whose criteria to update."),updates:r.z.array(r.z.object({id:r.z.string().min(1).describe("Criterion id (must match an existing item in metadata.criteria)."),status:r.z.enum(["unverified","verified"]).describe("New status for this criterion."),evidence:r.z.string().optional().describe("Optional evidence note (file path, request id, test name, command output, etc.) describing why the status changed.")})).min(1).describe("One or more per-criterion updates. Each id must exist in the quest's metadata.criteria; unknown ids return an error listing the valid ids. Updates merge into the existing item (other fields preserved); only status and evidence change.")}),exports.QuestAuditCompleteSchema=r.z.object({questId:r.z.string().min(1).describe("The quest documentId to audit (the long uuid form returned by plan_write create, or the project-scoped doc000NN id)."),summary:r.z.string().min(1).describe("Short prose summary of what evidence the audit found. Stored on the quest if this call reaches the threshold and flips status \u2014 visible to the user when they review."),evidence:r.z.array(r.z.string()).optional().describe("Optional list of evidence references (file paths, request IDs, test names, command outputs) that support the audit conclusion. Stored on the quest alongside the summary if status flips.")}),exports.SwitchToMainBranchSchema=r.z.object({}),exports.SwitchToBranchSchema=r.z.object({branch:r.z.string().optional()}),exports.RevertRequestSchema=r.z.object({requestId:c,reason:r.z.string(),confirmDestructive:r.z.boolean().optional()}),exports.ModifyFileSchema=r.z.object({operations:r.z.array(r.z.object({path:r.z.string().optional(),documentId:l.optional(),operationType:r.z.enum(["replace-string","insert-lines","replace-lines","delete-string","delete-lines"]),replaceString:r.z.object({replacements:r.z.array(r.z.object({old_str:r.z.string(),new_str:r.z.string()})),mode:r.z.enum(["unique","all"]).optional()}).optional(),insertLines:r.z.object({insertions:r.z.array(r.z.object({type:r.z.enum(["before","after","between"]),line:r.z.number().optional(),afterLine:r.z.number().optional(),beforeLine:r.z.number().optional(),content:r.z.union([r.z.string(),r.z.array(r.z.string())])}).refine((e=>"between"===e.type?void 0!==e.afterLine&&void 0!==e.beforeLine&&void 0===e.line:void 0!==e.line&&void 0===e.afterLine&&void 0===e.beforeLine),{message:"Invalid line parameters"}))}).optional(),replaceLines:r.z.object({replacements:r.z.array(r.z.object({lines:r.z.string(),content:r.z.union([r.z.string(),r.z.array(r.z.string())])}))}).optional(),deleteString:r.z.object({patterns:r.z.array(r.z.string()),mode:r.z.enum(["all","unique"]).optional()}).optional(),deleteLines:r.z.object({lines:r.z.string()}).optional()}).transform(d).refine(u,{message:"path or documentId required"}).refine((e=>"replace-string"===e.operationType?e.replaceString&&!e.insertLines&&!e.replaceLines&&!e.deleteString&&!e.deleteLines:"insert-lines"===e.operationType?e.insertLines&&!e.replaceString&&!e.replaceLines&&!e.deleteString&&!e.deleteLines:"replace-lines"===e.operationType?e.replaceLines&&!e.replaceString&&!e.insertLines&&!e.deleteString&&!e.deleteLines:"delete-string"===e.operationType?e.deleteString&&!e.replaceString&&!e.insertLines&&!e.replaceLines&&!e.deleteLines:"delete-lines"===e.operationType&&e.deleteLines&&!e.replaceString&&!e.insertLines&&!e.replaceLines&&!e.deleteString),{message:"Invalid operation data"})),comment:r.z.string()}),exports.ManageFilesSchema=r.z.object({operations:r.z.array(r.z.object({operationType:r.z.enum(["create-directory","move-files","delete-files","get-file-info"]),createDirectory:r.z.object({path:r.z.string()}).optional(),moveFiles:r.z.object({moves:r.z.array(r.z.object({source:r.z.string(),destination:r.z.string()}))}).optional(),deleteFiles:r.z.object({targets:r.z.array(r.z.object({path:r.z.string().optional(),documentId:l.optional()}).transform(d).refine(u,{message:"path or documentId required"}))}).optional(),getFileInfo:r.z.object({path:r.z.string()}).optional()}).refine((e=>"create-directory"===e.operationType?e.createDirectory&&!e.moveFiles&&!e.deleteFiles&&!e.getFileInfo:"move-files"===e.operationType?e.moveFiles&&!e.createDirectory&&!e.deleteFiles&&!e.getFileInfo:"delete-files"===e.operationType?e.deleteFiles&&!e.createDirectory&&!e.moveFiles&&!e.getFileInfo:"get-file-info"===e.operationType&&e.getFileInfo&&!e.createDirectory&&!e.moveFiles&&!e.deleteFiles),{message:"Invalid operation data"})),comment:r.z.string()}),exports.RequestOperationsSchema=r.z.object({requestId:c.optional(),operation:r.z.enum(["complete","merge","approve","revert","get-info","list","search","delete-empty"]),complete:r.z.preprocess((e=>"string"==typeof e?(()=>{try{return JSON.parse(e)}catch{return e}})():e),r.z.object({summary:r.z.string(),skipTestsReason:exports.skipTestsReasonSchema,skipTests:exports.skipTestsSchema,criteriaComments:r.z.array(r.z.object({key:r.z.string().min(1),comment:r.z.string().min(1)})).optional()})).optional(),merge:r.z.preprocess((e=>"string"==typeof e?(()=>{try{return JSON.parse(e)}catch{return e}})():e),r.z.object({verifications:r.z.array(r.z.object({key:r.z.string().min(2).max(4),evidence:r.z.string().min(1)})).min(1)})).optional(),revert:r.z.object({reason:r.z.string(),confirmDestructive:r.z.boolean().optional()}).optional(),list:r.z.object({filters:r.z.object({type:r.z.enum(["feature","bugfix"]).optional(),status:r.z.enum(["pending_approval","open","in_progress","ready_for_review","ready_for_merge","resolving_conflicts","completed","canceled"]).optional(),planDocumentId:r.z.string().optional().describe("Narrow to requests linked to this Plan document (use the plan's id or local id)."),questDocumentId:r.z.string().optional().describe("Narrow to requests spawned under this Quest document (use the quest's id or local id)."),dateFrom:r.z.string().optional(),dateTo:r.z.string().optional(),limit:r.z.number().min(1).max(100).default(20),offset:r.z.number().min(0).default(0),sortBy:r.z.enum(["created","updated"]).default("updated"),sortDirection:r.z.enum(["asc","desc"]).default("desc")}).optional()}).optional(),search:r.z.object({query:r.z.string().min(1),searchIn:r.z.enum(["title","description","criteria","all"]).default("all"),filters:r.z.object({type:r.z.enum(["feature","bugfix"]).optional(),status:r.z.enum(["pending_approval","open","in_progress","ready_for_review","ready_for_merge","resolving_conflicts","completed","canceled"]).optional(),planDocumentId:r.z.string().optional().describe("Narrow to requests linked to this Plan document."),questDocumentId:r.z.string().optional().describe("Narrow to requests spawned under this Quest document."),dateFrom:r.z.string().optional(),dateTo:r.z.string().optional(),limit:r.z.number().min(1).max(100).default(20),offset:r.z.number().min(0).default(0)}).optional()}).optional(),approve:r.z.object({}).optional()}).superRefine(((e,t)=>{e.requestId
3505
3526
  const r=e.operation,n={complete:{field:"complete",hint:'Provide: { complete: { summary: "..." } }'},merge:{field:"merge",hint:'Provide: { merge: { verifications: [{key: "1a", evidence: "..."}] } }'},revert:{field:"revert",hint:'Provide: { revert: { reason: "..." } }'},search:{field:"search",hint:'Provide: { search: { query: "..." } }'}}[r]
3506
3527
  n&&!e[n.field]&&t.addIssue({code:"custom",message:`'${r}' operation requires '${n.field}' data. ${n.hint}`})})),exports.QuickWriteSchema=r.z.object({title:r.z.string().min(1,"Title is required"),description:r.z.string().min(1,"Description is required"),path:r.z.string().optional(),documentId:l.optional(),content:r.z.union([r.z.string(),r.z.array(r.z.string())]),format:r.z.enum(["text","json"]).optional(),append:r.z.boolean().optional()}).transform(d).refine(u,{message:"path or documentId required"}),exports.QuickReplaceSchema=r.z.object({title:r.z.string().min(1,"Title is required"),description:r.z.string().min(1,"Description is required"),operations:r.z.array(r.z.object({path:r.z.string().optional(),documentId:l.optional(),replacements:r.z.array(r.z.object({old:r.z.string().min(1),new:r.z.string(),match_mode:r.z.enum(["unique","all"]).optional()})).min(1),fuzzy_match:r.z.boolean().optional()}).transform(d).refine(u,{message:"path or documentId required"})).min(1),fuzzy_match:r.z.boolean().optional(),comment:r.z.string().min(1,"Comment describing the change is required")}),exports.QuickManageSchema=r.z.object({title:r.z.string().min(1,"Title is required"),description:r.z.string().min(1,"Description is required"),operations:r.z.array(r.z.object({operationType:r.z.enum(["create-directory","move-files","delete-files"]),createDirectory:r.z.object({path:r.z.string()}).optional(),moveFiles:r.z.object({moves:r.z.array(r.z.object({source:r.z.string(),destination:r.z.string()}))}).optional(),deleteFiles:r.z.object({targets:r.z.array(r.z.object({path:r.z.string().optional(),documentId:l.optional()}).transform(d).refine(u,{message:"path or documentId required"}))}).optional()})).min(1)}),exports.GenerateRequestJournalSchema=r.z.object({requestId:r.z.string().regex(/^req[_\d].+$/,"Invalid request ID format. Expected format: req_[26 chars] or req######")}),exports.MetaWorkflowListSchema=r.z.object({}),exports.MetaWorkflowGetSchema=r.z.object({workflowId:r.z.string().describe("Workflow ID to retrieve")}),exports.MetaWorkflowCreateSchema=r.z.object({name:r.z.string().min(1).describe("Unique name for the workflow"),shortName:r.z.string().min(1).max(10).describe("Short display name (required, max 10 chars)"),description:r.z.string().optional().describe("Description of what the workflow does"),documentation:r.z.string().optional().describe("Detailed documentation"),defaultAgent:r.z.string().optional().describe("Default agent template to use"),completionMode:r.z.enum(["auto-merge","ask-me","test-by-default","always-test"]).optional().describe("Testing axis: auto-merge (skip tests), ask-me (notify human on completion), test-by-default (tests run; opt-out allowed), always-test (tests required)"),reviewMode:r.z.enum(["auto-finalize","manual-review"]).optional().describe("Review axis: auto-finalize (server merges on pass/skip) or manual-review (human approves via UI)"),approvalMode:r.z.enum(["none","require-approval"]).optional().describe("Pre-work approval: none (default) or require-approval")}),exports.MetaWorkflowUpdateSchema=r.z.object({workflowId:r.z.string().describe("Workflow ID to update"),name:r.z.string().min(1).optional().describe("New name"),shortName:r.z.string().max(10).optional().describe("New short name (max 10 chars)"),description:r.z.string().optional().describe("New description"),documentation:r.z.string().optional().describe("New documentation"),defaultAgent:r.z.string().optional().describe("New default agent"),completionMode:r.z.enum(["auto-merge","ask-me","test-by-default","always-test"]).optional().describe("Testing axis"),reviewMode:r.z.enum(["auto-finalize","manual-review"]).optional().describe("Review axis"),approvalMode:r.z.enum(["none","require-approval"]).optional().describe("Pre-work approval: none (default) or require-approval")}),exports.MetaRecipeListSchema=r.z.object({}),exports.MetaRecipeGetSchema=r.z.object({recipeId:r.z.string().describe("Recipe ID to retrieve")}),exports.MetaRecipeCreateSchema=r.z.object({name:r.z.string().min(1).describe("Unique name for the recipe"),description:r.z.string().optional().describe("Description of what the recipe does"),documentation:r.z.string().optional().describe("Detailed documentation")}),exports.MetaRecipeAddHookSchema=r.z.object({recipeId:r.z.string().describe("Recipe ID to add the hook to"),hookId:r.z.string().describe("Hook template ID to add"),position:r.z.number().int().min(0).optional().describe("Position in the recipe (0-based, appended if omitted)")}),exports.MetaRecipeRemoveHookSchema=r.z.object({recipeId:r.z.string().describe("Recipe ID to remove the hook from"),hookId:r.z.string().describe("Hook template ID to remove")}),exports.MetaHookListSchema=r.z.object({}),exports.MetaHookCreateSchema=r.z.object({name:r.z.string().min(1).describe("Unique name for the hook"),description:r.z.string().optional().describe("Description of what the hook does"),eventType:r.z.string().describe("Event type that triggers this hook (e.g., session_idle, request_completed, custom:plan_ready)"),category:r.z.enum(["alchemy","session","message","editing","reading","file_management","documents","history","tool"]).default("session").describe("Hook category"),agent:r.z.string().describe("Agent template to execute when hook fires"),promptTemplate:r.z.string().describe("Prompt template with {{variable}} substitution"),condition:r.z.record(r.z.any()).optional().describe("Condition object for filtering when hook fires"),executionMode:r.z.enum(["same_session","sub_agent","background","notification","direct_report"]).default("background").describe("How to execute the hook"),timing:r.z.enum(["before","after"]).default("after").describe("When to fire relative to the event"),maxIterations:r.z.number().int().min(1).optional().describe("Maximum times to fire per session"),emitEvent:r.z.string().optional().describe("Custom event to emit on completion"),emitEventOnCondition:r.z.union([r.z.object({condition:r.z.object({field:r.z.string(),operator:r.z.enum(["contains","not_contains","equals","not_equals","matches","not_matches"]),value:r.z.string()}),event:r.z.string()}),r.z.array(r.z.object({condition:r.z.object({field:r.z.string(),operator:r.z.enum(["contains","not_contains","equals","not_equals","matches","not_matches"]),value:r.z.string()}),event:r.z.string()}))]).optional().describe("Conditional event emission based on output")}),exports.MetaWorkflowAddRecipeSchema=r.z.object({workflowId:r.z.string().describe("Workflow ID to add the recipe to"),recipeId:r.z.string().describe("Recipe ID to add"),position:r.z.number().int().min(0).optional().describe("Position in the workflow (0-based, appended if omitted)")}),exports.MetaWorkflowRemoveRecipeSchema=r.z.object({workflowId:r.z.string().describe("Workflow ID to remove the recipe from"),recipeId:r.z.string().describe("Recipe ID to remove")}),exports.MetaAgentListSchema=r.z.object({}),exports.MetaReferenceSchema=r.z.object({}),exports.TestingProcessListSchema=r.z.object({}),exports.TestingProcessGetSchema=r.z.object({processId:r.z.string().describe("Test process ID to retrieve")})
3507
3528
  const m=r.z.object({name:r.z.string().min(1).describe('Command name (e.g., "Build", "Unit Tests")'),command:r.z.string().min(1).describe('Shell command to run (e.g., "npm test")'),required:r.z.boolean().default(!0).describe("If true, failure blocks the request (default: true)"),timeout:r.z.number().int().min(10).max(3600).default(300).describe("Max execution time in seconds (default: 300)")})
@@ -3654,11 +3675,11 @@ if(R.length>0&&n.apiClient){const e=R.map((e=>e.documentId))
3654
3675
  try{await(0,p.ensureDocumentsNotLocked)(e,n.apiClient,y,g,"insert-lines-documents")}catch(e){if(e instanceof d.ValidationError)return{content:[{type:"text",text:e.message}],isError:!0}
3655
3676
  throw e}}k=l.FileOperationPhase.TASK_VALIDATION,g.incrementCounter("insert_lines_phase",1,{phase:k})
3656
3677
  const M=j.length>0?j[0].path:"document-operation",$=await l.validateTaskId(T,M,o,y,g,v.metricsPrefix),O=$?.requestId
3657
- let D=null
3658
- j.length>0&&O&&(D=await l.validateWorkItemStates(T,j[0].path,o,i,n,y,g,v.metricsPrefix))
3659
- let A={taskPromoted:!1,requestPromoted:!1}
3660
- j.length>0&&O&&D&&(A=await l.autoPromoteTaskStatus(T,$.status,O,D.request?.status,j[0].path,o,i,y,g,v.metricsPrefix),A.taskPromoted&&y.info("Auto-promoted task to in_progress for insert-lines operation",{taskId:T,requestId:O,previousStatus:$.status,newStatus:A.newTaskStatus,triggeringOperation:"insert-lines"}))
3661
- const F=await l.ensureCorrectWorkspaceContext({taskPromoted:A.taskPromoted,requestId:O,context:n,operation:"insert-lines",logger:y,metrics:g,metricsPrefix:v.metricsPrefix})
3678
+ let A=null
3679
+ j.length>0&&O&&(A=await l.validateWorkItemStates(T,j[0].path,o,i,n,y,g,v.metricsPrefix))
3680
+ let D={taskPromoted:!1,requestPromoted:!1}
3681
+ j.length>0&&O&&A&&(D=await l.autoPromoteTaskStatus(T,$.status,O,A.request?.status,j[0].path,o,i,y,g,v.metricsPrefix),D.taskPromoted&&y.info("Auto-promoted task to in_progress for insert-lines operation",{taskId:T,requestId:O,previousStatus:$.status,newStatus:D.newTaskStatus,triggeringOperation:"insert-lines"}))
3682
+ const F=await l.ensureCorrectWorkspaceContext({taskPromoted:D.taskPromoted,requestId:O,context:n,operation:"insert-lines",logger:y,metrics:g,metricsPrefix:v.metricsPrefix})
3662
3683
  if(!F.success)throw Error(F.error)
3663
3684
  if(j.length>0){y.debug("Re-validating file paths with correct workspace context",{operationCount:j.length,workspaceBasePath:n.workspaceManager?.getCurrentContext(n.currentSessionId).basePath})
3664
3685
  for(let e=0;e<j.length;e++){const t=j[e],r=P[e],{normalizedPath:o,fullPath:s}=l.validateAndNormalizePath(r.path,n)
@@ -3775,20 +3796,20 @@ for(const e of j)M.push({type:"document",documentId:e.documentId,replacements:e.
3775
3796
  if(M.length>0&&n.apiClient){const e=M.map((e=>e.documentId))
3776
3797
  try{await(0,p.ensureDocumentsNotLocked)(e,n.apiClient,w,g,"replace-lines-documents")}catch(e){if(e instanceof d.ValidationError)return{content:[{type:"text",text:e.message}],isError:!0}
3777
3798
  throw e}}v=l.FileOperationPhase.TASK_VALIDATION,g.incrementCounter("replace_lines_phase",1,{phase:v})
3778
- const $=R.length>0?R[0].path:"document-operation",O=await l.validateTaskId(P,$,o,w,g,C.metricsPrefix),D=O?.requestId
3779
- let A=null
3780
- R.length>0&&D&&(A=await l.validateWorkItemStates(P,R[0].path,o,i,n,w,g,C.metricsPrefix))
3799
+ const $=R.length>0?R[0].path:"document-operation",O=await l.validateTaskId(P,$,o,w,g,C.metricsPrefix),A=O?.requestId
3800
+ let D=null
3801
+ R.length>0&&A&&(D=await l.validateWorkItemStates(P,R[0].path,o,i,n,w,g,C.metricsPrefix))
3781
3802
  let F={taskPromoted:!1,requestPromoted:!1}
3782
- R.length>0&&D&&A&&(F=await l.autoPromoteTaskStatus(P,O.status,D,A.request?.status,R[0].path,o,i,w,g,C.metricsPrefix),F.taskPromoted&&w.info("Auto-promoted task to in_progress for replace-lines operation",{taskId:P,requestId:D,previousStatus:O.status,newStatus:F.newTaskStatus,triggeringOperation:"replace-lines"}))
3783
- const N=await l.ensureCorrectWorkspaceContext({taskPromoted:F.taskPromoted,requestId:D,context:n,operation:"replace-lines",logger:w,metrics:g,metricsPrefix:C.metricsPrefix})
3803
+ R.length>0&&A&&D&&(F=await l.autoPromoteTaskStatus(P,O.status,A,D.request?.status,R[0].path,o,i,w,g,C.metricsPrefix),F.taskPromoted&&w.info("Auto-promoted task to in_progress for replace-lines operation",{taskId:P,requestId:A,previousStatus:O.status,newStatus:F.newTaskStatus,triggeringOperation:"replace-lines"}))
3804
+ const N=await l.ensureCorrectWorkspaceContext({taskPromoted:F.taskPromoted,requestId:A,context:n,operation:"replace-lines",logger:w,metrics:g,metricsPrefix:C.metricsPrefix})
3784
3805
  if(!N.success)throw Error(N.error)
3785
3806
  if(R.length>0){w.debug("Re-validating file paths with correct workspace context",{operationCount:R.length,workspaceBasePath:n.workspaceManager?.getCurrentContext(n.currentSessionId).basePath})
3786
3807
  for(let e=0;e<R.length;e++){const t=R[e],r=x[e],{normalizedPath:o,fullPath:s}=l.validateAndNormalizePath(r.path,n)
3787
- R[e]={...t,path:o,fullPath:s}}}if(M.length>0&&D)try{await(0,u.validateRequestStatusForDocumentOperation)(D,i,w,g,"replace-lines-document")}catch(e){if(e instanceof d.ValidationError)return{content:[{type:"text",text:e.message}],isError:!0}
3808
+ R[e]={...t,path:o,fullPath:s}}}if(M.length>0&&A)try{await(0,u.validateRequestStatusForDocumentOperation)(A,i,w,g,"replace-lines-document")}catch(e){if(e instanceof d.ValidationError)return{content:[{type:"text",text:e.message}],isError:!0}
3788
3809
  throw e}v=l.FileOperationPhase.RECORD_CREATION,g.incrementCounter("replace_lines_phase",1,{phase:v})
3789
- const z=[...R.map((e=>({type:"file",path:e.path,replacements:e.replacements}))),...M.map((e=>({type:"document",documentId:e.documentId,replacements:e.replacements})))],{toolRecordId:L}=await l.createFileOperationRecords(P,D,n.projectId,C.recordType,$,{operationCount:E.length,fileOperations:R.length,documentOperations:M.length,operations:z,userComment:S},o,s,w,g,C.metricsPrefix,y)
3810
+ const z=[...R.map((e=>({type:"file",path:e.path,replacements:e.replacements}))),...M.map((e=>({type:"document",documentId:e.documentId,replacements:e.replacements})))],{toolRecordId:L}=await l.createFileOperationRecords(P,A,n.projectId,C.recordType,$,{operationCount:E.length,fileOperations:R.length,documentOperations:M.length,operations:z,userComment:S},o,s,w,g,C.metricsPrefix,y)
3790
3811
  if(b=L,k=L,v=C.fileOperationPhase,g.incrementCounter("replace_lines_phase",1,{phase:v}),R.length>0&&n.git)try{w.debug("[REPLACE-LINES] Checking for user changes before replace operations")
3791
- const e=await(0,h.commitUserChangesBeforeToolOperation)(n.git,"replace-lines",{requestId:D,taskId:P,workspaceManager:n.workspaceManager},w,g)
3812
+ const e=await(0,h.commitUserChangesBeforeToolOperation)(n.git,"replace-lines",{requestId:A,taskId:P,workspaceManager:n.workspaceManager},w,g)
3792
3813
  e.userChangesCommitted&&(w.debug("[REPLACE-LINES] User changes committed before replace operations",{commitHash:e.commitHash?.substring(0,8),filesCommitted:e.filesCommitted}),w.info("User changes committed before replace-lines operation",{taskId:P,fileOperationsCount:R.length,userCommitHash:e.commitHash?.substring(0,8),userFilesCommitted:e.filesCommitted}))}catch(e){w.debug("[REPLACE-LINES] Pre-tool commit failed, continuing with replacements",{error:e instanceof Error?e.message:e+""}),w.warn("Pre-tool user changes commit failed, continuing with replace operation",{taskId:P,fileOperationsCount:R.length,error:e instanceof Error?e.message:e+""})}w.debug("[REPLACE-LINES] Proceeding with file operations",{fileCount:R.length})
3793
3814
  const H=[]
3794
3815
  let B=0,U=0,W=0,G=0
@@ -3957,7 +3978,7 @@ n&&n.push({filePath:r,recordId:e.id,type:"replace-in-files",timestamp:e.createdA
3957
3978
  _.has(t)||_.set(t,[])
3958
3979
  const r=_.get(t)
3959
3980
  r&&r.push({filePath:t,recordId:e.id,type:e.type,timestamp:e.createdAt,commitHash:e.metadata.commitHash,status:e.metadata.status||"",record:e})})),s.info(`Grouped records by file path. Found ${_.size} unique files.`)
3960
- for(const[D,A]of _.entries())A.sort(((e,t)=>new Date(e.timestamp).getTime()-new Date(t.timestamp).getTime())),s.info(`File ${D} has ${A.length} records.`),s.info("Records with commit hashes: "+A.filter((e=>e.commitHash)).length)
3981
+ for(const[A,D]of _.entries())D.sort(((e,t)=>new Date(e.timestamp).getTime()-new Date(t.timestamp).getTime())),s.info(`File ${A} has ${D.length} records.`),s.info("Records with commit hashes: "+D.filter((e=>e.commitHash)).length)
3961
3982
  const E=new Map
3962
3983
  for(const[F,N]of _.entries()){s.info(`Processing file: ${F} (${N.length} records)`)
3963
3984
  const z=new Map
@@ -4009,8 +4030,8 @@ for(const xe of v.allMoves)S+=`- **${xe.source}** \u2192 **${xe.destination}**\n
4009
4030
  for(const[je,Re]of E.entries()){const{normalizedPath:Me,fullPath:$e}=d.validateAndNormalizePath(je,t)
4010
4031
  let Oe=!1
4011
4032
  try{await a.access($e),Oe=!0}catch{}S+=`### ${je} ${Oe?"":"(Deleted)"}\n\n`
4012
- const De=new Date(Re.firstChange.timestamp).toLocaleString(),Ae=new Date(Re.lastChange.timestamp).toLocaleString()
4013
- S+=`**First Change**: ${De}\n`,S+=`**Last Change**: ${Ae}\n`,"pre-task"===Re.firstChange.status&&Re.firstChange.commitHash?S+=`**Pre-Task Commit**: ${Re.firstChange.commitHash.substring(0,8)}\n`:Re.firstChange.commitHash&&(S+=`**File Created During Task**: ${Re.firstChange.commitHash.substring(0,8)}\n`),Re.lastChange.commitHash&&(S+=`**Final Commit**: ${Re.lastChange.commitHash.substring(0,8)}\n`)
4033
+ const Ae=new Date(Re.firstChange.timestamp).toLocaleString(),De=new Date(Re.lastChange.timestamp).toLocaleString()
4034
+ S+=`**First Change**: ${Ae}\n`,S+=`**Last Change**: ${De}\n`,"pre-task"===Re.firstChange.status&&Re.firstChange.commitHash?S+=`**Pre-Task Commit**: ${Re.firstChange.commitHash.substring(0,8)}\n`:Re.firstChange.commitHash&&(S+=`**File Created During Task**: ${Re.firstChange.commitHash.substring(0,8)}\n`),Re.lastChange.commitHash&&(S+=`**Final Commit**: ${Re.lastChange.commitHash.substring(0,8)}\n`)
4014
4035
  const Fe=v.sourceToMove.get(je),Ne=v.destToMove.get(je)
4015
4036
  Fe?S+=`**\u2139\ufe0f Move Operation**: This file was moved TO **${Fe.destination}**\n`:Ne&&(S+=`**\u2139\ufe0f Move Operation**: This file was moved FROM **${Ne.source}**\n`),g&&!P?Re.hasDiff?(S+="\n**Changes**:\n",S+="```diff\n",S+=Re.diffText,S+="\n```\n\n"):S+=`\n**Note**: ${Re.diffText}\n\n`:P&&Re.hasDiff?(S+=`\n**Note**: File has changes (${Re.diffText?Re.diffText.split("\n").length:0} lines). `,S+=`Compare ${Re.firstChange.commitHash?Re.firstChange.commitHash.substring(0,8):"unknown"}..${Re.lastChange.commitHash?Re.lastChange.commitHash.substring(0,8):"unknown"} to see changes.\n\n`):Re.hasDiff?S+="\n**Note**: File has changes. Use showDiff=true to see the diff.\n\n":S+=`\n**Note**: ${Re.diffText}\n\n`,S+="---\n\n"}return p({success:!0,fileCount:_.size}),{content:[{type:"text",text:S}]}}catch(ze){return s.error("Error in task file history tool",{error:ze instanceof Error?ze.stack:ze+""}),p({success:!1,error:ze instanceof Error?ze.name:"unknown"}),{content:[{type:"text",text:"Error: "+(0,u.getErrorMessage)(ze)}],isError:!0}}}
4016
4037
  const i=t(9860),a=s(t(1943)),c=t(9671),l=t(2960),d=s(t(5967)),u=t(8824),h=t(4466)},6928(module){"use strict"
@@ -4369,7 +4390,9 @@ let r=`\n\n\u26a0\ufe0f **Performance Warning**: Large untracked files detected
4369
4390
  for(const t of e.slice(0,5))r+=`- \`${t.path}\` (${t.sizeMB} MB)\n`
4370
4391
  return e.length>5&&(r+=`- ... and ${e.length-5} more large files\n`),r+="\nConsider adding these to `.gitignore` or moving them outside the repository.",r}
4371
4392
  const h=t(3444),p=t(9860),m=t(3922),g=t(8824),f=u(t(9896)),y=u(t(6928))
4372
- t(857)},7575(e,exports){"use strict"
4393
+ t(857)},7499(e,exports){"use strict"
4394
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.TEST_MAX_ROUNDS=void 0,exports.decideTestRoundCap=function(e,t,r=exports.TEST_MAX_ROUNDS){const n=e.filter((e=>e.id!==t&&("failed"===e.status||"error"===e.status))).length+1
4395
+ return{escalate:n>=r,failedRounds:n}},exports.TEST_MAX_ROUNDS=3},7575(e,exports){"use strict"
4373
4396
  var t
4374
4397
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.FileOperationPhase=void 0,function(e){e.VALIDATION="validation",e.TASK_VALIDATION="task_validation",e.RECORD_CREATION="record_creation",e.FILE_WRITE="file_write",e.STRING_REPLACEMENT="string_replacement",e.GIT_COMMIT="git_commit",e.RECORD_UPDATE_SUCCESS="record_update_success",e.RECORD_UPDATE_FAILURE="record_update_failure"}(t||(exports.FileOperationPhase=t={}))},7609(e,exports,t){"use strict"
4375
4398
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleUpdatePlan=async function(e,t,o,s){const i=s.child({tool:"plan_operations",operation:"update"})
@@ -4570,7 +4593,26 @@ return!1}async cleanup(){this.logger.info("Cleaning up multi-agent workspace man
4570
4593
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.ConnectionAwareClient=void 0
4571
4594
  const r=t(4192),n=t(7163)
4572
4595
  class o extends r.BaseApiClient{constructor(e,t,r,o,s,i){t.debug("ConnectionAwareClient constructor parameters:",{hasAuthToken:!!o,hasOptions:!!s,optionsType:s?typeof s:"undefined",options:s?JSON.stringify(s):"undefined",hasConnectionManager:!!i}),super(e,t,r,o,s),this.isGettingAuthToken=!1,this.connectionStatusManager=i||{isInOfflineMode(){return!1},isOffline(){return!1},setOfflineMode(){},async checkConnectivity(){return!0},getHealthCheckStats(){return{isOffline:!1,consecutiveFailures:0,currentBackoffMs:0,lastCheckTime:0}},cleanup(){}},o&&this.logger.debug("Client initialized with auth token",{clientType:this.constructor.name,tokenPrefix:o.substring(0,10)+"..."})
4573
- try{(0,n.debugTokenAccess)(this,"ConnectionAwareClient constructor")}catch(e){this.logger.debug("Error accessing token details during init",{error:e instanceof Error?e.message:e+""})}}setConnectionStatusManager(e){this.logger.debug("Setting connection status manager in ConnectionAwareClient"),this.connectionStatusManager=e}}exports.ConnectionAwareClient=o},7660(e,exports,t){"use strict"
4596
+ try{(0,n.debugTokenAccess)(this,"ConnectionAwareClient constructor")}catch(e){this.logger.debug("Error accessing token details during init",{error:e instanceof Error?e.message:e+""})}}setConnectionStatusManager(e){this.logger.debug("Setting connection status manager in ConnectionAwareClient"),this.connectionStatusManager=e}}exports.ConnectionAwareClient=o},7648(e,exports,t){"use strict"
4597
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleRecordCriteriaVerification=async function(e,t,i,a,c){const l=a.child({tool:"record_criteria_verification"}),d=c.startTimer("tool_execution_time")
4598
+ try{const{requestId:a,verifications:c}=e
4599
+ if(!a)throw(0,r.createValidationError)("requestId is required")
4600
+ if(!Array.isArray(c)||0===c.length)throw(0,r.createValidationError)("verifications array is required and must be non-empty")
4601
+ const u=i.getRequestClient()
4602
+ let h
4603
+ try{h=await u.getRequest(a)}catch(e){throw(0,r.createValidationError)(`Failed to fetch request ${a}: ${(0,n.getErrorMessage)(e)}`)}const p=h?.acceptanceCriteria||[]
4604
+ if(0===p.length)return d({success:!1,reason:"no_criteria"}),{content:[{type:"text",text:`Request ${a} has no acceptance criteria to record.`}],isError:!0}
4605
+ const m=(0,s.buildCriteriaKeyMap)(p),g=new Set(m.keys()),f=c.filter((e=>!g.has(e.key))).map((e=>e.key))
4606
+ if(f.length>0)return d({success:!1,reason:"unknown_keys"}),{content:[{type:"text",text:`Unknown criterion key(s): ${f.join(", ")}\n\nValid criteria:\n${(0,s.formatCriteriaWithKeys)(p)}`}],isError:!0}
4607
+ const y=c.map((e=>({criterionId:m.get(e.key).id,evidence:e.evidence,status:"unmet"===e.status?"unmet":"met"})))
4608
+ let w,I=null
4609
+ try{const e=await fetch(`${(0,o.getActionServerBaseUrl)()}/api/branch-head?requestId=${encodeURIComponent(a)}`,{signal:AbortSignal.timeout(5e3)})
4610
+ e.ok&&(I=(await e.json())?.data?.sha??null)}catch(e){l.debug("branch-head fetch failed; verdicts recorded without sha",{error:(0,n.getErrorMessage)(e)})}try{const e=await i.proxyPost(`/api/projects/${t.projectId}/requests/${a}/verify-criteria`,{verifications:y,dimension:"review",headSha:I})
4611
+ w=e?.data||e}catch(e){return d({success:!1,error:"record_failed"}),{content:[{type:"text",text:"Failed to record criteria verification: "+(0,n.getErrorMessage)(e)}],isError:!0}}l.info("Recorded criteria verification",{requestId:a,verified:w.verified,remaining:w.remainingUnverified})
4612
+ const b=y.filter((e=>"met"===e.status)).length,k=y.length-b
4613
+ return d({success:!0}),{content:[{type:"text",text:`Recorded verdicts for ${y.length} criterion/criteria on ${a} (${b} met, ${k} unmet). ${w.remainingUnverified} remain unmet overall; the server opens a rework task for the coder when any are unmet. This does NOT merge; the coder finalizes after review.`}]}}catch(e){const t=(0,n.getErrorMessage)(e)
4614
+ return l.error("record_criteria_verification failed",{error:t}),d({success:!1,error:"unexpected"}),{content:[{type:"text",text:"Error: "+t}],isError:!0}}}
4615
+ const r=t(9860),n=t(8824),o=t(9414),s=t(5454)},7660(e,exports,t){"use strict"
4574
4616
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.projectContextMiddleware=exports.ProjectContext=exports.ProjectIDFactory=exports.ProjectID=void 0
4575
4617
  const r=t(4055)
4576
4618
  Object.defineProperty(exports,"ProjectID",{enumerable:!0,get(){return r.ProjectID}}),Object.defineProperty(exports,"ProjectIDFactory",{enumerable:!0,get(){return r.ProjectIDFactory}})
@@ -4792,7 +4834,7 @@ if(!j){await a.default.mkdir(E,{recursive:!0}),g.info("Successfully created dire
4792
4834
  try{const e=c.default.join(E,".gitkeep")
4793
4835
  await a.default.writeFile(e,"# This file makes the directory trackable by Git\n"),$=!0,g.info("Created .gitkeep file for Git tracking")}catch(e){g.warn("Failed to create .gitkeep file",{error:e instanceof Error?e.message:e+""}),$=!1}}const O=Date.now()-f
4794
4836
  p.recordValue("create_directory_time",O),p.incrementCounter("create_directory_success",1),I=l.FileOperationPhase.GIT_COMMIT,p.incrementCounter("create_directory_phase",1,{phase:I})
4795
- const D=`Create directory: ${_} - ${h||"Create directory"}`,A=!j&&$?_+"/.gitkeep":_,F=await l.commitToGit(C,A,R,M,D,r,g,p,b.metricsPrefix)
4837
+ const A=`Create directory: ${_} - ${h||"Create directory"}`,D=!j&&$?_+"/.gitkeep":_,F=await l.commitToGit(C,D,R,M,A,r,g,p,b.metricsPrefix)
4796
4838
  I=l.FileOperationPhase.RECORD_UPDATE_SUCCESS,p.incrementCounter("create_directory_phase",1,{phase:I}),await l.updateFileOperationRecords(C,y,w,_,b.recordType,`Successfully ${j?"verified":"created"} directory: ${_}`,{status:"completed",commitHash:F.commitHash,previousCommitHash:F.previousCommitHash,directoryExists:j,isDirectory:!0,userComment:h},`Successfully ${j?"verified":"created"} directory: ${_}`,n,o,g,p,b.metricsPrefix)
4797
4839
  const N=r.getCurrentBranchContext?await r.getCurrentBranchContext():{branch:null,requestId:null,isMain:!0}
4798
4840
  return{content:[{type:"text",text:j?"Directory already exists":`Successfully created directory${F.commitHash?"\nCommit: "+F.commitHash.substring(0,8):""}${N.requestId&&!N.permanentBranch?"\nBranch: "+N.requestId:""}`}]}}catch(t){if(e.taskId&&o)try{const n=t instanceof d.NotFoundError||t instanceof d.ValidationError||t instanceof u.ZodError
@@ -5392,18 +5434,18 @@ if(R.length>0&&r.apiClient)try{await(0,f.ensureDocumentsNotLocked)(R,r.apiClient
5392
5434
  throw e}_=u.FileOperationPhase.TASK_VALIDATION,c.incrementCounter("replace_in_files_phase",1,{phase:_})
5393
5435
  const M=P[0],$=M.documentId?"document:"+M.documentId:M.path
5394
5436
  let O=Date.now()
5395
- const D=await u.validateTaskId(T,$,o,k,c,"replace_in_files")
5437
+ const A=await u.validateTaskId(T,$,o,k,c,"replace_in_files")
5396
5438
  Date.now()
5397
- const A=D?.requestId,F=P.some((e=>e.path))
5439
+ const D=A?.requestId,F=P.some((e=>e.path))
5398
5440
  let N=!1
5399
- if(F&&A){O=Date.now()
5441
+ if(F&&D){O=Date.now()
5400
5442
  const e=await u.validateWorkItemStates(T,$,o,i,r,k,c,"replace_in_files")
5401
5443
  Date.now(),O=Date.now()
5402
- const t=await u.autoPromoteTaskStatus(T,D.status,A,e.request?.status,$,o,i,k,c,"replace_in_files")
5403
- t.taskPromoted&&k.info("Auto-promoted task to in_progress",{taskId:T,requestId:A,previousStatus:D.status,newStatus:t.newTaskStatus}),Date.now(),O=Date.now()
5404
- const n=await u.ensureCorrectWorkspaceContext({taskPromoted:t.taskPromoted,requestId:A,context:r,operation:"replace_in_files",logger:k,metrics:c,metricsPrefix:"replace_in_files"})
5444
+ const t=await u.autoPromoteTaskStatus(T,A.status,D,e.request?.status,$,o,i,k,c,"replace_in_files")
5445
+ t.taskPromoted&&k.info("Auto-promoted task to in_progress",{taskId:T,requestId:D,previousStatus:A.status,newStatus:t.newTaskStatus}),Date.now(),O=Date.now()
5446
+ const n=await u.ensureCorrectWorkspaceContext({taskPromoted:t.taskPromoted,requestId:D,context:r,operation:"replace_in_files",logger:k,metrics:c,metricsPrefix:"replace_in_files"})
5405
5447
  if(!n.success)throw Error(n.error)
5406
- Date.now(),N=!0}if(R.length>0&&A)try{await(0,m.validateRequestStatusForDocumentOperation)(A,i,k,c,"replace_in_files-documents")}catch(e){if(e instanceof p.ValidationError)return{content:[{type:"text",text:e.message}],isError:!0}
5448
+ Date.now(),N=!0}if(R.length>0&&D)try{await(0,m.validateRequestStatusForDocumentOperation)(D,i,k,c,"replace_in_files-documents")}catch(e){if(e instanceof p.ValidationError)return{content:[{type:"text",text:e.message}],isError:!0}
5407
5449
  throw e}O=Date.now()
5408
5450
  const z=new Map
5409
5451
  if(k.info("\u{1f50d} Starting external edit detection",{hasFileOperations:F,hasGit:!!r.git,operationCount:P.length}),F&&r.git)for(const e of P)if(e.path&&!e.documentId)try{const t=u.validateAndNormalizePath(e.path,r),n=await l.default.readFile(t.fullPath,"utf8"),o=void 0!==e.fuzzy_match?e.fuzzy_match:x,s=[]
@@ -5412,9 +5454,9 @@ s.push(...r)}if(s.length>0){const o=(0,y.calculateReplacementRegion)(n,s,t.fullP
5412
5454
  k.info("\u{1f50d} Checking for external edits",{path:t.normalizedPath,affectedLines:o.lineRanges.map((e=>`${e.start}-${e.end}`)).join(", "),matchCount:s.length})
5413
5455
  const i=await(0,w.detectExternalEdits)(r.git,o,k)
5414
5456
  k.info("\u{1f50d} External edit detection result",{path:t.normalizedPath,hasExternalEdits:i.hasExternalEdits,conflictingLines:i.conflictingLines,hasMessage:!!i.message}),i.hasExternalEdits&&i.message?(z.set(e.path,{message:i.message,debugInfo:i.debugInfo}),k.warn("\u26a0\ufe0f External edits detected before auto-commit",{path:t.normalizedPath,conflictingLines:i.conflictingLines})):(i.debugInfo&&z.set(e.path,{message:"",debugInfo:i.debugInfo}),k.info("\u2705 No external edit conflicts detected",{path:t.normalizedPath}))}else k.info("\u2139\ufe0f No matches found for external edit detection",{path:t.normalizedPath}),z.set(e.path,{message:"",debugInfo:{detectionRan:!1,reason:"No matches found - patterns not found in file",affectedRegion:"none",uncommittedChanges:"not checked",overlapDetected:!1}})}catch(t){k.warn("Failed to check for external edits before auto-commit",{path:e.path,error:(0,I.getErrorMessage)(t)})}if(Date.now(),O=Date.now(),F&&r.git)try{k.debug("[REPLACE-IN-FILES] Checking for user changes before batch operation")
5415
- const e=await(0,g.commitUserChangesBeforeToolOperation)(r.git,"replace_in_files",{requestId:A,taskId:T,workspaceManager:r.workspaceManager},k,c)
5457
+ const e=await(0,g.commitUserChangesBeforeToolOperation)(r.git,"replace_in_files",{requestId:D,taskId:T,workspaceManager:r.workspaceManager},k,c)
5416
5458
  e.userChangesCommitted&&(k.debug("[REPLACE-IN-FILES] User changes committed",{commitHash:e.commitHash?.substring(0,8),filesCommitted:e.filesCommitted}),k.info("User changes committed before batch operation",{taskId:T,userCommitHash:e.commitHash?.substring(0,8),userFilesCommitted:e.filesCommitted}))}catch(e){k.warn("Pre-tool user changes commit failed",{taskId:T,error:e instanceof Error?e.message:e+""})}Date.now(),O=Date.now(),_=u.FileOperationPhase.RECORD_CREATION,c.incrementCounter("replace_in_files_phase",1,{phase:_})
5417
- const{toolRecordId:L}=await u.createFileOperationRecords(T,A,r.projectId,"replace-in-files",$,{operationCount:P.length,totalReplacements:P.reduce(((e,t)=>e+t.replacements.length),0),globalFuzzyMatch:x,userComment:j},o,s,k,c,"replace_in_files",b)
5459
+ const{toolRecordId:L}=await u.createFileOperationRecords(T,D,r.projectId,"replace-in-files",$,{operationCount:P.length,totalReplacements:P.reduce(((e,t)=>e+t.replacements.length),0),globalFuzzyMatch:x,userComment:j},o,s,k,c,"replace_in_files",b)
5418
5460
  C=L,Date.now(),O=Date.now(),_=u.FileOperationPhase.STRING_REPLACEMENT,c.incrementCounter("replace_in_files_phase",1,{phase:_})
5419
5461
  const H=[],B=[]
5420
5462
  for(const e of P){const t=e.path?z.get(e.path):void 0,o=await n(e,x,r,0,0,t?.message,t?.debugInfo)