document360-writer 0.5.129 → 0.5.130
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/cli.js +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -389,7 +389,7 @@ ${eu}`}),X(f?"/doctor":"/init");return}f&&X("/doctor");try{let h=Te(e,s),x=Dt(h.
|
|
|
389
389
|
cwd: ${e}`}),Ar()},[]),De(()=>{let c=setInterval(()=>{Ar()},Qw);return()=>clearInterval(c)},[Ar]),De(()=>{if(N||pe||$r||Vn.current||fe!==null||He.length>0||S!==""||gn!==null||le.current.length>0||Qn.current||Sr)return;let c=xt(!1);c.kind==="recommend"&&(c.rec.lead||c.rec.picker)&&to(c.rec,{silent:!0})},[N,pe,fe,He,S,gn,Sr,Y,xt,$r]),De(()=>{let c=null,l=null,i=()=>{c&&clearTimeout(c),c=setTimeout(()=>{c=null;let f=`${Me.columns??80}x${Me.rows??24}`;f!==na.current&&(na.current=f,wu(d=>d+1),l&&clearTimeout(l),l=setTimeout(()=>{l=null,console.log("\x1B[?2026h\x1B[H\x1B[2J"+fl(It.current,Ze())+"\x1B[?2026l")},80))},400)};return Me.on("resize",i),()=>{c&&clearTimeout(c),l&&clearTimeout(l),Me.off("resize",i)}},[Me,Ze]);let je=Math.max(20,(Me.columns??80)-1),ra=Ni(()=>qw(e,s),[e,s,Y]),Er=(()=>{if(hu)return{text:"\u23F3 Update in progress \u2014 please don\u2019t close this window\u2026",isSetup:!0};if(xr!==null)return{text:`Update ready \u2014 run /exit, then relaunch to apply${xr?` v${xr}`:""}\u2026`,isSetup:!0};if(oo)return{text:"\u{1F6E0} press c to copy the d360-writer feedback \u2014 then send it to the team (Teams / Slack / email)\u2026",isSetup:!0};if(gn)return yu?{text:"\u2713 copied \u2014 paste it into your coding agent \xB7 I\u2019ll pick up automatically when it finishes\u2026",isSetup:!0}:{text:"\u2328 press c to copy the prompt above \u2014 then paste it into your coding agent\u2026",isSetup:!0};if(le.current.length>0)return le.current[0].mode==="conflict"?{text:"l keep local \xB7 p take portal \xB7 s skip \xB7 anything else cancels\u2026",isSetup:!0}:{text:"y to write \xB7 n to skip \xB7 anything else cancels\u2026",isSetup:!0};let c=oe(e);if(!c)return{text:"Run /init to set up this repo (or /help)\u2026",isSetup:!0};let l=Ii(e,(c.docsDir??ru).replace(/\/+$/,"")),i=(()=>{try{return sn(l)&&tw(l).length>0}catch{return!1}})();try{let f=Te(e,s),d=Dt(f.name);if(!(!!d&&!(ct(d)&&!d.refreshToken)))return{text:`Sign in with /login (profile "${f.name}")\u2026`,isSetup:!0};if(!f.project.projectId)return{text:"Sign in to Document360 to pick your project\u2026",isSetup:!0};if(!f.project.workspaceId&&!i)return{text:"Pick a workspace with /workspace\u2026",isSetup:!0}}catch{}return i?{text:Bw,isSetup:!1}:{text:"Ask me to write the docs for this repo",isSetup:!1}})(),kn=sl(S),sa=kn.length>0&&!N,Nr=p!==null?Od(p,8,je):null,so=Ce?Ce.paths.filter(c=>!Ce.query||c.toLowerCase().includes(Ce.query.toLowerCase())).slice(0,8):[],io=Re?Re.sessions.filter(c=>{let l=Re.query.toLowerCase();return!l||c.name.toLowerCase().includes(l)||c.firstPrompt.toLowerCase().includes(l)}).slice(0,8):[],Se=B((c,l)=>{Ae.current?.close(),Ae.current=Ud({cwd:e,resume:c,profileName:l??s,allowProdWrites:_t.current}),c||(qe.current={uuid:null,firstPrompt:null,titleFired:!1},ht(null)),Ht(0),qt(0)},[e,s]),ia=B((c,l,i)=>{if(Vw.test(c))try{let f=Te(e,s),d=typeof l.project_id=="string"&&l.project_id||f.project.projectId,w=ir(l,i),$=[],h=ar(i);c.endsWith("publish_article")&&h&&$.push(`Live: ${h}`);let x=lr(i);x?$.push(`Preview: ${x}`):w&&d&&$.push(`Preview: ${sr(f.connection.portalUrl,d,w,f.project.languageCode??"en")}`),$.length>0&&u({kind:"link",lines:$})}catch{}},[e,s,u]),ne=B(async(c,l)=>{Yn(!0),Vn.current=!1,Ue(),ut([]),un(null),Tr(null);let i=Gn.current;l?.quietUser||u({kind:"user",text:l?.echoDisplay&&l.display?l.display:c});let f=qe.current;f.firstPrompt||(f.firstPrompt=l?.display??c),pt.current=Date.now(),jt(0),T(!0),Xn.current.clear();let d="",w=!1,$=!1,h="",x={num:0,closed:!1,cueSeen:!1},b=null,U=()=>{b||(b=setTimeout(()=>{b=null,g(h.length>0?h:null)},60))},M=()=>{b&&clearTimeout(b),b=null,g(null)},K=(P=!1)=>{let{head:j,keep:H}=Ld(h,P);j.trim()&&u({kind:"assistant",text:Di(j.trimEnd(),x)}),h=H,M()};try{for await(let P of Ae.current.send(c))if(P.type==="session"){if(!f.uuid){f.uuid=P.sessionId;let j=new Date().toISOString(),H=Cr.current,ee=H??hw(f.firstPrompt??"session");fw({uuid:P.sessionId,name:ee,renamed:!!H,titled:!1,cwd:e,firstPrompt:f.firstPrompt??"",createdAt:j,updatedAt:j}),ht(ee),Cr.current=null}}else if(P.type==="text"){h+=P.delta,d+=P.delta;let j=Md(h);if(j>0){let H=h.slice(0,j).trimEnd();H&&u({kind:"assistant",text:Di(H,x)}),h=h.slice(j)}U(),jt(H=>H+P.delta.length)}else if(P.type==="tool"){w=!0;let j=or(P.name,P.input);j&&(K(),u({kind:"tool",title:j.title,sep:j.sep,arg:j.arg}),Xn.current.set(P.id,{name:P.name,input:P.input}))}else if(P.type==="article_diff"){let j=Ke(P.oldContent,P.newContent,Ze());j&&(K(),u({kind:"diff",added:j.added,removed:j.removed,lines:j.lines,hidden:j.hidden}))}else if(P.type==="tool_result"){P.isError&&/run \/login|not logged in|session expired|rejected the token/i.test(P.output)&&(Zn.current=!0);let j=Xn.current.get(P.id);if(j){Xn.current.delete(P.id),K();let H=rr(P.output,4,P.isError?void 0:j.name,j.input);u({kind:"tool-result",lines:H.lines,hidden:H.hidden,isError:P.isError}),P.isError||ia(j.name,j.input,P.output)}}else if(P.type==="result"){K(!0),Ht(H=>H+P.outputTokens),qt(H=>H+P.costUsd);let j=P.inputTokens+P.cacheReadTokens+P.cacheCreationTokens;if(u({kind:"done",seconds:Math.round((Date.now()-pt.current)/1e3),tokens:P.outputTokens,costUsd:P.costUsd,ok:P.ok,...j>0?{inTokens:j,cachedPct:Math.round(P.cacheReadTokens/j*100)}:{}}),!$&&!w&&!d.trim()&&!pn.current&&u({kind:"note",tone:"warn",text:"The agent produced no output. This usually means the Claude model/runtime did not start \u2014 check sign-in and model with /doctor (on a subscription, make sure Claude Code is logged in on this machine)."}),P.ok&&/🛠|📣 Feedback for the d360-writer developers/.test(d))try{let H=zd(e);if(sn(H)){let ee=gr(H,"utf8").trim();ee&&(jr(ee),u({kind:"note",tone:"info",text:"\u{1F6E0} Press c to copy this feedback \u2014 send it to the d360-writer team (Teams / Slack / email)."}))}}catch{}if(P.ok&&x.cueSeen)Vi(!0),ut([]);else{let H=P.ok?vd(d,tn.map(ee=>ee.name)):[];if(H.length>0)X(H[0]);else if(P.ok&&d.trim()){let ee=xt(!1);ee.kind==="recommend"&&(ee.rec.lead||ee.rec.picker)||yw(c,d,e).then(Ee=>{Ee&&Gn.current===i&&ge(Ee)}).catch(()=>{})}}if(f.uuid&&(_i(f.uuid),!f.titleFired)){f.titleFired=!0;let H=f.uuid,ee=f.firstPrompt;ee&&Wd(ee,e).then(Ee=>{Ee&&(pw(H,Ee),ht(Ee))}).catch(()=>{})}}else P.type==="error"&&(K(!0),$=!0,P.kind==="auth"&&(Zn.current=!0),u({kind:"note",text:`agent error: ${P.message}`,tone:"error"}))}finally{T(!1),M(),z(P=>P+1),qe.current.uuid&&kd(e,qe.current.uuid,It.current),Zn.current?(Zn.current=!1,hn.current=l?.display??c,ge("/login","\u21E5 tab to run /login \u2014 then I\u2019ll re-send your last prompt\u2026")):hn.current=null,pn.current&&(pn.current=!1,bt.current.length>0&&(u({kind:"note",tone:"info",text:`(${bt.current.length} queued message(s) discarded)`}),bt.current=[],Rr([])),u({kind:"note",tone:"warn",text:"Interrupted. What do you want to do next?"}))}},[e,u,ia,Ze,ge,X,Ue,xt]),Gt=B(async c=>{let l=yr(e,"standard");pt.current=Date.now(),jt(0),T(!0);let i=new AbortController;ye.current=i;let f=async(d,w)=>{let $=Li(d);te({verb:"Publishing",total:$.length,done:0,active:[],rows:Hn($),tools:0,chars:0,lastAt:Date.now(),startedAt:Date.now()}),u({kind:"note",tone:"info",text:w?`Resolving cross-links in ${d.length} article${d.length===1?"":"s"} whose targets published this run (second pass)\u2026 (esc to stop)`:`Publishing ${d.length} article${d.length===1?"":"s"} across ${$.length} partition${$.length===1?"":"s"} (\u2264${An} agents at once) on ${l.model}\u2026 (esc to stop)`});let h=!1;for await(let x of wr({cwd:e,partitions:$,promptFor:b=>Oo(b.paths),concurrency:An,profileName:s,allowProdWrites:_t.current,model:l.model,signal:i.signal}))if(x.type==="partition_status")x.status==="running"?te(b=>b&&{...b,active:[...b.active,x.label],rows:Oe(b.rows,x.label,"active"),lastAt:Date.now()}):te(b=>b&&{...b,active:b.active.filter(U=>U!==x.label),done:b.done+1,rows:Oe(b.rows,x.label,x.status==="done"?"done":"failed"),lastAt:Date.now()});else if(x.type==="partition_event")te(b=>{if(!b)return b;let U={...b,lastAt:Date.now()};return x.event.type==="tool"?U.tools=b.tools+1:x.event.type==="text"&&(U.chars=b.chars+x.event.delta.length),U});else if(x.type==="run_done"){h=x.aborted,qt(K=>K+x.totalCostUsd),Ht(K=>K+x.results.reduce((P,j)=>P+j.outputTokens,0));let b=x.aborted?"Stopped. ":"",U=t.kind==="api"?"api":"subscription";u({kind:"note",tone:x.aborted?"warn":x.ok?"ok":"warn",text:b+cs(x.results,$,U).join(`
|
|
390
390
|
`)});let M=ls(e,s);if(M&&u({kind:"note",tone:"info",text:M}),!x.aborted){try{let K=await Xd({cwd:e,profileName:s}),P=Qd(K);P?u({kind:"note",tone:"info",text:P}):K.skipped&&u({kind:"note",tone:"warn",text:`Order sync skipped: ${K.skipped}`})}catch(K){u({kind:"note",tone:"warn",text:`Order sync failed: ${K.message}`})}try{let K=Zd(await Jd({cwd:e,profileName:s}));K&&u({kind:"note",tone:"info",text:K})}catch(K){u({kind:"note",tone:"warn",text:`Related-articles sync failed: ${K.message}`})}}}return h};try{if(!await f(c,!1)){let w=await ds(e,s,c);w.length>0&&await f(w,!0)}}catch(d){u({kind:"note",tone:"error",text:`Publish run failed: ${d.message}`})}finally{ye.current=null,te(null),T(!1),z(d=>d+1)}},[e,s,u,t]),Ir=B(async c=>{await ne(is(c),{display:`/publish ${c}`,quietUser:!0});try{let l=await Lo(e,s,c.replace(/\\/g,"/"));l.length>0&&(un(`Related articles not yet published (${l.length}):`),ut([`/publish --related ${c}`,...l.map(i=>`/publish ${i}`)]))}catch{}},[e,s,ne]),aa=B(async(c,l)=>{let f=Pw(c,3),d=yr(e,"standard");pt.current=Date.now(),jt(0),T(!0);let w=new AbortController;ye.current=w,te({verb:"Authoring",unit:"spec",total:f.length,done:0,active:[],rows:Hn(f),tools:0,chars:0,lastAt:Date.now(),startedAt:Date.now()}),u({kind:"note",tone:"info",text:`Authoring ${c.length} screenshot spec${c.length===1?"":"s"} across ${f.length} partition${f.length===1?"":"s"} (\u22646 agents at once) on ${d.model}${d.forced?" (forced)":""}\u2026 (esc to stop)`});let $=!1;try{for await(let h of wr({cwd:e,partitions:f,promptFor:vc,concurrency:6,profileName:s,allowProdWrites:_t.current,model:d.model,signal:w.signal}))if(h.type==="partition_status")h.status==="running"?te(x=>x&&{...x,active:[...x.active,h.label],rows:Oe(x.rows,h.label,"active"),lastAt:Date.now()}):te(x=>x&&{...x,active:x.active.filter(b=>b!==h.label),done:x.done+1,rows:Oe(x.rows,h.label,h.status==="done"?"done":"failed"),lastAt:Date.now()});else if(h.type==="partition_event")te(x=>{if(!x)return x;let b={...x,lastAt:Date.now()};return h.event.type==="tool"?b.tools=x.tools+1:h.event.type==="text"&&(b.chars=x.chars+h.event.delta.length),b});else if(h.type==="run_done"){qt(M=>M+h.totalCostUsd),Ht(M=>M+h.results.reduce((K,P)=>K+P.outputTokens,0));let x=h.results.filter(M=>M.ok).length,b=h.aborted?"Stopped. ":"";$=!h.aborted&&l.setup;let U=h.aborted?"":$?" Refreshing the capture-setup checklist\u2026":" Next: /capture --all (or /capture-setup first for the staging checklist).";u({kind:"note",tone:h.aborted?"warn":h.ok?"ok":"warn",text:`${b}Authored specs \u2014 ${x}/${h.results.length} batches ok.${U}`})}}catch(h){u({kind:"note",tone:"error",text:`Spec authoring failed: ${h.message}`})}finally{ye.current=null,te(null),T(!1),z(h=>h+1)}if($){let h=await _n();h.kind==="forward-to-agent"&&h.prompt&&await ne(h.prompt,{display:h.display,quietUser:!0});let x=Js(e);x&&u({kind:"note",tone:"info",text:x.join(`
|
|
391
391
|
`)})}},[e,s,u,ne]),la=B(async c=>{let l=await Go([c]);l.kind==="forward-to-agent"&&l.prompt&&await ne(l.prompt,{display:l.display,quietUser:!0}),$c(e,c)&&ke(`Spec ready \u2014 /capture ${c} takes the screenshot; /screenshot picks the next shot.`,{kind:"cmd",cmd:`/capture ${c}`,reason:""})},[e,ne,ke]),_r=B(async c=>{if(!Yo(e)){u({kind:"note",tone:"warn",text:"Screenshot capture is not set up in this repo yet \u2014 run d360-capture init in this repo's terminal, then d360-capture auth (once per machine)."});return}T(!0);let l=new AbortController;ye.current=l;let i=c.map(b=>({label:b,items:1,status:"pending"}));te({verb:"Capturing",unit:"screenshot",total:c.length,done:0,active:[],rows:i,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(),d=[];try{for(let b of c){if(l.signal.aborted)break;te(j=>j&&{...j,active:[b],rows:Oe(j.rows,b,"active"),lastAt:Date.now()});let{exitCode:U,output:M}=await Qs({cwd:e,id:b,signal:l.signal,stdio:"pipe"});if(Dc(U,M)){u({kind:"note",tone:"error",text:"d360-capture is not installed on this machine. Run: npm i -g document360-capture \u2014 then /capture again."});return}let K=l.signal.aborted&&U!==0?"failed":Xs(e,b,U,f),P=K==="captured"?void 0:K==="skipped"?Pc(M)??"a data prerequisite isn't met \u2014 the spec's skip reason names what to stage":Tc(M);d.push({id:b,outcome:K,detail:P}),te(j=>j&&{...j,active:[],done:j.done+1,rows:Oe(j.rows,b,K==="captured"?"done":"failed"),lastAt:Date.now()})}}catch(b){u({kind:"note",tone:"error",text:`Capture run failed: ${b.message}`})}finally{ye.current=null,te(null),T(!1),z(b=>b+1)}if(d.length===0)return;let w=d.filter(b=>b.outcome==="captured"),$=d.filter(b=>b.outcome==="skipped"),h=d.filter(b=>b.outcome==="failed"),x=c.length-d.length;u({kind:"note",tone:h.length?"warn":w.length?"ok":"info",text:`\u2713 ${w.length} captured`+($.length?` \xB7 \u2298 ${$.length} skipped`:"")+(h.length?` \xB7 \u2717 ${h.length} failed`:"")+(x?` \xB7 ${x} not run (stopped)`:"")});for(let b of $)u({kind:"note",tone:"info",text:`\u2298 ${b.id} \u2014 ${b.detail}`});for(let b of h)b.detail&&u({kind:"note",tone:"error",text:`\u2717 ${b.id}
|
|
392
|
-
${b.detail}`});if(w.length>0){let b=Ac(e,w.map(M=>M.id)),U=b.length===1?`/publish ${b[0]}`:"/publish";ke(`PNG${w.length===1?"":"s"} saved to the screenshots folder \u2014 publish to put ${w.length===1?"it":"them"} in the article${b.length===1?"":"s"}: ${U}`,{kind:"cmd",cmd:U,reason:""})}else if($.length>0){let b=c.length===1?`/capture ${c[0]}`:"/capture";ke(
|
|
392
|
+
${b.detail}`});if(w.length>0){let b=Ac(e,w.map(M=>M.id)),U=b.length===1?`/publish ${b[0]}`:"/publish";ke(`PNG${w.length===1?"":"s"} saved to the screenshots folder \u2014 publish to put ${w.length===1?"it":"them"} in the article${b.length===1?"":"s"}: ${U}`,{kind:"cmd",cmd:U,reason:""})}else if($.length>0){let b=c.length===1?`/capture ${c[0]}`:"/capture";ke(null,{kind:"cmd",cmd:b,reason:""})}},[e,u,ke]),Or=B(c=>{let l=Dt(c);if(!l||ct(l)&&!l.refreshToken)return null;let i={...ln(l.idToken)??{},...ln(l.accessToken)??{}};return i.email??i.preferred_username??"signed in"},[]),Lr=B((c,l)=>{if(l){let i=oe(e);i&&(i.defaultProfile=c,Fd(i,e))}a(c),Se(void 0,c),u({kind:"note",tone:"ok",text:`\u2713 Switched to profile "${c}"${l?" (saved as default)":" (this session only)"} \u2014 agent restarted.`}),Or(c)||(u({kind:"note",tone:"warn",text:`Profile "${c}" is not signed in \u2014 sign in with /login.`}),X("/login")),z(i=>i+1)},[e,u,Se,Or,X]),ao=B((c,l,i)=>{go(e,c,l,i.id,i.name),u({kind:"note",tone:"ok",text:`Switched to workspace "${i.name??i.id}" (agent restarted).`}),Se(),Hd(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(`
|
|
393
393
|
`)}),Bt(vt()),wn.current=Ie(e).status==="current",Vt.current=ze(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,Se,Bt]),lo=B(async c=>{let l;try{l=await vn(e,c)}catch{u({kind:"note",tone:"info",text:"Next: pick a workspace \u2014 run /workspace."}),X("/workspace");return}if(l.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=l.workspaces.filter(d=>(d.workspace_type??"").toLowerCase()!=="apidocumentation"),f=i.length>0?i:l.workspaces;if(f.length===1){let d=f[0],w=l.workspaces.length-f.length;u({kind:"note",tone:"ok",text:`Selected the "${d.name??d.id}" workspace${w>0?" (skipped the API-documentation workspace)":""}.`}),ao(l.profile,l.projectId,d);return}u({kind:"note",tone:"info",text:`Next: pick the workspace your articles publish to (${f.length} available).`}),X("/workspace")},[e,u,ao,X]),co=B((c,l)=>{wo(e,c,l.id,l.name),u({kind:"note",tone:"ok",text:`Project set to "${l.name??l.id}" (agent restarted).`}),Se(),lo(c)},[e,u,Se,lo]),ca=B(async c=>{let l;try{l=Te(e,c)}catch{return}if(!l.project.workspaceId){if(l.project.projectId){if(!l.project.projectName)try{let{projects:i}=await nt(e,l.name),f=i.find(d=>d.id===l.project.projectId);f?.name&&Ow(e,l.name,{projectName:f.name})}catch{}await lo(l.name);return}try{let{projects:i}=await nt(e,l.name);if(i.length===1){let f=i[0];u({kind:"note",tone:"ok",text:`Using your Document360 project "${f.name??f.id}".`}),co(l.name,{id:f.id,name:f.name});return}if(i.length>1){let f=i.map(d=>({id:d.id,name:d.name??d.id,sub:d.sub_domain}));u({kind:"note",tone:"info",text:"Which Document360 project should these docs publish to?"}),gt({cursor:0,current:-1,rows:f,profile:l.name,environment:l.connection.name});return}}catch{}u({kind:"note",tone:"info",text:"Next: pick the Document360 project \u2014 run /project."}),X("/project")}},[e,u,lo,co,X]),Yt=B(()=>{let c=le.current[0];if(!c)return;Ue();let{plan:l,mode:i}=c;u({kind:"note",tone:"info",text:`\u25CF ${l.title} (${l.path})`});for(let d of l.notes)u({kind:"note",tone:"warn",text:`\u26A0 ${d}`});l.overwritesLocalChanges&&i==="pull"&&u({kind:"note",tone:"warn",text:"\u26A0 This OVERWRITES local edits made since the last sync."});let f=Ke(l.oldContent,l.newContent,Ze());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 ${l.path}? (y/n \u2014 anything else cancels)`})},[u,Ze,Ue]),Mr=B(()=>{if(le.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`}),Gt(c)):X("/pull")},[Yt,u,Gt,X]),da=B(c=>{if(le.current.length===0)return!1;let l=c.trim().toLowerCase(),i=le.current[0];if(i.mode==="conflict"){if(l==="l"||l==="local")le.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(l==="p"||l==="portal"){le.current.shift();try{qd({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(l==="s"||l==="skip"||l==="n")le.current.shift(),u({kind:"note",tone:"info",text:`Skipped ${i.plan.path} \u2014 still a conflict.`});else{let f=le.current.length;return le.current=[],Ot.current=[],u({kind:"note",tone:"info",text:`Resolve cancelled (${f} conflict${f===1?"":"s"} left untouched).`}),!0}return Mr(),!0}if(l==="y"||l==="yes"){let f=le.current.shift().plan;try{qd({cwd:e,profileName:s},f),u({kind:"note",tone:"ok",text:`\u2713 Pulled ${f.path} (sync base advanced).`})}catch(d){u({kind:"note",tone:"error",text:`Pull failed: ${d.message}`})}}else if(l==="n"||l==="no"){let f=le.current.shift().plan;u({kind:"note",tone:"info",text:`Skipped ${f.path}.`})}else{let f=le.current.length;return le.current=[],u({kind:"note",tone:"info",text:`Pull cancelled (${f} article(s) left untouched).`}),!0}return Mr(),!0},[e,s,Mr,u]),Ur=B(async c=>{u({kind:"note",tone:"info",text:"Checking Document360 for conflicts\u2026"});try{let l=await Oi({cwd:e,profileName:s}),i=c?.replace(/\\/g,"/"),f=l.entries.filter(w=>w.status==="conflict"&&w.path).map(w=>w.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 d=[];for(let w of f)try{d.push({plan:await Vd({cwd:e,profileName:s,relPath:w}),mode:"conflict"})}catch($){u({kind:"note",tone:"error",text:`Could not load ${w} from Document360: ${$.message}`})}if(d.length===0)return;u({kind:"note",tone:"info",text:`Resolving ${d.length} conflict${d.length===1?"":"s"} \u2014 for each: keep local, take portal, or skip.`}),le.current=d,Ot.current=[],Yt()}catch(l){u({kind:"note",tone:"error",text:`Could not compute sync status: ${l.message}`})}},[e,s,u,Yt]),ua=B(c=>{let l=Qn.current;if(!l)return!1;if(Qn.current=null,Ue(),c.trim()!==l.repoName)return u({kind:"note",tone:"warn",text:"Reset cancelled \u2014 nothing deleted."}),!0;let{removed:i,failed:f}=ci(e,l.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 d of f)u({kind:"note",tone:"error",text:` \u2717 ${d.path}: ${d.error}`});return l.hadDevhints&&(u({kind:"note",tone:"warn",text:To().join(`
|
|
394
394
|
`)}),Bt(zr())),u({kind:"note",tone:"info",text:`Clean slate \u2014 set up d360-writer again whenever you're ready:
|
|
395
395
|
${eu}`}),X("/init"),Yn(!1),!0},[e,u,Bt,Ue,X]),bn=B(async c=>{let l=c.slice(1).trim().split(/\s+/),i=(l[0]??"").toLowerCase(),f=l.slice(1);if(Yn(!0),Fo(f)&&["write","check","review","reorder"].includes(i)){let d=En(e,s);if(d.length===0){u({kind:"note",tone:"warn",text:"No categories yet \u2014 author some articles first, then scope to one."});return}let w=$=>i==="write"?`/write ${Ul(f,$).join(" ")}`.trim():`/${i} ${[$,...f.filter(h=>h!=="--scope")].join(" ")}`.trim();if(d.length===1){bn(w(d[0].token));return}yt({cursor:0,rows:d,run:$=>{bn(w($))}});return}if(at(i,f)){let d=En(e,s);if(d.length>1){let w=f.filter(b=>b.startsWith("--")).join(" "),$=b=>`/${i}${b?` ${i==="write"?`--scope ${b}`:b}`:""}${w?` ${w}`:""} ${b?"":"--all"}`.replace(/\s+/g," ").trim(),h=d.reduce((b,U)=>b+U.count,0),x={token:"",label:i==="review"?`Everything \u2014 all ${h} articles + structure (IA) review`:i==="reorder"?`Everything \u2014 reorder all ${d.length} categories`:`Everything \u2014 rewrite all ${h} planned articles`,count:h};yt({cursor:0,rows:[x,...d],run:b=>{bn($(b))}});return}}switch(i){case"help":mn({tab:0});return;case"exit":case"quit":Ae.current?.close(),r();return;case"clear":Se(),It.current=[],Yn(!1),Ue(),console.log("\x1B[?2026h\x1B[H\x1B[2J\x1B[3J\x1B[?2026l"),u({kind:"note",tone:"info",text:"Conversation reset (the previous session is still resumable via /resume)."});return;case"login":{let d;try{d=Te(e,s)}catch(w){u({kind:"note",tone:"error",text:w.message});return}u({kind:"note",tone:"info",text:`Profile "${d.name}" \u2192 ${d.connection.name} (${d.connection.apiUrl})${d.production?" \u26A0 PRODUCTION":""}`}),G(!0);try{let w=await rw(d.connection,{promptForRedirect:()=>Promise.reject(new Error("Manual login is CLI-only. Run: d360-writer login --manual"))},h=>u({kind:"note",tone:"info",text:h})),$=sw(d.name,w);if(iw($),await qr($,d.name,h=>u({kind:"note",tone:"info",text:h})),u({kind:"note",tone:"ok",text:`\u2713 Logged in to "${d.name}" as ${bo($)}`}),await ca(s),fn.current){let h=fn.current;fn.current=null;let x=_e(e,s);x.ok?(u({kind:"note",tone:"ok",text:`\u2713 Signed into the right project \u2014 tab to run ${h}.`}),X(h)):(u({kind:"note",tone:"warn",text:`${x.reason} (then ${h}.)`}),X("/login"),fn.current=h)}else hn.current&&(ge(hn.current,"\u21E5 tab to re-send your last prompt\u2026"),hn.current=null)}catch(w){u({kind:"note",tone:"error",text:`Login failed: ${w.message}`})}finally{G(!1),z(w=>w+1)}return}case"allow-prod":{let d=!1;try{d=Te(e,s).production}catch{}if(!d){u({kind:"note",tone:"info",text:"Current profile is not production \u2014 writes are already allowed."});return}_t.current=!0,Se(),u({kind:"note",tone:"warn",text:"\u26A0 Production writes authorized for this session."});return}case"rename":{let d=Hs(f.join(" ")),w=qe.current.uuid;if(!w){if(!d){u({kind:"note",tone:"info",text:"Usage: /rename <name>"});return}Cr.current=d,ht(d),rn(`${ur} ${d}`),u({kind:"note",tone:"ok",text:`Session will be named "${d}" once it starts.`});return}if(!d){u({kind:"note",tone:"info",text:"Thinking of a name\u2026"});let $=qe.current.firstPrompt??"";Wd($,e).then(h=>{h?(ge(`/rename ${h}`),u({kind:"note",tone:"info",text:`Suggestion: "${h}" \u2014 press tab to accept, or type /rename <your name>.`})):u({kind:"note",tone:"info",text:"Usage: /rename <name>"})}).catch(()=>u({kind:"note",tone:"info",text:"Usage: /rename <name>"}));return}ww(w,d),ht(d),rn(`${ur} ${d}`),u({kind:"note",tone:"ok",text:`Session renamed to "${d}".`});return}case"profile":{let d=f[0],w=oe(e);if(!d){let $=Object.entries(w?.profiles??{});if($.length===0){u({kind:"note",tone:"info",text:"No profiles. Run /init first."});return}let h=$.map(([U,M])=>({name:U,env:M.connection?.environment??"custom",prod:M.production===!0,who:Or(U)})),x=s??w?.defaultProfile,b=Math.max(0,h.findIndex(U=>U.name===x));Lt({cursor:b,current:b,rows:h});return}if(d==="add"){let $=qs(e,f[1],f[2]);if($){u({kind:"note",tone:"error",text:$});return}u({kind:"note",tone:"ok",text:`\u2713 Profile "${f[1]}" created (environment: ${f[2]??f[1]}).`}),Lr(f[1],!1);return}if(!w?.profiles?.[d]){u({kind:"note",tone:"error",text:`Unknown profile "${d}". Create it: /profile add ${d} <environment>`});return}Lr(d,!0);return}case"doctor":await In(f,{cwd:e,version:o});return;case"genre":{await Zo(f,{cwd:e});try{br.current=!(oe(e)?.appType??bw(e))}catch{}z(d=>d+1);return}case"roadmap":{if(f[0]?.toLowerCase()==="done"){let d=wi(e,f.slice(1).join(" ").trim());u({kind:"note",tone:d.ok?"ok":"warn",text:d.line}),z(w=>w+1);return}u({kind:"note",tone:"info",text:gi(e).join(`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "document360-writer",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.130",
|
|
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.
|
|
37
|
+
"document360-engine": "^0.2.134",
|
|
38
38
|
"ink": "^5.2.1",
|
|
39
39
|
"picocolors": "^1.1.1",
|
|
40
40
|
"react": "^18.3.1",
|