@jwcode/cli 3.0.0
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 +348 -0
- package/backend/.gitkeep +0 -0
- package/dist/__tests__/store.test.d.ts +1 -0
- package/dist/__tests__/store.test.js +43 -0
- package/dist/__tests__/theme.test.d.ts +1 -0
- package/dist/__tests__/theme.test.js +40 -0
- package/dist/__tests__/tokenEstimate.test.d.ts +1 -0
- package/dist/__tests__/tokenEstimate.test.js +48 -0
- package/dist/cli.js +83 -0
- package/dist/commands/index.d.ts +18 -0
- package/dist/commands/index.js +99 -0
- package/dist/components/ApprovalModal.d.ts +8 -0
- package/dist/components/ApprovalModal.js +47 -0
- package/dist/components/ChatArea.d.ts +10 -0
- package/dist/components/ChatArea.js +49 -0
- package/dist/components/CommandPalette.d.ts +6 -0
- package/dist/components/CommandPalette.js +72 -0
- package/dist/components/StatusLine.d.ts +1 -0
- package/dist/components/StatusLine.js +25 -0
- package/dist/components/TextInput.d.ts +10 -0
- package/dist/components/TextInput.js +118 -0
- package/dist/hooks/useAppState.d.ts +18 -0
- package/dist/hooks/useAppState.js +42 -0
- package/dist/hooks/useWebSocket.d.ts +3 -0
- package/dist/hooks/useWebSocket.js +8 -0
- package/package.json +43 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var dn=Object.defineProperty;var c=(t,e)=>dn(t,"name",{value:e,configurable:!0}),No=(t=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(t,{get:(e,n)=>(typeof require<"u"?require:e)[n]}):t)(function(t){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+t+'" is not supported')});var un=(t,e)=>()=>(t&&(e=t(t=0)),e);var pn=(t,e)=>{for(var n in e)dn(t,n,{get:e[n],enumerable:!0})};var zt={};pn(zt,{buildBackend:()=>yt,cleanupBackend:()=>ys,clearDaemonInfo:()=>po,findAvailablePorts:()=>qt,findInstallDir:()=>Gt,findJar:()=>ke,findJava:()=>Kt,findMvn:()=>co,findRunningDaemon:()=>Yt,isDaemonAlive:()=>fo,killPort:()=>at,portInUse:()=>$e,readDaemonInfo:()=>uo,startBackend:()=>Ss,startDaemon:()=>Xt,waitForBackend:()=>Vt,writeDaemonInfo:()=>kt});import{spawn as io,spawnSync as xs,execSync as Ee}from"node:child_process";import{existsSync as ge,readdirSync as Jt,statSync as ws}from"node:fs";import{join as ne,dirname as Ut}from"node:path";import{homedir as Ft}from"node:os";import{fileURLToPath as Ts}from"node:url";import{mkdirSync as ks,writeFileSync as Cs,readFileSync as Ms,unlinkSync as vs}from"node:fs";function Gt(){let t=ne(jt,"..","backend","jwcode-web.jar");if(ge(t))return ne(jt,"..");let e=ne(jt,"..","..");for(;e!==Ut(e);){if(ge(ne(e,"pom.xml")))return e;e=Ut(e)}return process.cwd()}function ke(t){let e=ne(t,"backend","jwcode-web.jar");if(ge(e))return e;let n=ne(t,"jwcode-web","target","jwcode-web.jar");if(ge(n))return n;let o=ne(t,"jwcode-web","target");if(ge(o))try{let s=Jt(o).filter(i=>i.startsWith("jwcode-web")&&i.endsWith(".jar")).map(i=>({name:i,mtime:ws(ne(o,i)).mtimeMs})).sort((i,d)=>d.mtime-i.mtime);if(s.length>0)return ne(o,s[0].name)}catch{}return null}function co(){let t=process.env.PATH?.split(";")||[];for(let e of t)for(let n of["mvn.cmd","mvn.bat","mvn"]){let o=ne(e,n);if(ge(o))return o}for(let e of["C:\\Program Files",Ft()])try{for(let n of Jt(e,{withFileTypes:!0}))if(n.isDirectory()&&n.name.startsWith("apache-maven")){let o=ne(e,n.name,"bin","mvn.cmd");if(ge(o))return o}}catch{}return"mvn"}function Kt(t){if(t){let n=ne(t,"backend","jre","bin",process.platform==="win32"?"java.exe":"java");if(ge(n))return n}let e=process.env.PATH?.split(";")||[];for(let n of e)for(let o of["java.exe","java"]){let s=ne(n,o);if(ge(s))return s}for(let n of["C:\\Program Files\\Java","C:\\Program Files (x86)\\Java",Ft()])try{for(let o of Jt(n,{withFileTypes:!0}))if(o.isDirectory()&&(o.name.startsWith("jdk")||o.name.startsWith("openjdk"))){let s=ne(n,o.name,"bin","java.exe");if(ge(s))return s}}catch{}return"java"}function yt(t){let n=`"${co()}" package -pl jwcode-web -am -q -DskipTests`;console.log(`[launcher] Building: ${n}`);try{let o=xs(n,[],{cwd:t,stdio:"pipe",shell:!0,windowsHide:!0});if(o.status!==0)throw new Error(o.stderr.toString()||o.stdout.toString())}catch(o){console.error("[launcher] Build failed:",String(o)),process.exit(1)}ke(t)||(console.error("[launcher] Build succeeded but jar not found"),process.exit(1))}function $e(t){try{return process.platform==="win32"?Ee(`netstat -ano | findstr :${t} | findstr LISTENING`,{encoding:"utf-8"}).trim().length>0:(Ee(`lsof -ti:${t}`,{stdio:"ignore"}),!0)}catch{return!1}}function qt(t=8080,e=20){for(let n=t;n<t+e;n++){if(!$e(n)&&!$e(n+1))return{httpPort:n,wsPort:n+1};$e(n+1)&&n++}throw new Error(`No available port pair found in range ${t}-${t+e}`)}function at(t){try{if(process.platform==="win32"){let e=Ee(`netstat -ano | findstr :${t} | findstr LISTENING`,{encoding:"utf-8"});for(let n of e.trim().split(`
|
|
3
|
+
`)){let o=n.trim().split(/\s+/).pop();if(o)try{Ee(`taskkill /F /PID ${o}`,{stdio:"ignore"}),console.log(`[launcher] Killed process on port ${t} (PID ${o})`)}catch{}}}else Ee(`lsof -ti:${t} | xargs kill -9 2>/dev/null`,{stdio:"ignore"})}catch{}}function Vt(t,e=60){let n=Date.now(),o=`http://localhost:${t}/api/system/status`;return new Promise(s=>{function i(){if(Date.now()-n>e*1e3){console.log(`[launcher] WARNING: Backend not responding after ${e}s`),s();return}fetch(o,{signal:AbortSignal.timeout(2e3)}).then(async d=>{let f=await d.text();d.status===200&&(f.includes("running")||f.includes("status"))?(console.log(`[launcher] Backend ready on port ${t}`),s()):setTimeout(i,1e3)}).catch(()=>setTimeout(i,1e3))}c(i,"check"),i()})}function Ss(t){let{installDir:e,workspaceDir:n,port:o,wsPort:s,forceKill:i}=t,d=Kt(e),f=ke(e);f||(console.error("[launcher] Backend JAR not found. Run with --build first, or ensure backend/jwcode-web.jar exists."),process.exit(1)),i&&(at(o),at(s)),console.log(`[launcher] Starting backend: ${d} -jar ${f}`),console.log(`[launcher] Workspace: ${n}`);let p=["-jar",f,String(o),String(s),n],m=io(d,p,{cwd:n,env:{...process.env,JWCODE_WS_PORT:String(s)},stdio:["ignore","pipe","pipe"],windowsHide:!0});return m.stdout?.on("data",()=>{}),m.stderr?.on("data",b=>{let h=b.toString("utf-8").trim();h&&(h.includes("Address already in use")||h.includes("BindException"))&&console.error(`[backend] ERROR: Port ${o} is in use.`)}),m.on("exit",b=>{b!==0&&b!==null&&console.error(`[launcher] Backend process exited with code ${b}`)}),m}function ys(t){if(t)if(console.log(`
|
|
4
|
+
[jwcode] Shutting down...`),process.platform==="win32")try{Ee(`taskkill /F /T /PID ${t.pid}`,{stdio:"ignore"})}catch{try{t.kill()}catch{}}else{try{process.kill(-t.pid,"SIGTERM")}catch{}t.kill("SIGTERM"),setTimeout(()=>{if(t&&!t.killed){try{process.kill(-t.pid,"SIGKILL")}catch{}t.kill("SIGKILL")}},5e3)}}function kt(t,e,n,o){try{ks(lo,{recursive:!0})}catch{}let s={pid:t,httpPort:e,wsPort:n,workspaceDir:o,startedAt:new Date().toISOString(),lastActivity:new Date().toISOString()};Cs(St,JSON.stringify(s,null,2),"utf-8")}function uo(){try{return ge(St)?JSON.parse(Ms(St,"utf-8")):null}catch{return null}}function po(){try{vs(St)}catch{}}function fo(t){try{return process.platform==="win32"?Ee(`tasklist /FI "PID eq ${t.pid}" /NH`,{encoding:"utf-8",timeout:3e3}).includes(String(t.pid)):(Ee(`kill -0 ${t.pid}`,{stdio:"ignore"}),!0)}catch{return!1}}function Xt(t){let{installDir:e,workspaceDir:n,port:o,wsPort:s,forceKill:i}=t,d=Kt(e),f=ke(e);f||(console.error("[launcher] Backend JAR not found. Run: npm install -g @jwcode/cli"),process.exit(1)),i&&(at(o),at(s));let p=["-jar",f,String(o),String(s),n],m=t.idleTimeout??300,b=io(d,p,{cwd:n,env:{...process.env,JWCODE_WS_PORT:String(s),JWCODE_DAEMON_IDLE_TIMEOUT:String(m)},stdio:"ignore",detached:!0,windowsHide:!0});return b.unref(),kt(b.pid,o,s,n),console.log(`[daemon] Started JWCode daemon (PID ${b.pid}) on ports ${o}/${s}`),b}function Yt(t){let e=uo();return e?fo(e)?t&&e.workspaceDir!==t?null:e:(po(),null):null}var bs,jt,lo,St,Ct=un(()=>{"use strict";bs=Ts(import.meta.url),jt=Ut(bs);c(Gt,"findInstallDir");c(ke,"findJar");c(co,"findMvn");c(Kt,"findJava");c(yt,"buildBackend");c($e,"portInUse");c(qt,"findAvailablePorts");c(at,"killPort");c(Vt,"waitForBackend");c(Ss,"startBackend");c(ys,"cleanupBackend");lo=ne(Ft(),".jwcode"),St=ne(lo,"daemon.json");c(kt,"writeDaemonInfo");c(uo,"readDaemonInfo");c(po,"clearDaemonInfo");c(fo,"isDaemonAlive");c(Xt,"startDaemon");c(Yt,"findRunningDaemon")});var Zt={};pn(Zt,{checkDaemonHealth:()=>Us,checkForUpdates:()=>$s,runNpmUpdate:()=>js});import{execSync as Es}from"node:child_process";import{homedir as ho}from"node:os";import{join as xo}from"node:path";import{existsSync as Ps,readFileSync as Is,writeFileSync as Os,mkdirSync as Ls}from"node:fs";function Hs(){try{return Ps(Qt)?JSON.parse(Is(Qt,"utf-8")):null}catch{return null}}function Ws(t){try{Ls(xo(ho(),".jwcode"),{recursive:!0}),Os(Qt,JSON.stringify({lastCheck:Date.now(),latestVersion:t}))}catch{}}async function $s(t){let e=Hs();if(e&&Date.now()-e.lastCheck<Ns)return{latest:e.latestVersion,current:t,url:"",body:"",publishedAt:"",updateAvailable:e.latestVersion!==t};try{let n=await fetch(Bs,{headers:{Accept:"application/vnd.github+json","User-Agent":"JWCode-CLI"}});if(!n.ok)return null;let o=await n.json(),s=(o.tag_name||"").replace(/^v/,"");return Ws(s),{latest:s,current:t,url:o.html_url||"",body:o.body||"",publishedAt:o.published_at||"",updateAvailable:s!==t}}catch{return null}}function js(){try{let t=process.platform==="win32"?"npm.cmd":"npm";return{ok:!0,message:Es(`"${t}" install -g @jwcode/cli@latest`,{encoding:"utf-8",stdio:"pipe",timeout:12e4}).trim()}}catch(t){return{ok:!1,message:String(t.stderr||t.message||t)}}}async function Us(t){try{let e=await fetch(`http://localhost:${t}/api/system/status`,{signal:AbortSignal.timeout(5e3)});if(e.status===200){let n=await e.json();return n?.status==="running"||n?.success===!0}return!1}catch{return!1}}var Bs,Qt,Ns,en=un(()=>{"use strict";Bs="https://api.github.com/repos/jwcode/jwcode/releases/latest",Qt=xo(ho(),".jwcode","last_update_check.json"),Ns=360*60*1e3;c(Hs,"readCache");c(Ws,"writeCache");c($s,"checkForUpdates");c(js,"runNpmUpdate");c(Us,"checkDaemonHealth")});import{render as tn}from"ink";import{createElement as nn}from"react";import{useState as le,useEffect as ro,useRef as bt,useCallback as ee}from"react";import{Box as z,Text as se,useStdout as ps}from"ink";import{useState as qo,useRef as Ue,useEffect as lt}from"react";import{Box as _t,Text as he,useInput as Vo}from"ink";var Ho=0,Wo=new Map;function $o(t){let e=++Ho;return Wo.set(e,t),e}c($o,"storePaste");function At(t){let e=$o(t),n=(t.match(/\n/g)||[]).length+1,o=[`Pasted text #${e} +${t.length} chars`];return n>1&&o.push(`+${n} lines`),{id:e,label:`[${o.join(" ")}]`}}c(At,"pasteSummary");var mn={bg:"",text:"#ffffff",muted:"#999999",border:"#505050",brand:"#d77757",primary:"#d77757",success:"#4eba65",warning:"#ffc107",error:"#ff6b80",info:"#b1b9f9",user:"#7ab4e8",assistant:"#ffffff",system:"#ff6b80",tool:"#b1b9f9",thinking:"#999999",plan:"#48968c",auto:"#ff7814",connected:"#4eba65",disconnected:"#ff6b80",diffAdded:"#38a660",diffRemoved:"#b3596b",diffAddedBg:"#225c2b",diffRemovedBg:"#7a2936",diffHeader:"#b1b9f9",diffFileHeader:"#d77757",diffPlaceholder:"#505050"},jo={...mn,text:"#1f2328",assistant:"#1f2328",muted:"#656d76",border:"#d0d7de",primary:"#cf5a2f",diffAdded:"#116329",diffRemoved:"#cf222e",diffAddedBg:"#dafbe1",diffRemovedBg:"#ffebe9",diffHeader:"#0550ae",diffFileHeader:"#cf5a2f",diffPlaceholder:"#6e7781"},Uo={dark:mn,light:jo};function Jo(){return process.env.JWCODE_THEME==="light"?"light":"dark"}c(Jo,"detectTheme");function Fo(){try{let t=process.env.JWCODE_THEME_COLORS;if(t)return JSON.parse(t)}catch{}return null}c(Fo,"loadCustomColors");var Go=Jo(),fn=Fo();function Ko(){let t=Uo[Go];return fn?{...t,...fn}:t}c(Ko,"getTheme");var u=Ko();import{jsx as Oe,jsxs as Je}from"react/jsx-runtime";function Xo(t){let e=0,n=0;for(let o of t)/[一-鿿㐀-䶿豈-]/.test(o)?e++:n++;return Math.ceil(e/1.5+n/4)}c(Xo,"estimateTokens");function ze({value:t,onChange:e,onSubmit:n,placeholder:o,disabled:s}){let i=Ue(t.length),[,d]=qo(0),f=Ue(t),p=Ue(e),m=Ue(n),b=Ue(s);lt(()=>{f.current=t}),lt(()=>{p.current=e}),lt(()=>{m.current=n}),lt(()=>{b.current=s}),i.current>t.length&&(i.current=t.length);let h=Ue({accumulating:!1,buf:""});Vo((M,a)=>{if(b.current)return;process.stderr.write(`[TextInput] input=${JSON.stringify(M.slice(0,20))} key.bs=${a.backspace} key.del=${a.delete} key.esc=${a.escape} key.ret=${a.return} key.ctrl=${a.ctrl} key.meta=${a.meta} key.tab=${a.tab} cursor=${i.current} val="${f.current.slice(0,20)}"
|
|
5
|
+
`);let r="[200~",g="[201~";if(M.includes(r)){if(h.current.accumulating=!0,h.current.buf=M.split(r).slice(1).join(r),h.current.buf.includes(g)){let l=h.current.buf.split(g);h.current.buf=l[0]||"",h.current.accumulating=!1;let{label:T}=At(h.current.buf);p.current(f.current+T)}return}if(h.current.accumulating&&M.includes(g)){let l=M.split(g);h.current.buf+=l[0]||"",h.current.accumulating=!1;let{label:T}=At(h.current.buf);p.current(f.current+T);return}if(h.current.accumulating){h.current.buf+=M;return}if(a.return){m.current(f.current);return}if(a.leftArrow){i.current=Math.max(0,i.current-1),d(l=>l+1);return}if(a.rightArrow){i.current=Math.min(f.current.length,i.current+1),d(l=>l+1);return}if(a.backspace){if(i.current>0){let l=i.current,T=f.current;p.current(T.slice(0,l-1)+T.slice(l)),i.current=l-1,d(y=>y+1)}return}if(a.delete&&i.current<f.current.length){let l=i.current,T=f.current;p.current(T.slice(0,l)+T.slice(l+1));return}if((M==="\x7F"||M==="\b"||M==="\x7F")&&!a.ctrl&&!a.meta){if(i.current>0){let l=i.current,T=f.current;p.current(T.slice(0,l-1)+T.slice(l)),i.current=l-1,d(y=>y+1)}return}if(M&&!a.ctrl&&!a.meta&&!a.tab&&!a.escape){let l=i.current,T=f.current;p.current(T.slice(0,l)+M+T.slice(l)),i.current=l+M.length,d(y=>y+1)}});let x=t||"",S=!x&&o,C=x?Xo(x):0,k=x.length,w=Math.min(i.current,t.length);return Je(_t,{flexDirection:"column",children:[Oe(_t,{children:x?Je(he,{children:[Oe(he,{children:t.slice(0,w)}),Oe(he,{inverse:!0,children:t[w]||" "}),Oe(he,{children:t.slice(w+1)})]}):Je(he,{children:[Oe(he,{dimColor:!0,children:o}),Oe(he,{dimColor:!0,children:"\u25B6"})]})}),k>0&&Je(_t,{children:[Je(he,{dimColor:!0,children:[" ",k," chars ~ ",C," tokens"]}),Je(he,{dimColor:!0,children:[" [",w+1,"/",k,"]"]}),C>1e5&&Oe(he,{color:u.error,children:" WARN: Approaching context limit"})]})]})}c(ze,"TextInput");import Rt from"ws";var Yo=3e4,zo=3e4,gn=50;function Dt(t){try{process.stderr.write(t)}catch{}}c(Dt,"stderr");function V(t,e){try{process.stderr.write("[dbg "+t+"] "+e+`
|
|
6
|
+
`)}catch{}}c(V,"debugLog");var dt=class{static{c(this,"JwCodeClient")}ws=null;handlers=new Map;_running=!1;_reconnecting=!1;_reconnectRetries=0;pingTimer=null;reconnectDelay=1e3;reconnectTimer=null;sessionId=null;backendUrl;wsUrl;token;constructor(e,n,o="default-token"){this.backendUrl=e,this.wsUrl=n,this.token=o}on(e,n){return this.handlers.has(e)||this.handlers.set(e,new Set),this.handlers.get(e).add(n),()=>this.handlers.get(e)?.delete(n)}async connect(){if(this._running=!0,this.ws){try{this.ws.close()}catch{}this.ws=null}return new Promise((e,n)=>{let o=!1,s=c((d,f)=>{o||(o=!0,d(f))},"settle");try{this.ws=new Rt(this.wsUrl)}catch(d){s(n,d);return}let i=this.ws._socket;i&&i.on("error",()=>{}),this.ws.on("message",d=>{let f;try{f=JSON.parse(d.toString())}catch{return}if(f.type==="auth_required"){this.ws.send(JSON.stringify({type:"auth",token:this.token}));return}if(f.type==="auth_success"){this._createSession().then(p=>{this.sessionId=p,this._startHeartbeat(),this._startReadLoop(),this._reconnecting=!1,this._reconnectRetries=0,this.reconnectDelay=1e3,s(e,p)}).catch(p=>s(n,p));return}if(f.type==="auth_failed"){s(n,new Error(`Auth failed: ${JSON.stringify(f)}`));return}}),this.ws.on("error",d=>{this.sessionId?this.dispatch({type:"error",data:`WebSocket error: ${d.message}`}):s(n,d)}),this.ws.on("close",()=>{this._stopHeartbeat(),!this.sessionId&&!this._reconnecting&&s(n,new Error("Connection closed before authentication")),this._running&&!this._reconnecting&&this._startReconnect()})})}async _createSession(){let n=await(await fetch(`${this.backendUrl}/api/sessions`,{method:"POST"})).json(),s=(n.data||{}).sessionId||n.sessionId||n.id||"default-session";return Dt(`[ws] Session: ${s}
|
|
7
|
+
`),s}_startReadLoop(){this.ws&&this.ws.on("message",e=>{let n;try{n=JSON.parse(e.toString())}catch{return}n.type==="auth_required"||n.type==="auth_success"||n.type==="auth_failed"||this.dispatch(n)})}_startHeartbeat(){this._stopHeartbeat(),this.pingTimer=setInterval(()=>{if(this.ws?.readyState===Rt.OPEN)try{this.ws.send(JSON.stringify({type:"ping"}))}catch{}},Yo)}_stopHeartbeat(){this.pingTimer&&(clearInterval(this.pingTimer),this.pingTimer=null)}_startReconnect(){if(!(!this._running||this._reconnecting)){if(this._reconnectRetries>=gn){Dt(`[ws] Max reconnect retries (${gn}) reached, giving up.
|
|
8
|
+
`),this.dispatch({type:"error",data:"Connection lost \u2014 max retries reached."});return}this._reconnecting=!0,this._reconnectRetries++,this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.reconnectTimer=setTimeout(async()=>{try{Dt(`[ws] Reconnecting (attempt ${this._reconnectRetries})...
|
|
9
|
+
`),this.dispatch({type:"notification",data:`Reconnecting (${this._reconnectRetries})...`}),await this.connect(),this.dispatch({type:"notification",data:"Reconnected."})}catch{this.reconnectDelay=Math.min(this.reconnectDelay*2,zo),this._reconnecting=!1,this._startReconnect()}},this.reconnectDelay)}}dispatch(e){let n=this.handlers.get(e.type);if(n)for(let o of n)try{o(e)}catch(s){console.error(`Handler error [${e.type}]:`,s)}}send(e,n,o){if(V("send",e+(n?" msg="+n.slice(0,40):"")+(o?" data="+JSON.stringify(o).slice(0,60):"")),!this.ws||this.ws.readyState!==Rt.OPEN){console.warn(`[ws] Not connected, dropping: ${e}`);return}let s={type:e,sessionId:this.sessionId};n!==void 0&&(s.message=n),o&&(s.data=JSON.stringify(o));try{this.ws.send(JSON.stringify(s))}catch(i){console.error(`[ws] send error [${e}]:`,i)}}chat(e,n=!1){this.send(n?"plan":"chat",e)}stop(){V("send","stop"),this.send("stop")}pause(){this.send("pause")}resume(){this.send("resume")}planConfirm(){this.send("plan_confirm")}updateDocs(){this.send("update_docs")}doctor(){this.send("doctor")}rewind(){this.send("rewind")}compact(){this.send("compact")}switchModel(e){this.send("model_change",void 0,{model:e})}approveHook(e){V("send","approveHook "+e),this.send("hook_allow",void 0,{approvalId:e})}denyHook(e){V("send","denyHook "+e),this.send("hook_deny",void 0,{approvalId:e})}exit(){this.send("exit")}init(){this.send("init")}effort(e){this.send("effort",void 0,{level:e})}branch(e){this.send("branch",void 0,{name:e})}mcp(e){this.send("mcp",void 0,{action:e})}skills(){this.send("skills")}agents(){this.send("agents")}config(e){this.send("config",void 0,{action:e})}plugin(e){this.send("plugin",void 0,{action:e})}async listFiles(e){try{let n=e?`${this.backendUrl}/api/files?path=${encodeURIComponent(e)}`:`${this.backendUrl}/api/files`,o=await fetch(n);if(!o.ok)return[];let s=await o.json(),i=[],d=c(f=>{for(let p of f)p.type==="file"&&i.push(p.path),p.children&&d(p.children)},"walk");return d(s?.data||s||[]),i}catch{return[]}}async readFileContent(e){try{let n=await fetch(`${this.backendUrl}/api/files/read?path=${encodeURIComponent(e)}`);if(!n.ok)return null;let o=await n.json();return o?.data||o}catch{return null}}async listSessions(){try{let n=await(await fetch(`${this.backendUrl}/api/sessions`)).json();return n?.data||n||[]}catch{return[]}}async deleteSession(e){try{return(await(await fetch(`${this.backendUrl}/api/sessions/${e}`,{method:"DELETE"})).json())?.success===!0}catch{return!1}}async getSessionMessages(e){try{return(await(await fetch(`${this.backendUrl}/api/sessions/${e}/messages`)).json())?.data||[]}catch{return[]}}async close(){this._running=!1,this._reconnecting=!1,this._stopHeartbeat(),this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.ws&&(this.ws.close(),this.ws=null)}};import{memo as cr,useState as lr,useEffect as dr,useRef as ur}from"react";import{Box as Qe,Text as R}from"ink";import{useSyncExternalStore as Qo,useRef as Zo}from"react";function hn(t,e){let n=t,o=new Set;return{getState:c(()=>n,"getState"),setState:c(s=>{let i=n,d=s(i);if(!Object.is(d,i)){n=d,e?.({newState:d,oldState:i});for(let f of o)f()}},"setState"),subscribe:c(s=>(o.add(s),()=>o.delete(s)),"subscribe")}}c(hn,"createStore");var er={messages:[],currentMessage:null,usage:{promptTokens:0,completionTokens:0,totalTokens:0,usageRatio:0},planMode:!1,autoMode:!1,planWaiting:!1,scrollOffset:0,modelName:"",connected:!1,statusText:"connecting...",tokenRate:0,toolCallsExpanded:!1,planTasks:[],pdcaPhase:"",degradation:{active:!1,retryCount:0,maxRetries:0,mode:"normal",message:""},compactionProgress:null,contentHeight:0},Et=null;function xe(){return Et||(Et=hn(er)),Et}c(xe,"getStore");var tr=c(t=>t.currentMessage!==null,"selIsGenerating");var nr=c(t=>t.planTasks,"selPlanTasks"),or=c(t=>t.pdcaPhase,"selPdcaPhase"),rr=c(t=>t.degradation,"selDegradation"),sr=c(t=>({messages:t.messages,currentMessage:t.currentMessage,scrollOffset:t.scrollOffset,contentHeight:t.contentHeight}),"selChatArea"),ar=c(t=>({usage:t.usage,modelName:t.modelName,planMode:t.planMode,autoMode:t.autoMode,connected:t.connected,statusText:t.statusText,messagesLen:t.messages.length,tokenRate:t.tokenRate,compactionProgress:t.compactionProgress}),"selStatusLine");function ir(t,e){if(Object.is(t,e))return!0;if(t===null||e===null||typeof t!="object"||typeof e!="object")return!1;let n=Object.keys(t),o=Object.keys(e);return n.length!==o.length?!1:n.every(s=>Object.is(t[s],e[s]))}c(ir,"shallowEqual");function oe(t){let e=xe(),n=Zo(null),o=c(()=>{let s=t(e.getState()),i=n.current;return i!==null&&ir(s,i.value)?i.value:(n.current={value:s},s)},"getSnapshot");return Qo(e.subscribe,o,o)}c(oe,"useAppSlice");var xn=c(()=>oe(tr),"useAppIsGenerating"),wn=c(()=>oe(sr),"useAppChatArea"),Pt=c(()=>oe(ar),"useAppStatusLine");var Tn=c(()=>oe(nr),"useAppPlanTasks"),bn=c(()=>oe(or),"useAppPdcaPhase"),Sn=c(()=>oe(rr),"useAppDegradation");function A(t){xe().setState(t)}c(A,"updateAppState");import{Fragment as Ae,jsx as O,jsxs as X}from"react/jsx-runtime";function ut(t){return t>=1e6?`${(t/1e6).toFixed(1)}M`:t>=1e3?`${Math.round(t/1e3)}K`:String(t)}c(ut,"formatTokens");function pr(t){return t<=0?"":t>=100?`${Math.round(t)}t/s`:t>=10?`${t.toFixed(1)}t/s`:`${t.toFixed(1)}t/s`}c(pr,"formatRate");function fr(t){if(t<=0)return"";if(t>=60){let e=Math.floor(t/60),n=t%60;return`${e}m${n}s`}return`${t}s`}c(fr,"formatElapsed");var yn=cr(c(function(){V("app","StatusLine mount planMode="+Pt().planMode);let{usage:e,modelName:n,planMode:o,autoMode:s,connected:i,statusText:d,messagesLen:f,tokenRate:p,compactionProgress:m}=Pt(),b=Sn(),h=bn(),x=Tn(),S=f,C=xn(),k=ur(0),[w,M]=lr(Date.now());dr(()=>{if(C){k.current===0&&(k.current=Date.now());let re=setInterval(()=>M(Date.now()),1e3);return()=>clearInterval(re)}else k.current=0},[C]);let a=C?Math.floor((w-k.current)/1e3):0,r=Math.min(100,Math.round(e.usageRatio*100)),g=Math.round(r/10),l="\u2588".repeat(g)+"\u2591".repeat(10-g),T=n||(i?"ready":"connecting..."),y=o?" Plan ":" Act ",E=o?u.plan:u.success,W=o?"mode-plan":"mode-act",_=i?"\u25CF":"\u25CB",D=i?u.connected:u.disconnected,P=d.startsWith("Error:"),Ce=r>90?u.error:r>70?u.warning:u.text,Pe=pr(p),Ie=fr(a),Me=e.promptTokens,$=e.completionTokens;return X(Qe,{flexDirection:"column",width:"100%",paddingRight:1,children:[X(Qe,{height:1,children:[O(R,{bold:!0,color:u.primary,children:"jwcode"}),O(R,{children:" "}),X(R,{backgroundColor:E,color:"black",children:[" ",y," "]},W),O(R,{children:" "}),s&&X(Ae,{children:[O(R,{backgroundColor:u.auto,color:"black",children:" AUTO "}),O(R,{children:" "})]}),h&&X(Ae,{children:[X(R,{backgroundColor:u.warning,color:"black",children:[" ",h," "]}),O(R,{children:" "})]}),x.length>0&&X(Ae,{children:[O(R,{color:u.primary,children:x.filter(re=>re.status==="completed").length}),O(R,{dimColor:!0,children:"/"}),O(R,{color:u.text,children:x.length}),O(R,{dimColor:!0,children:" tasks"}),O(R,{children:" "})]}),X(R,{color:D,children:[_," "]}),O(R,{color:u.success,children:T}),O(R,{children:" "}),X(R,{dimColor:!0,children:[S,"msgs"]}),Me>0||$>0?X(Ae,{children:[O(R,{children:" "}),O(R,{color:u.info,children:ut(Me)}),O(R,{dimColor:!0,children:"+"}),O(R,{color:u.success,children:ut($)}),O(R,{dimColor:!0,children:"="}),O(R,{color:u.warning,children:ut(e.totalTokens)})]}):X(Ae,{children:[O(R,{children:" t:"}),O(R,{color:u.warning,children:ut(e.totalTokens)})]}),O(R,{children:" "}),X(R,{color:Ce,children:[l," ",r,"%"]}),C&&Pe&&X(Ae,{children:[O(R,{children:" "}),O(R,{color:u.tool,children:Pe})]}),C&&Ie&&X(Ae,{children:[O(R,{children:" "}),O(R,{color:u.primary,children:Ie})]})]}),O(Qe,{height:1,children:b.active?X(R,{color:u.warning,dimColor:!0,children:["[",b.mode.toUpperCase(),"] ",b.message,b.retryCount>0?` (${b.retryCount}/${b.maxRetries})`:""]}):O(R,{children:" "})}),O(Qe,{height:1,children:m?X(Ae,{children:[X(R,{color:m.percent>=100?u.success:u.primary,children:[m.percent>=100?"\u2713":"\u2699"," ",m.message]}),O(R,{children:" "}),X(R,{color:u.warning,children:["\u2588".repeat(Math.round(m.percent/10)),"\u2591".repeat(10-Math.round(m.percent/10))]}),X(R,{dimColor:!0,children:[" ",m.percent,"%"]})]}):O(R,{children:" "})}),O(Qe,{height:1,children:d&&d!=="connecting..."?O(R,{color:P?u.error:u.muted,dimColor:!P,children:d.slice(0,100)}):O(R,{children:" "})})]})},"StatusLine"));import{Box as Y,Text as H}from"ink";import{useState as Dn,useMemo as Rr,useRef as ft,useLayoutEffect as Dr,useCallback as En,memo as tt,forwardRef as On}from"react";import{measureElement as Er}from"ink";import{useEffect as mr,useRef as It}from"react";import{useStdin as gr}from"ink";var hr="\x1B[?1000h\x1B[?1002h\x1B[?1006h",xr="\x1B[?1006l\x1B[?1002l\x1B[?1000l",Ze={topRow:0,trackHeight:0,contentHeight:0,viewportHeight:0,termCols:80};function kn(t){Ze=t}c(kn,"setScrollGeometry");function wr(t){let e=t.match(/\x1b\[<(\d+);(\d+);(\d+)([Mm])/);return e?{btn:parseInt(e[1],10),col:parseInt(e[2],10),row:parseInt(e[3],10),final:e[4]}:null}c(wr,"parseSgr");var Tr=2;function Cn(){let{stdin:t,internal_eventEmitter:e}=gr(),n=It(""),o=It(!1),s=It(0);mr(()=>{if(!t||!e||process.env.JWCODE_NO_MOUSE==="1")return;t.write(hr);let i=c(d=>{let f=typeof d=="string"?d:d.toString("utf-8");n.current+=f;let p=wr(n.current),m=0;if(p){let h=`\x1B[<${p.btn};${p.col};${p.row}`+p.final,x=n.current.indexOf(h)+h.length;if(p.btn===64||p.btn===65){let S=p.btn===64?3:-3;A(C=>{let k=Math.max(0,Ze.contentHeight-Ze.viewportHeight),w=Math.max(0,Math.min(C.scrollOffset+S,k));return{...C,scrollOffset:w}})}if(p.btn===0&&p.final==="M"){let S=Ze;if(p.col>S.termCols-Tr&&S.trackHeight>0&&S.contentHeight>S.viewportHeight){let k=p.row-S.topRow-1;if(k>=0&&k<S.trackHeight){let w=S.contentHeight-S.viewportHeight,M=1-k/Math.max(1,S.trackHeight-1),a=Math.round(M*w);A(r=>({...r,scrollOffset:Math.max(0,Math.min(a,w))})),o.current=!0,s.current=Date.now()}}}if(p.btn===32&&o.current){let S=Date.now();if(S-s.current<33){m=x,n.current=n.current.slice(m);return}s.current=S;let C=Ze,k=p.row-C.topRow-1,w=C.contentHeight-C.viewportHeight,M=1-Math.max(0,Math.min(1,k/Math.max(1,C.trackHeight-1))),a=Math.round(M*w);A(r=>({...r,scrollOffset:Math.max(0,Math.min(a,w))}))}p.btn===0&&p.final==="m"&&(o.current=!1),m=x}m>0?n.current=n.current.slice(m):n.current.length>128&&(n.current=n.current.slice(-64))},"onData");return e.on("input",i),()=>{e.removeListener("input",i),t.write(xr),n.current="",o.current=!1}},[t,e])}c(Cn,"useMouseWheel");import{memo as An,useState as br,useMemo as pt}from"react";import{Box as we,Text as Te}from"ink";import{jsx as be,jsxs as ue}from"react/jsx-runtime";var Sr=/^(---|\+\+\+) /,yr=/^@@ /,kr=/^\\(?!.*\\$)/;var Mn=30;function Cr(t){let e=t.split(`
|
|
10
|
+
`),n=[];for(let o of e){let s="context";Sr.test(o)?s="fileHeader":yr.test(o)?s="hunkHeader":o.startsWith("+")?s="addition":o.startsWith("-")?s="deletion":kr.test(o)&&(s="placeholder");let i=o;(s==="addition"||s==="deletion")&&(i=o.slice(1)),n.push({type:s,text:i,raw:o})}return n}c(Cr,"parseDiff");function vn(t,e){return t.filter(n=>n.type===e).length}c(vn,"countByType");var Ot=An(c(function({content:e,terminalCols:n=120,startCollapsed:o}){let s=pt(()=>Cr(e),[e]),i=s.length,d=pt(()=>vn(s,"addition"),[s]),f=pt(()=>vn(s,"deletion"),[s]),p=o??i>Mn,[m,b]=br(p),h=c(()=>b(a=>!a),"toggle"),x=n-6,S=pt(()=>{let a=[];for(let r of s)if(r.type==="fileHeader"&&r.raw.startsWith("+++")){let g=r.raw.replace(/^\+\+\+ (a|b)\//,"").trim();g&&g!=="/dev/null"&&a.push(g)}return[...new Set(a)]},[s]),C=S.length>0?S.join(", ")+` \u2192 +${d}/-${f}`:`Diff \u2192 +${d}/-${f}`;if(i===0)return null;let k=Mn,w=m?s.slice(0,k):s,M=i-k;return ue(we,{flexDirection:"column",paddingLeft:1,children:[ue(we,{children:[ue(Te,{color:u.primary,bold:!0,children:[" ","[",m?"+":"-","]"," "]}),be(Te,{color:u.muted,children:C})]}),be(we,{flexDirection:"column",children:w.map((a,r)=>be(vr,{line:a,maxWidth:x},r))}),m&&M>0&&ue(we,{children:[ue(Te,{color:u.info,dimColor:!0,children:[" \u2026 ",M," more lines"," "]}),be(Te,{color:u.primary,dimColor:!0,underline:!0,children:"[click or press to expand]"})]})]})},"DiffDisplay"));function Mr(t,e){return t.length<=e?t:t.slice(0,e-3)+"..."}c(Mr,"truncateLine");var vr=An(c(function({line:e,maxWidth:n}){let o=Mr(e.text,n);switch(e.type){case"fileHeader":return be(we,{children:ue(Te,{color:u.diffFileHeader,bold:!0,dimColor:!1,children:[" ",e.raw.slice(0,n)]})});case"hunkHeader":return be(we,{children:ue(Te,{color:u.diffHeader,dimColor:!0,children:[" ",e.raw.slice(0,n)]})});case"addition":return be(we,{children:ue(Te,{color:u.diffAdded,backgroundColor:u.diffAddedBg,children:[" + ",o]})});case"deletion":return be(we,{children:ue(Te,{color:u.diffRemoved,backgroundColor:u.diffRemovedBg,children:[" - ",o]})});case"placeholder":return be(we,{children:ue(Te,{color:u.diffPlaceholder,dimColor:!0,children:[" ",e.raw.slice(0,n)]})});default:return be(we,{children:ue(Te,{color:u.muted,dimColor:!0,children:[" ",o]})})}},"RenderDiffLine"));import{Box as ae,Text as Z}from"ink";import{memo as Ar}from"react";import{jsx as G,jsxs as Fe}from"react/jsx-runtime";var Lt=null;function _r(){if(!Lt)try{Lt=No("marked")}catch{return null}return Lt}c(_r,"getMarked");function _n(t,e){let n=[],o=0;for(let s of t){let i=s,d=e+"-"+o++;switch(i.type){case"strong":n.push(G(Z,{bold:!0,children:et(i.tokens||[])},d));break;case"em":n.push(G(Z,{italic:!0,children:et(i.tokens||[])},d));break;case"codespan":n.push(G(Z,{color:u.warning,backgroundColor:"#2d2d2d",children:i.text||""},d));break;case"link":{let f=et(i.tokens||[]),p=i.href||"";n.push(G(Z,{color:u.info,children:f+" ("+p+")"},d));break}default:n.push(G(Z,{children:i.text||i.raw||""},d));break}}return n}c(_n,"renderInlineTokens");function et(t){return t.map(e=>e.text||e.raw||"").join("")}c(et,"renderPlainText");var Rn=Ar(c(function({content:e,terminalCols:n}){let o=_r();if(!o||!e)return G(Z,{children:e});let s=[];try{s=new o.Lexer().lex(e)}catch{return G(Z,{children:e})}let i=[],d=0;for(let f of s){let p="md-"+d++,m=f;switch(m.type){case"heading":{let b=m.depth||1,h="#".repeat(b)+" ";i.push(G(ae,{marginTop:b===1?1:0,children:Fe(Z,{bold:!0,color:b<=2?u.primary:u.muted,children:[h,et(m.tokens||[])]})},p));break}case"paragraph":i.push(G(ae,{paddingLeft:0,children:G(Z,{children:_n(m.tokens||[],p)})},p));break;case"code":{let b=m.lang||"",h=m.text||"",x=Math.min(n-4,100),S=h.split(`
|
|
11
|
+
`);i.push(Fe(ae,{flexDirection:"column",marginY:0,children:[b&&G(ae,{paddingLeft:1,children:Fe(Z,{dimColor:!0,children:["\u250C\u2500 ",b]})}),S.map((C,k)=>G(ae,{paddingLeft:2,children:G(Z,{color:u.muted,dimColor:!0,children:C.slice(0,x)})},p+"-l"+k))]},p));break}case"blockquote":i.push(G(ae,{flexDirection:"column",paddingLeft:2,children:(m.tokens||[]).map((b,h)=>Fe(ae,{children:[Fe(Z,{dimColor:!0,children:["\u2502"," "]}),G(Z,{italic:!0,children:b.text||et(b.tokens||[])})]},p+"-b"+h))},p));break;case"list":{let b=m.items||[];i.push(G(ae,{flexDirection:"column",children:b.map((h,x)=>{let S=m.ordered?(m.start||1)+x+". ":" \u2022 ";return Fe(ae,{paddingLeft:1,children:[G(Z,{dimColor:!0,children:S}),G(ae,{flexDirection:"column",children:(h.tokens||[]).map((C,k)=>C.type==="text"?G(Z,{children:_n(C.tokens||[],p+"-it"+k)},p+"-it"+k):G(Z,{children:C.text||""},p+"-ip"+k))})]},p+"-i"+x)})},p));break}case"hr":i.push(G(ae,{children:G(Z,{dimColor:!0,children:"\u2500".repeat(Math.min(n-2,60))})},p));break;case"space":break;default:m.raw&&i.push(G(Z,{children:m.raw.trimEnd()},p));break}}return G(ae,{flexDirection:"column",children:i})},"MarkdownRenderer"));import{Fragment as Wn,jsx as I,jsxs as F}from"react/jsx-runtime";var Pn="-".repeat(60),gt=200,Pr=6;function ht(t){if(t<=0)return"";if(t>=60){let e=Math.floor(t/60),n=t%60;return e+"m"+n+"s"}return t+"s"}c(ht,"formatDuration");function Ln(t){if(!t||t.length<20)return!1;let e=t.split(`
|
|
12
|
+
`),n=!1,o=!1,s=0;for(let i of e)/^--- .+\/|^\+\+\+ .+\//.test(i)&&(n=!0),/^@@ -\d+,\d+ +\d+,\d+ @@/.test(i)&&(o=!0),/^[+-]/.test(i)&&!/^[+-]{3}/.test(i)&&!/^[+-]{4}/.test(i)&&s++;return(n||o)&&s>=2}c(Ln,"isDiffContent");function Bn(t,e){let n=t[e];if(!n)return!0;if(n.status==="running")return!1;let o=-1;for(let s=t.length-1;s>=0;s--)if(t[s].status==="complete"||t[s].status==="error"){o=s;break}return e!==o}c(Bn,"shouldStartCollapsed");var Ir="\xB7",In="#",Or="^",Lr="v";function Br({contentHeight:t,offset:e,viewportHeight:n}){let o=Math.max(2,n-2),s=Math.max(1,Math.floor(o*n/Math.max(t,1))),i=Math.max(0,t-n),d=i>0?Math.round((o-s)*e/i):0,f=[];for(let p=0;p<o;p++)p>=d&&p<d+s?f.push(In):f.push(Ir);return F(Y,{flexDirection:"column",width:2,flexShrink:0,minWidth:2,children:[I(H,{color:u.border,children:Or}),f.map((p,m)=>I(H,{color:p===In?u.text:u.border,children:p},m)),I(H,{color:u.border,children:Lr})]})}c(Br,"Scrollbar");function Nn({content:t,terminalCols:e}){return Ln(t)?I(Ot,{content:t,terminalCols:e}):I(Rn,{content:t,terminalCols:e})}c(Nn,"MessageContent");function Nr({result:t,terminalCols:e}){if(Ln(t)){let o=t.split(`
|
|
13
|
+
`),s=o.filter(m=>m.startsWith("+")&&!m.startsWith("+++")).length,i=o.filter(m=>m.startsWith("-")&&!m.startsWith("---")).length,d=`+${s}/-${i}`,p=t.length>2e3?t.slice(0,2e3)+`
|
|
14
|
+
... (truncated)`:t;return F(Y,{flexDirection:"column",paddingLeft:2,children:[F(H,{dimColor:!0,children:["Diff: ",d]}),I(Ot,{content:p,terminalCols:e})]})}let n=t.length>500?t.slice(0,500)+"...":t;return I(H,{color:u.muted,dimColor:!0,children:n})}c(Nr,"ToolResult");var Hr=tt(c(function({step:e}){let n=e.status==="success"?"[ok]":e.status==="error"?"[!!]":e.status==="running"?"[..]":"[--]",o=e.status==="success"?u.success:e.status==="error"?u.error:e.status==="running"?u.primary:u.primary,s=e.duration?ht(e.duration):e.status==="running"&&e.timestamp?ht(Math.floor((Date.now()-e.timestamp)/1e3)):"";return F(Y,{flexDirection:"column",children:[F(Y,{children:[F(H,{color:o,children:[" ",n," "]}),I(H,{bold:!0,color:o,children:e.title}),s&&F(Wn,{children:[I(H,{dimColor:!0,children:" "}),I(H,{color:u.muted,dimColor:!0,children:s})]})]}),e.thought&&F(H,{color:u.info,dimColor:!0,children:[" ",Ge(e.thought,200)]}),e.action&&F(H,{color:u.warning,children:[" ",Ge(e.action,200)]}),e.result&&F(H,{color:u.success,children:[" ",Ge(e.result,300)]})]})},"StepDisplay")),Hn=tt(c(function({tc:e,collapsed:n,onToggle:o,terminalCols:s}){let i=e.status==="complete"?"[ok]":e.status==="running"?"[..]":"[!!]",d=e.status==="complete"?u.success:e.status==="running"?u.warning:u.error,f=e.duration?ht(e.duration):e.status==="running"&&e.timestamp?ht(Math.floor((Date.now()-e.timestamp)/1e3)):"";return F(Y,{flexDirection:"column",paddingLeft:1,children:[F(Y,{children:[F(H,{color:d,children:[" ",i," "]}),I(H,{bold:!0,color:u.tool,children:e.name}),f&&F(Wn,{children:[I(H,{dimColor:!0,children:" "}),I(H,{color:u.muted,dimColor:!0,children:f})]}),I(H,{dimColor:!0,children:" "}),F(H,{color:u.info,dimColor:!0,children:["[",n?"+":"-","]"]})]}),!n&&e.args&&I(Y,{paddingLeft:4,children:I(H,{dimColor:!0,children:Ge(Ur(e.args),200)})}),e.result&&I(Y,{paddingLeft:2,flexDirection:"column",children:I(Nr,{result:e.result,terminalCols:s})})]})},"ToolCallDisplay")),Wr=tt(On(c(function({msg:e,expandedMessages:n,expandedTools:o,toolCallsExpanded:s,onToggleTool:i,onToggleMessage:d,terminalCols:f},p){return F(Y,{flexDirection:"column",marginBottom:1,ref:p,children:[e.type==="user"&&F(Y,{flexDirection:"column",children:[I(H,{dimColor:!0,children:Pn}),F(H,{color:u.user,bold:!0,children:[">"," ",e.content]})]}),e.type==="assistant"&&F(Y,{flexDirection:"column",children:[I(H,{children:" "}),e.steps.map((m,b)=>I(Hr,{step:m},m.id||b)),e.thinking&&F(Y,{flexDirection:"column",children:[I(H,{dimColor:!0,italic:!0,children:n.has(e.id)?e.thinking:Ge(e.thinking,gt)}),e.thinking.length>gt&&F(H,{dimColor:!0,children:["[",e.thinking.length-gt," more chars]"," -> to expand"]})]}),e.toolCalls.map((m,b)=>{let h=m.id||m.name||"tool-"+e.id+"-"+b,x=Bn(e.toolCalls,b),S=o.has(h);return I(Hn,{tc:m,terminalCols:f,collapsed:s||S?!1:x,onToggle:()=>i(h)},h)}),e.content&&I(Y,{paddingLeft:1,children:I(Nn,{content:e.content,terminalCols:f})}),I(H,{dimColor:!0,children:Pn})]}),e.type==="system"&&I(Y,{children:F(H,{color:u.error,children:["Error: ",e.content]})})]})},"MessageItem")),(t,e)=>t.msg.id===e.msg.id&&t.msg.content===e.msg.content&&t.msg.thinking===e.msg.thinking&&t.msg.type===e.msg.type&&t.expandedMessages.has(t.msg.id)===e.expandedMessages.has(e.msg.id)&&t.toolCallsExpanded===e.toolCallsExpanded&&t.terminalCols===e.terminalCols),$r=tt(On(c(function({msg:e,expandedTools:n,toolCallsExpanded:o,onToggleTool:s,terminalCols:i},d){return F(Y,{flexDirection:"column",ref:d,children:[e.thinking&&I(H,{dimColor:!0,italic:!0,children:Ge(e.thinking,gt)}),e.toolCalls.map((f,p)=>{let m=f.id||f.name||"tool-"+e.id+"-"+p,b=Bn(e.toolCalls,p),h=n.has(m);return I(Hn,{tc:f,terminalCols:i,collapsed:o||h?!1:b,onToggle:()=>s(m)},m)}),e.content&&I(Y,{paddingLeft:1,children:I(Nn,{content:e.content,terminalCols:i})})]})},"StreamingMessage")),(t,e)=>t.msg.id===e.msg.id&&t.msg.content===e.msg.content&&t.msg.thinking===e.msg.thinking&&t.toolCallsExpanded===e.toolCallsExpanded&&t.terminalCols===e.terminalCols);function mt(t,e){if(t.type==="user")return 3;let n=4;t.thinking&&(n+=Math.ceil(t.thinking.length/Math.max(1,e))+1),n+=t.steps.length;for(let o of t.toolCalls)n+=2,o.result&&(n+=Math.min(20,Math.ceil(o.result.length/Math.max(1,e))));return t.content&&(n+=Math.ceil(t.content.length/Math.max(1,e))),n}c(mt,"defaultMessageHeight");function jr(t){let e=t,n=0;for(;e;){let o=e.yogaNode;if(!o||typeof o.getComputedTop!="function")break;n+=o.getComputedTop(),e=e.parentNode??null}return n}c(jr,"getAbsoluteTopRow");var Bt=tt(c(function({terminalCols:e,terminalRows:n}){let{messages:o,currentMessage:s,scrollOffset:i}=wn(),d=oe(_=>_.toolCallsExpanded),[f,p]=Dn(new Set),[m,b]=Dn(new Set),h=Math.max(3,n-Pr),x=ft(new Map),S=ft(new Map),C=ft(0),k=ft(null),w=Rr(()=>s?o.filter(_=>_.id!==s.id):o,[o,s&&s.id]),M=Math.min(i,Math.max(0,C.current-h)),a=w.length,r=0;if(M===0){let _=0;for(let D=a-1;D>=0;D--){let P=S.current.get(w[D].id)??mt(w[D],Math.max(10,e-4));if(_+=P,_>h){r=D+1;break}r=D}}else{let _=0,D=M+h;for(let P=a-1;P>=0;P--){let Ce=S.current.get(w[P].id)??mt(w[P],Math.max(10,e-4));if(_+=Ce,_>D){r=P;break}r=P}}r=Math.max(0,r);let g=w.slice(r),l=En(_=>{p(D=>{let P=new Set(D);return P.has(_)?P.delete(_):P.add(_),P})},[]),T=En(_=>{b(D=>{let P=new Set(D);return P.has(_)?P.delete(_):P.add(_),P})},[]),y=C.current,E=y>h||i>0,W=c(_=>D=>{D?x.current.set(_,D):x.current.delete(_)},"registerMessageRef");return Dr(()=>{let _=0,D=x.current,P=S.current,Ce=Math.max(10,e-4);for(let[$,re]of D)try{let{height:Ye}=Er(re);Ye>0&&(P.set($,Ye),_+=Ye)}catch{}for(let $ of w)if(!D.has($.id)){let re=P.get($.id)??mt($,Ce);P.set($.id,re),_+=re}if(s){let $=s,re=P.get($.id)??mt($,Ce);P.set($.id,re),_+=re}_+=1,_!==C.current&&(C.current=_,A($=>$.contentHeight===_?$:{...$,contentHeight:_}));let Pe=jr(k.current)+1;kn({topRow:Pe,trackHeight:Math.max(2,h-2),contentHeight:_,viewportHeight:h,termCols:e});let Ie=Math.max(0,_-h);i>Ie&&A($=>({...$,scrollOffset:Ie}));let Me=new Set(w.map($=>$.id));s&&Me.add(s.id);for(let $ of S.current.keys())Me.has($)||(S.current.delete($),x.current.delete($))},[w,s?.id,e,h,i,f.size,m.size,d]),F(Y,{flexDirection:"row",width:"100%",overflow:"hidden",ref:k,children:[F(Y,{flexGrow:1,flexDirection:"column",overflow:"hidden",children:[I(Y,{children:I(H,{dimColor:!E,bold:E,children:E?`[${r+1}-${a} / ${a}]`:`[${a}]`})}),g.map(_=>I(Wr,{ref:W(_.id),msg:_,expandedMessages:m,expandedTools:f,terminalCols:e,toolCallsExpanded:d,onToggleTool:l,onToggleMessage:T},_.id)),s&&I($r,{ref:W(s.id),msg:s,terminalCols:e,expandedTools:f,toolCallsExpanded:d,onToggleTool:l},s.id)]}),E&&I(Br,{contentHeight:y,offset:M,viewportHeight:h})]})},"ChatArea"));function Ur(t){if(typeof t!="string")return JSON.stringify(t,null,2);try{return JSON.stringify(JSON.parse(t),null,2)}catch{return t}}c(Ur,"formatJson");function Ge(t,e){let n=typeof t=="string"?t:String(t??"");return n.length<=e?n:n.slice(0,e)+"..."}c(Ge,"truncate");import{useState as $n,useMemo as Gr,useEffect as jn}from"react";import{Box as Tt,Text as Le,useInput as Kr,useStdout as qr}from"ink";var Jr=[{cmd:"/help",desc:"\u663E\u793A\u6240\u6709\u547D\u4EE4",via:"local",action:null},{cmd:"/plan",desc:"\u5207\u6362\u89C4\u5212\u6A21\u5F0F (\u5148\u89C4\u5212\u518D\u6267\u884C)",via:"local",action:"plan_mode"},{cmd:"/auto",desc:"\u5207\u6362\u81EA\u52A8\u6A21\u5F0F (\u81EA\u52A8\u6279\u51C6\u5DE5\u5177\u6267\u884C)",via:"local",action:"auto_mode"},{cmd:"/context",desc:"\u663E\u793A\u5F53\u524D\u4F1A\u8BDD\u72B6\u6001",via:"local",action:"show_context"},{cmd:"/exit",desc:"\u9000\u51FA JWCode",via:"local",action:"__exit__"}],Fr=[{cmd:"/confirm",desc:"\u786E\u8BA4\u5F53\u524D\u89C4\u5212\u5E76\u5F00\u59CB\u6267\u884C",via:"ws",action:"__confirm_plan"},{cmd:"/cancel",desc:"\u53D6\u6D88\u5F53\u524D\u89C4\u5212",via:"ws",action:"__cancel_plan"},{cmd:"/stop",desc:"\u505C\u6B62\u5F53\u524D AI \u751F\u6210",via:"ws",action:"stop"},{cmd:"/pause",desc:"\u6682\u505C\u5F53\u524D AI \u751F\u6210",via:"ws",action:"pause"},{cmd:"/resume",desc:"\u6062\u590D\u6682\u505C\u7684 AI \u751F\u6210",via:"ws",action:"resume"},{cmd:"/clear",desc:"\u6E05\u9664\u5F53\u524D\u4F1A\u8BDD\u6D88\u606F",via:"ws",action:"clear"},{cmd:"/doctor",desc:"\u8FD0\u884C\u7CFB\u7EDF\u81EA\u8BCA\u65AD",via:"ws",action:"doctor"},{cmd:"/rewind",desc:"\u56DE\u6EDA\u5230\u6700\u8FD1\u7684\u68C0\u67E5\u70B9",via:"ws",action:"rewind"},{cmd:"/compact",desc:"\u538B\u7F29\u4F1A\u8BDD\u4E0A\u4E0B\u6587 (\u91CA\u653E token)",via:"ws",action:"compact"},{cmd:"/model",desc:"\u5207\u6362 AI \u6A21\u578B (\u7528\u6CD5: /model <\u6A21\u578B\u540D> \u6216 /model \u6253\u5F00\u9009\u62E9\u5668)",via:"ws",action:"model_change"},{cmd:"/setup",desc:"\u914D\u7F6E AI \u63D0\u4F9B\u5546\u548C API Key",via:"local",action:"setup_wizard"},{cmd:"/init",desc:"\u5206\u6790\u9879\u76EE\u5E76\u751F\u6210 JWCODE.md \u9879\u76EE\u8BB0\u5FC6\u6587\u4EF6",via:"ws",action:"init"},{cmd:"/effort",desc:"\u8BBE\u7F6E\u4EFB\u52A1\u52AA\u529B\u7EA7\u522B (low/medium/high)",via:"ws",action:"effort"},{cmd:"/branch",desc:"\u521B\u5EFA\u5206\u652F\u4F1A\u8BDD (\u7528\u6CD5: /branch <\u540D\u79F0>)",via:"ws",action:"branch"},{cmd:"/mcp",desc:"MCP \u670D\u52A1\u5668\u7BA1\u7406 (list/add/remove)",via:"ws",action:"mcp"},{cmd:"/skills",desc:"\u67E5\u770B\u53EF\u7528 Skills \u5217\u8868",via:"ws",action:"skills"},{cmd:"/agents",desc:"\u5217\u51FA\u914D\u7F6E\u7684 Agent \u4EE3\u7406",via:"ws",action:"agents"},{cmd:"/config",desc:"\u7BA1\u7406\u914D\u7F6E (get/set/list)",via:"ws",action:"config"},{cmd:"/plugin",desc:"\u63D2\u4EF6\u7BA1\u7406 (install/list/remove)",via:"ws",action:"plugin"},{cmd:"/tokens",desc:"\u663E\u793A Token \u4F7F\u7528\u8BE6\u60C5",via:"ws",action:"tokens"},{cmd:"/memory",desc:"\u6D4F\u89C8\u9879\u76EE\u8BB0\u5FC6",via:"ws",action:"memory"},{cmd:"/export",desc:"\u5BFC\u51FA\u4F1A\u8BDD (\u7528\u6CD5: /export <\u8DEF\u5F84>)",via:"ws",action:"export"},{cmd:"/checkpoint",desc:"\u8BBE\u7F6E\u6216\u6062\u590D\u68C0\u67E5\u70B9",via:"ws",action:"checkpoint"},{cmd:"/test",desc:"\u8FD0\u884C\u5F53\u524D\u9879\u76EE\u6D4B\u8BD5",via:"ws",action:"test"},{cmd:"/lint",desc:"\u5BF9\u53D8\u66F4\u6587\u4EF6\u8FD0\u884C Linter",via:"ws",action:"lint"},{cmd:"/search",desc:"\u641C\u7D22\u4EE3\u7801\u5E93 (\u7528\u6CD5: /search <\u5173\u952E\u8BCD>)",via:"ws",action:"search"},{cmd:"/project",desc:"\u751F\u6210\u9879\u76EE\u6587\u6863",via:"ws",action:"project"}],Nt=[...Jr,...Fr],xt={"/help":null,"/plan":{action:"plan_mode"},"/auto":{action:"auto_mode"},"/context":{action:"show_context"},"/exit":{action:"__exit__"},"/quit":{action:"__exit__"},"/confirm":{action:"__confirm_plan"},"/cancel":{action:"__cancel_plan"},"/stop":{action:"stop"},"/pause":{action:"pause"},"/resume":{action:"resume"},"/clear":{action:"clear"},"/doctor":{action:"doctor"},"/rewind":{action:"rewind"},"/compact":{action:"compact"},"/model":{action:"model_change",needsArg:!1},"/setup":{action:"setup_wizard"},"/init":{action:"init"},"/effort":{action:"effort",needsArg:!0},"/branch":{action:"branch",needsArg:!0},"/mcp":{action:"mcp",needsArg:!0},"/skills":{action:"skills"},"/agents":{action:"agents"},"/config":{action:"config",needsArg:!0},"/plugin":{action:"plugin",needsArg:!0},"/tokens":{action:"tokens"},"/memory":{action:"memory"},"/export":{action:"export",needsArg:!0},"/checkpoint":{action:"checkpoint"},"/test":{action:"test"},"/lint":{action:"lint"},"/search":{action:"search",needsArg:!0},"/project":{action:"project"}},wt=`
|
|
15
|
+
\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\u2557
|
|
16
|
+
\u2551 JWCode \u547D\u4EE4\u5E2E\u52A9 \u2551
|
|
17
|
+
\u2560\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\u2563
|
|
18
|
+
\u2551 \u672C\u5730\u547D\u4EE4: \u2551
|
|
19
|
+
\u2551 /help \u663E\u793A\u6B64\u5E2E\u52A9\u4FE1\u606F \u2551
|
|
20
|
+
\u2551 /plan \u5207\u6362\u89C4\u5212\u6A21\u5F0F \u2551
|
|
21
|
+
\u2551 /auto \u5207\u6362\u81EA\u52A8\u6A21\u5F0F \u2551
|
|
22
|
+
\u2551 /context \u663E\u793A\u5F53\u524D\u4F1A\u8BDD\u72B6\u6001 \u2551
|
|
23
|
+
\u2551 /exit \u9000\u51FA JWCode \u2551
|
|
24
|
+
\u2560\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\u2563
|
|
25
|
+
\u2551 \u540E\u7AEF\u547D\u4EE4: \u2551
|
|
26
|
+
\u2551 /confirm \u786E\u8BA4\u6267\u884C\u5F53\u524D\u89C4\u5212 \u2551
|
|
27
|
+
\u2551 /cancel \u53D6\u6D88\u5F53\u524D\u89C4\u5212 \u2551
|
|
28
|
+
\u2551 /stop \u505C\u6B62\u5F53\u524D AI \u751F\u6210 \u2551
|
|
29
|
+
\u2551 /pause \u6682\u505C\u5F53\u524D AI \u751F\u6210 \u2551
|
|
30
|
+
\u2551 /resume \u6062\u590D\u6682\u505C\u7684\u751F\u6210 \u2551
|
|
31
|
+
\u2551 /clear \u6E05\u9664\u5F53\u524D\u4F1A\u8BDD\u6D88\u606F \u2551
|
|
32
|
+
\u2551 /model [\u540D] \u5207\u6362/\u9009\u62E9 AI \u6A21\u578B \u2551
|
|
33
|
+
\u2551 /setup \u914D\u7F6E AI \u63D0\u4F9B\u5546\u548C API Key \u2551
|
|
34
|
+
\u2551 /compact \u538B\u7F29\u4F1A\u8BDD\u4E0A\u4E0B\u6587 \u2551
|
|
35
|
+
\u2551 /doctor \u7CFB\u7EDF\u81EA\u8BCA\u65AD \u2551
|
|
36
|
+
\u2551 /rewind \u56DE\u6EDA\u5230\u6700\u8FD1\u68C0\u67E5\u70B9 \u2551
|
|
37
|
+
\u2551 /init \u751F\u6210\u9879\u76EE JWCODE.md \u2551
|
|
38
|
+
\u2551 /effort <\u7EA7> \u8BBE\u7F6E\u52AA\u529B\u7EA7\u522B low/med/high \u2551
|
|
39
|
+
\u2551 /branch <\u540D> \u521B\u5EFA\u5206\u652F\u4F1A\u8BDD \u2551
|
|
40
|
+
\u2551 /mcp <\u64CD\u4F5C> MCP \u670D\u52A1\u5668\u7BA1\u7406 \u2551
|
|
41
|
+
\u2551 /skills \u67E5\u770B Skills \u5217\u8868 \u2551
|
|
42
|
+
\u2551 /agents \u5217\u51FA Agent \u4EE3\u7406 \u2551
|
|
43
|
+
\u2551 /config <\u64CD> \u7BA1\u7406\u914D\u7F6E (get/set/list) \u2551
|
|
44
|
+
\u2551 /plugin <\u64CD> \u63D2\u4EF6\u7BA1\u7406 \u2551
|
|
45
|
+
\u2551 /tokens \u663E\u793A Token \u4F7F\u7528\u8BE6\u60C5 \u2551
|
|
46
|
+
\u2551 /memory \u6D4F\u89C8\u9879\u76EE\u8BB0\u5FC6 \u2551
|
|
47
|
+
\u2551 /export <\u8DEF> \u5BFC\u51FA\u4F1A\u8BDD\u5230\u6587\u4EF6 \u2551
|
|
48
|
+
\u2551 /checkpoint \u8BBE\u7F6E\u6216\u6062\u590D\u68C0\u67E5\u70B9 \u2551
|
|
49
|
+
\u2551 /test \u8FD0\u884C\u5F53\u524D\u9879\u76EE\u6D4B\u8BD5 \u2551
|
|
50
|
+
\u2551 /lint \u5BF9\u53D8\u66F4\u6587\u4EF6\u8FD0\u884C Linter \u2551
|
|
51
|
+
\u2551 /search <\u8BCD> \u641C\u7D22\u4EE3\u7801\u5E93 \u2551
|
|
52
|
+
\u2551 /project \u751F\u6210\u9879\u76EE\u6587\u6863 \u2551
|
|
53
|
+
\u2560\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\u2563
|
|
54
|
+
\u2551 \u5FEB\u6377\u952E: \u2551
|
|
55
|
+
\u2551 \u2191\u2193 \u6D4F\u89C8\u8F93\u5165\u5386\u53F2 (\u6700\u8FD130\u6761) \u2551
|
|
56
|
+
\u2551 PgUp/PgDn \u7FFB\u9875\u6D4F\u89C8\u6D88\u606F \u2551
|
|
57
|
+
\u2551 Home/End \u8DF3\u5230\u6700\u65E9/\u6700\u65B0\u6D88\u606F \u2551
|
|
58
|
+
\u2551 Tab \u5207\u6362 Plan/Act \u6A21\u5F0F \u2551
|
|
59
|
+
\u2551 / \u6253\u5F00\u547D\u4EE4\u9762\u677F (\u53EF\u7FFB\u9875) \u2551
|
|
60
|
+
\u2551 Esc \u5173\u95ED\u9762\u677F/\u53D6\u6D88\u5BA1\u6279 \u2551
|
|
61
|
+
\u2560\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\u2563
|
|
62
|
+
\u2551 \u666E\u901A\u8F93\u5165\u5373\u53D1\u9001\u804A\u5929\u6D88\u606F \u2551
|
|
63
|
+
\u2551 \u8F93\u5165\u6846\u663E\u793A\u5B57\u7B26\u6570+token\u4F30\u7B97 \u2551
|
|
64
|
+
\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\u255D`;import{jsx as nt,jsxs as Ke}from"react/jsx-runtime";function Un({filter:t,onSelect:e}){let[n,o]=$n(0),[s,i]=$n(0),{stdout:d}=qr(),f=d?.rows||24,p=Gr(()=>{let h=t.replace(/^\//,"").toLowerCase();return h?Nt.filter(x=>x.cmd.toLowerCase().includes(h)||x.desc.includes(h)):Nt},[t]);jn(()=>{o(0),i(0)},[t]);let m=Math.max(5,Math.min(f-13,10));jn(()=>{i(h=>n<h?n:n>=h+m?n-m+1:h)},[n,m]);let b=p.slice(s,s+m);return Kr((h,x)=>{if(x.escape){e(null);return}if(x.downArrow){o(S=>Math.min(S+1,p.length-1));return}if(x.upArrow){o(S=>Math.max(S-1,0));return}if(x.pageDown){o(S=>Math.min(S+m,p.length-1));return}if(x.pageUp){o(S=>Math.max(S-m,0));return}if(x.home){o(0);return}if(x.end){o(p.length-1);return}x.return&&p.length>0&&n>=0&&n<p.length&&e(p[n].cmd)}),Ke(Tt,{flexDirection:"column",borderStyle:"single",borderColor:u.primary,paddingX:1,width:52,children:[Ke(Tt,{children:[nt(Le,{bold:!0,color:u.primary,children:"\u547D\u4EE4\u5217\u8868"}),nt(Le,{dimColor:!0,children:" \u2191\u2193\u9009\u62E9 / PgUp/PgDn\u7FFB\u9875 / \u56DE\u8F66\u786E\u8BA4 / Esc\u53D6\u6D88"})]}),b.map((h,x)=>{let S=s+x;return Ke(Tt,{paddingLeft:1,children:[nt(Le,{color:S===n?u.primary:void 0,bold:S===n,children:S===n?"> ":" "}),nt(Le,{color:u.success,children:h.cmd}),Ke(Le,{dimColor:!0,children:[" ",h.desc]}),Ke(Le,{color:h.via==="ws"?u.warning:u.info,dimColor:S!==n,children:["(",h.via==="ws"?"\u540E\u7AEF":"\u672C\u5730",")"]})]},h.cmd)}),p.length>m&&nt(Tt,{children:Ke(Le,{dimColor:!0,children:[" ",s+1,"-",Math.min(s+m,p.length)," / ",p.length]})})]})}c(Un,"CommandPalette");import{useState as Jn,useMemo as Vr,useEffect as Fn}from"react";import{Box as ot,Text as _e,useInput as Xr,useStdout as Yr}from"ink";import{jsx as Re,jsxs as rt}from"react/jsx-runtime";var Gn=60;function zr(t){let e=t.replace(/\\/g,"/").split("/");return e[e.length-1]||t}c(zr,"basename");function Qr(t){let e=t.replace(/\\/g,"/").split("/");return e.pop(),e.join("/")||"."}c(Qr,"dirname");function Kn({query:t,files:e,onSelect:n}){let[o,s]=Jn(0),[i,d]=Jn(0),{stdout:f}=Yr(),p=f?.rows||24,m=Vr(()=>{if(!t)return e;let x=t.toLowerCase().replace(/\\/g,"/");return e.filter(S=>S.toLowerCase().replace(/\\/g,"/").includes(x))},[e,t]);Fn(()=>{s(0),d(0)},[t]);let b=Math.max(4,Math.min(p-13,8));Fn(()=>{d(x=>o<x?o:o>=x+b?o-b+1:x)},[o,b]);let h=m.slice(i,i+b);return Xr((x,S)=>{if(S.escape){n(null);return}if(S.downArrow){s(C=>Math.min(C+1,m.length-1));return}if(S.upArrow){s(C=>Math.max(C-1,0));return}if(S.pageDown){s(C=>Math.min(C+b,m.length-1));return}if(S.pageUp){s(C=>Math.max(C-b,0));return}S.return&&m.length>0&&o>=0&&o<m.length&&n(m[o])}),rt(ot,{flexDirection:"column",borderStyle:"single",borderColor:u.warning,paddingX:1,width:64,children:[rt(ot,{children:[Re(_e,{bold:!0,color:u.warning,children:"@ \u641C\u7D22: "}),Re(_e,{color:u.success,children:t||"(\u8F93\u5165\u6587\u4EF6\u540D)"}),Re(_e,{dimColor:!0,children:" \u2191\u2193 \u9009\u62E9 \xB7 Enter \u63D2\u5165 \xB7 Esc \u5173\u95ED"})]}),m.length===0&&Re(ot,{paddingLeft:1,children:Re(_e,{dimColor:!0,children:" \u672A\u627E\u5230\u5339\u914D\u6587\u4EF6"})}),h.map((x,S)=>{let C=i+S,k=zr(x),w=Qr(x),M=x.length>Gn?"..."+x.slice(-(Gn-3)):x;return rt(ot,{paddingLeft:1,children:[Re(_e,{color:C===o?u.warning:void 0,bold:C===o,children:C===o?"\u276F ":" "}),Re(_e,{color:u.success,children:k}),rt(_e,{dimColor:!0,children:[" \u2014 ",w]})]},x)}),m.length>b&&Re(ot,{children:rt(_e,{dimColor:!0,children:[" ",i+1,"-",Math.min(i+b,m.length)," / ",m.length]})})]})}c(Kn,"FilePalette");import{useState as Ht,useMemo as Zr,useEffect as qn}from"react";import{Box as qe,Text as pe,useInput as es,useStdout as ts}from"ink";import{jsx as ye,jsxs as De}from"react/jsx-runtime";function Vn({sessions:t,onSelect:e,onDelete:n}){let[o,s]=Ht(0),[i,d]=Ht(0),[f,p]=Ht(""),{stdout:m}=ts(),b=m?.rows||24,h=Zr(()=>{let k=f.toLowerCase();return k?t.filter(w=>w.title.toLowerCase().includes(k)||w.id.toLowerCase().includes(k)):t},[t,f]);qn(()=>{s(0),d(0)},[f]);let x=Math.max(5,Math.min(b-12,10));qn(()=>{d(k=>o<k?o:o>=k+x?o-x+1:k)},[o,x]);let S=h.slice(i,i+x);es((k,w)=>{if(w.escape){e(null);return}if(w.downArrow){s(M=>Math.min(M+1,h.length-1));return}if(w.upArrow){s(M=>Math.max(M-1,0));return}if(w.pageDown){s(M=>Math.min(M+x,h.length-1));return}if(w.pageUp){s(M=>Math.max(M-x,0));return}w.return&&h.length>0&&o>=0&&o<h.length&&e(h[o]),w.delete&&n&&h.length>0&&o>=0&&o<h.length&&n(h[o].id),!w.ctrl&&!w.meta&&k&&k.length===1&&!w.return&&!w.escape&&p(M=>M+k),(w.backspace||w.delete)&&p(M=>M.slice(0,-1))});let C=c(k=>{try{let w=new Date(k);return w.toLocaleDateString()+" "+w.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}catch{return k}},"formatDate");return De(qe,{flexDirection:"column",borderStyle:"single",borderColor:u.primary,paddingX:1,width:66,children:[De(qe,{children:[ye(pe,{bold:!0,color:u.primary,children:"Session History"}),ye(pe,{dimColor:!0,children:" type to filter / Enter resume / Del delete / Esc close"})]}),f&&De(qe,{paddingLeft:1,children:[ye(pe,{dimColor:!0,children:"Filter: "}),ye(pe,{color:u.warning,children:f})]}),S.length===0&&ye(qe,{paddingLeft:2,children:ye(pe,{dimColor:!0,children:"No sessions found."})}),S.map((k,w)=>{let M=i+w;return De(qe,{paddingLeft:1,children:[ye(pe,{color:M===o?u.primary:void 0,bold:M===o,children:M===o?"> ":" "}),ye(pe,{color:u.success,children:k.id}),De(pe,{dimColor:!0,children:[" ",k.title.slice(0,30)]}),De(pe,{color:u.muted,children:[" [",k.messageCount," msgs]"]}),De(pe,{dimColor:!0,children:[" ",C(k.updatedAt||k.createdAt)]})]},k.id)}),h.length>x&&ye(qe,{children:De(pe,{dimColor:!0,children:[" ",i+1,"-",Math.min(i+x,h.length)," / ",h.length]})})]})}c(Vn,"SessionPicker");import{useState as Xn,useEffect as Yn,useRef as ns}from"react";import{Box as ie,Text as fe,useInput as os}from"ink";import{jsx as ce,jsxs as me}from"react/jsx-runtime";var zn=15;function rs(t,e){let n=t.toLowerCase();return/\b(rm|del|delete|drop|truncate|format|mkfs)\b/.test(n)?{level:"CRITICAL",reason:"Destructive - may delete data"}:/\b(bash|shell|exec|cmd|powershell|terminal)\b/.test(n)?/\b(rm\s+-rf|sudo|chmod\s+777|curl.*\|\s*(ba)?sh|wget.*-O|>\/dev\/|mkfs)\b/i.test(e)?{level:"CRITICAL",reason:"High-risk command - system-level operation"}:{level:"HIGH",reason:"Shell command execution"}:/\b(write|edit|save|upload|deploy|publish)\b/i.test(n)?{level:"HIGH",reason:"File write operation"}:/\b(install|uninstall|npm|pip|cargo|gem|apt|brew)\b/i.test(n)?{level:"HIGH",reason:"Package manager operation"}:/\b(git)\b/.test(n)&&/\b(push|force|hard\s*reset|rebase)\b/i.test(e)?{level:"HIGH",reason:"Git destructive operation"}:/\b(http|fetch|curl|wget|api|request|download)\b/i.test(n)?{level:"MEDIUM",reason:"Network request"}:/\b(read|open|list|ls|dir|cat|view|search|find|grep|glob)\b/i.test(n)?{level:"LOW",reason:"Read-only operation"}:{level:"MEDIUM",reason:"Tool invocation"}}c(rs,"classifyRisk");var ss={CRITICAL:u.error,HIGH:u.warning,MEDIUM:u.warning,LOW:u.info},as={CRITICAL:"!",HIGH:"!",MEDIUM:"*",LOW:"~"};function is(t,e){if(/\b(bash|shell|exec|cmd|powershell|terminal)\b/i.test(t))return e.slice(0,200);if(/\b(write|edit|save|create)\b/i.test(t)){let n=e.match(/(?:file_path|path|file)["\s:=]+([^\s",}]+)/i);if(n)return"File: "+n[1]+`
|
|
65
|
+
`+e.slice(0,160)}return e.length>200?e.slice(0,200)+"...":e}c(is,"extractPreview");function Qn({toolName:t,payload:e,onAllow:n,onDeny:o,onAllowSession:s,onAutoMode:i,queuePosition:d}){let[f,p]=Xn(0),[m,b]=Xn(zn),h=ns(null),{level:x,reason:S}=rs(t,e),C=ss[x],k=is(t,e);Yn(()=>(b(zn),h.current=setInterval(()=>{b(g=>g<=1?(h.current&&clearInterval(h.current),0):g-1)},1e3),()=>{h.current&&clearInterval(h.current)}),[t,e]),Yn(()=>{m===0&&n()},[m,n]);let w=m<=5,M=[{label:"Allow \u2014 execute this command",hint:"Press Enter or y to confirm",action:n},{label:"Deny \u2014 cancel this operation",hint:"Press Esc or n to cancel",action:o},{label:"Allow always this session",hint:"Don't ask again for "+t,action:s},{label:"Auto mode \u2014 allow all hooks",hint:"All future hooks will be auto-approved",action:i}],a=c(()=>M[f].action(),"execSelected");os((g,l)=>{if(l.escape){o();return}if(l.upArrow){p(T=>T===0?3:T-1);return}if(l.downArrow){p(T=>T===3?0:T+1);return}if(l.return){a();return}if(g==="1"){n();return}if(g==="2"){o();return}if(g==="3"){s();return}if(g==="4"){i();return}if(g==="y"||g==="Y"){n();return}if(g==="n"||g==="N"){o();return}});let r=x==="CRITICAL"?u.error:u.warning;return me(ie,{flexDirection:"column",borderStyle:"round",borderColor:r,marginTop:1,children:[me(ie,{paddingLeft:1,paddingRight:1,justifyContent:"space-between",children:[me(ie,{gap:1,children:[ce(fe,{bold:!0,children:"Permission Required"}),me(fe,{dimColor:!0,children:["\xB7 ",t]}),d&&d.total>1&&me(fe,{dimColor:!0,children:["(",d.current,"/",d.total,")"]})]}),me(fe,{color:w?u.error:u.muted,children:["Auto ",m,"s"]})]}),me(ie,{paddingLeft:1,paddingRight:1,children:[me(fe,{color:C,bold:x==="CRITICAL",children:[as[x]," ",x]}),me(fe,{dimColor:!0,children:[" \u2014 ",S]})]}),k?ce(ie,{flexDirection:"column",paddingLeft:1,paddingRight:1,marginTop:1,children:ce(ie,{marginLeft:2,children:ce(fe,{color:u.tool,children:k})})}):null,x==="CRITICAL"&&ce(ie,{paddingLeft:1,paddingRight:1,marginTop:1,children:ce(fe,{color:u.error,children:"This may cause irreversible changes \u2014 verify carefully"})}),ce(ie,{flexDirection:"column",marginTop:1,children:M.map((g,l)=>me(ie,{flexDirection:"column",paddingLeft:1,paddingRight:1,children:[ce(ie,{children:me(fe,{color:f===l?u.brand:u.muted,children:[f===l?"\u276F":" "," ",l+1,". ",g.label]})}),ce(ie,{marginLeft:4,children:ce(fe,{dimColor:!0,children:g.hint})})]},l))}),ce(ie,{paddingLeft:1,paddingRight:1,marginTop:1,children:ce(fe,{dimColor:!0,children:"1/2/3/4 to select \xB7 \u2191\u2193 to navigate \xB7 Enter to confirm \xB7 Esc to deny"})})]})}c(Qn,"ApprovalModal");function Be(t){if(typeof t.data=="string")try{return JSON.parse(t.data)}catch{return{}}return t.data||{}}c(Be,"parseData");function Ne(t,e=""){return{id:`msg-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,type:t,content:e,thinking:"",steps:[],toolCalls:[],timestamp:Date.now()}}c(Ne,"createMessage");import{useCallback as cs}from"react";var Zn=200;function Wt(t,e){let n=[...t.messages,e];return n.length>Zn?n.slice(n.length-Zn):n}c(Wt,"appendMessage");function eo(t){let e=typeof t=="string"?t:JSON.stringify(t);for(let n=0;n<10;n++)try{let o=JSON.parse(e);if(o&&typeof o=="object"&&!Array.isArray(o)){if(typeof o.command=="string")return o.command;if(typeof o.command=="object"){e=JSON.stringify(o.command);continue}return JSON.stringify(o,null,2)}return e}catch{return e}return e}c(eo,"cleanArgs");function to(t,e){return cs(n=>{let o="",s="",i=[],d=[],f=null,p=!1;function m(){p=!1;let a=o;o="";let r=s;s="";let g=i;i=[];let l=d;d=[],!(!a&&!r&&g.length===0&&l.length===0)&&A(T=>{if(!T.currentMessage)return T;let y=T.currentMessage;a&&(y={...y,content:y.content+a}),r&&(y={...y,thinking:y.thinking+r});for(let E of g)y=E(y);for(let E of l)y=E(y);return{...T,currentMessage:y}})}c(m,"doStreamFlush");function b(){p||(p=!0,f=setTimeout(m,32))}c(b,"scheduleStreamFlush");function h(){f&&(clearTimeout(f),f=null),p=!1,m()}c(h,"flushNow");let x=0,S=0,C=!0,k=null,w=!1;function M(){w=!1;let a=k;if(!a)return;k=null;let r=Number(a.promptTokens)||0,g=Number(a.completionTokens)||0,l=Number(a.totalTokens)||0,T=Number(a.usageRatio)||0;if(l<=0)return;let y=Date.now(),E=0;if(S>0&&x>0&&y>S&&l>x){let W=l-x,_=(y-S)/1e3,D=W/_,P=xe().getState().tokenRate;E=P>0?P*.6+D*.4:D}x=l,S=y,A(W=>({...W,usage:{promptTokens:r,completionTokens:g,totalTokens:l,usageRatio:T},modelName:a.model||W.modelName,tokenRate:E}))}c(M,"flushToken"),n.on("start",()=>{V("evt",">> STREAM START"),h();let a=Ne("assistant");A(r=>({...r,currentMessage:a,messages:Wt(r,a),scrollOffset:0}))}),n.on("content",a=>{let r=typeof a.data=="string"?a.data:a.data?String(a.data):"";o+=r,b()}),n.on("thinking",a=>{s+=typeof a.data=="string"?a.data:"",b()}),n.on("tool_call",a=>{let r=Be(a);V("evt","tool_call: "+(r.name||"?")+(r.complete?" complete":" running")),i.push(g=>{let l=r.id?g.toolCalls.findIndex(y=>y.id===r.id):-1;l<0&&r.name&&(l=g.toolCalls.findIndex(y=>y.name===r.name&&y.status==="running"));let T=[...g.toolCalls];if(l>=0){let y={...T[l]};r.args&&(y.args=eo(r.args)),r.complete&&(y.status="complete"),r.result&&(y.result=r.result),T[l]=y}else T.push({id:r.id||(r.name?`${r.name}-${Date.now()}`:""),name:r.name||"",args:r.args?eo(r.args):void 0,status:r.complete?"complete":"running",complete:!!r.complete,timestamp:Date.now()});return{...g,toolCalls:T}}),b()}),n.on("tool_result",a=>{let r=Be(a);i.push(g=>{let l=[...g.toolCalls];for(let T=l.length-1;T>=0;T--)if(l[T].name===r.toolName&&!l[T].result){let y=l[T],E=y.timestamp?Math.floor((Date.now()-y.timestamp)/1e3):void 0;l[T]={...y,result:r.result||"",status:"complete",duration:E};break}return{...g,toolCalls:l}}),b()}),n.on("complete",()=>{V("evt",">> STREAM COMPLETE"),h(),A(a=>{if(!a.currentMessage)return a;let r=[...a.messages],g=a.currentMessage;for(let l=r.length-1;l>=0;l--)if(r[l].type==="assistant"&&r[l].id===g.id){r[l]=g;break}return{...a,currentMessage:null,messages:r}})}),n.on("error",a=>{let r=String(a.data||"Error");V("evt","error: "+r.slice(0,80)),A(g=>({...g,statusText:"Error: "+r.slice(0,120)}))}),n.on("step_start",a=>{let r={};if(typeof a.data=="string")try{r=JSON.parse(a.data)}catch{}else a.data&&typeof a.data=="object"&&(r=a.data);d.push(g=>{let l={id:r.id||"step-"+Date.now(),title:r.title||r.description||"",thought:r.thought,action:r.action,status:"running",tools:[],timestamp:Date.now()};return{...g,steps:[...g.steps,l]}}),b()}),n.on("step_thinking",a=>{let r={};if(typeof a.data=="string")try{r=JSON.parse(a.data)}catch{}else a.data&&typeof a.data=="object"&&(r=a.data);d.push(g=>{let l=[...g.steps],T=r.id?l.findIndex(y=>y.id===r.id):l.length-1;return T>=0&&(l[T]={...l[T],status:"thinking",thought:r.thought||l[T].thought}),{...g,steps:l}}),b()}),n.on("step_action",a=>{let r={};if(typeof a.data=="string")try{r=JSON.parse(a.data)}catch{}else a.data&&typeof a.data=="object"&&(r=a.data);d.push(g=>{let l=[...g.steps],T=r.id?l.findIndex(y=>y.id===r.id):l.length-1;return T>=0&&(l[T]={...l[T],status:"action",action:r.action||l[T].action}),{...g,steps:l}}),b()}),n.on("step_complete",a=>{let r={};if(typeof a.data=="string")try{r=JSON.parse(a.data)}catch{}else a.data&&typeof a.data=="object"&&(r=a.data);d.push(g=>{let l=[...g.steps],T=r.id?l.findIndex(y=>y.id===r.id):l.length-1;if(T>=0){let y=r.duration?Number(r.duration):l[T].timestamp?Math.floor((Date.now()-l[T].timestamp)/1e3):void 0;l[T]={...l[T],status:r.status==="error"?"error":"success",result:r.result,duration:y}}return{...g,steps:l}}),b()}),n.on("plan_start",()=>{V("evt",">> PLAN START"),h();let a=Ne("assistant");A(r=>({...r,planWaiting:!1,currentMessage:a,messages:Wt(r,a),scrollOffset:0}))}),n.on("plan_thinking",a=>{let r=typeof a.data=="string"?a.data:a.data?String(a.data):"";s+=r+`
|
|
66
|
+
`,b()}),n.on("plan_tasks",()=>{o+=`
|
|
67
|
+
Task list generated
|
|
68
|
+
`,b()}),n.on("plan_error",a=>{let r=String(a.data||"Plan failed");A(g=>({...g,planWaiting:!1,statusText:"Plan error: "+r.slice(0,120)}))}),n.on("plan_mode_enter",()=>{V("evt","plan_mode_enter"),A(a=>({...a,planMode:!0,statusText:"Entered plan mode"}))}),n.on("plan_mode_exit",()=>{V("evt","plan_mode_exit"),A(a=>({...a,planMode:!1,statusText:"Exited plan mode"}))}),n.on("plan_task_start",a=>{let r=Be(a);A(g=>{let l=[...g.planTasks],T=l.findIndex(y=>y.id===r.id);return T>=0?l[T]={...l[T],...r,status:"running",timestamp:Date.now()}:l.push({...r,status:"running",timestamp:Date.now()}),{...g,planTasks:l,pdcaPhase:g.pdcaPhase||"Do"}})}),n.on("plan_task_update",a=>{let r=Be(a);A(g=>{let l=[...g.planTasks],T=l.findIndex(y=>y.id===r.id);return T>=0&&(l[T]={...l[T],...r}),{...g,planTasks:l}})}),n.on("plan_task_result",a=>{let r=Be(a);A(g=>{let l=[...g.planTasks],T=l.findIndex(y=>y.id===r.id);if(T>=0){let y=l[T].timestamp?Math.floor((Date.now()-l[T].timestamp)/1e3):void 0;l[T]={...l[T],...r,status:r.status||"completed",duration:y}}else l.push({...r});return{...g,planTasks:l}})}),n.on("plan_complete",a=>{h();let r=a.status;V("evt",">> PLAN COMPLETE status="+(r||"none"));let g=typeof a.data=="string"?a.data:"";A(l=>{if(!l.currentMessage)return l;let T=[...l.messages],y=l.currentMessage;for(let E=T.length-1;E>=0;E--)if(T[E].type==="assistant"&&T[E].id===y.id){T[E]={...y,content:g||"Plan complete."};break}return{...l,currentMessage:null,messages:T,planWaiting:r==="waiting_confirm"}})}),n.on("token_update",a=>{let r={};if(typeof a.data=="string")try{r=JSON.parse(a.data)}catch{}else a.data&&typeof a.data=="object"&&(r=a.data);C=!1,(Number(r.totalTokens)||0)>0&&(k=r,w||(w=!0,setTimeout(M,100)))}),n.on("compaction_progress",a=>{let r={};if(typeof a.data=="string")try{r=JSON.parse(a.data)}catch{}else a.data&&typeof a.data=="object"&&(r=a.data);A(g=>({...g,compactionProgress:{stage:String(r.stage||""),percent:Number(r.percent)||0,message:String(r.message||"")}}))}),n.on("context_compressed",a=>{let r={};if(typeof a.data=="string")try{r=JSON.parse(a.data)}catch{}else a.data&&typeof a.data=="object"&&(r=a.data);let g=Number(r.originalCount)||0,l=Number(r.compressedCount)||0,T=Number(r.tokensSaved)||0,y=T>=1e3?(T/1e3).toFixed(1)+"K":String(T);A(E=>({...E,statusText:"Context compressed "+g+" to "+l+" messages, freed "+y+" tokens",usage:{...E.usage,usageRatio:Math.max(0,E.usage.usageRatio-.15)},compactionProgress:null}))}),n.on("hook_ask",a=>{let r=Be(a),g=r.toolName||"";V("evt","hook_ask: "+g+" autoMode="+String(xe().getState().autoMode));let l=r.approvalId||"",T=r.toolName||"";if(xe().getState().autoMode||e.current.has(T)){n.approveHook(l);return}t(y=>y.some(E=>E.approvalId===l)?y:[...y,{approvalId:l,toolName:r.toolName||"",payload:r.askPayload||r.payload||JSON.stringify(r)}])}),n.on("doctor_result",a=>{let r=String(a.data||""),g=r.length>300?r.slice(0,300)+"...":r,l=Ne("assistant",g);A(T=>({...T,messages:Wt(T,l),statusText:"Doctor diagnosis complete"}))}),n.on("degradation_update",a=>{let r={};if(typeof a.data=="string")try{r=JSON.parse(a.data)}catch{}else a.data&&typeof a.data=="object"&&(r=a.data);A(g=>({...g,degradation:{active:r.active||!1,retryCount:Number(r.retryCount)||0,maxRetries:Number(r.maxRetries)||0,mode:r.mode||"normal",message:r.message||""}}))}),n.on("todo_update",a=>{let r=String(a.data||"");A(g=>({...g,statusText:"TODO: "+r.slice(0,100)}))}),n.on("todo_item_done",a=>{let r=String(a.data||"");A(g=>({...g,statusText:"Done: "+r.slice(0,100)}))}),n.on("todo_progress",a=>{let r=String(a.data||"");A(g=>({...g,statusText:r.slice(0,100)}))}),n.on("workspace_changed",a=>{let r=String(a.data||"Workspace changed");A(g=>({...g,statusText:r.slice(0,100)}))}),n.on("generation_paused",()=>{A(a=>({...a,statusText:"Generation paused -- press ESC to resume or ESC ESC to stop"}))}),n.on("generation_resumed",()=>{A(a=>({...a,statusText:""}))}),n.on("notification",a=>{let r=String(a.data||"");A(g=>({...g,statusText:r,connected:r==="Reconnected."?!0:g.connected}))})},[t])}c(to,"useStreamHandlers");import{useRef as ls}from"react";import{useInput as ds}from"ink";function no(t){let{showApproval:e,showHelp:n,isGenerating:o,terminalRows:s,viewportHeight:i,paletteOpen:d,clientRef:f,onDenyApproval:p,onCloseHelp:m,setHelpScroll:b,onToggleHelp:h,onNewSession:x,onSessionHistory:S}=t,C=ls(0);ds((k,w)=>{if(w.f1||w.ctrl&&k==="h"){h?h():n&&m();return}if(w.ctrl&&k==="n"&&!e&&!o){x?.();return}if(w.ctrl&&k==="r"&&!e&&!o){S?.();return}if(w.escape){if(e)return;if(n){m();return}if(o){let r=Date.now(),g=C.current;C.current=r,g>0&&r-g<500?(f.current?.stop(),A(l=>({...l,statusText:"Stopped (ESC\xD72)"}))):(f.current?.pause(),A(l=>({...l,statusText:"Paused \u2014 press ESC again to stop"})));return}}if(w.ctrl&&k==="s"&&!e){o&&(f.current?.pause(),A(r=>({...r,statusText:"Paused (Ctrl+S). Press again to resume."})));return}if(w.ctrl&&k==="l"&&!e&&!o){A(r=>({...r,messages:[],currentMessage:null,scrollOffset:0,contentHeight:0,statusText:"Screen cleared"}));return}if(w.ctrl&&k==="o"){A(r=>({...r,toolCallsExpanded:!r.toolCallsExpanded}));return}if(w.ctrl&&k==="e"){A(r=>({...r,toolCallsExpanded:!r.toolCallsExpanded}));return}if(n){let r=wt.split(`
|
|
69
|
+
`),g=Math.max(5,s-12);if(w.pageUp||w.upArrow){b(l=>Math.min(l+(w.pageUp?g:1),r.length-1));return}if(w.pageDown||w.downArrow){b(l=>Math.max(0,l-(w.pageDown?g:1)));return}if(w.home){b(()=>r.length-1);return}if(w.end){b(()=>0);return}}let M=c(()=>{let{contentHeight:r}=xe().getState();return Math.max(0,r-i)},"maxOffsetLive"),a=Math.max(1,i-2);if(w.pageUp){A(r=>({...r,scrollOffset:Math.min(M(),r.scrollOffset+a)}));return}if(w.upArrow&&!e&&!d){A(r=>({...r,scrollOffset:Math.min(M(),r.scrollOffset+1)}));return}if(w.pageDown){A(r=>({...r,scrollOffset:Math.max(0,r.scrollOffset-a)}));return}if(w.downArrow&&!e&&!d){A(r=>({...r,scrollOffset:Math.max(0,r.scrollOffset-1)}));return}if(w.home){A(r=>({...r,scrollOffset:M()}));return}if(w.end){A(r=>({...r,scrollOffset:0}));return}if(w.tab){A(r=>({...r,planMode:!r.planMode,planWaiting:!1}));return}})}c(no,"useKeyboardInput");var us=null;function oo(t){us=t}c(oo,"setClient");import{jsx as L,jsxs as Se}from"react/jsx-runtime";function so(t){for(let e=t.length-1;e>=0;e--)if(t[e]==="@"){if(e>0&&/\w/.test(t[e-1]))continue;return e}return-1}c(so,"findAtTrigger");function $t({backendUrl:t,wsUrl:e,onExit:n}){let[o,s]=le(""),[i,d]=le(!1),[f,p]=le(!1),[m,b]=le(""),[h,x]=le([]),S=bt(null),C=bt([]),[k,w]=le(!1),[M,a]=le(0),[r,g]=le([]),l=r.length>0?r[0]:null,T=bt(new Set),y=bt(null),{stdout:E}=ps(),[W,_]=le(E?.rows||24),[D,P]=le(E?.columns||80);ro(()=>{let v=c(()=>{_(E?.rows||24),P(E?.columns||80)},"onResize");return E?.on?.("resize",v),()=>{E?.off?.("resize",v)}},[E]);let Ce=6,Pe=oe(v=>v.connected),Ie=oe(v=>v.planWaiting),Me=oe(v=>v.currentMessage!==null),$=oe(v=>v.messages.length),re=oe(v=>v.modelName),Ye=oe(v=>v.planMode),[on,rn]=le(!1),[yo,sn]=le([]);Cn();let an=to(g,T);ro(()=>{let v=new dt(t,e);return y.current=v,oo(v),an(v),v.connect().then(async()=>{try{let U=(await(await fetch(t+"/api/models")).json()).data?.models?.[0]?.name||"";A(ve=>({...ve,connected:!0,modelName:U}))}catch{A(j=>({...j,connected:!0}))}}).catch(j=>{A(N=>({...N,statusText:"Connection failed: "+j.message}))}),()=>{v.close()}},[t,e,an]);let cn=ee(v=>{let j=v.trim();if(!j||!y.current)return;s(""),w(!1),d(!1);let N=j.startsWith("/")?j.split(/\s+/):[],q=N[0]||null,U=N.slice(1).join(" ");if(q&&q in xt){let ve=xt[q];if(ve===null){w(!0),a(0);return}let{action:de,needsArg:ct}=ve,J=y.current;switch(de){case"__exit__":n();return;case"__confirm_plan":A(Q=>Q.planWaiting?(J?.planConfirm(),{...Q,planWaiting:!1}):Q);return;case"__cancel_plan":A(Q=>({...Q,planWaiting:!1}));return;case"plan_mode":A(Q=>({...Q,planMode:!Q.planMode}));return;case"auto_mode":V("app","auto_mode toggle"),A(Q=>({...Q,autoMode:!Q.autoMode}));return;case"clear":A(Q=>({...Q,messages:[],currentMessage:null,contentHeight:0,scrollOffset:0}));return;case"model_change":ct&&U&&J?.switchModel(U);return;case"show_context":A(Q=>({...Q,statusText:"Messages: "+Q.messages.length+" | Plan: "+(Q.planMode?"On":"Off")+" | Model: "+(Q.modelName||"N/A")}));return;case"stop":J?.stop();return;case"pause":J?.pause();return;case"resume":J?.resume();return;case"doctor":J?.doctor();return;case"rewind":J?.rewind();return;case"compact":J?.compact();return;case"init":J?.init();return;case"effort":U&&J?.effort(U);return;case"branch":U&&J?.branch(U);return;case"mcp":U&&J?.mcp(U);return;case"skills":J?.skills();return;case"agents":J?.agents();return;case"config":U&&J?.config(U);return;case"plugin":U&&J?.plugin(U);return;case"tokens":J?.send("tokens");return;case"memory":J?.send("memory");return;case"export":U&&J?.send("export",void 0,{path:U});return;case"checkpoint":J?.send("checkpoint");return;case"test":J?.send("test");return;case"lint":J?.send("lint");return;case"search":U&&J?.send("search",void 0,{query:U});return;case"project":J?.send("project");return}return}j.startsWith("/")&&!(q&&q in xt)||ko(j)},[n]),ko=ee(async v=>{let j=y.current;if(!j)return;let N=v,q=C.current;C.current=[];let U=[];for(let de of q)try{let ct=await j.readFileContent(de);if(ct){N=N.replace(de,"").trim();let J=de.split(".").pop()||"";U.push(`<context ref="${de}">
|
|
70
|
+
\`\`\`${J}
|
|
71
|
+
${ct}
|
|
72
|
+
\`\`\`
|
|
73
|
+
</context>`)}}catch{}U.length>0&&(N=U.join(`
|
|
74
|
+
|
|
75
|
+
`)+`
|
|
76
|
+
|
|
77
|
+
`+N.trim(),N=N.trim());let ve=Ne("user",N);A(de=>({...de,messages:[...de.messages,ve]})),j.chat(N,xe().getState().planMode)},[]),Co=ee(v=>{i||f||cn(v)},[cn,i,f]),Mo=ee(v=>{s(v),d(v.startsWith("/"));let j=so(v);if(j>=0){let N=v.slice(j+1);if(/^[\w.\-\\\/\s]*$/.test(N)&&N.length<200){b(N),p(!0),S.current&&clearTimeout(S.current),S.current=setTimeout(async()=>{let q=y.current;if(q){let U=await q.listFiles(N.trim()||void 0);x(U)}},150);return}}p(!1),b("")},[]),vo=ee(v=>{if(v){let j=so(o);if(j>=0){let N=o.slice(0,j)+v+" ";s(N),C.current.push(v)}}p(!1),b(""),x([])},[o]),Ao=ee(v=>{v?(s(v),d(!1)):(d(!1),s(""))},[]),it=ee(()=>{g(v=>v.length>1?v.slice(1):[])},[]),je=ee(v=>{y.current?.approveHook(v),it()},[it]),vt=ee(v=>{y.current?.denyHook(v),it()},[it]),ln=i||f||on,_o=ee(()=>{A(v=>({...v,messages:[],currentMessage:null,scrollOffset:0,contentHeight:0,statusText:"New session started"})),y.current&&y.current.send("create_session")},[]),Ro=ee(async()=>{if(y.current){let v=await y.current.listSessions();sn(v),rn(!0)}},[]),Do=ee(async v=>{if(rn(!1),!v||!y.current)return;let N=(await y.current.getSessionMessages(v.id)).map(q=>Ne(q.type==="chat"||q.type==="plan"?"user":"assistant",q.data||q.message||JSON.stringify(q)));A(q=>({...q,messages:N.length>0?N:q.messages,scrollOffset:0,contentHeight:0,statusText:`Loaded session: ${v.id} (${N.length} msgs)`}))},[]),Eo=ee(async v=>{if(y.current){await y.current.deleteSession(v);let j=await y.current.listSessions();sn(j)}},[]),Po=c(()=>{k?w(!1):(w(!0),a(0))},"handleToggleHelp");no({showApproval:l!==null,showHelp:k,isGenerating:Me,terminalRows:W,viewportHeight:Math.max(3,W-Ce),paletteOpen:ln,clientRef:y,onDenyApproval:c(()=>{l&&vt(l.approvalId)},"onDenyApproval"),onCloseHelp:c(()=>w(!1),"onCloseHelp"),setHelpScroll:a,onToggleHelp:Po,onNewSession:_o,onSessionHistory:Ro});let Io=ee(()=>{l&&je(l.approvalId)},[l,je]),Oo=ee(()=>{l&&vt(l.approvalId)},[l,vt]),Lo=ee(()=>{l&&(T.current.add(l.toolName),je(l.approvalId))},[l,je]),Bo=ee(()=>{V("app","autoMode button pressed"),A(v=>({...v,autoMode:!0})),l&&je(l.approvalId)},[l,je]);return Se(z,{flexDirection:"column",width:"100%",children:[Pe?Se(z,{flexDirection:"column",children:[$===0&&!Me&&!!re&&Se(z,{flexDirection:"column",flexShrink:0,marginBottom:1,children:[Se(z,{flexDirection:"column",paddingX:1,children:[L(z,{children:L(se,{color:u.primary,bold:!0,children:">_ JWCode v3.0.0"})}),Se(z,{children:[L(se,{dimColor:!0,children:"model: "}),L(se,{color:u.success,children:re||"connecting..."}),L(se,{dimColor:!0,children:" /model to change"})]}),Se(z,{children:[L(se,{dimColor:!0,children:"directory: "}),L(se,{color:u.warning,children:process.cwd()})]})]}),L(z,{paddingLeft:3,children:L(se,{dimColor:!0,children:"Tip: Type / for commands, @ to reference files"})}),L(z,{paddingLeft:3,children:L(se,{dimColor:!0,children:"TUI shortcuts: F1 help | Ctrl+L clear | Ctrl+S pause | Ctrl+N new | Ctrl+R history | Tab mode | PgUp/PgDn scroll"})})]},"welcome"),L(z,{flexGrow:ln?0:1,flexDirection:"column",children:L(Bt,{terminalCols:D,terminalRows:W})}),Se(z,{flexDirection:"row",borderStyle:"single",borderColor:u.primary,paddingLeft:1,children:[L(se,{color:u.success,bold:!0,children:"> "}),L(ze,{value:o,onChange:Mo,onSubmit:Co,placeholder:"Type a message or / for commands...",disabled:l!==null})]}),Se(z,{flexDirection:"column",children:[i&&L(Un,{filter:o,onSelect:Ao},"command-palette"),f&&L(Kn,{query:m,files:h,onSelect:vo},"file-palette"),on&&L(Vn,{sessions:yo,onSelect:Do,onDelete:Eo},"session-picker"),k&&(()=>{let v=wt.split("\\n"),j=Math.max(5,Math.min(W-12,10)),N=Math.max(0,v.length-M),q=Math.max(0,N-j),U=v.slice(q,N);return Se(z,{flexDirection:"column",borderStyle:"single",borderColor:u.primary,paddingX:1,children:[v.length>j&&L(z,{children:L(se,{dimColor:!0,children:" "+(q+1)+"-"+N+" / "+v.length+" PgUp/PgDn scroll / Esc close"})}),U.map((ve,de)=>L(se,{color:u.primary,children:ve},de))]},"help-box")})(),l&&L(Qn,{toolName:l.toolName,payload:l.payload,onAllow:Io,onDeny:Oo,onAllowSession:Lo,onAutoMode:Bo,queuePosition:{current:1,total:r.length}},"approval-modal")]})]}):Se(z,{flexDirection:"column",children:[L(z,{flexGrow:1,flexDirection:"column",children:L(Bt,{terminalCols:D,terminalRows:W})}),L(z,{paddingLeft:1,children:L(se,{dimColor:!0,children:"Connecting..."})})]}),L(yn,{},Ye?"status-plan":"status-act"),!Pe&&L(z,{height:1,children:L(se,{color:u.error,children:"Backend not connected -- WebSocket reconnecting."})}),Ie&&L(z,{height:1,children:L(se,{color:u.warning,bold:!0,children:"Plan ready -- /confirm to execute, /cancel to discard."})})]})}c($t,"App");import{useState as He,useCallback as fs,useEffect as ms}from"react";import{Box as We,Text as B,useInput as gs}from"ink";import{Fragment as hs,jsx as K,jsxs as te}from"react/jsx-runtime";var Ve={openai:{name:"OpenAI",baseUrl:"https://api.openai.com/v1",apiType:"openai-completions",defaultModel:"gpt-4o"},anthropic:{name:"Anthropic",baseUrl:"https://api.anthropic.com/v1",apiType:"anthropic-messages",defaultModel:"claude-sonnet-4-6"},deepseek:{name:"DeepSeek",baseUrl:"https://api.deepseek.com/v1",apiType:"openai-completions",defaultModel:"deepseek-chat"},moonshot:{name:"Moonshot / Kimi",baseUrl:"https://api.moonshot.cn/v1",apiType:"openai-completions",defaultModel:"moonshot-v1-8k"},qwen:{name:"\u901A\u4E49\u5343\u95EE (Qwen)",baseUrl:"https://dashscope.aliyuncs.com/compatible-mode/v1",apiType:"openai-completions",defaultModel:"qwen-plus"},zhipu:{name:"\u667A\u8C31 (GLM)",baseUrl:"https://open.bigmodel.cn/api/paas/v4",apiType:"openai-completions",defaultModel:"glm-4-plus"},baichuan:{name:"\u767E\u5DDD (Baichuan)",baseUrl:"https://api.baichuan-ai.com/v1",apiType:"openai-completions",defaultModel:"Baichuan4"},minimax:{name:"MiniMax / \u6D77\u87BA",baseUrl:"https://api.minimax.chat/v1",apiType:"openai-completions",defaultModel:"abab6.5s-chat"},doubao:{name:"\u8C46\u5305 (Doubao)",baseUrl:"https://ark.cn-beijing.volces.com/api/v3",apiType:"openai-completions",defaultModel:"doubao-pro-32k"},hunyuan:{name:"\u817E\u8BAF\u6DF7\u5143 (Hunyuan)",baseUrl:"https://api.hunyuan.cloud.tencent.com/v1",apiType:"openai-completions",defaultModel:"hunyuan-pro"},spark:{name:"\u8BAF\u98DE\u661F\u706B (Spark)",baseUrl:"https://spark-api-open.xf-yun.com/v1",apiType:"openai-completions",defaultModel:"generalv3.5"},custom:{name:"Custom (\u81EA\u5B9A\u4E49)",baseUrl:"",apiType:"openai-completions",defaultModel:""}},st=Object.keys(Ve),ao=c(({backendUrl:t,onComplete:e,onCancel:n,mode:o="fullscreen"})=>{let[s,i]=He("select_provider"),[d,f]=He(0),[p,m]=He(""),[b,h]=He(""),[x,S]=He(""),[C,k]=He(""),[w,M]=He(""),a=o==="modal",r=fs(async()=>{i("saving");try{let W=Ve[p],_=x||W.baseUrl,D=await fetch(`${t}/api/config/provider`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({provider:p,baseUrl:_,apiType:W.apiType,apiKey:b,setDefault:!0,models:[{id:C,name:C,enabled:!0,priority:10}]})});if(!D.ok){let P=await D.text();throw new Error(P||`HTTP ${D.status}`)}i("done"),setTimeout(e,1200)}catch(W){M(String(W)),i("error")}},[t,p,x,b,C,e]);ms(()=>{if(s==="done")return;c(async()=>{try{(await(await fetch(`${t}/api/config/provider`)).json())?.data?.configured&&e()}catch{}},"check")()},[s,t,e]),gs((W,_)=>{if(_.escape&&a&&n){n();return}if(s==="select_provider"){if(_.upArrow||W==="k")f(D=>(D-1+st.length)%st.length);else if(_.downArrow||W==="j")f(D=>(D+1)%st.length);else if(_.return){let D=st[d];m(D);let P=Ve[D];S(P.baseUrl),k(P.defaultModel),i("enter_key")}return}if(s==="error"){if(_.escape&&a&&n){n();return}_.return&&(i("enter_key"),M(""))}});let g=c(()=>{if(b.trim().length<10){M("API key too short (min 10 characters).");return}M(""),p==="custom"?i("enter_model"):r()},"handleKeySubmit"),l=c(()=>{if(C.trim().length===0){M("Model name is required.");return}r()},"handleModelSubmit");if(s==="done")return te(We,{flexDirection:"column",padding:1,children:[K(B,{color:u.success,children:"\u2713 Configuration saved!"}),o==="fullscreen"&&K(B,{dimColor:!0,children:"Starting JWCode..."})]});let T=a?"single":"round",y=a?u.warning:u.primary,E=a?"Add Provider":"JWCode \u2014 First Run Setup";return te(We,{flexDirection:"column",padding:1,borderStyle:T,borderColor:y,children:[K(We,{marginBottom:1,children:K(B,{bold:!0,color:u.primary,children:E})}),o==="fullscreen"&&te(hs,{children:[K(B,{children:"Configure your AI provider to get started."}),K(B,{children:" "})]}),a&&n&&K(B,{dimColor:!0,children:"Esc to close"}),s==="select_provider"&&te(We,{flexDirection:"column",children:[K(B,{children:"Select provider (\u2191\u2193 to navigate, Enter to confirm):"}),st.map((W,_)=>te(B,{children:[_===d?"\u276F ":" ",K(B,{color:_===d?"green":void 0,bold:_===d,children:Ve[W].name}),_===d?" \u25C0":""]},W))]}),s==="enter_key"&&te(We,{flexDirection:"column",children:[te(B,{children:["Provider: ",K(B,{color:u.success,children:Ve[p].name})]}),te(B,{children:["Base URL: ",K(B,{color:u.info,children:x})]}),K(B,{children:" "}),K(B,{children:"Enter API Key:"}),K(ze,{value:b,onChange:h,onSubmit:g,placeholder:"sk-..."}),K(B,{dimColor:!0,children:"Press Enter to confirm"}),w&&te(B,{color:u.error,children:["Error: ",w]})]}),s==="enter_model"&&te(We,{flexDirection:"column",children:[te(B,{children:["Provider: ",K(B,{color:u.success,children:Ve[p].name})]}),te(B,{children:["API Key: ",K(B,{color:u.success,children:"configured"})]}),K(B,{children:" "}),te(B,{children:["Base URL: ",K(B,{color:u.info,children:x})]}),K(B,{children:" "}),K(B,{children:"Model ID:"}),K(ze,{value:C,onChange:k,onSubmit:l,placeholder:"model-name"}),K(B,{dimColor:!0,children:"Press Enter to confirm, type to edit model name"}),w&&te(B,{color:u.error,children:["Error: ",w]})]}),s==="saving"&&K(B,{color:u.warning,children:"Saving configuration to ~/.jwcode/config.yaml..."}),s==="error"&&te(We,{flexDirection:"column",children:[te(B,{color:u.error,children:["Failed to save: ",w]}),te(B,{children:["Press Enter to retry",a?", Esc to cancel":"","."]})]})]})},"SetupWizard");Ct();import{existsSync as wo}from"node:fs";import{join as To}from"node:path";import{readFileSync as As,existsSync as _s}from"node:fs";import{join as mo}from"node:path";import{homedir as Rs}from"node:os";var Xe={backend_url:"http://localhost:8080",ws_url:"ws://localhost:8081/ws",ws_auth_token:"default-token",workspace_dir:""};function Ds(t){let e={};for(let n of t.split(`
|
|
78
|
+
`)){let o=n.trim();if(!o||o.startsWith("#"))continue;let s=o.indexOf(":");if(s===-1)continue;let i=o.slice(0,s).trim(),d=o.slice(s+1).trim();if(typeof d=="string")if(d==="true"||d==="false")d=d==="true";else if(/^\d+$/.test(d))d=parseInt(d);else{let f=d.match(/^["'](.*)["']$/);f&&(d=f[1])}e[i]=d}return e}c(Ds,"parseYaml");function go(){let t=mo(Rs(),".jwcode"),e=mo(t,"config.yaml");if(!_s(e))return{...Xe};try{let n=As(e,"utf-8"),o=Ds(n);return{backend_url:o.backend_url||Xe.backend_url,ws_url:o.ws_url||Xe.ws_url,ws_auth_token:o.ws_auth_token||Xe.ws_auth_token,workspace_dir:o.workspace_dir||Xe.workspace_dir}}catch{return{...Xe}}}c(go,"loadConfig");["stdout","stderr"].forEach(t=>{let e=process[t];e&&e.on("error",n=>{n.code==="EPIPE"||n.code})});process.on("uncaughtException",t=>{t.code==="EPIPE"||t.code==="ECONNRESET"||(console.error("Fatal error:",t),process.exit(1))});var Mt="3.0.0";function bo(){console.log(`JWCode CLI v${Mt}`),console.log(""),console.log("Usage:"),console.log(" jwcode [options] Start backend + interactive terminal (default)"),console.log(" jwcode start [options] Start backend + interactive terminal"),console.log(" jwcode run [options] Connect to existing backend"),console.log(" jwcode stop Stop running daemon"),console.log(" jwcode update Check for updates / update CLI"),console.log(" jwcode version Print version"),console.log(""),console.log("Options:"),console.log(" -p, --port <port> HTTP port (default: auto, first available from 8080)"),console.log(" -w, --workspace <dir> Workspace directory (default: current directory)"),console.log(" -F, --force Kill existing process on target port before starting"),console.log(" -B, --build Force rebuild backend (dev mode only)"),console.log(" -b, --backend <url> Backend URL (run mode, WS auto-derived)"),console.log(" --ws <url> Override WebSocket URL"),console.log(""),console.log("Environment:"),console.log(" JWCODE_THEME=dark|light Color theme (default: dark)"),console.log(""),console.log("Keyboard shortcuts (in TUI):"),console.log(" / Open command palette"),console.log(" Tab Toggle Plan/Act mode"),console.log(" F1 Toggle help panel"),console.log(" Ctrl+N New session"),console.log(" Ctrl+R Session history picker"),console.log(" Ctrl+L Clear screen"),console.log(" Ctrl+S Pause/resume generation"),console.log(" Ctrl+E Toggle expand all tool calls"),console.log(" Up/Down Browse input history (last 30)"),console.log(" PgUp/PgDn Scroll message history"),console.log(" Home/End Jump to oldest/newest message"),console.log(" Esc Close palette / pause gen. / deny approval")}c(bo,"printUsage");function Js(){let t={},e=process.argv.slice(2);for(let n=0;n<e.length;n++){let o=e[n];o==="--build"||o==="-B"?t.build=!0:o==="--force"||o==="-F"?t.force=!0:o==="--port"||o==="-p"?t.port=e[++n]:o==="--workspace"||o==="-w"?t.workspace=e[++n]:o==="--backend"||o==="-b"?t.backend=e[++n]:o==="--ws"?t.ws=e[++n]:o.startsWith("-")||(t._cmd=o)}return t}c(Js,"parseArgs");async function Fs(t){let e=!!t.force,n=!!t.build,o=String(t.workspace||process.cwd()),s=Gt(),i=Yt(o);if(i){let x=i.httpPort,S=i.wsPort;console.log("[daemon] Reusing existing daemon (PID "+i.pid+") on ports "+x+"/"+S),kt(i.pid,x,S,o),await So(x,S,o,s);return}let d,f;if(t.port)d=parseInt(String(t.port),10),f=d+1,!e&&($e(d)||$e(f))&&(console.error(`[launcher] Port ${d} or ${f} is already in use.`),console.error("[launcher] Use --force to replace the existing process, or omit -p for auto port selection."),process.exit(1));else{let x=qt(8080);d=x.httpPort,f=x.wsPort}console.log("\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\u2557"),console.log("\u2551 JWCode \u2014 Java AI Coding Tool \u2551"),console.log("\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\u255D"),console.log(` Workspace: ${o}`),console.log(` HTTP API: http://localhost:${d}`),console.log(` WebSocket: ws://localhost:${f}/ws`),(n||!ke(s))&&(wo(To(s,"pom.xml"))?yt(s):ke(s)||(console.error("[launcher] Backend JAR not found and no pom.xml for build."),console.error("[launcher] Install via: npm install -g @jwcode/cli"),process.exit(1))),(n||!ke(s))&&(wo(To(s,"pom.xml"))?yt(s):ke(s)||(console.error("[launcher] Backend JAR not found. Run: npm install -g @jwcode/cli"),process.exit(1)));let p=Xt({installDir:s,workspaceDir:o,port:d,wsPort:f,forceKill:e,idleTimeout:300}),m=!1,b=c(()=>{m||(m=!0,process.stdout.write("\x1B[?1049l"),console.log(`
|
|
79
|
+
[jwcode] Closing. Daemon continues running in background.`),console.log("[daemon] Stop it with: jwcode stop"))},"cleanup");process.on("SIGINT",()=>{b(),process.exit(0)}),process.on("SIGTERM",()=>{b(),process.exit(0)}),process.on("exit",b),await Vt(d),Promise.resolve().then(()=>(en(),Zt)).then(({checkForUpdates:x})=>{x(Mt).then(S=>{S?.updateAvailable&&(console.log(`
|
|
80
|
+
Update available: v${S.current} \u2192 v${S.latest}`),console.log(` Run 'jwcode update' (or '${S.url}') to upgrade.
|
|
81
|
+
`))}).catch(()=>{})});let h=setInterval(()=>{Promise.resolve().then(()=>(Ct(),zt)).then(({readDaemonInfo:x,isDaemonAlive:S,startDaemon:C,writeDaemonInfo:k})=>{let w=x();if(w&&!S(w)){console.error(`
|
|
82
|
+
[daemon] Daemon process died. Attempting auto-restart...`);try{let M=C({installDir:s,workspaceDir:o,port:d,wsPort:f,forceKill:!0,idleTimeout:300});k(M.pid,d,f,o),console.error("[daemon] Daemon restarted successfully.")}catch(M){console.error("[daemon] Auto-restart failed:",String(M))}}}).catch(()=>{})},3e4);await So(d,f,o,s),clearInterval(h)}c(Fs,"cmdStart");async function So(t,e,n,o){let s=`http://localhost:${t}`,i=`ws://localhost:${e}/ws`;process.stdout.write("\x1B[?1049h"),process.stdout.write("\x1B[2J\x1B[H");let d=!1;try{d=(await(await fetch(`${s}/api/config/provider`)).json())?.data?.configured===!0}catch{}d||await new Promise(p=>{let{unmount:m}=tn(nn(ao,{backendUrl:s,onComplete:c(()=>{m(),p()},"onComplete")}))});let{unmount:f}=tn(nn($t,{backendUrl:s,wsUrl:i,onExit:c(()=>{process.stdout.write("\x1B[?1049l"),process.exit(0)},"onExit")}));await new Promise(p=>{process.on("SIGINT",()=>p()),process.on("SIGTERM",()=>p())}),process.stdout.write("\x1B[?1049l")}c(So,"startTUI");async function Gs(t){process.stdin.isTTY||(console.error("Error: jwcode requires a real terminal (TTY)."),console.error("Please run from a terminal emulator like Windows Terminal, CMD, or PowerShell."),process.exit(1)),process.env.JWCODE_NO_BRACKETED_PASTE!=="1"&&process.stdout.write("\x1B[?2004h");let e=go(),n=String(t.backend||e.backend_url),o=String(t.ws||n.replace(/^http/,"ws").replace(/:(\d+)/,(d,f)=>":"+(parseInt(f)+1))+"/ws"||e.ws_url);process.stdout.write("\x1B[?1049h"),process.stdout.write("\x1B[2J\x1B[H");let{unmount:s,waitUntilExit:i}=tn(nn($t,{backendUrl:n,wsUrl:o,onExit:c(()=>{process.stdout.write("\x1B[?1049l"),process.exit(0)},"onExit")}));await i(),process.stdout.write("\x1B[?1049l")}c(Gs,"cmdRun");async function Ks(){let{execSync:t}=await import("node:child_process"),{readDaemonInfo:e,clearDaemonInfo:n,isDaemonAlive:o}=await Promise.resolve().then(()=>(Ct(),zt)),s=e();if(!s){console.log("No daemon is running.");return}if(!o(s)){console.log("Daemon process is already dead. Cleaning up."),n();return}console.log("Stopping JWCode daemon (PID "+s.pid+")...");try{process.platform==="win32"?t("taskkill /F /T /PID "+s.pid,{stdio:"ignore"}):process.kill(s.pid,"SIGTERM"),n(),console.log("Daemon stopped.")}catch(i){console.error("Failed to stop daemon:",String(i))}}c(Ks,"cmdStop");async function qs(){let{checkForUpdates:t,runNpmUpdate:e}=await Promise.resolve().then(()=>(en(),Zt));console.log("Checking for updates...");let n=await t(Mt);if(!n){console.log("Could not check for updates. Try again later or install manually:"),console.log(" npm install -g @jwcode/cli@latest");return}if(!n.updateAvailable){console.log(`JWCode CLI is up to date (v${n.current}).`);return}if(console.log(`Update available: v${n.current} \u2192 v${n.latest}`),console.log(""),n.body){let i=n.body.split(`
|
|
83
|
+
`).filter(d=>d.trim()).slice(0,3);for(let d of i)console.log(" "+d);console.log("")}let s=(await import("node:readline")).createInterface({input:process.stdin,output:process.stdout});s.question("Update now? [Y/n] ",i=>{if(s.close(),i.toLowerCase()==="n"||i.toLowerCase()==="no"){console.log('Skipped. Run "jwcode update" later or: npm install -g @jwcode/cli@latest'),process.exit(0);return}console.log("Updating...");let d=e();d.ok?console.log("Updated successfully. Restart jwcode to use the new version."):(console.error("Update failed:",d.message),console.log("Try manually: npm install -g @jwcode/cli@latest")),process.exit(d.ok?0:1)})}c(qs,"cmdUpdate");async function Vs(){if(process.argv.includes("--help")||process.argv.includes("-h")||process.argv.includes("help")){bo();return}if(process.argv.includes("--version")||process.argv.includes("-v")||process.argv.includes("version")){console.log(`JWCode CLI v${Mt}`);return}let t=Js(),e=t._cmd||"start";switch(e){case"start":await Fs(t);break;case"run":await Gs(t);break;case"stop":await Ks();break;case"update":await qs();break;default:console.error(`Unknown command: ${e}`),bo(),process.exit(1)}}c(Vs,"main");Vs().catch(t=>{console.error("Fatal error:",t),process.exit(1)});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command definitions — Chinese descriptions.
|
|
3
|
+
* Local commands handled by TUI; WS commands sent to backend.
|
|
4
|
+
*/
|
|
5
|
+
export interface CmdEntry {
|
|
6
|
+
cmd: string;
|
|
7
|
+
desc: string;
|
|
8
|
+
via: 'local' | 'ws';
|
|
9
|
+
action: string | null;
|
|
10
|
+
}
|
|
11
|
+
export declare const LOCAL_COMMANDS: CmdEntry[];
|
|
12
|
+
export declare const WS_COMMANDS: CmdEntry[];
|
|
13
|
+
export declare const ALL_COMMANDS: CmdEntry[];
|
|
14
|
+
export declare const SLASH_COMMANDS: Record<string, {
|
|
15
|
+
action: string;
|
|
16
|
+
needsArg?: boolean;
|
|
17
|
+
} | null>;
|
|
18
|
+
export declare const HELP_TEXT = "\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\u2557\n\u2551 JWCode \u547D\u4EE4\u5E2E\u52A9 \u2551\n\u2560\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\u2563\n\u2551 \u672C\u5730\u547D\u4EE4: \u2551\n\u2551 /help \u663E\u793A\u6B64\u5E2E\u52A9\u4FE1\u606F \u2551\n\u2551 /plan \u5207\u6362\u89C4\u5212\u6A21\u5F0F \u2551\n\u2551 /auto \u5207\u6362\u81EA\u52A8\u6A21\u5F0F \u2551\n\u2551 /context \u663E\u793A\u5F53\u524D\u4F1A\u8BDD\u72B6\u6001 \u2551\n\u2551 /exit \u9000\u51FA JWCode \u2551\n\u2560\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\u2563\n\u2551 \u540E\u7AEF\u547D\u4EE4: \u2551\n\u2551 /confirm \u786E\u8BA4\u6267\u884C\u5F53\u524D\u89C4\u5212 \u2551\n\u2551 /cancel \u53D6\u6D88\u5F53\u524D\u89C4\u5212 \u2551\n\u2551 /stop \u505C\u6B62\u5F53\u524D AI \u751F\u6210 \u2551\n\u2551 /pause \u6682\u505C\u5F53\u524D AI \u751F\u6210 \u2551\n\u2551 /resume \u6062\u590D\u6682\u505C\u7684\u751F\u6210 \u2551\n\u2551 /clear \u6E05\u9664\u5F53\u524D\u4F1A\u8BDD\u6D88\u606F \u2551\n\u2551 /model <\u540D> \u5207\u6362 AI \u6A21\u578B \u2551\n\u2551 /compact \u538B\u7F29\u4F1A\u8BDD\u4E0A\u4E0B\u6587 \u2551\n\u2551 /doctor \u7CFB\u7EDF\u81EA\u8BCA\u65AD \u2551\n\u2551 /rewind \u56DE\u6EDA\u5230\u6700\u8FD1\u68C0\u67E5\u70B9 \u2551\n\u2551 /init \u751F\u6210\u9879\u76EE JWCODE.md \u2551\n\u2551 /effort <\u7EA7> \u8BBE\u7F6E\u52AA\u529B\u7EA7\u522B low/med/high \u2551\n\u2551 /branch <\u540D> \u521B\u5EFA\u5206\u652F\u4F1A\u8BDD \u2551\n\u2551 /mcp <\u64CD\u4F5C> MCP \u670D\u52A1\u5668\u7BA1\u7406 \u2551\n\u2551 /skills \u67E5\u770B Skills \u5217\u8868 \u2551\n\u2551 /agents \u5217\u51FA Agent \u4EE3\u7406 \u2551\n\u2551 /config <\u64CD> \u7BA1\u7406\u914D\u7F6E (get/set/list) \u2551\n\u2551 /plugin <\u64CD> \u63D2\u4EF6\u7BA1\u7406 \u2551\n\u2560\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\u2563\n\u2551 \u5FEB\u6377\u952E: \u2551\n\u2551 \u2191\u2193 \u6D4F\u89C8\u8F93\u5165\u5386\u53F2 (\u6700\u8FD130\u6761) \u2551\n\u2551 PgUp/PgDn \u7FFB\u9875\u6D4F\u89C8\u6D88\u606F \u2551\n\u2551 Home/End \u8DF3\u5230\u6700\u65E9/\u6700\u65B0\u6D88\u606F \u2551\n\u2551 Tab \u5207\u6362 Plan/Act \u6A21\u5F0F \u2551\n\u2551 / \u6253\u5F00\u547D\u4EE4\u9762\u677F (\u53EF\u7FFB\u9875) \u2551\n\u2551 Esc \u5173\u95ED\u9762\u677F/\u53D6\u6D88\u5BA1\u6279 \u2551\n\u2560\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\u2563\n\u2551 \u666E\u901A\u8F93\u5165\u5373\u53D1\u9001\u804A\u5929\u6D88\u606F \u2551\n\u2551 \u8F93\u5165\u6846\u663E\u793A\u5B57\u7B26\u6570+token\u4F30\u7B97 \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\u255D";
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// Local TUI commands
|
|
2
|
+
export const LOCAL_COMMANDS = [
|
|
3
|
+
{ cmd: '/help', desc: '显示所有命令', via: 'local', action: null },
|
|
4
|
+
{ cmd: '/plan', desc: '切换规划模式 (先规划再执行)', via: 'local', action: 'plan_mode' },
|
|
5
|
+
{ cmd: '/auto', desc: '切换自动模式 (自动批准工具执行)', via: 'local', action: 'auto_mode' },
|
|
6
|
+
{ cmd: '/context', desc: '显示当前会话状态', via: 'local', action: 'show_context' },
|
|
7
|
+
{ cmd: '/exit', desc: '退出 JWCode', via: 'local', action: '__exit__' },
|
|
8
|
+
];
|
|
9
|
+
// WebSocket commands sent to backend
|
|
10
|
+
export const WS_COMMANDS = [
|
|
11
|
+
{ cmd: '/confirm', desc: '确认当前规划并开始执行', via: 'ws', action: '__confirm_plan' },
|
|
12
|
+
{ cmd: '/cancel', desc: '取消当前规划', via: 'ws', action: '__cancel_plan' },
|
|
13
|
+
{ cmd: '/stop', desc: '停止当前 AI 生成', via: 'ws', action: 'stop' },
|
|
14
|
+
{ cmd: '/pause', desc: '暂停当前 AI 生成', via: 'ws', action: 'pause' },
|
|
15
|
+
{ cmd: '/resume', desc: '恢复暂停的 AI 生成', via: 'ws', action: 'resume' },
|
|
16
|
+
{ cmd: '/clear', desc: '清除当前会话消息', via: 'ws', action: 'clear' },
|
|
17
|
+
{ cmd: '/doctor', desc: '运行系统自诊断', via: 'ws', action: 'doctor' },
|
|
18
|
+
{ cmd: '/rewind', desc: '回滚到最近的检查点', via: 'ws', action: 'rewind' },
|
|
19
|
+
{ cmd: '/compact', desc: '压缩会话上下文 (释放 token)', via: 'ws', action: 'compact' },
|
|
20
|
+
{ cmd: '/model', desc: '切换 AI 模型 (用法: /model <模型名>)', via: 'ws', action: 'model_change' },
|
|
21
|
+
{ cmd: '/init', desc: '分析项目并生成 JWCODE.md 项目记忆文件', via: 'ws', action: 'init' },
|
|
22
|
+
{ cmd: '/effort', desc: '设置任务努力级别 (low/medium/high)', via: 'ws', action: 'effort' },
|
|
23
|
+
{ cmd: '/branch', desc: '创建分支会话 (用法: /branch <名称>)', via: 'ws', action: 'branch' },
|
|
24
|
+
{ cmd: '/mcp', desc: 'MCP 服务器管理 (list/add/remove)', via: 'ws', action: 'mcp' },
|
|
25
|
+
{ cmd: '/skills', desc: '查看可用 Skills 列表', via: 'ws', action: 'skills' },
|
|
26
|
+
{ cmd: '/agents', desc: '列出配置的 Agent 代理', via: 'ws', action: 'agents' },
|
|
27
|
+
{ cmd: '/config', desc: '管理配置 (get/set/list)', via: 'ws', action: 'config' },
|
|
28
|
+
{ cmd: '/plugin', desc: '插件管理 (install/list/remove)', via: 'ws', action: 'plugin' },
|
|
29
|
+
];
|
|
30
|
+
export const ALL_COMMANDS = [...LOCAL_COMMANDS, ...WS_COMMANDS];
|
|
31
|
+
// Action map: slash command -> { action, needsArg? }
|
|
32
|
+
export const SLASH_COMMANDS = {
|
|
33
|
+
'/help': null,
|
|
34
|
+
'/plan': { action: 'plan_mode' },
|
|
35
|
+
'/auto': { action: 'auto_mode' },
|
|
36
|
+
'/context': { action: 'show_context' },
|
|
37
|
+
'/exit': { action: '__exit__' },
|
|
38
|
+
'/quit': { action: '__exit__' },
|
|
39
|
+
'/confirm': { action: '__confirm_plan' },
|
|
40
|
+
'/cancel': { action: '__cancel_plan' },
|
|
41
|
+
'/stop': { action: 'stop' },
|
|
42
|
+
'/pause': { action: 'pause' },
|
|
43
|
+
'/resume': { action: 'resume' },
|
|
44
|
+
'/clear': { action: 'clear' },
|
|
45
|
+
'/doctor': { action: 'doctor' },
|
|
46
|
+
'/rewind': { action: 'rewind' },
|
|
47
|
+
'/compact': { action: 'compact' },
|
|
48
|
+
'/model': { action: 'model_change', needsArg: true },
|
|
49
|
+
'/init': { action: 'init' },
|
|
50
|
+
'/effort': { action: 'effort', needsArg: true },
|
|
51
|
+
'/branch': { action: 'branch', needsArg: true },
|
|
52
|
+
'/mcp': { action: 'mcp', needsArg: true },
|
|
53
|
+
'/skills': { action: 'skills' },
|
|
54
|
+
'/agents': { action: 'agents' },
|
|
55
|
+
'/config': { action: 'config', needsArg: true },
|
|
56
|
+
'/plugin': { action: 'plugin', needsArg: true },
|
|
57
|
+
};
|
|
58
|
+
export const HELP_TEXT = `
|
|
59
|
+
╔══════════════════════════════════════════╗
|
|
60
|
+
║ JWCode 命令帮助 ║
|
|
61
|
+
╠══════════════════════════════════════════╣
|
|
62
|
+
║ 本地命令: ║
|
|
63
|
+
║ /help 显示此帮助信息 ║
|
|
64
|
+
║ /plan 切换规划模式 ║
|
|
65
|
+
║ /auto 切换自动模式 ║
|
|
66
|
+
║ /context 显示当前会话状态 ║
|
|
67
|
+
║ /exit 退出 JWCode ║
|
|
68
|
+
╠══════════════════════════════════════════╣
|
|
69
|
+
║ 后端命令: ║
|
|
70
|
+
║ /confirm 确认执行当前规划 ║
|
|
71
|
+
║ /cancel 取消当前规划 ║
|
|
72
|
+
║ /stop 停止当前 AI 生成 ║
|
|
73
|
+
║ /pause 暂停当前 AI 生成 ║
|
|
74
|
+
║ /resume 恢复暂停的生成 ║
|
|
75
|
+
║ /clear 清除当前会话消息 ║
|
|
76
|
+
║ /model <名> 切换 AI 模型 ║
|
|
77
|
+
║ /compact 压缩会话上下文 ║
|
|
78
|
+
║ /doctor 系统自诊断 ║
|
|
79
|
+
║ /rewind 回滚到最近检查点 ║
|
|
80
|
+
║ /init 生成项目 JWCODE.md ║
|
|
81
|
+
║ /effort <级> 设置努力级别 low/med/high ║
|
|
82
|
+
║ /branch <名> 创建分支会话 ║
|
|
83
|
+
║ /mcp <操作> MCP 服务器管理 ║
|
|
84
|
+
║ /skills 查看 Skills 列表 ║
|
|
85
|
+
║ /agents 列出 Agent 代理 ║
|
|
86
|
+
║ /config <操> 管理配置 (get/set/list) ║
|
|
87
|
+
║ /plugin <操> 插件管理 ║
|
|
88
|
+
╠══════════════════════════════════════════╣
|
|
89
|
+
║ 快捷键: ║
|
|
90
|
+
║ ↑↓ 浏览输入历史 (最近30条) ║
|
|
91
|
+
║ PgUp/PgDn 翻页浏览消息 ║
|
|
92
|
+
║ Home/End 跳到最早/最新消息 ║
|
|
93
|
+
║ Tab 切换 Plan/Act 模式 ║
|
|
94
|
+
║ / 打开命令面板 (可翻页) ║
|
|
95
|
+
║ Esc 关闭面板/取消审批 ║
|
|
96
|
+
╠══════════════════════════════════════════╣
|
|
97
|
+
║ 普通输入即发送聊天消息 ║
|
|
98
|
+
║ 输入框显示字符数+token估算 ║
|
|
99
|
+
╚══════════════════════════════════════════╝`;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* ApprovalModal — permission prompt in Claude Code style.
|
|
4
|
+
* Arrow keys to select, Enter to confirm, Esc to cancel.
|
|
5
|
+
*/
|
|
6
|
+
import { useState } from 'react';
|
|
7
|
+
import { Box, Text, useInput } from 'ink';
|
|
8
|
+
export function ApprovalModal({ toolName, payload, onAllow, onDeny }) {
|
|
9
|
+
const [selected, setSelected] = useState(0); // 0=allow, 1=deny
|
|
10
|
+
useInput((_input, key) => {
|
|
11
|
+
if (key.escape || key.tab) {
|
|
12
|
+
onDeny();
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if (key.upArrow || key.downArrow) {
|
|
16
|
+
setSelected(prev => prev === 0 ? 1 : 0);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (key.return) {
|
|
20
|
+
if (selected === 0)
|
|
21
|
+
onAllow();
|
|
22
|
+
else
|
|
23
|
+
onDeny();
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (_input === '1') {
|
|
27
|
+
onAllow();
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (_input === '2') {
|
|
31
|
+
onDeny();
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (_input === 'y' || _input === 'Y') {
|
|
35
|
+
onAllow();
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (_input === 'n' || _input === 'N') {
|
|
39
|
+
onDeny();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
const desc = payload
|
|
44
|
+
? (payload.length > 200 ? payload.slice(0, 200) + '...' : payload)
|
|
45
|
+
: '';
|
|
46
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 2, paddingY: 1, marginTop: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, children: "Do you want to proceed?" }) }), _jsxs(Box, { flexDirection: "column", marginLeft: 2, marginBottom: 1, children: [_jsx(Box, { children: _jsxs(Text, { color: selected === 0 ? 'green' : undefined, children: [selected === 0 ? ' ❯' : ' ', " 1. Allow"] }) }), _jsx(Box, { children: _jsxs(Text, { color: selected === 1 ? 'red' : undefined, children: [selected === 1 ? ' ❯' : ' ', " 2. Deny"] }) })] }), _jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { dimColor: true, children: "Tool: " }), _jsx(Text, { color: "cyan", children: toolName }), desc ? _jsxs(Text, { dimColor: true, children: [" ", desc] }) : null] }), _jsxs(Box, { children: [_jsx(Text, { dimColor: true, children: " Esc to cancel \u00B7 " }), _jsx(Text, { dimColor: true, children: "\u2191\u2193 to select \u00B7 " }), _jsx(Text, { dimColor: true, children: "Enter to confirm" })] })] }));
|
|
47
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type Message } from '../protocol.js';
|
|
2
|
+
interface Props {
|
|
3
|
+
messages: Message[];
|
|
4
|
+
currentMessage: Message | null;
|
|
5
|
+
scrollOffset: number;
|
|
6
|
+
terminalRows: number;
|
|
7
|
+
reservedRows: number;
|
|
8
|
+
}
|
|
9
|
+
export declare function ChatArea({ messages, currentMessage, scrollOffset, terminalRows, reservedRows }: Props): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* ChatArea — renders message list with markdown, tool calls, steps, thinking.
|
|
4
|
+
* Shows newest messages at the bottom; uses scrollOffset to page through history.
|
|
5
|
+
*/
|
|
6
|
+
import { Box, Text } from 'ink';
|
|
7
|
+
const SEP = '─'.repeat(60);
|
|
8
|
+
export function ChatArea({ messages, currentMessage, scrollOffset, terminalRows, reservedRows }) {
|
|
9
|
+
const allMessages = currentMessage
|
|
10
|
+
? [...messages.filter(m => m.id !== currentMessage.id)]
|
|
11
|
+
: messages;
|
|
12
|
+
const availableRows = Math.max(10, terminalRows - reservedRows);
|
|
13
|
+
const maxVisible = Math.max(5, Math.floor(availableRows / 4));
|
|
14
|
+
const total = allMessages.length;
|
|
15
|
+
// scrollOffset = how many messages scrolled above the bottom
|
|
16
|
+
// 0 = at bottom (show newest), N = N messages scrolled up
|
|
17
|
+
const clampedOffset = Math.min(scrollOffset, total - 1);
|
|
18
|
+
const end = total - clampedOffset;
|
|
19
|
+
const start = Math.max(0, end - maxVisible);
|
|
20
|
+
const visibleMessages = allMessages.slice(start, end);
|
|
21
|
+
const isScrolledUp = clampedOffset > 0;
|
|
22
|
+
const hiddenAbove = start;
|
|
23
|
+
const hiddenBelow = clampedOffset;
|
|
24
|
+
return (_jsxs(Box, { flexDirection: "column", width: "100%", children: [isScrolledUp && (_jsx(Box, { children: _jsxs(Text, { color: "yellow", dimColor: true, children: ["\u25B2 [", start + 1, "-", end, "/", total, "] \u4E0A\u7FFB\u4E2D (PgUp/PgDn \u7FFB\u9875, Home \u5F00\u5934, End \u6700\u65B0)"] }) })), !isScrolledUp && total > maxVisible && (_jsx(Box, { children: _jsxs(Text, { color: "grey", dimColor: true, children: ["[", start + 1, "-", end, "/", total, "] \u2191/PgUp \u67E5\u770B\u66F4\u65E9\u6D88\u606F"] }) })), visibleMessages.map(msg => (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [msg.type === 'user' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: SEP }), _jsxs(Text, { color: "green", bold: true, children: ["> ", msg.content] })] })), msg.type === 'assistant' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: ' ' }), msg.steps.map((step, i) => (_jsx(StepDisplay, { step: step }, step.id || i))), msg.thinking && (_jsx(Text, { dimColor: true, italic: true, children: truncate(msg.thinking, 200) })), msg.toolCalls.map((tc, i) => (_jsx(ToolCallDisplay, { tc: tc }, tc.id || i))), msg.content && _jsx(Text, { children: msg.content }), _jsx(Text, { dimColor: true, children: SEP })] })), msg.type === 'system' && (_jsx(Box, { children: _jsxs(Text, { color: "red", children: ["Error: ", msg.content] }) }))] }, msg.id))), currentMessage && (_jsxs(Box, { flexDirection: "column", children: [currentMessage.thinking && (_jsx(Text, { dimColor: true, italic: true, children: truncate(currentMessage.thinking, 200) })), currentMessage.toolCalls.map((tc, i) => (_jsx(ToolCallDisplay, { tc: tc }, tc.id || i))), currentMessage.content && _jsx(Text, { children: currentMessage.content })] }, currentMessage.id))] }));
|
|
25
|
+
}
|
|
26
|
+
function StepDisplay({ step }) {
|
|
27
|
+
const icon = step.status === 'success' ? '✓' : step.status === 'error' ? '✗' : '▶';
|
|
28
|
+
const color = step.status === 'success' ? 'green' : step.status === 'error' ? 'red' : 'cyan';
|
|
29
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: color, children: [" ", icon, " ", step.title] }), step.thought && _jsxs(Text, { color: "blue", dimColor: true, children: [" ", truncate(step.thought, 200)] }), step.action && _jsxs(Text, { color: "yellow", children: [" ", truncate(step.action, 200)] }), step.result && _jsxs(Text, { color: "green", children: [" ", truncate(step.result, 300)] })] }));
|
|
30
|
+
}
|
|
31
|
+
function ToolCallDisplay({ tc }) {
|
|
32
|
+
const argsStr = tc.args ? truncate(formatJson(tc.args), 200) : '';
|
|
33
|
+
const statusIcon = tc.status === 'complete' ? '✓' : tc.status === 'running' ? '◷' : '✗';
|
|
34
|
+
const statusColor = tc.status === 'complete' ? 'green' : tc.status === 'running' ? 'yellow' : 'red';
|
|
35
|
+
return (_jsxs(Box, { flexDirection: "column", paddingLeft: 1, children: [_jsxs(Box, { children: [_jsxs(Text, { color: statusColor, children: [" ", statusIcon, " "] }), _jsx(Text, { bold: true, color: "magenta", children: tc.name }), argsStr && _jsxs(Text, { dimColor: true, children: [" ", argsStr] })] }), tc.result && (_jsx(Box, { paddingLeft: 4, children: _jsx(Text, { color: "green", dimColor: true, children: truncate(tc.result, 200) }) }))] }));
|
|
36
|
+
}
|
|
37
|
+
function formatJson(s) {
|
|
38
|
+
try {
|
|
39
|
+
return JSON.stringify(JSON.parse(s), null, 2);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return s;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function truncate(s, max) {
|
|
46
|
+
if (s.length <= max)
|
|
47
|
+
return s;
|
|
48
|
+
return s.slice(0, max) + '...';
|
|
49
|
+
}
|