brettai 0.1.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/dist/index.js +98 -0
- package/package.json +34 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var In=Object.defineProperty;var Rn=(e=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(e,{get:(t,n)=>(typeof require<"u"?require:t)[n]}):e)(function(e){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});var ue=(e,t,n)=>()=>{if(n)throw n[0];try{return e&&(t=e(e=0)),t}catch(r){throw n=[r],r}};var nt=(e,t)=>{for(var n in t)In(e,n,{get:t[n],enumerable:!0})};import{readFileSync as Gn,writeFileSync as Jn,mkdirSync as Xn,existsSync as Yn}from"fs";import{homedir as Kn}from"os";import{join as xt}from"path";import{chmodSync as Vn}from"fs";function Tt(){return process.env.SUPABASE_URL??"https://szocqslnprdbvmwmsamf.supabase.co"}function kt(){return process.env.SUPABASE_ANON_KEY??"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InN6b2Nxc2xucHJkYnZtd21zYW1mIiwicm9sZSI6ImFub24iLCJpYXQiOjE3ODEyMTI1NjcsImV4cCI6MjA5Njc4ODU2N30.Ys-335xRuHCyhlOc7Z3bJ7MyJPsW1I90BC0wj1mVvXs"}function Oe(){try{return Yn(pe)?JSON.parse(Gn(pe,"utf8")):null}catch{return null}}function Ct(e){Xn(wt,{recursive:!0}),Jn(pe,JSON.stringify(e,null,2),"utf8");try{Vn(pe,384)}catch{}}function vt(e){return{access_token:e.access_token,refresh_token:e.refresh_token,expires_at:Math.floor(Date.now()/1e3)+(e.expires_in??3600),user_id:e.user?.id??"",email:e.user?.email??""}}async function St(e,t){let n=await fetch(`${Tt()}/auth/v1/token?grant_type=password`,{method:"POST",headers:{apikey:kt(),"Content-Type":"application/json"},body:JSON.stringify({email:e,password:t})});if(!n.ok){let o=await n.text();throw new Error(`Login failed (${n.status}): ${o.slice(0,200)}`)}let r=vt(await n.json());return Ct(r),r}async function Zn(e){let t=await fetch(`${Tt()}/auth/v1/token?grant_type=refresh_token`,{method:"POST",headers:{apikey:kt(),"Content-Type":"application/json"},body:JSON.stringify({refresh_token:e.refresh_token})});if(!t.ok)throw new Error(`Session expired \u2014 run brett login again (${t.status})`);let n=vt(await t.json());return Ct(n),n}async function H(){let e=Oe();if(!e)return null;let t=Math.floor(Date.now()/1e3);if(e.expires_at-t<300)try{e=await Zn(e)}catch{return null}return e.access_token}var wt,pe,ne=ue(()=>{"use strict";wt=xt(Kn(),".brett"),pe=xt(wt,"auth.json")});import{promisify as Hr}from"node:util";import{exec as Wr}from"node:child_process";function on(e,t,n){let r=`${e}
|
|
3
|
+
${t}`,o=r.match(/Logged in to [\w.]+ account (\S+)/i)||r.match(/Logged in to [\w.]+ as (\S+)/i);return n===0&&o?{installed:!0,authed:!0,account:o[1],detail:`Connected as ${o[1]}`}:n===0&&/Logged in/i.test(r)?{installed:!0,authed:!0,detail:"Connected to GitHub"}:{installed:!0,authed:!1,detail:"installed but not signed in"}}async function zr(){try{await rn("gh --version",{timeout:1e4})}catch{return{installed:!1,authed:!1,detail:"not connected \u2014 needs the GitHub CLI"}}try{let{stdout:e,stderr:t}=await rn("gh auth status",{timeout:1e4});return on(e,t,0)}catch(e){return on(e.stdout??"",e.stderr??"",typeof e.code=="number"?e.code:1)}}async function sn(){let e=["Connections:"];for(let t of Object.values(J)){let n=await t.check(),r=n.authed?"\u2713":n.installed?"\u25CB":"\xB7";e.push(` ${r} ${t.name} \u2014 ${n.detail}`)}return e.push(""),e.push("Use /connect <name> for setup help (e.g. /connect github)."),e.join(`
|
|
4
|
+
`)}async function an(e){let t=J[e];if(!t)return`Unknown connector "${e}". Available: ${Object.keys(J).join(", ")}.`;let n=await t.check();return n.authed?`${t.name}: ${n.detail}. You're all set \u2014 ask me to create a branch, commit, or open a PR.`:n.installed?[`${t.name}: the GitHub CLI is installed but not signed in.`,"","To finish connecting:"," 1. Quit Brett (type /quit)"," 2. Run: brett connect github (or: gh auth login)"," 3. Start Brett again","","Then /connect will show GitHub as connected."].join(`
|
|
5
|
+
`):[`${t.name}: the GitHub CLI isn't installed yet.`,"","To connect GitHub:"," 1. Quit Brett (type /quit)"," 2. Install: winget install GitHub.cli"," 3. Run: brett connect github (or: gh auth login)"," 4. Start Brett again","","GitHub is optional \u2014 everything else works without it."].join(`
|
|
6
|
+
`)}var rn,J,qe=ue(()=>{"use strict";rn=Hr(Wr);J={github:{id:"github",name:"GitHub",check:zr,setupHint:`Brett uses the GitHub CLI for branches, commits, and pull requests.
|
|
7
|
+
1. winget install GitHub.cli
|
|
8
|
+
2. gh auth login
|
|
9
|
+
Once signed in, ask Brett to commit changes or open a PR.`}}});var gn={};nt(gn,{runLogin:()=>pn});import*as fn from"readline";async function pn(){let e=fn.createInterface({input:process.stdin,output:process.stdout}),t=o=>new Promise(s=>e.question(o,s));console.log(`Brett login
|
|
10
|
+
`);let n=await t("Email: "),r=await t("Password: ");e.close();try{let o=await St(n.trim(),r);console.log(`
|
|
11
|
+
Logged in as ${o.email}. Token stored in ~/.brett/auth.json`)}catch(o){console.error(`
|
|
12
|
+
${o instanceof Error?o.message:"Login failed"}`),process.exit(1)}}var oo,hn=ue(()=>{"use strict";ne();oo=process.argv[1]?.endsWith("login.ts")||process.argv[1]?.endsWith("login.js");oo&&pn()});var bn={};nt(bn,{runConnect:()=>io});import{spawnSync as so}from"child_process";async function io(e){if(!e){for(let o of Object.values(J)){let s=await o.check(),i=s.authed?"\u2713":s.installed?"\u25CB":"\xB7";console.log(` ${i} ${o.name} \u2014 ${s.detail}`)}console.log(`
|
|
13
|
+
Usage: brett connect <name> (e.g. brett connect github)`);return}let t=e.toLowerCase(),n=J[t];if(!n){console.log(`Unknown connector: ${e}`),console.log(`Available: ${Object.keys(J).join(", ")}`);return}let r=await n.check();if(r.authed){console.log(`${n.name}: ${r.detail}. Already connected.`);return}if(t==="github"){if(!r.installed){console.log(`GitHub CLI (gh) is not installed.
|
|
14
|
+
`),console.log(" Install: winget install GitHub.cli"),console.log(" Then run: brett connect github");return}console.log(`Signing in to GitHub...
|
|
15
|
+
`),so("gh",["auth","login"],{stdio:"inherit"}).status===0?console.log(`
|
|
16
|
+
GitHub connected! Start brett and it can create branches, commits, and PRs.`):console.log(`
|
|
17
|
+
Sign-in was not completed. Try again: brett connect github`);return}console.log(`${n.name} setup:
|
|
18
|
+
|
|
19
|
+
${n.setupHint}`)}var yn=ue(()=>{"use strict";qe()});import{render as co}from"ink";import{execSync as xn}from"child_process";import*as dn from"dotenv";import{useState as _,useCallback as X,useRef as M,useEffect as cn}from"react";import On from"react";import{Box as Mn,Text as D}from"ink";var f={colors:{brett:"#6aada0",user:"#e8e4df",system:"#8a8d86",muted:"#a9a49c",dim:"#5c5f5a",prompt:"#6aada0",success:"#8fc99b",error:"#e8857a",border:"#3a3e3a",tierAdvanced:"#e8c97a",tierMax:"#c4b5d4"}};import{jsx as K,jsxs as de}from"react/jsx-runtime";var rt=["Cheap by default. Smart when it counts.","Code it. Ship it. Own it.","Ready. What are we building?"],ot=On.memo(({repo:e})=>{let t=rt[Math.floor(Math.random()*rt.length)];return de(Mn,{flexDirection:"column",borderStyle:"round",borderColor:f.colors.brett,paddingX:1,marginBottom:1,children:[de(D,{children:[K(D,{color:f.colors.brett,children:"\u25C6 "}),K(D,{color:f.colors.brett,bold:!0,children:"Brett"}),K(D,{color:f.colors.dim,children:" \xB7 v0.1.0"})]}),K(D,{children:" "}),de(D,{color:f.colors.muted,children:[" ",t]}),K(D,{color:f.colors.dim,children:" /help for commands \xB7 Tab to complete \xB7 /quit to exit"}),K(D,{children:" "}),de(D,{color:f.colors.dim,children:[" repo: ",e??"none"]})]})});import{Box as ee,Text as L,Static as Dn}from"ink";import{jsx as B,jsxs as Q}from"react/jsx-runtime";function Bn(e){return e==="advanced"?B(L,{color:f.colors.tierAdvanced,children:" [advanced]"}):e==="max"?B(L,{color:f.colors.tierMax,children:" [max]"}):null}function Ln(e,t){return e.role==="user"?Q(ee,{marginTop:1,children:[B(L,{color:f.colors.prompt,bold:!0,children:"> "}),B(L,{color:f.colors.user,children:e.content})]},t):e.role==="system"?B(ee,{children:Q(L,{color:f.colors.system,children:["\xB7 ",e.content]})},t):Q(ee,{flexDirection:"column",marginTop:1,children:[Q(L,{color:f.colors.brett,bold:!0,children:["brett",Bn(e.tier)]}),Q(L,{color:f.colors.user,children:[e.content,e.streaming?B(L,{color:f.colors.brett,children:"\u258B"}):null]})]},t)}var st=({messages:e,header:t})=>{let n=e.filter(o=>!o.streaming),r=[{kind:"header"},...n.map((o,s)=>({kind:"msg",msg:o,idx:s}))];return B(ee,{flexDirection:"column",children:B(Dn,{items:r,children:(o,s)=>o.kind==="header"?B(ee,{children:t},"header"):Ln(o.msg,`msg-${s}`)})})};import{useState as $e,useEffect as Nn}from"react";import{Box as te,Text as N}from"ink";import{useState as it,useEffect as me,useRef as at}from"react";import{Text as q,useInput as jn}from"ink";import{jsx as fe,jsxs as _e}from"react/jsx-runtime";var ct=({value:e,onChange:t,onSubmit:n,onTab:r,placeholder:o="",focus:s=!0})=>{let[i,a]=it(e.length),[p,C]=it(!0),S=at(e),b=at(i);me(()=>{S.current=e},[e]),me(()=>{b.current=i},[i]),me(()=>{i>e.length&&(a(e.length),b.current=e.length)},[e,i]),me(()=>{let y=setInterval(()=>C(w=>!w),500);return()=>clearInterval(y)},[]);let h=(y,w)=>{S.current=y,b.current=w,t(y),a(w)};return jn((y,w)=>{C(!0);let T=S.current,E=b.current;if(w.return){n?.(T);return}if(w.tab){r?.();return}if(w.leftArrow){let v=Math.max(0,E-1);b.current=v,a(v);return}if(w.rightArrow){let v=Math.min(T.length,E+1);b.current=v,a(v);return}if(w.ctrl&&y==="a"){b.current=0,a(0);return}if(w.ctrl&&y==="e"){b.current=T.length,a(T.length);return}if(w.backspace||w.delete){if(E===0)return;h(T.slice(0,E-1)+T.slice(E),E-1);return}if(!(w.ctrl||w.meta||w.tab||w.escape||w.upArrow||w.downArrow)&&y){let v=y.replace(/\r?\n/g," ");h(T.slice(0,E)+v+T.slice(E),E+v.length)}},{isActive:s}),e.length===0?_e(q,{children:[fe(q,{color:f.colors.brett,children:p?"\u258F":" "}),o?fe(q,{color:f.colors.dim,children:o}):null]}):i>=e.length?_e(q,{color:f.colors.user,children:[e,fe(q,{color:f.colors.brett,children:p?"\u258F":" "})]}):_e(q,{color:f.colors.user,children:[e.slice(0,i),fe(q,{inverse:p,children:e.slice(i,i+1)}),e.slice(i+1)]})};import{jsx as G,jsxs as j}from"react/jsx-runtime";var Fn=[{cmd:"/plan",desc:"enter planning mode"},{cmd:"/build",desc:"hand spec to the coder"},{cmd:"/connect",desc:"show or set up connections"},{cmd:"/max",desc:"most capable model, next message"},{cmd:"/topup",desc:"add credits"},{cmd:"/usage",desc:"session cost so far"},{cmd:"/help",desc:"all commands"},{cmd:"/quit",desc:"exit"}],lt=["Cooking","Crunching","Tinkering","Wrangling","Forging","Conjuring"],ut=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],Un=()=>{let[e,t]=$e(0);return Nn(()=>{let n=setInterval(()=>t(r=>(r+1)%ut.length),80);return()=>clearInterval(n)},[]),G(N,{color:f.colors.brett,children:ut[e]})},dt=({onSubmit:e,isThinking:t,elapsedSeconds:n,liveTokens:r,planMode:o})=>{let[s,i]=$e(""),[a]=$e(()=>lt[Math.floor(Math.random()*lt.length)]),p=/(\x1b)?\[?<?\d+;\d+;\d+[Mm]/g,C=h=>i(h.replace(p,"")),S=h=>{!h.trim()||t||(i(""),e(h.trim()))},b=s.startsWith("/")?Fn.filter(h=>h.cmd.startsWith(s.toLowerCase())&&h.cmd!==s.toLowerCase()):[];return t?j(te,{marginTop:1,gap:1,children:[G(Un,{}),j(N,{color:f.colors.muted,children:[a,"\u2026"]}),j(N,{color:f.colors.dim,children:["(",n,"s \xB7 \u2193 ",r," tokens \xB7 esc to interrupt)"]})]}):j(te,{flexDirection:"column",marginTop:1,children:[j(te,{borderStyle:"round",borderColor:o?f.colors.tierAdvanced:f.colors.border,paddingX:1,children:[G(N,{color:o?f.colors.tierAdvanced:f.colors.prompt,bold:!0,children:o?"\u25B8 plan ":"\u25B8 "}),G(te,{flexGrow:1,children:G(ct,{value:s,onChange:C,onSubmit:S,onTab:()=>{b.length>0&&i(b[0].cmd+" ")},placeholder:""})})]}),b.length>0?G(te,{flexDirection:"column",marginLeft:2,marginTop:0,children:b.map((h,y)=>j(N,{children:[G(N,{color:y===0?f.colors.brett:f.colors.dim,bold:y===0,children:h.cmd}),j(N,{color:f.colors.dim,children:[" ",h.desc]})]},h.cmd))}):j(N,{dimColor:!0,italic:!0,children:[" ",o?"planning \xB7 /build when ready":"/help \xB7 @file to include code \xB7 /max for hard problems"]})]})};import{Box as Ie,Text as F,useInput as Hn}from"ink";import{jsx as U,jsxs as Re}from"react/jsx-runtime";function Wn(e){return e.startsWith("+")?U(F,{color:f.colors.success,children:e}):e.startsWith("-")?U(F,{color:f.colors.error,children:e}):e.startsWith("@")?U(F,{color:f.colors.muted,children:e}):U(F,{color:f.colors.dim,children:e})}var mt=({filePath:e,diffText:t,onAccept:n,onReject:r})=>{Hn((s,i)=>{(s==="y"||i.return)&&n(),(s==="n"||i.escape)&&r()});let o=t.split(`
|
|
20
|
+
`).slice(0,40);return Re(Ie,{flexDirection:"column",marginTop:1,children:[Re(F,{color:f.colors.muted,children:["Review change to ",e,":"]}),U(Ie,{flexDirection:"column",marginY:1,children:o.map((s,i)=>U(Ie,{children:Wn(s)},i))}),Re(F,{color:f.colors.dim,children:[U(F,{color:f.colors.success,children:"y"})," apply \xB7"," ",U(F,{color:f.colors.error,children:"n"})," skip"]})]})};var ft="You are Brett, a coding assistant built by the Brett team. Produce code changes as unified diffs. To run a command, emit a ```run block. Be direct and technically accurate.",pt="You are Brett, a coding assistant. Produce code changes as unified diffs. Be direct and technically accurate.";function zn(e){return Math.ceil(e.length/4)}var qn=1024,gt=4,ht=20;function yt(e,t){let n=0,r=zn(e)>=qn,o=r?[{type:"text",text:e,cache_control:{type:"ephemeral"}}]:e;r&&n++;let s=t.map(a=>({...a})),i=gt-n;if(s.length>1&&i>0){let a=s.length-2;if(a>=0&&(bt(s[a]),n++),s.length>ht&&n<gt){let p=s.length-ht+5;p>0&&p<a&&(bt(s[p]),n++)}}return{system:o,messages:s}}function bt(e){if(typeof e.content=="string")e.content=[{type:"text",text:e.content,cache_control:{type:"ephemeral"}}];else if(Array.isArray(e.content)&&e.content.length>0){let t=e.content[e.content.length-1];t.cache_control={type:"ephemeral"}}}ne();var Qn=process.env.OPENROUTER_URL??"",er=process.env.MAX_MODEL??"",Me=null;function Et(e){Me=e}function tr(){return process.env.PLATFORM_API_URL??"https://vericlaw-coding-agent-production.up.railway.app"}function De(){return{key:process.env.CORE_API_KEY??"",url:process.env.CORE_BASE_URL??""}}function nr(){return process.env.CORE_MODEL??""}function Pt(){return process.env.MID_MODEL??""}function rr(){return process.env.OPENROUTER_API_KEY??""}function or(){return(process.env.VERICLAW_ENV??"").toLowerCase()==="development"}function ge(e){return or()?!1:(e.onError(new Error("Not logged in. Run: brett login")),!0)}async function At(e,t){let n=e.body.getReader(),r=new TextDecoder,o="",s=null,i="";for(;;){let{done:a,value:p}=await n.read();if(a)break;i+=r.decode(p,{stream:!0});let C=i.split(`
|
|
21
|
+
`);i=C.pop()??"";for(let S of C){let b=S.trim();if(!b||!b.startsWith("data:"))continue;let h=b.slice(5).trim();if(h!=="[DONE]")try{let y=JSON.parse(h);if(y.error)throw new Error(y.detail??String(y.error));let w=y.choices?.[0]?.delta?.content;w&&(o+=w,t.onToken(w)),y.usage&&(s={promptTokens:y.usage.prompt_tokens??0,completionTokens:y.usage.completion_tokens??0})}catch(y){if(y instanceof Error&&!(y instanceof SyntaxError))throw y}}}t.onDone(o,s)}async function he(e,t,n,r){let o=await H();if(!o||!Me)return!1;try{let s=await fetch(`${tr()}/v1/chat/completions`,{method:"POST",headers:{Authorization:`Bearer ${o}`,"Content-Type":"application/json"},body:JSON.stringify({tier:e,team_id:Me,messages:t,...r?{prompt_mode:r}:{}})});if(!s.ok||!s.body){let i=`Platform error ${s.status}`;try{i=(await s.json()).detail??i}catch{}throw s.status===402&&(i="Out of credits \u2014 top up your balance."),new Error(i)}return await At(s,n),!0}catch(s){return n.onError(s instanceof Error?s:new Error("Stream failed")),!0}}async function be(e,t,n,r,o,s,i){try{let a=await fetch(`${e}/chat/completions`,{method:"POST",headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json",...o},body:JSON.stringify({model:n,messages:r,temperature:.6,max_tokens:8192,stream:!0,stream_options:{include_usage:!0},...s})});if(!a.ok||!a.body)throw new Error(`Request failed: ${a.status}`);await At(a,i)}catch(a){i.onError(a instanceof Error?a:new Error("Stream failed"))}}function _t(e){return[{role:"system",content:ft},...e.filter(t=>t.role!=="system")]}async function $t(e,t){if(await he("core",e,t)||ge(t))return;let n=_t(e),{key:r,url:o}=De();return be(o,r,nr(),n,{},{reasoning_effort:"max"},t)}async function It(e,t){if(await he("advanced",e,t)||ge(t))return;let n=_t(e),{key:r,url:o}=De();return be(o,r,Pt(),n,{},{reasoning_effort:"max"},t)}async function Rt(e,t,n,r="planning"){if(await he(r,t,n,"chatter")||ge(n))return;let o=[{role:"system",content:e},...t.filter(a=>a.role!=="system")],{key:s,url:i}=De();return be(i,s,Pt(),o,{},{reasoning_effort:"max"},n)}async function Ot(e,t){if(await he("max",e,t)||ge(t))return;let n=e.filter(i=>i.role!=="system"),{system:r,messages:o}=yt(pt,n),s=[{role:"system",content:r},...o];return be(Qn,rr(),er,s,{"HTTP-Referer":"https://brett.dev","X-Title":"Brett"},{},t)}import{applyPatch as sr,parsePatch as ir}from"diff";function ye(e){let t=/^---\s/m.test(e)&&/^\+\+\+\s/m.test(e),n=/^@@\s-\d+(,\d+)?\s\+\d+(,\d+)?\s@@/m.test(e);return t&&n}function ar(e){let t=e.split(`
|
|
22
|
+
`),n=[],r=0;for(;r<t.length;){let o=t[r],s=o.match(/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@(.*)$/);if(s){let i=[],a=r+1;for(;a<t.length&&!t[a].startsWith("@@")&&!t[a].startsWith("---");)i.push(t[a]),a++;let p=i.filter(S=>S.startsWith(" ")||S.startsWith("-")).length,C=i.filter(S=>S.startsWith(" ")||S.startsWith("+")).length;n.push(`@@ -${s[1]},${p} +${s[2]},${C} @@${s[3]}`),n.push(...i),r=a}else n.push(o),r++}return n.join(`
|
|
23
|
+
`)}function Mt(e,t){if(!ye(t))return{ok:!1,error:"Not a unified diff \u2014 looks like a full-file rewrite or prose. Re-prompt for a diff."};let n=ar(t),r;try{r=ir(n)}catch(i){return{ok:!1,error:`Could not parse diff: ${i.message}`}}if(!r.length||!r[0].hunks.length)return{ok:!1,error:"Diff parsed but contained no hunks."};let o=r.reduce((i,a)=>i+a.hunks.length,0),s=sr(e,n,{fuzzFactor:2});return s===!1?{ok:!1,error:"Context did not match \u2014 the file changed since the diff was generated. Rejected to avoid corruption.",hunks:o}:{ok:!0,content:s,hunks:o}}import{readFileSync as Be,writeFileSync as Dt,mkdirSync as cr,existsSync as lr}from"fs";import{dirname as ur}from"path";import{resolve as Le}from"path";function Lt(e){let t=[],n=/```(?:diff)?\n([\s\S]*?)```/g,r,o=!1;for(;(r=n.exec(e))!==null;){let i=r[1];if(ye(i)){o=!0;let a=Bt(i);a&&t.push({filePath:a,diffText:i})}}if(!o&&ye(e)){let i=Bt(e);i&&t.push({filePath:i,diffText:e})}let s=new Map;for(let i of t)s.set(i.filePath,i);return[...s.values()]}function Bt(e){let t=e.match(/^\+\+\+\s+b\/(.+)$/m);if(t)return t[1].trim();let n=e.match(/^\+\+\+\s+(.+)$/m);return n?n[1].trim().replace(/^b\//,""):null}function dr(e,t){if(/^---\s+\/dev\/null\s*$/m.test(e))return!0;if(!t){let n=e.split(`
|
|
24
|
+
`).filter(o=>/^[ +-]/.test(o)&&!/^[-+]{3}\s/.test(o));if(n.length>0&&n.every(o=>o.startsWith("+")))return!0}return!1}function mr(e){let t=e.split(`
|
|
25
|
+
`),n=[];for(let r of t)/^\+{3}\s/.test(r)||/^-{3}\s/.test(r)||r.startsWith("@@")||r.startsWith("+")&&n.push(r.slice(1));return n.join(`
|
|
26
|
+
`)}function jt(e,t){let n=Le(t,e.filePath);if(!n.startsWith(Le(t)+Rn("path").sep)&&n!==Le(t))return{ok:!1,filePath:e.filePath,error:`Path escapes repository root \u2014 blocked: ${e.filePath}`};let r=n,o=lr(r);if(dr(e.diffText,o)){if(o)return{ok:!1,filePath:e.filePath,error:`Refusing to overwrite existing file as new: ${e.filePath}`};let C=mr(e.diffText);cr(ur(r),{recursive:!0}),Dt(r,C,"utf8");let S=Be(r,"utf8");return{ok:!0,filePath:e.filePath,verified:S===C}}if(!o)return{ok:!1,filePath:e.filePath,error:`File not found: ${e.filePath}`};let s=Be(r,"utf8"),i=Mt(s,e.diffText);if(!i.ok||i.content===void 0)return{ok:!1,filePath:e.filePath,error:i.error};Dt(r,i.content,"utf8");let p=Be(r,"utf8")===i.content;return{ok:!0,filePath:e.filePath,verified:p}}import{readFileSync as fr,statSync as pr,readdirSync as gr,existsSync as hr}from"fs";import{resolve as je}from"path";var Nt=24e3,br=6e4,yr=/@([\w./\\-]+)/g;function xr(e,t){let n=[],r=[],o=[],s=0,i=[...e.matchAll(yr)];for(let a of i){let p=a[1].replace(/[.,;:]+$/,""),C=je(t,p);if(!C.startsWith(je(t)+"/")&&C!==je(t)){r.push(p);continue}if(!hr(C)){r.push(p);continue}if(pr(C).isDirectory()){try{let b=gr(C,{withFileTypes:!0}).slice(0,50).map(h=>h.isDirectory()?`${h.name}/`:h.name);o.push(`Directory listing of ${p}:
|
|
27
|
+
${b.join(`
|
|
28
|
+
`)}`),n.push(p)}catch{r.push(p)}continue}if(s>=br){r.push(p+" (size budget exceeded)");continue}try{let b=fr(C,"utf8"),h=!1;b.length>Nt&&(b=b.slice(0,Nt),h=!0),s+=b.length,o.push(`Contents of ${p}${h?" (truncated)":""}:
|
|
29
|
+
\`\`\`
|
|
30
|
+
${b}
|
|
31
|
+
\`\`\``),n.push(p)}catch{r.push(p)}}return{cleanedInput:e,contextBlock:o.length>0?o.join(`
|
|
32
|
+
|
|
33
|
+
`):"",files:n,misses:r}}function xe(e,t){let n=xr(e,t);return n.contextBlock?{apiContent:`${e}
|
|
34
|
+
|
|
35
|
+
--- Mentioned files (read by the CLI) ---
|
|
36
|
+
${n.contextBlock}`,exp:n}:{apiContent:e,exp:n}}var we=class{events=[];filesTouched=new Set;record(t,n,r){this.events.push({kind:t,detail:n,filePath:r,at:new Date}),r&&this.filesTouched.add(r)}hasContent(){return this.events.some(t=>t.kind==="diff_accepted"||t.kind==="diff_rejected"||t.kind==="correction"||t.kind==="escalation"||t.kind==="command")||this.events.filter(t=>t.kind==="user").length>=3}buildTranscript(){let t=[];this.filesTouched.size>0&&(t.push(`Files touched this session: ${[...this.filesTouched].join(", ")}`),t.push(""));for(let n of this.events)switch(n.kind){case"user":t.push(`USER: ${n.detail}`);break;case"brett":t.push(`BRETT: ${n.detail}`);break;case"diff_accepted":t.push(`[DIFF ACCEPTED] ${n.filePath}: ${n.detail}`);break;case"diff_rejected":t.push(`[DIFF REJECTED] ${n.filePath}: ${n.detail}`);break;case"correction":t.push(`[USER CORRECTION] ${n.detail}`);break;case"escalation":t.push(`[ESCALATED] ${n.detail}`);break;case"command":t.push(`[COMMAND] ${n.detail}`);break}return t.join(`
|
|
37
|
+
`)}};ne();function Ne(){return process.env.PLATFORM_API_URL??"https://vericlaw-coding-agent-production.up.railway.app"}async function Ft(e){let t=await H();if(!t)return null;try{let n=await fetch(`${Ne()}/v1/identity/resolve`,{method:"POST",headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json"},body:JSON.stringify({repo_name:e})});if(!n.ok)return null;let r=await n.json();return{teamId:r.team_id,repoId:r.repo_id??null}}catch{return null}}async function Ut(e,t,n){let r=await H();if(!r)return[];try{let o=await fetch(`${Ne()}/v1/memory/retrieve`,{method:"POST",headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"},body:JSON.stringify({team_id:e,repo_id:t,query:n,count:8})});return o.ok?(await o.json()).memories??[]:[]}catch{return[]}}async function Ht(e,t,n){let r=await H();if(!r)return null;try{let o=await fetch(`${Ne()}/v1/memory/compress`,{method:"POST",headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"},body:JSON.stringify({team_id:e,repo_id:t,transcript:n})});return o.ok?await o.json():null}catch{return null}}function Wt(){return Oe()!==null}import{writeFileSync as wr,readFileSync as Tr,existsSync as kr,mkdirSync as Cr}from"fs";import{join as zt}from"path";var qt=".brett",vr="spec.md";function Gt(e){return zt(e,qt,vr)}function Fe(e,t){Cr(zt(e,qt),{recursive:!0});let n=Gt(e);return wr(n,t,"utf8"),n}function Ue(e){let t=Gt(e);return kr(t)?Tr(t,"utf8"):null}function He(e){let t=e.match(/```spec\n([\s\S]*?)```/);return t?t[1].trim():null}var Jt="You are Brett in planning mode. Ask focused questions to understand requirements before any code is written. When requirements are clear, produce a spec inside a ```spec fenced block. Do not write code or diffs in planning mode.";function Xt(e){return`A planning session produced the following specification. It is the BINDING definition of the task. Implement exactly what it says: do not add features it doesn't mention, do not skip requirements it includes, and treat its "Out of scope" section as forbidden territory. If the spec is ambiguous on a point you need, ask before assuming.
|
|
38
|
+
|
|
39
|
+
--- SPECIFICATION ---
|
|
40
|
+
${e}
|
|
41
|
+
--- END SPECIFICATION ---
|
|
42
|
+
|
|
43
|
+
Begin implementing. Produce diffs as usual.`}ne();import{Box as qr,useApp as Gr}from"ink";import{readdirSync as Sr,statSync as xs,readFileSync as ws,existsSync as Ts}from"fs";import{join as Yt,relative as Er,resolve as Cs,sep as Pr}from"path";var Ar=new Set([".git","node_modules","dist","build",".next",".cache","coverage",".venv","venv","__pycache__",".brett",".idea",".vscode"]),_r=new Set([".DS_Store","package-lock.json","yarn.lock"]),Kt=500,$r=8;function Ir(e){let t=[],n=!1;function r(o,s){if(s>$r)return;if(t.length>=Kt){n=!0;return}let i;try{i=Sr(o,{withFileTypes:!0})}catch{return}i.sort((a,p)=>a.isDirectory()!==p.isDirectory()?a.isDirectory()?-1:1:a.name.localeCompare(p.name));for(let a of i){if(t.length>=Kt){n=!0;return}if(a.isDirectory()){if(Ar.has(a.name))continue;r(Yt(o,a.name),s+1)}else{if(_r.has(a.name))continue;t.push(Er(e,Yt(o,a.name)).split(Pr).join("/"))}}}return r(e,0),{tree:t.sort().join(`
|
|
44
|
+
`),fileCount:t.length,truncated:n}}function Vt(e){let t=Ir(e);return`Repository structure (${t.fileCount} files${t.truncated?", list truncated":""}):
|
|
45
|
+
${t.tree}
|
|
46
|
+
|
|
47
|
+
You can see the file tree above. When you need a file's contents to make a change, ask the user to @-mention it (e.g. "show me @src/auth.ts").`}import{exec as Rr}from"node:child_process";import{promisify as Or}from"node:util";var Zt=Or(Rr),Mr=6e4,Dr=6e5,Qt=3e4,Br=["curl","wget","axel","aria2c","nc","telnet","lynx","w3m","links","httpie","xh","http-prompt","chrome","firefox","safari","ssh","scp","sftp","ftp","rsync","alias"],Lr=["ls","echo","pwd","date","cal","uptime","whoami","id","groups","env","printenv","which","type","whereis","whatis","uname","hostname","df","du","free","ps","time","timeout","head","tail","wc","sort","uniq","grep","find","tree","stat","file","cat","git status","git log","git diff","git show","git branch","git tag","git remote","git ls-files","git ls-remote","git rev-parse","git config --get","git config --list","git describe","git blame","git grep","git shortlog","git fetch","node --version","node -v","npm --version","npm -v","npm ls","npm list","npm test","npm run","npm outdated","tsc","pytest","go test","go build","go vet","python -m pytest"],jr=[{re:/(^|\s)git\s+rm\s+-[a-z]*r[a-z]*f|(^|\s)git\s+rm\s+-[a-z]*f[a-z]*r/i,warning:"This will DELETE files from your working tree and git index \u2014 it can wipe your entire codebase."},{re:/(^|\s)git\s+reset\s+--hard/i,warning:"This will THROW AWAY all uncommitted changes \u2014 any work not yet committed is lost."},{re:/(^|\s)git\s+clean\s+-[a-z]*f/i,warning:"This will DELETE all untracked files \u2014 anything not yet committed to git is gone."},{re:/(^|\s)git\s+checkout\s+--orphan/i,warning:"This creates a branch disconnected from history; combined with file removal it can leave your tree empty."},{re:/(^|\s)git\s+push\b.*(--force|--force-with-lease|\s-f\b)/i,warning:"This will FORCE-PUSH and can overwrite history on the remote \u2014 other people's work may be lost."},{re:/(^|\s)rm\s+-[a-z]*r[a-z]*f|(^|\s)rm\s+-[a-z]*f[a-z]*r/i,warning:"This will permanently DELETE files and folders from your disk. They are NOT recoverable."},{re:/\b(drop\s+table|truncate\s+table|delete\s+from)\b/i,warning:"This will DELETE data from a database \u2014 it is not recoverable."}];function We(e){for(let{re:t,warning:n}of jr)if(t.test(e))return n;return null}var en=e=>e.replace(/^([A-Za-z_][A-Za-z0-9_]*=\S*\s+)+/,"");function Nr(e){let t=[],n="",r=null;for(let o=0;o<e.length;o++){let s=e[o],i=e[o+1];if(r){n+=s,s===r&&(r=null);continue}if(s==='"'||s==="'"){r=s,n+=s;continue}if(s===";"||s===`
|
|
48
|
+
`||s==="|"||s==="&"){(s==="&"&&i==="&"||s==="|"&&i==="|")&&o++,t.push(n),n="";continue}n+=s}return n.trim()&&t.push(n),t.map(o=>o.trim()).filter(Boolean)}var Fr=e=>(en(e).split(/\s+/)[0]||"").toLowerCase();function Ur(e){let t=en(e).toLowerCase().trim();return Lr.some(n=>{if(!t.startsWith(n))return!1;let r=t[n.length];return r===void 0||r===" "||r==="-"})}function ze(e){if(/\$\(/.test(e)||e.includes("`"))return"needs-permission";let t=Nr(e);if(/(^|\s)(>|>>|tee|sed\s+-i|dd\b)|writeFileSync|appendFileSync|fs\.write|set-content|out-file|add-content/i.test(e))return"banned";if(We(e))return"dangerous";if(t.length===0)return"needs-permission";for(let r of t)if(Br.includes(Fr(r)))return"banned";return t.every(Ur)?"safe":"needs-permission"}function Te(e){if(e.length<=Qt)return e;let t=Math.floor(Qt/2),n=e.slice(t,e.length-t).split(`
|
|
49
|
+
`).length;return`${e.slice(0,t)}
|
|
50
|
+
|
|
51
|
+
... [${n} lines truncated] ...
|
|
52
|
+
|
|
53
|
+
${e.slice(e.length-t)}`}async function tn(e,t){let n=(e||"").trim();if(!n)return{content:"missing command",isError:!0,exitCode:1};if(/^gh(\s|$)/.test(n))try{await Zt("gh --version",{cwd:t.cwd,timeout:1e4})}catch{return{content:"The GitHub CLI (gh) is not installed, so this command can't run. Install it (winget install GitHub.cli) and run `gh auth login`, then try again. The developer can also run /connect github for setup help.",isError:!0,exitCode:127}}let r=ze(n);if(r==="banned")return{content:`Command blocked: it writes files or uses a network/remote tool that isn't allowed (${n.split(/\s+/)[0]}). Change files through reviewed diffs instead.`,isError:!0,exitCode:1};if((r==="needs-permission"||r==="dangerous")&&!(t.requestPermission?await t.requestPermission(n):!1))return{content:"Command not run (permission denied).",isError:!0,exitCode:1};let o=Math.min(Math.max(t.timeout??Mr,1),Dr);try{let{stdout:s,stderr:i}=await Zt(n,{cwd:t.cwd,timeout:o,maxBuffer:10485760}),a=Te(s),p=Te(i);return a&&p&&(a+=`
|
|
54
|
+
`),p&&(a+=`
|
|
55
|
+
`+p),{content:a.trim()||"no output",isError:!1,exitCode:0}}catch(s){let i=Te(s.stdout||""),a=Te(s.stderr||"");i&&a&&(i+=`
|
|
56
|
+
`),a&&(i+=`
|
|
57
|
+
`+a);let p=typeof s.code=="number"?s.code:1;return i+=s.killed?`
|
|
58
|
+
Command timed out after ${o}ms`:`
|
|
59
|
+
Exit code ${p}`,{content:i.trim(),isError:!0,exitCode:p}}}function nn(e){let t=e.match(/```[ \t]*run[ \t]*\r?\n([\s\S]*?)```/);if(!t)return null;let n=t[1].trim();return n.length>0?n:null}qe();import{jsx as ke,jsxs as ro}from"react/jsx-runtime";dn.config();var Jr=12,ln={core:{in:.14,out:.28},planning:{in:.3,out:1.2},advanced:{in:1.4,out:4.4},max:{in:5,out:25}};function Xr(e,t,n){let r=ln[n]??ln.core;return t/1e6*r.in+e/1e6*r.out}var Yr=["i am deepseek","i'm deepseek","deepseek","deep seek","\u6DF1\u5EA6\u6C42\u7D22","created by deepseek","trained by deepseek","developed by deepseek","deepseek-v3","deepseek-v4","deepseek-r1","deepseek's model","deepseek model","created by google","made by google","created by openai","made by openai","created by anthropic","made by anthropic","created by meta","made by meta","created by mistral","made by mistral","i was created by","i am an ai made by","i'm an ai assistant created by","i am a language model developed by","i am a large language model created by","i am a large language model trained by","large language model created by","large language model trained by","trained on a massive dataset","my core programming","i am gpt","i'm gpt","i am chatgpt","i'm chatgpt","created by openai","made by openai","trained by openai","developed by openai","i am claude","i'm claude","claude opus","claude sonnet","claude haiku","created by anthropic","made by anthropic","trained by anthropic","i am gemini","i'm gemini","gemini pro","gemini ultra","created by google","made by google","trained by google","developed by google deepmind","i am llama","i'm llama","i am meta ai","i'm meta ai","created by meta","made by meta","trained by meta","i am mistral","i'm mistral","mistral large","mistral medium","created by mistral","made by mistral","i am minimax","i'm minimax","minimax-m","as a minimax","as minimax","created by minimax","made by minimax","i am qwen","i'm qwen","created by alibaba","made by alibaba","i am command","i'm command r","created by cohere","made by cohere","i am grok","i'm grok","created by xai","made by xai","i was created by","i am an ai made by","i'm an ai assistant created by","i am a language model developed by","i am a large language model created by","i am a large language model trained by","large language model created by","large language model trained by","trained on a massive dataset","my core programming"],Kr="I'm Brett \u2014 your coding assistant. What are you working on?";function Vr(e){let t=e.replace(/```[\s\S]*?```/g,"").toLowerCase();return Yr.some(n=>t.includes(n))?Kr:e}var Zr=["that's not right","thats not right","try again","try harder","still broken","still wrong","not what i wanted","wrong","incorrect","nope","not quite","you're not getting it","missing the point","this isn't working"],Qr=e=>Zr.some(t=>e.toLowerCase().includes(t)),eo=e=>["/max","!max"].includes(e.toLowerCase().trim());function un(e){let t=e.toLowerCase(),n=0;return/traceback|exception|stack trace|\w+\.(ts|tsx|js|jsx|py|go|rs|java):\d+|error:/i.test(e)&&(n+=2),/\b(refactor|architect|race condition|memory leak|root cause|redesign|concurren|deadlock|optimi[sz]e|profil)/i.test(t)&&(n+=1),/\b(debug|why is|why does|failing|broken|investigate)\b/i.test(t)&&(n+=1),(e.match(/\b[\w/.-]+\.(ts|tsx|js|jsx|py|go|rs|java|cpp|c|rb|php)\b/g)||[]).length>=2&&(n+=1),e.length>280&&(n+=1),n>=2}function to(e){let t=e.toLowerCase(),n=/@[\w./-]+/.test(e),r=/\b[\w/.-]+\.(ts|tsx|js|jsx|py|go|rs|java|cpp|c|rb|php|css|html|md|json)\b/.test(e);if(n||r)return!1;let o=0;return/\b(i want|i'd like|we want|we need|i need)\b.*\b(users?|people|customers?|teams?|able to|feature|system|way to)\b/.test(t)&&(o+=2),/\b(let's|lets|can you|could you|please|i wanna|i want to)\s+(build|create|make|add|implement|design|set up|develop)\b/.test(t)&&(o+=2),/\b(build|create|implement|design)\s+(a|an|the)\s+\w+/.test(t)&&(o+=1),/\b(users?|people|customers?)\s+(can|should|to be able|will be able|need to)\b/.test(t)&&(o+=1),/\b(feature|system|flow|pipeline|dashboard|workflow|integration|mechanism)\b/.test(t)&&(o+=1),e.length>120&&(o+=1),o>=2}var no=`Brett \u2014 cheap by default, smart when it counts.
|
|
60
|
+
|
|
61
|
+
Coding
|
|
62
|
+
@file include a file's contents (e.g. @src/auth.ts)
|
|
63
|
+
/plan plan a feature \u2014 Brett asks questions, writes a spec
|
|
64
|
+
/build hand the spec to the coder
|
|
65
|
+
/max use the most capable model for your next message
|
|
66
|
+
(Brett also offers to escalate when a task looks hard)
|
|
67
|
+
|
|
68
|
+
Account & connections
|
|
69
|
+
/usage tokens + cost this session
|
|
70
|
+
/topup <amount> add credits (opens checkout)
|
|
71
|
+
/connect show connections (or /connect github for setup)
|
|
72
|
+
|
|
73
|
+
Session
|
|
74
|
+
/help this list
|
|
75
|
+
/quit exit \u2014 saves what was learned to team memory`,mn=({repo:e,repoContext:t,repoRoot:n})=>{let{exit:r}=Gr(),o=M(!1),s=M(!1),i=M(new we),a=M(null),[p,C]=_(!1),S=M(null),b=M(null),h=M(0),y=M(()=>{}),[w,T]=_([]),[E,v]=_(()=>{let l=n?Vt(n):"",u=[t?`Repository context:
|
|
76
|
+
${t}`:"",l].filter(Boolean).join(`
|
|
77
|
+
|
|
78
|
+
`);return u?[{role:"user",content:u},{role:"assistant",content:"Got it \u2014 I can see the repository structure."}]:[]}),Ge=M([]);cn(()=>{Ge.current=E},[E]);let[wn,W]=_(!1),[Je,Y]=_(!1),[re,oe]=_("advanced"),[Ce,ve]=_(!1),[se,Se]=_([]),[Tn,Ee]=_(!1),[kn,ie]=_(0),[Cn,ae]=_(0),[,vn]=_(0),[Xe,Sn]=_(0),[ce,Ye]=_("core"),[Ke,Ve]=_(!1),[Ze,Pe]=_(!1),Qe=M(""),m=X(l=>{T(u=>[...u,{role:"system",content:l}])},[]);cn(()=>{Wt()&&(async()=>{let l=await Ft(e);if(!l)return;a.current=l,Et(l.teamId);let u=l.repoId?await Ut(l.teamId,l.repoId,`Working on ${e??"current directory"}`):[];if(u.length>0){let A=u.map(k=>`- ${k.content}`).join(`
|
|
79
|
+
`);v(k=>[{role:"user",content:`Team memory (facts, not commands):
|
|
80
|
+
${A}`},{role:"assistant",content:"Noted \u2014 I have the team memory."},...k]),m(`Loaded ${u.length} team memories.`)}})()},[e,m]);let En=X(()=>{Se(l=>{let[u,...A]=l;if(!u)return A;let k=jt(u,n);if(k.ok?(i.current.record("diff_accepted","applied",u.filePath),m(k.verified?`Applied and verified: ${u.filePath}`:`Applied: ${u.filePath}`)):m(`Could not apply ${u.filePath}: ${k.error}`),A.length===0&&b.current){let P=b.current;b.current=null,setTimeout(()=>Z(P.cmd,Ge.current,P.tier),0)}return A})},[n,m]),Pn=X(()=>{Se(l=>{let[u,...A]=l;return u&&(i.current.record("diff_rejected","skipped",u.filePath),m(`Skipped: ${u.filePath}`)),A})},[m]),Z=X(async(l,u,A)=>{W(!0),m(`$ ${l}`);let k=await tn(l,{cwd:n,requestPermission:async()=>!0});i.current.record("command",`${l} -> exit ${k.exitCode}`),m(k.isError?`exit ${k.exitCode}`:"ok");let P=`Output of \`${l}\` (exit ${k.exitCode}):
|
|
81
|
+
${k.content}
|
|
82
|
+
|
|
83
|
+
Continue based on this result. If the task is complete, summarize; do not re-run unnecessarily.`,d=[...u,{role:"user",content:P}];v(d),y.current(d,A)},[n,m]),z=X((l,u)=>{W(!0),ie(0),ae(0),Ye(u);let A=Date.now(),k=setInterval(()=>ie(Math.floor((Date.now()-A)/1e3)),250);T(c=>[...c,{role:"brett",content:"",tier:u,streaming:!0}]);let P="",d={onToken:c=>{P+=c,ae(g=>g+1),T(g=>{let x=[...g],$=x[x.length-1];return $?.role==="brett"&&(x[x.length-1]={...$,content:P}),x})},onDone:(c,g)=>{clearInterval(k),W(!1);let x=Vr(c);T(R=>{let O=[...R],tt=O[O.length-1];return tt?.role==="brett"&&(O[O.length-1]={...tt,content:x,streaming:!1}),O});let $=[...l,{role:"assistant",content:x}];v($),i.current.record("brett",x.slice(0,300));let le=g?.completionTokens??Math.round(x.length/4),$n=g?.promptTokens??0;if(vn(R=>R+le),Sn(R=>R+Xr(le,$n,u)),s.current){let R=He(x);R&&(Fe(n,R),m("Spec saved to .brett/spec.md \u2014 review, then /build."));return}let et=Lt(x),I=nn(x);if(et.length>0){b.current=I?{cmd:I,tier:u}:null,h.current=0,Se(et);return}if(I){if(h.current>=Jr){m("(command loop limit reached \u2014 stopping)"),h.current=0;return}h.current+=1;let R=ze(I);if(R==="banned"){m(`Blocked: ${I}`);let O=[...$,{role:"user",content:`Command \`${I}\` was blocked (network/remote tools aren't allowed). Try another approach.`}];v(O),y.current(O,u);return}if(R==="dangerous"){let O=We(I)??"This command can destroy work.";S.current={cmd:I,history:$,tier:u},C(!0),m(`\u26A0 STOP \u2014 DESTRUCTIVE COMMAND
|
|
84
|
+
|
|
85
|
+
${I}
|
|
86
|
+
|
|
87
|
+
${O}
|
|
88
|
+
|
|
89
|
+
Only run this if you are certain. Type "yes" to confirm, anything else to skip.`);return}if(R==="needs-permission"){S.current={cmd:I,history:$,tier:u},C(!0),m(`Run this command? ${I} [y/n]`);return}Z(I,$,u);return}h.current=0},onError:c=>{clearInterval(k),W(!1),T(g=>g.filter((x,$)=>!($===g.length-1&&x.streaming))),m(`Something went wrong: ${c.message}`),u==="core"?(oe("advanced"),Y(!0),m("Try a more capable model? [y/n]")):u==="advanced"&&(oe("max"),Y(!0),m("Try the most capable model? [y/n]"))}};u==="core"?$t(l,d):u==="advanced"?It(l,d):Ot(l,d)},[n,m,Z]);y.current=z;let Ae=X(l=>{W(!0),ie(0),ae(0),Ye("advanced");let u=Date.now(),A=setInterval(()=>ie(Math.floor((Date.now()-u)/1e3)),250);T(P=>[...P,{role:"brett",content:"",tier:"advanced",streaming:!0}]);let k="";Rt(Jt,l,{onToken:P=>{k+=P,ae(d=>d+1),T(d=>{let c=[...d],g=c[c.length-1];return g?.role==="brett"&&(c[c.length-1]={...g,content:k}),c})},onDone:P=>{clearInterval(A),W(!1),T(c=>{let g=[...c],x=g[g.length-1];return x?.role==="brett"&&(g[g.length-1]={...x,content:P,streaming:!1}),g}),v(c=>[...c,{role:"assistant",content:P}]);let d=He(P);d&&(Fe(n,d),m("Spec saved to .brett/spec.md \u2014 review, then /build."))},onError:P=>{clearInterval(A),W(!1),T(d=>d.filter((c,g)=>!(g===d.length-1&&c.streaming))),m(`Something went wrong: ${P.message}`)}})},[m]),An=X(l=>{let u=l.toLowerCase().trim();if(["/quit","quit","exit","/exit"].includes(u)){let d=i.current,c=a.current;c&&c.repoId&&d.hasContent()?(m("Saving session to team memory\u2026"),Ht(c.teamId,c.repoId,d.buildTranscript()).finally(()=>r())):r();return}if(u==="/help"){m(no);return}if(u==="/usage"){m(`This session: ~$${Xe.toFixed(4)} \xB7 last tier ${ce}`);return}if(u==="/connect"||u.startsWith("/connect ")){let d=l.trim().split(/\s+/)[1]?.toLowerCase();(async()=>{let c=d?await an(d):await sn();m(c)})();return}if(u==="/plan"){Pe(!0),Ee(!0),s.current=!0,m(Ue(n)?"Planning mode (existing spec loaded \u2014 refine or /build).":"Planning mode. Describe what you want; I'll ask until it's clear.");return}if(u==="/build"){let d=Ue(n);if(!d){m("No spec yet \u2014 use /plan first.");return}Ee(!1),s.current=!1,h.current=0,m("Handing the spec to the coder.");let c=[...E,{role:"user",content:Xt(d)}];v(c),z(c,"core");return}if(u.startsWith("/topup")){let d=parseInt(u.split(/\s+/)[1]??"20",10)||20,c=a.current;if(!c){m("Log in first (npm run login).");return}(async()=>{let g=await H();if(!g){m("Log in first (npm run login).");return}try{let x=await fetch(`${process.env.PLATFORM_API_URL??"http://localhost:8000"}/v1/billing/checkout`,{method:"POST",headers:{Authorization:`Bearer ${g}`,"Content-Type":"application/json"},body:JSON.stringify({team_id:c.teamId,amount_usd:d})}),$=await x.json();if(x.ok&&$.checkout_url){let{exec:le}=await import("child_process");le(`start "" "${$.checkout_url}"`),m(`Opening checkout in your browser ($${d})\u2026`)}else m(`Top-up failed: ${$.detail??x.status}`)}catch(x){m(`Top-up failed: ${x instanceof Error?x.message:"network error"}`)}})();return}if(eo(l)){o.current=!0,Y(!1),ve(!1),m("\u26A1 Max tier \u2014 premium rate (~30\xD7 core cost). Use for showstopper problems only.");return}if(Je){let d=["y","yes","yeah","yep","sure","ok","okay"].includes(u);Y(!1),d?(i.current.record("escalation",`to ${re}`),m(re==="max"?"\u26A1 Max tier engaged \u2014 premium rate (~30\xD7 core cost).":"Using a more capable model."),z(E,re)):(ve(!0),m("Staying on the fast model."));return}if(Ke){let d=["y","yes","yeah","yep","sure","ok","okay"].includes(u);Ve(!1);let c=Qe.current;if(d){Pe(!0),Ee(!0),s.current=!0,m("Planning mode. Let me ask a few questions first.");let g=[...E,{role:"user",content:c}];v(g),Ae(g)}else{Pe(!0),m("No problem \u2014 coding it directly. (Tip: /plan anytime you want me to scope a feature first.)");let g=[...E,{role:"user",content:c}];v(g),z(g,"core")}return}if(p){let d=["y","yes","yeah","yep","sure","ok","okay"].includes(u);C(!1);let c=S.current;if(S.current=null,!c)return;if(d)Z(c.cmd,c.history,c.tier);else{m("Command skipped.");let g=[...c.history,{role:"user",content:`The user declined to run \`${c.cmd}\`. Continue without it.`}];v(g),y.current(g,c.tier)}return}if(o.current){o.current=!1,h.current=0;let{apiContent:d}=xe(l,n);T(g=>[...g,{role:"user",content:l}]);let c=[...E,{role:"user",content:d}];v(c),z(c,"max");return}if(s.current){i.current.record("user",l);let{apiContent:d,exp:c}=xe(l,n);c.files.length&&m(`Read: ${c.files.join(", ")}`),c.misses.length&&m(`Couldn't find: ${c.misses.join(", ")}`),T(x=>[...x,{role:"user",content:l}]);let g=[...E,{role:"user",content:d}];v(g),Ae(g);return}if(!s.current&&!Ze&&to(l)){Qe.current=l,T(d=>[...d,{role:"user",content:l},{role:"brett",content:"This sounds like a feature worth planning first \u2014 want me to ask a few questions before we code? [y/n]",tier:"core"}]),Ve(!0);return}if(Qr(l)){let d=ce==="advanced"||ce==="max"?"max":"advanced";i.current.record("correction",l),T(c=>[...c,{role:"user",content:l},{role:"brett",content:d==="max"?"Still rough \u2014 bring in the most capable model? [y/n]":"Want me to try a more capable model? [y/n]",tier:"core"}]),v(c=>[...c,{role:"user",content:l}]),oe(d),Y(!0);return}if(!Ce&&un(l)){T(d=>[...d,{role:"user",content:l},{role:"brett",content:"This looks hard. Use a more capable model? [y/n]",tier:"core"}]),v(d=>[...d,{role:"user",content:l}]),oe("advanced"),Y(!0);return}Ce&&!un(l)&&ve(!1),h.current=0,i.current.record("user",l);let{apiContent:A,exp:k}=xe(l,n);k.files.length&&m(`Read: ${k.files.join(", ")}`),k.misses.length&&m(`Couldn't find: ${k.misses.join(", ")}`),T(d=>[...d,{role:"user",content:l}]);let P=[...E,{role:"user",content:A}];v(P),z(P,"core")},[E,Je,Ke,p,Ze,re,Ce,ce,Xe,n,z,Ae,Z,r,m]),_n=se.length===0;return ro(qr,{flexDirection:"column",paddingX:1,children:[ke(st,{messages:w,header:ke(ot,{repo:e})}),se.length>0?ke(mt,{filePath:se[0].filePath,diffText:se[0].diffText,onAccept:En,onReject:Pn}):null,_n&&ke(dt,{onSubmit:An,isThinking:wn,elapsedSeconds:kn,liveTokens:Cn,planMode:Tn})]})};import{jsx as po}from"react/jsx-runtime";var[,,V,...ao]=process.argv;if(V==="login"){await import("dotenv/config");let{runLogin:e}=await Promise.resolve().then(()=>(hn(),gn));await e(),process.exit(0)}if(V==="connect"){let{runConnect:e}=await Promise.resolve().then(()=>(yn(),bn));await e(ao[0]),process.exit(0)}(V==="--version"||V==="-v")&&(console.log("brett v0.1.0"),process.exit(0));(V==="--help"||V==="-h")&&(console.log(`Usage: brett [command]
|
|
90
|
+
|
|
91
|
+
Commands:
|
|
92
|
+
(none) start the coding assistant
|
|
93
|
+
login sign in to Brett
|
|
94
|
+
connect [service] set up integrations (e.g. brett connect github)
|
|
95
|
+
--version, -v show version
|
|
96
|
+
--help, -h show this help`),process.exit(0));await import("dotenv/config");function lo(){let e=process.cwd();try{let t=xn("git rev-parse --show-toplevel",{cwd:e,encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim(),n=t.split(/[/\\]/).pop()??null,r=[`Repository: ${n}`];try{let o=xn("git log --oneline -10",{cwd:t,encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim();o&&(r.push(`
|
|
97
|
+
Recent commits:`),r.push(o))}catch{}return{name:n,context:r.join(`
|
|
98
|
+
`),root:t}}catch{return{name:null,context:null,root:process.cwd()}}}var{name:uo,context:mo,root:fo}=lo();co(po(mn,{repo:uo,repoContext:mo,repoRoot:fo}),{exitOnCtrlC:!0,patchConsole:!1});
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "brettai",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Brett — coding agent",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"brett": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist/"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"dev": "tsx src/index.tsx",
|
|
14
|
+
"build": "node build.mjs",
|
|
15
|
+
"login": "tsx src/login.ts",
|
|
16
|
+
"connect": "tsx src/connect.ts"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"diff": "^9.0.0",
|
|
20
|
+
"dotenv": "^17.4.2",
|
|
21
|
+
"ink": "^5.0.0",
|
|
22
|
+
"ink-text-input": "^6.0.0",
|
|
23
|
+
"ink-use-stdout-dimensions": "^1.0.5",
|
|
24
|
+
"react": "^18.0.0"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/diff": "^7.0.2",
|
|
28
|
+
"@types/node": "^20.19.42",
|
|
29
|
+
"@types/react": "^18.0.0",
|
|
30
|
+
"esbuild": "^0.25.0",
|
|
31
|
+
"tsx": "^4.0.0",
|
|
32
|
+
"typescript": "^5.0.0"
|
|
33
|
+
}
|
|
34
|
+
}
|