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 +76 -89
- package/dist/http-server.js +10 -12
- package/dist/index.js +10 -12
- package/dist/mcp-direct.js +60 -62
- package/package.json +1 -1
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:"
|
|
114
|
-
const
|
|
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
|
|
117
|
-
const m=e=>{if(!
|
|
118
|
-
const t=
|
|
119
|
-
|
|
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`),
|
|
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=>{
|
|
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||
|
|
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),
|
|
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
|
|
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
|
|
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),
|
|
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:
|
|
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
|
|
1044
|
-
try{
|
|
1045
|
-
try{if((0,s.existsSync)(
|
|
1046
|
-
await e.raw(["reset","--hard",I]),m.info("Reset pool slot to latest branch HEAD",{worktreePath:
|
|
1047
|
-
try{await T.raw(["worktree","remove","--force",
|
|
1048
|
-
try{if(!(await T.branch()).all.includes(I))return(0,c.releaseSlot)(
|
|
1049
|
-
await T.raw(["worktree","add","--detach",
|
|
1050
|
-
m.info("Pool slot worktree already exists, using as-is",{worktreePath:
|
|
1051
|
-
j||m.info("Pool slot fingerprint matches; skipping setup",{worktreePath:
|
|
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:
|
|
1055
|
-
for(const r of t)try{m.info("Setup command",{command:r,worktreePath:
|
|
1056
|
-
e>0&&m.info("Pruned drift from warm slot",{worktreePath:
|
|
1057
|
-
try{const e=(0,n.simpleGit)(
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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)(
|
|
1069
|
-
for(const e of D);return(0,c.watchPidAndRelease)(x,
|
|
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
|
|
1561
|
-
let
|
|
1562
|
-
if(i?.projectId)if(await this.seedAgentsFromRemote(i.projectId),_=await this.fetchProjectFromRemote(i.projectId),
|
|
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
|
|
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
|
-
|
|
1576
|
-
const e=`---\ndescription: Fast codebase exploration and research subagent\nmode: subagent\n---\n\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
|
|
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
|
|
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
|
|
1701
|
-
const
|
|
1702
|
-
const
|
|
1703
|
-
if(0===
|
|
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
|
|
1706
|
-
await
|
|
1707
|
-
let
|
|
1708
|
-
if(!
|
|
1709
|
-
const e=await
|
|
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(
|
|
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,
|
|
1717
|
-
const
|
|
1718
|
-
let
|
|
1719
|
-
try{const e=JSON.parse((0,d.readFileSync)(
|
|
1720
|
-
if(
|
|
1721
|
-
t&&(e.id=t,e.registeredRemotely=!0,e.updatedAt=(new Date).toISOString(),(0,d.writeFileSync)(
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
t.
|
|
1725
|
-
|
|
1726
|
-
try{
|
|
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
|
|
1742
|
-
if(console.log(""),console.log(" Action server .... 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)),
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
1753
|
-
if(console.log("\u{1f310} "+e.authUrl),o(e.authUrl),console.log("\u23f1\ufe0f Waiting for re-authentication..."),await n(
|
|
1754
|
-
e?.token&&(
|
|
1755
|
-
const
|
|
1756
|
-
switch("--help"!==
|
|
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:
|
|
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: "+
|
|
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})()
|
package/dist/http-server.js
CHANGED
|
@@ -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
|
|
3329
|
-
const
|
|
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(
|
|
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&&(
|
|
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}),
|
|
3341
|
-
|
|
3342
|
-
|
|
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
|
|
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
|
|
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
|
|
3338
|
-
const
|
|
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(
|
|
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&&(
|
|
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}),
|
|
3350
|
-
|
|
3351
|
-
|
|
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
|
|
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
|
|
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"
|
package/dist/mcp-direct.js
CHANGED
|
@@ -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),
|
|
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),
|
|
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: ${
|
|
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:** ${
|
|
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:** ${
|
|
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:
|
|
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),
|
|
2386
|
-
let
|
|
2387
|
-
O&&
|
|
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&&
|
|
2390
|
-
const Q=await d.ensureCorrectWorkspaceContext({taskPromoted:V.taskPromoted,requestId:
|
|
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:
|
|
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&&
|
|
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($,
|
|
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
|
|
3105
|
-
const
|
|
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(
|
|
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&&(
|
|
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}),
|
|
3117
|
-
|
|
3118
|
-
|
|
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
|
|
3179
|
-
let
|
|
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,
|
|
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,
|
|
3190
|
-
o.recordValue("delete_files_time",Q),o.incrementCounter("delete_files_operations_total",$),o.incrementCounter("delete_files_operations_success",
|
|
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(
|
|
3193
|
-
const e=
|
|
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=
|
|
3197
|
-
await c.updateFileOperationRecords(x,y,w,D,I.recordType,
|
|
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(
|
|
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=
|
|
3202
|
-
return`\u2022 ${e.type}: ${t}: ${e.error}`})).join("\n"):"All delete operations failed:\n"+
|
|
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===
|
|
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
|
|
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&&
|
|
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",
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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:
|
|
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`,
|
|
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`,
|
|
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,
|
|
3756
|
-
if(t.git&&H.commitHash)try{U=await(0,c.findPreviousCommit)(t.git,F,H.commitHash),U?(
|
|
3757
|
-
if(o.info(`Using ${
|
|
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:
|
|
3764
|
-
continue}}catch(me){o.warn("Error getting new file content for "+F,{error:(0,u.getErrorMessage)(me)})}E.set(F,{firstChange:
|
|
3765
|
-
continue}if(H.commitHash===B.commitHash&&
|
|
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"!==
|
|
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"===
|
|
3770
|
-
try{const ye=await t.git.show([`${
|
|
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:
|
|
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
|
|
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
|
|
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}))),
|
|
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,...
|
|
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,
|
|
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"
|