document360-writer 0.5.28 → 0.5.30

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.
Files changed (2) hide show
  1. package/dist/cli.js +2 -2
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -263,7 +263,7 @@ Articles:
263
263
  `),truncated:c>0||m>0}}}return{text:e,truncated:!1}}function hl(e){let t=!1,n=0,o=0;for(let r of e.split(`
264
264
  `)){let s=o+r.length;/^\s*```/.test(r)?t=!t:!t&&r.trim()===""&&s<e.length&&(n=s+1),o=s+1}return n}import{Fragment as fs,jsx as E,jsxs as B}from"react/jsx-runtime";var Bf={project:".d360-writer.json",user:"/model",env:"ANTHROPIC_MODEL","claude-settings":"Claude Code settings","claude-default":""},$l=` 1. /init \u2014 pick your Document360 environment & scaffold config
265
265
  2. /login \u2014 sign in & pick your Document360 project (workspace auto-selected)
266
- 3. I'll hand you a one-paste prompt for your builder \u2014 then just ask me to write the docs`;function Yf(e,t,n,o){let r=ms(n),s=r.state==="ready"?r.kind==="api"?"API key":"subscription":r.state==="maybe"?"subscription":"not signed in \u2014 run /doctor (needs Claude Code, not Claude Desktop)",c=hs(e),p=Q(e),m=(p?.docsDir??an).replace(/\/+$/,""),v=p?.mode==="engineer"?"engineer \xB7 full source access (dogfooding)":`writer \xB7 edits limited to ${m}/ + config`,b={version:t,claude:s,model:c.model??"Claude Code default model",modelSource:Bf[c.source],who:null,sessionHint:null,profile:"\u2014",apiUrl:"\u2014",project:"\u2014",cwd:e,prod:!1,loggedOut:!0,configured:p!==null,mode:v};if(p===null)return b;try{let D=ue(e,o);b.profile=D.name,b.apiUrl=D.connection.apiUrl,b.prod=D.production,b.project=D.project.projectName??D.project.projectId??"(chosen at login)";let k=Ye(D.name);if(k){let R={..._t(k.idToken)??{},..._t(k.accessToken)??{}},C=R.email??R.preferred_username??"signed in";_e(k)?k.refreshToken&&(b.who=C,b.loggedOut=!1,b.sessionHint="session expired \u2014 refreshing\u2026"):(b.who=C,b.loggedOut=!1,b.sessionHint=`session valid until ${new Date(k.expiresAt).toLocaleString(void 0,{hour:"2-digit",minute:"2-digit",day:"2-digit",month:"short"})}`)}}catch{}return b}function Gf(e,t){try{let n=ue(e,t),o=Ye(n.name);if(!o)return{text:`profile "${n.name}" \u2014 not logged in (/login)`,prod:n.production};let r={..._t(o.idToken)??{},..._t(o.accessToken)??{}},s=r.email??r.preferred_username??"signed in";return _e(o)&&!o.refreshToken?{text:`profile "${n.name}" \u2014 session expired (/login)`,prod:n.production}:{text:`${s} \xB7 profile "${n.name}"`,prod:n.production}}catch(n){return{text:n.message.split(".")[0],prod:!1}}}var vl=["Drafting","Composing","Outlining","Researching","Documenting","Structuring","Polishing","Synthesizing","Curating","Distilling","Weaving","Wrangling","Pondering"],yo=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],Vf="Ask me to write or update an article\u2026";function ho({ch:e,dim:t}){let[n,o]=F(!0);return ve(()=>{let r=setInterval(()=>o(s=>!s),530);return()=>clearInterval(r)},[]),E(S,{inverse:n,color:t&&!n?"gray":void 0,children:e})}var zf=/^mcp__document360__d360_(create_article|update_article|fork_article|publish_article)$/;function Cl(e){let t=e.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n/);return t?e.slice(t[0].length):e}var Kf=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function Pl(e){try{return wo(e,{withFileTypes:!0}).filter(t=>t.isDirectory()&&!t.name.startsWith(".")).length>6}catch{return!1}}function Rl(e,t){let n=t??[];return n.length===0?["src","api","services","packages","modules"].some(o=>Pl(ut(e,o))):n.some(o=>!o.includes("/")&&!o.endsWith(".md")&&Pl(ut(e,o)))}function Tl(e,t){try{let n=Q(e);if(!n)return!1;let o=ue(e,t),r=go(e,o.name);if(r&&Object.keys(r.articles).length>0)return!0;let s=ut(e,(n.docsDir??an).replace(/\/+$/,""));return!!(Be(s)&&wo(s).length>0||Be(gs(e)))}catch{return!1}}function Xf({startTime:e,chars:t}){let[n,o]=F(0);ve(()=>{let m=setInterval(()=>o(v=>v+1),120);return()=>clearInterval(m)},[]);let r=yo[n%yo.length],s=vl[Math.floor(n/16)%vl.length],c=Math.floor((Date.now()-e)/1e3),p=Math.round(t/4);return B(te,{children:[E(S,{color:U,children:` ${r} ${s}\u2026 `}),E(S,{color:"gray",children:`(${Jt(c)} \xB7 ~${p} tokens \xB7 esc to interrupt)`})]})}var Jf=12e4;function Qf({p:e}){let[t,n]=F(0);ve(()=>{let N=setInterval(()=>n(H=>H+1),150);return()=>clearInterval(N)},[]);let o=yo[t%yo.length],r=Math.floor((Date.now()-e.startedAt)/1e3),s=Math.floor((Date.now()-e.lastAt)/1e3),c=Math.round(e.chars/4),p=Date.now()-e.lastAt>=Jf,m=e.verb??"Converting",v=e.unit??"article",b=e.rows.reduce((N,H)=>N+H.items,0),D=m.toLowerCase(),k=e.rows,R=0;if(k.length>co){let N=k.findIndex(se=>se.status==="active"||se.status==="pending"),H=Math.max(0,Math.min(N-2,k.length-co));R=k.slice(0,H).filter(se=>se.status==="done").length,k=k.slice(H,H+co)}let C=Math.min(40,Math.max(...k.map(N=>as(N.label).length),0)),T=N=>N==="failed"?"red":N==="active"?U:void 0;return B(te,{flexDirection:"column",children:[B(te,{children:[E(S,{color:U,children:` ${o} ${m} ${b} ${v}${b===1?"":"s"} `}),E(S,{color:"gray",children:`\xB7 ${e.done}/${e.total} \xB7 ${e.tools} tool call${e.tools===1?"":"s"} \xB7 ~${c} tokens \xB7 ${Jt(r)} \xB7 esc to stop`})]}),R>0&&E(S,{dimColor:!0,children:` \u2714 +${R} done`}),k.map(N=>B(S,{color:T(N.status),dimColor:N.status==="done"||N.status==="pending",children:[` ${ol[N.status]} ${as(N.label).padEnd(C)}`,N.status!=="pending"?` (${N.items} ${v}${N.items===1?"":"s"})`:"",N.status==="active"?` \u2190 ${D}\u2026`:""]},N.label)),p&&E(S,{color:"yellow",children:` \u26A0 no activity for ${Jt(s)} \u2014 press esc to stop if it's wedged`})]})}var Zf=10800*1e3;function Sl({cwd:e,auth:t,profileName:n,version:o}){let{exit:r}=sf(),[s,c]=F(n),[p,m]=F(null),[v,b]=F({text:"",pos:0}),D=v.text,k=V(l=>{b(i=>{let a=typeof l=="function"?l(i.text):l;return{text:a,pos:a.length}})},[]),R=V(l=>{b(i=>({text:i.text.slice(0,i.pos)+l+i.text.slice(i.pos),pos:i.pos+l.length}))},[]),C=V(()=>{b(l=>{if(l.pos===0)return l;let i=l.text.slice(0,l.pos),a=dl(i)??i.slice(0,-1);return{text:a+l.text.slice(l.pos),pos:a.length}})},[]),[T,N]=F(!1),[H,se]=F(!1),[Ce,ie]=F(0),[Pe,le]=F(null),[Ge,Y]=F([]),dt=X(0),[_l,ln]=F(!1),[Ml,cn]=F(!1),[ko,bs]=F(null),[Ol,pt]=F(0),Ve=X(0),[Mt,ft]=F(0),[mt,un]=F(null),xs=X(new Map),Ll=X(0),ht=X([]),ge=X(null);ge.current===null&&(ge.current=gl({cwd:e,profileName:s,allowProdWrites:!1}));let Se=X({uuid:null,firstPrompt:null,titleFired:!1}),dn=X(new Map),gt=X(!1),bo=X([]),Oe=X([]),xo=X(null),[$s,ze]=F(null),$o=X(null),[Ke,Xe]=F(null),[De,wt]=F(null),[Le,yt]=F(null),[de,kt]=F(null),[we,Je]=F(null),[Qe,bt]=F(null),[vs,Ze]=F(null),[ye,et]=F(null),[fe,xt]=F(null),[Cs,vo]=F([]),tt=X([]),Ot=X(!1),ke=X(null),[Ps,Z]=F(null),pn=X(!1),fn=X(null),Lt=X(null),[be,nt]=F(null),[Co,Ut]=F(null),[Rs,$t]=F(0),[Ts,vt]=F(0),[Ul,Ss]=F(0),Po=cs(()=>{try{return hs(e).model??"auto"}catch{return null}},[e,Ul]),{stdout:Re}=lf(),[,Wl]=F(0),Ds=X(`${Re.columns??80}x${Re.rows??24}`),Ue=V(()=>Math.max(20,(Re.columns??80)-1),[Re]),d=V(l=>{ht.current.push(l),console.log(or(l,Ue()))},[Ue]),[Ro,To]=F(null),Ct=V(l=>{To(l),d({kind:"note",tone:"info",text:"\u2328 Press c to copy the prompt above to your clipboard (then paste it into your coding agent)."})},[d]),So=X(!1),Do=V(async()=>{try{if(So.current||!Uf(Lf())||Q(e)?.mode==="engineer")return;let l=await Of({pkg:lt,currentVersion:o,force:!0});if(!l.updateAvailable||!l.latest)return;So.current=!0,d({kind:"note",tone:"info",text:`\u2B06 Updating ${lt} ${l.current} \u2192 ${l.latest}\u2026 (wait for the \u2713 before you /exit)`}),cn(!0),(await Jn().finally(()=>cn(!1))).ok?(d({kind:"note",tone:"ok",text:`\u2713 Updated to v${l.latest} \u2014 run /exit, then relaunch to apply.`}),Y(a=>a.includes("/exit")?a:[...a,"/exit"]),bs(l.latest)):d({kind:"note",tone:"warn",text:`Update available (v${l.latest}) but auto-update failed \u2014 run: ${Xn}`})}catch{}},[e,o,d]),jo=V(l=>{let i=Ie(e,s);return i.ok?!0:(d({kind:"note",tone:"warn",text:i.reason}),Lt.current=l,Y(["/login"]),!1)},[e,s,d]),[Ao,Eo]=F(null),Wt=X(!0),mn=X(0),Ft=X(0);ve(()=>{let l=setInterval(()=>{if(Wt.current){mn.current=0,Ft.current=0;return}if(!T)try{if(!Be(gs(e)))return;if(Ee(e).status==="current"){Wt.current=!0,mn.current=0,Ft.current=0,d({kind:"note",tone:"ok",text:"\u2713 Your builder finished the hand-off \u2014 the architecture brief is in. I can read this repo now and write accurate docs."}),To(null),le("write the docs for this repo");return}mn.current+=1;let i=mn.current;if((i===4||i%15===0)&&Ft.current<3){Ft.current+=1;let a=Ft.current===3?" (I'll stay quiet now \u2014 type anything to continue.)":"";d({kind:"note",tone:"info",text:zt(e)?`\u{1F440} Your builder is working on the architecture brief \u2014 I'll continue automatically when it finishes.${a}`:`\u23F3 Waiting for your builder to run the prompt \u2014 paste it into your coding agent; I'll pick up automatically when it's done.${a}`})}}catch{}},4e3);return()=>clearInterval(l)},[e,T,d]),ve(()=>{let l=`d360-writer \xB7 ${$s??sn(e)}`;if(!T){ro(0),Nt(`${io} ${l}`);return}ro(3);let i=0;Nt(`${so[0]} ${l}`);let a=setInterval(()=>{i=(i+1)%so.length,Nt(`${so[i]} ${l}`)},400);return()=>clearInterval(a)},[T,e,$s]),ve(()=>()=>ro(0),[]);let Io=V(l=>{try{let i=Q(e);if(!i)return{kind:"idle"};let a=ue(e,s),g=Ye(a.name),u=!!g&&(!_e(g)||!!g.refreshToken),h=Tl(e,s)||!Rl(e,i.authoritativeSourceFiles),w=ms(t).state==="missing",y=go(e,a.name),P=y?Object.keys(y.articles).length:0,j=ut(e,(i.docsDir??an).replace(/\/+$/,"")),G=!1;try{G=Be(j)&&wo(j).length>0}catch{}let ee=0;if(y?.lastAnalyzedCommit)try{let A=cf("git",["rev-list","--count",`${y.lastAnalyzedCommit}..HEAD`],{cwd:e,encoding:"utf8",timeout:1200});A.status===0&&(ee=parseInt((A.stdout||"").trim(),10)||0)}catch{}let _=0;try{let A=xl(e);if(A.length>0){let L=(i.docsDir??an).replace(/\/+$/,"");_=vr(e,L,A.map(J=>J.path)).length}}catch{}let x={ready:u&&h&&!w,hasDocs:G,tracked:P,pendingText:It(Et(e)),behind:ee,pendingWrites:_,autoAudit:!!i.autoAudit,profileName:a.name,allowAutoAudit:l};return il(x)}catch{return{kind:"idle"}}},[e,s,t]);ve(()=>{let l=performance.now(),i=[],a=(w,y)=>{let P=performance.now();try{return y()}finally{let j=Math.round(performance.now()-P);j>=80&&i.push(`${w}: ${j}ms`)}};d({kind:"banner",info:a("banner",()=>Yf(e,o,t,s))});let g=ms(t).state==="missing";if(g&&d({kind:"note",tone:"warn",text:`\u26A0 Set up Claude first \u2014 d360-writer's agent runs on Claude Code, and it's not signed in on this machine yet.
266
+ 3. I'll hand you a one-paste prompt for your builder \u2014 then just ask me to write the docs`;function Yf(e,t,n,o){let r=ms(n),s=r.state==="ready"?r.kind==="api"?"API key":"subscription":r.state==="maybe"?"subscription":"not signed in \u2014 run /doctor (needs Claude Code, not Claude Desktop)",c=hs(e),p=Q(e),m=(p?.docsDir??an).replace(/\/+$/,""),v=p?.mode==="engineer"?"engineer \xB7 full source access (dogfooding)":`writer \xB7 edits limited to ${m}/ + config`,b={version:t,claude:s,model:c.model??"Claude Code default model",modelSource:Bf[c.source],who:null,sessionHint:null,profile:"\u2014",apiUrl:"\u2014",project:"\u2014",cwd:e,prod:!1,loggedOut:!0,configured:p!==null,mode:v};if(p===null)return b;try{let D=ue(e,o);b.profile=D.name,b.apiUrl=D.connection.apiUrl,b.prod=D.production,b.project=D.project.projectName??D.project.projectId??"(chosen at login)";let k=Ye(D.name);if(k){let R={..._t(k.idToken)??{},..._t(k.accessToken)??{}},C=R.email??R.preferred_username??"signed in";_e(k)?k.refreshToken&&(b.who=C,b.loggedOut=!1,b.sessionHint="session expired \u2014 refreshing\u2026"):(b.who=C,b.loggedOut=!1,b.sessionHint=`session valid until ${new Date(k.expiresAt).toLocaleString(void 0,{hour:"2-digit",minute:"2-digit",day:"2-digit",month:"short"})}`)}}catch{}return b}function Gf(e,t){try{let n=ue(e,t),o=Ye(n.name);if(!o)return{text:`profile "${n.name}" \u2014 not logged in (/login)`,prod:n.production};let r={..._t(o.idToken)??{},..._t(o.accessToken)??{}},s=r.email??r.preferred_username??"signed in";return _e(o)&&!o.refreshToken?{text:`profile "${n.name}" \u2014 session expired (/login)`,prod:n.production}:{text:`${s} \xB7 profile "${n.name}"`,prod:n.production}}catch(n){return{text:n.message.split(".")[0],prod:!1}}}var vl=["Drafting","Composing","Outlining","Researching","Documenting","Structuring","Polishing","Synthesizing","Curating","Distilling","Weaving","Wrangling","Pondering"],yo=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],Vf="Ask me to write or update an article\u2026";function ho({ch:e,dim:t}){let[n,o]=F(!0);return ve(()=>{let r=setInterval(()=>o(s=>!s),530);return()=>clearInterval(r)},[]),E(S,{inverse:n,color:t&&!n?"gray":void 0,children:e})}var zf=/^mcp__document360__d360_(create_article|update_article|fork_article|publish_article)$/;function Cl(e){let t=e.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n/);return t?e.slice(t[0].length):e}var Kf=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function Pl(e){try{return wo(e,{withFileTypes:!0}).filter(t=>t.isDirectory()&&!t.name.startsWith(".")).length>6}catch{return!1}}function Rl(e,t){let n=t??[];return n.length===0?["src","api","services","packages","modules"].some(o=>Pl(ut(e,o))):n.some(o=>!o.includes("/")&&!o.endsWith(".md")&&Pl(ut(e,o)))}function Tl(e,t){try{let n=Q(e);if(!n)return!1;let o=ue(e,t),r=go(e,o.name);if(r&&Object.keys(r.articles).length>0)return!0;let s=ut(e,(n.docsDir??an).replace(/\/+$/,""));return!!(Be(s)&&wo(s).length>0||Be(gs(e)))}catch{return!1}}function Xf({startTime:e,chars:t}){let[n,o]=F(0);ve(()=>{let m=setInterval(()=>o(v=>v+1),120);return()=>clearInterval(m)},[]);let r=yo[n%yo.length],s=vl[Math.floor(n/16)%vl.length],c=Math.floor((Date.now()-e)/1e3),p=Math.round(t/4);return B(te,{children:[E(S,{color:U,children:` ${r} ${s}\u2026 `}),E(S,{color:"gray",children:`(${Jt(c)} \xB7 ~${p} tokens \xB7 esc to interrupt)`})]})}var Jf=12e4;function Qf({p:e}){let[t,n]=F(0);ve(()=>{let N=setInterval(()=>n(H=>H+1),150);return()=>clearInterval(N)},[]);let o=yo[t%yo.length],r=Math.floor((Date.now()-e.startedAt)/1e3),s=Math.floor((Date.now()-e.lastAt)/1e3),c=Math.round(e.chars/4),p=Date.now()-e.lastAt>=Jf,m=e.verb??"Converting",v=e.unit??"article",b=e.rows.reduce((N,H)=>N+H.items,0),D=m.toLowerCase(),k=e.rows,R=0;if(k.length>co){let N=k.findIndex(se=>se.status==="active"||se.status==="pending"),H=Math.max(0,Math.min(N-2,k.length-co));R=k.slice(0,H).filter(se=>se.status==="done").length,k=k.slice(H,H+co)}let C=Math.min(40,Math.max(...k.map(N=>as(N.label).length),0)),T=N=>N==="failed"?"red":N==="active"?U:void 0;return B(te,{flexDirection:"column",children:[B(te,{children:[E(S,{color:U,children:` ${o} ${m} ${b} ${v}${b===1?"":"s"} `}),E(S,{color:"gray",children:`\xB7 ${e.done}/${e.total} \xB7 ${e.tools} tool call${e.tools===1?"":"s"} \xB7 ~${c} tokens \xB7 ${Jt(r)} \xB7 esc to stop`})]}),R>0&&E(S,{dimColor:!0,children:` \u2714 +${R} done`}),k.map(N=>B(S,{color:T(N.status),dimColor:N.status==="done"||N.status==="pending",children:[` ${ol[N.status]} ${as(N.label).padEnd(C)}`,N.status!=="pending"?` (${N.items} ${v}${N.items===1?"":"s"})`:"",N.status==="active"?` \u2190 ${D}\u2026`:""]},N.label)),p&&E(S,{color:"yellow",children:` \u26A0 no activity for ${Jt(s)} \u2014 press esc to stop if it's wedged`})]})}var Zf=10800*1e3;function Sl({cwd:e,auth:t,profileName:n,version:o}){let{exit:r}=sf(),[s,c]=F(n),[p,m]=F(null),[v,b]=F({text:"",pos:0}),D=v.text,k=V(l=>{b(i=>{let a=typeof l=="function"?l(i.text):l;return{text:a,pos:a.length}})},[]),R=V(l=>{b(i=>({text:i.text.slice(0,i.pos)+l+i.text.slice(i.pos),pos:i.pos+l.length}))},[]),C=V(()=>{b(l=>{if(l.pos===0)return l;let i=l.text.slice(0,l.pos),a=dl(i)??i.slice(0,-1);return{text:a+l.text.slice(l.pos),pos:a.length}})},[]),[T,N]=F(!1),[H,se]=F(!1),[Ce,ie]=F(0),[Pe,le]=F(null),[Ge,Y]=F([]),dt=X(0),[_l,ln]=F(!1),[Ml,cn]=F(!1),[ko,bs]=F(null),[Ol,pt]=F(0),Ve=X(0),[Mt,ft]=F(0),[mt,un]=F(null),xs=X(new Map),Ll=X(0),ht=X([]),ge=X(null);ge.current===null&&(ge.current=gl({cwd:e,profileName:s,allowProdWrites:!1}));let Se=X({uuid:null,firstPrompt:null,titleFired:!1}),dn=X(new Map),gt=X(!1),bo=X([]),Oe=X([]),xo=X(null),[$s,ze]=F(null),$o=X(null),[Ke,Xe]=F(null),[De,wt]=F(null),[Le,yt]=F(null),[de,kt]=F(null),[we,Je]=F(null),[Qe,bt]=F(null),[vs,Ze]=F(null),[ye,et]=F(null),[fe,xt]=F(null),[Cs,vo]=F([]),tt=X([]),Ot=X(!1),ke=X(null),[Ps,Z]=F(null),pn=X(!1),fn=X(null),Lt=X(null),[be,nt]=F(null),[Co,Ut]=F(null),[Rs,$t]=F(0),[Ts,vt]=F(0),[Ul,Ss]=F(0),Po=cs(()=>{try{return hs(e).model??"auto"}catch{return null}},[e,Ul]),{stdout:Re}=lf(),[,Wl]=F(0),Ds=X(`${Re.columns??80}x${Re.rows??24}`),Ue=V(()=>Math.max(20,(Re.columns??80)-1),[Re]),d=V(l=>{ht.current.push(l),console.log(or(l,Ue()))},[Ue]),[Ro,To]=F(null),Ct=V(l=>{To(l),d({kind:"note",tone:"info",text:"\u2328 Press c to copy the prompt above to your clipboard (then paste it into your coding agent)."})},[d]),So=X(!1),Do=V(async()=>{try{if(So.current||!Uf(Lf())||Q(e)?.mode==="engineer")return;let l=await Of({pkg:lt,currentVersion:o,force:!0});if(!l.updateAvailable||!l.latest)return;So.current=!0,d({kind:"note",tone:"info",text:`\u2B06 Updating ${lt} ${l.current} \u2192 ${l.latest}\u2026 (wait for the \u2713 before you /exit)`}),cn(!0),(await Jn().finally(()=>cn(!1))).ok?(d({kind:"note",tone:"ok",text:`\u2713 Updated to v${l.latest} \u2014 run /exit, then relaunch to apply.`}),Y(a=>a.includes("/exit")?a:[...a,"/exit"]),bs(l.latest)):d({kind:"note",tone:"warn",text:`Update available (v${l.latest}) but auto-update failed \u2014 run: ${Xn}`})}catch{}},[e,o,d]),jo=V(l=>{let i=Ie(e,s);return i.ok?!0:(d({kind:"note",tone:"warn",text:i.reason}),Lt.current=l,Y(["/login"]),!1)},[e,s,d]),[Ao,Eo]=F(null),Wt=X(!0),mn=X(0),Ft=X(0);ve(()=>{let l=setInterval(()=>{if(Wt.current){mn.current=0,Ft.current=0;return}if(!T)try{if(!Be(gs(e)))return;if(Ee(e).status==="current"){Wt.current=!0,mn.current=0,Ft.current=0,d({kind:"note",tone:"ok",text:"\u2713 Your builder finished the hand-off \u2014 the architecture brief is in. I can read this repo now and write accurate docs."}),To(null),le("write the docs for this repo");return}mn.current+=1;let i=mn.current;if((i===4||i%15===0)&&Ft.current<3){Ft.current+=1;let a=Ft.current===3?" (I'll stay quiet now \u2014 type anything to continue.)":"";d({kind:"note",tone:"info",text:zt(e)?`\u{1F440} Your builder is working on the architecture brief \u2014 I'll continue automatically when it finishes.${a}`:`\u23F3 Waiting for your builder to run the prompt \u2014 paste it into your coding agent; I'll pick up automatically when it's done.${a}`})}}catch{}},4e3);return()=>clearInterval(l)},[e,T,d]),ve(()=>{let l=$s??sn(e);if(!T){ro(0),Nt(`${io} ${l}`);return}ro(3);let i=0;Nt(`${so[0]} ${l}`);let a=setInterval(()=>{i=(i+1)%so.length,Nt(`${so[i]} ${l}`)},400);return()=>clearInterval(a)},[T,e,$s]),ve(()=>()=>ro(0),[]);let Io=V(l=>{try{let i=Q(e);if(!i)return{kind:"idle"};let a=ue(e,s),g=Ye(a.name),u=!!g&&(!_e(g)||!!g.refreshToken),h=Tl(e,s)||!Rl(e,i.authoritativeSourceFiles),w=ms(t).state==="missing",y=go(e,a.name),P=y?Object.keys(y.articles).length:0,j=ut(e,(i.docsDir??an).replace(/\/+$/,"")),G=!1;try{G=Be(j)&&wo(j).length>0}catch{}let ee=0;if(y?.lastAnalyzedCommit)try{let A=cf("git",["rev-list","--count",`${y.lastAnalyzedCommit}..HEAD`],{cwd:e,encoding:"utf8",timeout:1200});A.status===0&&(ee=parseInt((A.stdout||"").trim(),10)||0)}catch{}let _=0;try{let A=xl(e);if(A.length>0){let L=(i.docsDir??an).replace(/\/+$/,"");_=vr(e,L,A.map(J=>J.path)).length}}catch{}let x={ready:u&&h&&!w,hasDocs:G,tracked:P,pendingText:It(Et(e)),behind:ee,pendingWrites:_,autoAudit:!!i.autoAudit,profileName:a.name,allowAutoAudit:l};return il(x)}catch{return{kind:"idle"}}},[e,s,t]);ve(()=>{let l=performance.now(),i=[],a=(w,y)=>{let P=performance.now();try{return y()}finally{let j=Math.round(performance.now()-P);j>=80&&i.push(`${w}: ${j}ms`)}};d({kind:"banner",info:a("banner",()=>Yf(e,o,t,s))});let g=ms(t).state==="missing";if(g&&d({kind:"note",tone:"warn",text:`\u26A0 Set up Claude first \u2014 d360-writer's agent runs on Claude Code, and it's not signed in on this machine yet.
267
267
  ${df}
268
268
  Run /doctor anytime to re-check and see the fix. (You can configure Document360 meanwhile \u2014 the agent just needs Claude before it writes.)`}),!Q(e)){d({kind:"note",tone:"info",text:`Welcome! This repo isn't set up for d360-writer yet \u2014 three steps and you're writing docs:
269
269
  ${$l}`}),Y(g?["/doctor","/init"]:["/init"]);return}g&&Y(["/doctor"]);try{let w=ue(e,s),y=Ye(w.name);y&&_e(y)&&y.refreshToken?hf({profile:w.name,connection:w.connection}).then(()=>{d({kind:"note",tone:"ok",text:"\u2713 Document360 session refreshed."}),ie(P=>P+1)}).catch(()=>{d({kind:"note",tone:"warn",text:"Document360 session refresh failed \u2014 do you want to log in now? (press 1)"}),Y(["/login"])}):(!y||_e(y))&&(d({kind:"note",tone:"warn",text:`Profile "${w.name}" is not signed in \u2014 do you want to log in now? (press 1)`}),Y(["/login"]))}catch{}try{let w=Q(e),y=ue(e,s),P=Ye(y.name);w&&P&&!_e(P)&&Rl(e,w.authoritativeSourceFiles)&&!Tl(e,s)&&(d({kind:"note",tone:"info",text:"Large repo \u2014 the docs scope isn\u2019t set yet. Run /scope to choose which folders back your docs."}),Y(["/scope"]))}catch{}try{if(a("reconcile-devhints",()=>Nn(e)),Ee(e).status!=="current")if(zt(e))d({kind:"note",tone:"info",text:`\u{1F514} A newer dev\u2194docs protocol (v${ne}) is available \u2014 your builder can re-run /devhints when convenient. Your docs aren't blocked.`});else{let w=_n(e);w.length&&(d({kind:"note",tone:"warn",text:w.join(`
@@ -273,7 +273,7 @@ cwd: ${e}`}),Do()},[]),ve(()=>{let l=setInterval(()=>{Do()},Zf);return()=>clearI
273
273
  `)});let y=ar(e,s);y&&d({kind:"note",tone:"info",text:y})}}catch(u){d({kind:"note",tone:"error",text:`Publish run failed: ${u.message}`})}finally{ke.current=null,Z(null),N(!1),ie(u=>u+1)}},[e,s,d,t]),Mo=V(async l=>{await ce(ir(l),{display:`/publish ${l}`,quietUser:!0});try{let i=await Fn(e,s,l.replace(/\\/g,"/"));i.length>0&&(Ze(`Related articles not yet published (${i.length}):`),Y([`/publish --related ${l}`,...i.map(a=>`/publish ${a}`)]))}catch{}},[e,s,ce]),Oo=V(l=>{let i=Ye(l);if(!i||_e(i)&&!i.refreshToken)return null;let a={..._t(i.idToken)??{},..._t(i.accessToken)??{}};return a.email??a.preferred_username??"signed in"},[]),Lo=V((l,i)=>{if(i){let a=Q(e);a&&(a.defaultProfile=l,yl(a,e))}c(l),me(void 0,l),d({kind:"note",tone:"ok",text:`\u2713 Switched to profile "${l}"${i?" (saved as default)":" (this session only)"} \u2014 agent restarted.`}),Oo(l)||(d({kind:"note",tone:"warn",text:`Profile "${l}" is not signed in \u2014 do you want to log in now? (press 1)`}),Y(["/login"])),ie(a=>a+1)},[e,d,me,Oo]),yn=V((l,i,a)=>{Cn(e,l,i,a.id,a.name),d({kind:"note",tone:"ok",text:`Switched to workspace "${a.name??a.id}" (agent restarted).`}),me(),go(e,l)||(d({kind:"note",tone:"ok",text:"\u2713 Connected. Configuration is done \u2014 one important step remains."}),d({kind:"note",tone:"warn",text:"\u26A0 Before I write the docs, hand the prompt below to your builder (your repo's coding agent). It gives me the architecture brief that makes the docs accurate:"}),d({kind:"note",tone:"info",text:Dt(St(e)).join(`
274
274
  `)}),Ct(rt()),Wt.current=Ee(e).status==="current"||zt(e),d({kind:"note",tone:"info",text:`No separate builder agent? Then just ask me now: "write the docs for this repo" \u2014 I'll read the code directly.`}))},[e,d,me,Ct]),kn=V(async l=>{let i;try{i=await Yt(e,l)}catch{d({kind:"note",tone:"info",text:"Next: pick a workspace \u2014 run /workspace."}),Y(["/workspace"]);return}if(i.workspaces.length===0){d({kind:"note",tone:"warn",text:"This project has no workspaces yet. Create one in Document360, then run /workspace."});return}let a=i.workspaces.filter(u=>(u.workspace_type??"").toLowerCase()!=="apidocumentation"),g=a.length>0?a:i.workspaces;if(g.length===1){let u=g[0],h=i.workspaces.length-g.length;d({kind:"note",tone:"ok",text:`Selected the "${u.name??u.id}" workspace${h>0?" (skipped the API-documentation workspace)":""}.`}),yn(i.profile,i.projectId,u);return}d({kind:"note",tone:"info",text:`Next: pick the workspace your articles publish to (${g.length} available).`}),Y(["/workspace"])},[e,d,yn]),bn=V((l,i)=>{qo(e,l,i.id,i.name),d({kind:"note",tone:"ok",text:`Project set to "${i.name??i.id}" (agent restarted).`}),me(),kn(l)},[e,d,me,kn]),Is=V(async l=>{let i;try{i=ue(e,l)}catch{return}if(!i.project.workspaceId){if(i.project.projectId){if(!i.project.projectName)try{let{projects:a}=await ot(e,i.name),g=a.find(u=>u.id===i.project.projectId);g?.name&&Hf(e,i.name,{projectName:g.name})}catch{}await kn(i.name);return}try{let{projects:a}=await ot(e,i.name);if(a.length===1){let g=a[0];d({kind:"note",tone:"ok",text:`Using your Document360 project "${g.name??g.id}".`}),bn(i.name,{id:g.id,name:g.name});return}if(a.length>1){let g=a.map(u=>({id:u.id,name:u.name??u.id,sub:u.sub_domain}));d({kind:"note",tone:"info",text:"Which Document360 project should these docs publish to?"}),Je({cursor:0,current:-1,rows:g,profile:i.name,environment:i.connection.name});return}}catch{}d({kind:"note",tone:"info",text:"Next: pick the Document360 project \u2014 run /project."}),Y(["/project"])}},[e,d,kn,bn]),xn=V(()=>{let l=Oe.current[0];if(!l)return;d({kind:"note",tone:"info",text:`\u25CF ${l.title} (${l.path})`});for(let a of l.notes)d({kind:"note",tone:"warn",text:`\u26A0 ${a}`});l.overwritesLocalChanges&&d({kind:"note",tone:"warn",text:"\u26A0 This OVERWRITES local edits made since the last sync."});let i=it(l.oldContent,l.newContent,Ue());d(i?{kind:"diff",added:i.added,removed:i.removed,lines:i.lines,hidden:i.hidden}:{kind:"note",tone:"info",text:"Local file already matches the remote content \u2014 applying only advances the sync base."}),d({kind:"note",tone:"info",text:`Write ${l.path}? (y/n \u2014 anything else cancels)`})},[d,Ue]),Ns=V(l=>{if(Oe.current.length===0)return!1;let i=l.trim().toLowerCase();if(i==="y"||i==="yes"){let a=Oe.current.shift();try{Tf({cwd:e,profileName:s},a),d({kind:"note",tone:"ok",text:`\u2713 Pulled ${a.path} (sync base advanced).`}),Oe.current.length===0&&Y(g=>g.length>0?g:["/pull"])}catch(g){d({kind:"note",tone:"error",text:`Pull failed: ${g.message}`})}}else if(i==="n"||i==="no"){let a=Oe.current.shift();d({kind:"note",tone:"info",text:`Skipped ${a.path}.`})}else{let a=Oe.current.length;return Oe.current=[],d({kind:"note",tone:"info",text:`Pull cancelled (${a} article(s) left untouched).`}),!0}return xn(),!0},[e,s,xn,d]),_s=V(l=>{let i=xo.current;if(!i)return!1;if(xo.current=null,le(null),dt.current++,l.trim()!==i.repoName)return d({kind:"note",tone:"warn",text:"Reset cancelled \u2014 nothing deleted."}),!0;let{removed:a,failed:g}=es(e,i.targets);d({kind:"note",tone:g.length?"warn":"ok",text:`\u2713 Reset complete \u2014 removed ${a.length} item${a.length===1?"":"s"}. The repo is back to its original state.`});for(let u of g)d({kind:"note",tone:"error",text:` \u2717 ${u.path}: ${u.error}`});return i.hadDevhints&&(d({kind:"note",tone:"warn",text:Mn().join(`
275
275
  `)}),Ct(Ko())),d({kind:"note",tone:"info",text:`Clean slate \u2014 set up d360-writer again whenever you're ready:
276
- ${$l}`}),Y(["/init"]),ln(!1),!0},[e,d,Ct]),Ms=V(async l=>{let i=l.slice(1).trim().split(/\s+/),a=(i[0]??"").toLowerCase(),g=i.slice(1);switch(ln(!0),a){case"help":Ut({tab:0});return;case"exit":case"quit":ge.current?.close(),r();return;case"clear":me(),ht.current=[],ln(!1),le(null),dt.current++,d({kind:"note",tone:"info",text:"Conversation reset (the previous session is still resumable via /resume)."});return;case"login":{let u;try{u=ue(e,s)}catch(h){d({kind:"note",tone:"error",text:h.message});return}d({kind:"note",tone:"info",text:`Profile "${u.name}" \u2192 ${u.connection.name} (${u.connection.apiUrl})${u.production?" \u26A0 PRODUCTION":""}`}),se(!0);try{let h=await pf(u.connection,{promptForRedirect:()=>Promise.reject(new Error("Manual login is CLI-only. Run: d360-writer login --manual"))},y=>d({kind:"note",tone:"info",text:y})),w=ff(u.name,h);if(mf(w),await Bo(w,u.name,y=>d({kind:"note",tone:"info",text:y})),d({kind:"note",tone:"ok",text:`\u2713 Logged in to "${u.name}" as ${Rn(w)}`}),await Is(s),Lt.current){let y=Lt.current;Lt.current=null;let P=Ie(e,s);P.ok?(d({kind:"note",tone:"ok",text:`\u2713 Signed into the right project \u2014 press 1 to run ${y}.`}),Y([y])):(d({kind:"note",tone:"warn",text:`${P.reason} (then ${y}.)`}),Y(["/login"]),Lt.current=y)}else fn.current&&(le(fn.current),fn.current=null,d({kind:"note",tone:"info",text:"Press tab to re-send your last prompt."}))}catch(h){d({kind:"note",tone:"error",text:`Login failed: ${h.message}`})}finally{se(!1),ie(h=>h+1)}return}case"allow-prod":{let u=!1;try{u=ue(e,s).production}catch{}if(!u){d({kind:"note",tone:"info",text:"Current profile is not production \u2014 writes are already allowed."});return}gt.current=!0,me(),d({kind:"note",tone:"warn",text:"\u26A0 Production writes authorized for this session."});return}case"rename":{let u=Mr(g.join(" ")),h=Se.current.uuid;if(!h){if(!u){d({kind:"note",tone:"info",text:"Usage: /rename <name>"});return}$o.current=u,ze(u),Nt(`${io} d360-writer \xB7 ${u}`),d({kind:"note",tone:"ok",text:`Session will be named "${u}" once it starts.`});return}if(!u){d({kind:"note",tone:"info",text:"Thinking of a name\u2026"});let w=Se.current.firstPrompt??"";wl(w,e).then(y=>{y?(le(`/rename ${y}`),d({kind:"note",tone:"info",text:`Suggestion: "${y}" \u2014 press tab to accept, or type /rename <your name>.`})):d({kind:"note",tone:"info",text:"Usage: /rename <name>"})}).catch(()=>d({kind:"note",tone:"info",text:"Usage: /rename <name>"}));return}Pf(h,u),ze(u),Nt(`${io} d360-writer \xB7 ${u}`),d({kind:"note",tone:"ok",text:`Session renamed to "${u}".`});return}case"profile":{let u=g[0],h=Q(e);if(!u){let w=Object.entries(h?.profiles??{});if(w.length===0){d({kind:"note",tone:"info",text:"No profiles. Run /init first."});return}let y=w.map(([G,ee])=>({name:G,env:ee.connection?.environment??"custom",prod:ee.production===!0,who:Oo(G)})),P=s??h?.defaultProfile,j=Math.max(0,y.findIndex(G=>G.name===P));wt({cursor:j,current:j,rows:y});return}if(u==="add"){let w=Or(e,g[1],g[2]);if(w){d({kind:"note",tone:"error",text:w});return}d({kind:"note",tone:"ok",text:`\u2713 Profile "${g[1]}" created (environment: ${g[2]??g[1]}).`}),Lo(g[1],!1);return}if(!h?.profiles?.[u]){d({kind:"note",tone:"error",text:`Unknown profile "${u}". Create it: /profile add ${u} <environment>`});return}Lo(u,!0);return}case"doctor":await nn(g,{cwd:e});return;case"mcp":await Ln(g);return;case"init":{if(Q(e)){d({kind:"note",tone:"info",text:"This repo is already set up \u2014 edit .d360-writer.json directly (or d360-writer init for the CLI wizard)."});return}let u=Ef().map(h=>({name:h,apiUrl:If(h).apiUrl}));yt({cursor:0,rows:u});return}case"resume":{let u=g.join(" ").trim(),h=Cf(e).filter(P=>P.uuid!==Se.current.uuid);if(!u){if(!h.length){d({kind:"note",tone:"info",text:"No saved sessions for this repo yet."});return}nt({query:"",cursor:0,sessions:h});return}let w=vf(e,u);if(!w){d({kind:"note",tone:"error",text:`No session matches "${u}".`});return}me(w.uuid),Se.current={uuid:w.uuid,firstPrompt:w.firstPrompt,titleFired:!0},ze(w.name),ds(w.uuid),ht.current=[];let y=is(e,w.uuid);for(let P of y)d(P);d({kind:"note",tone:"ok",text:y.length?`Resumed "${w.name}" \u2014 restored ${y.length} message(s); continue where you left off.`:`Resumed "${w.name}" (agent memory reconnected; no saved transcript to replay).`});return}case"workspace":{let u=g.join(" ").trim(),h;try{h=await Yt(e,s)}catch(y){d({kind:"note",tone:"error",text:`Could not list workspaces: ${y.message}`});return}if(!u){let y=h.workspaces.map(j=>({id:j.id,name:j.name??j.id,type:j.workspace_type}));if(y.length===0){d({kind:"note",tone:"info",text:"No workspaces in this project."});return}let P=Math.max(0,y.findIndex(j=>j.id===h.current));kt({cursor:P,current:P,rows:y,profile:h.profile,projectId:h.projectId,projectName:h.projectName,environment:h.environment});return}let w=Ho(h.workspaces,u);if(!w){d({kind:"note",tone:"error",text:`No workspace matches "${u}".`});return}yn(h.profile,h.projectId,w);return}case"project":{let u=g.join(" ").trim(),h;try{h=await ot(e,s)}catch(y){d({kind:"note",tone:"error",text:`Could not list projects: ${y.message} (signed in? try /login)`});return}if(!u){let y=h.projects.map(j=>({id:j.id,name:j.name??j.id,sub:j.sub_domain}));if(y.length===0){d({kind:"note",tone:"info",text:"No projects found for this identity."});return}let P=y.findIndex(j=>j.id===h.current);Je({cursor:Math.max(0,P),current:P,rows:y,profile:h.profile,environment:h.environment});return}let w=Bs(h.projects,u);if(!w){d({kind:"note",tone:"error",text:`No project matches "${u}". Available: ${h.projects.map(y=>y.name??y.id).join(", ")}`});return}bn(h.profile,w);return}case"logout":{let u;try{u=ue(e,s).name}catch(w){d({kind:"note",tone:"error",text:w.message});return}let h=yf(u);kf(e,u),me(),ie(w=>w+1),d({kind:"note",tone:h?"ok":"info",text:h?`\u2713 Signed out of "${u}" and cleared its project/workspace selection.`:`Profile "${u}" was not signed in. Cleared any project/workspace selection.`}),d({kind:"note",tone:"info",text:"Run /login to sign in and pick a project."}),Y(["/login"]);return}case"publish":{if(!jo(l.trim()))return;if(g[0]==="--related"&&g[1]){let h=await Fn(e,s,g[1].replace(/\\/g,"/"));if(h.length===0){d({kind:"note",tone:"ok",text:"\u2713 All related articles are already published."});return}await wn(h);return}if(g[0]&&g[0]!=="--all"){await Mo(g[0]);return}let u=g[0]==="--all";d({kind:"note",tone:"info",text:"Checking what needs publishing\u2026"});try{let h=await kl({cwd:e,profileName:s}),w=Zt(h.entries),y=Un(h.entries);if(y>0&&d({kind:"note",tone:"warn",text:`\u26A0 ${y} article${y===1?"":"s"} already exist on Document360 but aren\u2019t linked \u2014 left OUT of publish (publishing would duplicate them). Run /catch-up (or ask me to link them) first.`}),w.length===0){d({kind:"note",tone:"ok",text:y>0?"\u2713 Nothing else is ahead of Document360 to publish.":"\u2713 Nothing is ahead of Document360 \u2014 no publish candidates."});let P=h.counts["unknown-base"]??0;P>0&&d({kind:"note",tone:"info",text:`(${P} article(s) have no sync base yet \u2014 publish those by path if needed.)`});return}if(u){await wn(w.map(P=>P.path));return}bt({cursor:0,rows:Ni(w)})}catch(h){d({kind:"note",tone:"error",text:`Could not compute sync status: ${h.message}`}),d({kind:"note",tone:"info",text:"Publish a specific article: /publish <article-path>"})}return}case"preview":{let u=g.join(" ").trim();if(!u){let w=[];try{w=Object.keys(go(e,ue(e,s).name)?.articles??{})}catch{}if(w.length===0){d({kind:"note",tone:"info",text:"No tracked articles to pick from yet. Usage: /preview <path-to.md | article-id>"});return}et({query:"",cursor:0,paths:w});return}let h=uf(u)?u:ut(e,u);if(Be(h)){try{d({kind:"preview",name:sn(h),text:Cl(us(h,"utf8"))})}catch(w){d({kind:"note",tone:"error",text:`Could not read ${h}: ${w.message}`})}return}if(Kf.test(u)){try{let w=ue(e,s),y={profile:w.name,connection:w.connection},P=w.project.projectId??gf(y),j=await wf(y,P,u);d({kind:"preview",name:j.title??u,text:j.content??"*(article has no content)*"})}catch(w){d({kind:"note",tone:"error",text:`Could not fetch article: ${w.message}`})}return}d({kind:"note",tone:"error",text:`"${u}" is neither a file (relative to ${e}) nor an article id.`});return}case"model":{let u=g[0]?.trim();if(!u){let P=Gn(hs(e));Xe({cursor:P,current:P});return}let{lines:h,changed:w,effective:y}=tn(e,u);for(let P of h)d({kind:"note",tone:P.startsWith("\u26A0")?"warn":P.startsWith("\u2713")?"ok":"info",text:P});w&&(Ss(P=>P+1),ge.current?.setModel(y));return}case"convert":{if(!Q(e)){d({kind:"note",tone:"error",text:"No d360-writer config here. Run /init first."});return}let{scope:u,run:h}=hr(g),w=Mf(e,s);if(w.length===0){d({kind:"note",tone:"error",text:"No tracked articles in d360-category-map.json. Publish some first (/publish), then /convert."});return}let y=gr(w,u);if(y.length===0){d({kind:"note",tone:"error",text:`No tracked articles under "${u}". (${w.length} are tracked overall.)`});return}let P=ps(y),j=3,G=`/convert${u?` --scope ${u}`:""} --run`,ee=mo(e,"light");if(!h){let x=bl({files:wr(e,y),op:"convert",model:ee.model}),A=u?`Scope: ${u} (${y.length} of ${w.length} tracked)
276
+ ${$l}`}),Y(["/init"]),ln(!1),!0},[e,d,Ct]),Ms=V(async l=>{let i=l.slice(1).trim().split(/\s+/),a=(i[0]??"").toLowerCase(),g=i.slice(1);switch(ln(!0),a){case"help":Ut({tab:0});return;case"exit":case"quit":ge.current?.close(),r();return;case"clear":me(),ht.current=[],ln(!1),le(null),dt.current++,d({kind:"note",tone:"info",text:"Conversation reset (the previous session is still resumable via /resume)."});return;case"login":{let u;try{u=ue(e,s)}catch(h){d({kind:"note",tone:"error",text:h.message});return}d({kind:"note",tone:"info",text:`Profile "${u.name}" \u2192 ${u.connection.name} (${u.connection.apiUrl})${u.production?" \u26A0 PRODUCTION":""}`}),se(!0);try{let h=await pf(u.connection,{promptForRedirect:()=>Promise.reject(new Error("Manual login is CLI-only. Run: d360-writer login --manual"))},y=>d({kind:"note",tone:"info",text:y})),w=ff(u.name,h);if(mf(w),await Bo(w,u.name,y=>d({kind:"note",tone:"info",text:y})),d({kind:"note",tone:"ok",text:`\u2713 Logged in to "${u.name}" as ${Rn(w)}`}),await Is(s),Lt.current){let y=Lt.current;Lt.current=null;let P=Ie(e,s);P.ok?(d({kind:"note",tone:"ok",text:`\u2713 Signed into the right project \u2014 press 1 to run ${y}.`}),Y([y])):(d({kind:"note",tone:"warn",text:`${P.reason} (then ${y}.)`}),Y(["/login"]),Lt.current=y)}else fn.current&&(le(fn.current),fn.current=null,d({kind:"note",tone:"info",text:"Press tab to re-send your last prompt."}))}catch(h){d({kind:"note",tone:"error",text:`Login failed: ${h.message}`})}finally{se(!1),ie(h=>h+1)}return}case"allow-prod":{let u=!1;try{u=ue(e,s).production}catch{}if(!u){d({kind:"note",tone:"info",text:"Current profile is not production \u2014 writes are already allowed."});return}gt.current=!0,me(),d({kind:"note",tone:"warn",text:"\u26A0 Production writes authorized for this session."});return}case"rename":{let u=Mr(g.join(" ")),h=Se.current.uuid;if(!h){if(!u){d({kind:"note",tone:"info",text:"Usage: /rename <name>"});return}$o.current=u,ze(u),Nt(`${io} ${u}`),d({kind:"note",tone:"ok",text:`Session will be named "${u}" once it starts.`});return}if(!u){d({kind:"note",tone:"info",text:"Thinking of a name\u2026"});let w=Se.current.firstPrompt??"";wl(w,e).then(y=>{y?(le(`/rename ${y}`),d({kind:"note",tone:"info",text:`Suggestion: "${y}" \u2014 press tab to accept, or type /rename <your name>.`})):d({kind:"note",tone:"info",text:"Usage: /rename <name>"})}).catch(()=>d({kind:"note",tone:"info",text:"Usage: /rename <name>"}));return}Pf(h,u),ze(u),Nt(`${io} ${u}`),d({kind:"note",tone:"ok",text:`Session renamed to "${u}".`});return}case"profile":{let u=g[0],h=Q(e);if(!u){let w=Object.entries(h?.profiles??{});if(w.length===0){d({kind:"note",tone:"info",text:"No profiles. Run /init first."});return}let y=w.map(([G,ee])=>({name:G,env:ee.connection?.environment??"custom",prod:ee.production===!0,who:Oo(G)})),P=s??h?.defaultProfile,j=Math.max(0,y.findIndex(G=>G.name===P));wt({cursor:j,current:j,rows:y});return}if(u==="add"){let w=Or(e,g[1],g[2]);if(w){d({kind:"note",tone:"error",text:w});return}d({kind:"note",tone:"ok",text:`\u2713 Profile "${g[1]}" created (environment: ${g[2]??g[1]}).`}),Lo(g[1],!1);return}if(!h?.profiles?.[u]){d({kind:"note",tone:"error",text:`Unknown profile "${u}". Create it: /profile add ${u} <environment>`});return}Lo(u,!0);return}case"doctor":await nn(g,{cwd:e});return;case"mcp":await Ln(g);return;case"init":{if(Q(e)){d({kind:"note",tone:"info",text:"This repo is already set up \u2014 edit .d360-writer.json directly (or d360-writer init for the CLI wizard)."});return}let u=Ef().map(h=>({name:h,apiUrl:If(h).apiUrl}));yt({cursor:0,rows:u});return}case"resume":{let u=g.join(" ").trim(),h=Cf(e).filter(P=>P.uuid!==Se.current.uuid);if(!u){if(!h.length){d({kind:"note",tone:"info",text:"No saved sessions for this repo yet."});return}nt({query:"",cursor:0,sessions:h});return}let w=vf(e,u);if(!w){d({kind:"note",tone:"error",text:`No session matches "${u}".`});return}me(w.uuid),Se.current={uuid:w.uuid,firstPrompt:w.firstPrompt,titleFired:!0},ze(w.name),ds(w.uuid),ht.current=[];let y=is(e,w.uuid);for(let P of y)d(P);d({kind:"note",tone:"ok",text:y.length?`Resumed "${w.name}" \u2014 restored ${y.length} message(s); continue where you left off.`:`Resumed "${w.name}" (agent memory reconnected; no saved transcript to replay).`});return}case"workspace":{let u=g.join(" ").trim(),h;try{h=await Yt(e,s)}catch(y){d({kind:"note",tone:"error",text:`Could not list workspaces: ${y.message}`});return}if(!u){let y=h.workspaces.map(j=>({id:j.id,name:j.name??j.id,type:j.workspace_type}));if(y.length===0){d({kind:"note",tone:"info",text:"No workspaces in this project."});return}let P=Math.max(0,y.findIndex(j=>j.id===h.current));kt({cursor:P,current:P,rows:y,profile:h.profile,projectId:h.projectId,projectName:h.projectName,environment:h.environment});return}let w=Ho(h.workspaces,u);if(!w){d({kind:"note",tone:"error",text:`No workspace matches "${u}".`});return}yn(h.profile,h.projectId,w);return}case"project":{let u=g.join(" ").trim(),h;try{h=await ot(e,s)}catch(y){d({kind:"note",tone:"error",text:`Could not list projects: ${y.message} (signed in? try /login)`});return}if(!u){let y=h.projects.map(j=>({id:j.id,name:j.name??j.id,sub:j.sub_domain}));if(y.length===0){d({kind:"note",tone:"info",text:"No projects found for this identity."});return}let P=y.findIndex(j=>j.id===h.current);Je({cursor:Math.max(0,P),current:P,rows:y,profile:h.profile,environment:h.environment});return}let w=Bs(h.projects,u);if(!w){d({kind:"note",tone:"error",text:`No project matches "${u}". Available: ${h.projects.map(y=>y.name??y.id).join(", ")}`});return}bn(h.profile,w);return}case"logout":{let u;try{u=ue(e,s).name}catch(w){d({kind:"note",tone:"error",text:w.message});return}let h=yf(u);kf(e,u),me(),ie(w=>w+1),d({kind:"note",tone:h?"ok":"info",text:h?`\u2713 Signed out of "${u}" and cleared its project/workspace selection.`:`Profile "${u}" was not signed in. Cleared any project/workspace selection.`}),d({kind:"note",tone:"info",text:"Run /login to sign in and pick a project."}),Y(["/login"]);return}case"publish":{if(!jo(l.trim()))return;if(g[0]==="--related"&&g[1]){let h=await Fn(e,s,g[1].replace(/\\/g,"/"));if(h.length===0){d({kind:"note",tone:"ok",text:"\u2713 All related articles are already published."});return}await wn(h);return}if(g[0]&&g[0]!=="--all"){await Mo(g[0]);return}let u=g[0]==="--all";d({kind:"note",tone:"info",text:"Checking what needs publishing\u2026"});try{let h=await kl({cwd:e,profileName:s}),w=Zt(h.entries),y=Un(h.entries);if(y>0&&d({kind:"note",tone:"warn",text:`\u26A0 ${y} article${y===1?"":"s"} already exist on Document360 but aren\u2019t linked \u2014 left OUT of publish (publishing would duplicate them). Run /catch-up (or ask me to link them) first.`}),w.length===0){d({kind:"note",tone:"ok",text:y>0?"\u2713 Nothing else is ahead of Document360 to publish.":"\u2713 Nothing is ahead of Document360 \u2014 no publish candidates."});let P=h.counts["unknown-base"]??0;P>0&&d({kind:"note",tone:"info",text:`(${P} article(s) have no sync base yet \u2014 publish those by path if needed.)`});return}if(u){await wn(w.map(P=>P.path));return}bt({cursor:0,rows:Ni(w)})}catch(h){d({kind:"note",tone:"error",text:`Could not compute sync status: ${h.message}`}),d({kind:"note",tone:"info",text:"Publish a specific article: /publish <article-path>"})}return}case"preview":{let u=g.join(" ").trim();if(!u){let w=[];try{w=Object.keys(go(e,ue(e,s).name)?.articles??{})}catch{}if(w.length===0){d({kind:"note",tone:"info",text:"No tracked articles to pick from yet. Usage: /preview <path-to.md | article-id>"});return}et({query:"",cursor:0,paths:w});return}let h=uf(u)?u:ut(e,u);if(Be(h)){try{d({kind:"preview",name:sn(h),text:Cl(us(h,"utf8"))})}catch(w){d({kind:"note",tone:"error",text:`Could not read ${h}: ${w.message}`})}return}if(Kf.test(u)){try{let w=ue(e,s),y={profile:w.name,connection:w.connection},P=w.project.projectId??gf(y),j=await wf(y,P,u);d({kind:"preview",name:j.title??u,text:j.content??"*(article has no content)*"})}catch(w){d({kind:"note",tone:"error",text:`Could not fetch article: ${w.message}`})}return}d({kind:"note",tone:"error",text:`"${u}" is neither a file (relative to ${e}) nor an article id.`});return}case"model":{let u=g[0]?.trim();if(!u){let P=Gn(hs(e));Xe({cursor:P,current:P});return}let{lines:h,changed:w,effective:y}=tn(e,u);for(let P of h)d({kind:"note",tone:P.startsWith("\u26A0")?"warn":P.startsWith("\u2713")?"ok":"info",text:P});w&&(Ss(P=>P+1),ge.current?.setModel(y));return}case"convert":{if(!Q(e)){d({kind:"note",tone:"error",text:"No d360-writer config here. Run /init first."});return}let{scope:u,run:h}=hr(g),w=Mf(e,s);if(w.length===0){d({kind:"note",tone:"error",text:"No tracked articles in d360-category-map.json. Publish some first (/publish), then /convert."});return}let y=gr(w,u);if(y.length===0){d({kind:"note",tone:"error",text:`No tracked articles under "${u}". (${w.length} are tracked overall.)`});return}let P=ps(y),j=3,G=`/convert${u?` --scope ${u}`:""} --run`,ee=mo(e,"light");if(!h){let x=bl({files:wr(e,y),op:"convert",model:ee.model}),A=u?`Scope: ${u} (${y.length} of ${w.length} tracked)
277
277
  `:"",L=`
278
278
  Model: ${ee.model}${ee.forced?" (forced)":" \u2014 mechanical work; /model to override"}`;d({kind:"note",tone:"info",text:A+kr(P,x,j).join(`
279
279
  `)+L}),Y([G]);return}Ve.current=Date.now(),pt(0),N(!0);let _=new AbortController;ke.current=_,Z({verb:"Converting",total:P.length,done:0,active:[],rows:rn(P),tools:0,chars:0,lastAt:Date.now(),startedAt:Date.now()}),d({kind:"note",tone:"info",text:`Converting ${y.length} articles across ${P.length} partitions (\u2264${j} agents at once) on ${ee.model}\u2026 (esc to stop)`});try{for await(let x of fo({cwd:e,partitions:P,promptFor:yr,concurrency:j,profileName:s,allowProdWrites:gt.current,model:ee.model,signal:_.signal}))if(x.type==="partition_status")x.status==="running"?Z(A=>A&&{...A,active:[...A.active,x.label],rows:Ne(A.rows,x.label,"active"),lastAt:Date.now()}):Z(A=>A&&{...A,active:A.active.filter(L=>L!==x.label),done:A.done+1,rows:Ne(A.rows,x.label,x.status==="done"?"done":"failed"),lastAt:Date.now()});else if(x.type==="partition_event")Z(A=>{if(!A)return A;let L={...A,lastAt:Date.now()};return x.event.type==="tool"?L.tools=A.tools+1:x.event.type==="text"&&(L.chars=A.chars+x.event.delta.length),L});else if(x.type==="run_done"){vt(J=>J+x.totalCostUsd),$t(J=>J+x.results.reduce((q,K)=>q+K.outputTokens,0));let A=x.aborted?"Stopped. ":"",L=t.kind==="api"?"api":"subscription";d({kind:"note",tone:x.aborted?"warn":x.ok?"ok":"warn",text:A+br(x.results,P,L).join(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "document360-writer",
3
- "version": "0.5.28",
3
+ "version": "0.5.30",
4
4
  "description": "Standalone documentation agent CLI. Reads your code, writes your docs. Specialized for Document360 publishing.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -34,7 +34,7 @@
34
34
  "@inquirer/prompts": "^8.4.3",
35
35
  "commander": "^14.0.3",
36
36
  "diff": "^8.0.4",
37
- "document360-engine": "^0.2.68",
37
+ "document360-engine": "^0.2.69",
38
38
  "ink": "^5.2.1",
39
39
  "picocolors": "^1.1.1",
40
40
  "react": "^18.3.1",