document360-writer 0.5.142 → 0.5.144

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
@@ -379,7 +379,7 @@ ${o.body}`:"","",`Close the loop IN THE SAME TURN the step's goal state is reach
379
379
  `).length;return`[Pasted text #${e} +${n} line${n===1?"":"s"}]`}function Zu(e,t){return e.replace(Rw,n=>t.get(n)??n)}function ed(e){let t=e.match(/\[Pasted text #\d+ \+\d+ lines?\]$/);return t?e.slice(0,-t[0].length):null}function Rr(e,t){let n=Math.max(1,t),o=[],r=0;for(let s of e.split(`
380
380
  `)){if(s.length===0)o.push({start:r,end:r});else for(let l=0;l<s.length;l+=n)o.push({start:r+l,end:r+Math.min(l+n,s.length)});r+=s.length+1}return o.length>0?o:[{start:0,end:0}]}function Sr(e,t){let n=0;for(let o=0;o<e.length&&e[o].start<=t;o++)n=o;return n}function td(e,t,n){let o=Sr(e,t),r=o+n;if(r<0||r>=e.length)return t;let s=Math.min(t,e[o].end)-e[o].start;return Math.min(e[r].start+s,e[r].end)}function nd(e,t,n){let o=e[Sr(e,t)];return n==="start"?o.start:o.end}function od(e,t,n){let o=Math.max(1,n),r=e.split(`
381
381
  `),s=0;for(let l=r.length-1;l>=0;l--){let d=r[l];if(s+=Math.max(1,Math.ceil(d.length/o)),s>=t){let m=s-t;return{text:[m>0?d.slice(m*o):d,...r.slice(l+1)].join(`
382
- `),truncated:l>0||m>0}}}return{text:e,truncated:!1}}function rd(e,t){if(t||e.length===0||/\s$/.test(e))return{head:e,keep:""};let n=e.search(/\s\S*$/);return n<0?{head:"",keep:e}:{head:e.slice(0,n),keep:e.slice(n)}}function sd(e){let t=!1,n=0,o=0;for(let r of e.split(`
382
+ `),truncated:l>0||m>0}}}return{text:e,truncated:!1}}function rd(e,t){if(t||e.length===0||/\s$/.test(e)||/[.!?:…]["')\]]?$/.test(e))return{head:e,keep:""};let n=e.search(/\s\S*$/);return n<0?{head:"",keep:e}:{head:e.slice(0,n),keep:e.slice(n)}}function sd(e){let t=!1,n=0,o=0;for(let r of e.split(`
383
383
  `)){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 Kn,jsx as E,jsxs as z}from"react/jsx-runtime";var my={project:".d360-writer/config.json",user:"/model",env:"ANTHROPIC_MODEL",auto:"auto \u2014 right-sized for docs \xB7 /model to change"},$d=` 1. /init \u2014 pick your Document360 environment & scaffold config
384
384
  2. /login \u2014 sign in & pick your Document360 project (workspace auto-selected)
385
385
  3. I'll hand you a one-paste prompt for your builder \u2014 then just ask me to write the docs`;function gy(e,t,n,o){let r=aa(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)",l=la(e),d=re(e),m=(d?.docsDir??Rd).replace(/\/+$/,""),y=d?.mode==="engineer"?"engineer \xB7 full source access (dogfooding)":`writer \xB7 edits limited to ${m}/ + config`,b={version:t,engineVersion:Cd,captureVersion:er(),claude:s,model:l.model,modelSource:my[l.source],who:null,sessionHint:null,profile:"\u2014",apiUrl:"\u2014",project:"\u2014",cwd:e,prod:!1,loggedOut:!0,configured:d!==null,mode:y};if(d===null)return b;try{let v=De(e,o);b.profile=v.name,b.apiUrl=v.connection.apiUrl,b.prod=v.production,b.project=v.project.projectName??v.project.projectId??"(chosen at login)";let x=At(v.name);if(x){let I={...un(x.idToken)??{},...un(x.accessToken)??{}},P=I.email??I.preferred_username??"signed in";pt(x)?x.refreshToken&&(b.who=P,b.loggedOut=!1,b.sessionHint="session expired \u2014 refreshing\u2026"):(b.who=P,b.loggedOut=!1,b.sessionHint=`session valid until ${new Date(x.expiresAt).toLocaleString(void 0,{hour:"2-digit",minute:"2-digit",day:"2-digit",month:"short"})}`)}}catch{}return b}function wy(e,t){try{let n=De(e,t),o=At(n.name);if(!o)return{text:`profile "${n.name}" \u2014 not logged in (/login)`,prod:n.production};let r={...un(o.idToken)??{},...un(o.accessToken)??{}},s=r.email??r.preferred_username??"signed in";return pt(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 xd=["Drafting","Composing","Outlining","Researching","Documenting","Structuring","Polishing","Synthesizing","Curating","Distilling","Weaving","Wrangling","Pondering"],dn=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],yy="Ask me to write or update an article\u2026";function cn({ch:e,dim:t}){let[n,o]=B(!0);return Ae(()=>{let r=setInterval(()=>o(s=>!s),530);return()=>clearInterval(r)},[]),E(A,{inverse:n,color:t&&!n?"gray":void 0,children:e})}var ky=/^mcp__document360__d360_(create_article|update_article|fork_article|publish_article)$/;function vd(e){let t=e.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n/);return t?e.slice(t[0].length):e}var by=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function $y({startTime:e,chars:t}){let[n,o]=B(0);Ae(()=>{let m=setInterval(()=>o(y=>y+1),120);return()=>clearInterval(m)},[]);let r=dn[n%dn.length],s=xd[Math.floor(n/16)%xd.length],l=Math.floor((Date.now()-e)/1e3),d=Math.round(t/4);return z(se,{children:[E(A,{color:V,children:` ${r} ${s}\u2026 `}),E(A,{color:"gray",children:`(${En(l)} \xB7 ~${d} tokens \xB7 esc to interrupt)`})]})}function xy({text:e}){let[t,n]=B(0);return Ae(()=>{let o=setInterval(()=>n(r=>r+1),120);return()=>clearInterval(o)},[]),E(se,{paddingX:1,children:E(A,{color:V,children:`${dn[t%dn.length]} ${e}`})})}var vy=12e4;function Cy({p:e}){let[t,n]=B(0);Ae(()=>{let D=setInterval(()=>n(U=>U+1),150);return()=>clearInterval(D)},[]);let o=dn[t%dn.length],r=Math.floor((Date.now()-e.startedAt)/1e3),s=Math.floor((Date.now()-e.lastAt)/1e3),l=Math.round(e.chars/4),d=Date.now()-e.lastAt>=vy,m=e.verb??"Converting",y=e.unit??"article",b=e.rows.reduce((D,U)=>D+U.items,0),v=m.toLowerCase(),x=e.rows,I=0;if(x.length>Cr){let D=x.findIndex(G=>G.status==="active"||G.status==="pending"),U=Math.max(0,Math.min(D-2,x.length-Cr));I=x.slice(0,U).filter(G=>G.status==="done").length,x=x.slice(U,U+Cr)}let P=Math.min(40,Math.max(...x.map(D=>Ji(D.label).length),0)),j=D=>D==="failed"?"red":D==="active"?V:void 0;return z(se,{flexDirection:"column",children:[z(se,{children:[E(A,{color:V,children:` ${o} ${m} ${b} ${y}${b===1?"":"s"} `}),E(A,{color:"gray",children:`\xB7 ${e.done}/${e.total} \xB7 ${e.tools} tool call${e.tools===1?"":"s"} \xB7 ~${l} tokens \xB7 ${En(r)} \xB7 esc to stop`})]}),I>0&&E(A,{dimColor:!0,children:` \u2714 +${I} done`}),x.map(D=>z(A,{color:j(D.status),dimColor:D.status==="done"||D.status==="pending",children:[` ${Yu[D.status]} ${Ji(D.label).padEnd(P)}`,D.status==="active"&&D.itemsDone!==void 0?` (${D.itemsDone}/${D.items} ${y}${D.items===1?"":"s"})`:D.status!=="pending"?` (${D.items} ${y}${D.items===1?"":"s"})`:"",D.status==="active"?` \u2190 ${v}\u2026`:""]},D.label)),d&&E(A,{color:"yellow",children:` \u26A0 no activity for ${En(s)} \u2014 press esc to stop if it's wedged`})]})}var Ry=10800*1e3;function Sd({cwd:e,auth:t,profileName:n,version:o}){let{exit:r}=Sw(),[s,l]=B(n),[d,m]=B(null),[y,b]=B({text:"",pos:0}),v=y.text,x=Y(c=>{b(a=>{let i=typeof c=="function"?c(a.text):c;return{text:i,pos:i.length}})},[]),I=Y(c=>{b(a=>({text:a.text.slice(0,a.pos)+c+a.text.slice(a.pos),pos:a.pos+c.length}))},[]),P=Y(()=>{b(c=>{if(c.pos===0)return c;let a=c.text.slice(0,c.pos),i=ed(a)??a.slice(0,-1);return{text:i+c.text.slice(c.pos),pos:i.length}})},[]),[j,D]=B(!1),[U,G]=B(!1),[K,X]=B(0),[he,Je]=B(!1),[ge,jt]=B(null),[ve,ht]=B(null),[Be,ft]=B([]),Xn=ee(!1),jr=ee(!1),[Er,pa]=B(!1),Qn=ee(0),[Nd,Jn]=B(!1),[Id,Zn]=B(!1),[Nr,ha]=B(null),[_d,Et]=B(0),mt=ee(0),[pn,Nt]=B(0),[It,eo]=B(null),fa=ee(new Map),Ld=ee(0),_t=ee([]),je=ee(null);je.current===null&&(je.current=id({cwd:e,profileName:s,allowProdWrites:!1}));let Ve=ee({uuid:null,firstPrompt:null,titleFired:!1}),to=ee(new Map),Lt=ee(!1),Ir=ee([]),ce=ee([]),Ot=ee([]),no=ee(null),[ma,gt]=B(null),_r=ee(null),[wt,yt]=B(null),[Ge,Mt]=B(null),[Ye,Ut]=B(null),[we,Wt]=B(null),[Ce,kt]=B(null),[Ze,Ft]=B(null),[et,bt]=B(null),[ze,$t]=B(null),[ga,hn]=B(null),[Re,xt]=B(null),[ke,qt]=B(null),[wa,Lr]=B([]),vt=ee([]),fn=ee(!1),fe=ee(null),[oo,oe]=B(null),[ya,ro]=B(null),so=ee(!1),mn=ee(null),gn=ee(null),[Se,Ct]=B(null),[io,wn]=B(null),Or=!!(Ye||we||Ce||Ze||et||ze||Re||ke||Se||io),[ka,Ht]=B(0),[ba,Bt]=B(0),[Od,$a]=B(0),Mr=ea(()=>{try{let c=la(e);return c.source==="auto"?`${c.model} (auto)`:c.model}catch{return null}},[e,Od]),{stdout:Oe}=Tw(),[,Md]=B(0),xa=ee(`${Oe.columns??80}x${Oe.rows??24}`),tt=Y(()=>Math.max(20,(Oe.columns??80)-1),[Oe]),u=Y(c=>{_t.current.push(c),console.log(gs(c,tt()))},[tt]),ye=Y((c,a)=>{Qn.current++,jt(c),ht(a??null)},[]),Me=Y(()=>{Qn.current++,jt(null),ht(null)},[]),Q=Y(c=>ye(c,dr({kind:"cmd",cmd:c,reason:""})),[ye]),ao=Y((c,a)=>{!a?.silent&&c.note&&u({kind:"note",tone:c.tone,text:c.note}),c.picker&&(ft(c.picker.options.map(i=>({cmd:i.cmd,desc:i.desc}))),hn(c.picker.title)),c.lead&&ye(c.lead.kind==="cmd"?c.lead.cmd:c.lead.text,dr(c.lead))},[u,ye]),be=Y((c,a,i="info")=>{c&&u({kind:"note",tone:i,text:c}),a&&ye(a.kind==="cmd"?a.cmd:a.text,dr(a))},[u,ye]),[yn,Ur]=B(null),[Ud,va]=B(!1),Vt=Y(c=>{Ur(c),va(!1),u({kind:"note",tone:"info",text:"\u2328 Press c to copy the prompt above to your clipboard (then paste it into your coding agent)."})},[u]),Wr=ee(!1),Fr=Y(async()=>{try{if(Wr.current||!ay(iy())||re(e)?.mode==="engineer")return;let c=await sy({pkg:sn,currentVersion:o,force:!0});if(!c.updateAvailable||!c.latest)return;Wr.current=!0,u({kind:"note",tone:"info",text:`\u2B06 Updating ${sn} ${c.current} \u2192 ${c.latest}\u2026 (wait for the \u2713 before you /exit)`}),Zn(!0),(await sr().finally(()=>Zn(!1))).ok?(u({kind:"note",tone:"ok",text:`\u2713 Updated to v${c.latest} \u2014 run /exit, then relaunch to apply.`}),Q("/exit"),ha(c.latest)):u({kind:"note",tone:"warn",text:`Update available (v${c.latest}) but auto-update failed \u2014 run: ${rr}`})}catch{}},[e,o,u,Q]),lo=Y(c=>{let a=Ie(e,s);return a.ok?!0:(u({kind:"note",tone:"warn",text:a.reason}),gn.current=c,Q("/login"),!1)},[e,s,u,Q]),[co,qr]=B(null),Rt=Y(c=>{let a=Gn(e,s,{claudeMissing:aa(t).state==="missing",allowAutoAudit:c,genreUnknown:jr.current});return a?Vn(a):{kind:"idle"}},[e,s,t]),kn=ee(!0),Gt=ee(!1),uo=ee(0),bn=ee(0);Ae(()=>{let c=setInterval(()=>{if(kn.current){uo.current=0,bn.current=0;return}if(!j)try{if(!ln(fd(e)))return;if(Ne(e).status==="current"){kn.current=!0,uo.current=0,bn.current=0;let i=Rt(!1),f=i.kind==="recommend"?i.rec:null,R=Gt.current?`\u2713 Your builder finished the protocol update (v${le}) \u2014 the dev\u2194docs hand-off is current again.`:"\u2713 Your builder finished the hand-off \u2014 the architecture brief is in. I can read this repo now and write accurate docs.";u({kind:"note",tone:"ok",text:`${R}${f?.lead?` ${Bn(f.lead)}`:""}`}),Ur(null),f&&(f.lead||f.picker)?ao(f,{silent:!0}):Gt.current||ye("write the docs for this repo"),X(F=>F+1);return}if(Gt.current)return;uo.current+=1;let a=uo.current;if((a===4||a%15===0)&&bn.current<3){bn.current+=1;let i=bn.current===3?" (I'll stay quiet now \u2014 type anything to continue.)":"";u({kind:"note",tone:"info",text:Xe(e)?`\u{1F440} Your builder is working on the architecture brief \u2014 I'll continue automatically when it finishes.${i}`:`\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.${i}`})}}catch{}},4e3);return()=>clearInterval(c)},[e,j,u,Rt,ao,ye]),Ae(()=>{let c=ma??zn(e);if(!j){br(0),an(`${xr} ${c}`);return}br(3);let a=0;an(`${$r[0]} ${c}`);let i=setInterval(()=>{a=(a+1)%$r.length,an(`${$r[a]} ${c}`)},400);return()=>clearInterval(i)},[j,e,ma]),Ae(()=>()=>br(0),[]),Ae(()=>{let c=performance.now(),a=[],i=(h,k)=>{let w=performance.now();try{return k()}finally{let T=Math.round(performance.now()-w);T>=80&&a.push(`${h}: ${T}ms`)}};u({kind:"banner",info:i("banner",()=>gy(e,o,t,s))});let f=aa(t).state==="missing";if(f&&u({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.
@@ -396,7 +396,7 @@ ${S}`)}catch{}a?.quietUser||u({kind:"user",text:a?.echoDisplay&&a.display?a.disp
396
396
  `)})}},[e,s,u,te,fo]),Ta=Y(async c=>{let a=await qo([c]);a.kind==="forward-to-agent"&&a.prompt&&await te(a.prompt,{display:a.display,quietUser:!0}),Ol(e,c)&&be(`Spec ready \u2014 /screenshot capture ${c} takes the screenshot; bare /screenshot picks the next shot.`,{kind:"cmd",cmd:`/screenshot capture ${c}`,reason:""})},[e,te,be]),Gr=Y(async(c,a)=>{if(!nr(e)){await te(yi(),{display:"/screenshot capture \u2014 first-time setup",quietUser:!0});return}if(!zc(e)){u({kind:"note",tone:"info",text:"No saved login for screenshot capture yet \u2014 opening a browser window. Sign in to your app (MFA included); I'll continue automatically once the session saves. (esc cancels)"}),D(!0),ro("Waiting for you to finish logging in (browser window)\u2026");let $=new AbortController;fe.current=$;let _;try{_=await Kc({cwd:e,signal:$.signal})}catch(S){u({kind:"note",tone:"error",text:`Could not launch the login: ${S.message}`});return}finally{fe.current=null,ro(null),D(!1)}if(_.exitCode!==0){wi(_.exitCode,_.output)?u({kind:"note",tone:"error",text:"d360-capture is not installed on this machine. Run: npm i -g document360-capture \u2014 then /screenshot capture again."}):/unknown option/i.test(_.output)?u({kind:"note",tone:"warn",text:"Your d360-capture is older than this writer. Update it: npm i -g document360-capture \u2014 then /screenshot capture again."}):u({kind:"note",tone:$.signal.aborted?"warn":"error",text:$.signal.aborted?"Login cancelled \u2014 nothing saved.":`Login did not complete:
397
397
  ${mi(_.output)}`});return}u({kind:"note",tone:"ok",text:"\u2713 Login saved \u2014 continuing with the capture."})}let i=new Map(c.map($=>[$,Ul(e,$)]));if(c.length>1){let $=c.filter(_=>i.get(_).mutates);if($.length>0&&(c=c.filter(_=>!i.get(_).mutates),u({kind:"note",tone:"warn",text:`\u26A0 ${$.length} spec${$.length===1?"":"s"} perform${$.length===1?"s":""} a REAL write in your app and ${$.length===1?"is":"are"} excluded from bulk capture. Run each deliberately, on its own:
398
398
  `+$.map(_=>`/screenshot capture ${_}${i.get(_).what?` (${i.get(_).what})`:""}`).join(`
399
- `)}),c.length===0))return}else if(c.length===1&&i.get(c[0]).mutates){let $=i.get(c[0]);u({kind:"note",tone:"warn",text:`\u26A0 This capture performs a REAL write in your app${$.what?`: ${$.what}`:$.declared?"":" (detected from the spec: form fill + commit click / file upload)"}. Running because you named it explicitly \u2014 check the result and clean up what it created.`})}D(!0);let f=new AbortController;fe.current=f;let R=c.map($=>({label:$,items:1,status:"pending"}));oe({verb:"Capturing",unit:"screenshot",total:c.length,done:0,active:[],rows:R,tools:0,chars:0,lastAt:Date.now(),startedAt:Date.now()}),u({kind:"note",tone:"info",text:`Capturing ${c.length} screenshot${c.length===1?"":"s"} with d360-capture (a real browser drives your app)\u2026 (esc to stop)`});let F=Date.now(),p=[];try{for(let $ of c){if(f.signal.aborted)break;oe(W=>W&&{...W,active:[$],rows:_e(W.rows,$,"active"),lastAt:Date.now()});let{exitCode:_,output:S}=await gi({cwd:e,id:$,signal:f.signal,stdio:"pipe"});if(wi(_,S)){u({kind:"note",tone:"error",text:"d360-capture is not installed on this machine. Run: npm i -g document360-capture \u2014 then /screenshot capture again."});return}let q=f.signal.aborted&&_!==0?"failed":fi(e,$,_,F),O=q==="captured"?void 0:q==="skipped"?Gc(S)??"a data prerequisite isn't met \u2014 the spec's skip reason names what to stage":Yc(S);p.push({id:$,outcome:q,detail:O}),oe(W=>W&&{...W,active:[],done:W.done+1,rows:_e(W.rows,$,q==="captured"?"done":"failed"),lastAt:Date.now()})}}catch($){u({kind:"note",tone:"error",text:`Capture run failed: ${$.message}`})}finally{fe.current=null,oe(null),D(!1),X($=>$+1)}if(p.length===0)return;let h=p.filter($=>$.outcome==="captured"),k=p.filter($=>$.outcome==="skipped"),w=p.filter($=>$.outcome==="failed"),T=c.length-p.length;u({kind:"note",tone:w.length?"warn":h.length?"ok":"info",text:`\u2713 ${h.length} captured`+(k.length?` \xB7 \u2298 ${k.length} skipped`:"")+(w.length?` \xB7 \u2717 ${w.length} failed`:"")+(T?` \xB7 ${T} not run (stopped)`:"")});for(let $ of k)u({kind:"note",tone:"info",text:`\u2298 ${$.id} \u2014 ${$.detail}`});for(let $ of w)$.detail&&u({kind:"note",tone:"error",text:`\u2717 ${$.id}
399
+ `)}),c.length===0))return}else if(c.length===1&&i.get(c[0]).mutates){let $=i.get(c[0]);u({kind:"note",tone:"warn",text:`\u26A0 This capture performs a REAL write in your app${$.what?`: ${$.what}`:$.declared?"":" (detected from the spec: form fill + commit click / file upload)"}. Running because you named it explicitly \u2014 check the result and clean up what it created. Tip: point the capture scope (.d360-writer/capture.json) at a demo/non-production context for shots like this.`})}D(!0);let f=new AbortController;fe.current=f;let R=c.map($=>({label:$,items:1,status:"pending"}));oe({verb:"Capturing",unit:"screenshot",total:c.length,done:0,active:[],rows:R,tools:0,chars:0,lastAt:Date.now(),startedAt:Date.now()}),u({kind:"note",tone:"info",text:`Capturing ${c.length} screenshot${c.length===1?"":"s"} with d360-capture (a real browser drives your app)\u2026 (esc to stop)`});let F=Date.now(),p=[];try{for(let $ of c){if(f.signal.aborted)break;oe(W=>W&&{...W,active:[$],rows:_e(W.rows,$,"active"),lastAt:Date.now()});let{exitCode:_,output:S}=await gi({cwd:e,id:$,signal:f.signal,stdio:"pipe"});if(wi(_,S)){u({kind:"note",tone:"error",text:"d360-capture is not installed on this machine. Run: npm i -g document360-capture \u2014 then /screenshot capture again."});return}let q=f.signal.aborted&&_!==0?"failed":fi(e,$,_,F),O=q==="captured"?void 0:q==="skipped"?Gc(S)??"a data prerequisite isn't met \u2014 the spec's skip reason names what to stage":Yc(S);p.push({id:$,outcome:q,detail:O}),oe(W=>W&&{...W,active:[],done:W.done+1,rows:_e(W.rows,$,q==="captured"?"done":"failed"),lastAt:Date.now()})}}catch($){u({kind:"note",tone:"error",text:`Capture run failed: ${$.message}`})}finally{fe.current=null,oe(null),D(!1),X($=>$+1)}if(p.length===0)return;let h=p.filter($=>$.outcome==="captured"),k=p.filter($=>$.outcome==="skipped"),w=p.filter($=>$.outcome==="failed"),T=c.length-p.length;u({kind:"note",tone:w.length?"warn":h.length?"ok":"info",text:`\u2713 ${h.length} captured`+(k.length?` \xB7 \u2298 ${k.length} skipped`:"")+(w.length?` \xB7 \u2717 ${w.length} failed`:"")+(T?` \xB7 ${T} not run (stopped)`:"")});for(let $ of k)u({kind:"note",tone:"info",text:`\u2298 ${$.id} \u2014 ${$.detail}`});for(let $ of w)$.detail&&u({kind:"note",tone:"error",text:`\u2717 ${$.id}
400
400
  ${$.detail}`});if(h.length>0){let $=Xc(e,h.map(S=>S.id)),_=$.length===1?`/publish ${$[0]}`:"/publish --screenshots";be(`PNG${h.length===1?"":"s"} saved \u2014 ${_} re-saves the article${$.length===1?"":"s"} so the image${h.length===1?"":"s"} embed${h.length===1?"s":""}.`,{kind:"cmd",cmd:_,reason:""})}else if(k.length>0){let $=c.length===1?`/screenshot capture ${c[0]}`:"/screenshot capture";be(null,{kind:"cmd",cmd:$,reason:""})}p.length===c.length&&w.length===0&&k.length===0&&fo(a?.runLine)},[e,u,be,te,fo]),Yr=Y(c=>{let a=At(c);if(!a||pt(a)&&!a.refreshToken)return null;let i={...un(a.idToken)??{},...un(a.accessToken)??{}};return i.email??i.preferred_username??"signed in"},[]),zr=Y((c,a)=>{if(a){let i=re(e);i&&(i.defaultProfile=c,ld(i,e))}l(c),Pe(void 0,c),u({kind:"note",tone:"ok",text:`\u2713 Switched to profile "${c}"${a?" (saved as default)":" (this session only)"} \u2014 agent restarted.`}),Yr(c)||(u({kind:"note",tone:"warn",text:`Profile "${c}" is not signed in \u2014 sign in with /login.`}),Q("/login")),X(i=>i+1)},[e,u,Pe,Yr,Q]),mo=Y((c,a,i)=>{vo(e,c,a,i.id,i.name),u({kind:"note",tone:"ok",text:`Switched to workspace "${i.name??i.id}" (agent restarted).`}),Pe(),cd(e,c)||(u({kind:"note",tone:"ok",text:"\u2713 Connected. Configuration is done \u2014 one important step remains."}),u({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:"}),u({kind:"note",tone:"info",text:Zt(Jt(e)).join(`
401
401
  `)}),Vt(Pt()),kn.current=Ne(e).status==="current",Gt.current=Xe(e),u({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,u,Pe,Vt]),go=Y(async c=>{let a;try{a=await Sn(e,c)}catch{u({kind:"note",tone:"info",text:"Next: pick a workspace \u2014 run /workspace."}),Q("/workspace");return}if(a.workspaces.length===0){u({kind:"note",tone:"warn",text:"This project has no workspaces yet. Create one in Document360, then run /workspace."});return}let i=a.workspaces.filter(R=>(R.workspace_type??"").toLowerCase()!=="apidocumentation"),f=i.length>0?i:a.workspaces;if(f.length===1){let R=f[0],F=a.workspaces.length-f.length;u({kind:"note",tone:"ok",text:`Selected the "${R.name??R.id}" workspace${F>0?" (skipped the API-documentation workspace)":""}.`}),mo(a.profile,a.projectId,R);return}u({kind:"note",tone:"info",text:`Next: pick the workspace your articles publish to (${f.length} available).`}),Q("/workspace")},[e,u,mo,Q]),wo=Y((c,a)=>{Co(e,c,a.id,a.name),u({kind:"note",tone:"ok",text:`Project set to "${a.name??a.id}" (agent restarted).`}),Pe(),go(c)},[e,u,Pe,go]),Da=Y(async c=>{let a;try{a=De(e,c)}catch{return}if(!a.project.workspaceId){if(a.project.projectId){if(!a.project.projectName)try{let{projects:i}=await rt(e,a.name),f=i.find(R=>R.id===a.project.projectId);f?.name&&uy(e,a.name,{projectName:f.name})}catch{}await go(a.name);return}try{let{projects:i}=await rt(e,a.name);if(i.length===1){let f=i[0];u({kind:"note",tone:"ok",text:`Using your Document360 project "${f.name??f.id}".`}),wo(a.name,{id:f.id,name:f.name});return}if(i.length>1){let f=i.map(R=>({id:R.id,name:R.name??R.id,sub:R.sub_domain}));u({kind:"note",tone:"info",text:"Which Document360 project should these docs publish to?"}),kt({cursor:0,current:-1,rows:f,profile:a.name,environment:a.connection.name});return}}catch{}u({kind:"note",tone:"info",text:"Next: pick the Document360 project \u2014 run /project."}),Q("/project")}},[e,u,go,wo,Q]),Yt=Y(()=>{let c=ce.current[0];if(!c)return;Me();let{plan:a,mode:i}=c;u({kind:"note",tone:"info",text:`\u25CF ${a.title} (${a.path})`});for(let R of a.notes)u({kind:"note",tone:"warn",text:`\u26A0 ${R}`});a.overwritesLocalChanges&&i==="pull"&&u({kind:"note",tone:"warn",text:"\u26A0 This OVERWRITES local edits made since the last sync."});let f=Qe(a.oldContent,a.newContent,tt());u(f?{kind:"diff",added:f.added,removed:f.removed,lines:f.lines,hidden:f.hidden}:{kind:"note",tone:"info",text:"Local file already matches the remote content \u2014 applying only advances the sync base."}),i==="conflict"?(u({kind:"note",tone:"warn",text:"Conflict \u2014 edited both here and on the portal (diff shows local \u2192 portal)."}),u({kind:"note",tone:"info",text:"Keep local (publish, overwrites portal) \xB7 take Portal (overwrites local) \xB7 Skip? (l/p/s \u2014 anything else cancels)"})):u({kind:"note",tone:"info",text:`Write ${a.path}? (y/n \u2014 anything else cancels)`})},[u,tt,Me]),Kr=Y(()=>{if(ce.current.length>0){Yt();return}let c=Ot.current;Ot.current=[],c.length>0?(u({kind:"note",tone:"info",text:`Publishing ${c.length} kept-local article${c.length===1?"":"s"} over the portal\u2026`}),St(c)):Q("/pull")},[Yt,u,St,Q]),Aa=Y(c=>{if(ce.current.length===0)return!1;let a=c.trim().toLowerCase(),i=ce.current[0];if(i.mode==="conflict"){if(a==="l"||a==="local")ce.current.shift(),Ot.current.push(i.plan.path),u({kind:"note",tone:"info",text:`Keeping local ${i.plan.path} \u2014 will publish over the portal.`});else if(a==="p"||a==="portal"){ce.current.shift();try{ud({cwd:e,profileName:s},i.plan),u({kind:"note",tone:"ok",text:`\u2713 Took the portal copy of ${i.plan.path} (local overwritten, sync base advanced).`})}catch(f){u({kind:"note",tone:"error",text:`Take-portal failed: ${f.message}`})}}else if(a==="s"||a==="skip"||a==="n")ce.current.shift(),u({kind:"note",tone:"info",text:`Skipped ${i.plan.path} \u2014 still a conflict.`});else{let f=ce.current.length;return ce.current=[],Ot.current=[],u({kind:"note",tone:"info",text:`Resolve cancelled (${f} conflict${f===1?"":"s"} left untouched).`}),!0}return Kr(),!0}if(a==="y"||a==="yes"){let f=ce.current.shift().plan;try{ud({cwd:e,profileName:s},f),u({kind:"note",tone:"ok",text:`\u2713 Pulled ${f.path} (sync base advanced).`})}catch(R){u({kind:"note",tone:"error",text:`Pull failed: ${R.message}`})}}else if(a==="n"||a==="no"){let f=ce.current.shift().plan;u({kind:"note",tone:"info",text:`Skipped ${f.path}.`})}else{let f=ce.current.length;return ce.current=[],u({kind:"note",tone:"info",text:`Pull cancelled (${f} article(s) left untouched).`}),!0}return Kr(),!0},[e,s,Kr,u]),Xr=Y(async c=>{u({kind:"note",tone:"info",text:"Checking Document360 for conflicts\u2026"});try{let a=await oa({cwd:e,profileName:s}),i=c?.replace(/\\/g,"/"),f=a.entries.filter(F=>F.status==="conflict"&&F.path).map(F=>F.path);if(i){if(!f.includes(i)){u({kind:"note",tone:"ok",text:`${i} isn\u2019t in conflict \u2014 nothing to resolve.`});return}f=[i]}if(f.length===0){u({kind:"note",tone:"ok",text:"\u2713 No conflicts \u2014 local and Document360 agree on every tracked article."});return}let R=[];for(let F of f)try{R.push({plan:await pd({cwd:e,profileName:s,relPath:F}),mode:"conflict"})}catch(p){u({kind:"note",tone:"error",text:`Could not load ${F} from Document360: ${p.message}`})}if(R.length===0)return;u({kind:"note",tone:"info",text:`Resolving ${R.length} conflict${R.length===1?"":"s"} \u2014 for each: keep local, take portal, or skip.`}),ce.current=R,Ot.current=[],Yt()}catch(a){u({kind:"note",tone:"error",text:`Could not compute sync status: ${a.message}`})}},[e,s,u,Yt]),ja=Y(c=>{let a=no.current;if(!a)return!1;if(no.current=null,Me(),c.trim()!==a.repoName)return u({kind:"note",tone:"warn",text:"Reset cancelled \u2014 nothing deleted."}),!0;let{removed:i,failed:f}=ji(e,a.targets);u({kind:"note",tone:f.length?"warn":"ok",text:`\u2713 Reset complete \u2014 removed ${i.length} item${i.length===1?"":"s"}. The repo is back to its original state.`});for(let R of f)u({kind:"note",tone:"error",text:` \u2717 ${R.path}: ${R.error}`});return a.hadDevhints&&(u({kind:"note",tone:"warn",text:_o().join(`
402
402
  `)}),Vt(ss())),u({kind:"note",tone:"info",text:`Clean slate \u2014 set up d360-writer again whenever you're ready:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "document360-writer",
3
- "version": "0.5.142",
3
+ "version": "0.5.144",
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.143",
37
+ "document360-engine": "^0.2.151",
38
38
  "ink": "^5.2.1",
39
39
  "picocolors": "^1.1.1",
40
40
  "react": "^18.3.1",