alchemylab 1.4.8 → 1.4.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -109,25 +109,25 @@ const o=s(t(6928)),n=s(t(857)),i=s(t(9896)),a=t(3962),c=t(5419),l=t(5317),d=t(88
109
109
  exports.UnifiedLauncher=class{constructor(e,t,r){this.logger=e,this.installContext=t,this.configurer=r}async launch(e){const{projectPath:t,settings:s,multiAgent:o}=e,{installPath:n,nodePath:i}=this.installContext
110
110
  this.logger.info("UnifiedLauncher: Starting launch",{projectPath:t,installPath:n})
111
111
  try{const a=await this.configurer.configureOpenCode(n,t,i,s,o)
112
- return a.success?(this.logger.info("UnifiedLauncher: Configuration complete",{configPath:a.configPath}),this.logger.info("[plugin-diag] pre-spawn",r()),this.launchBackground(e,a)):(this.logger.error("UnifiedLauncher: Configuration failed",{error:a.error}),{success:!1,error:a.error})}catch(e){return this.logger.error("UnifiedLauncher: Launch failed",{error:(0,d.getErrorMessage)(e)}),{success:!1,error:(0,d.getErrorMessage)(e)}}}async launchBackground(e,s){const{projectPath:n,envVars:u}=e
113
- try{(0,l.execFileSync)("which",["opencode"],{stdio:"pipe"})}catch{return{success:!1,error:"OpenCode not found in PATH."}}this.logger.info("UnifiedLauncher: Launching opencode serve in background",{command:"opencode serve",cwd:n})
114
- const{resolveServeCommand:h}=await Promise.resolve().then(t.bind(t,9068)),g=h()
112
+ return a.success?(this.logger.info("UnifiedLauncher: Configuration complete",{configPath:a.configPath}),this.logger.info("[plugin-diag] pre-spawn",r()),this.launchBackground(e,a)):(this.logger.error("UnifiedLauncher: Configuration failed",{error:a.error}),{success:!1,error:a.error})}catch(e){return this.logger.error("UnifiedLauncher: Launch failed",{error:(0,d.getErrorMessage)(e)}),{success:!1,error:(0,d.getErrorMessage)(e)}}}async launchBackground(e,s){const{projectPath:n,envVars:u}=e,{resolveServeCommand:h,findOpencodeDir:g}=await Promise.resolve().then(t.bind(t,9068))
113
+ if(!g())try{(0,l.execFileSync)("which",["opencode"],{stdio:"pipe"})}catch{return{success:!1,error:"alchemylab-opencode package not installed and no system `opencode` on PATH."}}this.logger.info("UnifiedLauncher: Launching opencode serve in background",{command:"opencode serve",cwd:n})
114
+ const p=h()
115
115
  return new Promise((e=>{const t=o.default.join(n,".alchemy_lab","opencode"),h=o.default.join(t,"log")
116
- let p=null
117
- const m=e=>{if(!p)return
118
- const t=p
119
- p=null,t.end(e)}
116
+ let g=null
117
+ const m=e=>{if(!g)return
118
+ const t=g
119
+ g=null,t.end(e)}
120
120
  try{i.default.mkdirSync(h,{recursive:!0})
121
121
  const e=o.default.join(h,"opencode-serve.log")
122
- i.default.writeFileSync(e,`# opencode-serve.log \u2014 launched ${(new Date).toISOString()}\n`),p=i.default.createWriteStream(e,{flags:"a"})}catch(e){this.logger.warn("UnifiedLauncher: Could not open opencode log file",{error:(0,d.getErrorMessage)(e)})}const f=(0,l.spawn)(g.command,[...g.prependArgs,"--port","0","--data-dir",t],{stdio:["ignore","pipe","pipe"],cwd:n,detached:!0,env:{...process.env,...(0,c.getOpencodeSpawnEnv)(n,(0,a.isDevMode)()),...u}})
122
+ i.default.writeFileSync(e,`# opencode-serve.log \u2014 launched ${(new Date).toISOString()}\n`),g=i.default.createWriteStream(e,{flags:"a"})}catch(e){this.logger.warn("UnifiedLauncher: Could not open opencode log file",{error:(0,d.getErrorMessage)(e)})}const f=(0,l.spawn)(p.command,[...p.prependArgs,"--port","0","--data-dir",t],{stdio:["ignore","pipe","pipe"],cwd:n,detached:!0,env:{...process.env,...(0,c.getOpencodeSpawnEnv)(n,(0,a.isDevMode)()),...u}})
123
123
  let y="",v=!1
124
124
  const w=t=>{y+=t
125
125
  const o=y.match(/listening on http:\/\/[^:]+:(\d+)/)
126
126
  if(o&&!v){const t=parseInt(o[1],10)
127
127
  v=!0,this.logger.info("UnifiedLauncher: Discovered opencode serve port",{pid:f.pid,port:t}),this.logger.info("[plugin-diag] port-ready",{pid:f.pid,port:t,...r()}),setTimeout((()=>{this.logger.info("[plugin-diag] port-ready+5s",{pid:f.pid,...r()})}),5e3).unref(),setTimeout((()=>{this.logger.info("[plugin-diag] port-ready+15s",{pid:f.pid,...r()})}),15e3).unref(),f.unref(),e({success:!0,pid:f.pid,port:t,configPath:s.configPath})}}
128
- f.stdout?.on("data",(e=>{p&&p.write(e),w(e.toString())})),f.stderr?.on("data",(e=>{if(p){const t=e.toString().split("\n")
128
+ f.stdout?.on("data",(e=>{g&&g.write(e),w(e.toString())})),f.stderr?.on("data",(e=>{if(g){const t=e.toString().split("\n")
129
129
  for(let e=0;e<t.length;e++){const r=t[e]
130
- 0===r.length&&e===t.length-1||p.write(`[stderr] ${r}\n`)}}w(e.toString())})),f.on("exit",((e,t)=>{m(`\n# opencode exited code=${e} signal=${t} at=${(new Date).toISOString()}\n`)})),setTimeout((()=>{v||(v=!0,this.logger.warn("UnifiedLauncher: Timeout waiting for opencode serve port",{outputBuffer:y.slice(0,500)}),f.unref(),e({success:!0,pid:f.pid,configPath:s.configPath,error:"Could not determine port from opencode serve output"}))}),1e4),f.on("error",(t=>{v||(v=!0,this.logger.error("UnifiedLauncher: opencode serve failed to start",{error:t.message}),m(`\n# child error: ${t.message}\n`),e({success:!1,error:"Failed to start opencode serve: "+t.message}))})),f.on("exit",(t=>{v||(v=!0,this.logger.error("UnifiedLauncher: opencode serve exited early",{code:t,output:y.slice(0,500)}),e({success:!1,error:"opencode serve exited with code "+t}))}))}))}}},1253(e,exports,t){"use strict"
130
+ 0===r.length&&e===t.length-1||g.write(`[stderr] ${r}\n`)}}w(e.toString())})),f.on("exit",((e,t)=>{m(`\n# opencode exited code=${e} signal=${t} at=${(new Date).toISOString()}\n`)})),setTimeout((()=>{v||(v=!0,this.logger.warn("UnifiedLauncher: Timeout waiting for opencode serve port",{outputBuffer:y.slice(0,500)}),f.unref(),e({success:!0,pid:f.pid,configPath:s.configPath,error:"Could not determine port from opencode serve output"}))}),1e4),f.on("error",(t=>{v||(v=!0,this.logger.error("UnifiedLauncher: opencode serve failed to start",{error:t.message}),m(`\n# child error: ${t.message}\n`),e({success:!1,error:"Failed to start opencode serve: "+t.message}))})),f.on("exit",(t=>{v||(v=!0,this.logger.error("UnifiedLauncher: opencode serve exited early",{code:t,output:y.slice(0,500)}),e({success:!1,error:"opencode serve exited with code "+t}))}))}))}}},1253(e,exports,t){"use strict"
131
131
  var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
132
132
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.TokenManager=void 0
133
133
  const s=r(t(6928)),o=r(t(1943)),n=r(t(6982)),i=r(t(857)),a=t(9067),c=t(8824),l="alchemy-lab"
@@ -305,7 +305,7 @@ Object.defineProperty(exports,"__esModule",{value:!0}),exports.partMeta=function
305
305
  return{...e,_meta:{...s,alchemy:{...o,...r,kind:t}}}},exports.readAlchemyMeta=function(e){return e?._meta?.alchemy}},2366(e,exports,t){"use strict"
306
306
  var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
307
307
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.AlchemyActionServer=void 0
308
- const s=r(t(7252)),o=r(t(8577)),n=t(9896),i=t(5959),a=t(2640),c=t(4192),l=t(1152),d=r(t(6928)),u=r(t(9896)),h=r(t(857)),g=t(5607),p=t(8984),m=t(8524),f=t(8824),y=t(2096),v=t(7210),w=t(7227),I=t(5419),b=t(3962),E=t(2928),S=t(6563),C=t(1401),k=t(1292),T=t(8649),_=t(4306),j=t(9068),A=t(3435),P=t(8463),M=t(489),O=t(3807),R=t(7478),D=t(5317),x=t(355),$=t(833)
308
+ const s=r(t(7252)),o=r(t(8577)),n=t(9896),i=t(5959),a=t(2640),c=t(4192),l=t(1152),d=r(t(6928)),u=r(t(9896)),h=r(t(857)),g=t(5607),p=t(8984),m=t(8524),f=t(8824),y=t(2096),v=t(7210),w=t(7227),I=t(5419),b=t(3962),E=t(2928),S=t(6563),k=t(1401),C=t(1292),T=t(8649),_=t(4306),j=t(9068),A=t(3435),P=t(8463),M=t(489),O=t(3807),R=t(7478),D=t(5317),x=t(355),$=t(833)
309
309
  class q{updateAuthToken(e){this.options.authToken=e,this.apiClient.authToken=e,this.configurer.authToken=e}constructor(e,t){this.directReportDedup=new Map,this.activeMcpCalls=new Map,this.pendingAgentResync=!1,this.settingsSyncCounter=0,this.versionWarnings=[],this.updateBlocked=!1,this._pendingUpdateBlock=!1,this.isPolling=!1,this.isProcessingAction=!1,this.openCodeUrlCache=null,this.openCodePort=null,this.proxyAbortControllers=new Map,this.trackedChildPids=new Set,this.sseEventBuffer=[],this.droppedEventCount=0,this.firstDropAt=0,this.hasRemoteEventConsumers=!1,this.cachedProjectConfig=null,this.logger=e,this.options=t,this.port=t.port||3456,this.startTime=new Date
310
310
  const r=(0,l.createMetricsCollector)(!1)
311
311
  this.apiClient=new c.BaseApiClient(t.remoteServerUrl,e,r,t.authToken,{timeout:35e3}),this.configurer=new p.MCPClientConfigurer(e,t.remoteServerUrl,t.authToken),this.cliBridge=new m.CLIBridgeWebSocket(e),(0,m.setCLIBridge)(this.cliBridge),this.hookExecutor=new v.HookExecutor(e,"",t.projectPath,this.apiClient,{cachedDefaultModel:this.cachedDefaultModel,trackChildPid:e=>this.trackedChildPids.add(e)}),this.app=this.createApp()}createApp(){const e=(0,s.default)()
@@ -438,7 +438,7 @@ if(r&&!a.has(r))return void t.status(400).json({success:!1,error:"agent must be
438
438
  const c=await(0,T.spawnPlannerWithDoc)({agent:r||"planner",planId:s,prompt:o,title:n||"Planner",parentSessionId:i,apiClient:this.apiClient,logger:this.logger,projectId:this.projectId,projectPath:this.options.projectPath,trackChildPid:e=>this.trackedChildPids.add(e)})
439
439
  if(!c.success||!c.sessionId)return void t.status(500).json({success:!1,error:c.error||"spawn failed"})
440
440
  t.json({success:!0,data:{sessionId:c.sessionId,planId:c.planId,pid:c.pid}})}catch(e){(0,A.respondWithError)(t,this.logger,"Spawn planner",e)}}async handleSpawnSession(e,t){try{if(!(0,A.validateRequest)(e,t,this.projectId,["agent","prompt"]))return
441
- const{agent:r,prompt:s,requestId:o,parentSessionId:n,title:i,specialization:a,specializationHeadline:c,allowParallel:l,callerAgentType:d}=e.body,u=this.projectId,h=new k.SessionSpawnService({apiClient:this.apiClient,logger:this.logger,projectId:u,getDefaultModel:()=>this.cachedDefaultModel||void 0}),g=await h.prepareSession({agent:r,prompt:s,projectPath:this.options.projectPath,requestId:o,parentSessionId:n,title:i,specialization:a,specializationHeadline:c,allowParallel:l,callerAgentType:d})
441
+ const{agent:r,prompt:s,requestId:o,parentSessionId:n,title:i,specialization:a,specializationHeadline:c,allowParallel:l,callerAgentType:d}=e.body,u=this.projectId,h=new C.SessionSpawnService({apiClient:this.apiClient,logger:this.logger,projectId:u,getDefaultModel:()=>this.cachedDefaultModel||void 0}),g=await h.prepareSession({agent:r,prompt:s,projectPath:this.options.projectPath,requestId:o,parentSessionId:n,title:i,specialization:a,specializationHeadline:c,allowParallel:l,callerAgentType:d})
442
442
  t.json({success:!0,data:g})}catch(e){(0,A.respondWithError)(t,this.logger,"Spawn session",e)}}async handleRunTestsNow(e,r){try{if(!(0,A.validateRequest)(e,r,this.projectId,["requestId"]))return
443
443
  const{requestId:s,parentSessionId:o,testProcessIds:n,commandNames:i}=e.body||{},{handleRunTestsAction:a}=await Promise.resolve().then(t.bind(t,6555)),c={projectId:this.projectId,projectPath:this.options.projectPath,logger:this.logger,apiClient:this.apiClient,trackChildPid:e=>this.trackedChildPids.add(e),cachedDefaultModel:this.cachedDefaultModel??null},l=await a({requestId:s,parentSessionId:o,testProcessIds:n,commandNames:i,mode:"auto"},c)
444
444
  if(!l.success)return void r.status(500).json({success:!1,error:l.error||l.message})
@@ -598,7 +598,7 @@ if(s.ok){const e=await s.json(),t=e?.data?.settings||e?.settings
598
598
  void 0!==t?.defaultModel&&(this.updateDefaultModel(t.defaultModel),this.logger.info("Fetched defaultModel on startup via quick poll",{defaultModel:this.cachedDefaultModel}))}}catch(e){this.logger.debug("Quick poll for startup settings failed",{error:(0,f.getErrorMessage)(e)})}this.startPolling(),this.projectId&&this.startSSEEventForwarder(),this.projectId&&this.startStuckSessionWatchdog(),this.projectId&&this.startTimerHookScheduler(),this.projectId&&this.startQuestTickScheduler(),this.startMcpStaleCleanup()}trackChildPid(e){this.trackedChildPids.add(e)}killTrackedProcesses(){for(const e of this.trackedChildPids)try{process.kill(-e,"SIGTERM"),this.logger.info("Killed tracked process group",{pid:e})}catch(t){if("ESRCH"!==t?.code){try{process.kill(e,"SIGTERM")}catch{}this.logger.debug("Could not kill process group, fell back to single kill",{pid:e,error:(0,f.getErrorMessage)(t)})}}this.trackedChildPids.clear()
599
599
  try{const e=d.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
600
600
  if(u.default.existsSync(e)){const t=u.default.readFileSync(e,"utf-8"),r=JSON.parse(t)
601
- if(r.pid)try{process.kill(-r.pid,"SIGTERM"),this.logger.info("Killed OpenCode process group from config",{pid:r.pid})}catch(e){if("ESRCH"!==e?.code){try{process.kill(r.pid,"SIGTERM")}catch{}this.logger.debug("Could not kill OpenCode group, fell back to single kill",{pid:r.pid,error:(0,f.getErrorMessage)(e)})}}}}catch{}}startTimerHookScheduler(){this.timerHookScheduler=new S.TimerHookScheduler({hookExecutor:this.hookExecutor,apiClient:this.apiClient,logger:this.logger,getOpenCodeUrl:()=>this.getOpenCodeUrl(),getDefaultModel:()=>this.cachedDefaultModel||void 0,projectId:this.projectId||"",projectPath:this.options.projectPath}),this.timerHookScheduler.start()}startQuestTickScheduler(){this.questTickScheduler=new C.QuestTickScheduler({apiClient:this.apiClient,logger:this.logger,getOpenCodeUrl:()=>this.getOpenCodeUrl(),getDefaultModel:()=>this.cachedDefaultModel||void 0,projectId:this.projectId||"",projectPath:this.options.projectPath}),this.questTickScheduler.start()}startStuckSessionWatchdog(){this.stuckSessionWatchdog=new E.StuckSessionWatchdog({getOpenCodeUrl:()=>this.getOpenCodeUrl(),getDefaultModel:()=>this.cachedDefaultModel||void 0,logger:this.logger,onStuckDetected:e=>this.handleStuckSessionDetected(e),onStuckResolved:e=>this.handleStuckSessionResolved(e)}),this.stuckSessionWatchdog.start()}handleStuckSessionDetected(e){this.sseEventBuffer.push({type:"session.stuck",data:{type:"session.stuck",properties:{sessionId:e.sessionId,messageId:e.messageId,detectedAt:e.detectedAt,sessionTitle:e.sessionTitle,retryCount:e.retryCount}}}),this.logger.warn("Stuck session detected",{sessionId:e.sessionId,title:e.sessionTitle})}handleStuckSessionResolved(e){this.sseEventBuffer.push({type:"session.unstuck",data:{type:"session.unstuck",properties:{sessionId:e}}})}handleGetStuckSessions(e,t){const r=this.stuckSessionWatchdog?.getStuckSessions()??[]
601
+ if(r.pid)try{process.kill(-r.pid,"SIGTERM"),this.logger.info("Killed OpenCode process group from config",{pid:r.pid})}catch(e){if("ESRCH"!==e?.code){try{process.kill(r.pid,"SIGTERM")}catch{}this.logger.debug("Could not kill OpenCode group, fell back to single kill",{pid:r.pid,error:(0,f.getErrorMessage)(e)})}}}}catch{}}startTimerHookScheduler(){this.timerHookScheduler=new S.TimerHookScheduler({hookExecutor:this.hookExecutor,apiClient:this.apiClient,logger:this.logger,getOpenCodeUrl:()=>this.getOpenCodeUrl(),getDefaultModel:()=>this.cachedDefaultModel||void 0,projectId:this.projectId||"",projectPath:this.options.projectPath}),this.timerHookScheduler.start()}startQuestTickScheduler(){this.questTickScheduler=new k.QuestTickScheduler({apiClient:this.apiClient,logger:this.logger,getOpenCodeUrl:()=>this.getOpenCodeUrl(),getDefaultModel:()=>this.cachedDefaultModel||void 0,projectId:this.projectId||"",projectPath:this.options.projectPath}),this.questTickScheduler.start()}startStuckSessionWatchdog(){this.stuckSessionWatchdog=new E.StuckSessionWatchdog({getOpenCodeUrl:()=>this.getOpenCodeUrl(),getDefaultModel:()=>this.cachedDefaultModel||void 0,logger:this.logger,onStuckDetected:e=>this.handleStuckSessionDetected(e),onStuckResolved:e=>this.handleStuckSessionResolved(e)}),this.stuckSessionWatchdog.start()}handleStuckSessionDetected(e){this.sseEventBuffer.push({type:"session.stuck",data:{type:"session.stuck",properties:{sessionId:e.sessionId,messageId:e.messageId,detectedAt:e.detectedAt,sessionTitle:e.sessionTitle,retryCount:e.retryCount}}}),this.logger.warn("Stuck session detected",{sessionId:e.sessionId,title:e.sessionTitle})}handleStuckSessionResolved(e){this.sseEventBuffer.push({type:"session.unstuck",data:{type:"session.unstuck",properties:{sessionId:e}}})}handleGetStuckSessions(e,t){const r=this.stuckSessionWatchdog?.getStuckSessions()??[]
602
602
  t.json({success:!0,data:r})}handleTestInjectStuckSession(e,t){if(!this.stuckSessionWatchdog)return void t.status(503).json({success:!1,error:"Watchdog not running"})
603
603
  const{sessionId:r,title:s}=e.body||{},o=this.stuckSessionWatchdog.injectTestStuck(r,s)
604
604
  this.logger.info("Test-injected stuck session",{sessionId:o.sessionId}),t.json({success:!0,data:o})}async handleRetryStuckSession(e,t){const{sessionId:r}=e.params
@@ -899,9 +899,9 @@ break}await s(500)}for(;Date.now()<n;){const o=await r(e,t)
899
899
  if("error"===o)return{status:"error"}
900
900
  if(("idle"===o||"completed"===o)&&i)return{status:"completed"}
901
901
  "busy"!==o&&"running"!==o||(i=!0),await s(2e3)}return{status:"timeout"}}(y,w,g),b=Date.now()-v,E=await async function(e,t){try{const r=await fetch(`${e}/session/${t}/message?limit=200`,{signal:AbortSignal.timeout(1e4)})
902
- return r.ok?(await r.json()).map((e=>({role:e.info?.role||"unknown",text:(e.parts||[]).filter((e=>"text"===e.type)).map((e=>e.text)).join("\n"),toolCalls:(e.parts||[]).filter((e=>"tool_use"===e.type)).map((e=>({name:e.name,id:e.id}))),timestamp:e.info?.time?.created}))):[]}catch{return[]}}(y,w),S=function(e){return e.reduce(((e,t)=>e+(t.toolCalls?.length||0)),0)}(E),C=function(e){for(let t=e.length-1;t>=0;t--)if("assistant"===e[t].role&&e[t].text)return e[t].text
902
+ return r.ok?(await r.json()).map((e=>({role:e.info?.role||"unknown",text:(e.parts||[]).filter((e=>"text"===e.type)).map((e=>e.text)).join("\n"),toolCalls:(e.parts||[]).filter((e=>"tool_use"===e.type)).map((e=>({name:e.name,id:e.id}))),timestamp:e.info?.time?.created}))):[]}catch{return[]}}(y,w),S=function(e){return e.reduce(((e,t)=>e+(t.toolCalls?.length||0)),0)}(E),k=function(e){for(let t=e.length-1;t>=0;t--)if("assistant"===e[t].role&&e[t].text)return e[t].text
903
903
  return""}(E)
904
- return{success:!0,message:"Prompt completed",data:{sessionId:w,status:I.status,messages:E,toolCallCount:S,elapsedMs:b,finalResponse:C}}}
904
+ return{success:!0,message:"Prompt completed",data:{sessionId:w,status:I.status,messages:E,toolCallCount:S,elapsedMs:b,finalResponse:k}}}
905
905
  const o=t(8148)},4769(e,exports,t){"use strict"
906
906
  function r(e){return(0,o.getOpenCodeServerUrl)(e.projectPath,e.logger)}async function s(e,t,s,o){const n=r(s)
907
907
  if(!n||!t)return!1
@@ -1040,33 +1040,33 @@ if(u?.length)b=(await Promise.all(u.map((e=>f.get(`/api/projects/${y}/test-proce
1040
1040
  else{const e=await f.get(`/api/projects/${y}/test-processes/active`)
1041
1041
  b=e.success&&e.data?Array.isArray(e.data)?e.data:[e.data]:[]}if(0===b.length)return{success:!1,message:"No test processes configured for this project. Set up testing first.",error:"No test processes"}
1042
1042
  const E=Math.max(1,Math.min(8,b[0]?.config?.poolSize??2)),S=b[0]?.config?.fingerprint||[]
1043
- let C
1044
- try{C=await(0,c.acquireSlot)(v,E)}catch(e){return{success:!1,message:"Could not acquire test worktree slot: "+(0,i.getErrorMessage)(e),error:(0,i.getErrorMessage)(e)}}const k=C.path,T=(0,n.simpleGit)(v)
1045
- try{if((0,s.existsSync)(k))try{const e=(0,n.simpleGit)(k)
1046
- await e.raw(["reset","--hard",I]),m.info("Reset pool slot to latest branch HEAD",{worktreePath:k,branchName:I,slotIndex:C.index})}catch(e){m.warn("Could not reset slot, recreating",{error:(0,i.getErrorMessage)(e)})
1047
- try{await T.raw(["worktree","remove","--force",k])}catch{}}if(!(0,s.existsSync)(k)){(0,s.mkdirSync)((0,o.join)(v,".alchemy_lab","test-worktrees"),{recursive:!0})
1048
- try{if(!(await T.branch()).all.includes(I))return(0,c.releaseSlot)(k),{success:!1,message:`Branch ${I} not found`,error:"Branch not found"}
1049
- await T.raw(["worktree","add","--detach",k,I]),m.info("Created pool slot worktree (detached)",{worktreePath:k,branchName:I,slotIndex:C.index})}catch(e){if(!(e+"").includes("already exists"))return(0,c.releaseSlot)(k),{success:!1,message:"Failed to create pool slot worktree: "+(0,i.getErrorMessage)(e),error:(0,i.getErrorMessage)(e)}
1050
- m.info("Pool slot worktree already exists, using as-is",{worktreePath:k})}}}catch(e){throw(0,c.releaseSlot)(k),e}const _=await(0,c.computeFingerprint)(k,S),j=0===S.length||_!==C.storedFingerprint,A=0===S.length?null:_===C.storedFingerprint
1051
- j||m.info("Pool slot fingerprint matches; skipping setup",{worktreePath:k,slotIndex:C.index,fingerprintFiles:S})
1043
+ let k
1044
+ try{k=await(0,c.acquireSlot)(v,E)}catch(e){return{success:!1,message:"Could not acquire test worktree slot: "+(0,i.getErrorMessage)(e),error:(0,i.getErrorMessage)(e)}}const C=k.path,T=(0,n.simpleGit)(v)
1045
+ try{if((0,s.existsSync)(C))try{const e=(0,n.simpleGit)(C)
1046
+ await e.raw(["reset","--hard",I]),m.info("Reset pool slot to latest branch HEAD",{worktreePath:C,branchName:I,slotIndex:k.index})}catch(e){m.warn("Could not reset slot, recreating",{error:(0,i.getErrorMessage)(e)})
1047
+ try{await T.raw(["worktree","remove","--force",C])}catch{}}if(!(0,s.existsSync)(C)){(0,s.mkdirSync)((0,o.join)(v,".alchemy_lab","test-worktrees"),{recursive:!0})
1048
+ try{if(!(await T.branch()).all.includes(I))return(0,c.releaseSlot)(C),{success:!1,message:`Branch ${I} not found`,error:"Branch not found"}
1049
+ await T.raw(["worktree","add","--detach",C,I]),m.info("Created pool slot worktree (detached)",{worktreePath:C,branchName:I,slotIndex:k.index})}catch(e){if(!(e+"").includes("already exists"))return(0,c.releaseSlot)(C),{success:!1,message:"Failed to create pool slot worktree: "+(0,i.getErrorMessage)(e),error:(0,i.getErrorMessage)(e)}
1050
+ m.info("Pool slot worktree already exists, using as-is",{worktreePath:C})}}}catch(e){throw(0,c.releaseSlot)(C),e}const _=await(0,c.computeFingerprint)(C,S),j=0===S.length||_!==k.storedFingerprint,A=0===S.length?null:_===k.storedFingerprint
1051
+ j||m.info("Pool slot fingerprint matches; skipping setup",{worktreePath:C,slotIndex:k.index,fingerprintFiles:S})
1052
1052
  const{execSync:P}=await Promise.resolve().then(t.t.bind(t,5317,23))
1053
1053
  if(j){for(const e of b){const t=e.config?.setup||[]
1054
- if(0!==t.length){m.info("Running setup commands for test process",{processName:e.name,commandCount:t.length,slotIndex:C.index})
1055
- for(const r of t)try{m.info("Setup command",{command:r,worktreePath:k}),P(r,{cwd:k,timeout:12e4,encoding:"utf-8",stdio:["pipe","pipe","pipe"],shell:"/bin/sh",env:{...process.env,FORCE_COLOR:"0"}})}catch(t){m.warn("Setup command failed",{command:r,processName:e.name,exitCode:t.status,stderr:(t.stderr||"").slice(-500)})}}}S.length>0&&await(0,c.writeSlotFingerprint)(k,_),await(0,c.writeWarmSnapshot)(k)}else{const{removed:e}=await(0,c.restoreToWarmSnapshot)(k)
1056
- e>0&&m.info("Pruned drift from warm slot",{worktreePath:k,slotIndex:C.index,removed:e})}let M
1057
- try{const e=(0,n.simpleGit)(k)
1058
- M=(await e.revparse(["HEAD"])).trim()}catch(e){m.warn("Failed to capture worktree HEAD; commit_sha will be NULL on this run",{worktreePath:k,error:(0,i.getErrorMessage)(e)})}const O=[],R=[],D=[]
1054
+ if(0!==t.length){m.info("Running setup commands for test process",{processName:e.name,commandCount:t.length,slotIndex:k.index})
1055
+ for(const r of t)try{m.info("Setup command",{command:r,worktreePath:C}),P(r,{cwd:C,timeout:12e4,encoding:"utf-8",stdio:["pipe","pipe","pipe"],shell:"/bin/sh",env:{...process.env,FORCE_COLOR:"0"}})}catch(t){m.warn("Setup command failed",{command:r,processName:e.name,exitCode:t.status,stderr:(t.stderr||"").slice(-500)})}}}S.length>0&&await(0,c.writeSlotFingerprint)(C,_),await(0,c.writeWarmSnapshot)(C)}else{const{removed:e}=await(0,c.restoreToWarmSnapshot)(C)
1056
+ e>0&&m.info("Pruned drift from warm slot",{worktreePath:C,slotIndex:k.index,removed:e})}let M
1057
+ try{const e=(0,n.simpleGit)(C)
1058
+ M=(await e.revparse(["HEAD"])).trim()}catch(e){m.warn("Failed to capture worktree HEAD; commit_sha will be NULL on this run",{worktreePath:C,error:(0,i.getErrorMessage)(e)})}const O=[],R=[],D=[]
1059
1059
  let x
1060
- const $={worktreePath:k,commitSha:M,fingerprintHit:A}
1060
+ const $={worktreePath:C,commitSha:M,fingerprintHit:A}
1061
1061
  for(const process of b){let t
1062
1062
  const s=e.testRunIds?.[O.length]
1063
1063
  try{s?(await f.patch(`/api/projects/${y}/test-runs/${s}`,$),t=(await f.get(`/api/projects/${y}/test-runs/${s}`)).data,O.push(t.id)):(t=(await f.post(`/api/projects/${y}/test-runs`,{requestId:d,testProcessId:process.id,parentSessionId:h,...$})).data,O.push(t.id))}catch(e){m.error("Failed to get/create test run record",{requestId:d,processId:process.id,preCreatedId:s,error:(0,i.getErrorMessage)(e)})
1064
1064
  continue}const o=r(g,process,0,y,p,e.commandNames,e.skipCommandNames),n=p?"tester":"auto-tester"
1065
- try{const e=await(0,a.spawnActionAgent)({agent:n,prompt:o,cwd:k,projectPath:v,projectId:y,title:`[${d}] Test: ${process.name}`,requestId:d,parentSessionId:h,allowParallel:!0,apiClient:f,logger:m,trackChildPid:w})
1065
+ try{const e=await(0,a.spawnActionAgent)({agent:n,prompt:o,cwd:C,projectPath:v,projectId:y,title:`[${d}] Test: ${process.name}`,requestId:d,parentSessionId:h,allowParallel:!0,apiClient:f,logger:m,trackChildPid:w})
1066
1066
  if(!e.success)throw Error(e.error||"Spawn failed")
1067
- R.push(t.id),e.sessionId&&D.push(e.sessionId),e.pid&&(x=e.pid),e.sessionId&&(f.patch(`/api/projects/${y}/test-runs/${t.id}`,{testerSessionId:e.sessionId}).catch((e=>m.warn("Failed to set testerSessionId on test run",{error:(0,i.getErrorMessage)(e)}))),f.post(`/api/projects/${y}/session-test-process-associations`,{opencodeSessionId:e.sessionId,testProcessId:process.id}).catch((t=>m.warn("Failed to create session\u2194test-process association",{sessionId:e.sessionId,testProcessId:process.id,error:(0,i.getErrorMessage)(t)})))),m.info("Spawned test-runner agent",{testRunId:t.id,processName:process.name,worktreePath:k,pid:e.pid,sessionId:e.sessionId})}catch(e){m.error("Failed to spawn test-runner agent",{testRunId:t.id,error:(0,i.getErrorMessage)(e)})
1068
- try{await f.patch(`/api/projects/${y}/test-runs/${t.id}`,{status:"error",results:{steps:[],summary:"Failed to spawn test agent: "+(0,i.getErrorMessage)(e)}})}catch{}}}if(0===R.length)return(0,c.releaseSlot)(k),{success:!1,message:"Failed to spawn any test agents",error:"All agent spawns failed"}
1069
- for(const e of D);return(0,c.watchPidAndRelease)(x,k,12e5),{success:!0,message:`Started ${R.length} test run(s) for request ${d}`,data:{testRunIds:O,agentsSpawned:R.length,processCount:b.length,worktreePath:k,spawnedSessionIds:D}}}catch(e){const t=(0,i.getErrorMessage)(e)
1067
+ R.push(t.id),e.sessionId&&D.push(e.sessionId),e.pid&&(x=e.pid),e.sessionId&&(f.patch(`/api/projects/${y}/test-runs/${t.id}`,{testerSessionId:e.sessionId}).catch((e=>m.warn("Failed to set testerSessionId on test run",{error:(0,i.getErrorMessage)(e)}))),f.post(`/api/projects/${y}/session-test-process-associations`,{opencodeSessionId:e.sessionId,testProcessId:process.id}).catch((t=>m.warn("Failed to create session\u2194test-process association",{sessionId:e.sessionId,testProcessId:process.id,error:(0,i.getErrorMessage)(t)})))),m.info("Spawned test-runner agent",{testRunId:t.id,processName:process.name,worktreePath:C,pid:e.pid,sessionId:e.sessionId})}catch(e){m.error("Failed to spawn test-runner agent",{testRunId:t.id,error:(0,i.getErrorMessage)(e)})
1068
+ try{await f.patch(`/api/projects/${y}/test-runs/${t.id}`,{status:"error",results:{steps:[],summary:"Failed to spawn test agent: "+(0,i.getErrorMessage)(e)}})}catch{}}}if(0===R.length)return(0,c.releaseSlot)(C),{success:!1,message:"Failed to spawn any test agents",error:"All agent spawns failed"}
1069
+ for(const e of D);return(0,c.watchPidAndRelease)(x,C,12e5),{success:!0,message:`Started ${R.length} test run(s) for request ${d}`,data:{testRunIds:O,agentsSpawned:R.length,processCount:b.length,worktreePath:C,spawnedSessionIds:D}}}catch(e){const t=(0,i.getErrorMessage)(e)
1070
1070
  return m.error("run-tests action failed",{requestId:d,error:t}),{success:!1,message:"Failed to run tests: "+t,error:t}}}
1071
1071
  const s=t(9896),o=t(6928),n=t(3807),i=t(8824),a=t(8148),c=t(489)},6563(e,exports,t){"use strict"
1072
1072
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.TimerHookScheduler=void 0
@@ -1557,11 +1557,11 @@ t?.mode?.alchemy?.tools&&(m=t.mode.alchemy.tools,this.logger.info("Found existin
1557
1557
  i?.openCodeModel?b.model=i.openCodeModel:p?b.model=p:f&&(b.model=f)
1558
1558
  const S=c.join(s,".opencode","agent")
1559
1559
  await r.mkdir(S,{recursive:!0})
1560
- const C=E||"anthropic/claude-opus-4-5-20251101"
1561
- let k,T=null,_=null
1562
- if(i?.projectId)if(await this.seedAgentsFromRemote(i.projectId),_=await this.fetchProjectFromRemote(i.projectId),k=_?.metadata?.globalSystemPrompt,k&&this.logger.info("Using global system prompt from project metadata",{projectId:i.projectId,promptLength:k.length}),T=await this.fetchAgentsFromRemote(i.projectId),T&&T.length>0){this.logger.info("Using agents from remote database",{count:T.length})
1560
+ const k=E||"anthropic/claude-opus-4-5-20251101"
1561
+ let C,T=null,_=null
1562
+ if(i?.projectId)if(await this.seedAgentsFromRemote(i.projectId),_=await this.fetchProjectFromRemote(i.projectId),C=_?.metadata?.globalSystemPrompt,C&&this.logger.info("Using global system prompt from project metadata",{projectId:i.projectId,promptLength:C.length}),T=await this.fetchAgentsFromRemote(i.projectId),T&&T.length>0){this.logger.info("Using agents from remote database",{count:T.length})
1563
1563
  for(const e of T){if(!e.enabled)continue
1564
- const t=this.generateAgentMarkdown(e,d,C,k),s=c.join(S,e.name+".md")
1564
+ const t=this.generateAgentMarkdown(e,d,k,C),s=c.join(S,e.name+".md")
1565
1565
  await r.writeFile(s,t,"utf8"),this.logger.info(`Created ${e.name} agent file from remote config`,{agentPath:s,mode:e.mode})}}else this.logger.warn("No agents found - check remote server or enable agents via UI")
1566
1566
  else this.logger.warn("No projectId provided - agents must be configured via database")
1567
1567
  const j={$schema:"https://opencode.ai/config.json",model:E,mcp:{[d]:{type:"local",command:[n,u,s],enabled:!0,timeout:6e5}}}
@@ -1572,8 +1572,8 @@ for(const[r,s]of Object.entries(e.tools||{})){const e=s,n=r.replace(/-/g,"_")
1572
1572
  t[o.OPENCODE_BUILTIN_TOOLS.has(r)?r:`${d}_${n}`]=e}const r=_?.metadata?.bashToolAgents||{}
1573
1573
  e.name in r&&(t.bash=!!r[e.name]),t.skill=!1,("subagent"===e.mode||"tester"===e.name||"auto-tester"===e.name)&&void 0===t.task&&(t.task=!1)
1574
1574
  let s=""
1575
- k&&(s+=k.trim()+"\n\n"),e.prompt&&(s+=e.prompt),e.promptAddition&&(s+="\n\n"+e.promptAddition),j.agent[e.name]={tools:t,...s?{prompt:s}:{},...e.description?{description:e.description}:{},mode:"utility"===e.mode?"primary":e.mode||"primary",...e.color?{color:e.color}:{}}}j.agent.general={...j.agent.general,disable:!0},j.agent.explore={...j.agent.explore,tools:{grep:!1,glob:!1,list:!1,read:!1,bash:!1,[d+"_search"]:!0,[d+"_read_files"]:!0,[d+"_view_asset"]:!0,[d+"_query_history"]:!0,webfetch:!0,websearch:!0,codesearch:!0}}
1576
- const e=`---\ndescription: Fast codebase exploration and research subagent\nmode: subagent\n---\n\n${k?k.trim()+"\n\n":""}You are a codebase exploration agent. Your job is to find specific information and return concise, targeted answers.\n\n## Response Guidelines\n\n- **Be concise.** Return only what was asked for. A typical answer is 100-500 words.\n- **Lead with the answer.** State findings first, then supporting details.\n- **Reference specific files and lines.** Use \`file_path:line_number\` format.\n- **Don't exhaustively document everything you find.** Focus on what's relevant to the question.\n- **Use structured output** when listing multiple items (bullet points, not prose).\n\n## Strategy\n\n1. Start with \`search\` to find relevant files by name or content patterns\n2. Use \`read_files\` to read specific sections (use line ranges, don't read entire large files)\n3. Use \`query_history\` for understanding why code changed or tracing decisions\n\n## Anti-patterns to Avoid\n\n- Don't produce multi-thousand-word reports for simple questions\n- Don't read every file in a directory when a targeted search would suffice\n- Don't explain how tools work \u2014 just use them and report findings\n`
1575
+ C&&(s+=C.trim()+"\n\n"),e.prompt&&(s+=e.prompt),e.promptAddition&&(s+="\n\n"+e.promptAddition),j.agent[e.name]={tools:t,...s?{prompt:s}:{},...e.description?{description:e.description}:{},mode:"utility"===e.mode?"primary":e.mode||"primary",...e.color?{color:e.color}:{}}}j.agent.general={...j.agent.general,disable:!0},j.agent.explore={...j.agent.explore,tools:{grep:!1,glob:!1,list:!1,read:!1,bash:!1,[d+"_search"]:!0,[d+"_read_files"]:!0,[d+"_view_asset"]:!0,[d+"_query_history"]:!0,webfetch:!0,websearch:!0,codesearch:!0}}
1576
+ const e=`---\ndescription: Fast codebase exploration and research subagent\nmode: subagent\n---\n\n${C?C.trim()+"\n\n":""}You are a codebase exploration agent. Your job is to find specific information and return concise, targeted answers.\n\n## Response Guidelines\n\n- **Be concise.** Return only what was asked for. A typical answer is 100-500 words.\n- **Lead with the answer.** State findings first, then supporting details.\n- **Reference specific files and lines.** Use \`file_path:line_number\` format.\n- **Don't exhaustively document everything you find.** Focus on what's relevant to the question.\n- **Use structured output** when listing multiple items (bullet points, not prose).\n\n## Strategy\n\n1. Start with \`search\` to find relevant files by name or content patterns\n2. Use \`read_files\` to read specific sections (use line ranges, don't read entire large files)\n3. Use \`query_history\` for understanding why code changed or tracing decisions\n\n## Anti-patterns to Avoid\n\n- Don't produce multi-thousand-word reports for simple questions\n- Don't read every file in a directory when a targeted search would suffice\n- Don't explain how tools work \u2014 just use them and report findings\n`
1577
1577
  await r.writeFile(c.join(S,"explore.md"),e,"utf8"),this.logger.info("Created explore agent override with research prompt"),this.logger.info("Added agent tools to opencode.json",{agentCount:Object.keys(j.agent).length})}const A=e=>"string"==typeof e&&e.includes(".opencode/plugin/"),P=(await this.fetchPluginManifest())["opencode-claude-auth"],M=j.plugin,O=Array.isArray(M)?M.filter((e=>!A(e))):[]
1578
1578
  P&&this.applyAuthPluginPin(O,P),O.length>0?j.plugin=O:delete j.plugin,await r.writeFile(h,JSON.stringify(j,null,2),"utf8"),this.logger.info("OpenCode configured successfully",{configPath:h})
1579
1579
  try{const e=await Promise.resolve().then(t.t.bind(t,857,23)),s=c.join(e.homedir(),".alchemy_lab","opencode","config"),o=c.join(s,"opencode.json")
@@ -1634,12 +1634,12 @@ e&&(this.pollingStatus=n.AUTHENTICATED,this.tokenResult=e,this.stopPolling(),r&&
1634
1634
  function r(){const e=process.env.ALCHEMY_OPENCODE_DIR
1635
1635
  if(e)return e
1636
1636
  try{const e=("undefined"!=typeof require?require:t(4245)).resolve("alchemylab-opencode/package.json")
1637
- return(0,n.dirname)(e)}catch{}}function s(e){return 22===e||e>=24}function o(e,t){return{command:function(){try{const e=(0,a.execFileSync)("node",["--version"],{encoding:"utf8"}).trim()
1637
+ return(0,n.dirname)(e)}catch{}}function s(e){return e>=24}function o(e,t){return{command:function(){try{const e=(0,a.execFileSync)("node",["--version"],{encoding:"utf8"}).trim()
1638
1638
  if(s(parseInt(e.replace("v","").split(".")[0],10)))return"node"}catch{}const e=process.env.NVM_DIR||(0,n.join)(process.env.HOME||"",".nvm"),t=(0,n.join)(e,"versions","node")
1639
1639
  if((0,i.existsSync)(t))try{const e=(0,i.readdirSync)(t).filter((e=>s(parseInt(e.replace("v","").split(".")[0],10)))).sort(((e,t)=>{const r=parseInt(e.replace("v","").split(".")[0],10)
1640
1640
  return parseInt(t.replace("v","").split(".")[0],10)-r}))
1641
1641
  if(e.length>0){const r=(0,n.join)(t,e[0],"bin","node")
1642
- if((0,i.existsSync)(r))return r}}catch{}throw Error(`AlchemyLab requires Node.js 22 or 24+ to run the OpenCode server (Node 23 is not supported).\nYour current version: ${process.version}\nInstall a compatible version: nvm install 22 (or download from https://nodejs.org)`)}(),prependArgs:["--no-warnings",(0,n.join)(e,t)]}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.findOpencodeDir=r,exports.resolveServeCommand=function(){const e=r()
1642
+ if((0,i.existsSync)(r))return r}}catch{}throw Error(`AlchemyLab requires Node.js 24 or later to run the OpenCode server.\nYour current version: ${process.version}\nInstall a compatible version: nvm install 24 (or download from https://nodejs.org)`)}(),prependArgs:["--no-warnings",(0,n.join)(e,t)]}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.findOpencodeDir=r,exports.resolveServeCommand=function(){const e=r()
1643
1643
  return e?o((0,n.join)(e,"dist"),"serve.js"):{command:"opencode",prependArgs:["serve"]}},exports.resolveRunCommand=function(){const e=r()
1644
1644
  return e?o((0,n.join)(e,"dist"),"run.js"):{command:"opencode",prependArgs:[]}}
1645
1645
  const n=t(6928),i=t(9896),a=t(5317)},9266(e,exports,t){"use strict"
@@ -1697,63 +1697,50 @@ if(i.ok){const e=await i.json()
1697
1697
  return e.data?.id||e.id}s.info(`Project registration failed (HTTP ${i.status})`)}catch(e){s.info("Project registration failed",{error:(0,I.getErrorMessage)(e)})}}Object.defineProperty(i,"__esModule",{value:!0})
1698
1698
  const l=e(3444),d=e(9896),u=e(1943),h=e(6928),g=e(5317),p=e(857),m=e(1253),f=e(8984),y=e(2366),v=e(3962),w=e(1187),I=e(8824),b=e(6800),E=e(4139);(async function(){23===parseInt(process.version.replace("v","").split(".")[0],10)&&(console.error(`\n\u274c Node.js ${process.version} is not supported.`),console.error(" AlchemyLab requires Node.js 22 or 24+ (Node 23 has a broken sqlite API)."),console.error("\n Switch with: nvm use 22 or nvm use 24\n"),process.exit(1))
1699
1699
  const i=process.argv.slice(2)
1700
- let S
1701
- const C=i.indexOf("--port");-1!==C&&i[C+1]&&(S=parseInt(i[C+1],10),isNaN(S)&&(console.error("\u274c Invalid port number"),process.exit(1)))
1702
- const k=i.filter(((e,t)=>"--port"!==e&&(-1===C||t!==C+1)))
1703
- if(0===k.length)return void await async function(r={}){const i=process.cwd(),l=(0,h.join)(i,E.ALCHEMY_DIR_NAME)
1700
+ let g
1701
+ const p=i.indexOf("--port");-1!==p&&i[p+1]&&(g=parseInt(i[p+1],10),isNaN(g)&&(console.error("\u274c Invalid port number"),process.exit(1)))
1702
+ const S=i.filter(((e,t)=>"--port"!==e&&(-1===p||t!==p+1)))
1703
+ if(0===S.length)return void await async function(r={}){const i=process.cwd(),l=(0,h.join)(i,E.ALCHEMY_DIR_NAME)
1704
1704
  await(0,u.mkdir)(l,{recursive:!0})
1705
- const S=(0,h.join)(l,"server.log"),{createLogger:C}=await Promise.resolve().then(e.t.bind(e,2782,23)),k=C({serviceName:"alchemy-server",console:!1,logToFile:!0,logFilePath:S,baseDir:i}),T=await a(k),_=new m.TokenManager(k,i,{remoteServerUrl:T,allowInteractiveAuth:!1})
1706
- await _.initialize()
1707
- let j=await _.getToken()
1708
- if(!j?.token){console.log(""),console.log("\u{1f511} Not logged in. Opening browser to authenticate...")
1709
- const e=await _.startAuthenticationFlow()
1710
- console.log("\u{1f310} "+e.authUrl),o(e.authUrl),console.log(""),console.log("\u{1f446} Browser window opened. Complete login there."),console.log("\u23f1\ufe0f Waiting for authentication..."),await n(_)||(console.error("\u274c Authentication timed out. Run `alchemy` again to retry."),process.exit(1)),j=await _.getToken(),console.log(`\u2705 Welcome, ${j?.username||"user"}!`),console.log("")}let A=j?.token
1705
+ const g=(0,h.join)(l,"server.log"),{createLogger:p}=await Promise.resolve().then(e.t.bind(e,2782,23)),S=p({serviceName:"alchemy-server",console:!1,logToFile:!0,logFilePath:g,baseDir:i}),k=await a(S),C=new m.TokenManager(S,i,{remoteServerUrl:k,allowInteractiveAuth:!1})
1706
+ await C.initialize()
1707
+ let T=await C.getToken()
1708
+ if(!T?.token){console.log(""),console.log("\u{1f511} Not logged in. Opening browser to authenticate...")
1709
+ const e=await C.startAuthenticationFlow()
1710
+ console.log("\u{1f310} "+e.authUrl),o(e.authUrl),console.log(""),console.log("\u{1f446} Browser window opened. Complete login there."),console.log("\u23f1\ufe0f Waiting for authentication..."),await n(C)||(console.error("\u274c Authentication timed out. Run `alchemy` again to retry."),process.exit(1)),T=await C.getToken(),console.log(`\u2705 Welcome, ${T?.username||"user"}!`),console.log("")}let _=T?.token
1711
1711
  await async function(e,t,r,o){if(s(e))return
1712
1712
  console.log("\u{1f4c1} Initializing AlchemyLab project..."),console.log("")
1713
1713
  const n=(0,h.join)(e,E.ALCHEMY_DIR_NAME)
1714
1714
  let i
1715
1715
  await(0,u.mkdir)(n,{recursive:!0}),r?(console.log(`\u{1f4cb} Registering project "${(0,h.basename)(e)||"Local Project"}"...`),i=await c(t,r,e,o),console.log(i?"\u2705 Project registered: "+i:"\u26a0\ufe0f Project registration failed (will retry next run)")):console.log("\u26a0\ufe0f No auth token \u2014 skipping project registration")
1716
- const a=(0,h.join)(n,"project.json");(0,d.writeFileSync)(a,JSON.stringify({id:i||null,name:(0,h.basename)(e)||"Local Project",status:"active",registeredRemotely:!!i,createdAt:(new Date).toISOString(),updatedAt:(new Date).toISOString(),metadata:{basePath:e,alchemyPath:n}},null,2),"utf-8"),console.log("")}(i,T,A,k)
1717
- const P=(0,h.join)(i,E.ALCHEMY_DIR_NAME,"project.json")
1718
- let M
1719
- try{const e=JSON.parse((0,d.readFileSync)(P,"utf-8"))
1720
- if(M=e.id||void 0,!1===e.registeredRemotely&&A){const t=await c(T,A,i,k,"cli-init-retry")
1721
- t&&(e.id=t,e.registeredRemotely=!0,e.updatedAt=(new Date).toISOString(),(0,d.writeFileSync)(P,JSON.stringify(e,null,2),"utf-8"),M=t,console.log("\u2705 Project registered: "+t))}}catch{k.warn("Could not read project.json")}await async function(t){try{return void(0,g.execFileSync)("which",["opencode"],{stdio:"pipe"})}catch{}let r="latest"
1722
- try{const e=(0,h.join)(t,E.ALCHEMY_DIR_NAME,"config.json")
1723
- if((0,d.existsSync)(e)){const t=JSON.parse((0,d.readFileSync)(e,"utf-8"))
1724
- t.opencodeVersion&&(r=t.opencodeVersion)}}catch{}console.log(""),console.log("\u{1f4e6} OpenCode not found in PATH. Installing...")
1725
- const s="latest"===r?"latest":"v"+r
1726
- try{(0,g.execFileSync)("which",["go"],{stdio:"pipe"}),console.log(" Running: go install github.com/sst/opencode@"+s),(0,g.execFileSync)("go",["install","github.com/sst/opencode@"+s],{stdio:"inherit",timeout:12e4})
1727
- try{return(0,g.execFileSync)("which",["opencode"],{stdio:"pipe"}),void console.log("\u2705 OpenCode installed successfully via go install")}catch{console.log("\u26a0\ufe0f go install completed but opencode not found in PATH"),console.log(" Ensure $(go env GOPATH)/bin is in your PATH")}}catch{}try{const{platform:t,arch:s}=await Promise.resolve().then(e.t.bind(e,857,23)),o=t(),n=s(),i="darwin"===o?"darwin":"linux"===o?"linux":null,a="arm64"===n?"arm64":"x64"===n?"amd64":null
1728
- if(i&&a){const e=`opencode_${i}_${a}`,t="latest"===r?"https://github.com/sst/opencode/releases/latest/download/"+e:`https://github.com/sst/opencode/releases/download/v${r}/${e}`
1729
- console.log(" Downloading from: "+t)
1730
- const s=(0,h.join)((0,p.homedir)(),".local","bin")
1731
- await(0,u.mkdir)(s,{recursive:!0})
1732
- const o=(0,h.join)(s,"opencode")
1733
- return(0,g.execFileSync)("curl",["-fsSL","-o",o,t],{stdio:"inherit",timeout:6e4}),(0,g.execFileSync)("chmod",["+x",o],{stdio:"pipe"}),(process.env.PATH||"").split(":").includes(s)||(console.log(` \u26a0\ufe0f Add ${s} to your PATH:`),console.log(` export PATH="${s}:$PATH"`),console.log(""),process.env.PATH=`${s}:${process.env.PATH}`),void console.log("\u2705 OpenCode installed successfully")}}catch(e){console.log("\u26a0\ufe0f Binary download failed: "+(0,I.getErrorMessage)(e))}console.log(""),console.log("\u26a0\ufe0f Could not auto-install OpenCode."),console.log(""),console.log(" Install manually:"),console.log(" go install github.com/sst/opencode@latest"),console.log(" Or download from:"),console.log(" https://github.com/sst/opencode/releases"),console.log("")}(i)
1734
- const O=t()
1735
- console.log(["\n .oo 8 8 o 8 \n .P 8 8 8 8 8 \n .P 8 8 .oPYo. 8oPYo. .oPYo. ooYoYo. o o 8 .oPYo. 8oPYo. \n oPooo8 8 8 ' 8 8 8oooo8 8' 8 8 8 8 8 .oooo8 8 8 \n .P 8 8 8 . 8 8 8. 8 8 8 8 8 8 8 8 8 8 \n.P 8 8 `YooP' 8 8 `Yooo' 8 8 8 `YooP8 8oooo `YooP8 `YooP' \n..:::::....:.....:..:::..:.....:..:..:..:....8 ......:.....::.....:","::::::::::::::::::::::::::::::::::::::::::ooP'.::::::::::::::::::: v"+O,"::::::::::::::::::::::::::::::::::::::::::...::::::::::::::::::::::\n"].join("\n")),console.log("\u{1f4c1} Project: "+i)
1736
- const R=r.port||await(0,b.findAvailablePort)(3456),D=new y.AlchemyActionServer(k,{projectPath:i,remoteServerUrl:T,port:R,authToken:A,tokenManager:_})
1737
- try{const t=(0,v.getInstallContext)(),r=new f.MCPClientConfigurer(k,T,A),s=new w.UnifiedLauncher(k,t,r),[,a,c]=await Promise.all([D.start(),(async()=>{if(process.env.FRONTEND_URL)return process.env.FRONTEND_URL
1738
- if(T.includes("api.alchemylabcode.com"))return"https://alchemylabcode.com"
1739
- try{const e=await fetch(T+"/health")
1716
+ const a=(0,h.join)(n,"project.json");(0,d.writeFileSync)(a,JSON.stringify({id:i||null,name:(0,h.basename)(e)||"Local Project",status:"active",registeredRemotely:!!i,createdAt:(new Date).toISOString(),updatedAt:(new Date).toISOString(),metadata:{basePath:e,alchemyPath:n}},null,2),"utf-8"),console.log("")}(i,k,_,S)
1717
+ const j=(0,h.join)(i,E.ALCHEMY_DIR_NAME,"project.json")
1718
+ let A
1719
+ try{const e=JSON.parse((0,d.readFileSync)(j,"utf-8"))
1720
+ if(A=e.id||void 0,!1===e.registeredRemotely&&_){const t=await c(k,_,i,S,"cli-init-retry")
1721
+ t&&(e.id=t,e.registeredRemotely=!0,e.updatedAt=(new Date).toISOString(),(0,d.writeFileSync)(j,JSON.stringify(e,null,2),"utf-8"),A=t,console.log("\u2705 Project registered: "+t))}}catch{S.warn("Could not read project.json")}const P=t()
1722
+ console.log(["\n .oo 8 8 o 8 \n .P 8 8 8 8 8 \n .P 8 8 .oPYo. 8oPYo. .oPYo. ooYoYo. o o 8 .oPYo. 8oPYo. \n oPooo8 8 8 ' 8 8 8oooo8 8' 8 8 8 8 8 .oooo8 8 8 \n .P 8 8 8 . 8 8 8. 8 8 8 8 8 8 8 8 8 8 \n.P 8 8 `YooP' 8 8 `Yooo' 8 8 8 `YooP8 8oooo `YooP8 `YooP' \n..:::::....:.....:..:::..:.....:..:..:..:....8 ......:.....::.....:","::::::::::::::::::::::::::::::::::::::::::ooP'.::::::::::::::::::: v"+P,"::::::::::::::::::::::::::::::::::::::::::...::::::::::::::::::::::\n"].join("\n")),console.log("\u{1f4c1} Project: "+i)
1723
+ const M=r.port||await(0,b.findAvailablePort)(3456),O=new y.AlchemyActionServer(S,{projectPath:i,remoteServerUrl:k,port:M,authToken:_,tokenManager:C})
1724
+ try{const t=(0,v.getInstallContext)(),r=new f.MCPClientConfigurer(S,k,_),s=new w.UnifiedLauncher(S,t,r),[,a,c]=await Promise.all([O.start(),(async()=>{if(process.env.FRONTEND_URL)return process.env.FRONTEND_URL
1725
+ if(k.includes("api.alchemylabcode.com"))return"https://alchemylabcode.com"
1726
+ try{const e=await fetch(k+"/health")
1740
1727
  if(e.ok){const t=await e.json()
1741
- if(t?.data?.frontendUrl)return t.data.frontendUrl}}catch{}return T.replace(/:\d+/,":3000")})(),s.launch({projectPath:i,settings:M?{projectId:M}:void 0}).catch((e=>(k.warn("Failed to launch OpenCode serve",{error:(0,I.getErrorMessage)(e)}),{success:!1,error:(0,I.getErrorMessage)(e)})))])
1742
- if(console.log(""),console.log(" Action server .... port "+D.getPort()),c.success&&"port"in c&&c.port){"pid"in c&&c.pid&&D.trackChildPid(c.pid),D.setOpenCodePort(c.port),console.log(" OpenCode serve ... port "+c.port)
1743
- const e=(0,h.join)(i,E.ALCHEMY_DIR_NAME,"opencode.json");(0,d.writeFileSync)(e,JSON.stringify({port:c.port,pid:"pid"in c?c.pid:void 0,startedAt:(new Date).toISOString()},null,2)),k.info("Wrote OpenCode config",{path:e,port:c.port})}else c.success?console.log(" OpenCode serve ... started"):console.log(" OpenCode serve ... failed (run manually: opencode serve)")
1728
+ if(t?.data?.frontendUrl)return t.data.frontendUrl}}catch{}return k.replace(/:\d+/,":3000")})(),s.launch({projectPath:i,settings:A?{projectId:A}:void 0}).catch((e=>(S.warn("Failed to launch OpenCode serve",{error:(0,I.getErrorMessage)(e)}),{success:!1,error:(0,I.getErrorMessage)(e)})))])
1729
+ if(console.log(""),console.log(" Action server .... port "+O.getPort()),c.success&&"port"in c&&c.port){"pid"in c&&c.pid&&O.trackChildPid(c.pid),O.setOpenCodePort(c.port),console.log(" OpenCode serve ... port "+c.port)
1730
+ const e=(0,h.join)(i,E.ALCHEMY_DIR_NAME,"opencode.json");(0,d.writeFileSync)(e,JSON.stringify({port:c.port,pid:"pid"in c?c.pid:void 0,startedAt:(new Date).toISOString()},null,2)),S.info("Wrote OpenCode config",{path:e,port:c.port})}else c.success?console.log(" OpenCode serve ... started"):console.log(" OpenCode serve ... failed (run manually: opencode serve)")
1744
1731
  console.log(""),console.log(` \u2192 Alchemy UI: \x1b]8;;${a}\x07${a}\x1b]8;;\x07`),console.log("")
1745
1732
  let l=!1,u=null
1746
- const g=async()=>{l||(l=!0,u&&clearInterval(u),console.log(""),console.log("Shutting down..."),await D.stop(),process.exit(0))}
1733
+ const g=async()=>{l||(l=!0,u&&clearInterval(u),console.log(""),console.log("Shutting down..."),await O.stop(),process.exit(0))}
1747
1734
  process.on("SIGTERM",g),process.on("SIGINT",g)
1748
- const{InteractiveShell:p}=await e.e(994).then(e.t.bind(e,5994,23)),m=new p({projectPath:i,logger:k,remoteServerUrl:T,authToken:A,projectId:M})
1735
+ const{InteractiveShell:p}=await e.e(994).then(e.t.bind(e,5994,23)),m=new p({projectPath:i,logger:S,remoteServerUrl:k,authToken:_,projectId:A})
1749
1736
  let y=!1
1750
- u=setInterval((async()=>{if(!y)try{if(await _.hasValidToken())return
1737
+ u=setInterval((async()=>{if(!y)try{if(await C.hasValidToken())return
1751
1738
  y=!0,console.log(""),console.log("\u26a0\ufe0f Auth token expired. Opening browser to re-authenticate...")
1752
- const e=await _.startAuthenticationFlow()
1753
- if(console.log("\u{1f310} "+e.authUrl),o(e.authUrl),console.log("\u23f1\ufe0f Waiting for re-authentication..."),await n(_)){const e=await _.getToken()
1754
- e?.token&&(A=e.token,D.updateAuthToken(A)),console.log("\u2705 Re-authenticated successfully.")}else console.log("\u274c Re-authentication timed out. Some features may not work until you restart.")}catch(e){k.warn("Token watchdog check failed",{error:(0,I.getErrorMessage)(e)})}finally{y=!1}}),3e5),await m.start(g)}catch(e){k.error("Failed to start server",{error:(0,I.getErrorMessage)(e)}),console.error("\u274c Failed to start: "+(0,I.getErrorMessage)(e)),process.exit(1)}}({port:S})
1755
- const T=k[0].toLowerCase()
1756
- switch("--help"!==T&&"-h"!==T||(r(),process.exit(0)),"--version"!==T&&"-v"!==T||(console.log("alchemy v"+t()),process.exit(0)),T){case"serve":await async function(e={}){const r=process.cwd()
1739
+ const e=await C.startAuthenticationFlow()
1740
+ if(console.log("\u{1f310} "+e.authUrl),o(e.authUrl),console.log("\u23f1\ufe0f Waiting for re-authentication..."),await n(C)){const e=await C.getToken()
1741
+ e?.token&&(_=e.token,O.updateAuthToken(_)),console.log("\u2705 Re-authenticated successfully.")}else console.log("\u274c Re-authentication timed out. Some features may not work until you restart.")}catch(e){S.warn("Token watchdog check failed",{error:(0,I.getErrorMessage)(e)})}finally{y=!1}}),3e5),await m.start(g)}catch(e){S.error("Failed to start server",{error:(0,I.getErrorMessage)(e)}),console.error("\u274c Failed to start: "+(0,I.getErrorMessage)(e)),process.exit(1)}}({port:g})
1742
+ const k=S[0].toLowerCase()
1743
+ switch("--help"!==k&&"-h"!==k||(r(),process.exit(0)),"--version"!==k&&"-v"!==k||(console.log("alchemy v"+t()),process.exit(0)),k){case"serve":await async function(e={}){const r=process.cwd()
1757
1744
  s(r)||(console.log("\u{1f4c1} Current directory is not an AlchemyLab project."),console.log(""),console.log("\u{1f4a1} Run `alchemy` to initialize this project."),console.log(""),process.exit(1))
1758
1745
  const o=(0,l.createEnvLogger)({serviceName:"alchemy-server",console:!0,baseDir:r}),n=t()
1759
1746
  console.log(`\n\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n\u2551 AlchemyLab Server v${n.padEnd(10)} \u2551\n\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d\n`),console.log("\u{1f4c1} Project: "+r)
@@ -1765,9 +1752,9 @@ const h=e.port||await(0,b.findAvailablePort)(3456),g=new y.AlchemyActionServer(o
1765
1752
  try{await g.start(),console.log(""),console.log("\u2705 Action server running!"),console.log(` Health: http://127.0.0.1:${g.getPort()}/health`),console.log(""),console.log("Press Ctrl+C to stop the server."),console.log("")
1766
1753
  let e=!1
1767
1754
  const t=async()=>{e||(e=!0,console.log(""),console.log("Shutting down action server..."),await g.stop(),process.exit(0))}
1768
- process.on("SIGTERM",t),process.on("SIGINT",t),await new Promise((()=>{}))}catch(e){o.error("Failed to start server",{error:(0,I.getErrorMessage)(e)}),console.error("\u274c Failed to start: "+(0,I.getErrorMessage)(e)),process.exit(1)}}({port:S})
1755
+ process.on("SIGTERM",t),process.on("SIGINT",t),await new Promise((()=>{}))}catch(e){o.error("Failed to start server",{error:(0,I.getErrorMessage)(e)}),console.error("\u274c Failed to start: "+(0,I.getErrorMessage)(e)),process.exit(1)}}({port:g})
1769
1756
  break
1770
1757
  case"logout":await async function(){const e=process.cwd(),t=(0,l.createEnvLogger)({serviceName:"alchemy-cli",console:!1,baseDir:e}),r=await a(t),s=new m.TokenManager(t,e,{remoteServerUrl:r,allowInteractiveAuth:!1})
1771
1758
  await s.initialize(),await s.hasValidToken()?await s.removeToken()?console.log("Logged out successfully."):(console.error("Failed to remove auth token."),process.exit(1)):console.log("Not currently logged in.")}()
1772
1759
  break
1773
- default:console.error("\u274c Unknown command: "+T),console.log(""),r(),process.exit(1)}})().catch((e=>{console.error("\u{1f4a5} Unexpected error:",e),process.exit(1)}))})(),module.exports=i})()
1760
+ default:console.error("\u274c Unknown command: "+k),console.log(""),r(),process.exit(1)}})().catch((e=>{console.error("\u{1f4a5} Unexpected error:",e),process.exit(1)}))})(),module.exports=i})()
@@ -3324,23 +3324,21 @@ try{const e=await c.proxyPost(`/api/projects/${t.projectId}/requests/${n}/valida
3324
3324
  g=e?.data||e}catch(e){const t=(0,s.getErrorMessage)(e)
3325
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
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()
3328
- let y="auto-merge"!==g.completionMode&&!g.testsSkipped
3329
- const w="manual-review"===g.reviewMode,I=y&&!1===g.hasActiveTestProcesses&&("test-by-default"===g.completionMode||"always-test"===g.completionMode)
3330
- I&&(y=!1)
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
3331
3330
  let b,k=null
3332
- if(y)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
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
3333
3332
  if(i&&"passed"===i.status&&i.commitSha){let e=null
3334
3333
  if(r&&r.ok){const t=await r.json()
3335
- e=t?.data?.sha??null}e&&e===i.commitSha&&(y=!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=y||w?"ready_for_review":"ready_for_merge"
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"
3336
3335
  try{await f.updateRequest(n,{status:v,completionSummary:o,...b?{skippedTestRunId:b}:{}})}catch(e){const t=(0,s.getErrorMessage)(e)
3337
3336
  return m({success:!1,error:"status_transition_failed"}),{content:[{type:"text",text:"Failed to transition request: "+t}],isError:!0}}let C
3338
3337
  try{C=await f.getRequest(n)}catch(e){const t=(0,s.getErrorMessage)(e)
3339
3338
  return m({success:!1,error:"fetch_request_failed"}),{content:[{type:"text",text:"Failed to fetch request criteria: "+t}],isError:!0}}const _=C?.acceptanceCriteria||[]
3340
- if(m({success:!0}),y){const e=g.completionMode
3341
- let t
3342
- return t="ask-me"===e?"A notification has been created asking the user to choose how to handle testing (auto-test, interactive-test, or skip).":"always-test"===e?"Tests are running automatically in the background (always-test 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.":"Tests are running automatically in the background (test-by-default 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.",{content:[{type:"text",text:`Request ${n} moved to ready_for_review. ${t} Do NOT call merge or any other tool. Your work is done. Stop here and wait.`}]}}if(w)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.`}]}
3343
- const E=k?`\n\n_Skipped re-running tests: ${k}._\n`:"",S=I?`\n\n_No test processes are configured for this project; ${g.completionMode} mode is being treated as auto-merge._\n`:""
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`:""
3344
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=""
3345
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})
3346
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}}}
@@ -5165,12 +5163,12 @@ e&&(this.pollingStatus=s.AUTHENTICATED,this.tokenResult=e,this.stopPolling(),r&&
5165
5163
  function r(){const e=process.env.ALCHEMY_OPENCODE_DIR
5166
5164
  if(e)return e
5167
5165
  try{const e=("undefined"!=typeof require?require:t(4245)).resolve("alchemylab-opencode/package.json")
5168
- return(0,s.dirname)(e)}catch{}}function n(e){return 22===e||e>=24}function o(e,t){return{command:function(){try{const e=(0,a.execFileSync)("node",["--version"],{encoding:"utf8"}).trim()
5166
+ return(0,s.dirname)(e)}catch{}}function n(e){return e>=24}function o(e,t){return{command:function(){try{const e=(0,a.execFileSync)("node",["--version"],{encoding:"utf8"}).trim()
5169
5167
  if(n(parseInt(e.replace("v","").split(".")[0],10)))return"node"}catch{}const e=process.env.NVM_DIR||(0,s.join)(process.env.HOME||"",".nvm"),t=(0,s.join)(e,"versions","node")
5170
5168
  if((0,i.existsSync)(t))try{const e=(0,i.readdirSync)(t).filter((e=>n(parseInt(e.replace("v","").split(".")[0],10)))).sort(((e,t)=>{const r=parseInt(e.replace("v","").split(".")[0],10)
5171
5169
  return parseInt(t.replace("v","").split(".")[0],10)-r}))
5172
5170
  if(e.length>0){const r=(0,s.join)(t,e[0],"bin","node")
5173
- if((0,i.existsSync)(r))return r}}catch{}throw Error(`AlchemyLab requires Node.js 22 or 24+ to run the OpenCode server (Node 23 is not supported).\nYour current version: ${process.version}\nInstall a compatible version: nvm install 22 (or download from https://nodejs.org)`)}(),prependArgs:["--no-warnings",(0,s.join)(e,t)]}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.findOpencodeDir=r,exports.resolveServeCommand=function(){const e=r()
5171
+ if((0,i.existsSync)(r))return r}}catch{}throw Error(`AlchemyLab requires Node.js 24 or later to run the OpenCode server.\nYour current version: ${process.version}\nInstall a compatible version: nvm install 24 (or download from https://nodejs.org)`)}(),prependArgs:["--no-warnings",(0,s.join)(e,t)]}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.findOpencodeDir=r,exports.resolveServeCommand=function(){const e=r()
5174
5172
  return e?o((0,s.join)(e,"dist"),"serve.js"):{command:"opencode",prependArgs:["serve"]}},exports.resolveRunCommand=function(){const e=r()
5175
5173
  return e?o((0,s.join)(e,"dist"),"run.js"):{command:"opencode",prependArgs:[]}}
5176
5174
  const s=t(6928),i=t(9896),a=t(5317)},9072(e,exports,t){"use strict"
package/dist/index.js CHANGED
@@ -3333,23 +3333,21 @@ try{const e=await c.proxyPost(`/api/projects/${t.projectId}/requests/${n}/valida
3333
3333
  m=e?.data||e}catch(e){const t=(0,s.getErrorMessage)(e)
3334
3334
  return p.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(!m.canComplete){let e=`Cannot complete request ${n}: ${m.reason}\n`
3335
3335
  return m.blockedByTasks&&m.blockedByTasks.length>0&&(e+="\n**Tasks that must be completed or canceled first:**\n",m.blockedByTasks.forEach(((t,r)=>{e+=`${r+1}. **${t.taskId}** - ${t.title} (Status: ${t.status})\n`}))),m.lifoViolation&&(e+="\n**Requests that must be re-completed first (LIFO order):**\n",m.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(m.testsSkipped&&(l||d&&d.length>0))try{const e=c.getToolRecordClient()
3336
- e&&await e.createToolRecord({projectId:t.projectId,requestId:n,type:"tests-skipped",metadata:{...l?{reason:l}:{},...d&&d.length>0?{skipTests:d}:{},completionMode:m.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()
3337
- let y="auto-merge"!==m.completionMode&&!m.testsSkipped
3338
- const w="manual-review"===m.reviewMode,b=y&&!1===m.hasActiveTestProcesses&&("test-by-default"===m.completionMode||"always-test"===m.completionMode)
3339
- b&&(y=!1)
3336
+ e&&await e.createToolRecord({projectId:t.projectId,requestId:n,type:"tests-skipped",metadata:{...l?{reason:l}:{},...d&&d.length>0?{skipTests:d}:{},completionMode:m.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"!==m.completionMode&&!m.testsSkipped&&!1===m.hasActiveTestProcesses&&("test-by-default"===m.completionMode||"always-test"===m.completionMode)
3337
+ let w="auto-merge"!==m.completionMode&&!m.testsSkipped&&!y
3338
+ const b="manual-review"===m.reviewMode
3340
3339
  let I,k=null
3341
- if(y)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
3340
+ 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
3342
3341
  if(i&&"passed"===i.status&&i.commitSha){let e=null
3343
3342
  if(r&&r.ok){const t=await r.json()
3344
- e=t?.data?.sha??null}e&&e===i.commitSha&&(y=!1,k=`latest test run ${i.id} passed against current branch HEAD ${e.slice(0,7)}`,I=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=y||w?"ready_for_review":"ready_for_merge"
3343
+ 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)}`,I=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||b?"ready_for_review":"ready_for_merge"
3345
3344
  try{await f.updateRequest(n,{status:v,completionSummary:o,...I?{skippedTestRunId:I}:{}})}catch(e){const t=(0,s.getErrorMessage)(e)
3346
3345
  return g({success:!1,error:"status_transition_failed"}),{content:[{type:"text",text:"Failed to transition request: "+t}],isError:!0}}let C
3347
3346
  try{C=await f.getRequest(n)}catch(e){const t=(0,s.getErrorMessage)(e)
3348
3347
  return g({success:!1,error:"fetch_request_failed"}),{content:[{type:"text",text:"Failed to fetch request criteria: "+t}],isError:!0}}const _=C?.acceptanceCriteria||[]
3349
- if(g({success:!0}),y){const e=m.completionMode
3350
- let t
3351
- return t="ask-me"===e?"A notification has been created asking the user to choose how to handle testing (auto-test, interactive-test, or skip).":"always-test"===e?"Tests are running automatically in the background (always-test 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.":"Tests are running automatically in the background (test-by-default 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.",{content:[{type:"text",text:`Request ${n} moved to ready_for_review. ${t} Do NOT call merge or any other tool. Your work is done. Stop here and wait.`}]}}if(w)return{content:[{type:"text",text:`Request ${n} submitted for human review.${m.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.`}]}
3352
- const E=k?`\n\n_Skipped re-running tests: ${k}._\n`:"",S=b?`\n\n_No test processes are configured for this project; ${m.completionMode} mode is being treated as auto-merge._\n`:""
3348
+ if(g({success:!0}),w){const e=m.completionMode
3349
+ 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(b)return{content:[{type:"text",text:`Request ${n} submitted for human review.${m.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.`}]}
3350
+ const E=k?`\n\n_Skipped re-running tests: ${k}._\n`:"",S=y?`\n\n_No test processes are configured for this project; ${m.completionMode} mode is being treated as auto-merge._\n`:""
3353
3351
  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=""
3354
3352
  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})
3355
3353
  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}}}
@@ -5253,12 +5251,12 @@ e&&(this.pollingStatus=s.AUTHENTICATED,this.tokenResult=e,this.stopPolling(),r&&
5253
5251
  function r(){const e=process.env.ALCHEMY_OPENCODE_DIR
5254
5252
  if(e)return e
5255
5253
  try{const e=("undefined"!=typeof require?require:t(4245)).resolve("alchemylab-opencode/package.json")
5256
- return(0,s.dirname)(e)}catch{}}function n(e){return 22===e||e>=24}function o(e,t){return{command:function(){try{const e=(0,a.execFileSync)("node",["--version"],{encoding:"utf8"}).trim()
5254
+ return(0,s.dirname)(e)}catch{}}function n(e){return e>=24}function o(e,t){return{command:function(){try{const e=(0,a.execFileSync)("node",["--version"],{encoding:"utf8"}).trim()
5257
5255
  if(n(parseInt(e.replace("v","").split(".")[0],10)))return"node"}catch{}const e=process.env.NVM_DIR||(0,s.join)(process.env.HOME||"",".nvm"),t=(0,s.join)(e,"versions","node")
5258
5256
  if((0,i.existsSync)(t))try{const e=(0,i.readdirSync)(t).filter((e=>n(parseInt(e.replace("v","").split(".")[0],10)))).sort(((e,t)=>{const r=parseInt(e.replace("v","").split(".")[0],10)
5259
5257
  return parseInt(t.replace("v","").split(".")[0],10)-r}))
5260
5258
  if(e.length>0){const r=(0,s.join)(t,e[0],"bin","node")
5261
- if((0,i.existsSync)(r))return r}}catch{}throw Error(`AlchemyLab requires Node.js 22 or 24+ to run the OpenCode server (Node 23 is not supported).\nYour current version: ${process.version}\nInstall a compatible version: nvm install 22 (or download from https://nodejs.org)`)}(),prependArgs:["--no-warnings",(0,s.join)(e,t)]}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.findOpencodeDir=r,exports.resolveServeCommand=function(){const e=r()
5259
+ if((0,i.existsSync)(r))return r}}catch{}throw Error(`AlchemyLab requires Node.js 24 or later to run the OpenCode server.\nYour current version: ${process.version}\nInstall a compatible version: nvm install 24 (or download from https://nodejs.org)`)}(),prependArgs:["--no-warnings",(0,s.join)(e,t)]}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.findOpencodeDir=r,exports.resolveServeCommand=function(){const e=r()
5262
5260
  return e?o((0,s.join)(e,"dist"),"serve.js"):{command:"opencode",prependArgs:["serve"]}},exports.resolveRunCommand=function(){const e=r()
5263
5261
  return e?o((0,s.join)(e,"dist"),"run.js"):{command:"opencode",prependArgs:[]}}
5264
5262
  const s=t(6928),i=t(9896),a=t(5317)},9072(e,exports,t){"use strict"
@@ -1533,7 +1533,7 @@ exports.def={name:"quick_manage",description:"WHEN TO USE: trivial single-file e
1533
1533
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.buildTaskEndpoint=function(e,t,r){const n=r?"/"+r.replace(/^\//,""):""
1534
1534
  return`/api/projects/${e}/tasks/${encodeURIComponent(t)}${n}`}},2462(e,exports,t){"use strict"
1535
1535
  Object.defineProperty(exports,"__esModule",{value:!0})
1536
- const r=t(2245),n=t(9080),s=t(5692),o=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),b=t(9373),I=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),x=t(8850),P=t(3556),R=t(1391),M=t(6315),$=t(7713),j=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)(s.def),(0,r.registerTool)(o.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)(b.def),(0,r.registerTool)(I.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)(x.def),(0,r.registerTool)(P.def),(0,r.registerTool)(R.def),(0,r.registerTool)(M.def),(0,r.registerTool)($.def),(0,r.registerTool)(j.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"
1536
+ const r=t(2245),n=t(9080),s=t(5692),o=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),b=t(9373),I=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),x=t(8850),P=t(3556),R=t(1391),M=t(6315),$=t(7713),j=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),G=t(8456),W=t(3322),V=t(5247),Q=t(8849),K=t(542);(0,r.registerTool)(n.def),(0,r.registerTool)(s.def),(0,r.registerTool)(o.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)(b.def),(0,r.registerTool)(I.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)(x.def),(0,r.registerTool)(P.def),(0,r.registerTool)(R.def),(0,r.registerTool)(M.def),(0,r.registerTool)($.def),(0,r.registerTool)(j.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)(G.def),(0,r.registerTools)(W.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"
1537
1537
  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"
1538
1538
  async function r(e,t,r){if(v.has(r)&&C>=t){if(0===t)return 0
1539
1539
  try{return(await y.default.readFile(e,"utf-8")).split("\n").length}catch{return}}}function n(e,t,r){const n="depth"in e,s=n?e:r,o=n?e.isDirectory:e.isDirectory(),a=e.name,c=o?"[\u{1f4c1} DIR]":"[\u{1f4c4} FILE]"
@@ -2046,17 +2046,17 @@ try{const s=await r.documentFileManager.getDocumentAsFile(e.documentId),o=r.docu
2046
2046
  t.filename=o?.filename
2047
2047
  const i=await l.default.readFile(s,"utf8"),{newContent:a,results:c}=await n(i,e.replacements,e.mode)
2048
2048
  t.replacements=c,t.successfulReplacements=c.filter((e=>e.success)).length,t.failedReplacements=c.filter((e=>!e.success)).length,a!==i?(await l.default.writeFile(s,a,"utf8"),r.documentFileManager.markForSync(e.documentId),t.success=!0,B++,y.info("Document updated successfully",{documentId:e.documentId,filename:t.filename,successfulReplacements:t.successfulReplacements,failedReplacements:t.failedReplacements})):0===t.failedReplacements&&(t.success=!0),H+=t.failedReplacements}catch(r){t.error=(0,g.getErrorMessage)(r),t.failedReplacements=e.replacements.length,H+=t.failedReplacements,y.error("Document operation failed",{documentId:e.documentId,error:t.error})}L.push(t)}if(M.length>0)try{for(const e of M){const t=L.find((t=>t.documentId===e.documentId))
2049
- t?.success&&await r.documentFileManager.syncDocument(e.documentId)}}catch(e){y.warn("Some documents may not have synced properly",{error:e})}const U=await d.handleMultiFileGitCommit(T,R,L,B,{operationType:"Replace strings",toolRecordId:I,successField:"successfulReplacements",logger:y,metrics:c,metricsPrefix:"replace_strings"},r),W=d.processMultiFileOperationResults(L,"successfulReplacements"),{successfulOperations:G,failedOperations:V,totalSuccessfulChanges:Q,overallSuccess:K,successfulFiles:J,failedFiles:Y}=W
2050
- try{await o.updateToolRecord(I,{metadata:{path:R.length>0?R[0].path:"document:"+M[0]?.documentId,status:K?"completed":"failed",error:K?void 0:V+" operations failed",totalOperations:L.length,successfulFiles:J,failedFiles:Y,operations:N,totalSuccessfulReplacements:Q,totalFailedReplacements:H,operationsWithChanges:B,processingTime:Date.now()-w,userComment:E,...R.length>0&&U.success&&U.commitHash&&{commitHash:U.commitHash,previousCommitHash:U.previousCommitHash||void 0}},note:K?`String replacement completed: ${G}/${L.length} operations successful${R.length>0?" (committed to git)":""}`:`String replacement partially failed: ${V}/${L.length} operations failed`})}catch(e){y.warn("Failed to update tool record",{recordError:e})}d.formatMultiFileToolResponse("String Replacement",{successfulOperations:G,failedOperations:V,totalSuccessfulChanges:Q,overallSuccess:K,successfulFiles:J,failedFiles:Y},U,F)
2049
+ t?.success&&await r.documentFileManager.syncDocument(e.documentId)}}catch(e){y.warn("Some documents may not have synced properly",{error:e})}const U=await d.handleMultiFileGitCommit(T,R,L,B,{operationType:"Replace strings",toolRecordId:I,successField:"successfulReplacements",logger:y,metrics:c,metricsPrefix:"replace_strings"},r),G=d.processMultiFileOperationResults(L,"successfulReplacements"),{successfulOperations:W,failedOperations:V,totalSuccessfulChanges:Q,overallSuccess:K,successfulFiles:J,failedFiles:Y}=G
2050
+ try{await o.updateToolRecord(I,{metadata:{path:R.length>0?R[0].path:"document:"+M[0]?.documentId,status:K?"completed":"failed",error:K?void 0:V+" operations failed",totalOperations:L.length,successfulFiles:J,failedFiles:Y,operations:N,totalSuccessfulReplacements:Q,totalFailedReplacements:H,operationsWithChanges:B,processingTime:Date.now()-w,userComment:E,...R.length>0&&U.success&&U.commitHash&&{commitHash:U.commitHash,previousCommitHash:U.previousCommitHash||void 0}},note:K?`String replacement completed: ${W}/${L.length} operations successful${R.length>0?" (committed to git)":""}`:`String replacement partially failed: ${V}/${L.length} operations failed`})}catch(e){y.warn("Failed to update tool record",{recordError:e})}d.formatMultiFileToolResponse("String Replacement",{successfulOperations:W,failedOperations:V,totalSuccessfulChanges:Q,overallSuccess:K,successfulFiles:J,failedFiles:Y},U,F)
2051
2051
  let Z="## String Replacement Results\n\n"
2052
2052
  if(r.workspaceManager){const e=r.workspaceManager.getCurrentContext(r.currentSessionId)
2053
- e.permanentBranch||(Z+=`**Branch:** \`${e.branchName}\`\n\n`)}if(Z+="**Summary:**\n",Z+=`- **Files processed:** ${G+V}\n`,Z+=`- **Successful operations:** ${G}\n`,Z+=`- **Failed operations:** ${V}\n`,Z+=`- **Total replacements:** ${Q} successful, ${H} failed\n\n`,U.success&&U.commitHash&&(Z+="**Git Commit:**\n",Z+=`- **Commit Hash:** ${U.commitHash.substring(0,8)}\n`,Z+=`- **Files Committed:** ${U.filesCommitted.length}\n`,U.previousCommitHash&&(Z+=`- **Previous Commit:** ${U.previousCommitHash.substring(0,8)}\n`),Z+="\n"),1===L.length){const e=L[0]
2053
+ e.permanentBranch||(Z+=`**Branch:** \`${e.branchName}\`\n\n`)}if(Z+="**Summary:**\n",Z+=`- **Files processed:** ${W+V}\n`,Z+=`- **Successful operations:** ${W}\n`,Z+=`- **Failed operations:** ${V}\n`,Z+=`- **Total replacements:** ${Q} successful, ${H} failed\n\n`,U.success&&U.commitHash&&(Z+="**Git Commit:**\n",Z+=`- **Commit Hash:** ${U.commitHash.substring(0,8)}\n`,Z+=`- **Files Committed:** ${U.filesCommitted.length}\n`,U.previousCommitHash&&(Z+=`- **Previous Commit:** ${U.previousCommitHash.substring(0,8)}\n`),Z+="\n"),1===L.length){const e=L[0]
2054
2054
  if(Z+=`**Operation:** ${e.success?"\u2705 Success":"\u274c Failed"}\n`,"file"===e.type?Z+=`**File:** ${e.path}\n`:(Z+="**Document:** "+e.documentId,e.filename&&(Z+=` (${e.filename})`),Z+="\n"),e.error?Z+=`**Error:** ${e.error}\n`:(Z+=`**Replacements:** ${e.successfulReplacements} successful`,e.failedReplacements>0&&(Z+=`, ${e.failedReplacements} failed`),Z+="\n"),e.replacements.some((e=>!e.success))){Z+="**Failed Replacement Details:**\n"
2055
- for(const t of e.replacements.filter((e=>!e.success)))Z+=`- "${t.old_str}" \u2192 "${t.new_str}": ${t.error}\n`}}else{Z+="**Summary:**\n",Z+=`- **Total Operations:** ${L.length}\n`,Z+=`- **Successful:** ${G}\n`,Z+=`- **Failed:** ${V}\n`,Z+=`- **Total Replacements:** ${Q} successful, ${H} failed\n\n`,Z+="**Detailed Results:**\n\n"
2055
+ for(const t of e.replacements.filter((e=>!e.success)))Z+=`- "${t.old_str}" \u2192 "${t.new_str}": ${t.error}\n`}}else{Z+="**Summary:**\n",Z+=`- **Total Operations:** ${L.length}\n`,Z+=`- **Successful:** ${W}\n`,Z+=`- **Failed:** ${V}\n`,Z+=`- **Total Replacements:** ${Q} successful, ${H} failed\n\n`,Z+="**Detailed Results:**\n\n"
2056
2056
  for(const e of L){if("file"===e.type?Z+=`### File: ${e.path}\n`:(Z+="### Document: "+e.documentId,e.filename&&(Z+=` (${e.filename})`),Z+="\n"),Z+=`- **Status:** ${e.success?"\u2705 Success":"\u274c Failed"}\n`,e.error?Z+=`- **Error:** ${e.error}\n`:(Z+=`- **Replacements:** ${e.successfulReplacements} successful`,e.failedReplacements>0&&(Z+=`, ${e.failedReplacements} failed`),Z+="\n"),e.replacements.some((e=>!e.success))){Z+="- **Failed Replacement Details:**\n"
2057
2057
  for(const t of e.replacements.filter((e=>!e.success)))Z+=` - "${t.old_str}" \u2192 "${t.new_str}": ${t.error}\n`}Z+="\n"}}M.length>0&&(Z+="\u{1f4c4} **Document Changes:** Modified documents have been automatically synced back to the tracking system.\n\n")
2058
2058
  const X=Date.now()-w
2059
- return y.info("Replace strings operation completed",{duration:X,success:K,totalOperations:L.length,successfulOperations:G,failedOperations:V}),c.incrementCounter("replace_strings_total",1,{success:K}),{content:[{type:"text",text:Z}],isError:!K}}catch(t){const r=Date.now()-w
2059
+ return y.info("Replace strings operation completed",{duration:X,success:K,totalOperations:L.length,successfulOperations:W,failedOperations:V}),c.incrementCounter("replace_strings_total",1,{success:K}),{content:[{type:"text",text:Z}],isError:!K}}catch(t){const r=Date.now()-w
2060
2060
  if(y.error("Replace strings operation failed",{error:t instanceof Error?t.stack:t+"",duration:r,phase:k}),I)try{await o.updateToolRecord(I,{metadata:{path:e?.operations?.[0]?.path||e?.operations?.[0]?.documentId,status:"failed",error:(0,g.getErrorMessage)(t),phase:k,duration:r,userComment:e?.comment},note:"String replacement failed: "+(0,g.getErrorMessage)(t)})}catch(e){}return c.incrementCounter("replace_strings_errors",1,{phase:k}),{content:[{type:"text",text:"\u274c **Replace Strings Failed:** "+(0,g.getErrorMessage)(t)}],isError:!0}}}
2061
2061
  const l=c(t(1943)),d=a(t(5967)),u=t(9860),h=t(5003),p=t(7478),m=t(9949),g=t(8824)},3322(e,exports,t){"use strict"
2062
2062
  var r,n=this&&this.__createBinding||(Object.create?function(e,t,r,n){void 0===n&&(n=r)
@@ -2382,18 +2382,18 @@ throw e}if(!r.documentFileManager)throw Error("Document file manager not availab
2382
2382
  N=await r.documentFileManager.getDocumentAsFile(S)
2383
2383
  const e=r.documentFileManager.getCachedDocumentInfo(S)
2384
2384
  F=e?.filename,L=!0,z=!1}_=d.FileOperationPhase.TASK_VALIDATION,y.incrementCounter("write_file_phase",1,{phase:_})
2385
- const B=O?H:"document:"+S,U=await d.validateTaskId($,B,s,b,y,E.metricsPrefix),W=U?.requestId
2386
- let G=null
2387
- O&&W&&(G=await d.validateWorkItemStates($,B,s,i,r,b,y,E.metricsPrefix))
2385
+ const B=O?H:"document:"+S,U=await d.validateTaskId($,B,s,b,y,E.metricsPrefix),G=U?.requestId
2386
+ let W=null
2387
+ O&&G&&(W=await d.validateWorkItemStates($,B,s,i,r,b,y,E.metricsPrefix))
2388
2388
  let V={taskPromoted:!1,requestPromoted:!1}
2389
- O&&W&&G&&(V=await d.autoPromoteTaskStatus($,U.status,W,G.request?.status,B,s,i,b,y,E.metricsPrefix),V.taskPromoted&&b.info("Auto-promoted task to in_progress for file operation",{taskId:$,requestId:W,previousStatus:U.status,newStatus:V.newTaskStatus,triggeringOperation:"write_file"}))
2390
- const Q=await d.ensureCorrectWorkspaceContext({taskPromoted:V.taskPromoted,requestId:W,context:r,operation:"write_file",logger:b,metrics:y,metricsPrefix:E.metricsPrefix,requireRequestContext:O})
2389
+ O&&G&&W&&(V=await d.autoPromoteTaskStatus($,U.status,G,W.request?.status,B,s,i,b,y,E.metricsPrefix),V.taskPromoted&&b.info("Auto-promoted task to in_progress for file operation",{taskId:$,requestId:G,previousStatus:U.status,newStatus:V.newTaskStatus,triggeringOperation:"write_file"}))
2390
+ const Q=await d.ensureCorrectWorkspaceContext({taskPromoted:V.taskPromoted,requestId:G,context:r,operation:"write_file",logger:b,metrics:y,metricsPrefix:E.metricsPrefix,requireRequestContext:O})
2391
2391
  if(!Q.success)throw Error(Q.error)
2392
- if(Q.switched&&b.info("Workspace context successfully established",{requestId:W,branchName:Q.branchName,operation:"write_file"}),O&&H){const e=d.validateAndNormalizePath(H,r)
2392
+ if(Q.switched&&b.info("Workspace context successfully established",{requestId:G,branchName:Q.branchName,operation:"write_file"}),O&&H){const e=d.validateAndNormalizePath(H,r)
2393
2393
  D=e.normalizedPath,A=e.fullPath,N=A
2394
- try{await c.default.access(A),L=!0}catch{L=!1,z=!0}b.debug("Path validated with correct workspace context",{rawPath:H,normalizedPath:D,fullPath:A,workspaceBasePath:r.workspaceManager?.getCurrentContext(r.currentSessionId).basePath})}if(j&&W)try{await(0,h.validateRequestStatusForDocumentOperation)(W,i,b,y,"write_file-document")}catch(e){if(e instanceof u.ValidationError)return{content:[{type:"text",text:e.message}],isError:!0}
2394
+ try{await c.default.access(A),L=!0}catch{L=!1,z=!0}b.debug("Path validated with correct workspace context",{rawPath:H,normalizedPath:D,fullPath:A,workspaceBasePath:r.workspaceManager?.getCurrentContext(r.currentSessionId).basePath})}if(j&&G)try{await(0,h.validateRequestStatusForDocumentOperation)(G,i,b,y,"write_file-document")}catch(e){if(e instanceof u.ValidationError)return{content:[{type:"text",text:e.message}],isError:!0}
2395
2395
  throw e}_=d.FileOperationPhase.RECORD_CREATION,y.incrementCounter("write_file_phase",1,{phase:_})
2396
- const{toolRecordId:K}=await d.createFileOperationRecords($,W,r.projectId,E.recordType,B,{operationType:j?"document":"file",documentId:j?S:void 0,documentFilename:F,filePath:O?D:void 0,contentLength:"string"==typeof q?q.length:JSON.stringify(q).length,format:T||"text",append:x||!1,wasFileCreated:!L,userComment:P},s,o,b,y,E.metricsPrefix,w)
2396
+ const{toolRecordId:K}=await d.createFileOperationRecords($,G,r.projectId,E.recordType,B,{operationType:j?"document":"file",documentId:j?S:void 0,documentFilename:F,filePath:O?D:void 0,contentLength:"string"==typeof q?q.length:JSON.stringify(q).length,format:T||"text",append:x||!1,wasFileCreated:!L,userComment:P},s,o,b,y,E.metricsPrefix,w)
2397
2397
  let J
2398
2398
  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)
2399
2399
  else J="string"==typeof q?q:JSON.stringify(q,null,2)
@@ -3100,23 +3100,21 @@ try{const e=await c.proxyPost(`/api/projects/${t.projectId}/requests/${n}/valida
3100
3100
  g=e?.data||e}catch(e){const t=(0,o.getErrorMessage)(e)
3101
3101
  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`
3102
3102
  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()
3103
- 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,o.getErrorMessage)(e)})}const f=c.getRequestClient()
3104
- let y="auto-merge"!==g.completionMode&&!g.testsSkipped
3105
- const w="manual-review"===g.reviewMode,b=y&&!1===g.hasActiveTestProcesses&&("test-by-default"===g.completionMode||"always-test"===g.completionMode)
3106
- b&&(y=!1)
3103
+ 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,o.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)
3104
+ let w="auto-merge"!==g.completionMode&&!g.testsSkipped&&!y
3105
+ const b="manual-review"===g.reviewMode
3107
3106
  let I,k=null
3108
- if(y)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,o.getErrorMessage)(e)}),null)))]),s=e?.data||e||[],i=Array.isArray(s)&&s.length>0?s[0]:null
3107
+ 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,o.getErrorMessage)(e)}),null)))]),s=e?.data||e||[],i=Array.isArray(s)&&s.length>0?s[0]:null
3109
3108
  if(i&&"passed"===i.status&&i.commitSha){let e=null
3110
3109
  if(r&&r.ok){const t=await r.json()
3111
- e=t?.data?.sha??null}e&&e===i.commitSha&&(y=!1,k=`latest test run ${i.id} passed against current branch HEAD ${e.slice(0,7)}`,I=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,o.getErrorMessage)(e)})}const v=y||w?"ready_for_review":"ready_for_merge"
3110
+ 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)}`,I=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,o.getErrorMessage)(e)})}const v=w||b?"ready_for_review":"ready_for_merge"
3112
3111
  try{await f.updateRequest(n,{status:v,completionSummary:s,...I?{skippedTestRunId:I}:{}})}catch(e){const t=(0,o.getErrorMessage)(e)
3113
3112
  return m({success:!1,error:"status_transition_failed"}),{content:[{type:"text",text:"Failed to transition request: "+t}],isError:!0}}let C
3114
3113
  try{C=await f.getRequest(n)}catch(e){const t=(0,o.getErrorMessage)(e)
3115
3114
  return m({success:!1,error:"fetch_request_failed"}),{content:[{type:"text",text:"Failed to fetch request criteria: "+t}],isError:!0}}const _=C?.acceptanceCriteria||[]
3116
- if(m({success:!0}),y){const e=g.completionMode
3117
- let t
3118
- return t="ask-me"===e?"A notification has been created asking the user to choose how to handle testing (auto-test, interactive-test, or skip).":"always-test"===e?"Tests are running automatically in the background (always-test 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.":"Tests are running automatically in the background (test-by-default 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.",{content:[{type:"text",text:`Request ${n} moved to ready_for_review. ${t} Do NOT call merge or any other tool. Your work is done. Stop here and wait.`}]}}if(w)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.`}]}
3119
- const E=k?`\n\n_Skipped re-running tests: ${k}._\n`:"",S=b?`\n\n_No test processes are configured for this project; ${g.completionMode} mode is being treated as auto-merge._\n`:""
3115
+ if(m({success:!0}),w){const e=g.completionMode
3116
+ 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(b)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.`}]}
3117
+ 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`:""
3120
3118
  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 o="An unexpected error occurred",i=""
3121
3119
  r instanceof n.ZodError?(o="Validation error",i=r.errors.map((e=>`${e.path.join(".")}: ${e.message}`)).join(", ")):r instanceof Error?(o=r.message,i=r.stack||""):i=r+"",p.error("Complete request failed",{error:o,details:i})
3122
3120
  try{await(0,s.createFailureToolRecord)("complete-request",r,{error:o,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: "+o}],isError:!0}}}
@@ -3175,33 +3173,33 @@ const e=await(0,u.commitUserChangesBeforeToolOperation)(r.git,"delete-files",{re
3175
3173
  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:x,totalOperations:P.length,fileOperations:R.length,documentOperations:M.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:x,totalOperations:P.length,fileOperations:R.length,documentOperations:M.length,error:e instanceof Error?e.message:e+""})}g.debug("[DELETE-FILES] Proceeding with mixed delete operations",{totalOperations:P.length,fileDeletes:R.length,documentDeletes:M.length}),b=c.FileOperationPhase.RECORD_CREATION,o.incrementCounter("delete_files_phase",1,{phase:b})
3176
3174
  const{toolRecordId:B,localToolRecordId:U}=await c.createFileOperationRecords(x,F,r.projectId,I.recordType,D,{totalOperations:P.length,fileOperations:R.length,documentOperations:M.length,filePaths:O.map((e=>e.normalized)),documentIds:M.map((e=>e.target)),userComment:S,hadUserMods:H.hadUserMods,userModsCommitHash:H.userModsCommitHash},d,k,g,o,I.metricsPrefix,i)
3177
3175
  y=B,w=B,b=I.fileOperationPhase,o.incrementCounter("delete_files_phase",1,{phase:b})
3178
- const W=[]
3179
- let G=0
3176
+ const G=[]
3177
+ let W=0
3180
3178
  M.length>0&&n.setProjectId(r.projectId)
3181
3179
  for(let e=0;e<O.length;e++){const t=O[e],r={type:"file",path:t.normalized,success:!1,replacements:[],successfulReplacements:0,failedReplacements:0}
3182
3180
  try{try{await a.default.access(t.full)}catch(e){throw Error("File not found: "+t.normalized)}if((await a.default.stat(t.full)).isDirectory())throw Error("Cannot 'delete' a directory: "+t.normalized)
3183
- await a.default.unlink(t.full),r.success=!0,G++,g.info("Successfully deleted file",{path:t.normalized})}catch(e){r.error=(0,m.getErrorMessage)(e),g.warn("Failed to delete file "+t.normalized,{error:r.error})}W.push(r)}const V=M.length>0?n.getDocumentClient():null
3181
+ await a.default.unlink(t.full),r.success=!0,W++,g.info("Successfully deleted file",{path:t.normalized})}catch(e){r.error=(0,m.getErrorMessage)(e),g.warn("Failed to delete file "+t.normalized,{error:r.error})}G.push(r)}const V=M.length>0?n.getDocumentClient():null
3184
3182
  for(const e of M){const t=e.target,r={type:"document",documentId:t,success:!1,replacements:[],successfulReplacements:0,failedReplacements:0}
3185
3183
  try{let e
3186
3184
  try{if(e=await V.getDocument(t),r.filename=e.filename,g.info("Document found for archival",{documentId:e.id,filename:e.filename,type:e.type,requestId:e.requestId}),e.requestId)try{await(0,h.validateRequestStatusForDocumentOperation)(e.requestId,v,g,o,"delete-document")}catch(e){if(e instanceof l.ValidationError)throw Error(e.message)
3187
3185
  throw e}try{await(0,p.ensureDocumentNotLocked)(t,n,g,o,"delete-document-archive")}catch(e){if(e instanceof l.ValidationError)throw Error(e.message)
3188
3186
  throw e}}catch(e){throw Error(`Document not found or validation failed: ${t}. ${(0,m.getErrorMessage)(e)}`)}const s=await V.archiveDocument(t)
3189
- r.success=!0,r.filename=s.filename,G++,g.info("Document archived successfully",{documentId:s.id,filename:s.filename,archivedAt:s.archived_at})}catch(e){r.error=(0,m.getErrorMessage)(e),g.warn("Failed to delete document "+t,{error:r.error})}W.push(r)}const Q=Date.now()-f
3190
- o.recordValue("delete_files_time",Q),o.incrementCounter("delete_files_operations_total",$),o.incrementCounter("delete_files_operations_success",G),o.incrementCounter("delete_files_operations_failed",$-G),o.incrementCounter("delete_files_file_operations",R.length),o.incrementCounter("delete_files_document_operations",M.length),G===$?o.incrementCounter("delete_files_success",1):G>0?o.incrementCounter("delete_files_partial_success",1):o.incrementCounter("delete_files_total_failure",1)
3187
+ r.success=!0,r.filename=s.filename,W++,g.info("Document archived successfully",{documentId:s.id,filename:s.filename,archivedAt:s.archived_at})}catch(e){r.error=(0,m.getErrorMessage)(e),g.warn("Failed to delete document "+t,{error:r.error})}G.push(r)}const Q=Date.now()-f
3188
+ o.recordValue("delete_files_time",Q),o.incrementCounter("delete_files_operations_total",$),o.incrementCounter("delete_files_operations_success",W),o.incrementCounter("delete_files_operations_failed",$-W),o.incrementCounter("delete_files_file_operations",R.length),o.incrementCounter("delete_files_document_operations",M.length),W===$?o.incrementCounter("delete_files_success",1):W>0?o.incrementCounter("delete_files_partial_success",1):o.incrementCounter("delete_files_total_failure",1)
3191
3189
  let K=null
3192
- if(G>0){b=c.FileOperationPhase.GIT_COMMIT,o.incrementCounter("delete_files_phase",1,{phase:b})
3193
- const e=W.filter((e=>e.success)),t=e.filter((e=>"file"===e.type)).length,n=e.filter((e=>"document"===e.type)).length
3190
+ if(W>0){b=c.FileOperationPhase.GIT_COMMIT,o.incrementCounter("delete_files_phase",1,{phase:b})
3191
+ const e=G.filter((e=>e.success)),t=e.filter((e=>"file"===e.type)).length,n=e.filter((e=>"document"===e.type)).length
3194
3192
  let s
3195
3193
  s=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(x,D,B,U,s,r,g,o,I.metricsPrefix)}b=c.FileOperationPhase.RECORD_UPDATE_SUCCESS,o.incrementCounter("delete_files_phase",1,{phase:b})
3196
- const J=W.filter((e=>e.success)),Y=W.filter((e=>!e.success))
3197
- await c.updateFileOperationRecords(x,y,w,D,I.recordType,G===$?`Successfully deleted all ${$} operations`:`Deleted ${G}/${$} operations successfully`,{status:G===$?"completed":"partial_success",commitHash:K?.commitHash,previousCommitHash:K?.previousCommitHash,totalOperations:$,fileOperations:R.length,documentOperations:M.length,successCount:G,failureCount:$-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===$?`Successfully deleted all ${$} operations`:`Deleted ${G}/${$} operations successfully`,d,k,g,o,I.metricsPrefix)
3194
+ const J=G.filter((e=>e.success)),Y=G.filter((e=>!e.success))
3195
+ await c.updateFileOperationRecords(x,y,w,D,I.recordType,W===$?`Successfully deleted all ${$} operations`:`Deleted ${W}/${$} operations successfully`,{status:W===$?"completed":"partial_success",commitHash:K?.commitHash,previousCommitHash:K?.previousCommitHash,totalOperations:$,fileOperations:R.length,documentOperations:M.length,successCount:W,failureCount:$-W,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},W===$?`Successfully deleted all ${$} operations`:`Deleted ${W}/${$} operations successfully`,d,k,g,o,I.metricsPrefix)
3198
3196
  const Z=r.getCurrentBranchContext?await r.getCurrentBranchContext():{branch:null,requestId:null,isMain:!0}
3199
3197
  let X=""
3200
- if(G===$){const e=J.filter((e=>"file"===e.type)).length,t=J.filter((e=>"document"===e.type)).length
3201
- X=e>0&&t>0?`Successfully deleted ${e} files and ${t} documents`:e>0?`Successfully deleted all ${e} ${1===e?"file":"files"}`:`Successfully archived all ${t} ${1===t?"document":"documents"}`}else X=G>0?`Successfully deleted ${G}/${$} operations.\n\nFailed operations:\n`+W.filter((e=>!e.success)).map((e=>{const t="file"===e.type?e.path:`${e.filename||e.documentId} (${e.documentId})`
3202
- return`\u2022 ${e.type}: ${t}: ${e.error}`})).join("\n"):"All delete operations failed:\n"+W.map((e=>{const t="file"===e.type?e.path:`${e.filename||e.documentId} (${e.documentId})`
3198
+ if(W===$){const e=J.filter((e=>"file"===e.type)).length,t=J.filter((e=>"document"===e.type)).length
3199
+ X=e>0&&t>0?`Successfully deleted ${e} files and ${t} documents`:e>0?`Successfully deleted all ${e} ${1===e?"file":"files"}`:`Successfully archived all ${t} ${1===t?"document":"documents"}`}else X=W>0?`Successfully deleted ${W}/${$} operations.\n\nFailed operations:\n`+G.filter((e=>!e.success)).map((e=>{const t="file"===e.type?e.path:`${e.filename||e.documentId} (${e.documentId})`
3200
+ return`\u2022 ${e.type}: ${t}: ${e.error}`})).join("\n"):"All delete operations failed:\n"+G.map((e=>{const t="file"===e.type?e.path:`${e.filename||e.documentId} (${e.documentId})`
3203
3201
  return`\u2022 ${e.type}: ${t}: ${e.error}`})).join("\n")
3204
- return K?.commitHash&&(X+="\nCommit: "+K.commitHash.substring(0,8)),Z.requestId&&(X+="\nBranch: "+Z.requestId),{content:[{type:"text",text:X}],isError:0===G}}catch(t){if(e.taskId&&n)try{const s=n.getToolRecordClient(),o=t instanceof l.NotFoundError||t instanceof l.ValidationError||t instanceof d.ZodError
3202
+ return K?.commitHash&&(X+="\nCommit: "+K.commitHash.substring(0,8)),Z.requestId&&(X+="\nBranch: "+Z.requestId),{content:[{type:"text",text:X}],isError:0===W}}catch(t){if(e.taskId&&n)try{const s=n.getToolRecordClient(),o=t instanceof l.NotFoundError||t instanceof l.ValidationError||t instanceof d.ZodError
3205
3203
  await s.createToolRecord({...o?{}:{taskId:e.taskId},projectId:r.projectId,type:"delete-files",metadata:{status:"failed",timestamp:(new Date).toISOString(),totalOperations:e.operations?.length||e.paths?.length||0,fileOperations:e.paths?.length||e.operations?.filter((e=>e.path))?.length||0,documentOperations:e.operations?.filter((e=>e.documentId))?.length||0,paths:e.paths||e.operations?.filter((e=>e.path))?.map((e=>e.path))||[],documentIds:e.operations?.filter((e=>e.documentId))?.map((e=>e.documentId))||[],userComment:e.comment,error:(0,m.getErrorMessage)(t),errorDetails:t instanceof Error?t.stack:t+"",phase:b,attemptedTaskId:e.taskId,taskIdValidationFailed:o},note:"Failed to delete files - "+(0,m.getErrorMessage)(t)})}catch(t){g.error("Failed to create failure record for delete-files operation",{error:t instanceof Error?t.message:t+"",taskId:e.taskId,totalOperations:e.operations?.length||e.paths?.length||0,fileOperations:e.paths?.length||e.operations?.filter((e=>e.path))?.length||0,documentOperations:e.operations?.filter((e=>e.documentId))?.length||0})}const s=Date.now()-f
3206
3204
  if(o.recordValue("delete_files_time",s),o.incrementCounter("delete_files_errors",1),g.error("Error in delete-files operation:",{error:(0,m.getErrorMessage)(t),time:s}),(y||w)&&e.taskId){b=c.FileOperationPhase.RECORD_UPDATE_FAILURE
3207
3205
  try{const r=n.getTaskClient(),s=n.getToolRecordClient()
@@ -3445,10 +3443,10 @@ try{const s=await n.documentFileManager.getDocumentAsFile(e.documentId),o=n.docu
3445
3443
  t.filename=o?.filename
3446
3444
  const i=await c.default.readFile(s,"utf8"),{newContent:a,results:l}=await r(i,e.insertions)
3447
3445
  t.insertions=l,t.successfulInsertions=l.filter((e=>e.success)).length,t.failedInsertions=l.filter((e=>!e.success)).length,t.totalLinesAdded=l.filter((e=>e.success)).reduce(((e,t)=>e+(t.linesAdded||0)),0),a!==i&&t.successfulInsertions>0?(await c.default.writeFile(s,a,"utf8"),n.documentFileManager.markForSync(e.documentId),t.success=!0,U++,y.info("Document updated successfully",{documentId:e.documentId,filename:t.filename,successfulInsertions:t.successfulInsertions,failedInsertions:t.failedInsertions,linesAdded:t.totalLinesAdded})):0===t.failedInsertions&&(t.success=!0),H+=t.failedInsertions,B+=t.totalLinesAdded}catch(r){t.error=(0,m.getErrorMessage)(r),t.failedInsertions=e.insertions.length,H+=t.failedInsertions,y.error("Document operation failed",{documentId:e.documentId,error:t.error})}L.push(t)}if(M.length>0)try{for(const e of M){const t=L.find((t=>t.documentId===e.documentId))
3448
- t?.success&&await n.documentFileManager.syncDocument(e.documentId)}}catch(e){y.warn("Some documents may not have synced properly",{error:e})}const W=await l.handleMultiFileGitCommit(T,R,L,U,{operationType:"Insert lines",toolRecordId:I,successField:"successfulInsertions",logger:y,metrics:g,metricsPrefix:"insert_lines"},n),G=l.processMultiFileOperationResults(L,"successfulInsertions"),{successfulOperations:V,failedOperations:Q,totalSuccessfulChanges:K,overallSuccess:J,successfulFiles:Y,failedFiles:Z}=G
3449
- try{await o.updateToolRecord(I,{metadata:{path:R.length>0?R[0].path:"document:"+M[0]?.documentId,status:J?"completed":"failed",error:J?void 0:Q+" operations failed",totalOperations:L.length,successfulFiles:Y,failedFiles:Z,operations:N,totalSuccessfulInsertions:K,totalFailedInsertions:H,totalLinesAdded:B,operationsWithChanges:U,processingTime:Date.now()-w,userComment:E,...R.length>0&&W.success&&W.commitHash&&{commitHash:W.commitHash,previousCommitHash:W.previousCommitHash||void 0}},note:J?`Line insertion completed: ${V}/${L.length} operations successful${R.length>0?" (committed to git)":""}`:`Line insertion partially failed: ${Q}/${L.length} operations failed`})}catch(e){y.warn("Failed to update tool record",{recordError:e})}let X="## Line Insertion Results\n\n"
3446
+ t?.success&&await n.documentFileManager.syncDocument(e.documentId)}}catch(e){y.warn("Some documents may not have synced properly",{error:e})}const G=await l.handleMultiFileGitCommit(T,R,L,U,{operationType:"Insert lines",toolRecordId:I,successField:"successfulInsertions",logger:y,metrics:g,metricsPrefix:"insert_lines"},n),W=l.processMultiFileOperationResults(L,"successfulInsertions"),{successfulOperations:V,failedOperations:Q,totalSuccessfulChanges:K,overallSuccess:J,successfulFiles:Y,failedFiles:Z}=W
3447
+ try{await o.updateToolRecord(I,{metadata:{path:R.length>0?R[0].path:"document:"+M[0]?.documentId,status:J?"completed":"failed",error:J?void 0:Q+" operations failed",totalOperations:L.length,successfulFiles:Y,failedFiles:Z,operations:N,totalSuccessfulInsertions:K,totalFailedInsertions:H,totalLinesAdded:B,operationsWithChanges:U,processingTime:Date.now()-w,userComment:E,...R.length>0&&G.success&&G.commitHash&&{commitHash:G.commitHash,previousCommitHash:G.previousCommitHash||void 0}},note:J?`Line insertion completed: ${V}/${L.length} operations successful${R.length>0?" (committed to git)":""}`:`Line insertion partially failed: ${Q}/${L.length} operations failed`})}catch(e){y.warn("Failed to update tool record",{recordError:e})}let X="## Line Insertion Results\n\n"
3450
3448
  if(n.workspaceManager){const e=n.workspaceManager.getCurrentContext(n.currentSessionId)
3451
- e.permanentBranch||(X+=`**Branch:** \`${e.branchName}\`\n\n`)}if(X+="**Summary:**\n",X+=`- **Files processed:** ${V+Q}\n`,X+=`- **Successful operations:** ${V}\n`,X+=`- **Failed operations:** ${Q}\n`,X+=`- **Total insertions:** ${K} successful, ${H} failed\n`,X+=`- **Total Lines Added:** ${B}\n`,X+="\n",W.success&&W.commitHash&&(X+="**Git Commit:**\n",X+=`- **Commit Hash:** ${W.commitHash.substring(0,8)}\n`,X+=`- **Files Committed:** ${W.filesCommitted.length}\n`,W.previousCommitHash&&(X+=`- **Previous Commit:** ${W.previousCommitHash.substring(0,8)}\n`),X+="\n"),1===L.length){const e=L[0]
3449
+ e.permanentBranch||(X+=`**Branch:** \`${e.branchName}\`\n\n`)}if(X+="**Summary:**\n",X+=`- **Files processed:** ${V+Q}\n`,X+=`- **Successful operations:** ${V}\n`,X+=`- **Failed operations:** ${Q}\n`,X+=`- **Total insertions:** ${K} successful, ${H} failed\n`,X+=`- **Total Lines Added:** ${B}\n`,X+="\n",G.success&&G.commitHash&&(X+="**Git Commit:**\n",X+=`- **Commit Hash:** ${G.commitHash.substring(0,8)}\n`,X+=`- **Files Committed:** ${G.filesCommitted.length}\n`,G.previousCommitHash&&(X+=`- **Previous Commit:** ${G.previousCommitHash.substring(0,8)}\n`),X+="\n"),1===L.length){const e=L[0]
3452
3450
  if(X+=`**Operation:** ${e.success?"\u2705 Success":"\u274c Failed"}\n`,"file"===e.type?X+=`**File:** ${e.path}\n`:(X+="**Document:** "+e.documentId,e.filename&&(X+=` (${e.filename})`),X+="\n"),e.error?X+=`**Error:** ${e.error}\n`:(X+=`**Insertions:** ${e.successfulInsertions} successful`,e.failedInsertions>0&&(X+=`, ${e.failedInsertions} failed`),X+="\n",X+=`**Lines Added:** ${e.totalLinesAdded}\n`),e.insertions.some((e=>!e.success))){X+="**Failed Insertion Details:**\n"
3453
3451
  for(const t of e.insertions.filter((e=>!e.success))){const e="between"===t.type?`between lines ${t.afterLine} and ${t.beforeLine}`:`${t.type} line ${t.line}`
3454
3452
  X+=`- ${t.type} insertion ${e}: ${t.error}\n`}}}else{X+="**Summary:**\n",X+=`- **Total Operations:** ${L.length}\n`,X+=`- **Successful:** ${V}\n`,X+=`- **Failed:** ${Q}\n`,X+=`- **Total Insertions:** ${K} successful, ${H} failed\n`,X+=`- **Total Lines Added:** ${B}\n\n`,X+="**Detailed Results:**\n\n"
@@ -3590,21 +3588,21 @@ if(I=L,k=L,v=C.fileOperationPhase,g.incrementCounter("replace_lines_phase",1,{ph
3590
3588
  const e=await(0,h.commitUserChangesBeforeToolOperation)(n.git,"replace-lines",{requestId:D,taskId:x,workspaceManager:n.workspaceManager},w,g)
3591
3589
  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:x,fileOperationsCount:M.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:x,fileOperationsCount:M.length,error:e instanceof Error?e.message:e+""})}w.debug("[REPLACE-LINES] Proceeding with file operations",{fileCount:M.length})
3592
3590
  const H=[]
3593
- let B=0,U=0,W=0,G=0
3591
+ let B=0,U=0,G=0,W=0
3594
3592
  for(const e of M){const t={type:"file",path:e.path,success:!1,replacements:[],successfulReplacements:0,failedReplacements:0,totalLinesReplaced:0,totalLinesAdded:0}
3595
3593
  try{if(await c.default.access(e.fullPath),!(await c.default.stat(e.fullPath)).isFile())throw Error("Path is not a file: "+e.path)
3596
3594
  const n=await c.default.readFile(e.fullPath,"utf8"),{newContent:s,results:o}=await r(n,e.replacements)
3597
- t.replacements=o,t.successfulReplacements=o.filter((e=>e.success)).length,t.failedReplacements=o.filter((e=>!e.success)).length,t.totalLinesReplaced=o.filter((e=>e.success)).reduce(((e,t)=>e+(t.linesReplaced||0)),0),t.totalLinesAdded=o.filter((e=>e.success)).reduce(((e,t)=>e+(t.linesAdded||0)),0),s!==n&&t.successfulReplacements>0?(await c.default.writeFile(e.fullPath,s,"utf8"),t.success=!0,G++,w.info("File updated successfully",{path:e.path,successfulReplacements:t.successfulReplacements,failedReplacements:t.failedReplacements,linesReplaced:t.totalLinesReplaced,linesAdded:t.totalLinesAdded})):0===t.failedReplacements&&(t.success=!0),B+=t.failedReplacements,U+=t.totalLinesReplaced,W+=t.totalLinesAdded}catch(r){t.error=(0,m.getErrorMessage)(r),t.failedReplacements=e.replacements.length,B+=t.failedReplacements,w.error("File operation failed",{path:e.path,error:t.error})}H.push(t)}for(const e of $){const t={type:"document",documentId:e.documentId,success:!1,replacements:[],successfulReplacements:0,failedReplacements:0,totalLinesReplaced:0,totalLinesAdded:0}
3595
+ t.replacements=o,t.successfulReplacements=o.filter((e=>e.success)).length,t.failedReplacements=o.filter((e=>!e.success)).length,t.totalLinesReplaced=o.filter((e=>e.success)).reduce(((e,t)=>e+(t.linesReplaced||0)),0),t.totalLinesAdded=o.filter((e=>e.success)).reduce(((e,t)=>e+(t.linesAdded||0)),0),s!==n&&t.successfulReplacements>0?(await c.default.writeFile(e.fullPath,s,"utf8"),t.success=!0,W++,w.info("File updated successfully",{path:e.path,successfulReplacements:t.successfulReplacements,failedReplacements:t.failedReplacements,linesReplaced:t.totalLinesReplaced,linesAdded:t.totalLinesAdded})):0===t.failedReplacements&&(t.success=!0),B+=t.failedReplacements,U+=t.totalLinesReplaced,G+=t.totalLinesAdded}catch(r){t.error=(0,m.getErrorMessage)(r),t.failedReplacements=e.replacements.length,B+=t.failedReplacements,w.error("File operation failed",{path:e.path,error:t.error})}H.push(t)}for(const e of $){const t={type:"document",documentId:e.documentId,success:!1,replacements:[],successfulReplacements:0,failedReplacements:0,totalLinesReplaced:0,totalLinesAdded:0}
3598
3596
  try{const s=await n.documentFileManager.getDocumentAsFile(e.documentId),o=n.documentFileManager.getCachedDocumentInfo(e.documentId)
3599
3597
  t.filename=o?.filename
3600
3598
  const i=await c.default.readFile(s,"utf8"),{newContent:a,results:l}=await r(i,e.replacements)
3601
- t.replacements=l,t.successfulReplacements=l.filter((e=>e.success)).length,t.failedReplacements=l.filter((e=>!e.success)).length,t.totalLinesReplaced=l.filter((e=>e.success)).reduce(((e,t)=>e+(t.linesReplaced||0)),0),t.totalLinesAdded=l.filter((e=>e.success)).reduce(((e,t)=>e+(t.linesAdded||0)),0),a!==i&&t.successfulReplacements>0?(await c.default.writeFile(s,a,"utf8"),n.documentFileManager.markForSync(e.documentId),t.success=!0,G++,w.info("Document updated successfully",{documentId:e.documentId,filename:t.filename,successfulReplacements:t.successfulReplacements,failedReplacements:t.failedReplacements,linesReplaced:t.totalLinesReplaced,linesAdded:t.totalLinesAdded})):0===t.failedReplacements&&(t.success=!0),B+=t.failedReplacements,U+=t.totalLinesReplaced,W+=t.totalLinesAdded}catch(r){t.error=(0,m.getErrorMessage)(r),t.failedReplacements=e.replacements.length,B+=t.failedReplacements,w.error("Document operation failed",{documentId:e.documentId,error:t.error})}H.push(t)}if($.length>0)try{for(const e of $){const t=H.find((t=>t.documentId===e.documentId))
3602
- t?.success&&await n.documentFileManager.syncDocument(e.documentId)}}catch(e){w.warn("Some documents may not have synced properly",{error:e})}const V=await l.handleMultiFileGitCommit(x,M,H,G,{operationType:"Replace lines",toolRecordId:k,successField:"successfulReplacements",logger:w,metrics:g,metricsPrefix:"replace_lines"},n),Q=l.processMultiFileOperationResults(H,"successfulReplacements"),{successfulOperations:K,failedOperations:J,totalSuccessfulChanges:Y,overallSuccess:Z,successfulFiles:X,failedFiles:ee}=Q
3603
- try{await o.updateToolRecord(k,{metadata:{path:M.length>0?M[0].path:"document:"+$[0]?.documentId,status:Z?"completed":"failed",error:Z?void 0:J+" operations failed",totalOperations:H.length,successfulFiles:X,failedFiles:ee,operations:z,totalSuccessfulReplacements:Y,totalFailedReplacements:B,totalLinesReplaced:U,totalLinesAdded:W,operationsWithChanges:G,processingTime:Date.now()-b,userComment:S,...M.length>0&&V.success&&V.commitHash&&{commitHash:V.commitHash,previousCommitHash:V.previousCommitHash||void 0}},note:Z?`Line replacement completed: ${K}/${H.length} operations successful${M.length>0?" (committed to git)":""}`:`Line replacement partially failed: ${J}/${H.length} operations failed`})}catch(e){w.warn("Failed to update tool record",{recordError:e})}let te="## Line Replacement Results\n\n"
3599
+ t.replacements=l,t.successfulReplacements=l.filter((e=>e.success)).length,t.failedReplacements=l.filter((e=>!e.success)).length,t.totalLinesReplaced=l.filter((e=>e.success)).reduce(((e,t)=>e+(t.linesReplaced||0)),0),t.totalLinesAdded=l.filter((e=>e.success)).reduce(((e,t)=>e+(t.linesAdded||0)),0),a!==i&&t.successfulReplacements>0?(await c.default.writeFile(s,a,"utf8"),n.documentFileManager.markForSync(e.documentId),t.success=!0,W++,w.info("Document updated successfully",{documentId:e.documentId,filename:t.filename,successfulReplacements:t.successfulReplacements,failedReplacements:t.failedReplacements,linesReplaced:t.totalLinesReplaced,linesAdded:t.totalLinesAdded})):0===t.failedReplacements&&(t.success=!0),B+=t.failedReplacements,U+=t.totalLinesReplaced,G+=t.totalLinesAdded}catch(r){t.error=(0,m.getErrorMessage)(r),t.failedReplacements=e.replacements.length,B+=t.failedReplacements,w.error("Document operation failed",{documentId:e.documentId,error:t.error})}H.push(t)}if($.length>0)try{for(const e of $){const t=H.find((t=>t.documentId===e.documentId))
3600
+ t?.success&&await n.documentFileManager.syncDocument(e.documentId)}}catch(e){w.warn("Some documents may not have synced properly",{error:e})}const V=await l.handleMultiFileGitCommit(x,M,H,W,{operationType:"Replace lines",toolRecordId:k,successField:"successfulReplacements",logger:w,metrics:g,metricsPrefix:"replace_lines"},n),Q=l.processMultiFileOperationResults(H,"successfulReplacements"),{successfulOperations:K,failedOperations:J,totalSuccessfulChanges:Y,overallSuccess:Z,successfulFiles:X,failedFiles:ee}=Q
3601
+ try{await o.updateToolRecord(k,{metadata:{path:M.length>0?M[0].path:"document:"+$[0]?.documentId,status:Z?"completed":"failed",error:Z?void 0:J+" operations failed",totalOperations:H.length,successfulFiles:X,failedFiles:ee,operations:z,totalSuccessfulReplacements:Y,totalFailedReplacements:B,totalLinesReplaced:U,totalLinesAdded:G,operationsWithChanges:W,processingTime:Date.now()-b,userComment:S,...M.length>0&&V.success&&V.commitHash&&{commitHash:V.commitHash,previousCommitHash:V.previousCommitHash||void 0}},note:Z?`Line replacement completed: ${K}/${H.length} operations successful${M.length>0?" (committed to git)":""}`:`Line replacement partially failed: ${J}/${H.length} operations failed`})}catch(e){w.warn("Failed to update tool record",{recordError:e})}let te="## Line Replacement Results\n\n"
3604
3602
  if(n.workspaceManager){const e=n.workspaceManager.getCurrentContext(n.currentSessionId)
3605
- e.permanentBranch||(te+=`**Branch:** \`${e.branchName}\`\n\n`)}if(te+="**Summary:**\n",te+=`- **Files processed:** ${K+J}\n`,te+=`- **Successful operations:** ${K}\n`,te+=`- **Failed operations:** ${J}\n`,te+=`- **Total replacements:** ${Y} successful, ${B} failed\n`,te+=`- **Total Lines Replaced:** ${U}\n`,W>0&&(te+=`- **Total Lines Added:** ${W}\n`),te+="\n",V.success&&V.commitHash&&(te+="**Git Commit:**\n",te+=`- **Commit Hash:** ${V.commitHash.substring(0,8)}\n`,te+=`- **Files Committed:** ${V.filesCommitted.length}\n`,V.previousCommitHash&&(te+=`- **Previous Commit:** ${V.previousCommitHash.substring(0,8)}\n`),te+="\n"),1===H.length){const e=H[0]
3603
+ e.permanentBranch||(te+=`**Branch:** \`${e.branchName}\`\n\n`)}if(te+="**Summary:**\n",te+=`- **Files processed:** ${K+J}\n`,te+=`- **Successful operations:** ${K}\n`,te+=`- **Failed operations:** ${J}\n`,te+=`- **Total replacements:** ${Y} successful, ${B} failed\n`,te+=`- **Total Lines Replaced:** ${U}\n`,G>0&&(te+=`- **Total Lines Added:** ${G}\n`),te+="\n",V.success&&V.commitHash&&(te+="**Git Commit:**\n",te+=`- **Commit Hash:** ${V.commitHash.substring(0,8)}\n`,te+=`- **Files Committed:** ${V.filesCommitted.length}\n`,V.previousCommitHash&&(te+=`- **Previous Commit:** ${V.previousCommitHash.substring(0,8)}\n`),te+="\n"),1===H.length){const e=H[0]
3606
3604
  if(te+=`**Operation:** ${e.success?"\u2705 Success":"\u274c Failed"}\n`,"file"===e.type?te+=`**File:** ${e.path}\n`:(te+="**Document:** "+e.documentId,e.filename&&(te+=` (${e.filename})`),te+="\n"),e.error?te+=`**Error:** ${e.error}\n`:(te+=`**Replacements:** ${e.successfulReplacements} successful`,e.failedReplacements>0&&(te+=`, ${e.failedReplacements} failed`),te+="\n",te+=`**Lines Replaced:** ${e.totalLinesReplaced}\n`,e.totalLinesAdded>0&&(te+=`**Lines Added:** ${e.totalLinesAdded}\n`)),e.replacements.some((e=>!e.success))){te+="**Failed Replacement Details:**\n"
3607
- for(const t of e.replacements.filter((e=>!e.success)))te+=`- Replace ${"number"==typeof t.lines?"line "+t.lines:Array.isArray(t.lines)?`lines [${t.lines.join(", ")}]`:`lines ${t.lines.start}-${t.lines.end}`}: ${t.error}\n`}}else{te+="**Summary:**\n",te+=`- **Total Operations:** ${H.length}\n`,te+=`- **Successful:** ${K}\n`,te+=`- **Failed:** ${J}\n`,te+=`- **Total Replacements:** ${Y} successful, ${B} failed\n`,te+=`- **Total Lines Replaced:** ${U}\n`,W>0&&(te+=`- **Total Lines Added:** ${W}\n`),te+="\n",te+="**Detailed Results:**\n\n"
3605
+ for(const t of e.replacements.filter((e=>!e.success)))te+=`- Replace ${"number"==typeof t.lines?"line "+t.lines:Array.isArray(t.lines)?`lines [${t.lines.join(", ")}]`:`lines ${t.lines.start}-${t.lines.end}`}: ${t.error}\n`}}else{te+="**Summary:**\n",te+=`- **Total Operations:** ${H.length}\n`,te+=`- **Successful:** ${K}\n`,te+=`- **Failed:** ${J}\n`,te+=`- **Total Replacements:** ${Y} successful, ${B} failed\n`,te+=`- **Total Lines Replaced:** ${U}\n`,G>0&&(te+=`- **Total Lines Added:** ${G}\n`),te+="\n",te+="**Detailed Results:**\n\n"
3608
3606
  for(const e of H){if("file"===e.type?te+=`### File: ${e.path}\n`:(te+="### Document: "+e.documentId,e.filename&&(te+=` (${e.filename})`),te+="\n"),te+=`- **Status:** ${e.success?"\u2705 Success":"\u274c Failed"}\n`,e.error&&(te+=`- **Error:** ${e.error}\n`),te+=`- **Replacements:** ${e.successfulReplacements} successful`,e.failedReplacements>0&&(te+=`, ${e.failedReplacements} failed`),te+="\n",te+=`- **Lines Replaced:** ${e.totalLinesReplaced}\n`,e.totalLinesAdded>0&&(te+=`- **Lines Added:** ${e.totalLinesAdded}\n`),e.replacements.some((e=>!e.success))){te+="- **Failed Replacement Details:**\n"
3609
3607
  for(const t of e.replacements.filter((e=>!e.success)))te+=` - Replace ${"number"==typeof t.lines?"line "+t.lines:Array.isArray(t.lines)?`lines [${t.lines.join(", ")}]`:`lines ${t.lines.start}-${t.lines.end}`}: ${t.error}\n`}te+="\n"}}$.length>0&&(te+="\u{1f4c4} **Document Changes:** Modified documents have been automatically synced back to the tracking system.\n\n")
3610
3608
  const re=Date.now()-b
@@ -3752,22 +3750,22 @@ ne&&ne.trim().length>0?(te=ne.split("\n").map((e=>"+"+e)).join("\n"),E.set(F,{fi
3752
3750
  te=await t.git.diff(oe),E.set(F,{firstChange:Z,lastChange:X,hasDiff:!(!te||0>=te.trim().length),diffText:te||"No changes detected in git diff"})}}catch(ie){o.warn("Error generating diff for "+F,{error:ie instanceof Error?ie.message:ie+""}),E.set(F,{firstChange:Z,lastChange:X,hasDiff:!0,diffText:"Error generating diff, but git history is available"})}continue}}else o.info("No git history found for "+F)}}catch(ae){o.warn("Error getting git history for "+F,{error:(0,u.getErrorMessage)(ae)})}E.set(F,{firstChange:N[0],lastChange:N[N.length-1],hasDiff:!1,diffText:"No commit hashes available for this file"})
3753
3751
  continue}const H=L[0],B=L[L.length-1]
3754
3752
  o.info(`First commit: ${H.commitHash?H.commitHash:"unknown"}, Last commit: ${B.commitHash?B.commitHash:"unknown"}`)
3755
- let U=null,W=!1
3756
- if(t.git&&H.commitHash)try{U=await(0,c.findPreviousCommit)(t.git,F,H.commitHash),U?(W=!0,o.info(`Found previous commit: ${U} for ${F}`)):o.info(`No previous commit found for ${F}, likely a new file`)}catch(ce){o.warn("Error finding previous commit for "+F,{error:(0,u.getErrorMessage)(ce)})}const G=W&&U?{...H,commitHash:U,status:"pre-task"}:H
3757
- if(o.info(`Using ${W?"pre-task":"first task"} commit as baseline for ${F}: ${G.commitHash}`),o.info(`Last commit for ${F}: ${B.commitHash}`),o.info("Commits are "+(G.commitHash===B.commitHash?"identical":"different")),H.commitHash===B.commitHash&&!W){if(g&&!W&&t.git&&B.commitHash)try{const le=[`${B.commitHash}:${F}`]
3753
+ let U=null,G=!1
3754
+ if(t.git&&H.commitHash)try{U=await(0,c.findPreviousCommit)(t.git,F,H.commitHash),U?(G=!0,o.info(`Found previous commit: ${U} for ${F}`)):o.info(`No previous commit found for ${F}, likely a new file`)}catch(ce){o.warn("Error finding previous commit for "+F,{error:(0,u.getErrorMessage)(ce)})}const W=G&&U?{...H,commitHash:U,status:"pre-task"}:H
3755
+ if(o.info(`Using ${G?"pre-task":"first task"} commit as baseline for ${F}: ${W.commitHash}`),o.info(`Last commit for ${F}: ${B.commitHash}`),o.info("Commits are "+(W.commitHash===B.commitHash?"identical":"different")),H.commitHash===B.commitHash&&!G){if(g&&!G&&t.git&&B.commitHash)try{const le=[`${B.commitHash}:${F}`]
3758
3756
  o.info(`Getting new file content using git show: ${B.commitHash?B.commitHash.substring(0,8):"unknown"}:${F}`)
3759
3757
  const de=await t.git.show(le)
3760
3758
  if(de&&de.trim().length>0){let ue=""
3761
3759
  const he=de.split("\n")
3762
3760
  for(const pe of he)ue+=`+${pe}\n`
3763
- E.set(F,{firstChange:G,lastChange:B,hasDiff:!0,diffText:ue}),o.info(`Formatted new file content as diff, ${he.length} lines`)
3764
- continue}}catch(me){o.warn("Error getting new file content for "+F,{error:(0,u.getErrorMessage)(me)})}E.set(F,{firstChange:G,lastChange:B,hasDiff:!1,diffText:"Only one commit available for this file"})
3765
- continue}if(H.commitHash===B.commitHash&&W){if(U===H.commitHash){E.set(F,{firstChange:G,lastChange:B,hasDiff:!1,diffText:"No changes detected"})
3761
+ E.set(F,{firstChange:W,lastChange:B,hasDiff:!0,diffText:ue}),o.info(`Formatted new file content as diff, ${he.length} lines`)
3762
+ continue}}catch(me){o.warn("Error getting new file content for "+F,{error:(0,u.getErrorMessage)(me)})}E.set(F,{firstChange:W,lastChange:B,hasDiff:!1,diffText:"Only one commit available for this file"})
3763
+ continue}if(H.commitHash===B.commitHash&&G){if(U===H.commitHash){E.set(F,{firstChange:W,lastChange:B,hasDiff:!1,diffText:"No changes detected"})
3766
3764
  continue}o.info("Special case: First and last task commits are the same, but pre-task commit is different")}let V="",Q=!1
3767
- if(g||"pre-task"!==G.status){try{if(t.git){try{await t.git.show([`${B.commitHash}:${F}`])}catch(fe){o.warn(`File ${F} does not exist in commit ${B.commitHash}, might be deleted or moved`),V="File was deleted or moved from this location during the task",Q=!0,E.set(F,{firstChange:G,lastChange:B,hasDiff:Q,diffText:V})
3765
+ if(g||"pre-task"!==W.status){try{if(t.git){try{await t.git.show([`${B.commitHash}:${F}`])}catch(fe){o.warn(`File ${F} does not exist in commit ${B.commitHash}, might be deleted or moved`),V="File was deleted or moved from this location during the task",Q=!0,E.set(F,{firstChange:W,lastChange:B,hasDiff:Q,diffText:V})
3768
3766
  continue}let ge
3769
- if("pre-task"===G.status?(ge=[`${G.commitHash}..${B.commitHash}`,"--",F],o.info(`Comparing pre-task commit with final commit: ${G.commitHash?G.commitHash.substring(0,8):"unknown"}..${B.commitHash?B.commitHash.substring(0,8):"unknown"} for ${F}`)):(ge=["4b825dc642cb6eb9a060e54bf8d69288fbee4904.."+B.commitHash,"--",F],o.info(`File created during task, showing full file diff using empty tree object: ${B.commitHash?B.commitHash.substring(0,8):"unknown"} for ${F}`)),V=await t.git.diff(ge),Q=!!V&&V.trim().length>0,!Q&&G.commitHash!==B.commitHash){o.info("No diff found between commits but commits are different, trying direct file content comparison")
3770
- try{const ye=await t.git.show([`${G.commitHash}:${F}`]),we=await t.git.show([`${B.commitHash}:${F}`])
3767
+ if("pre-task"===W.status?(ge=[`${W.commitHash}..${B.commitHash}`,"--",F],o.info(`Comparing pre-task commit with final commit: ${W.commitHash?W.commitHash.substring(0,8):"unknown"}..${B.commitHash?B.commitHash.substring(0,8):"unknown"} for ${F}`)):(ge=["4b825dc642cb6eb9a060e54bf8d69288fbee4904.."+B.commitHash,"--",F],o.info(`File created during task, showing full file diff using empty tree object: ${B.commitHash?B.commitHash.substring(0,8):"unknown"} for ${F}`)),V=await t.git.diff(ge),Q=!!V&&V.trim().length>0,!Q&&W.commitHash!==B.commitHash){o.info("No diff found between commits but commits are different, trying direct file content comparison")
3768
+ try{const ye=await t.git.show([`${W.commitHash}:${F}`]),we=await t.git.show([`${B.commitHash}:${F}`])
3771
3769
  if(ye!==we&&(Q=!0,V="Changes detected through direct content comparison (not visible in standard diff)",g)){const be=ye.split("\n"),Ie=we.split("\n")
3772
3770
  Math.abs(be.length-Ie.length)>0&&(V+=`\n\nLine count changed: ${be.length} \u2192 ${Ie.length}`),ye.includes("\0")||we.includes("\0")?V+="\n\nBinary content detected - changes not displayed":ye.replace(/\s+/g,"")===we.replace(/\s+/g,"")&&(V+="\n\nWhitespace-only changes detected")}}catch(ke){o.warn("Error in direct file comparison: "+(ke instanceof Error?ke.message:ke+""))}}o.info(`Diff generation result for ${F}: hasDiff=${Q}, length=${V.length}`)}}catch(ve){if(o.warn("Error getting diff for "+F,{error:(0,u.getErrorMessage)(ve),firstCommit:H.commitHash||"unknown",lastCommit:B.commitHash||"unknown"}),V="Error getting diff: "+(0,u.getErrorMessage)(ve),"pre-task"!==H.status&&t.git)try{const Ce=[`${B.commitHash}:${F}`]
3773
3771
  o.info(`Trying fallback to show file content using git show: ${B.commitHash?B.commitHash.substring(0,8):"unknown"}:${F}`)
@@ -3776,7 +3774,7 @@ if(_e&&_e.trim().length>0){if(g){let Ee=""
3776
3774
  const Se=_e.split("\n")
3777
3775
  for(const qe of Se)Ee+=`+${qe}\n`
3778
3776
  V=Ee}else V="File created during this task. Content:\n"+_e
3779
- Q=!0,o.info("Fallback succeeded, got file content of length "+_e.length)}}catch(Te){o.warn("Fallback also failed for "+F,{error:Te instanceof Error?Te.message:Te+""})}}E.set(F,{firstChange:G,lastChange:B,hasDiff:Q,diffText:V||"No changes detected between commits"})}else Q=G.commitHash!==B.commitHash,V=Q?"File has changes":"No changes detected between commits",E.set(F,{firstChange:G,lastChange:B,hasDiff:Q,diffText:V})}let S=`# File Changes for Task: ${m}\n\n`
3777
+ Q=!0,o.info("Fallback succeeded, got file content of length "+_e.length)}}catch(Te){o.warn("Fallback also failed for "+F,{error:Te instanceof Error?Te.message:Te+""})}}E.set(F,{firstChange:W,lastChange:B,hasDiff:Q,diffText:V||"No changes detected between commits"})}else Q=W.commitHash!==B.commitHash,V=Q?"File has changes":"No changes detected between commits",E.set(F,{firstChange:W,lastChange:B,hasDiff:Q,diffText:V})}let S=`# File Changes for Task: ${m}\n\n`
3780
3778
  b&&"object"==typeof b&&(S+="## Task Information\n",S+=`- **Title**: ${b.title||"Unknown"}\n`,S+=`- **Status**: ${b.status||"Unknown"}\n`,S+=`- **Description**: ${b.description||"No description"}\n\n`)
3781
3779
  const q=3e3
3782
3780
  let T=0,x=!1
@@ -4949,12 +4947,12 @@ e&&(this.pollingStatus=o.AUTHENTICATED,this.tokenResult=e,this.stopPolling(),r&&
4949
4947
  function r(){const e=process.env.ALCHEMY_OPENCODE_DIR
4950
4948
  if(e)return e
4951
4949
  try{const e=("undefined"!=typeof require?require:t(4245)).resolve("alchemylab-opencode/package.json")
4952
- return(0,o.dirname)(e)}catch{}}function n(e){return 22===e||e>=24}function s(e,t){return{command:function(){try{const e=(0,a.execFileSync)("node",["--version"],{encoding:"utf8"}).trim()
4950
+ return(0,o.dirname)(e)}catch{}}function n(e){return e>=24}function s(e,t){return{command:function(){try{const e=(0,a.execFileSync)("node",["--version"],{encoding:"utf8"}).trim()
4953
4951
  if(n(parseInt(e.replace("v","").split(".")[0],10)))return"node"}catch{}const e=process.env.NVM_DIR||(0,o.join)(process.env.HOME||"",".nvm"),t=(0,o.join)(e,"versions","node")
4954
4952
  if((0,i.existsSync)(t))try{const e=(0,i.readdirSync)(t).filter((e=>n(parseInt(e.replace("v","").split(".")[0],10)))).sort(((e,t)=>{const r=parseInt(e.replace("v","").split(".")[0],10)
4955
4953
  return parseInt(t.replace("v","").split(".")[0],10)-r}))
4956
4954
  if(e.length>0){const r=(0,o.join)(t,e[0],"bin","node")
4957
- if((0,i.existsSync)(r))return r}}catch{}throw Error(`AlchemyLab requires Node.js 22 or 24+ to run the OpenCode server (Node 23 is not supported).\nYour current version: ${process.version}\nInstall a compatible version: nvm install 22 (or download from https://nodejs.org)`)}(),prependArgs:["--no-warnings",(0,o.join)(e,t)]}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.findOpencodeDir=r,exports.resolveServeCommand=function(){const e=r()
4955
+ if((0,i.existsSync)(r))return r}}catch{}throw Error(`AlchemyLab requires Node.js 24 or later to run the OpenCode server.\nYour current version: ${process.version}\nInstall a compatible version: nvm install 24 (or download from https://nodejs.org)`)}(),prependArgs:["--no-warnings",(0,o.join)(e,t)]}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.findOpencodeDir=r,exports.resolveServeCommand=function(){const e=r()
4958
4956
  return e?s((0,o.join)(e,"dist"),"serve.js"):{command:"opencode",prependArgs:["serve"]}},exports.resolveRunCommand=function(){const e=r()
4959
4957
  return e?s((0,o.join)(e,"dist"),"run.js"):{command:"opencode",prependArgs:[]}}
4960
4958
  const o=t(6928),i=t(9896),a=t(5317)},9072(e,exports,t){"use strict"
@@ -5204,10 +5202,10 @@ C=L,Date.now(),O=Date.now(),_=u.FileOperationPhase.STRING_REPLACEMENT,c.incremen
5204
5202
  const H=[],B=[]
5205
5203
  for(const e of x){const t=e.path?z.get(e.path):void 0,s=await n(e,P,r,0,0,t?.message,t?.debugInfo)
5206
5204
  H.push(s),s.successfulReplacements>0&&"file"===s.type&&s.path&&B.push(s.path)}Date.now(),O=Date.now()
5207
- const U=x.filter((e=>e.path&&!e.documentId)).map((e=>({path:e.path}))),W=await u.handleMultiFileGitCommit(T,U,H,B.length,{operationType:"Replace in files",toolRecordId:L||C||"unknown",successField:"successfulReplacements",logger:k,metrics:c,metricsPrefix:"replace_in_files"},r),G=W.commitHash
5205
+ const U=x.filter((e=>e.path&&!e.documentId)).map((e=>({path:e.path}))),G=await u.handleMultiFileGitCommit(T,U,H,B.length,{operationType:"Replace in files",toolRecordId:L||C||"unknown",successField:"successfulReplacements",logger:k,metrics:c,metricsPrefix:"replace_in_files"},r),W=G.commitHash
5208
5206
  Date.now(),Date.now()
5209
5207
  const V=H.filter((e=>e.success)).length,Q=H.length-V,K=H.reduce(((e,t)=>e+t.successfulReplacements),0),J=H.reduce(((e,t)=>e+t.failedReplacements),0),Y=H.map((e=>({type:e.type,path:e.path,documentId:e.documentId,filename:e.filename,replacements:e.replacements.map((e=>({old_str:e.old,new_str:e.new,success:e.success,error:e.error,matchesFound:e.matchesFound,matchType:e.matchType,lines:e.lines})))}))),Z=H.filter((e=>e.success)).map((e=>({path:e.path,documentId:e.documentId,filename:e.filename,successfulReplacements:e.successfulReplacements,failedReplacements:e.failedReplacements}))),X=H.filter((e=>!e.success)).map((e=>({path:e.path,documentId:e.documentId,filename:e.filename,error:e.error,successfulReplacements:e.successfulReplacements,failedReplacements:e.failedReplacements})))
5210
- await o.updateToolRecord(C,{metadata:{path:j,status:0===Q?"completed":"partial",operationCount:x.length,operations:Y,successfulFiles:Z,failedFiles:X,successfulReplacements:K,failedReplacements:J,processingTime:Date.now()-v,userComment:R,...W.success&&W.commitHash&&{commitHash:W.commitHash,previousCommitHash:W.previousCommitHash||void 0}}})
5208
+ await o.updateToolRecord(C,{metadata:{path:j,status:0===Q?"completed":"partial",operationCount:x.length,operations:Y,successfulFiles:Z,failedFiles:X,successfulReplacements:K,failedReplacements:J,processingTime:Date.now()-v,userComment:R,...G.success&&G.commitHash&&{commitHash:G.commitHash,previousCommitHash:G.previousCommitHash||void 0}}})
5211
5209
  const ee=await async function(e,t,r,n,s){const o=e.filter((e=>e.success)).length,i=e.length-o,a=e.filter((e=>!e.success&&!e.error&&e.successfulReplacements>0)).length
5212
5210
  let c="## Replace In Files - Batch Results\n\n"
5213
5211
  if(s.workspaceManager)try{const e=s.workspaceManager.getCurrentContext(s.currentSessionId)
@@ -5221,7 +5219,7 @@ if(o.length>0){c+="\n**Failed Replacement Details:**\n"
5221
5219
  for(const e of o)c+=`- \u274c \`${e.old.substring(0,40)}${e.old.length>40?"...":""}\` \u2192 \`${e.new.substring(0,40)}${e.new.length>40?"...":""}\`: ${e.error}\n`,e.details&&e.details.length>0&&(c+=" **Candidates found:**\n",c+=e.details.slice(0,5).map((e=>` - Line ${e.line}: \`${e.actually_matched.substring(0,40)}${e.actually_matched.length>40?"...":""}\` (${e.match_type})`)).join("\n")+"\n",e.details.length>5&&(c+=` ... and ${e.details.length-5} more matches\n`)),e.similarMatches&&e.similarMatches.length>0&&(c+=" **Did you mean? (Similar matches found):**\n",c+=e.similarMatches.map((e=>` - Line ${e.line}: \`${e.text.substring(0,60)}${e.text.length>60?"...":""}\` (${e.reason}, similarity: ${(100*e.similarity).toFixed(0)}%)`)).join("\n")+"\n")}c+="\n"}const l=e.filter((e=>"document"===e.type&&(e.success||e.successfulReplacements>0)))
5222
5220
  l.length>0&&(c+=`\n**Document Changes:** ${l.length} modified document(s) synced back to tracking system.\n`)
5223
5221
  const d=s.workspaceManager?.consumeLargeFileWarning(s.currentSessionId)
5224
- return d&&(c+=d),c}(H,G,K,J,r)
5222
+ return d&&(c+=d),c}(H,W,K,J,r)
5225
5223
  return{content:[{type:"text",text:ee}],isError:0===K,...(0,d.alchemyMeta)({kind:"replace-in-files",toolRecordId:C,failed:J>0})}}catch(e){const t=Date.now()-v
5226
5224
  if(k.error("Replace in files batch operation failed",{error:e instanceof Error?e.stack:e+"",duration:t,phase:_}),C)try{await o.updateToolRecord(C,{metadata:{status:"failed",error:(0,b.getErrorMessage)(e),phase:_,duration:t}})}catch(e){}return c.incrementCounter("replace_in_files_errors",1,{phase:_}),{content:[{type:"text",text:"\u274c **Replace In Files Failed:** "+(0,b.getErrorMessage)(e)}],isError:!0}}},c(t(857))
5227
5225
  const l=c(t(1943)),d=(a(t(9896)),a(t(6928)),c(t(6982)),t(2272)),u=a(t(5967)),h=t(3626),p=t(9860),m=t(5003),g=t(7478),f=t(9949),y=t(9947),w=t(1318),b=t(8824)},9470(e,exports){"use strict"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "alchemylab",
3
- "version": "1.4.8",
3
+ "version": "1.4.10",
4
4
  "private": false,
5
5
  "description": "AlchemyLab - agentic coding IDEm",
6
6
  "author": "Testfire Software",