alchemylab 1.4.13 → 2.0.1

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/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # AlchemyLab
2
+
3
+ AlchemyLab runs a fleet of coding agents against your project and keeps a record of what they did: every change is a request with a branch, tests, review and a merge you can read back.
4
+
5
+ ## Install
6
+
7
+ Requires Node 24 or newer. Docker is optional; with it, tests and experiments run in sealed containers.
8
+
9
+ ```
10
+ npm install -g alchemylab
11
+ ```
12
+
13
+ ## Start
14
+
15
+ Run it in the directory of the project you want to work on:
16
+
17
+ ```
18
+ cd my-project
19
+ alchemy
20
+ ```
21
+
22
+ The first run asks you to log in with your AlchemyLab account and opens the agent console. Keep it running; the web app talks to it.
23
+
24
+ ## Work
25
+
26
+ Open the project at https://alchemylabcode.com and say what you want in plain words. The lead agent registers each ask as a request, hands it to a coder, and reports back in the same stream. Questions that need you show up there too.
27
+
28
+ ## Commands
29
+
30
+ ```
31
+ alchemy start the local server and the agent console (default)
32
+ alchemy serve start the local server only
33
+ alchemy logout forget the stored login
34
+ alchemy --help
35
+ ```
36
+
37
+ ## More
38
+
39
+ Documentation and help live inside the app under Help.
package/dist/129.js ADDED
@@ -0,0 +1,25 @@
1
+ "use strict"
2
+ exports.id=129,exports.ids=[129],exports.modules={27129:function(t,exports,e){function r(t,e){return new Promise((r=>{(0,l.execFile)("git",e,{cwd:t,maxBuffer:16777216},((t,e,i)=>{r({code:t?"number"==typeof t.code?t.code:1:0,stdout:String(e??""),stderr:String(i??"")})}))}))}async function i(t){return 0===(await r(t,["rev-parse","--verify","--quiet","HEAD"])).code}function s(t){try{return u.realpathSync(t)}catch{return d.resolve(t)}}var o,a=this&&this.__createBinding||(Object.create?function(t,e,r,i){void 0===i&&(i=r)
3
+ var s=Object.getOwnPropertyDescriptor(e,r)
4
+ s&&!("get"in s?!e.__esModule:s.writable||s.configurable)||(s={enumerable:!0,get:function(){return e[r]}}),Object.defineProperty(t,i,s)}:function(t,e,r,i){void 0===i&&(i=r),t[i]=e[r]}),n=this&&this.__setModuleDefault||(Object.create?function(t,e){Object.defineProperty(t,"default",{enumerable:!0,value:e})}:function(t,e){t.default=e}),c=this&&this.__importStar||(o=function(t){return o=Object.getOwnPropertyNames||function(t){var e=[]
5
+ for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[e.length]=r)
6
+ return e},o(t)},function(t){if(t&&t.__esModule)return t
7
+ var e={}
8
+ if(null!=t)for(var r=o(t),i=0;i<r.length;i++)"default"!==r[i]&&a(e,t,r[i])
9
+ return n(e,t),e})
10
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.hasCommits=i,exports.ensureProjectRepository=async function(t){let e=null
11
+ if(!u.existsSync(d.join(t,".git"))){const i=await r(t,["rev-parse","--show-toplevel"])
12
+ if(0===i.code&&i.stdout.trim()){const o=s(i.stdout.trim())
13
+ if(o!==s(t)){const i=await r(t,["ls-files","--","."])
14
+ if(0!==i.code||i.stdout.trim())return{status:"failed",message:`This folder's files belong to the git repository at ${o}, so AlchemyLab will not create a second repository here. Start AlchemyLab in ${o} (requests branch from that repository's default branch), or run \`git init\` here yourself if a nested repository is really what you want.`}
15
+ e=o}}}if(u.existsSync(d.join(t,".git")))return await(0,m.ensureAlchemyIgnoreEntries)(t),await i(t)?{status:"existing",hasHistory:!0}:{status:"existing",hasHistory:!1,message:'This repository has no commits yet. Requests branch from your default branch, so make a first commit (for example `git add -A && git commit -m "Initial commit"`), then start AlchemyLab again.'}
16
+ const o=await r(t,["init","-q","-b","main"])
17
+ if(0!==o.code)return{status:"failed",message:`Could not create a git repository here: ${o.stderr.trim()||"git is not available"}. Install git and run \`git init\` in this folder.`}
18
+ await(0,m.ensureAlchemyIgnoreEntries)(t)
19
+ const a=[];(await r(t,["config","user.email"])).stdout.trim()||a.push("-c","user.name=AlchemyLab","-c","user.email=alchemylab@localhost")
20
+ const n=await r(t,["add","-A"])
21
+ if(0!==n.code)return{status:"failed",message:`Created the repository but could not record the folder: ${n.stderr.trim()}`}
22
+ let c=""
23
+ try{c=(await(0,f.holdBackStagedCredentials)(t)).note}catch(t){return{status:"failed",message:`Created the repository but could not check the folder for key material before its first commit: ${t instanceof Error?t.message:String(t)}`}}const l=(await r(t,["diff","--cached","--name-only"])).stdout.split("\n").filter(Boolean).length,h=await r(t,[...a,"commit","-q","--allow-empty","-m","Initial commit (AlchemyLab created this repository)"+(c?`\n\n${c}`:"")])
24
+ return 0!==h.code?{status:"failed",message:`Created the repository but could not make its first commit: ${h.stderr.trim()}`}:{status:"created",filesCommitted:l,message:(l>0?`Created a git repository and recorded the ${l} file${1===l?"":"s"} already here as its first commit.`:"Created a git repository with an empty first commit.")+(e?` This folder is inside the repository at ${e}, which tracks nothing here, so it has its own.`:"")+(c?` ${c}`:"")}}
25
+ const l=e(35317),u=c(e(79896)),d=c(e(16928)),m=e(84935),f=e(82182)}}
package/dist/19.js ADDED
@@ -0,0 +1,260 @@
1
+ "use strict"
2
+ exports.id=19,exports.ids=[19],exports.modules={16554:(e,exports,t)=>{function n(e,t,n){if(j)try{const s=`[${(new Date).toISOString()}] ${t}${n?" "+JSON.stringify(n):""}\n`,o=(0,x.join)(e.basePath||process.cwd(),".alchemy_lab","spawn-debug.log");(0,v.appendFileSync)(o,s)}catch{}}async function s(e,n,s,o,i,r){if(!(e&&n&&s&&o))return null
3
+ try{const i=await e.proxyPost(`/api/projects/${n}/requests/${s}/pickup-compose`,{sessionId:o}),a=i?.data?.text??i?.text,l="string"==typeof a&&a.trim()?a.trim():null
4
+ if(l&&/Project map/.test(l))return l
5
+ let c=null
6
+ try{const{generateProjectMap:e}=await t.e(491).then(t.t.bind(t,23491,23))
7
+ c=e(r||process.cwd())}catch{c=null}return l||c?[l,c].filter(Boolean).join("\n\n"):null}catch(e){return i.warn("pickup-compose failed; the association hook will deliver guidance as an inject",{requestId:s,error:(0,k.getErrorMessage)(e)}),null}}async function o(e){const{apiClient:t,fromSessionId:n,toSessionId:s,body:o,requestId:i,logger:r}=e
8
+ if(!t||!n)return
9
+ const a=t.getSessionMessageClient()
10
+ if(a)try{const e=await a.createInstruction({fromSessionId:n,toSessionId:s,body:o,...i?{requestId:i}:{}})
11
+ return e?.messageCode}catch(e){return void r.warn("Failed to mint instruction code",{error:(0,k.getErrorMessage)(e),fromSessionId:n,toSessionId:s})}}async function i(e,t,n,s,o){if(e&&t&&n)try{await e.proxyPost(`/api/projects/${t}/session-messages/${encodeURIComponent(n)}/delivered`,{ok:s.ok,error:s.detail||s.error,messageId:s.messageId})}catch(e){o.warn("instruction delivery receipt not written",{instructionCode:n,error:(0,k.getErrorMessage)(e)})}}async function r(e,t,r,a,l){const{agent:c="work",prompt:d,requestId:u,allowParallel:p=!1,useExistingSession:g}=e
12
+ if(n(t,"handleSpawn called",{agent:c,promptLength:d.length,requestId:u,useExistingSession:g,cliOriginSessionId:l?.sessionId||null}),!(0,A.isValidAgent)(c))return{content:[{type:"text",text:`\u274c Unknown agent '${c}'.\n\n**Available agents:** ${(0,A.getAgentNames)().join(", ")}\n\nChoose one of the available agents listed above to see detailed descriptions of each agent.`}],isError:!0}
13
+ const f=l?.sessionId,h=f?I.sessionIdentity.getAgentTypeFor(f).then((e=>e||void 0)):Promise.resolve(void 0),m=async function(e){try{const t=`http://localhost:${E}/api/agents/by-name/${encodeURIComponent(e)}`,n=await fetch(t,{signal:AbortSignal.timeout(8e3)})
14
+ if(!n.ok)return null
15
+ const s=await n.json()
16
+ return s.success?s.data??null:null}catch{return null}}(c).catch((()=>null)),v=(0,$.getServerUrl)(t,r),x=(async()=>{if(!a||!u)return null
17
+ const e=a.getSessionRequestAssociationClient()
18
+ return e?await e.getSessionsForRequest(u,!0):null})().catch((e=>(r.debug("Failed to check for existing workers",{error:(0,k.getErrorMessage)(e)}),null))),[T,q,P,R]=await Promise.all([h,m,v,x])
19
+ try{if(n(t,"Spawn agent type check",{callerSessionId:f||null,callerAgentType:T||null,pid:process.pid}),T){const e=(0,A.getAllowedSubagents)(T)
20
+ if(e&&!e.includes(c))return{content:[{type:"text",text:`\u274c Agent type '${T}' cannot spawn '${c}'.\n\n**Allowed subagents for ${T}:** ${e.join(", ")}\n\nUse one of the allowed agent types instead.`}],isError:!0}}}catch(e){n(t,"allowedSubagents check failed",{error:(0,k.getErrorMessage)(e)})}let U=q?.requestContextMode
21
+ if(!U){const e=(0,A.getBuiltInAgentConfig)(c)
22
+ U=e?.requestContextMode}if(U&&"none"!==U&&!u)return{content:[{type:"text",text:`\u274c Agent '${c}' requires a requestId to be provided.\n\nThis agent has requestContextMode: '${U}' which means it needs to work in the context of a specific request.\n\nPlease provide a requestId when spawning this agent.`}],isError:!0}
23
+ if(u&&"required"===U&&a)try{const e=await a.getRequestClient().getRequest(u)
24
+ if("draft"===e?.status){const t=e?.intake,n="rejected"===t?.status?`the intake reviewer rejected it: ${String(t?.objection||"").slice(0,240)||"(no objection text)"}`:"pending"===t?.status?"it is held at intake awaiting a verdict":"it has not been registered"
25
+ return{content:[{type:"text",text:`\u274c ${u} is a DRAFT: ${n}.\n\nWork cannot start on drafts. Wait for the request to be registered (the user resolves the intake question from the timeline), or address the objection with a revised proposal. Do NOT spawn workers for draft requests.`}],isError:!0}}}catch(e){n(t,"draft-spawn guard read failed (continuing)",{error:(0,k.getErrorMessage)(e)})}if(n(t,"serverUrl resolved",{serverUrl:P}),!t.workspaceManager?.getCurrentContext(t.currentSessionId)?.basePath&&!t.basePath)throw new w.ValidationError("Working directory not available in context")
26
+ const M=l?.sessionId
27
+ let O=""
28
+ if(R){const e=(M?R.filter((e=>e.opencodeSessionId!==M)):R).filter((e=>!1!==e.working))
29
+ if(e.length>0){const t=e.map((e=>e.opencodeSessionId)).join(", ")
30
+ if(!p)return r.warn("Blocked spawn: request already has active workers",{requestId:u,existingWorkerCount:e.length,existingSessionIds:e.map((e=>e.opencodeSessionId))}),{content:[{type:"text",text:`\u274c Cannot spawn: Request ${u} already has ${e.length} active worker(s): ${t}\n\nOptions:\n\u2022 Use \`useExistingSession: "${e[0].opencodeSessionId}"\` to send work to the existing session\n\u2022 Use \`allowParallel: true\` if you really need multiple workers on the same request`}],isError:!0}
31
+ O=`\u26a0\ufe0f Warning: Request ${u} already has ${e.length} active worker(s): ${t}. Proceeding anyway (allowParallel=true).\n\n`,r.warn("Spawning parallel subagent for request with existing active workers",{requestId:u,existingWorkerCount:e.length,existingSessionIds:e.map((e=>e.opencodeSessionId)),allowParallel:!0})}}let D,L,_=d
32
+ u&&(D=`requests/${u}`,n(t,"Setting initialBranch for agent",{agent:c,requestId:u,initialBranch:D,agentRequestContextMode:U||"undefined"}),_="readonly"===U?`Working on request ${u} (READONLY mode - you cannot modify files, only explore and report).\n\n${d}`:`Working on request ${u}.\n\n${d}`)
33
+ let F,N=!1
34
+ if(g){if(L=g,r.info("Using existing session for spawn",{sessionId:L,requestId:u}),!P)throw new w.ValidationError("Could not determine OpenCode server URL")
35
+ try{let e
36
+ try{const t=await fetch(`http://localhost:${E}/api/project-settings`,{signal:AbortSignal.timeout(8e3)})
37
+ if(t.ok){const n=await t.json()
38
+ if(n.data?.defaultModel){const[t,...s]=n.data.defaultModel.split("/")
39
+ e={providerID:t,modelID:s.join("/")}}}}catch{}F=await o({apiClient:a,fromSessionId:l?.sessionId,toSessionId:L,body:_,requestId:u,logger:r})
40
+ let n=_
41
+ const d=await s(a,t.projectId,u,L,r,t.mainBasePath||t.basePath)
42
+ d&&(n=`${d}\n\n---\n\n${n}`)
43
+ const p={body:_,fromSessionId:l?.sessionId,fromAgent:T,...u?{requestId:u}:{},...F?{instructionCode:F}:{}},g=await(0,S.deliverToSession)(L,n,{kind:"agent-delegation",partPayload:p})
44
+ if(await i(a,t.projectId,F,g,r),!g.ok)throw new Error((0,S.deliveryFailureText)(L,g))
45
+ r.info("Prompt delivered to existing session",{sessionId:L,agent:c,messageId:g.messageId})}catch(e){throw new Error(`Failed to use existing session: ${(0,k.getErrorMessage)(e)}`)}}else{N=!0,n(t,"Creating session via spawn-session endpoint")
46
+ const i=M
47
+ j&&(0,b.logSessionIdentity)("subagent_operations.spawn","_meta",{parentSessionId:i})
48
+ try{const g=`http://localhost:${E}/api/spawn-session`,f=await fetch(g,{method:"POST",signal:AbortSignal.timeout(15e3),headers:{"Content-Type":"application/json"},body:JSON.stringify({agent:c,prompt:d,requestId:u,parentSessionId:i,title:e.title,specialization:e.specialization,specializationHeadline:e.specializationHeadline??(e.specialization?e.specialization.split(/[.\n]/)[0].trim().slice(0,80):void 0),allowParallel:p,callerAgentType:T})})
49
+ if(!f.ok){const e=await f.text().catch((()=>"Unknown error"))
50
+ throw new Error(`Spawn session endpoint failed: ${f.status} ${e}`)}const h=await f.json()
51
+ if(!h.success||!h.data)throw new Error(h.error||"Spawn session returned unsuccessful")
52
+ const{sessionId:m,model:w,serverUrl:$,initialBranch:S}=h.data
53
+ D=S,F=await o({apiClient:a,fromSessionId:l?.sessionId,toSessionId:m,body:_,requestId:u,logger:r})
54
+ let I=_
55
+ const b="pickupGuidance"in(h?.data??{})?h.data.pickupGuidance??null:await s(a,t.projectId,u,m,r,t.mainBasePath||t.basePath)
56
+ b&&(I=`${b}\n\n---\n\n${I}`)
57
+ const k=await(0,y.deliverInitialPrompt)({serverUrl:$,sessionId:m,agent:c,prompt:I,model:w,directory:t.mainBasePath,metadata:{alchemy:{kind:"agent-delegation",payload:{body:_,fromSessionId:l?.sessionId,fromAgent:T,...u?{requestId:u}:{},...F?{instructionCode:F}:{}}}}})
58
+ if(!k.success)throw new Error(`Failed to spawn subagent: ${k.error}`)
59
+ L=m,n(t,"Spawn successful via unified endpoint",{sessionId:m,parentSessionId:i,model:w,instructionCode:F}),r.info("Background subagent spawned via unified endpoint",{agent:c,requestId:u,sessionId:m,parentSessionId:i,instructionCode:F})}catch(e){throw new Error(`Failed to spawn subagent: ${(0,k.getErrorMessage)(e)}`)}}let B=O
60
+ B+=g?`Prompt sent to existing session ${L}.`:`Background ${c} subagent spawned successfully.`,u&&(B+=`\nWorking on: ${u}`),F&&(B+=`\nInstruction code: ${F} (the specialist will cite this on their reports/questions via relatesTo)`),B+="\n\nThe subagent is now working independently. You can continue with other tasks."
61
+ const V=!!L&&"unknown"!==L&&"spawning"!==L
62
+ return{content:[{type:"text",text:B}],...(0,C.alchemyMeta)({kind:"spawn-subagent",spawnedSessionId:V?L:void 0,agentType:c,requestId:u||void 0,description:e.title||d.slice(0,50)||c,...F?{instructionCode:F}:{}}),...F?{instructionCode:F}:{}}}async function a(e,t,n,s,r,a,l){if(!await(0,$.getServerUrl)(n,s))throw new w.ValidationError("Could not determine OpenCode server URL")
63
+ if(s.info("Sending message to subagent session",{sessionId:e,promptLength:t.length}),r)try{const t=l?.sessionId
64
+ if(t){const n=r.getCLISessionClient()
65
+ if(n){let o=await n.getByExternalId(t)
66
+ if(o||(o=await n.createOrUpdateSession({externalSessionId:t,providerId:"opencode",status:"active"}),s.info("Created caller session in DB for re-parenting",{callerSessionId:t,dbId:o?.id})),o){const i=await n.getByExternalId(e).catch((()=>null)),r=i?.parentSessionId??null
67
+ r===o.id?s.debug("Parent unchanged for continued agent; no re-parent write",{sessionId:e}):(await n.createOrUpdateSession({externalSessionId:e,providerId:"opencode",parentSessionId:o.id,status:"active"}),s.info("RE-PARENTED continued agent: reports now flow to the current caller",{sessionId:e,fromParentDbId:r,toParentDbId:o.id,toCallerSessionId:t}))}}}}catch(e){s.warn("Failed to update parent session - reports may go to wrong parent",{error:(0,k.getErrorMessage)(e)})}let c=a
68
+ if(!c)try{const t=`http://localhost:${E}/api/sessions/${encodeURIComponent(e)}/agent-type`,n=await fetch(t,{signal:AbortSignal.timeout(8e3)})
69
+ if(n.ok){const e=await n.json()
70
+ e.success&&e.agentType&&(c=e.agentType)}}catch{s.warn("Agent type lookup failed for session",{sessionId:e})}if(!c)throw new w.ValidationError(`Cannot continue session ${e}: unable to determine agent type. The session's agent type could not be looked up from the action server.`)
71
+ try{let a
72
+ try{const e=await fetch(`http://localhost:${E}/api/project-settings`,{signal:AbortSignal.timeout(8e3)})
73
+ if(e.ok){const t=await e.json()
74
+ if(t.data?.defaultModel){const[e,...n]=t.data.defaultModel.split("/")
75
+ a={providerID:e,modelID:n.join("/")}}}}catch{}const d=await o({apiClient:r,fromSessionId:l?.sessionId,toSessionId:e,body:t,logger:s}),u=t,p={body:t,fromSessionId:l?.sessionId,fromAgent:await I.sessionIdentity.getAgentTypeFor(l?.sessionId)||void 0,...d?{instructionCode:d}:{}},g=await(0,S.deliverToSession)(e,u,{kind:"agent-followup",partPayload:p})
76
+ return await i(r,n.projectId,d,g,s),g.ok?(s.info("Message delivered to subagent session",{sessionId:e,agent:c,instructionCode:d,messageId:g.messageId}),{content:[{type:"text",text:[d?`Message delivered to session ${e} (${d}); it is in that session's transcript.`:`Message delivered to session ${e}; it is in that session's transcript.`,c?`That session is a ${c}. It will process this in the background -- it is NOT proof that work started; check for tasks before reporting progress.`:"Role unknown for that session. It will process this in the background -- that is not proof work started; check for tasks before reporting progress."].join("\n")}],...d?{_meta:{alchemy:{kind:"continue-subagent",instructionCode:d}},instructionCode:d}:{}}):{content:[{type:"text",text:(0,S.deliveryFailureText)(e,g)+(c?` That session is a ${c}.`:"")}],isError:!0,...d?{_meta:{alchemy:{kind:"continue-subagent",instructionCode:d,failed:!0}},instructionCode:d}:{}}}catch(e){throw new Error(`Failed to continue session: ${(0,k.getErrorMessage)(e)}`)}}function l(e){const t=Math.floor(e/1e3)
77
+ if(t<60)return`${t}s`
78
+ const n=Math.floor(t/60)
79
+ return n<60?`${n}m ${t%60}s`:`${Math.floor(n/60)}h ${n%60}m`}function c(e){const t=[]
80
+ for(const n of e)if("assistant"===n.info.role&&n.parts)for(const e of n.parts)if("tool"===e.type&&e.tool&&e.state){const s=e.state.input||{}
81
+ let o=e.tool.replace(/^alchemy_/,"")
82
+ const i=s.path||s.file||s.directory,r=s.needle||s.query||s.pattern
83
+ "string"==typeof i?o+=` ${i.split("/").pop()||i}`:"string"==typeof r&&(o+=` "${r.length>30?r.slice(0,27)+"...":r}"`),t.push({name:o,state:e.state.status,time:n.info.time.created})}t.sort(((e,t)=>t.time-e.time))
84
+ const n=t.find((e=>"pending"===e.state||"running"===e.state)),s=t.filter((e=>"completed"===e.state||"error"===e.state)).slice(0,3),o=t.length>0?t[0].time:null
85
+ return{current:n?.name??null,recent:s.map((e=>e.name)),lastActivityTime:o}}function d(e){return e<1e3?"<1s ago":e<6e4?`${Math.floor(e/1e3)}s ago`:e<36e5?`${Math.floor(e/6e4)}m ago`:`${Math.floor(e/36e5)}h ago`}function u(e,t){const n=e||t,s=Date.now()-n
86
+ if(e&&s<q)return{status:"working",detail:`last action ${d(s)}`,isReady:!1}
87
+ const o=d(s).replace(/ ago$/,"")
88
+ return s>P&&e?{status:"stalled",detail:`no activity for ${o}`,isReady:!0}:e?{status:"idle",detail:`no activity for ${o}`,isReady:!0}:{status:"idle",detail:"no tool activity",isReady:!0}}function p(e){let t=0,n=0,s=0
89
+ for(const o of e.trim().split("\n")){if(!o)continue
90
+ const[e,i]=o.split("\t")
91
+ "-"!==e&&(t+=parseInt(e,10)||0),"-"!==i&&(n+=parseInt(i,10)||0),s++}return{additions:t,deletions:n,files:s}}function g(e,t,n,s){const o=e.get(t)
92
+ return e.set(t,{at:s,fingerprint:n}),o&&o.fingerprint===n?`Nothing changed since your last check at ${new Date(o.at).toISOString().slice(11,19)} UTC (${d(s-o.at)}): same activity, same messages, same tokens.`:null}async function f(e,n,s,o){const i=await(0,$.getServerUrl)(n,s)
93
+ if(!i)throw new w.ValidationError("Could not determine OpenCode server URL")
94
+ s.info("Checking status of subagent session",{sessionId:e})
95
+ try{const r=`${i.replace(/\/$/,"")}/session/${e}`,a=await fetch(r,{signal:AbortSignal.timeout(8e3)})
96
+ if(!a.ok)return{content:[{type:"text",text:`Session ${e} not found or has exited.`}],isError:!0}
97
+ const d=await a.json(),f=`${i.replace(/\/$/,"")}/session/${e}/message`,h=await fetch(f,{signal:AbortSignal.timeout(8e3)})
98
+ let m={input:0,output:0,reasoning:0},w="",y=u(null,d.time.updated),$=-1
99
+ if(h.ok){const e=await h.json()
100
+ $=e.length
101
+ for(const t of e)"assistant"===t.info.role&&t.info.tokens&&(m.input+=t.info.tokens.input||0,m.output+=t.info.tokens.output||0,m.reasoning+=t.info.tokens.reasoning||0)
102
+ const t=c(e)
103
+ y=u(t.lastActivityTime,d.time.updated),t.current&&(w+=`\nCurrent: ${t.current}`),t.recent.length>0&&(w+=`\nRecent: ${t.recent.join(", ")}`)}const S=Date.now()-d.time.created,I=y.status,b=y.isReady,k=m.input+m.output+m.reasoning
104
+ let v="",x="",A=null
105
+ if(o)try{const t=o.getSessionRequestAssociationClient()
106
+ if(t){const n=await t.getActiveRequestsForSession(e,!1)
107
+ if(n.length>0){const t=n.filter((e=>!e.endedAt)).sort(((e,t)=>new Date(t.startedAt).getTime()-new Date(e.startedAt).getTime())),o=n.filter((e=>e.endedAt)).sort(((e,t)=>new Date(t.endedAt).getTime()-new Date(e.endedAt).getTime())),i=o[0],r=t[0]
108
+ i&&r?A=new Date(i.endedAt).getTime()>new Date(r.startedAt).getTime()?i.requestLocalId:r.requestLocalId:i?A=i.requestLocalId:r&&(A=r.requestLocalId)
109
+ const a=[]
110
+ t.length>0&&a.push(`Active: ${t.map((e=>e.requestLocalId)).join(", ")}`),o.length>0&&a.push(`Previous: ${o.map((e=>e.requestLocalId)).join(", ")}`),a.length>0&&(x=`\nRequests: ${a.join(" | ")}`),s.debug("Found request associations",{sessionId:e,activeCount:t.length,historicalCount:o.length})}}}catch(e){s.debug("Failed to get request associations, falling back to title extraction",{error:e instanceof Error?e.message:String(e)})}if(A||(A=function(e){const t=e.match(/\[(req\d{6})\]/)
111
+ return t?t[1]:null}(d.title||"")),A&&n.basePath){const e=await async function(e,n){try{const{execFileSync:s}=await Promise.resolve().then(t.t.bind(t,35317,23)),o=`requests/${e}`
112
+ let i=!1
113
+ try{s("git",["rev-parse","--verify",o],{cwd:n,stdio:"pipe"}),i=!0}catch{i=!1}if(i){const e=s("git",["diff",`main...${o}`,"--numstat"],{cwd:n,encoding:"utf8"})
114
+ if(e.trim())return p(e)}try{const t=s("git",["log","main","--merges","--oneline","-n","50",`--grep=${e}`],{cwd:n,encoding:"utf8"}).trim()
115
+ if(t){const e=t.split("\n")[0].split(" ")[0],o=s("git",["diff",`${e}^1...${e}`,"--numstat"],{cwd:n,encoding:"utf8"})
116
+ if(o.trim())return{...p(o),merged:!0}}}catch{}return null}catch{return null}}(A,n.basePath)
117
+ if(e){const t=e.additions+e.deletions,n=e.merged?" (merged)":""
118
+ v=`\nDiff vs main (${A}): +${e.additions}/-${e.deletions} lines (${t} total) in ${e.files} file${1!==e.files?"s":""}${n}`}}const C=g(R,`${n.currentSessionId??""}>${e}`,`${d.time.updated}:${$}:${k}`,Date.now())
119
+ return{content:[{type:"text",text:`${C?`${C}\n\n`:""}Session ${e}\nTitle: ${d.title||"Untitled"}\nStatus: ${I} (${y.detail})\nReady for input: ${b?"Yes":"No"}\nAge: ${l(S)}\nTokens: ${k.toLocaleString()} (in: ${m.input.toLocaleString()}, out: ${m.output.toLocaleString()}${m.reasoning>0?`, reasoning: ${m.reasoning.toLocaleString()}`:""})${w}${x}${v}`}]}}catch(e){throw new Error(`Failed to check session status: ${(0,k.getErrorMessage)(e)}`)}}async function h(e,t,n){const s=await(0,$.getServerUrl)(t,n)
120
+ if(!s)throw new w.ValidationError("Could not determine OpenCode server URL")
121
+ n.info("Aborting subagent session",{sessionId:e})
122
+ try{const t=`${s.replace(/\/$/,"")}/session/${e}/abort`,o=await fetch(t,{signal:AbortSignal.timeout(8e3),method:"POST"})
123
+ if(!o.ok){const e=await o.text().catch((()=>"Unknown error"))
124
+ throw new Error(`Failed to abort session: ${o.status} ${e}`)}return n.info("Session aborted",{sessionId:e}),{content:[{type:"text",text:`Session ${e} has been aborted.`}]}}catch(e){throw new Error(`Failed to abort session: ${(0,k.getErrorMessage)(e)}`)}}async function m(e,t,n,s={}){const{activeOnly:o=!1,requestId:i,limit:r,minTokens:a,maxTokens:d,maxAgeHours:p}=s,g=await(0,$.getServerUrl)(e,t)
125
+ if(!g)throw new w.ValidationError("Could not determine OpenCode server URL")
126
+ t.info("Listing sessions",{filters:s})
127
+ try{const t=`${g.replace(/\/$/,"")}/session?limit=1000`,f=await fetch(t,{signal:AbortSignal.timeout(8e3)})
128
+ if(!f.ok)throw new Error(`Failed to fetch sessions: ${f.status}`)
129
+ let h=await f.json()
130
+ if(0===h.length)return{content:[{type:"text",text:"No sessions found."}]}
131
+ const m=h
132
+ s.parentSessionId&&(h=h.filter((e=>(e.parentId||e.parentID)===s.parentSessionId))),h.sort(((e,t)=>t.time.created-e.time.created))
133
+ const w=`${g.replace(/\/$/,"")}/session/status`,y=await fetch(w,{signal:AbortSignal.timeout(8e3)}),$=y.ok?await y.json():{}
134
+ let S=h
135
+ if(o&&(S=S.filter((e=>"idle"!==($[e.id]?.type||"idle")))),void 0!==p){const e=60*p*60*1e3,t=Date.now()-e
136
+ S=S.filter((e=>e.time.created>=t))}if(0===S.length)return{content:[{type:"text",text:"No sessions match the specified filters."}]}
137
+ let I={}
138
+ const b=S.map((e=>e.id))
139
+ let k={},v=[]
140
+ if("specialist"===s.agentType){const e=new Set(b)
141
+ for(const t of m){const n=t.parentId||t.parentID
142
+ n&&e.has(n)&&(k[n]||(k[n]=[]),k[n].push(t.id),v.push(t.id))}}if(n)try{const e=n.getCLISessionClient()
143
+ if(e){const t=[...b,...v]
144
+ I=await e.batchEnrich(t)}}catch{}const x=g.replace(/\/$/,""),A=await Promise.allSettled(S.map((e=>fetch(`${x}/session/${e.id}/message`,{signal:AbortSignal.timeout(8e3)}).then((e=>e.ok?e.json():[])).catch((()=>[]))))),C=[]
145
+ for(let e=0;e<S.length;e++){const t=S[e],n=Date.now()-t.time.created
146
+ let o=0,r=null,l=[],p=u(null,t.time.updated)
147
+ const g=A[e],f="fulfilled"===g.status?g.value:[]
148
+ for(const e of f)"assistant"===e.info.role&&e.info.tokens&&(o+=(e.info.tokens.input||0)+(e.info.tokens.output||0)+(e.info.tokens.reasoning||0))
149
+ if(f.length>0){const e=c(f)
150
+ p=u(e.lastActivityTime,t.time.updated),r=e.current||null,l=e.recent}const h=p.status
151
+ if(void 0!==a&&o<a)continue
152
+ if(void 0!==d&&o>d)continue
153
+ const m=I[t.id]
154
+ if(s.agentType&&(!m||m.agentType!==s.agentType))continue
155
+ if("archived"===m?.status)continue
156
+ let w=""
157
+ const y=[],b=[]
158
+ m?.associations&&b.push(...m.associations)
159
+ const v=k[t.id]
160
+ if(v)for(const e of v){const t=I[e]
161
+ if(t?.associations){const n=$[e]?.type||"idle",s="idle"===n||"stalled"===n
162
+ for(const e of t.associations)s&&!e.endedAt?b.push({...e,endedAt:"idle"}):b.push(e)}}if(b.length>0){const e=new Map
163
+ for(const t of b){const n=e.get(t.requestLocalId)
164
+ void 0!==n&&(n||t.endedAt)||e.set(t.requestLocalId,!t.endedAt)}const t=[...e.entries()].filter((([,e])=>e)).map((([e])=>e)),n=[...e.entries()].filter((([,e])=>!e)).map((([e])=>e))
165
+ y.push(...t,...n)
166
+ const s=[]
167
+ if(t.length>0&&s.push(`active: ${t.join(", ")}`),n.length>0){const e=5,t=n.slice(0,e),o=n.length-t.length
168
+ s.push(`prev: ${t.join(", ")}${o>0?` +${o} more`:""}`)}s.length>0&&(w=` [${s.join(" | ")}]`)}if(i&&!y.includes(i))continue
169
+ const x=t.title||"Untitled",T=x.length>50?x.slice(0,47)+"...":x
170
+ C.push({id:t.id,title:T,status:h,age:n,totalTokens:o,requestsInfo:w,allRequestIds:y,statusDetail:p.detail,specialization:m?.specialization??null,currentAction:r,recentActions:l})}if(0===C.length)return{content:[{type:"text",text:"No sessions match the specified filters."}]}
171
+ const T=r?C.slice(0,r):C
172
+ let j={}
173
+ if("specialist"===s.agentType&&n&&e.projectId){const t={},s=[]
174
+ for(const e of T)for(const n of e.allRequestIds)t[n]||(t[n]=!0,s.push(n))
175
+ if(s.length>0)try{const t=await n.proxyPost(`/api/projects/${e.projectId}/areas/by-requests`,{requestLocalIds:s}),o=t?.data?.areasByRequest??t?.areasByRequest
176
+ o&&"object"==typeof o&&(j=o)}catch{}}const E=T.map((e=>{const t=[]
177
+ for(const n of e.allRequestIds){const e=j[n]
178
+ if(Array.isArray(e)){for(const n of e){if(t.length>=5)break
179
+ "string"!=typeof n||t.includes(n)||t.push(n)}if(t.length>=5)break}}const n=(e.specialization||"").split(",").map((e=>e.trim())).filter(Boolean),o=n.length>0?n.slice(0,3):t.slice(0,3),i=o.length>0?`\n Areas: ${o.join(", ")}`:"",r=e.currentAction?`\n Current: ${e.currentAction}`:"",a="specialist"!==s.agentType&&e.recentActions.length>0?`\n Recent: ${e.recentActions.join(", ")}`:""
180
+ return`\u2022 ${e.id}\n ${e.title}${i}\n Status: ${e.status} (${e.statusDetail}) | Age: ${l(e.age)} | ${e.totalTokens.toLocaleString()} tokens${e.requestsInfo}${r}${a}`})),q=r&&C.length>r?` (showing ${r} of ${C.length})`:""
181
+ return{content:[{type:"text",text:`Found ${T.length} session${1!==T.length?"s":""}${q}:\n\n${E.join("\n\n")}`}]}}catch(e){throw new Error(`Failed to list sessions: ${(0,k.getErrorMessage)(e)}`)}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleSpawn=r,exports.handleContinue=a,exports.extractRecentActivity=c,exports.formatTimeSince=d,exports.deriveActivityStatus=u,exports.noteStatusCheck=g,exports.handleCheckStatus=f,exports.handleAbort=h,exports.handleListSessions=m,exports.handleSubagentOperations=async function(e,t,n,s,o,i){const l=n.child({tool:"subagent_operations"}),c=s.startTimer("tool_execution_time")
182
+ try{const n=e,{sessionId:d,operation:u}=n
183
+ let p
184
+ switch(l.info("Processing subagent_operations",{operation:u,sessionId:d}),u){case"spawn":p=await r({agent:n.agent,prompt:n.prompt,requestId:n.requestId,allowParallel:n.allowParallel,useExistingSession:n.useExistingSession},t,l,o,i)
185
+ break
186
+ case"continue":p=await a(d,n.prompt,t,l,o,void 0,i)
187
+ break
188
+ case"check-status":p=await f(d,t,l,o)
189
+ break
190
+ case"abort":p=await h(d,t,l)
191
+ break
192
+ case"read-messages":{const e=!(!n.compact&&null==n.select),s=!e&&null==n.limit,o=n.limit??(e?50:8)
193
+ p=await async function(e,t,n,s,o){const i=await(0,$.getServerUrl)(n,s)
194
+ if(!i)throw new w.ValidationError("Could not determine OpenCode server URL")
195
+ s.info("Reading messages from subagent session",{sessionId:e,limit:t,compact:o?.compact,select:o?.select})
196
+ try{const n=`${i.replace(/\/$/,"")}/session/${e}/message?limit=${t}`,s=await fetch(n,{signal:AbortSignal.timeout(8e3)})
197
+ if(!s.ok){if(404===s.status)return{content:[{type:"text",text:`Session ${e} not found.`}],isError:!0}
198
+ throw new Error(`Failed to fetch messages: ${s.status}`)}const r=await s.json()
199
+ if(0===r.length)return{content:[{type:"text",text:`Session ${e} has no messages yet.`}]}
200
+ const a=(e,t)=>e.length>t?e.slice(0,t)+"\u2026[truncated]":e,l=e=>"user"===e.info.role?"USER":"ASSISTANT",c=e=>new Date(e.info.time.created).toLocaleTimeString(),d=e=>`\u{1f527} ${e.tool||"tool"}${"error"===e.state?.status?" \u2717":""}`,u=e=>Array.isArray(e)?e.map((e=>"image"===e?.type?"[image]":"string"==typeof e?.text?e.text:JSON.stringify(e))).join(" "):"string"==typeof e?e:JSON.stringify(e??"")
201
+ if("number"==typeof o?.select){const t=r[o.select-1]
202
+ if(!t)return{content:[{type:"text",text:`No message #${o.select} (session has ${r.length}; use compact to index).`}],isError:!0}
203
+ const n=[`Session ${e}: message ${o.select} of ${r.length}: ${l(t)} (${c(t)}):`,""]
204
+ for(const e of t.parts)if("text"===e.type&&e.text)n.push(`TEXT: ${a(e.text,6e3)}`)
205
+ else if("tool"===e.type){const t=e.state||{}
206
+ n.push(`\u{1f527} ${e.tool||"tool"} [${t.status??"unknown"}]`),n.push(` input: ${a("string"==typeof t.input?t.input:JSON.stringify(t.input??{}),2500)}`)
207
+ const s=u(t.output)
208
+ s&&n.push(` output: ${a(s,1500)}`)}return{content:[{type:"text",text:n.join("\n")}]}}if(o?.compact){const t=r.map(((e,t)=>{const n=e.parts.filter((e=>"tool"===e.type)).map(d),s=e.parts.filter((e=>"text"===e.type&&e.text)).map((e=>e.text)).join(" ").replace(/\s+/g," ").trim().slice(0,140),o=[...n,s?`"${s}${s.length>=140?"\u2026":""}"`:""].filter(Boolean).join(" \xb7 ")
209
+ return`[${t+1}] ${"USER"===l(e)?"USER":"ASST"} (${c(e)}): ${o||"(empty)"}`}))
210
+ return{content:[{type:"text",text:`Session ${e}: ${r.length} messages (compact; use select:N for one in full):\n\n${t.join("\n")}`}]}}const p=(e,t)=>{const n=[`[${t+1}] ${l(e)} (${c(e)}):`]
211
+ for(const t of e.parts)if("text"===t.type&&t.text)n.push(a(t.text,5e3))
212
+ else if("tool"===t.type){const e=t.state||{},s=u(e.output).replace(/\s+/g," ").trim()
213
+ n.push(`\u{1f527} ${t.tool||"tool"} [${e.status??"unknown"}]${s?` \u2192 ${a(s,200)}`:""}`)}return n.length>1?n.join("\n"):`${n[0]}\n(no content)`}
214
+ if(o?.latest){const t=e=>e.parts.some((e=>"text"===e.type&&e.text||"tool"===e.type))
215
+ let n=r.length-1
216
+ for(;n>0&&!t(r[n]);)n--
217
+ const s=r.length-1-n
218
+ return{content:[{type:"text",text:`Session ${e}: latest message${s>0?` (skipped ${s} trailing empty message${1===s?"":"s"}; use compact:true for the full trajectory)`:""}:\n\n${p(r[n],n)}`}]}}const g=r.map(((e,t)=>p(e,t))).join("\n\n---\n\n")
219
+ return{content:[{type:"text",text:`Session ${e}: last ${r.length} messages:\n\n${g}`}]}}catch(e){throw new Error(`Failed to read messages: ${(0,k.getErrorMessage)(e)}`)}}(d,o,t,l,{compact:n.compact,select:n.select,latest:s})
220
+ break}case"list-sessions":const e={activeOnly:n.activeOnly,requestId:n.requestId,limit:n.limit,minTokens:n.minTokens,maxTokens:n.maxTokens,maxAgeHours:n.maxAgeHours,parentSessionId:i?.sessionId}
221
+ p=await m(t,l,o,e)
222
+ break
223
+ default:throw new w.ValidationError(`Unknown operation: ${u}`)}c({success:!0}),s.incrementCounter(`subagent_operations_${u}_success`,1)
224
+ const g=T[u]
225
+ return g?(0,C.withAlchemyKind)(p,g):p}catch(e){return l.error("Subagent operations failed",{error:e instanceof Error?e.stack:String(e)}),c({success:!1}),s.incrementCounter("subagent_operations_errors",1),{content:[{type:"text",text:`Subagent operation failed: ${(0,k.getErrorMessage)(e)}`}],isError:!0}}}
226
+ const w=t(98547),y=t(3928),$=t(16714),S=t(49725),I=t(7451),b=t(87907),k=t(21319),v=t(79896),x=t(16928),A=t(97734),C=t(62263),T={spawn:"spawn-subagent",continue:"continue-subagent","check-status":"check-subagent-status",abort:"abort-subagent","read-messages":"read-subagent-messages","list-sessions":"list-subagent-sessions"},j="true"===process.env.ALCHEMY_DEBUG_LOGS,E=(0,t(53603).getActionServerPort)(),q=6e4,P=3e5,R=new Map},16714:(e,exports,t)=>{Object.defineProperty(exports,"__esModule",{value:!0}),exports.invalidateServerUrlCache=function(){s=null},exports.getServerUrl=async function(e,o){const i=process.env.OPENCODE_TUI_URL
227
+ if(s&&s.basePath===e.basePath&&s.envUrl===i)return s.result
228
+ let r=i
229
+ if(!r)try{const{readFileSync:n}=await Promise.resolve().then(t.t.bind(t,79896,23)),{join:s}=await Promise.resolve().then(t.t.bind(t,16928,23)),i=n(s(e.basePath,".alchemy_lab","opencode.json"),"utf8"),a=JSON.parse(i)
230
+ a.port&&(r=`http://127.0.0.1:${a.port}`,o.debug("Discovered OpenCode server URL from .alchemy_lab/opencode.json",{serverUrl:r}))}catch(e){o.debug("Could not read .alchemy_lab/opencode.json",{error:e instanceof Error?e.message:String(e)})}if(r)try{(0,n.noteServe)(Number(new URL(r).port),(0,n.currentServePassword)(e.basePath))}catch{}return s={basePath:e.basePath,envUrl:i,result:r},r}
231
+ const n=t(7245)
232
+ let s=null},24019:(e,exports,t)=>{exports.formatLiveWorker=function(e){const t=e.agentType?e.agentType:"worker",n=[],s=!0===e.busy?"mid-turn":!1===e.busy?"between turns":"unknown"
233
+ return n.push(`- ${t} (${e.sessionId}): ${e.status}, ${e.detail}; ${s}`),e.current&&n.push(` now: ${e.current}`),e.recent.length>0&&n.push(` recently: ${e.recent.join(", ")}`),e.lastSaid&&n.push(` last said${e.lastSaidAgo?` (${e.lastSaidAgo})`:""}: "${e.lastSaid}"`),e.lastRefusal&&n.push(` last refused (its tool said): ${e.lastRefusal}`),n.join("\n")},exports.readLiveWorker=async function(e,t){if(!e)return null
234
+ const i=t.filter((e=>(0,s.isWorkerAgentType)(e.agentType??""))),r=(i.length>0?i:t).slice(-1)[0]
235
+ if(!r)return null
236
+ const a=e.replace(/\/$/,"")
237
+ try{const[e,t,s]=await Promise.all([fetch(`${a}/session/${r.sessionId}`,{signal:AbortSignal.timeout(5e3)}),fetch(`${a}/session/${r.sessionId}/message?limit=60`,{signal:AbortSignal.timeout(8e3)}),fetch(`${a}/session/status`,{signal:AbortSignal.timeout(5e3)}).catch((()=>null))])
238
+ if(!e.ok||!t.ok)return null
239
+ const i=await e.json(),l=await t.json()
240
+ let c=null
241
+ if(s&&s.ok)try{const e=await s.json(),t=e?.[r.sessionId]
242
+ c=void 0!==t&&"busy"===("string"==typeof t?t:t?.type)}catch{}const d=(0,n.extractRecentActivity)(l),u=(0,n.deriveActivityStatus)(d.lastActivityTime,i.time?.updated??Date.now())
243
+ let p=null,g=null
244
+ for(let e=l.length-1;e>=0;e--){const t=l[e]
245
+ if("assistant"!==t.info.role)continue
246
+ const s=(t.parts??[]).filter((e=>"text"===e.type&&e.text)).map((e=>e.text)).join(" ").replace(/\s+/g," ").trim()
247
+ if(s){p=s.length>220?s.slice(0,217)+"...":s,g=(0,n.formatTimeSince)(Date.now()-(t.info.time.completed??t.info.time.created))
248
+ break}}const f=function(e){for(let t=e.length-1;t>=0;t--){const n=e[t]
249
+ if("user"===n.info.role)return null
250
+ const s=n.parts??[]
251
+ for(let e=s.length-1;e>=0;e--){const t=s[e]
252
+ if("tool"!==t.type)continue
253
+ const n="error"===t.state?.status?String(t.state?.error??""):"string"==typeof t.state?.output?t.state.output:""
254
+ if("error"===t.state?.status||o.test(n)){const e=String(t.tool??"tool").replace(/^alchemy_/,""),s=n.replace(/\s+/g," ").trim()
255
+ return`${e}: ${s.length>240?s.slice(0,237)+"...":s}`}}}return null}(l)
256
+ return{sessionId:r.sessionId,agentType:r.agentType,busy:c,status:u.status,detail:u.detail,current:d.current,recent:d.recent,lastSaid:p,lastSaidAgo:g,lastRefusal:f}}catch{return null}}
257
+ const n=t(16554),s=t(47221),o=/^\s*(?:Error:|\u26d4|\u274c|Not filed|Not started|Not kept|Cannot |STOP:)/},49725:(e,exports,t)=>{Object.defineProperty(exports,"__esModule",{value:!0}),exports.deliverToSession=async function(e,t,s={}){const o=s.noVerify?0:s.verifySeconds??20
258
+ try{const i=await fetch(`${(0,n.getActionServerBaseUrl)()}/api/sessions/${encodeURIComponent(e)}/deliver`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({message:t,kind:s.kind,partPayload:s.partPayload,noVerify:s.noVerify,verifySeconds:s.verifySeconds}),signal:AbortSignal.timeout(1e3*(o+25))}),r=await i.json().catch((()=>null))
259
+ return i.ok?r?.data??{ok:!1,error:"deliver_route",detail:"empty response"}:{ok:!1,error:"deliver_route",detail:r?.error||`HTTP ${i.status}`}}catch(e){return{ok:!1,error:"deliver_route",detail:e instanceof Error?e.message:String(e)}}},exports.deliveryFailureText=function(e,t){return`\u274c NOT delivered to session ${e}: ${t.detail||t.error||"unknown reason"}. The session did not receive this and nothing will act on it.`}
260
+ const n=t(53603)}}
@@ -1,21 +1,21 @@
1
1
  "use strict"
2
- exports.id=800,exports.ids=[800],exports.modules={3800(e,exports,t){var r,o=this&&this.__createBinding||(Object.create?function(e,t,r,o){void 0===o&&(o=r)
2
+ exports.id=237,exports.ids=[237],exports.modules={80237:function(e,exports,t){var r,o=this&&this.__createBinding||(Object.create?function(e,t,r,o){void 0===o&&(o=r)
3
3
  var i=Object.getOwnPropertyDescriptor(t,r)
4
- i&&!("get"in i?!t.__esModule:i.writable||i.configurable)||(i={enumerable:!0,get:()=>t[r]}),Object.defineProperty(e,o,i)}:function(e,t,r,o){void 0===o&&(o=r),e[o]=t[r]}),i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),c=this&&this.__importStar||(r=function(e){return r=Object.getOwnPropertyNames||function(e){var t=[]
5
- for(var r in e)({}).hasOwnProperty.call(e,r)&&(t[t.length]=r)
4
+ i&&!("get"in i?!t.__esModule:i.writable||i.configurable)||(i={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,o,i)}:function(e,t,r,o){void 0===o&&(o=r),e[o]=t[r]}),i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),c=this&&this.__importStar||(r=function(e){return r=Object.getOwnPropertyNames||function(e){var t=[]
5
+ for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[t.length]=r)
6
6
  return t},r(e)},function(e){if(e&&e.__esModule)return e
7
7
  var t={}
8
8
  if(null!=e)for(var c=r(e),s=0;s<c.length;s++)"default"!==c[s]&&o(t,e,c[s])
9
9
  return i(t,e),t})
10
10
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.SimpleProjectCreationLock=void 0
11
- const s=c(t(1943)),a=c(t(6928)),n=t(4139)
11
+ const s=c(t(91943)),a=c(t(16928)),n=t(21992)
12
12
  exports.SimpleProjectCreationLock=class{constructor(e,t){this.maxWaitMs=3e4,this.checkIntervalMs=500,this.lockFilePath=a.join(e,".alchemy-project-creation.lock"),this.projectFilePath=a.join(e,n.ALCHEMY_DIR_NAME,"project.json"),this.logger=t.child({component:"simple-project-creation-lock"})}async acquireLock(){this.logger.info("Attempting to acquire project creation lock",{processId:process.pid,lockFile:this.lockFilePath})
13
13
  const e=await this.loadExistingProject()
14
14
  if(e)return this.logger.info("Project already exists, no creation needed",{projectId:e.id}),{acquired:!1,shouldCreate:!1,projectData:e}
15
15
  try{const e={processId:process.pid,timestamp:Date.now(),lockFile:this.lockFilePath}
16
16
  return await s.writeFile(this.lockFilePath,JSON.stringify(e,null,2),{flag:"wx"}),this.logger.info("Successfully acquired project creation lock",{processId:process.pid,lockFile:this.lockFilePath}),{acquired:!0,shouldCreate:!0}}catch(e){return"EEXIST"===e.code?(this.logger.info("Lock already held by another process, will wait",{processId:process.pid}),await this.waitForProjectCreation()):(this.logger.error("Error acquiring project creation lock",{error:e.message}),{acquired:!0,shouldCreate:!0})}}async releaseLock(){try{await s.unlink(this.lockFilePath),this.logger.info("Released project creation lock",{processId:process.pid})}catch(e){"ENOENT"!==e.code&&this.logger.warn("Failed to release project creation lock",{error:e.message})}}async waitForProjectCreation(){const e=Math.floor(this.maxWaitMs/this.checkIntervalMs)
17
17
  this.logger.info("Waiting for project creation by another process",{maxAttempts:e,processId:process.pid})
18
- for(let t=1;e>=t;t++){await this.sleep(this.checkIntervalMs)
18
+ for(let t=1;t<=e;t++){await this.sleep(this.checkIntervalMs)
19
19
  const r=await this.loadExistingProject()
20
20
  if(r)return this.logger.info("Project creation completed by another process",{projectId:r.id,attempt:t,processId:process.pid}),{acquired:!1,shouldCreate:!1,projectData:r}
21
21
  if(!await this.lockFileExists()&&(this.logger.warn("Lock disappeared but no project found, creation may have failed",{attempt:t,processId:process.pid}),t>e/2))return this.logger.info("Attempting to acquire lock after apparent creation failure"),{acquired:!0,shouldCreate:!0}}return this.logger.error("Timeout waiting for project creation",{maxWaitMs:this.maxWaitMs,processId:process.pid}),{acquired:!0,shouldCreate:!0}}async loadExistingProject(){try{const e=await s.readFile(this.projectFilePath,"utf8"),t=JSON.parse(e)
package/dist/301.js ADDED
@@ -0,0 +1,138 @@
1
+ "use strict"
2
+ exports.id=301,exports.ids=[301],exports.modules={27301:function(t,exports,e){var r=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}}
3
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.InteractiveShell=void 0
4
+ const s=r(e(23785)),a=e(35317),i=e(83807),o=r(e(16928)),n=r(e(79896)),l=r(e(96895)),c=e(21992),u=e(56997),h=e(47499),d=e(53189),f=e(53603)
5
+ exports.InteractiveShell=class{constructor(t){if(this.currentBranch="main",this.currentStatus=null,this.history=[],this.statusCache={map:new Map,ts:0},this.pathExecutableCache=null,this.projectPath=t.projectPath,this.currentSymlink=o.default.join(t.projectPath,c.ALCHEMY_DIR_NAME,"current"),this.currentDir=t.projectPath,this.logger=t.logger,this.historyFile=o.default.join(t.projectPath,c.ALCHEMY_DIR_NAME,"shell-history.txt"),this.projectId=t.projectId,this.remoteServerUrl=t.remoteServerUrl,this.authToken=t.authToken,t.remoteServerUrl){const e=(0,h.createMetricsCollector)(!1)
6
+ this.apiClient=new u.BaseApiClient(t.remoteServerUrl,t.logger,e,t.authToken)}}async publishPr(t,e){if(!t||!/^req\d{6}$/.test(t))return void console.log(l.default.red("Usage: publish req###### [base-branch]"))
7
+ if(!this.projectId||!this.remoteServerUrl)return void console.log(l.default.red("Project is not registered with the remote yet."))
8
+ const r=this.projectId,s={"X-Client-Type":"alchemy-local-service"}
9
+ this.authToken&&(s.Authorization=`Bearer ${this.authToken}`)
10
+ try{console.log(l.default.gray(`Publishing ${t} as a GitHub PR (uses your local gh auth; no token is stored)...`))
11
+ const a=await(0,d.publishRequestPr)({projectPath:this.projectPath,requestLocalId:t,base:e},{run:(0,d.defaultRunner)(),fetchRequest:async t=>{const e=await(this.apiClient?.get(`/api/projects/${r}/requests/${t}`))
12
+ return e?.data??e??null},fetchReceiptMarkdown:async t=>{const e=await fetch(`${this.remoteServerUrl}/api/projects/${r}/requests/${t}/receipt?format=md`,{headers:s})
13
+ if(!e.ok)throw new Error(`Receipt fetch failed: HTTP ${e.status}`)
14
+ return await e.text()}})
15
+ console.log(l.default.green(`${a.created?"PR created":"Receipt added to the existing PR"}: ${a.prUrl}`)),a.statusPosted&&console.log(l.default.gray("Commit status alchemylab/gates set on the head commit."))}catch(t){console.log(l.default.red(t instanceof Error?t.message:String(t)))}}async linearImport(t){if(t&&/^[A-Za-z][A-Za-z0-9]{1,9}-\d{1,6}$/.test(t))try{const e=(0,f.getActionServerBaseUrl)(),r=await fetch(`${e}/api/connections/linear/import`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({issueKey:t.toUpperCase()})}),s=await r.json()
16
+ s.success&&s.data?.requestId?console.log(l.default.green(`Imported ${s.data.issue} as ${s.data.requestId}.`)):console.log(l.default.red(s.error||"Import failed."))}catch(t){console.log(l.default.red(t instanceof Error?t.message:String(t)))}else console.log(l.default.red("Usage: linear <issue key> e.g. linear ENG-123"))}async start(t){return this.shutdownCallback=t,this.currentBranch="main",this.updateCurrentSymlink(),this.loadHistory(),this.rl=s.default.createInterface({input:process.stdin,output:process.stdout,prompt:this.buildPrompt(),historySize:500,removeHistoryDuplicates:!0,completer:t=>this.completer(t)}),this.history.length>0&&(this.rl.history=[...this.history].reverse()),this.printHelp(!0),this.rl.prompt(),new Promise((t=>{this.rl.on("line",(async e=>{const r=e.trim()
17
+ if(r){if(this.appendHistory(r),await this.processCommand(r))return this.rl.close(),void t()
18
+ this.rl.prompt()}else this.rl.prompt()}))
19
+ let e,r=!1
20
+ this.rl.on("SIGINT",(()=>{r?(e&&clearTimeout(e),this.rl.close()):(r=!0,console.log(""),console.log(l.default.gray("Press Ctrl+C again to exit, or continue typing.")),this.rl.prompt(),e=setTimeout((()=>{r=!1}),2e3))})),this.rl.on("close",(async()=>{this.shutdownCallback&&await this.shutdownCallback(),t()}))}))}buildPrompt(){const t=l.default.green(this.currentBranch),e=this.currentStatus&&"main"!==this.currentBranch?` ${l.default.gray("|")} ${this.colorStatus(this.currentStatus)}`:""
21
+ return`${l.default.cyan("[")}${t}${e}${l.default.cyan("]")} ${l.default.yellow("alchemy")}${l.default.gray(">")} `}colorStatus(t){switch(t){case"completed":return l.default.green(t)
22
+ case"in_progress":return l.default.yellow(t)
23
+ case"open":return l.default.blue(t)
24
+ case"ready_for_review":return l.default.magenta("review")
25
+ case"canceled":default:return l.default.gray(t)
26
+ case"reverted":return l.default.red(t)}}async fetchRequestStatus(t){if(!this.apiClient||!this.projectId)return null
27
+ try{const e=await this.apiClient.get(`/api/projects/${this.projectId}/requests/${t}`),r=e?.data||e
28
+ return r?.status||null}catch{return null}}async fetchBulkStatuses(){if(Date.now()-this.statusCache.ts<1e4&&this.statusCache.map.size>0)return this.statusCache.map
29
+ if(!this.apiClient||!this.projectId)return new Map
30
+ try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/requests?limit=100&sortBy=updated&sortDirection=desc`),e=t?.data||t||[],r=new Map
31
+ for(const t of Array.isArray(e)?e:[])t.localId&&t.status&&r.set(t.localId,t.status)
32
+ return this.statusCache={map:r,ts:Date.now()},r}catch{return this.statusCache.map}}formatRequestWithStatus(t,e,r){return`${r?l.default.green(t):t}${e?` ${this.colorStatus(e)}`:""}`}updatePrompt(){this.rl.setPrompt(this.buildPrompt())}getPathExecutables(){if(this.pathExecutableCache)return this.pathExecutableCache
33
+ const t=new Set,e=(process.env.PATH||"").split(o.default.delimiter)
34
+ for(const r of e)try{if(!n.default.existsSync(r))continue
35
+ for(const e of n.default.readdirSync(r))t.add(e)}catch{}return this.pathExecutableCache=Array.from(t).sort(),this.pathExecutableCache}completer(t){const e=["br","branch","logs","pwd","cd ","publish ","linear ","help","exit","quit"],r=t.includes(" "),s=t.indexOf(" "),a=r?t.slice(0,s).toLowerCase():""
36
+ if(r&&("br"===a||"branch"===a))return setImmediate((()=>this.rl.write("\n"))),[[],""]
37
+ if(!r){const r=e.filter((e=>e.startsWith(t))),s=t?this.getPathExecutables().filter((e=>e.startsWith(t))):[],a=[...new Set([...r,...s])]
38
+ if(a.length>0)return[a,t]
39
+ if(!t)return[e,t]}const i=t.lastIndexOf(" "),l=i>=0?t.slice(i+1):t,c=i>=0?t.slice(0,i+1):""
40
+ try{const e=l.includes(o.default.sep)||l.includes("/"),r=e?o.default.resolve(this.currentDir,o.default.dirname(l)):this.currentDir,s=e?o.default.basename(l):l,a=e?o.default.dirname(l)+"/":""
41
+ if(!n.default.existsSync(r))return[[],t]
42
+ const i=n.default.readdirSync(r,{withFileTypes:!0}).filter((t=>t.name.startsWith(s)&&!t.name.startsWith("."))).map((t=>{const e=t.isDirectory()?t.name+"/":t.name
43
+ return c+a+e}))
44
+ if(i.length>0)return[i,t]}catch{}return[[],t]}async detectCurrentBranch(){try{const t=(0,i.simpleGit)(this.currentDir),e=(await t.branch()).current||"main"
45
+ this.currentBranch=this.extractRequestId(e)||e}catch{this.currentBranch="main"}}async processCommand(t){const e=t.split(/\s+/),r=e[0].toLowerCase(),s=e.slice(1)
46
+ switch(r){case"exit":case"quit":return this.shutdownCallback&&await this.shutdownCallback(),!0
47
+ case"help":return this.printHelp(!1),!1
48
+ case"br":case"branch":return await this.listBranches(s.length>0?s.join(" "):void 0),!1
49
+ case"logs":return await this.tailLogs(s),!1
50
+ case"pwd":return console.log(this.getVirtualCwd()),!1
51
+ case"cd":return this.changeDirectory(s[0]||"~"),!1
52
+ case"publish":return await this.publishPr(s[0],s[1]),!1
53
+ case"linear":return await this.linearImport(s[0]),!1
54
+ default:return this.executeCommand(t),!1}}changeDirectory(t){try{let e
55
+ if(e="~"===t||""===t?this.currentSymlink:o.default.isAbsolute(t)?t:o.default.resolve(this.currentDir,t),!n.default.existsSync(e)||!n.default.statSync(e).isDirectory())return void console.log(l.default.red(`cd: no such directory: ${t}`))
56
+ this.currentDir=e}catch(t){console.log(l.default.red(`cd: ${t.message}`))}}async tailLogs(t){const r=o.default.join(this.projectPath,c.ALCHEMY_DIR_NAME,"server.log")
57
+ if(!n.default.existsSync(r))return void console.log(l.default.gray("No log file found yet. Logs will appear once the server processes requests."))
58
+ const s=t[0]&&/^\d+$/.test(t[0])?parseInt(t[0],10):50
59
+ console.log(l.default.gray(`Tailing ${r} (last ${s} lines, Ctrl+C to stop)`))
60
+ const{spawn:a}=await Promise.resolve().then(e.t.bind(e,35317,23)),i=a("tail",["-n",String(s),"-f",r],{stdio:["ignore","inherit","inherit"]})
61
+ return new Promise((t=>{const e=process.stdin,r=e.isRaw
62
+ this.rl.pause(),e.setRawMode(!0),e.resume()
63
+ const s=()=>{e.removeListener("data",a)
64
+ try{i.kill("SIGTERM")}catch{}try{e.setRawMode(r??!1)}catch{}this.rl.resume()},a=t=>{3!==t[0]&&"q"!==t.toString()||i.kill("SIGTERM")}
65
+ e.on("data",a),i.on("close",(()=>{s(),t()}))}))}updateCurrentSymlink(){try{let t
66
+ if("main"===this.currentBranch)t=this.projectPath
67
+ else{const e=o.default.join(this.projectPath,c.ALCHEMY_DIR_NAME,"worktrees",this.currentBranch)
68
+ t=n.default.existsSync(e)?e:this.projectPath}try{n.default.unlinkSync(this.currentSymlink)}catch{}n.default.symlinkSync(t,this.currentSymlink),this.currentDir=this.currentSymlink}catch(t){this.logger.warn("Failed to update current symlink",{error:t.message})}}getVirtualCwd(){return this.currentDir}executeCommand(t){try{(0,a.execSync)(t,{cwd:this.currentDir,stdio:"inherit",env:{...process.env,PWD:this.currentDir,GIT_DIR:void 0,GIT_WORK_TREE:void 0}})}catch(t){void 0!==t.status&&null!==t.status||console.log(l.default.red(`Error: ${t.message}`))}}getWorktreeRequestIds(){const t=o.default.join(this.projectPath,c.ALCHEMY_DIR_NAME,"worktrees"),e=new Set
69
+ if(n.default.existsSync(t))for(const r of n.default.readdirSync(t,{withFileTypes:!0}))r.isDirectory()&&e.add(r.name)
70
+ return e}extractRequestId(t){const e=t.match(/^(?:requests\/)?(req\d+)/)
71
+ return e?e[1]:null}async listBranches(t){try{const e=(0,i.simpleGit)(this.projectPath),r=await e.branch(),s=this.getWorktreeRequestIds(),a=await this.fetchBulkStatuses(),o=new Set,n=[],c=[]
72
+ for(const t of r.all){if(t.startsWith("remotes/"))continue
73
+ if("main"===t)continue
74
+ const e=this.extractRequestId(t)
75
+ e&&!o.has(e)&&(o.add(e),n.push(e))}for(const t of s)o.has(t)||(o.add(t),n.push(t))
76
+ n.sort(((t,e)=>t.localeCompare(e)))
77
+ for(const t of n){const e=this.currentBranch===t||this.currentBranch===`requests/${t}`
78
+ c.push(this.formatRequestWithStatus(t,a.get(t),e))}if(n.push("main"),c.push("main"===this.currentBranch?l.default.green("main"):"main"),0===n.length)return void console.log(l.default.gray("No branches found."))
79
+ if(t&&n.includes(t))return void(t!==this.currentBranch&&await this.switchBranch(t))
80
+ let u=n.indexOf(this.currentBranch)
81
+ u<0&&(u=n.length-1)
82
+ const h=await this.selectFromList(n,"Select branch:",c,t?0:u,!0,t)
83
+ this.rl.pause(),h&&h!==this.currentBranch?await this.switchBranch(h):h||console.log(l.default.gray("Cancelled.")),this.rl.resume()}catch(t){console.log(l.default.red(`Failed to list branches: ${t.message}`))}}async ensureWorktree(t,e){const r=o.default.join(this.projectPath,c.ALCHEMY_DIR_NAME,"worktrees"),s=o.default.join(r,t)
84
+ if(n.default.existsSync(s)&&n.default.statSync(s).isDirectory())return s
85
+ console.log(l.default.gray(`Creating worktree for ${t}...`)),n.default.mkdirSync(r,{recursive:!0})
86
+ const a=(0,i.simpleGit)(this.projectPath)
87
+ return await a.raw(["worktree","add",s,e]),console.log(l.default.gray(`Worktree created at ${s}`)),s}async selectFromList(t,e,r,s,a,i){const o=t,n=r||t
88
+ return new Promise((t=>{let r=0
89
+ const c=process.stdin,u=c.isRaw
90
+ let h=!1,d=i||"",f=o.map(((t,e)=>e))
91
+ null!=s&&s>=0&&s<o.length&&(r=s)
92
+ const p=()=>{if(!d)return void(f=o.map(((t,e)=>e)))
93
+ const t=d.toLowerCase()
94
+ let e=o.map(((t,e)=>({item:t,i:e}))).filter((({item:e})=>e.toLowerCase().startsWith(t)))
95
+ 0===e.length&&/^\d+$/.test(t)&&(e=o.map(((t,e)=>({item:t,i:e}))).filter((({item:e})=>e.replace(/\D/g,"").endsWith(t)))),0===e.length&&(e=o.map(((t,e)=>({item:t,i:e}))).filter((({item:e})=>e.toLowerCase().includes(t)))),f=e.map((t=>t.i))},g=process.stdout.rows||24,y=Math.max(3,g-4)
96
+ let m=0,w=0,$=0
97
+ this.rl.pause()
98
+ const S=()=>{const t=f.length,s=t>y
99
+ s?r<m?m=r:r>=m+y&&(m=r-y+1):m=0
100
+ const i=(()=>{const t=f.length
101
+ if(0===t)return 1
102
+ const e=t>y
103
+ let r=e?Math.min(y,t-m):t
104
+ return e&&m>0&&r++,e&&m+y<t&&r++,r})(),o=i+1
105
+ C&&$>1&&process.stdout.write(`\r\x1b[${$-1}A\r`),o>w&&(w=o),$=w
106
+ const c=w-o
107
+ for(let t=0;t<c;t++)process.stdout.write("\x1b[2K\n")
108
+ if(0===t)process.stdout.write(`\x1b[2K ${l.default.gray("no matches")}\n`)
109
+ else{const e=s&&m>0,a=s&&m+y<t,i=s?Math.min(m+y,t):t,o=s?m:0
110
+ e&&process.stdout.write(`\x1b[2K ${l.default.gray(`\u25b2 ${m} more`)}\n`)
111
+ for(let t=o;t<i;t++){const e=f[t],s=t===r?l.default.cyan("\u276f "):" ",a=t===r?l.default.cyan(n[e]):n[e]
112
+ process.stdout.write(`\x1b[2K${s}${a}\n`)}a&&process.stdout.write(`\x1b[2K ${l.default.gray(`\u25bc ${t-i} more`)}\n`)}a&&d?process.stdout.write(`\x1b[2K${l.default.gray(">")} ${l.default.yellow(d)}${l.default.gray("_")}`):a?process.stdout.write(`\x1b[2K${l.default.gray(">")} ${l.default.gray("type to filter...")}`):process.stdout.write(`\x1b[2K${l.default.gray(e)}`),C=!0}
113
+ d&&(p(),r=0)
114
+ let C=!1
115
+ S(),c.setRawMode(!0),c.resume()
116
+ const b=()=>{if(!h){h=!0,c.removeListener("data",v)
117
+ try{c.setRawMode(u??!1)}catch{}C&&$>1&&process.stdout.write(`\r\x1b[${$-1}A\r`),process.stdout.write("\x1b[J"),this.rl.resume()}},v=e=>{if(!h)try{const s=e.toString()
118
+ "\x1b[A"===s?f.length>0&&(r=Math.max(0,r-1),S()):"\x1b[B"===s?f.length>0&&(r=Math.min(f.length-1,r+1),S()):"\r"===s||"\n"===s?f.length>0&&(b(),t(o[f[r]])):"\x1b"===s||"\x03"===s?(b(),t(null)):a&&"\x7f"===s?d.length>0&&(d=d.slice(0,-1),p(),r=0,m=0,S()):a&&1===s.length&&s>=" "&&s<="~"&&(d+=s,p(),r=0,m=0,S())}catch{b(),t(null)}}
119
+ c.on("data",v)}))}findMatchingBranches(t,e,r){const s=new Set
120
+ for(const t of e){const e=this.extractRequestId(t)
121
+ e&&s.add(e)}for(const t of r)s.add(t)
122
+ const a=Array.from(s)
123
+ if(s.has(t))return[t]
124
+ const i=a.filter((e=>e.startsWith(t)))
125
+ if(i.length>0)return i.sort()
126
+ if(/^\d+$/.test(t)){const e=a.filter((e=>e.replace(/\D/g,"").endsWith(t)))
127
+ if(e.length>0)return e.sort()}return[]}async switchBranch(t){try{if("main"===t)return this.currentBranch="main",this.currentStatus=null,this.updateCurrentSymlink(),this.updatePrompt(),console.log(l.default.green("Switched to main")),void console.log(l.default.gray(` ${this.currentDir}`))
128
+ const e=(0,i.simpleGit)(this.projectPath),r=await e.branch(),s=this.getWorktreeRequestIds(),a=this.findMatchingBranches(t,r.all,s)
129
+ if(0===a.length)return console.log(l.default.red(`No branch matching: ${t}`)),void console.log(l.default.gray(' Use "branches" to see available branches'))
130
+ let o
131
+ if(1===a.length)o=a[0]
132
+ else{const e=await this.fetchBulkStatuses(),r=a.map((t=>this.formatRequestWithStatus(t,e.get(t),!1))),s=await this.selectFromList(a,`Multiple matches for "${t}" \u2014 select one:`,r,void 0,!0)
133
+ if(!s)return void console.log(l.default.gray("Cancelled."))
134
+ o=s}const n=r.all.includes(`requests/${o}`)?`requests/${o}`:o
135
+ await this.ensureWorktree(o,n),this.currentBranch=o,this.updateCurrentSymlink(),this.currentStatus=await this.fetchRequestStatus(o),this.updatePrompt()
136
+ const c=this.currentStatus?` ${l.default.gray("|")} ${this.colorStatus(this.currentStatus)}`:""
137
+ console.log(l.default.green(`Switched to ${o}`)+c),console.log(l.default.gray(` ${this.getVirtualCwd()}`))}catch(t){console.log(l.default.red(`Failed to switch: ${t.message}`))}}printHelp(t){t?(console.log(""),console.log(l.default.gray('Type "help" for available commands. Commands run in the current branch directory.')),console.log("")):(console.log(""),console.log(l.default.bold("Interactive Shell Commands:")),console.log(` ${l.default.yellow("branch")} / ${l.default.yellow("br")} Browse and switch branches`),console.log(` ${l.default.yellow("logs")} ${l.default.gray("[N]")} Tail action server log (Ctrl+C to stop, N = lines of history)`),console.log(` ${l.default.yellow("cd")} ${l.default.gray("<dir>")} Change directory within current branch`),console.log(` ${l.default.yellow("pwd")} Show current working directory`),console.log(` ${l.default.yellow("publish")} ${l.default.gray("<req id>")} Push the request branch and open a GitHub PR with its receipt`),console.log(` ${l.default.yellow("linear")} ${l.default.gray("<ENG-123>")} Import a Linear issue as a request`),console.log(` ${l.default.yellow("help")} Show this help`),console.log(` ${l.default.yellow("exit")} / ${l.default.yellow("quit")} Stop the server and exit`),console.log(""),console.log(l.default.gray(" Any other input is executed as a shell command in the current directory.")),console.log(""))}loadHistory(){try{if(n.default.existsSync(this.historyFile)){const t=n.default.readFileSync(this.historyFile,"utf-8")
138
+ this.history=t.split("\n").filter((t=>""!==t.trim())),this.history.length>500&&(this.history=this.history.slice(-500))}}catch{}}appendHistory(t){try{this.history.push(t),n.default.appendFileSync(this.historyFile,t+"\n")}catch{}}}}}
package/dist/380.js ADDED
@@ -0,0 +1,17 @@
1
+ "use strict"
2
+ exports.id=380,exports.ids=[380],exports.modules={94380:function(e,exports,t){function r(e){return i.default.createHash("md5").update(e).digest("hex")}async function s(e){try{return r(await o.default.promises.readFile(e))}catch{return null}}var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
3
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.syncPluginsGlobal=async function(e,t,n){const i={synced:[],skipped:[],errors:[]},l=n||function(){const e=process.argv[1]
4
+ let t=e
5
+ try{t=o.default.realpathSync(e)}catch{}return a.default.dirname(t)}(),d=a.default.join((0,u.getGlobalConfigDir)(e),"plugin")
6
+ try{await o.default.promises.mkdir(d,{recursive:!0})}catch(e){return t.error("Failed to create global plugin directory",{destDir:d,error:e instanceof Error?e.message:"Unknown error"}),i}const f=a.default.join(d,"package.json"),p='{"type":"module"}\n'
7
+ try{await o.default.promises.readFile(f,"utf8").catch((()=>null))!==p&&await o.default.promises.writeFile(f,p,"utf8")}catch(e){t.warn("Failed to write global plugin package.json",{error:e instanceof Error?e.message:"Unknown error"})}for(const e of c){const n=a.default.join(l,e.sourceName),u=a.default.join(d,e.destName)
8
+ try{if(!o.default.existsSync(n)){t.warn(`Plugin source not found: ${e.sourceName}`,{sourcePath:n}),i.errors.push(e.destName)
9
+ continue}const a=await o.default.promises.readFile(n,"utf8").catch((()=>null))
10
+ if(null===a){t.warn(`Could not read plugin source: ${e.sourceName}`),i.errors.push(e.destName)
11
+ continue}const c=r(a),l=await s(u)
12
+ null===l||c!==l?(await o.default.promises.writeFile(u,a,"utf8"),t.info(`Synced plugin: ${e.destName}`,{destDir:d,reason:null===l?"not installed":"updated"}),i.synced.push(e.destName)):i.skipped.push(e.destName)}catch(r){t.error(`Failed to sync plugin: ${e.destName}`,{error:r instanceof Error?r.message:"Unknown error"}),i.errors.push(e.destName)}}return i.synced.length>0&&t.info("Synced global plugins",{destDir:d,synced:i.synced}),i},exports.cleanupLegacyProjectPlugins=async function(e,t){const r=a.default.join(e,".opencode","plugin"),s=[]
13
+ if(!o.default.existsSync(r))return{removed:s}
14
+ for(const e of l){const t=a.default.join(r,e)
15
+ if(o.default.existsSync(t))try{await o.default.promises.rm(t,{recursive:!0,force:!0}),s.push(e)}catch{}}s.length>0&&t.info("Removed legacy per-project plugin copies",{projectPluginDir:r,removed:s})
16
+ try{0===(await o.default.promises.readdir(r)).length&&await o.default.promises.rmdir(r)}catch{}return{removed:s}}
17
+ const o=n(t(79896)),a=n(t(16928)),i=n(t(76982)),u=t(41306),c=[{sourceName:"plugins/hooks/plugin.js",destName:"event-hooks.js"},{sourceName:"plugins/inject-session.js",destName:"inject-session.js"},{sourceName:"plugins/system-logger.js",destName:"system-logger.js"}],l=[...c.map((e=>e.destName)),"package.json","lib"]}}
package/dist/403.js ADDED
@@ -0,0 +1,4 @@
1
+ "use strict"
2
+ exports.id=403,exports.ids=[403],exports.modules={15403:(t,exports)=>{exports.formatCombinedOutput=function(t,e){const n=e.maxOut??o,u=t.length>n
3
+ return{output:u?"\u2026"+t.slice(-n):t,truncatedNote:u?`\n_(output truncated: view full with \`log:${e.invocationId}\`)_`:"",logFooter:`Log: \`log:${e.invocationId}\``}}
4
+ const o=3e3}}
package/dist/491.js ADDED
@@ -0,0 +1,31 @@
1
+ "use strict"
2
+ exports.id=491,exports.ids=[491],exports.modules={23491:function(e,exports,t){function s(e,t="",n=0,r=[]){if(n>4||r.length>=i)return r
3
+ let c=[]
4
+ try{c=a.default.readdirSync(l.default.join(e,t),{withFileTypes:!0})}catch{return r}for(const f of c.sort(((e,t)=>e.name.localeCompare(t.name)))){if(f.name.startsWith(".")||o.has(f.name))continue
5
+ const c=t?`${t}/${f.name}`:f.name
6
+ if(f.isDirectory())s(e,c,n+1,r)
7
+ else if(f.isFile())try{r.push({rel:c,size:a.default.statSync(l.default.join(e,c)).size})}catch{}if(r.length>=i)break}return r}function n(e,t){let s=""
8
+ try{s=a.default.readFileSync(e,"utf8")}catch{return[]}if(s.length>4e5)return[]
9
+ const n=[],r=e=>{e?.[1]&&!n.includes(e[1])&&n.push(e[1])},l=s.split("\n")
10
+ for(const e of l)if(".py"!==t){if(r(e.match(/^\s{0,2}(?:export\s+)?(?:async\s+)?function\s*\*?\s*([A-Za-z_$][\w$]*)/)),r(e.match(/^\s{0,2}(?:export\s+)?class\s+([A-Za-z_$][\w$]*)/)),r(e.match(/^\s{0,2}(?:export\s+)?(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:async\s*)?(?:\([^)]*\)|[A-Za-z_$][\w$]*)\s*=>/)),n.length>=30)break}else r(e.match(/^(?:async\s+)?def\s+([A-Za-z_]\w*)/)),r(e.match(/^class\s+([A-Za-z_]\w*)/))
11
+ return n}var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
12
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.generateProjectMap=function(e){if(!e||!a.default.existsSync(e))return null
13
+ const t=s(e)
14
+ if(0===t.length)return null
15
+ const r=[]
16
+ r.push("Files (path, size):")
17
+ for(const e of t)r.push(` ${e.rel} (${e.size>=1024?`${Math.round(e.size/1024)} KB`:`${e.size} B`})`)
18
+ const o=t.filter((e=>c.has(l.default.extname(e.rel))))
19
+ for(const t of o.slice(0,12)){const s=n(l.default.join(e,t.rel),l.default.extname(t.rel))
20
+ s.length&&r.push(`${t.rel}: ${s.join(", ")}`)}const i=function(e,t){const s=new Set
21
+ for(const n of t){if(!/\.(m?js|cjs|ts|tsx|jsx)$/.test(n.rel))continue
22
+ let t=""
23
+ try{t=a.default.readFileSync(l.default.join(e,n.rel),"utf8")}catch{continue}for(const e of t.matchAll(/localStorage\.(?:getItem|setItem)\(\s*['"`]([^'"`]{1,80})['"`]/g))s.add(e[1])
24
+ for(const e of t.matchAll(/(?:NYCKEL|KEY|STORAGE)[A-Z_]*\s*=\s*['"`]([^'"`]{1,80})['"`]/g))s.add(e[1])
25
+ if(s.size>20)break}return[...s]}(e,t)
26
+ i.length&&r.push(`Storage keys: ${i.join(", ")}`)
27
+ const u=t.find((e=>/^readme\.md$/i.test(e.rel)))
28
+ if(u)try{const t=a.default.readFileSync(l.default.join(e,u.rel),"utf8").split("\n").filter((e=>/^#{1,3}\s/.test(e))).slice(0,12).map((e=>e.replace(/^#+\s*/,"").trim()))
29
+ t.length&&r.push(`README sections: ${t.join(" | ")}`)}catch{}let h=r.join("\n")
30
+ return h.length>f&&(h=h.slice(0,f)+"\n[... map clipped]"),`Project map (generated from the worktree at pickup; read this, then open only the files you will change):\n${h}`}
31
+ const a=r(t(79896)),l=r(t(16928)),o=new Set(["node_modules",".git",".alchemy",".alchemy_lab","dist","build","coverage",".next","vendor","__pycache__"]),c=new Set([".js",".mjs",".cjs",".ts",".tsx",".jsx",".py",".go",".rs",".rb",".java",".kt",".swift"]),i=80,f=3500}}