document360-writer 0.5.313 → 0.5.315
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 +36 -36
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -336,12 +336,12 @@ ${n.map(c=>` \u25CB ${c.title}`).join(`
|
|
|
336
336
|
`)}`};let a=i[0].title;IS(e,a,{doneAt:new Date().toISOString()});let l=gy(e)?.next;return{ok:!0,line:`\u2713 Marked done: ${a}${l?` \u2014 next up: ${l.title}${l.run?` (${l.run})`:""}`:" \u2014 that was the last step: campaign complete."}`}}function yy(e,t){let n=r=>r.trim().replace(/\s+/g," ").toLowerCase(),o=n(t);return o.startsWith("/")?Ms(e).find(r=>r.status==="open"&&!!r.run&&n(r.run)===o)??null:null}function Ga(e,t){let n=(Array.isArray(t)?t:[t]).filter(s=>s.trim().startsWith("/"));if(n.length===0)return[];let o=new Set,r=[];for(let s of Ms(e))s.status==="open"&&n.some(i=>uy(i,s.run))&&(o.has(s.title)||(o.add(s.title),r.push(s.title)));return r}function Ye(e,t){if(!t)return[];try{let n=Ga(e,t).filter(o=>Ir(e,o).ok);return n.length===0?[]:n.length===1?[`\u2713 That completed a docs-campaign step \u2014 marked done: ${n[0]}`]:[`\u2713 That completed ${n.length} docs-campaign steps \u2014 marked done:`,...n.map(o=>` \u2022 ${o}`)]}catch{return[]}}function qa(e,t){let n=Ga(e,t);if(n.length===0)return null;let o=n.map(r=>`"${r}"`).join(", ");return[n.length===1?`Note: this command executes the open docs-roadmap step ${o}.`:`Note: this command executes ${n.length} open docs-roadmap steps: ${o}.`,`Close the loop IN THE SAME TURN the goal state is reached: call d360_roadmap_done with exactly the title of EACH step it completed (${o}), then tell the user they are marked done \u2014 including when you find the goal already holds ("no action needed" completes the STEP, not just the turn). Leave one open ONLY if its work is genuinely incomplete, and say exactly what remains.`].join(`
|
|
337
337
|
`)}function wy(e,t){let n=t.trim().toLowerCase();if(!n)return null;let o=Ms(e).find(r=>r.status==="open"&&r.title.trim().toLowerCase()===n);return o?[`Execute this open docs-roadmap step: "${o.title}".`,o.run?`Its run command: ${o.run}`:"",o.body?`Step detail:
|
|
338
338
|
${o.body}`:"","",`Close the loop IN THE SAME TURN the step's goal state is reached: call d360_roadmap_done with exactly the title "${o.title}", then tell the user it is marked done. This INCLUDES discovering the goal already holds \u2014 "no action needed" completes the STEP, not just the turn, so stamp it done then too. Leave it open ONLY if the work is genuinely incomplete (blocked, or background results not yet applied) \u2014 then say exactly what remains before it can be closed.`].filter(Boolean).join(`
|
|
339
|
-
`):null}async function by(e,t){let n=t.cwd;if(e[0]?.toLowerCase()==="done"){let o=Ir(n,e.slice(1).join(" ").trim());return console.log(o.ok?Q(o.line):V(o.line)),{kind:"continue"}}for(let o of od(n,{all:e.some(r=>r.toLowerCase()==="--all")}))console.log(o);return{kind:"continue"}}var sd={"local-ahead":"modified locally","untracked-local":"new (never published)",conflict:"\u26A0 conflict \u2014 pushing overwrites the portal edit"},JS="\u26A0 held on Document360 in another editor \u2014 /resolve settles it";function Uo(e){return e.filter(t=>t.path!==null&&t.status in sd&&!t.link).map(t=>({path:t.path,label:sd[t.status]}))}function _o(e,t=[]){let n=Uo(e),o=new Set(t),r=new Set([...e.filter(s=>s.status==="conflict"&&s.path).map(s=>s.path),...o]);return{clean:n.filter(s=>!r.has(s.path)),conflicts:n.filter(s=>r.has(s.path)).map(s=>o.has(s.path)&&s.label!==sd.conflict?{...s,label:JS}:s)}}function Mo(e,t){try{return WS(e,Ka(e,t).name).map(n=>n.path)}catch{return[]}}function Us(e,t=0){let n=e!==1;if(t>=e)return`\u26A0 ${e} article${n?"s":""} NOT pushed (nothing on the portal overwritten) \u2014 Document360 holds ${n?"their bodies":"its body"} in another editor, so ${n?"they cannot be":"it cannot be"} read back. Settle ${n?"them":"it"}: /resolve (or ask your Document360 admin to move ${n?"them":"it"} to the Markdown editor).`;let o=t>0?` (${t} of ${n?"them are":"these is"} held in another editor and cannot be read back)`:"";return`\u26A0 ${e} conflict${n?"s":""} NOT pushed (portal edit${n?"s":""} kept safe) \u2014 ${n?"these were":"it was"} edited both here and on the portal, so ${n?"they are":"it is"} left untouched.${o} Settle ${n?"them":"it"}: /resolve (or /push <path> to keep yours, /pull <path> to take the portal's).`}function id(e,t){let n=t.scope?.replace(/\\/g,"/").replace(/\/+$/,"");return!n||!n.endsWith(".md")||!xy(e,t.profileName).map(r=>r.replace(/\\/g,"/")).some(r=>r===n||r.endsWith(`/${n}`)||n.endsWith(`/${r}`))?null:{cmd:Se("/publish","",n),reason:"it takes this draft live \u2014 until it does, your readers still see the previous version"}}function Lr(e,t){let n=xy(e,t),o=n.length;if(o===0)return null;let r=o===1?"":"s",s=0;try{s=FS(e,n).length}catch{}if(s>0){let i=o-s,a=`${s===o?o===1?"It is":"All of them are":`${s} of them ${s===1?"is":"are"}`} waiting on a missing picture`;return`\u2139 ${o} article${r} ${o===1?"is":"are"} saved on Document360 as a draft and not live yet \u2014 readers still see the previous version. ${a}, so /publish holds ${s===1?"it":"them"} back${i>0?` and takes the other ${i} live once ${i===1?"it has":"they have"} been signed off.`:"."}`}return`\u2139 ${o} article${r} ${o===1?"is":"are"} saved on Document360 as a draft and not live yet \u2014 readers still see the previous version. /publish takes ${o===1?"it":"them"} live once ${o===1?"it has":"they have"} been signed off.`}function Ya(e){return e.filter(t=>t.link).length}function ad(e,t){try{let n=vy(e,Ka(e,t).name);if(!n)return null;let o=0;for(let r of Object.keys(n.articles))try{o+=QS(LS(OS(e,r),"utf8"),r,n).unresolved}catch{}return o===0?null:`\u2139 ${o} cross-link${o===1?"":"s"} point to pages that aren't on Document360 yet \u2014 they show as plain text for now and turn into links once those pages are pushed.`}catch{return null}}function Ey(e,t=[]){let n=[];e.length>1&&n.push({kind:"all",label:`Push all ${e.length} articles`,paths:e.map(r=>r.path),indent:!1});let o=new Map;for(let r of e){let s=r.path.replace(/\\/g,"/").split("/").slice(0,-1).join("/"),i=o.get(s);i?i.push(r):o.set(s,[r])}for(let r of[...o.keys()].sort()){let s=o.get(r).sort((i,a)=>i.path.localeCompare(a.path));n.push({kind:"category",label:`${qn(r)} (${s.length})`,paths:s.map(i=>i.path),indent:!1});for(let i of s){let a=i.path.replace(/\\/g,"/").split("/").pop()??i.path;n.push({kind:"article",label:a,paths:[i.path],indent:!0,note:i.label})}}for(let r of[...t].sort((s,i)=>s.path.localeCompare(i.path))){let s=r.path.replace(/\\/g,"/").split("/").pop()??r.path;n.push({kind:"article",label:s,paths:[r.path],indent:!1,note:r.label,conflict:!0})}return n}async function Fs(e,t){try{let n=Ka(e,t),o=n.project.projectId,r=n.project.workspaceId;if(!o||!r)return null;let s=await XS({profile:n.name,connection:n.connection,projectId:o,projectName:n.project.projectName},{cwd:e,profile:n.name,projectPath:`/v3/projects/${o}`,workspaceId:r,verifyHidden:!1});if(s.kind!=="created")return null;if(s.hidden!=="confirmed"){let i=s.seeded.length===0?" Its internal articles were NOT created, because that would put docs-agent scaffolding in front of your readers.":"";return`\u26A0 Created the "${ky}" folder on Document360 for reviewer feedback, but could NOT confirm it is hidden from readers.${i} Open its category settings in the portal and turn on "Hide from readers", then run /catch-up.`}return`\u2139 Created a hidden "${ky}" folder on Document360 \u2014 it's where your reviewers leave feedback for d360-writer to action (confirmed hidden from readers; its README explains how). /catch-up picks up what they write.`}catch{return null}}var Lt="--skip-review";function Ws(e,t){return BS(e,t)}function za(e,t){try{return Ws(e,Sy(e,t)).length}catch{return 0}}function Ot(e,t,n="",o,r){let s=za(e,o),i=Xa(e,o),a=o?` ${o}`:"",l=o?"/push":"/push --all",c=ly(e,{just:r}),f=!!o&&o.endsWith(".md"),g=c&&f?` (${c.short} \u2014 ${c.cmd} once this page is delivered.)`:"";if(c&&!f){let T=s>0?` (${s} article${s===1?"":"s"}${o&&!o.endsWith(".md")?` in ${o}`:""} ${s===1?"carries":"carry"} text no editor has read, so ${s===1?"it cannot":"they cannot"} be sent up until /review${a} --blocked --fix runs \u2014 that can wait until the writing is done.)`:i>0?` (${i} article${i===1?"":"s"} break${i===1?"s":""} house style rules that block sending up \u2014 /lint${a} --blocked --fix clears that whenever you want.)`:"";return{text:`${n}${c.text}${T}`,cmd:c.cmd}}if(s===0&&i===0)return g?{...t,text:`${t.text}${g}`}:t;let y=o&&!o.endsWith(".md")?` in ${o}`:"";if(s>0){let T=s===1?"":"s",C=i>0?` ${i} also break house style rules \u2014 /lint${a} --blocked --fix clears those afterwards.`:"",P=`/review${a} --blocked --fix`;return{text:`${n}${s} article${T}${y} ${s===1?"carries":"carry"} text no editor has read, so pushing ${s===1?"it is":"them is"} blocked. ${P} reads ${s===1?"it":"just those"} and applies its fixes.${C} Then ${l}.${g}`,cmd:P}}return{text:`${n}${i} article${i===1?"":"s"}${y} break${i===1?"s":""} house style rules that block publishing. /lint${a} --blocked --fix corrects them in place \u2014 finding them costs nothing \u2014 then ${l}.${g}`,cmd:`/lint${a} --blocked --fix`}}function Xa(e,t){try{return Ry(e,Sy(e,t)).length}catch{return 0}}function Ny(e,t){return Ry(e,t)}function ld(e,t,n){try{let o=vy(e,Ka(e,n).name);if(!o)return 0;let r=i=>i.replace(/\\/g,"/"),s=new Set(Object.keys(o.articles??{}).map(r));return t.filter(i=>s.has(r(i))).length}catch{return 0}}function cd(e,t){let n=`/push${t.scope?` ${t.scope}`:" --all"}`,o=l=>`${l} article${l===1?"":"s"}`,r=[],s=e.pushed.length,i=t.elapsedMs!==void 0?` \xB7 ${tC(t.elapsedMs)}`:"",a=e.screenshotsUploaded>0?` \xB7 ${e.screenshotsUploaded} screenshot${e.screenshotsUploaded===1?"":"s"} uploaded`:"";if(s===0&&e.failed.length===0?r.push("Nothing was pushed \u2014 no articles were sent."):t.recorded<s?(r.push(`Pushed ${t.recorded}/${o(s)}${a}${i}.`),r.push(`${s-t.recorded} article${s-t.recorded===1?"":"s"} reported success but ${s-t.recorded===1?"is":"are"} not recorded in the category map \u2014 re-run ${n}.`)):(r.push(`Pushed ${o(s)}${e.created>0?` (${e.created} new)`:""}${a}${i}.`),r.push(`Saved on Document360 as draft${s===1?"":"s"} \u2014 readers don't see ${s===1?"it":"them"} until /publish takes ${s===1?"it":"them"} live.`)),e.stopped?.reason==="unavailable"&&r.push(`Stopped: ${e.stopped.streak} articles in a row could not reach Document360${e.notRun.length>0?`, so the remaining ${e.notRun.length} ${e.notRun.length===1?"was":"were"} not attempted`:""}.`),e.stopped?.reason==="identity"&&r.push(`Nothing was pushed. ${e.stopped.error}`),e.failed.length>0){let l=e.failed.filter(g=>g.refused),c=eC(e.failed,t.docsRoot),f=e.failed.length;if(c.length===1&&f>1)r.push(`${o(f)} could not be pushed \u2014 all with the same error: ${c[0].error}`),r.push(...$y(c[0].paths," \u2717 "));else{r.push(`${o(f)} could not be pushed:`);for(let g of c)g.paths.length===1?r.push(` \u2717 ${g.paths[0]} \u2014 ${g.error}`):(r.push(` \u2717 ${o(g.paths.length)} \u2014 ${g.error}`),r.push(...$y(g.paths," ")))}l.length<e.failed.length&&(e.stopped?r.push(`Nothing was lost \u2014 your articles are unchanged. Re-run ${n} when Document360 is back.`):r.push(`Re-run ${n} to retry the rest \u2014 articles already saved are sent again only if they changed.`))}if(e.notRun.length>0&&!e.stopped&&r.push(`\u26A0 ${o(e.notRun.length)} never started (the run was stopped): ${ZS(e.notRun)}`),e.missingShots.length>0){let l=e.missingShots.flatMap(c=>c.ids);r.push(`\u26A0 ${l.length} screenshot${l.length===1?"":"s"} in ${o(e.missingShots.length)} ${l.length===1?"has":"have"} not been taken, so the description was published instead: /screenshot capture`)}if(e.categoryLookupFailed.length>0){let l=e.categoryLookupFailed.length;r.push(`\u26A0 Could not check Document360 for existing categories, so ${l===1?"a new one was":`${l} new ones were`} created. There may now be two with the same name: /doctor lists them.`),r.push(` Reason: ${e.categoryLookupFailed[0].reason}`)}return t.conflictsSkipped&&t.conflictsSkipped>0&&r.push(Us(t.conflictsSkipped,t.unreadableSkipped??0)),r}var ZS=(e,t=5)=>`${e.slice(0,t).join(", ")}${e.length>t?`, +${e.length-t} more`:""}`,rd=5;function $y(e,t){let n=e.slice(0,rd).map(o=>`${t}${o}`);return e.length>rd&&n.push(`${" ".repeat(t.length)}\u2026 +${e.length-rd} more`),n}function eC(e,t){let n=t?`${t.replace(/\\/g,"/").replace(/\/+$/,"")}/`:null,o=s=>{let i=s.replace(/\\/g,"/");return n&&i.startsWith(n)?i.slice(n.length):i},r=new Map;for(let s of e){let i=r.get(s.error);i?i.push(o(s.path)):r.set(s.error,[o(s.path)])}return[...r.entries()].map(([s,i])=>({error:s,paths:i})).sort((s,i)=>i.paths.length-s.paths.length)}function tC(e){let t=Math.round(e/1e3);return t<60?`${t}s`:`${Math.floor(t/60)}m ${String(t%60).padStart(2,"0")}s`}async function Jt(e,t,n=0,o){let r=HS(e.cwd,t,{allowUnreviewed:o?.skipReview===!0,allowStyleErrors:o?.skipStyle===!0});if(!r.clear){for(let c of GS(r,t.length,{act:"push"}))console.log(V(c));return{kind:"continue"}}let s={aborted:!1,partitions:0,failed:0,notRun:0,producedNothing:!0},i={retryable:0,stopped:!1,paths:[]},a=async(c,f)=>{let g=Date.now();console.log(d(f?`Resolving cross-links in ${c.length} article${c.length===1?"":"s"} whose targets published this run (second pass)\u2026`:`Pushing ${c.length} article${c.length===1?"":"s"} to Document360 (${YS} at a time)\u2026`));let y=await qS({cwd:e.cwd,...e.profileName?{profileName:e.profileName}:{},paths:c,...o?.allowUnpullable===!0?{allowUnpullable:!0}:{},onEvent:G=>{G.type==="article_done"?console.log(Q(` \u2713 ${G.result.path}${G.result.action==="created"?" (new)":""}`)):G.type==="article_failed"&&console.log(_(` \u2717 ${G.failure.path}`))}});console.log("");let x=ld(e.cwd,c,e.profileName);f||(i={retryable:y.failed.filter(G=>!G.refused).length,stopped:!!y.stopped,paths:y.failed.map(G=>G.path)},s={aborted:y.aborted||!!y.stopped,partitions:MS(c).length,failed:y.failed.length,notRun:y.notRun.length,producedNothing:y.pushed.length===0,verifiedComplete:x>=c.length,leftBehind:n});let T=y.pushed.length>0;for(let G of cd(y,{recorded:x,elapsedMs:Date.now()-g,docsRoot:zS(e.cwd),...f?{}:{conflictsSkipped:n,unreadableSkipped:o?.unreadableSkipped??0},...o?.scope?{scope:o.scope}:{}}))console.log(T&&y.failed.length===0?Q(G):V(G));let C=y.aborted||!!y.stopped;if(!T)return C;let P=ad(e.cwd,e.profileName);P&&console.log(d(P));let S=Lr(e.cwd,e.profileName);if(S&&console.log(d(S)),C)return C;try{let G=Py(await Cy({cwd:e.cwd,profileName:e.profileName,onRateLimit:D=>console.log(d(` Document360 rate limit \u2014 waiting ${Math.ceil(D.waitMs/1e3)}s\u2026`))}));G&&console.log(G.startsWith("\u26A0")?V(G):d(G))}catch{}try{let G=Ay(await Ty({cwd:e.cwd,profileName:e.profileName}));G&&console.log(d(G))}catch{}let j=await Fs(e.cwd,e.profileName);return j&&console.log(d(j)),C};try{if(!await a(t,!1)){let f=await ud(e.cwd,e.profileName,t,i.paths);if(f.length>0&&await a(f,!0),Qt(s))for(let g of Ye(e.cwd,o?.scope?`/push ${o.scope}`:"/push --all"))console.log(Q(g))}}catch(c){console.log(_(`Push run failed: ${c.message}`))}if(console.log(""),i.retryable>0){let c=Se("/push",o?.scope?"":"--all",o?.scope);return console.log(d(i.stopped?`Next: ${c} \u2014 your articles are unchanged, so it picks up where the run left off.`:`Next: ${c} \u2014 retries the ${i.retryable} article${i.retryable===1?"":"s"} that could not be saved.`)),{kind:"continue",ownNext:!0}}let l=id(e.cwd,{scope:o?.scope,profileName:e.profileName});return l?(console.log(d(`Next: ${l.cmd} \u2014 ${l.reason}.`)),{kind:"continue",ownNext:!0}):{kind:"continue"}}async function Qa(e,t,n){let o=US(e,n);if(o.length===0)return[];let r=new Set(Uo((await Va({cwd:e,profileName:t})).entries).map(s=>s.path));return o.filter(s=>r.has(s))}async function ud(e,t,n,o=[]){try{let r=new Set(n),s=new Set(o),i=await Va({cwd:e,profileName:t});return Uo(i.entries).map(a=>a.path).filter(a=>r.has(a)&&!s.has(a))}catch{return[]}}async function nC(e,t,n){try{let o=await VS({cwd:e,...t?{profileName:t}:{},relPath:n.replace(/\\/g,"/"),preview:!0});console.log(""),console.log(Q(`${o.title}`)),console.log(d(`${o.path} \xB7 ${o.articleId?`updates article ${o.articleId}`:"would be created as a new article"}`)),o.shots.length>0&&console.log(d(`${o.shots.length} screenshot${o.shots.length===1?"":"s"} embedded: ${o.shots.map(r=>r.id).join(", ")}`)),o.missingShots.length>0&&console.log(V(`\u26A0 ${o.missingShots.length} screenshot${o.missingShots.length===1?"":"s"} not taken yet \u2014 the description publishes instead: ${o.missingShots.join(", ")}`)),console.log(d("\u2500".repeat(Math.min(80,Math.max(20,(process.stdout.columns??80)-1))))),console.log(o.body),console.log(d("\u2500".repeat(Math.min(80,Math.max(20,(process.stdout.columns??80)-1))))),console.log(d(`Nothing was sent. Run /push ${o.path} to save it as a draft.`))}catch(o){console.log(_(o.message))}return{kind:"continue"}}async function Ja(e,t){let n=Je(t?.cwd??process.cwd(),t?.profileName);if(!n.ok)return console.log(_(n.reason)),{kind:"continue"};let o=e.includes(Lt),r=e.includes(KS);if(e.includes("--preview")){let i=e.find(a=>!a.startsWith("--"));return i?await nC(t?.cwd??process.cwd(),t?.profileName,i):(console.log(_("Which article? Use /push --preview <path> \u2014 a preview is per article.")),{kind:"continue"})}let s=e[0];if(e.includes("--screenshots")||e.includes("--shots")){let i=t?.cwd??process.cwd(),a=e.find(x=>!x.startsWith("--")),l=He(i),c=_a(l,a);if(c.length===0){let x=l.filter(T=>T.state==="captured").length;return console.log(d(x>0?`${x} screenshot${x===1?" has":"s have"} been taken but the callouts aren't drawn on yet, so there is nothing to embed \u2014 /screenshot annotate finishes ${x===1?"it":"them"}.`:a?`No captured screenshots in ${a} \u2014 /push --screenshots covers the whole knowledge base.`:"No captured screenshots yet \u2014 /screenshot capture first.")),{kind:"continue"}}let f=Ma(l,a);f&&console.log(f);let g=Ua(l);g&&console.log(g);let y=Se("","--screenshots",a).trim();return await Jt(t??{cwd:process.cwd()},c,0,{skipReview:o,skipStyle:r,scope:y})}if(s==="--related"&&e[1]){if(!t)return{kind:"continue"};let i=await Qa(t.cwd,t.profileName,e[1].replace(/\\/g,"/"));return i.length===0?(console.log(Q("\u2713 All related articles are already on Document360.")),{kind:"continue"}):await Jt(t,i,0,{skipReview:o,skipStyle:r,scope:`--related ${e[1]}`})}if(s==="--all"){console.log(d("Checking what needs pushing\u2026"));try{let i=await Va({cwd:t?.cwd??process.cwd()}),a=new Set(Mo(t?.cwd??process.cwd(),t?.profileName)),{clean:l,conflicts:c}=_o(i.entries,a),f=c.filter(C=>a.has(C.path)).length,g=Ya(i.entries),y=e.includes("--force"),x=y&&c.length>0;g>0&&console.log(V(`\u26A0 ${g} article(s) already exist on Document360 but aren't linked \u2014 left out (pushing would duplicate them). Run /catch-up or ask me to link them first.`)),c.length>0&&!x&&console.log(V(Us(c.length,f)));let T=x?[...l,...c]:l;if(T.length===0){if(c.length>0){let P=Lr(t?.cwd??process.cwd(),t?.profileName);return P&&console.log(d(P)),console.log(d("Resolve the conflicts: /resolve")),{kind:"continue"}}console.log(Q(g>0?"\u2713 Nothing else is ahead of Document360 to push.":"\u2713 Nothing is ahead of Document360 \u2014 nothing to push."));let C=Lr(t?.cwd??process.cwd(),t?.profileName);if(C&&console.log(d(C)),t)try{let P=Py(await Cy({cwd:t.cwd,profileName:t.profileName,onRateLimit:j=>console.log(d(` Document360 rate limit \u2014 waiting ${Math.ceil(j.waitMs/1e3)}s\u2026`))}));P&&console.log(P.startsWith("\u26A0")?V(P):d(P));let S=Ay(await Ty({cwd:t.cwd,profileName:t.profileName}));S&&console.log(d(S))}catch{}return{kind:"continue"}}return await Jt(t??{cwd:process.cwd()},T.map(C=>C.path),x?0:c.length,{skipReview:o,skipStyle:r,allowUnpullable:y,unreadableSkipped:x?0:f})}catch(i){return console.log(_(`Could not compute sync status: ${i.message}`)),{kind:"continue"}}}if(s&&s.startsWith("--")&&(s=e.find(i=>!i.startsWith("--"))??""),!s){console.log(d("Checking what needs pushing\u2026"));let i,a=0,l=0;try{let c=await Va({cwd:t?.cwd??process.cwd()}),f=new Set(Mo(t?.cwd??process.cwd(),t?.profileName)),{clean:g,conflicts:y}=_o(c.entries,f);l=y.filter(T=>f.has(T.path)).length,i=g,a=y.length;let x=Ya(c.entries);if(x>0&&console.log(V(`\u26A0 ${x} article(s) already exist on Document360 but aren't linked \u2014 left out (pushing would duplicate them). Run /catch-up or ask me to link them first.`)),y.length>0&&console.log(V(Us(y.length,l))),i.length===0&&y.length>0)return console.log(d("Resolve the conflicts: /resolve")),{kind:"continue"}}catch(c){return console.log(_(`Could not compute sync status: ${c.message}`)),console.log(d("Push a specific article: /push <article-path>")),{kind:"continue"}}if(i.length===0)return console.log(Q("\u2713 Nothing is ahead of Document360 \u2014 nothing to push.")),console.log(d(" (Articles without a sync base are unverified \u2014 push those by path if needed.)")),{kind:"continue"};if(!process.stdin.isTTY||!t){for(let c of i)console.log(` ${c.path} ${d(`(${c.label})`)}`);return console.log(d("Run: /push <article-path>")),{kind:"continue"}}try{s=await t.withPausedInput(()=>_S({message:"Push which article?",choices:[...i.length>1?[{name:`All ${i.length} candidates`,value:"--all",description:"one agent run over every candidate"}]:[],...i.map(c=>({name:c.path,value:c.path,description:c.label}))]}))}catch{return console.log(d("Cancelled.")),{kind:"continue"}}if(s==="--all")return await Jt(t,i.map(c=>c.path),a,{skipReview:o,skipStyle:r,unreadableSkipped:l})}return await Jt(t??{cwd:process.cwd()},[s],0,{skipReview:o,skipStyle:r,scope:s})}ie();import{select as iC}from"@inquirer/prompts";import{computeSyncStatus as aC,localAheadPaths as lC,pendingDrafts as cC,publishArticleLive as uC,publishGateVerdict as dC,publishGateLines as pC,blockedPaths as hC,placeholderRemedy as fC,resolveActiveProfile as eM,SKIP_STYLE_FLAG as mC,SKIP_SHOTS_FLAG as gC}from"document360-engine";import{gateFixTarget as oC}from"document360-engine";var pd=e=>"ruleId"in e,Za=e=>pd(e)?e.severity:"warning",zn=e=>e===1?"":"s";function Bs(e,t){let n=t??oC(e),o=n?` ${n}`:"";return e.unreviewed.length>0?`/review${o} --blocked --fix`:`/lint${o} --blocked --fix`}var dd=new Map;var rC=e=>pd(e)?e.fp||`${e.ruleId}:${e.path}:${e.line}:${e.term}`:`${e.kind}:${e.path}:${e.line}`;function Hs(e){let{cwd:t,after:n,scope:o,aborted:r,blocked:s}=e,i=n.filter(x=>Za(x)==="error").length,a=n.filter(x=>Za(x)==="warning").length,l=n.filter(x=>Za(x)==="suggestion").length,c=Se("/lint","--blocked --fix",o);if(r){let x=Se("/lint",s?"--blocked --fix":"--fix",o);return{text:`Stopped. Nothing else changed \u2014 ${x} picks up where this left off.`,cmd:x}}if(i>0){let x=n.filter(j=>Za(j)==="error"),T=new Set(x.map(j=>j.path)).size,C=`${t}::${o??""}`,P=x.map(rC).sort().join("|"),S=dd.get(C)===P;if(dd.set(C,P),S){let j=Se("/lint","--blocked --full",o),G=[...new Set(x.filter(pd).map(D=>D.ruleId))];return{text:`The same ${i} error${zn(i)} in ${T} article${zn(T)} survived two ${c} passes, so a third would change nothing \u2014 ${i===1?"it needs":"they need"} a person. ${j} shows ${i===1?"it":"them"} in place. If ${G.length===1?`${G[0]} is`:"a rule is"} wrong for your product, switch it off in .d360-writer/config.json under "styleGuide": { "rules": { "<rule>": "off" } }.`,cmd:j}}return{text:`${i} error${zn(i)} in ${T} article${zn(T)} still block${i===1?"s":""} publishing. ${c} has another go at ${i===1?"it":"just those"} \u2014 if a second pass leaves the same ones, they need a human eye.`,cmd:c}}dd.delete(`${t}::${o??""}`);let f=a>0&&l>0?`${a} warning${zn(a)} and ${l} suggestion${zn(l)} remain (neither blocks publishing). `:a>0?`${a} warning${zn(a)} remain${a===1?"s":""}, which ${a===1?"does":"do"} not block publishing. `:l>0?`${l} suggestion${zn(l)} remain${l===1?"s":""} \u2014 ${l===1?"it is":"they are"} judgment calls and ${l===1?"does":"do"} not block publishing. `:"",g=f===""?"\u2713 Nothing left to fix. ":`\u2713 ${f}`,y=o?"/push":"/push --all";return Ot(t,{text:`${g}Nothing is blocking${o?` in ${o}`:""} \u2014 ${y} saves the articles to Document360 as drafts.`,cmd:y},g,o)}var sC={check:"/check is now /lint (same command)."};function el(){return"That saves drafts rather than taking anything live, so running /push. (/publish takes saved drafts live.)"}var Dy=new Set;function Gs(e){let t=sC[e.replace(/^\//,"")];return!t||Dy.has(e)?null:(Dy.add(e),t)}var yC=150,wC=e=>e>0?new Promise(t=>setTimeout(t,e)):Promise.resolve(),fd=e=>e===1?"":"s",hd=e=>e.replace(/\\/g,"/").split("/").pop()??e;function md(e){return e.includes("--screenshots")||e.includes("--shots")||e[0]==="--related"}async function gd(e,t,n){await aC({cwd:e,profileName:t});let o=n?n.replace(/\\/g,"/").replace(/\/+$/,""):null;return cC(e,t).filter(r=>!o||r.path===o||r.path.startsWith(`${o}/`)).map(r=>({...r,label:bC(e,r.path)}))}function bC(e,t){let n=Pa(e,t)??Is(t),o=Ca(t),r=o?qn(o):"";return r?`${r} \u203A ${n}`:n}function kC(e){let t=Math.min(58,Math.max(...e.map(n=>n.label.length),20));return e.map(n=>` ${n.label.padEnd(t)} ${n.version?`draft v${n.version} \u2192 live`:"draft \u2192 live"}`)}function yd(e,t,n){let r=[`\u2713 Nothing is waiting to go live${n?` in ${n}`:""} \u2014 readers are seeing the latest version of every article.`],s=n?n.replace(/\\/g,"/").replace(/\/+$/,""):null,i=lC(e,t).filter(a=>!s||a===s||a.startsWith(`${s}/`)).length;return i>0&&r.push(`${i} article${fd(i)} ${i===1?"has":"have"} local edits that aren't on Document360 yet \u2014 that's /push${n?` ${n}`:""}.`),{lines:r,ahead:i}}function tl(e,t,n){let o=null;try{o=fC(e,t.placeholders)?.cmd??null}catch{}return{cmd:t.unreviewed.length>0||t.styleErrors.length>0?Bs(t,n):o,shotFix:o}}async function wd(e,t,n,o={}){let r={published:[],blocked:[],failed:[]},s=0;for(let i of n){o.onProgress?.(s,n.length,i.label);let a=await uC({articleId:i.articleId,cwd:e,profileName:t,allowUnreviewed:o.skipReview===!0,allowStyleErrors:o.skipStyle===!0,allowProduction:o.allowProduction===!0});a.ok?r.published.push({path:i.path,...a.url?{url:a.url}:{}}):a.blocked?r.blocked.push({path:i.path,message:a.message}):r.failed.push({path:i.path,message:a.error.message}),s+=1,s<n.length&&await wC(yC)}return r}function bd(e){let{published:t,blocked:n,failed:o}=e,r=[];if(t.length>0){r.push(`\u2713 ${t.length} article${fd(t.length)} ${t.length===1?"is":"are"} now live \u2014 readers see ${t.length===1?"it":"them"} now.`);let s=t.filter(i=>i.url);for(let i of s.slice(0,3))r.push(` \u2B95 ${i.url}`);s.length>3&&r.push(` \u2B95 \u2026and ${s.length-3} more`)}if(n.length>0){r.push(`\u2717 ${n.length} refused by the publish gates \u2014 still ${n.length===1?"a draft":"drafts"}, nothing was lost:`);for(let s of n.slice(0,3))r.push(` \xB7 ${hd(s.path)}`);n.length>3&&r.push(` \xB7 \u2026and ${n.length-3} more`),r.push(" /status names what is blocking each one, and the command that clears it. It costs nothing.")}if(o.length>0){r.push(`\u2717 ${o.length} failed \u2014 ${o.length===1?"it is":"they are"} still a draft, so re-running /publish retries ${o.length===1?"it":"them"}:`);for(let s of o.slice(0,3))r.push(` \xB7 ${hd(s.path)} \u2014 ${s.message}`);o.length>3&&r.push(` \xB7 \u2026and ${o.length-3} more`)}return r.length===0&&r.push("Nothing was published."),r}async function jy(e,t){let n=t?.cwd??process.cwd();if(md(e))return console.log(d(`\u2192 ${el()}`)),Ja(e,t);let o=Je(n,t?.profileName);if(!o.ok)return console.log(_(o.reason)),{kind:"continue"};let r=e.includes(Lt),s=e.includes(mC),i=e.includes(gC),a=e.includes("--all"),l=e.find(P=>!P.startsWith("--"));console.log(d("Checking what is waiting to go live\u2026"));let c;try{c=await gd(n,t?.profileName,l)}catch(P){return console.log(_(`Could not check Document360: ${P.message}`)),{kind:"continue"}}if(c.length===0){for(let P of yd(n,t?.profileName,l).lines)console.log(P.startsWith("\u2713")?Q(P):d(P));return{kind:"continue"}}let f=dC(n,c.map(P=>P.path),{allowUnreviewed:r,allowStyleErrors:s,checkScreenshots:!0,allowPlaceholders:i}),g=new Set(hC(f)),y=c.filter(P=>!g.has(P.path)),x=tl(n,f,l);if(g.size>0)for(let P of pC(f,c.length,{scope:l,act:"publish",going:y.length,shotFix:x.shotFix}))console.log(V(P));if(y.length===0)return x.cmd&&console.log(d(`Run: ${x.cmd}`)),{kind:"continue"};c=y,console.log(""),console.log(`${c.length} draft${fd(c.length)} ${c.length===1?"is":"are"} ahead of what readers see:`);for(let P of kC(c.slice(0,12)))console.log(d(P));c.length>12&&console.log(d(` \u2026and ${c.length-12} more`)),console.log("");let T=c;if(!a){let P=await iC({message:"Publish live to your readers?",choices:[{name:`Yes \u2014 publish all ${c.length} live`,value:"all"},...c.slice(0,8).map(S=>({name:`Just ${S.label}`,value:S.path})),{name:"No \u2014 leave them as drafts",value:"none"}]});if(P==="none")return console.log(d("Left as drafts. Nothing changed.")),{kind:"continue"};P!=="all"&&(T=c.filter(S=>S.path===P))}let C=await wd(n,t?.profileName,T,{skipReview:r,skipStyle:s,allowProduction:t?.allowProdWrites?.()===!0,onProgress:(P,S,j)=>console.log(d(` \u25B8 ${P+1}/${S} ${hd(j)}\u2026`))});console.log("");for(let P of bd(C))console.log(P.startsWith("\u2713")?Q(P):P.startsWith("\u2717")?V(P):d(P));return{kind:"continue"}}import{loadKnownConflicts as $C,localArticleTitle as vC,resolveActiveProfile as xC,surfaceCoverageSafe as Iy,readProjectConfig as Ly,recordDeclinedSurface as RC,areaExists as SC,declineTargets as Oy}from"document360-engine";function CC(e,t){let n=vC(e,t);return n||(t.split(/[\\/]/).pop()??t).replace(/\.md$/,"").replace(/^\d+[-_]/,"").replace(/[-_]+/g," ")}function nl(e,t){let n=[];try{n=$C(e,xC(e,t).name)}catch{return null}if(n.length===0)return null;let o=n.length,r=n.slice(0,3).map(i=>CC(e,i)),s=o>3?`${r.join(", ")} and ${o-3} more`:r.join(", ");return{paths:n,note:`\u26A0 ${o} article${o===1?"":"s"} ${o===1?"has":"have"} been changed both here and on Document360 (${s}), so your local copy is not a reliable picture of what your readers have. An audit over it could tell you to write an article that already exists, or miss one that needs your decision \u2014 so it needs ${o===1?"that":"those"} settled first.`}}var Or={cmd:"/resolve",reason:"it walks each one with you \u2014 keep yours, take the portal's, or skip"};function ol(e,t){let n=t.indexOf("--decline");if(n<0)return null;let o=t[n+1];if(!o||o.startsWith("--")){let a=Iy(e,Ly(e)),l=a.recognised?a.areas.slice(0,5).flatMap(c=>Oy(c).map(f=>` /audit --decline ${f}`)):[];return["Name the path to decline \u2014 a folder, or a single file.",...l.length?["","No article covers these right now. Run one of these to settle it for good:",...l]:["","For example:"," /audit --decline src/internal-admin"]]}let r=o.replace(/\\/g,"/").replace(/\/+$/,"");if(!SC(e,r))return[`There is nothing at ${r}, so there is nothing to decline. Copy the path from /eval or /audit.`];let s=t.slice(n+2).filter(a=>!a.startsWith("--")).join(" ")||void 0;return RC(e,r,s)?[`\u2713 ${r} will not be reported as needing an article. /audit and /eval leave it out from now on, and say how many you have declined.`]:[`Could not write .d360-writer/declined-surfaces.json, so ${r} was not declined.`]}async function qs(e){if(e?.cwd){let r=nl(e.cwd,e.profileName);if(r)return{kind:"audit-blocked",note:r.note,lead:Or}}let t=e?.cwd?Iy(e.cwd,Ly(e.cwd)):null,n=[];if(t?.recognised&&t.areas.length>0){let r=t.areas.slice(0,15);n.push("","2.7. These parts of the product are cited by NO article's `sources:` frontmatter. This list is DETERMINISTIC and complete as of now \u2014 it does not depend on the diff window above, so treat it as authoritative rather than re-deriving it:",...r.map(s=>` - ${s.folder} (${s.files} of ${s.total} file${s.total===1?"":"s"} uncited, e.g. ${s.sample.join(", ")}) \u2014 to decline: ${Oy(s).map(i=>`/audit --decline ${i}`).join(" \xB7 ")}`),...t.areas.length>r.length?[` \u2026and ${t.areas.length-r.length} more areas.`]:[]," EVERY row above must leave this run accounted for: a `create` row, or a stated reason it needs no article (internal-only, unreachable by a reader, already covered by an existing article's prose). Do NOT silently drop one \u2014 if you list fewer paths than this section handed you, say which you left out and why."," A `decline` verdict is not a record. For EVERY row you decline, print its exact /audit --decline line (copied from above \u2014 it already picks folder-or-file correctly) in the closing block, each on its own line so the terminal makes it a button. A judgement that leaves the turn as prose changes nothing and will be re-derived, at this price, on every future audit.",...t.declined>0?[` (${t.declined} more ${t.declined===1?"is":"are"} already declined and left out of that list.)`]:[])}return{kind:"forward-to-agent",prompt:["Run the gap-analysis skill against this repo.","","Follow its stages in order:","1. d360_sync_status first; stop if any article is changed in both places, or changed on Document360 and not yet pulled down (the local copy cannot be trusted until that is resolved). Say which articles, in plain words, never the internal status word \u2014 and tell the user to run /resolve, which walks the affected articles one at a time (keep the local copy, take the portal's, or skip). Do NOT ask them to report back to you: they will run /resolve and then /audit again.","2. git diff since the lastAnalyzedCommit marker in d360-category-map.json (or last 30 days if no marker), filtered to the authoritativeSourceFiles paths.","2.5. d360_pending_hints: the source agent's own notes on what it shipped. Verify each against the CURRENT code (its `sources` exist, and the code as it stands shows the surface it claims) before trusting it; never judge it by its `branch` or `commit`. Fold verified ones into the table, flag the rest.","2.6. Anything d360_pending_hints returns under `held` is NOT a proposal. Its cited code has uncommitted changes, so the builder is still writing that surface and has already been asked whether it is done. List each held item at the END of the table as a `waiting` row (Reason: the code behind it is not finished), do NOT propose create/update/retire for it, and do NOT stamp it consumed.",...n,"3. Map changes (diff + hints) to articles via their `sources:` frontmatter; read only implicated articles and changed source files. If articles are missing `sources:`, this is the one-time bootstrap pass, so backfill them.","4. Output the proposal table: | Article (path or proposed title) | Action (create | update | retire | link) | Reason | Evidence | Scope |. Leave every hint OPEN \u2014 this run proposes, it does not write articles, so nothing has been acted on yet.","5. End with the skill's closing block, and put NOTHING below it \u2014 no receipts, no roadmap confirmations, no feedback marker line, no summary. It ends in a question, and a question the user has to scroll back to find is one you did not really ask.","","Do not start writing or updating any article yet; the proposal table is the deliverable.","The audited-up-to marker is recorded for you when this command finishes. Read it to size the diff window; never write it, and never report it as a missing capability."].join(`
|
|
339
|
+
`):null}async function by(e,t){let n=t.cwd;if(e[0]?.toLowerCase()==="done"){let o=Ir(n,e.slice(1).join(" ").trim());return console.log(o.ok?Q(o.line):V(o.line)),{kind:"continue"}}for(let o of od(n,{all:e.some(r=>r.toLowerCase()==="--all")}))console.log(o);return{kind:"continue"}}var sd={"local-ahead":"modified locally","untracked-local":"new (never published)",conflict:"\u26A0 conflict \u2014 pushing overwrites the portal edit"},JS="\u26A0 held on Document360 in another editor \u2014 /resolve settles it";function Uo(e){return e.filter(t=>t.path!==null&&t.status in sd&&!t.link).map(t=>({path:t.path,label:sd[t.status]}))}function _o(e,t=[]){let n=Uo(e),o=new Set(t),r=new Set([...e.filter(s=>s.status==="conflict"&&s.path).map(s=>s.path),...o]);return{clean:n.filter(s=>!r.has(s.path)),conflicts:n.filter(s=>r.has(s.path)).map(s=>o.has(s.path)&&s.label!==sd.conflict?{...s,label:JS}:s)}}function Mo(e,t){try{return WS(e,Ka(e,t).name).map(n=>n.path)}catch{return[]}}function Us(e,t=0){let n=e!==1;if(t>=e)return`\u26A0 ${e} article${n?"s":""} NOT pushed (nothing on the portal overwritten) \u2014 Document360 holds ${n?"their bodies":"its body"} in another editor, so ${n?"they cannot be":"it cannot be"} read back. Settle ${n?"them":"it"}: /resolve (or ask your Document360 admin to move ${n?"them":"it"} to the Markdown editor).`;let o=t>0?` (${t} of ${n?"them are":"these is"} held in another editor and cannot be read back)`:"";return`\u26A0 ${e} conflict${n?"s":""} NOT pushed (portal edit${n?"s":""} kept safe) \u2014 ${n?"these were":"it was"} edited both here and on the portal, so ${n?"they are":"it is"} left untouched.${o} Settle ${n?"them":"it"}: /resolve (or /push <path> to keep yours, /pull <path> to take the portal's).`}function id(e,t){let n=t.scope?.replace(/\\/g,"/").replace(/\/+$/,"");return!n||!n.endsWith(".md")||!xy(e,t.profileName).map(r=>r.replace(/\\/g,"/")).some(r=>r===n||r.endsWith(`/${n}`)||n.endsWith(`/${r}`))?null:{cmd:Se("/publish","",n),reason:"it takes this draft live \u2014 until it does, your readers still see the previous version"}}function Lr(e,t){let n=xy(e,t),o=n.length;if(o===0)return null;let r=o===1?"":"s",s=0;try{s=FS(e,n).length}catch{}if(s>0){let i=o-s,a=`${s===o?o===1?"It is":"All of them are":`${s} of them ${s===1?"is":"are"}`} waiting on a missing picture`;return`\u2139 ${o} article${r} ${o===1?"is":"are"} saved on Document360 as a draft and not live yet \u2014 readers still see the previous version. ${a}, so /publish holds ${s===1?"it":"them"} back${i>0?` and takes the other ${i} live once ${i===1?"it has":"they have"} been signed off.`:"."}`}return`\u2139 ${o} article${r} ${o===1?"is":"are"} saved on Document360 as a draft and not live yet \u2014 readers still see the previous version. /publish takes ${o===1?"it":"them"} live once ${o===1?"it has":"they have"} been signed off.`}function Ya(e){return e.filter(t=>t.link).length}function ad(e,t){try{let n=vy(e,Ka(e,t).name);if(!n)return null;let o=0;for(let r of Object.keys(n.articles))try{o+=QS(LS(OS(e,r),"utf8"),r,n).unresolved}catch{}return o===0?null:`\u2139 ${o} cross-link${o===1?"":"s"} point to pages that aren't on Document360 yet \u2014 they show as plain text for now and turn into links once those pages are pushed.`}catch{return null}}function Ey(e,t=[]){let n=[];e.length>1&&n.push({kind:"all",label:`Push all ${e.length} articles`,paths:e.map(r=>r.path),indent:!1});let o=new Map;for(let r of e){let s=r.path.replace(/\\/g,"/").split("/").slice(0,-1).join("/"),i=o.get(s);i?i.push(r):o.set(s,[r])}for(let r of[...o.keys()].sort()){let s=o.get(r).sort((i,a)=>i.path.localeCompare(a.path));n.push({kind:"category",label:`${qn(r)} (${s.length})`,paths:s.map(i=>i.path),indent:!1});for(let i of s){let a=i.path.replace(/\\/g,"/").split("/").pop()??i.path;n.push({kind:"article",label:a,paths:[i.path],indent:!0,note:i.label})}}for(let r of[...t].sort((s,i)=>s.path.localeCompare(i.path))){let s=r.path.replace(/\\/g,"/").split("/").pop()??r.path;n.push({kind:"article",label:s,paths:[r.path],indent:!1,note:r.label,conflict:!0})}return n}async function Fs(e,t){try{let n=Ka(e,t),o=n.project.projectId,r=n.project.workspaceId;if(!o||!r)return null;let s=await XS({profile:n.name,connection:n.connection,projectId:o,projectName:n.project.projectName},{cwd:e,profile:n.name,projectPath:`/v3/projects/${o}`,workspaceId:r,verifyHidden:!1});if(s.kind!=="created")return null;if(s.hidden!=="confirmed"){let i=s.seeded.length===0?" Its internal articles were NOT created, because that would put docs-agent scaffolding in front of your readers.":"";return`\u26A0 Created the "${ky}" folder on Document360 for reviewer feedback, but could NOT confirm it is hidden from readers.${i} Open its category settings in the portal and turn on "Hide from readers", then run /catch-up.`}return`\u2139 Created a hidden "${ky}" folder on Document360 \u2014 it's where your reviewers leave feedback for d360-writer to action (confirmed hidden from readers; its README explains how). /catch-up picks up what they write.`}catch{return null}}var Lt="--skip-review";function Ws(e,t){return BS(e,t)}function za(e,t){try{return Ws(e,Sy(e,t)).length}catch{return 0}}function Ot(e,t,n="",o,r){let s=za(e,o),i=Xa(e,o),a=o?` ${o}`:"",l=o?"/push":"/push --all",c=ly(e,{just:r}),f=!!o&&o.endsWith(".md"),g=c&&f?` (${c.short} \u2014 ${c.cmd} once this page is delivered.)`:"";if(c&&!f){let T=s>0?` (${s} article${s===1?"":"s"}${o&&!o.endsWith(".md")?` in ${o}`:""} ${s===1?"carries":"carry"} text no editor has read, so ${s===1?"it cannot":"they cannot"} be sent up until /review${a} --blocked --fix runs \u2014 that can wait until the writing is done.)`:i>0?` (${i} article${i===1?"":"s"} break${i===1?"s":""} house style rules that block sending up \u2014 /lint${a} --blocked --fix clears that whenever you want.)`:"";return{text:`${n}${c.text}${T}`,cmd:c.cmd}}if(s===0&&i===0)return g?{...t,text:`${t.text}${g}`}:t;let y=o&&!o.endsWith(".md")?` in ${o}`:"";if(s>0){let T=s===1?"":"s",C=i>0?` ${i} also break house style rules \u2014 /lint${a} --blocked --fix clears those afterwards.`:"",P=`/review${a} --blocked --fix`;return{text:`${n}${s} article${T}${y} ${s===1?"carries":"carry"} text no editor has read, so pushing ${s===1?"it is":"them is"} blocked. ${P} reads ${s===1?"it":"just those"} and applies its fixes.${C} Then ${l}.${g}`,cmd:P}}return{text:`${n}${i} article${i===1?"":"s"}${y} break${i===1?"s":""} house style rules that block publishing. /lint${a} --blocked --fix corrects them in place \u2014 finding them costs nothing \u2014 then ${l}.${g}`,cmd:`/lint${a} --blocked --fix`}}function Xa(e,t){try{return Ry(e,Sy(e,t)).length}catch{return 0}}function Ny(e,t){return Ry(e,t)}function ld(e,t,n){try{let o=vy(e,Ka(e,n).name);if(!o)return 0;let r=i=>i.replace(/\\/g,"/"),s=new Set(Object.keys(o.articles??{}).map(r));return t.filter(i=>s.has(r(i))).length}catch{return 0}}function cd(e,t){let n=`/push${t.scope?` ${t.scope}`:" --all"}`,o=l=>`${l} article${l===1?"":"s"}`,r=[],s=e.pushed.length,i=t.elapsedMs!==void 0?` \xB7 ${tC(t.elapsedMs)}`:"",a=e.screenshotsUploaded>0?` \xB7 ${e.screenshotsUploaded} screenshot${e.screenshotsUploaded===1?"":"s"} uploaded`:"";s===0&&e.failed.length===0?r.push("Nothing was pushed \u2014 no articles were sent."):t.recorded<s?(r.push(`Pushed ${t.recorded}/${o(s)}${a}${i}.`),r.push(`${s-t.recorded} article${s-t.recorded===1?"":"s"} reported success but ${s-t.recorded===1?"is":"are"} not recorded in the category map \u2014 re-run ${n}.`)):(r.push(`Pushed ${o(s)}${e.created>0?` (${e.created} new)`:""}${a}${i}.`),r.push(`Saved on Document360 as draft${s===1?"":"s"} \u2014 readers don't see ${s===1?"it":"them"} until /publish takes ${s===1?"it":"them"} live.`)),e.stopped?.reason==="unavailable"&&r.push(`Stopped: ${e.stopped.streak} articles in a row could not reach Document360${e.notRun.length>0?`, so the remaining ${e.notRun.length} ${e.notRun.length===1?"was":"were"} not attempted`:""}.`);for(let l of e.duplicateCategories??[])r.push(`\u26A0 Document360 now holds ${l.ids.length} categories called "${l.name}" \u2014 your articles are split between them.`);if((e.duplicateCategories??[]).length>0&&r.push(" Nothing here deletes a category. Remove the extra ones in the portal, then run /push --all again."),e.stopped?.reason==="identity"&&r.push(`Nothing was pushed. ${e.stopped.error}`),e.failed.length>0){let l=e.failed.filter(g=>g.refused),c=eC(e.failed,t.docsRoot),f=e.failed.length;if(c.length===1&&f>1)r.push(`${o(f)} could not be pushed \u2014 all with the same error: ${c[0].error}`),r.push(...$y(c[0].paths," \u2717 "));else{r.push(`${o(f)} could not be pushed:`);for(let g of c)g.paths.length===1?r.push(` \u2717 ${g.paths[0]} \u2014 ${g.error}`):(r.push(` \u2717 ${o(g.paths.length)} \u2014 ${g.error}`),r.push(...$y(g.paths," ")))}l.length<e.failed.length&&(e.stopped?r.push(`Nothing was lost \u2014 your articles are unchanged. Re-run ${n} when Document360 is back.`):r.push(`Re-run ${n} to retry the rest \u2014 articles already saved are sent again only if they changed.`))}if(e.notRun.length>0&&!e.stopped&&r.push(`\u26A0 ${o(e.notRun.length)} never started (the run was stopped): ${ZS(e.notRun)}`),e.missingShots.length>0){let l=e.missingShots.flatMap(c=>c.ids);r.push(`\u26A0 ${l.length} screenshot${l.length===1?"":"s"} in ${o(e.missingShots.length)} ${l.length===1?"has":"have"} not been taken, so the description was published instead: /screenshot capture`)}if(e.categoryLookupFailed.length>0){let l=e.categoryLookupFailed.length;r.push(`\u26A0 Could not check Document360 for existing categories, so ${l===1?"a new one was":`${l} new ones were`} created. There may now be two with the same name: /doctor lists them.`),r.push(` Reason: ${e.categoryLookupFailed[0].reason}`)}return t.conflictsSkipped&&t.conflictsSkipped>0&&r.push(Us(t.conflictsSkipped,t.unreadableSkipped??0)),r}var ZS=(e,t=5)=>`${e.slice(0,t).join(", ")}${e.length>t?`, +${e.length-t} more`:""}`,rd=5;function $y(e,t){let n=e.slice(0,rd).map(o=>`${t}${o}`);return e.length>rd&&n.push(`${" ".repeat(t.length)}\u2026 +${e.length-rd} more`),n}function eC(e,t){let n=t?`${t.replace(/\\/g,"/").replace(/\/+$/,"")}/`:null,o=s=>{let i=s.replace(/\\/g,"/");return n&&i.startsWith(n)?i.slice(n.length):i},r=new Map;for(let s of e){let i=r.get(s.error);i?i.push(o(s.path)):r.set(s.error,[o(s.path)])}return[...r.entries()].map(([s,i])=>({error:s,paths:i})).sort((s,i)=>i.paths.length-s.paths.length)}function tC(e){let t=Math.round(e/1e3);return t<60?`${t}s`:`${Math.floor(t/60)}m ${String(t%60).padStart(2,"0")}s`}async function Jt(e,t,n=0,o){let r=HS(e.cwd,t,{allowUnreviewed:o?.skipReview===!0,allowStyleErrors:o?.skipStyle===!0});if(!r.clear){for(let c of GS(r,t.length,{act:"push"}))console.log(V(c));return{kind:"continue"}}let s={aborted:!1,partitions:0,failed:0,notRun:0,producedNothing:!0},i={retryable:0,stopped:!1,paths:[]},a=async(c,f)=>{let g=Date.now();console.log(d(f?`Resolving cross-links in ${c.length} article${c.length===1?"":"s"} whose targets published this run (second pass)\u2026`:`Pushing ${c.length} article${c.length===1?"":"s"} to Document360 (${YS} at a time)\u2026`));let y=await qS({cwd:e.cwd,...e.profileName?{profileName:e.profileName}:{},paths:c,...o?.allowUnpullable===!0?{allowUnpullable:!0}:{},onEvent:G=>{G.type==="article_done"?console.log(Q(` \u2713 ${G.result.path}${G.result.action==="created"?" (new)":""}`)):G.type==="article_failed"&&console.log(_(` \u2717 ${G.failure.path}`))}});console.log("");let x=ld(e.cwd,c,e.profileName);f||(i={retryable:y.failed.filter(G=>!G.refused).length,stopped:!!y.stopped,paths:y.failed.map(G=>G.path)},s={aborted:y.aborted||!!y.stopped,partitions:MS(c).length,failed:y.failed.length,notRun:y.notRun.length,producedNothing:y.pushed.length===0,verifiedComplete:x>=c.length,leftBehind:n});let T=y.pushed.length>0;for(let G of cd(y,{recorded:x,elapsedMs:Date.now()-g,docsRoot:zS(e.cwd),...f?{}:{conflictsSkipped:n,unreadableSkipped:o?.unreadableSkipped??0},...o?.scope?{scope:o.scope}:{}}))console.log(T&&y.failed.length===0?Q(G):V(G));let C=y.aborted||!!y.stopped;if(!T)return C;let P=ad(e.cwd,e.profileName);P&&console.log(d(P));let S=Lr(e.cwd,e.profileName);if(S&&console.log(d(S)),C)return C;try{let G=Py(await Cy({cwd:e.cwd,profileName:e.profileName,onRateLimit:D=>console.log(d(` Document360 rate limit \u2014 waiting ${Math.ceil(D.waitMs/1e3)}s\u2026`))}));G&&console.log(G.startsWith("\u26A0")?V(G):d(G))}catch{}try{let G=Ay(await Ty({cwd:e.cwd,profileName:e.profileName}));G&&console.log(d(G))}catch{}let j=await Fs(e.cwd,e.profileName);return j&&console.log(d(j)),C};try{if(!await a(t,!1)){let f=await ud(e.cwd,e.profileName,t,i.paths);if(f.length>0&&await a(f,!0),Qt(s))for(let g of Ye(e.cwd,o?.scope?`/push ${o.scope}`:"/push --all"))console.log(Q(g))}}catch(c){console.log(_(`Push run failed: ${c.message}`))}if(console.log(""),i.retryable>0){let c=Se("/push",o?.scope?"":"--all",o?.scope);return console.log(d(i.stopped?`Next: ${c} \u2014 your articles are unchanged, so it picks up where the run left off.`:`Next: ${c} \u2014 retries the ${i.retryable} article${i.retryable===1?"":"s"} that could not be saved.`)),{kind:"continue",ownNext:!0}}let l=id(e.cwd,{scope:o?.scope,profileName:e.profileName});return l?(console.log(d(`Next: ${l.cmd} \u2014 ${l.reason}.`)),{kind:"continue",ownNext:!0}):{kind:"continue"}}async function Qa(e,t,n){let o=US(e,n);if(o.length===0)return[];let r=new Set(Uo((await Va({cwd:e,profileName:t})).entries).map(s=>s.path));return o.filter(s=>r.has(s))}async function ud(e,t,n,o=[]){try{let r=new Set(n),s=new Set(o),i=await Va({cwd:e,profileName:t});return Uo(i.entries).map(a=>a.path).filter(a=>r.has(a)&&!s.has(a))}catch{return[]}}async function nC(e,t,n){try{let o=await VS({cwd:e,...t?{profileName:t}:{},relPath:n.replace(/\\/g,"/"),preview:!0});console.log(""),console.log(Q(`${o.title}`)),console.log(d(`${o.path} \xB7 ${o.articleId?`updates article ${o.articleId}`:"would be created as a new article"}`)),o.shots.length>0&&console.log(d(`${o.shots.length} screenshot${o.shots.length===1?"":"s"} embedded: ${o.shots.map(r=>r.id).join(", ")}`)),o.missingShots.length>0&&console.log(V(`\u26A0 ${o.missingShots.length} screenshot${o.missingShots.length===1?"":"s"} not taken yet \u2014 the description publishes instead: ${o.missingShots.join(", ")}`)),console.log(d("\u2500".repeat(Math.min(80,Math.max(20,(process.stdout.columns??80)-1))))),console.log(o.body),console.log(d("\u2500".repeat(Math.min(80,Math.max(20,(process.stdout.columns??80)-1))))),console.log(d(`Nothing was sent. Run /push ${o.path} to save it as a draft.`))}catch(o){console.log(_(o.message))}return{kind:"continue"}}async function Ja(e,t){let n=Je(t?.cwd??process.cwd(),t?.profileName);if(!n.ok)return console.log(_(n.reason)),{kind:"continue"};let o=e.includes(Lt),r=e.includes(KS);if(e.includes("--preview")){let i=e.find(a=>!a.startsWith("--"));return i?await nC(t?.cwd??process.cwd(),t?.profileName,i):(console.log(_("Which article? Use /push --preview <path> \u2014 a preview is per article.")),{kind:"continue"})}let s=e[0];if(e.includes("--screenshots")||e.includes("--shots")){let i=t?.cwd??process.cwd(),a=e.find(x=>!x.startsWith("--")),l=He(i),c=_a(l,a);if(c.length===0){let x=l.filter(T=>T.state==="captured").length;return console.log(d(x>0?`${x} screenshot${x===1?" has":"s have"} been taken but the callouts aren't drawn on yet, so there is nothing to embed \u2014 /screenshot annotate finishes ${x===1?"it":"them"}.`:a?`No captured screenshots in ${a} \u2014 /push --screenshots covers the whole knowledge base.`:"No captured screenshots yet \u2014 /screenshot capture first.")),{kind:"continue"}}let f=Ma(l,a);f&&console.log(f);let g=Ua(l);g&&console.log(g);let y=Se("","--screenshots",a).trim();return await Jt(t??{cwd:process.cwd()},c,0,{skipReview:o,skipStyle:r,scope:y})}if(s==="--related"&&e[1]){if(!t)return{kind:"continue"};let i=await Qa(t.cwd,t.profileName,e[1].replace(/\\/g,"/"));return i.length===0?(console.log(Q("\u2713 All related articles are already on Document360.")),{kind:"continue"}):await Jt(t,i,0,{skipReview:o,skipStyle:r,scope:`--related ${e[1]}`})}if(s==="--all"){console.log(d("Checking what needs pushing\u2026"));try{let i=await Va({cwd:t?.cwd??process.cwd()}),a=new Set(Mo(t?.cwd??process.cwd(),t?.profileName)),{clean:l,conflicts:c}=_o(i.entries,a),f=c.filter(C=>a.has(C.path)).length,g=Ya(i.entries),y=e.includes("--force"),x=y&&c.length>0;g>0&&console.log(V(`\u26A0 ${g} article(s) already exist on Document360 but aren't linked \u2014 left out (pushing would duplicate them). Run /catch-up or ask me to link them first.`)),c.length>0&&!x&&console.log(V(Us(c.length,f)));let T=x?[...l,...c]:l;if(T.length===0){if(c.length>0){let P=Lr(t?.cwd??process.cwd(),t?.profileName);return P&&console.log(d(P)),console.log(d("Resolve the conflicts: /resolve")),{kind:"continue"}}console.log(Q(g>0?"\u2713 Nothing else is ahead of Document360 to push.":"\u2713 Nothing is ahead of Document360 \u2014 nothing to push."));let C=Lr(t?.cwd??process.cwd(),t?.profileName);if(C&&console.log(d(C)),t)try{let P=Py(await Cy({cwd:t.cwd,profileName:t.profileName,onRateLimit:j=>console.log(d(` Document360 rate limit \u2014 waiting ${Math.ceil(j.waitMs/1e3)}s\u2026`))}));P&&console.log(P.startsWith("\u26A0")?V(P):d(P));let S=Ay(await Ty({cwd:t.cwd,profileName:t.profileName}));S&&console.log(d(S))}catch{}return{kind:"continue"}}return await Jt(t??{cwd:process.cwd()},T.map(C=>C.path),x?0:c.length,{skipReview:o,skipStyle:r,allowUnpullable:y,unreadableSkipped:x?0:f})}catch(i){return console.log(_(`Could not compute sync status: ${i.message}`)),{kind:"continue"}}}if(s&&s.startsWith("--")&&(s=e.find(i=>!i.startsWith("--"))??""),!s){console.log(d("Checking what needs pushing\u2026"));let i,a=0,l=0;try{let c=await Va({cwd:t?.cwd??process.cwd()}),f=new Set(Mo(t?.cwd??process.cwd(),t?.profileName)),{clean:g,conflicts:y}=_o(c.entries,f);l=y.filter(T=>f.has(T.path)).length,i=g,a=y.length;let x=Ya(c.entries);if(x>0&&console.log(V(`\u26A0 ${x} article(s) already exist on Document360 but aren't linked \u2014 left out (pushing would duplicate them). Run /catch-up or ask me to link them first.`)),y.length>0&&console.log(V(Us(y.length,l))),i.length===0&&y.length>0)return console.log(d("Resolve the conflicts: /resolve")),{kind:"continue"}}catch(c){return console.log(_(`Could not compute sync status: ${c.message}`)),console.log(d("Push a specific article: /push <article-path>")),{kind:"continue"}}if(i.length===0)return console.log(Q("\u2713 Nothing is ahead of Document360 \u2014 nothing to push.")),console.log(d(" (Articles without a sync base are unverified \u2014 push those by path if needed.)")),{kind:"continue"};if(!process.stdin.isTTY||!t){for(let c of i)console.log(` ${c.path} ${d(`(${c.label})`)}`);return console.log(d("Run: /push <article-path>")),{kind:"continue"}}try{s=await t.withPausedInput(()=>_S({message:"Push which article?",choices:[...i.length>1?[{name:`All ${i.length} candidates`,value:"--all",description:"one agent run over every candidate"}]:[],...i.map(c=>({name:c.path,value:c.path,description:c.label}))]}))}catch{return console.log(d("Cancelled.")),{kind:"continue"}}if(s==="--all")return await Jt(t,i.map(c=>c.path),a,{skipReview:o,skipStyle:r,unreadableSkipped:l})}return await Jt(t??{cwd:process.cwd()},[s],0,{skipReview:o,skipStyle:r,scope:s})}ie();import{select as iC}from"@inquirer/prompts";import{computeSyncStatus as aC,localAheadPaths as lC,pendingDrafts as cC,publishArticleLive as uC,publishGateVerdict as dC,publishGateLines as pC,blockedPaths as hC,placeholderRemedy as fC,resolveActiveProfile as eM,SKIP_STYLE_FLAG as mC,SKIP_SHOTS_FLAG as gC}from"document360-engine";import{gateFixTarget as oC}from"document360-engine";var pd=e=>"ruleId"in e,Za=e=>pd(e)?e.severity:"warning",zn=e=>e===1?"":"s";function Bs(e,t){let n=t??oC(e),o=n?` ${n}`:"";return e.unreviewed.length>0?`/review${o} --blocked --fix`:`/lint${o} --blocked --fix`}var dd=new Map;var rC=e=>pd(e)?e.fp||`${e.ruleId}:${e.path}:${e.line}:${e.term}`:`${e.kind}:${e.path}:${e.line}`;function Hs(e){let{cwd:t,after:n,scope:o,aborted:r,blocked:s}=e,i=n.filter(x=>Za(x)==="error").length,a=n.filter(x=>Za(x)==="warning").length,l=n.filter(x=>Za(x)==="suggestion").length,c=Se("/lint","--blocked --fix",o);if(r){let x=Se("/lint",s?"--blocked --fix":"--fix",o);return{text:`Stopped. Nothing else changed \u2014 ${x} picks up where this left off.`,cmd:x}}if(i>0){let x=n.filter(j=>Za(j)==="error"),T=new Set(x.map(j=>j.path)).size,C=`${t}::${o??""}`,P=x.map(rC).sort().join("|"),S=dd.get(C)===P;if(dd.set(C,P),S){let j=Se("/lint","--blocked --full",o),G=[...new Set(x.filter(pd).map(D=>D.ruleId))];return{text:`The same ${i} error${zn(i)} in ${T} article${zn(T)} survived two ${c} passes, so a third would change nothing \u2014 ${i===1?"it needs":"they need"} a person. ${j} shows ${i===1?"it":"them"} in place. If ${G.length===1?`${G[0]} is`:"a rule is"} wrong for your product, switch it off in .d360-writer/config.json under "styleGuide": { "rules": { "<rule>": "off" } }.`,cmd:j}}return{text:`${i} error${zn(i)} in ${T} article${zn(T)} still block${i===1?"s":""} publishing. ${c} has another go at ${i===1?"it":"just those"} \u2014 if a second pass leaves the same ones, they need a human eye.`,cmd:c}}dd.delete(`${t}::${o??""}`);let f=a>0&&l>0?`${a} warning${zn(a)} and ${l} suggestion${zn(l)} remain (neither blocks publishing). `:a>0?`${a} warning${zn(a)} remain${a===1?"s":""}, which ${a===1?"does":"do"} not block publishing. `:l>0?`${l} suggestion${zn(l)} remain${l===1?"s":""} \u2014 ${l===1?"it is":"they are"} judgment calls and ${l===1?"does":"do"} not block publishing. `:"",g=f===""?"\u2713 Nothing left to fix. ":`\u2713 ${f}`,y=o?"/push":"/push --all";return Ot(t,{text:`${g}Nothing is blocking${o?` in ${o}`:""} \u2014 ${y} saves the articles to Document360 as drafts.`,cmd:y},g,o)}var sC={check:"/check is now /lint (same command)."};function el(){return"That saves drafts rather than taking anything live, so running /push. (/publish takes saved drafts live.)"}var Dy=new Set;function Gs(e){let t=sC[e.replace(/^\//,"")];return!t||Dy.has(e)?null:(Dy.add(e),t)}var yC=150,wC=e=>e>0?new Promise(t=>setTimeout(t,e)):Promise.resolve(),fd=e=>e===1?"":"s",hd=e=>e.replace(/\\/g,"/").split("/").pop()??e;function md(e){return e.includes("--screenshots")||e.includes("--shots")||e[0]==="--related"}async function gd(e,t,n){await aC({cwd:e,profileName:t});let o=n?n.replace(/\\/g,"/").replace(/\/+$/,""):null;return cC(e,t).filter(r=>!o||r.path===o||r.path.startsWith(`${o}/`)).map(r=>({...r,label:bC(e,r.path)}))}function bC(e,t){let n=Pa(e,t)??Is(t),o=Ca(t),r=o?qn(o):"";return r?`${r} \u203A ${n}`:n}function kC(e){let t=Math.min(58,Math.max(...e.map(n=>n.label.length),20));return e.map(n=>` ${n.label.padEnd(t)} ${n.version?`draft v${n.version} \u2192 live`:"draft \u2192 live"}`)}function yd(e,t,n){let r=[`\u2713 Nothing is waiting to go live${n?` in ${n}`:""} \u2014 readers are seeing the latest version of every article.`],s=n?n.replace(/\\/g,"/").replace(/\/+$/,""):null,i=lC(e,t).filter(a=>!s||a===s||a.startsWith(`${s}/`)).length;return i>0&&r.push(`${i} article${fd(i)} ${i===1?"has":"have"} local edits that aren't on Document360 yet \u2014 that's /push${n?` ${n}`:""}.`),{lines:r,ahead:i}}function tl(e,t,n){let o=null;try{o=fC(e,t.placeholders)?.cmd??null}catch{}return{cmd:t.unreviewed.length>0||t.styleErrors.length>0?Bs(t,n):o,shotFix:o}}async function wd(e,t,n,o={}){let r={published:[],blocked:[],failed:[]},s=0;for(let i of n){o.onProgress?.(s,n.length,i.label);let a=await uC({articleId:i.articleId,cwd:e,profileName:t,allowUnreviewed:o.skipReview===!0,allowStyleErrors:o.skipStyle===!0,allowProduction:o.allowProduction===!0});a.ok?r.published.push({path:i.path,...a.url?{url:a.url}:{}}):a.blocked?r.blocked.push({path:i.path,message:a.message}):r.failed.push({path:i.path,message:a.error.message}),s+=1,s<n.length&&await wC(yC)}return r}function bd(e){let{published:t,blocked:n,failed:o}=e,r=[];if(t.length>0){r.push(`\u2713 ${t.length} article${fd(t.length)} ${t.length===1?"is":"are"} now live \u2014 readers see ${t.length===1?"it":"them"} now.`);let s=t.filter(i=>i.url);for(let i of s.slice(0,3))r.push(` \u2B95 ${i.url}`);s.length>3&&r.push(` \u2B95 \u2026and ${s.length-3} more`)}if(n.length>0){r.push(`\u2717 ${n.length} refused by the publish gates \u2014 still ${n.length===1?"a draft":"drafts"}, nothing was lost:`);for(let s of n.slice(0,3))r.push(` \xB7 ${hd(s.path)}`);n.length>3&&r.push(` \xB7 \u2026and ${n.length-3} more`),r.push(" /status names what is blocking each one, and the command that clears it. It costs nothing.")}if(o.length>0){r.push(`\u2717 ${o.length} failed \u2014 ${o.length===1?"it is":"they are"} still a draft, so re-running /publish retries ${o.length===1?"it":"them"}:`);for(let s of o.slice(0,3))r.push(` \xB7 ${hd(s.path)} \u2014 ${s.message}`);o.length>3&&r.push(` \xB7 \u2026and ${o.length-3} more`)}return r.length===0&&r.push("Nothing was published."),r}async function jy(e,t){let n=t?.cwd??process.cwd();if(md(e))return console.log(d(`\u2192 ${el()}`)),Ja(e,t);let o=Je(n,t?.profileName);if(!o.ok)return console.log(_(o.reason)),{kind:"continue"};let r=e.includes(Lt),s=e.includes(mC),i=e.includes(gC),a=e.includes("--all"),l=e.find(P=>!P.startsWith("--"));console.log(d("Checking what is waiting to go live\u2026"));let c;try{c=await gd(n,t?.profileName,l)}catch(P){return console.log(_(`Could not check Document360: ${P.message}`)),{kind:"continue"}}if(c.length===0){for(let P of yd(n,t?.profileName,l).lines)console.log(P.startsWith("\u2713")?Q(P):d(P));return{kind:"continue"}}let f=dC(n,c.map(P=>P.path),{allowUnreviewed:r,allowStyleErrors:s,checkScreenshots:!0,allowPlaceholders:i}),g=new Set(hC(f)),y=c.filter(P=>!g.has(P.path)),x=tl(n,f,l);if(g.size>0)for(let P of pC(f,c.length,{scope:l,act:"publish",going:y.length,shotFix:x.shotFix}))console.log(V(P));if(y.length===0)return x.cmd&&console.log(d(`Run: ${x.cmd}`)),{kind:"continue"};c=y,console.log(""),console.log(`${c.length} draft${fd(c.length)} ${c.length===1?"is":"are"} ahead of what readers see:`);for(let P of kC(c.slice(0,12)))console.log(d(P));c.length>12&&console.log(d(` \u2026and ${c.length-12} more`)),console.log("");let T=c;if(!a){let P=await iC({message:"Publish live to your readers?",choices:[{name:`Yes \u2014 publish all ${c.length} live`,value:"all"},...c.slice(0,8).map(S=>({name:`Just ${S.label}`,value:S.path})),{name:"No \u2014 leave them as drafts",value:"none"}]});if(P==="none")return console.log(d("Left as drafts. Nothing changed.")),{kind:"continue"};P!=="all"&&(T=c.filter(S=>S.path===P))}let C=await wd(n,t?.profileName,T,{skipReview:r,skipStyle:s,allowProduction:t?.allowProdWrites?.()===!0,onProgress:(P,S,j)=>console.log(d(` \u25B8 ${P+1}/${S} ${hd(j)}\u2026`))});console.log("");for(let P of bd(C))console.log(P.startsWith("\u2713")?Q(P):P.startsWith("\u2717")?V(P):d(P));return{kind:"continue"}}import{loadKnownConflicts as $C,localArticleTitle as vC,resolveActiveProfile as xC,surfaceCoverageSafe as Iy,readProjectConfig as Ly,recordDeclinedSurface as RC,areaExists as SC,declineTargets as Oy}from"document360-engine";function CC(e,t){let n=vC(e,t);return n||(t.split(/[\\/]/).pop()??t).replace(/\.md$/,"").replace(/^\d+[-_]/,"").replace(/[-_]+/g," ")}function nl(e,t){let n=[];try{n=$C(e,xC(e,t).name)}catch{return null}if(n.length===0)return null;let o=n.length,r=n.slice(0,3).map(i=>CC(e,i)),s=o>3?`${r.join(", ")} and ${o-3} more`:r.join(", ");return{paths:n,note:`\u26A0 ${o} article${o===1?"":"s"} ${o===1?"has":"have"} been changed both here and on Document360 (${s}), so your local copy is not a reliable picture of what your readers have. An audit over it could tell you to write an article that already exists, or miss one that needs your decision \u2014 so it needs ${o===1?"that":"those"} settled first.`}}var Or={cmd:"/resolve",reason:"it walks each one with you \u2014 keep yours, take the portal's, or skip"};function ol(e,t){let n=t.indexOf("--decline");if(n<0)return null;let o=t[n+1];if(!o||o.startsWith("--")){let a=Iy(e,Ly(e)),l=a.recognised?a.areas.slice(0,5).flatMap(c=>Oy(c).map(f=>` /audit --decline ${f}`)):[];return["Name the path to decline \u2014 a folder, or a single file.",...l.length?["","No article covers these right now. Run one of these to settle it for good:",...l]:["","For example:"," /audit --decline src/internal-admin"]]}let r=o.replace(/\\/g,"/").replace(/\/+$/,"");if(!SC(e,r))return[`There is nothing at ${r}, so there is nothing to decline. Copy the path from /eval or /audit.`];let s=t.slice(n+2).filter(a=>!a.startsWith("--")).join(" ")||void 0;return RC(e,r,s)?[`\u2713 ${r} will not be reported as needing an article. /audit and /eval leave it out from now on, and say how many you have declined.`]:[`Could not write .d360-writer/declined-surfaces.json, so ${r} was not declined.`]}async function qs(e){if(e?.cwd){let r=nl(e.cwd,e.profileName);if(r)return{kind:"audit-blocked",note:r.note,lead:Or}}let t=e?.cwd?Iy(e.cwd,Ly(e.cwd)):null,n=[];if(t?.recognised&&t.areas.length>0){let r=t.areas.slice(0,15);n.push("","2.7. These parts of the product are cited by NO article's `sources:` frontmatter. This list is DETERMINISTIC and complete as of now \u2014 it does not depend on the diff window above, so treat it as authoritative rather than re-deriving it:",...r.map(s=>` - ${s.folder} (${s.files} of ${s.total} file${s.total===1?"":"s"} uncited, e.g. ${s.sample.join(", ")}) \u2014 to decline: ${Oy(s).map(i=>`/audit --decline ${i}`).join(" \xB7 ")}`),...t.areas.length>r.length?[` \u2026and ${t.areas.length-r.length} more areas.`]:[]," EVERY row above must leave this run accounted for: a `create` row, or a stated reason it needs no article (internal-only, unreachable by a reader, already covered by an existing article's prose). Do NOT silently drop one \u2014 if you list fewer paths than this section handed you, say which you left out and why."," A `decline` verdict is not a record. For EVERY row you decline, print its exact /audit --decline line (copied from above \u2014 it already picks folder-or-file correctly) in the closing block, each on its own line so the terminal makes it a button. A judgement that leaves the turn as prose changes nothing and will be re-derived, at this price, on every future audit.",...t.declined>0?[` (${t.declined} more ${t.declined===1?"is":"are"} already declined and left out of that list.)`]:[])}return{kind:"forward-to-agent",prompt:["Run the gap-analysis skill against this repo.","","Follow its stages in order:","1. d360_sync_status first; stop if any article is changed in both places, or changed on Document360 and not yet pulled down (the local copy cannot be trusted until that is resolved). Say which articles, in plain words, never the internal status word \u2014 and tell the user to run /resolve, which walks the affected articles one at a time (keep the local copy, take the portal's, or skip). Do NOT ask them to report back to you: they will run /resolve and then /audit again.","2. git diff since the lastAnalyzedCommit marker in d360-category-map.json (or last 30 days if no marker), filtered to the authoritativeSourceFiles paths.","2.5. d360_pending_hints: the source agent's own notes on what it shipped. Verify each against the CURRENT code (its `sources` exist, and the code as it stands shows the surface it claims) before trusting it; never judge it by its `branch` or `commit`. Fold verified ones into the table, flag the rest.","2.6. Anything d360_pending_hints returns under `held` is NOT a proposal. Its cited code has uncommitted changes, so the builder is still writing that surface and has already been asked whether it is done. List each held item at the END of the table as a `waiting` row (Reason: the code behind it is not finished), do NOT propose create/update/retire for it, and do NOT stamp it consumed.",...n,"3. Map changes (diff + hints) to articles via their `sources:` frontmatter; read only implicated articles and changed source files. If articles are missing `sources:`, this is the one-time bootstrap pass, so backfill them.","4. Output the proposal table: | Article (path or proposed title) | Action (create | update | retire | link) | Reason | Evidence | Scope |. Leave every hint OPEN \u2014 this run proposes, it does not write articles, so nothing has been acted on yet.","5. End with the skill's closing block, and put NOTHING below it \u2014 no receipts, no roadmap confirmations, no feedback marker line, no summary. It ends in a question, and a question the user has to scroll back to find is one you did not really ask.","","Do not start writing or updating any article yet; the proposal table is the deliverable.","The audited-up-to marker is recorded for you when this command finishes. Read it to size the diff window; never write it, and never report it as a missing capability."].join(`
|
|
340
340
|
`),display:"/audit"}}ie();import{readProjectConfig as PC,writeProjectConfig as _y,autoBudgetUsd as TC,autoModeEnabled as AC,DEFAULT_AUTO_BUDGET_USD as EC}from"document360-engine";function Ke(e){return`$${e%1===0?e.toFixed(0):e.toFixed(2)}`}function Vs(e){return e?"I run the next step myself instead of waiting for you \u2014 writing, reviewing, checking style, saving and going live.":"I suggest the next step and wait for you to run it."}function Ys(e){let t=AC(e),n=TC(e),o=e.auto?.budgetUsd===void 0?" (the standard limit \u2014 nothing set for this repo)":"",r=[{tone:"info",text:`Mode: ${t?"auto":"manual"} \u2014 ${Vs(t)}`}];return t?(r.push({tone:"info",text:`Spending limit: ${Ke(n)} a run${o}. I stop and report when it is reached.`}),r.push({tone:"info",text:"Screenshots are never part of an automatic run \u2014 they need your product running."}),r.push({tone:"info",text:"Stop a run at any time: esc. Switch back to manual: shift+tab, or /auto off."})):r.push({tone:"info",text:`Turn it on: /auto on \xB7 or shift+tab. Spending limit if you do: ${Ke(n)} a run.`}),r}function rl(e,t,n,o=new Date){let r={...t,auto:{...t.auto,enabled:n,decidedAt:o.toISOString()}};return _y(r,e),[n?{tone:"ok",text:Y("\u2713")+"Auto mode ON \u2014 "+Vs(!0)}:{tone:"ok",text:Y("\u2713")+"Manual mode \u2014 "+Vs(!1)},...Ys(r)]}function $d(e,t,n){if(!Number.isFinite(n)||n<=0)return null;let o={...t,auto:{...t.auto,enabled:t.auto?.enabled===!0,budgetUsd:n}};return _y(o,e),[{tone:"ok",text:Y("\u2713")+`Spending limit set to ${Ke(n)} a run.`},...Ys(o)]}function vd(e){let t=e.findIndex(r=>r==="--budget"||r.startsWith("--budget="));if(t===-1)return{malformed:!1};let n=e[t].includes("=")?e[t].split("=").slice(1).join("="):e[t+1];if(n===void 0)return{malformed:!0};let o=Number(String(n).replace(/^\$/,"").replace(/,/g,""));return!Number.isFinite(o)||o<=0?{malformed:!0}:{usd:o,malformed:!1}}function xd(e,t){let n=e.findIndex(o=>o==="--budget");return e.filter((o,r)=>!o.startsWith("--")&&!(n!==-1&&r===n+1)&&o!==String(t)).map(o=>o.trim().toLowerCase()).find(o=>o.length>0)}var kd=e=>{for(let t of e)console.log(t.tone==="ok"?Q(t.text):t.tone==="warn"?V(t.text):t.tone==="error"?_(t.text):d(t.text))};async function My(e,t){let n=t.cwd,o=PC(n);if(!o)return console.log(_("No d360-writer config here. Run /init first.")),{kind:"continue"};let r=vd(e);if(r.malformed)return console.log(_(`I could not read that spending limit. Give it as an amount, like: /auto --budget 150 (the standard limit is ${Ke(EC)}).`)),{kind:"continue"};let s=xd(e,r.usd);if(r.usd!==void 0&&!s){let i=$d(n,o,r.usd);return i&&kd(i),{kind:"continue"}}if(!s||s==="status")return kd(Ys(o)),{kind:"continue"};if(s==="on"||s==="off"){let i=r.usd!==void 0?{...o,auto:{...o.auto,enabled:o.auto?.enabled===!0,budgetUsd:r.usd}}:o;return kd(rl(n,i,s==="on")),{kind:"continue"}}return console.log(_(`I don't know "${s}". Use: /auto on \xB7 /auto off \xB7 /auto status \xB7 /auto --budget <amount>`)),{kind:"continue"}}ie();import{readProjectConfig as NC}from"document360-engine";function Rd(){return["Run the discover-product skill against this repo.","","Build (or continue refining) the product brief at `.d360-writer/product-brief.md` by interviewing the","source repo's coding agent (the builder):","1. Gather all grounding: the code surface (analyze-codebase pass), `.d360-writer/architecture/`"," (+ CREATOR-NOTES), any specs under `.d360-writer/input/`, existing docs, git log, and the current"," product brief if one exists.","2. Self-assess against the 8-point readiness rubric (identity, audience, core mental model,"," misconceptions, positioning, key concepts & terminology, critical journeys, boundaries/non-goals).","3. Write/update the brief, with every claim cited to its grounding, and keep its frontmatter current"," (status, rounds, open_questions, last_updated).","4. For the dimensions you cannot yet ground confidently, ask the builder a focused batch (\u22646) of pointed",' questions via d360_ask_builder, with the subject prefixed "[discovery] ". Then END the turn;'," builder answers async; /catch-up will ingest the answers and continue the loop.","","Set status: confident and hand off to propose-structure ONLY when every rubric dimension passes. If the","builder stays silent across a few rounds, degrade gracefully per the skill (best-effort brief, flagged","open questions) rather than blocking the docs. Do NOT write or publish any articles in this turn."].join(`
|
|
341
341
|
`)}async function Uy(e,t){let n=t?.cwd??process.cwd();return NC(n)?{kind:"forward-to-agent",prompt:Rd(),display:"/discover"}:(console.log(_("No d360-writer config here. Run /init first.")),{kind:"continue"})}ie();import{checkbox as DC}from"@inquirer/prompts";import{inventoryRepo as jC,readProjectConfig as Fy,writeProjectConfig as IC}from"document360-engine";function Sd(e,t){let n=Fy(e);n&&(n.authoritativeSourceFiles=t,IC(n,e))}function sl(e){let t=[`${e.fileCount}${e.fileCount>=5e3?"+":""} files`];return e.stacks.length&&t.push(e.stacks.join("+")),t.push(e.reason),t.join(" \xB7 ")}async function Wy(e,t){let n=t?.cwd??process.cwd();if(!Fy(n))return console.log(_("No .d360-writer/config.json here. Run /init first.")),{kind:"continue"};let o=jC(n);if(o.length===0)return console.log(d('No candidate source folders found. Set "authoritativeSourceFiles" in .d360-writer/config.json manually.')),{kind:"continue"};if(!process.stdin.isTTY||!t){console.log(""),console.log(Z("Recommended documentation scope (run /scope in the REPL to choose):"));for(let s of o)console.log(` ${s.recommended?"\u25C9":"\u25CB"} ${s.path} ${d(sl(s))}`);return console.log(""),{kind:"continue"}}let r;try{r=await t.withPausedInput(()=>DC({message:"Which folders back the user docs? (space toggles, enter confirms)",choices:o.map(s=>({name:`${s.path} (${sl(s)})`,value:s.path,checked:s.recommended})),pageSize:20}))}catch{return console.log(d("Cancelled.")),{kind:"continue"}}if(r.length===0)return console.log(d("Nothing selected \u2014 scope unchanged.")),{kind:"continue"};Sd(n,r),console.log(Q(`\u2713 Scoped to ${r.length} folder(s) \u2014 written to .d360-writer/config.json`));for(let s of r)console.log(` ${s}`);return console.log(""),{kind:"continue"}}ie();import{confirm as VC}from"@inquirer/prompts";import{applyPull as Ad,computeSyncStatus as Zy,compareRemoteSources as YC,planPull as ew,planImport as KC,applyImport as zC,establishBaselines as XC,rebaselineServerMeta as QC,D360AuthError as JC}from"document360-engine";ie();var By=[{status:"conflict",header:"Changed in both places \u2014 pick a direction (/pull <path> or /push <path>):",paint:_,mark:"!"},{status:"local-ahead",header:"Changed here \u2014 push with /push <path>:",paint:V,mark:"M"},{status:"remote-ahead",header:"Changed on Document360 \u2014 pull with /pull <path> (or just /pull):",paint:Z,mark:"M"},{status:"deleted-local",header:"Removed locally \u2014 still on Document360 (pull to restore, or leave it):",paint:_,mark:"D"},{status:"deleted-remote",header:"Gone from Document360 \u2014 still here (publish to recreate, or delete the file):",paint:_,mark:"D"},{status:"orphaned",header:"Gone from both sides \u2014 these tracking entries point at nothing now:",paint:Te,mark:"X"},{status:"untracked-local",header:"New here \u2014 not on Document360 yet (publish to add them):",paint:d,mark:"?"},{status:"untracked-remote",header:"On Document360 \u2014 not in your local docs yet (run /pull to import them):",paint:d,mark:"?"},{status:"unknown-base",header:"Tracked, not yet compared \u2014 run /pull --baseline to set a baseline (or the next publish/pull sets one):",paint:Te,mark:"\xB7"}];function LC(e){return e.path?e.path:`${e.title??"(untitled)"} ${Te(`[${e.articleId}]`)}`}function OC(e){let t=e/1e3;if(t<60)return`${t.toFixed(1)}s`;let n=Math.floor(t/60);return`${n}m ${Math.round(t-n*60)}s`}function il(e,t){let n=[],o=e.entries.filter(f=>f.link);if(o.length>0){n.push(""),n.push(Qe("Already on Document360, just not linked \u2014 ask me to link these (a republish would duplicate them):"));for(let f of o)n.push(` ${V("~")} ${V(f.path??"")} ${Te(`\u2192 "${f.link.title}"`)}`)}for(let f of By){let g=e.entries.filter(y=>y.status===f.status&&!(f.status==="untracked-local"&&y.link));if(g.length!==0){n.push(""),n.push(Qe(f.header));for(let y of g)n.push(` ${f.paint(f.mark)} ${f.paint(LC(y))}${y.detail?Te(` (${y.detail})`):""}`)}}let r=e.counts["in-sync"]??0,s=e.entries.length;n.push("");let i={"in-sync":"up to date","local-ahead":"changed here","remote-ahead":"changed on Document360",conflict:"changed both places","untracked-local":"new here","untracked-remote":"only on Document360","deleted-local":"removed locally","deleted-remote":"gone from Document360",orphaned:"gone both sides","unknown-base":"not yet compared"},a=o.length,l=s===r?Q("\u2713 everything up to date"):[r>0?Q(`\u2713 ${r} up to date`):null,a>0?`${a} to link`:null,...By.map(f=>{let g=(e.counts[f.status]??0)-(f.status==="untracked-local"?a:0);return g>0?`${g} ${i[f.status]}`:null})].filter(Boolean).join(Te(" \xB7 ")),c=t!==void 0?` \xB7 synced in ${OC(t)}`:"";return n.push(l+Te(` \xB7 ${s} tracked+seen \xB7 profile "${e.profile}" \xB7 docs root ${e.docsRoot}/${c}`)),n}import{structuredPatch as _C}from"diff";import Hy from"picocolors";var MC=80,_r=400,UC="\x1B[48;2;74;28;28m",FC="\x1B[48;2;24;66;24m",Gy="\x1B[49m",Cd=e=>String(e).padStart(5);function qy(e,t){let n=Math.min(e.length,t.length),o=0;for(;o<n&&e[o]===t[o];)o++;return o}function Vy(e,t,n){if(n===void 0||n<t-4)return e.slice(0,t);let o=Math.max(0,n-12);return`\u2026${e.slice(o).slice(0,Math.max(8,t-1))}`}function _t(e,t,n,o=MC){let r=T=>{let C=T.replace(/\r\n/g,`
|
|
342
342
|
`);return C.endsWith(`
|
|
343
343
|
`)||C===""?C:C+`
|
|
344
|
-
`},s=r(e),i=r(t);if(s===i)return null;let a=Math.max(20,n-10),l=_C("a","b",s,i,"","",{context:3}),c=0,f=0,g=[];l.hunks.forEach((T,C)=>{C>0&&g.push(Hy.gray(" \u2026"));let P=T.oldStart,S=T.newStart,j=[],G=[],D=()=>{for(let H=0;H<j.length;H++){let I=H<G.length?qy(j[H],G[H]):void 0;f++,g.push(`${UC}${Cd(P++)} - ${Vy(j[H],a,I)}${Gy}`)}for(let H=0;H<G.length;H++){let I=H<j.length?qy(j[H],G[H]):void 0;c++,g.push(`${FC}${Cd(S++)} + ${Vy(G[H],a,I)}${Gy}`)}j=[],G=[]};for(let H of T.lines){let I=H[0],oe=H.slice(1);I==="-"?j.push(oe):I==="+"?G.push(oe):(D(),g.push(Hy.gray(Cd(S))+" "+oe.slice(0,a)),P++,S++)}D()});let y=Math.max(1,o),x=g.slice(0,y);return{added:c,removed:f,lines:x,hidden:Math.max(0,g.length-y)}}import{select as Xy}from"@inquirer/prompts";import{applyPull as Qy,computeSyncStatus as HC,isPullWouldBlankError as Ky,localArticleTitle as GC,planPull as zy}from"document360-engine";import{listUnpullable as WC,resolveActiveProfile as BC}from"document360-engine";function al(e,t,n,o){let r=n.filter(a=>a.status==="conflict"&&a.path).map(a=>a.path),s=new Set(o),i=[];try{i=WC(e,BC(e,t).name).map(a=>a.path).filter(a=>s.has(a))}catch{}return[...new Set([...r,...i])].sort()}function ll(){return["Document360 will not give this article back as markdown \u2014 it is not on the Markdown (Editor 3.0) editor, so there is no portal copy to show you or to take.","Your local copy is intact, and publishing it over the portal settles this. Skipping leaves the article flagged, and it will be waiting again next time."]}function cl(){return"Publish your copy over the portal (settles it) \xB7 Skip?"}function Yy(){return"(l publish yours \xB7 s skip \u2014 anything else stops, keeping your answers so far)"}function ul(e){if(e.length===0)return[];let t=e.length,n=e.slice(0,3).join(", ")+(t>3?`, +${t-3} more`:"");return[`${t} article${t===1?"":"s"} stay${t===1?"s":""} flagged \u2014 Document360 cannot hand ${t===1?"its":"their"} markdown back: ${n}`,"Two ways out: run /resolve and press l to publish your copy over the portal, or ask your Document360 admin to move the article back to the Markdown (Editor 3.0) editor, then run /resolve again."]}function Pd(e){return`Cannot take the portal copy of ${e} \u2014 Document360 will not return it as markdown. Left as it was.`}ie();var dl=e=>e===1?"":"s";async function Td(e,t,n=1){console.log(""),console.log(`${Z("\u25CF")} ${Qe(t.title)} ${Te(`(${t.path})`)}`);for(let i of t.notes)console.log(V(` \u26A0 ${i}`));let o=_t(t.oldContent,t.newContent,Math.max(40,(process.stdout.columns??80)-1),_r);if(o){console.log(Te(` \u23BF local \u2192 portal: +${o.added} / -${o.removed} line${dl(o.added+o.removed)}`));for(let i of o.lines)console.log(i);o.hidden>0&&console.log(d(` \u2026 +${o.hidden} more diff lines`))}else console.log(d(" Local already matches the portal content \u2014 resolving just advances the sync base."));let r=n>1,s=await e.withPausedInput(()=>Xy({message:`Conflict \u2014 ${t.path}${r?` (${n} left)`:""}`,choices:[{name:"Keep local (publish \u2014 overwrites the portal edit)",value:"local"},{name:"Take portal (overwrites your local edits)",value:"portal"},{name:"Skip (leave it a conflict)",value:"skip"},...r?[{name:`Keep local for ALL ${n} remaining`,value:"local-all"},{name:`Take portal for ALL ${n} remaining`,value:"portal-all"},{name:`Skip ALL ${n} remaining`,value:"skip-all"}]:[]]})).catch(()=>"skip");return s==="portal"?(Qy({cwd:e.cwd,profileName:e.profileName},t),console.log(Q(" \u2713 Took the portal copy (local overwritten, sync base advanced).")),"took-portal"):s==="local"?"keep-local":s==="skip"?(console.log(d(" Skipped \u2014 still a conflict.")),"skipped"):s==="local-all"?"keep-local-all":s==="portal-all"?"took-portal-all":"skipped-all"}async function qC(e,t){console.log(""),console.log(`${Z("\u25CF")} ${Qe(GC(e.cwd,t)??t)} ${Te(`(${t})`)}`);for(let o of ll())console.log(V(` \u26A0 ${o}`));return await e.withPausedInput(()=>Xy({message:cl(),choices:[{name:"Publish your copy over the portal (settles it)",value:"local"},{name:"Skip (leave it a conflict)",value:"skip"}]})).catch(()=>"skip")==="local"?"keep-local":(console.log(d(" Skipped \u2014 still a conflict.")),"skipped")}async function Jy(e,t){let n=Je(t.cwd,t.profileName);if(!n.ok)return console.log(_(n.reason)),{kind:"continue"};let o=e.find(r=>!r.startsWith("--"))?.replace(/\\/g,"/");try{let r=await HC({cwd:t.cwd,profileName:t.profileName}),s=al(t.cwd,t.profileName,r.entries,Uo(r.entries).map(l=>l.path));if(o){if(!s.includes(o))return console.log(Q(`${o} isn't waiting on a decision \u2014 nothing to resolve.`)),{kind:"continue"};s=[o]}if(s.length===0)return console.log(Q("\u2713 No conflicts \u2014 local and Document360 agree on every tracked article.")),{kind:"continue"};let i=[],a=[];for(let[l,c]of s.entries()){let f;try{f=await zy({cwd:t.cwd,profileName:t.profileName,relPath:c})}catch(x){if(!Ky(x))throw x;await qC(t,c)==="keep-local"?i.push(c):a.push(c);continue}let g=await Td(t,f,s.length-l);if(g==="keep-local"&&i.push(c),!g.endsWith("-all"))continue;let y=s.slice(l);if(g==="keep-local-all")i.push(...y),console.log(d(` Keeping local for all ${y.length} \u2014 publishing them over the portal.`));else if(g==="took-portal-all"){let x=0;for(let T of y)try{Qy({cwd:t.cwd,profileName:t.profileName},await zy({cwd:t.cwd,profileName:t.profileName,relPath:T})),x++}catch(C){console.log(_(` \u2717 ${T}: ${C.message}`)),Ky(C)&&a.push(T)}console.log(Q(` \u2713 Took the portal copy of ${x} article${dl(x)} (local overwritten, sync base advanced).`))}else console.log(d(` Skipped all ${y.length} \u2014 still conflict${dl(y.length)}.`));break}for(let l of ul(a))console.log(V(`\u26A0 ${l}`));if(i.length>0)return console.log(d(`Publishing ${i.length} kept-local article${dl(i.length)} over the portal\u2026`)),await Jt(t,i,0,{allowUnpullable:!0})}catch(r){console.log(_(`Resolve failed: ${r.message}`))}return{kind:"continue"}}async function tw(e,t){let n=Je(t.cwd,t.profileName);if(!n.ok)return console.log(_(n.reason)),{kind:"continue"};let o=e.find(r=>r.startsWith("--"));try{if(o==="--status")return await ZC(t),{kind:"continue"};if(o==="--baseline")return await eP(t,e.filter(i=>i!=="--baseline")),{kind:"continue"};if(o==="--compare")return await tP(t),{kind:"continue"};if(o)return console.log(_(`Unknown option: ${o}`)),console.log(d("Usage: /pull pull portal edits + import new portal articles")),console.log(d(" /pull <path> pull one article (or resolve a conflict)")),console.log(d(" /pull --status drift report only (no writes)")),console.log(d(" /pull --baseline set a sync baseline for not-yet-compared articles")),{kind:"continue"};let r=e[0];if(r)return await nP(t,r),{kind:"continue"};console.log(d("Pulling changes from Document360\u2026"));let s=await Ed({cwd:t.cwd,profileName:t.profileName});for(let i of Nd(s))console.log(i.tone==="ok"?Q(i.text):i.tone==="warn"?V(i.text):d(i.text));console.log("")}catch(r){r instanceof JC?console.log(_(r.message)):console.log(_(`Pull failed: ${r.message}`))}return{kind:"continue"}}async function Ed(e){let t=await Zy({cwd:e.cwd,profileName:e.profileName}),n={pulled:[],imported:[],conflicts:[],toPublish:0,failed:[]};for(let o of t.entries)if(o.status==="remote-ahead"&&o.path)try{let r=await ew({cwd:e.cwd,profileName:e.profileName,relPath:o.path});Ad({cwd:e.cwd,profileName:e.profileName},r),n.pulled.push(o.path)}catch(r){n.failed.push({ref:o.path,error:r.message})}else if(o.status==="untracked-remote"&&o.articleId)try{let r=await KC({cwd:e.cwd,profileName:e.profileName,articleId:o.articleId,...o.remoteCategory?{categoryPath:o.remoteCategory.path}:{}});zC({cwd:e.cwd,profileName:e.profileName},r),n.imported.push({title:r.title,path:r.path})}catch(r){n.failed.push({ref:o.title??o.articleId,error:r.message})}else o.status==="conflict"&&o.path?n.conflicts.push(o.path):(o.status==="local-ahead"||o.status==="untracked-local"&&!o.link)&&o.path&&n.toPublish++;return n}var pl=(e,t=5)=>`${e.slice(0,t).join(", ")}${e.length>t?`, +${e.length-t} more`:""}`,Fo=e=>e===1?"":"s";function Nd(e){let t=[];if(e.pulled.length&&t.push({tone:"ok",text:`\u2713 Pulled ${e.pulled.length} portal edit${Fo(e.pulled.length)} into local: ${pl(e.pulled)}`}),e.imported.length&&t.push({tone:"ok",text:`\u2713 Imported ${e.imported.length} new portal article${Fo(e.imported.length)}: ${pl(e.imported.map(n=>n.path))}`}),e.toPublish&&t.push({tone:"info",text:`\u2139 ${e.toPublish} article${Fo(e.toPublish)} changed locally \u2014 /push saves them to Document360.`}),e.conflicts.length&&t.push({tone:"warn",text:`\u26A0 ${e.conflicts.length} article${Fo(e.conflicts.length)} changed both here and on Document360 \u2014 left untouched. Resolve them: /resolve (or /push <path> to keep yours, /pull <path> to take the portal's). (${pl(e.conflicts)})`}),e.failed.length){t.push({tone:"warn",text:`\u26A0 ${e.failed.length} item${Fo(e.failed.length)} could not be imported.`});let n=new Map;for(let o of e.failed){let r=o.error.replace(/^"[^"]*" could not be imported: /,"");n.set(r,[...n.get(r)??[],o.ref])}for(let[o,r]of n)t.push({tone:"warn",text:` ${o}`}),t.push({tone:"warn",text:` ${pl(r)}`})}return t.length===0&&t.push({tone:"ok",text:"\u2713 Already in sync with Document360 \u2014 nothing to pull."}),t}async function ZC(e){console.log(d("Checking Document360 for drift\u2026"));let t=Date.now(),n=await Zy({cwd:e.cwd,profileName:e.profileName});for(let o of il(n,Date.now()-t))console.log(o);console.log("")}async function eP(e,t){let n=t[0]&&t[0]!=="--all"?t[0].replace(/\\/g,"/"):void 0;console.log(d(n?`Setting the sync baseline for ${n}\u2026`:"Setting a sync baseline for tracked-but-not-yet-compared articles\u2026"));let o=await XC({cwd:e.cwd,profileName:e.profileName,relPaths:n?[n]:void 0}),r=n?{rebaselined:[],skipped:[]}:await QC({cwd:e.cwd,profileName:e.profileName});if(o.baselined.length===0&&o.skipped.length===0&&r.rebaselined.length===0){console.log(Q("\u2713 Nothing to baseline \u2014 every tracked article already has a sync baseline."));return}if(o.baselined.length>0&&console.log(Q(`\u2713 Recorded ${o.baselined.length} article(s) as matching Document360 \u2014 they read in-sync until either side changes.`)),r.rebaselined.length>0&&console.log(Q(`\u2713 Refreshed the metadata baseline for ${r.rebaselined.length} article(s) \u2014 cleared false "metadata changed" drift.`)),o.skipped.length>0){console.log(V(`${o.skipped.length} skipped:`));for(let s of o.skipped)console.log(d(` - ${s.path} (${s.reason})`))}}async function tP(e){console.log(d("Comparing the manifest against the per-article path (read-only)\u2026"));let t=await YC({cwd:e.cwd,profileName:e.profileName});console.log(` Legacy listing: ${Qe(String(t.legacyCount))} \xB7 Manifest: ${Qe(String(t.manifestCount))} \xB7 in both: ${t.inBoth}`),t.onlyInLegacy.length===0&&t.onlyInManifest.length===0&&t.mappedMissingFromManifest.length===0?console.log(Q("\u2713 Parity \u2014 the manifest sees exactly the same articles. Safe to make it the default sync source.")):(t.mappedMissingFromManifest.length&&console.log(_(`\u2717 ${t.mappedMissingFromManifest.length} MAPPED article(s) missing from the manifest: ${t.mappedMissingFromManifest.slice(0,5).join(", ")}`)),t.onlyInLegacy.length&&console.log(V(` ${t.onlyInLegacy.length} only in the legacy listing: ${t.onlyInLegacy.slice(0,5).join(", ")}`)),t.onlyInManifest.length&&console.log(V(` ${t.onlyInManifest.length} only in the manifest: ${t.onlyInManifest.slice(0,5).join(", ")}`)),console.log(d(" Send this output \u2014 a mismatch pinpoints where the manifest differs before we flip."))),console.log("")}async function nP(e,t){let n=t.replace(/\\/g,"/"),o=await ew({cwd:e.cwd,profileName:e.profileName,relPath:n});if(o.overwritesLocalChanges){await Td(e,o)==="keep-local"&&(console.log(d(`Publishing ${o.path} over the portal\u2026`)),await Jt(e,[o.path]));return}console.log(""),console.log(`${Z("\u25CF")} ${Qe(o.title)} ${Te(`(${o.path})`)}`);for(let i of o.notes)console.log(V(` \u26A0 ${i}`));let r=_t(o.oldContent,o.newContent,Math.max(40,(process.stdout.columns??80)-1),_r);if(!r){console.log(d(" Local file already matches the remote content \u2014 advancing the sync base only.")),Ad({cwd:e.cwd,profileName:e.profileName},o);return}console.log(Te(` \u23BF Added ${r.added} line${Fo(r.added)}, removed ${r.removed} line${Fo(r.removed)}`));for(let i of r.lines)console.log(i);if(r.hidden>0&&console.log(d(` \u2026 +${r.hidden} more diff lines`)),!await e.withPausedInput(()=>VC({message:`Write ${o.path}?`,default:!o.overwritesLocalChanges}))){console.log(d(" Skipped."));return}Ad({cwd:e.cwd,profileName:e.profileName},o),console.log(Q(` \u2713 Pulled ${o.path} (sync base advanced).`))}ie();import{statSync as hP}from"node:fs";import{resolve as fP}from"node:path";import{estimateBulkCost as mP,planPartitions as gP,readProjectConfig as yP,resolveModelForOperation as wP,runPartitioned as bP,trackedArticlePaths as kP}from"document360-engine";function Xn(e,t){return e.aborted?null:e.blocked?[`\u2717 ${t} didn\u2019t start \u2014 ${e.blocked}.`,"Nothing was changed. Fix that, then re-run."]:e.allEmpty?[`\u2717 ${t} did nothing \u2014 every batch produced no output, so no agent actually ran.`,"Run /doctor \u2014 it runs a real turn with your skills and tools and reports what failed. /logs shows Claude Code\u2019s own error, which was recorded either way, and offers to copy it.","A Claude usage or session limit also does this, and it ends every turn instantly \u2014 if you have just hit one, wait for the reset and re-run."]:null}function nw(e){return!e.bailedEmpty||!e.notRun?null:`\u26A0 Stopped early \u2014 ${e.notRun} batch${e.notRun===1?" was":"es were"} not attempted, because two in a row produced no output (that is systemic, not this work: usually a Claude usage limit). Re-run to finish the rest; anything already done is skipped.`}var Mr=class{activeMs=0;last=0;timer=null;startedAt=0;stopped=null;start(t=Date.now()){this.timer||(this.startedAt=t,this.last=t,this.timer=setInterval(()=>this.tick(),1e3),this.timer.unref?.())}tick(t=Date.now()){let n=t-this.last;this.last=t,!(n<=0)&&(this.activeMs+=Math.min(n,5e3))}stop(t=Date.now()){return this.stopped?this.stopped:(this.timer&&(clearInterval(this.timer),this.timer=null),this.tick(t),this.stopped={activeSeconds:Math.max(0,Math.round(this.activeMs/1e3)),elapsedSeconds:Math.max(0,Math.round((t-this.startedAt)/1e3))},this.stopped)}};function hl(e,t){let n=t-e;return n>=300&&n>=t*.2}function Mt(e,t,n,o=Date.now(),r){let s=t.reduce((a,l)=>a+(l.inputTokens??0),0),i=t.reduce((a,l)=>a+(l.cacheReadTokens??0),0);return{seconds:Math.max(0,Math.round((o-e)/1e3)),outputTokens:t.reduce((a,l)=>a+l.outputTokens,0),inputTokens:s,...s>0?{cachedPct:Math.round(i/s*100)}:{},...r!==void 0?{activeSeconds:r}:{},costUsd:n}}function fl(e){return e.activeSeconds!==void 0&&hl(e.activeSeconds,e.seconds)?`${Dd(e.seconds)} elapsed \xB7 ${Dd(e.activeSeconds)} working`:Dd(e.seconds)}var ow=e=>e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(1)}k`:`${e}`,Dd=e=>{if(e<60)return`${e}s`;let t=Math.floor(e/60);return t<60?`${t}m ${e%60}s`:`${Math.floor(t/60)}h ${t%60}m`};function un(e){let t=e.cachedPct!==void 0?`, ${e.cachedPct}% cached`:"",n=e.costUsd>0?`, this turn ${e.costUsd<.01?e.costUsd.toFixed(4):e.costUsd.toFixed(2)}`:"";return` (took ${fl(e)}, ${ow(e.inputTokens)}\u2192${ow(e.outputTokens)} tokens${t}${n})`}import Re from"picocolors";import Od from"wrap-ansi";import _d from"string-width";ie();import Qn from"picocolors";import gl from"wrap-ansi";import ml from"string-width";var Wo=[{name:"init",usage:"/init",desc:"Pick an environment, scaffold .d360-writer/config.json, and set up the builder hand-off (prints the one-paste prompt)",group:"start"},{name:"login",usage:"/login",desc:"Sign in to Document360 (browser)",group:"start"},{name:"project",usage:"/project [name]",desc:"Choose the Document360 project (picker; lists all you can access)",group:"start"},{name:"devhints",usage:"/devhints",desc:"Re-show/refresh the builder hand-off prompt (set up by /init; run anytime to hand your repo agent the latest)",group:"start"},{name:"scope",usage:"/scope",desc:"Choose which repo folders back the docs (monorepos)",group:"start"},{name:"genre",usage:"/genre [web|cli|service|library|auto]",desc:"Set/show the product genre \u2014 picks the docs craft playbook (usually auto-detected; set it if it guessed wrong)",group:"start"},{name:"mcp",usage:"/mcp [list|add|remove]",desc:"Connect MCP servers (Notion, Linear, \u2026)",group:"start"},{name:"discover",usage:"/discover",desc:"Interview your builder to understand the product (identity, mental model, misconceptions, positioning) \u2192 pins a product brief that grounds the docs; asks focused questions and loops until confident",group:"docs"},{name:"draft",usage:"/draft [<feature>]",desc:"Draft a feature's articles from its PRDs/specs in .d360-writer/input/ + the code",group:"docs"},{name:"document",usage:"/document [<what>] [--push]",desc:"Document a shipped change \u2014 the agent picks the action (new section/article/update); bare: act on builder requests. --push then saves the result to Document360 as drafts, which still needs an editorial review first",group:"docs"},{name:"write",usage:"/write [--preview|--scope <cat>|<path>] [--rewrite] [--concurrency N] [--skip-style]",desc:"Write the planned articles in parallel (starts immediately; --preview shows plan + cost first; --rewrite regenerates already-written articles; --concurrency default 3). Anything that would block publishing is corrected in one automatic pass before the run finishes; --skip-style leaves it to /lint",group:"docs"},{name:"screenshot",usage:"/screenshot [author|capture|annotate|verify|checklist|list] [id|path|--all|--failed|--blocked]",desc:"Screenshots, one family \u2014 bare: pick what to do; author: write capture specs; capture: take the shots (--all re-shoots everything; --failed re-runs the ones that did not work; --blocked re-runs the ones waiting on app data); annotate: draw the callouts on shots already taken, no browser needed (--all --force re-draws after a wording or style change); verify: judge the finished pictures and mark them verified (both run automatically after a capture); checklist: what to stage + dev test-id requests; list: status + what to stage",group:"docs"},{name:"preview",usage:"/preview [path|id]",desc:"Render an article (no arg: pick from tracked)",group:"docs"},{name:"push",usage:"/push [path|--all|--related <path>|[<category>] --screenshots] [--preview] [--skip-review]",desc:"Save your articles to Document360 as drafts \u2014 readers don't see them until /publish (no arg: pick; --all: every candidate; --screenshots: re-save articles with captured shots so images embed, optionally narrowed to one category; --related <path>: also save that article's related articles that were never saved; --preview <path>: show exactly what would be sent, and send nothing). Blocked until each article has had /review --fix \u2014 AI-written text gets an editor first; --skip-review overrides deliberately",group:"publish"},{name:"publish",usage:"/publish [path|<category>|--all] [--skip-review]",desc:"Take the drafts on Document360 LIVE to your readers (the portal's own Publish). Always lists what would become visible and asks first; no arg or a category narrows it. Needs an editorial review \u2014 /review --blocked --fix",group:"publish"},{name:"style-guide",usage:"/style-guide [list | use [<preset>] | keep | sweep [skip] | pull [<n>] | learn | compile | explain <rule>]",desc:"Choose the writing rules your docs are held to. Bare: what is active now; list: every pack you can turn on; use: switch packs, hold every article to them and apply the free text swaps (bare: pick from a list); keep: settle on the rules you have today; sweep: how much of your knowledge base still needs reading against a guide you switched to (skip stops me raising it); pull: bring your Document360 project's own style guide down; learn: turn what your reviewers keep asking for into standing rules; compile: turn its mechanical statements into checks; explain: what one rule means and how to silence it",group:"docs"},{name:"lint",usage:"/lint [<category|path>] [--blocked] [--fix] [--full] [--baselined]",desc:"Lint the docs against the house rules: leaked internals, invented commands, stale facts, AI-tells, em dashes, callout shape (deterministic, no tokens). Every finding names the rule that produced it. Scope to a category/path; --blocked narrows to the errors that block publishing and leaves warnings and suggestions alone; --fix has the agent correct them in place; --full lists every finding instead of a capped summary; --baselined shows the issues that were already in your articles when you chose this style guide, which never block publishing",group:"docs"},{name:"review",usage:"/review [<category|path> | --all | --blocked | --stale] [--fix]",desc:"Independent senior-editor review: style guide, terminology/voice, grammar/clarity, page structure, improvement suggestions. A whole-knowledge-base run also gets one editor on the SHAPE of the docs \u2014 reading order, categories that have grown too big, articles filed in the wrong place, names that no longer fit. Bare: pick a category or everything; --blocked reads only the articles publishing is blocked on; --stale reads the ones the current editor has not read yet, so running it twice costs nothing; --all reviews the whole KB; --fix applies the corrections in place",group:"docs"},{name:"reorder",usage:"/reorder [<category> | --all]",desc:"Reorder articles within a category to the best reading order (renumbers NN- prefixes, fixes references + map); bare: pick a category or everything; new order syncs to Document360 on next /push",group:"docs"},{name:"restructure",usage:"/restructure [<what to change>]",desc:"Reshape the knowledge base: move an article to a different category, split one that has grown too big, merge two, rename one, or nest one inside another. A category left empty by a merge is hidden from readers, never deleted. Bare: reviews how the docs are organized and reports what should change, without touching anything; the changes reach Document360 on the next /push",group:"docs"},{name:"structure",usage:"/structure",desc:"Show the documentation reading order (categories \u2192 articles) as it will appear on Document360",group:"docs"},{name:"status",usage:"/status [--all]",desc:'What stands between your work and your readers, before you spend anything: which articles no editor has read, which break a style rule your config makes blocking, which would show "[Screenshot: \u2026]" text to a reader, which are in conflict with the portal, and which are saved as drafts readers cannot see yet. Each group names the one command that clears it. Reads local files and the last sync \u2014 no network, no tokens. --all lists every article gate by gate',group:"publish"},{name:"retire",usage:"/retire [<path>] [--yes]",desc:"Take an article off your readers on Document360 \u2014 it stops being visible, and nothing offers to publish it again. Nothing is deleted: the article, its version history and every reviewer comment stay. Bare: lists what is worth retiring (articles on Document360 that are no longer in your docs folder). /push puts one back \u2014 reviving is a publish like any other",group:"publish"},{name:"untrack",usage:"/untrack [<path>] [--undo] [--list] [--yes]",desc:"Disown a page that was imported from Document360 but is not one of your docs. /catch-up and /pull import every article they find in your categories; this records the ones you do not want, so they are never imported again \u2014 and the decision travels with the repo, so a teammate\u2019s clone inherits it. Nothing on Document360 changes and nothing is deleted: the markdown moves to .d360-writer/declined/. Bare: lists what this repo has declined. --undo puts one back",group:"publish"},{name:"reuse",usage:'/reuse [pull | add <variable|snippet> <name> "<value>" | audit [<category>] [--create <n\u2026>]]',desc:"The terms, values and blocks your project already maintains on Document360, so your articles reference them instead of repeating them. A variable holds a value like your product name in one place \u2014 change it there and every article changes; type it out by hand and /lint flags it. A snippet does the same for a whole block, and the writer references yours by name instead of writing its own version. A term is defined once and every article is checked for explaining it the first time it appears. Reading is free; pull refreshes from Document360, add creates a new one there, and audit reads every article to work out what belongs in one place",group:"docs"},{name:"eval",usage:"/eval [<category>] [--all]",desc:"How does your whole knowledge base score? Reads every article and reports what no single-article check can see: internal links written as portal URLs, links pointing at headings that no longer exist, link text that names a different article, the command summary line appearing in several shapes, two names for the same section, and how many style findings were accepted as exempt when you chose your style guide. Costs nothing and calls nothing",group:"docs"},{name:"terms",usage:'/terms [<category>] [--all] [--approve "<term>"]',desc:"Is your knowledge base calling the same thing by the same name? Reads every article and reports terms spelled more than one way, short names that may be the long name abbreviated, and article titles out of step with the rest of their kind \u2014 the consistency an editor reading three articles at a time cannot see. Costs nothing and calls nothing. --approve records a spelling as the approved one, so every later run enforces it",group:"docs"},{name:"roadmap",usage:"/roadmap [--all | done [step]]",desc:"Show the docs campaign (the multi-session plan the agent maintains), grouped by the part of the job \u2014 progress + next step; `--all` also lists the steps a later pass replaced; `done` marks the next open step finished (add words from a title to pick another)",group:"docs"},{name:"audit",usage:"/audit [--auto on|off] [--decline <path>]",desc:"Gap analysis: code vs docs vs Document360 (what changed); --auto runs it at launch when code changed; --decline settles a folder or file that needs no article",group:"publish"},{name:"inbox",usage:"/inbox",desc:"Act on messages from the repo's coding agent (requests to document, answers to your questions)",group:"publish"},{name:"ask",usage:"/ask <question>",desc:"Ask your developers something the code cannot tell you: why a feature exists, when to use it, whether it can be undone",group:"publish"},{name:"share",usage:"/share [status|on|off]",desc:"Share your docs work with the team: get your builder's latest, then save + send yours (runs automatically; `off` leaves it to your coding agent)",group:"publish"},{name:"catch-up",usage:"/catch-up [--push] [--include-unfinished]",desc:"Process ALL pending work (builder hints + requests + Document360 portal drift) \u2192 write \u2192 save to Document360 as drafts. --push does that last step for you, which still needs an editorial review first. A note whose code your builder has not committed is left for later, and they are asked whether it is done; --include-unfinished writes those pages anyway. For code changes, that's /audit",group:"publish"},{name:"comments",usage:"/comments [<article>] [--scope <category>] [--answer] [--all] [--resolve] [--answered] [--full]",desc:"Reviewer comments left on passages of your articles in the Document360 portal \u2014 bare: a table by article, pick a number to read its comments in full, then answer or go back; --answer: fix, push the draft, reply (threads stay open unless you add --resolve); --full re-reads every article",group:"publish"},{name:"pull",usage:"/pull [<path> | --status | --baseline]",desc:"Bring Document360 down: pull portal edits + import NEW portal articles. --status: drift report only \xB7 --baseline: set a sync base \xB7 <path>: one article / resolve a conflict",group:"publish"},{name:"resolve",usage:"/resolve [<path>]",desc:"Resolve conflicts (edited both here and on the portal): shows the diff, then keep local (push), take portal (pull), or skip \u2014 per article, or one answer for all remaining. Bare: walk them all",group:"publish"},{name:"convert",usage:"/convert [--scope <folder>] [--run]",desc:"Convert tracked articles to DFM (one-off legacy migration)",group:"publish"},{name:"profile",usage:"/profile [name|add <name> [env]]",desc:"Where this repo publishes \u2014 switch, or connect another environment (picker; s = session)",group:"setup"},{name:"workspace",usage:"/workspace [name]",desc:"Switch the Document360 workspace (picker)",group:"setup"},{name:"auto",usage:"/auto [on|off|status] [--budget <amount>]",desc:"Auto or manual: in auto I run the next step myself \u2014 write, review, check style, save, go live \u2014 instead of waiting for you (shift+tab cycles it; esc stops a run)",group:"setup"},{name:"model",usage:"/model [name|default]",desc:"Set the Claude model for d360-writer",group:"setup"},{name:"verbose",usage:"/verbose [on|off]",desc:"Show the raw transcript \u2014 every tool call with its file path and full result, instead of article-shaped cards with my bookkeeping collapsed (bare: toggle)",group:"setup"},{name:"logout",usage:"/logout [--all-projects]",desc:"Sign out of this project + clear its workspace selection (then /login to re-pick)",group:"setup"},{name:"allow-prod",usage:"/allow-prod",desc:"Authorize taking articles live on a production profile",group:"setup"},{name:"doctor",usage:"/doctor",desc:"Health-check: auth, profile, workspace, map, API, and a real agent turn",group:"setup"},{name:"logs",usage:"/logs",desc:"Show the most recent problem d360-writer recorded, and where the logs are \u2014 press c to copy it for the team",group:"setup"},{name:"update",usage:"/update",desc:"Update d360-writer \u2014 and d360-capture when installed \u2014 to the latest versions",group:"setup"},{name:"feedback",usage:"/feedback",desc:"Copy the repo's open d360-writer tool-feedback to your clipboard, to relay to the d360-writer team",group:"setup"},{name:"reset",usage:"/reset",desc:"[DANGER] Delete all d360-writer files (docs, config, screenshots); types repo name to confirm",group:"setup"},{name:"resume",usage:"/resume [name]",desc:"Resume a session (no arg lists them)",group:"session"},{name:"rename",usage:"/rename [name]",desc:"Name the session (no arg: suggest one)",group:"session"},{name:"clear",usage:"/clear",desc:"Clear the screen + reset the conversation (resumable)",group:"session"},{name:"help",usage:"/help",desc:"Show this help",group:"session"},{name:"exit",usage:"/exit",desc:"Quit",group:"session"}],rw={"/catch-up":"write up everything your builder flagged","/audit":"check the code for anything new or missing in the docs","/discover":"interview your builder so the docs are grounded in the real product","/pull":"pull edits + new articles from Document360","/push":"save your articles to Document360 as drafts","/publish":"take the signed-off drafts live to your readers","/write":"author the planned articles","/draft":"plan articles from PRDs + the code","/login":"sign in to Document360","/workspace":"choose the Document360 workspace","/project":"choose the Document360 project","/scope":"choose which folders back the docs","/genre":"set this repo's product type (picks the docs playbook)","/init":"set up this repo","/screenshot":"work the screenshot pipeline (author specs, take shots, draw the callouts)","/doctor":"run a health check","/logs":"show the last problem, and copy it for the team","/auto":"choose whether I run the next step myself or wait for you","/style-guide":"choose the writing rules your docs are held to","/lint":"check the docs against those rules (costs nothing)"};function sw(e){let t=e.trim().replace(/^\//,"").split(/\s+/)[0]??"",n=`/${t}`;if(rw[n])return rw[n];let o=Wo.find(r=>r.name===t);return o?o.desc:""}function iw(e){if(!e.startsWith("/"))return[];let t=e.slice(1).toLowerCase().split(/\s/)[0]??"";return Wo.filter(n=>n.name.startsWith(t))}function aw(e){let t=0,n="";for(let o of e){if(o==="["){t++;continue}if(o==="]"){t=Math.max(0,t-1);continue}t===0&&(n+=o)}return/<[^>]+>/.test(n)}var oP=new RegExp(`^\\s*/(?:${Wo.map(e=>e.name).join("|")})(?:\\s|$)`),lw=e=>oP.test(e),cw=e=>/^\s*(-{3,}|\*{3,}|_{3,})\s*$/.test(e),uw=e=>/^\s*\|?[\s:|-]*-[\s:|-]*\|?\s*$/.test(e)&&e.includes("-"),dw=e=>e.replace(/^\s*\|/,"").replace(/\|\s*$/,"").split("|").map(t=>t.trim());function rP(e){let t=e.replace(/\r/g,"").split(`
|
|
344
|
+
`},s=r(e),i=r(t);if(s===i)return null;let a=Math.max(20,n-10),l=_C("a","b",s,i,"","",{context:3}),c=0,f=0,g=[];l.hunks.forEach((T,C)=>{C>0&&g.push(Hy.gray(" \u2026"));let P=T.oldStart,S=T.newStart,j=[],G=[],D=()=>{for(let H=0;H<j.length;H++){let I=H<G.length?qy(j[H],G[H]):void 0;f++,g.push(`${UC}${Cd(P++)} - ${Vy(j[H],a,I)}${Gy}`)}for(let H=0;H<G.length;H++){let I=H<j.length?qy(j[H],G[H]):void 0;c++,g.push(`${FC}${Cd(S++)} + ${Vy(G[H],a,I)}${Gy}`)}j=[],G=[]};for(let H of T.lines){let I=H[0],oe=H.slice(1);I==="-"?j.push(oe):I==="+"?G.push(oe):(D(),g.push(Hy.gray(Cd(S))+" "+oe.slice(0,a)),P++,S++)}D()});let y=Math.max(1,o),x=g.slice(0,y);return{added:c,removed:f,lines:x,hidden:Math.max(0,g.length-y)}}import{select as Xy}from"@inquirer/prompts";import{applyPull as Qy,computeSyncStatus as HC,isPullWouldBlankError as Ky,localArticleTitle as GC,planPull as zy}from"document360-engine";import{listUnpullable as WC,resolveActiveProfile as BC}from"document360-engine";function al(e,t,n,o){let r=n.filter(a=>a.status==="conflict"&&a.path).map(a=>a.path),s=new Set(o),i=[];try{i=WC(e,BC(e,t).name).map(a=>a.path).filter(a=>s.has(a))}catch{}return[...new Set([...r,...i])].sort()}function ll(){return["Document360 will not give this article back as markdown \u2014 it is not on the Markdown (Editor 3.0) editor, so there is no portal copy to show you or to take.","Your local copy is intact, and publishing it over the portal settles this. Skipping leaves the article flagged, and it will be waiting again next time."]}function cl(){return"Publish your copy over the portal (settles it) \xB7 Skip?"}function Yy(){return"(l publish yours \xB7 s skip \u2014 anything else stops, keeping your answers so far)"}function ul(e){if(e.length===0)return[];let t=e.length,n=e.slice(0,3).join(", ")+(t>3?`, +${t-3} more`:"");return[`${t} article${t===1?"":"s"} stay${t===1?"s":""} flagged \u2014 Document360 cannot hand ${t===1?"its":"their"} markdown back: ${n}`,"Two ways out: run /resolve and press l to publish your copy over the portal, or ask your Document360 admin to move the article back to the Markdown (Editor 3.0) editor, then run /resolve again."]}function Pd(e){return`Cannot take the portal copy of ${e} \u2014 Document360 will not return it as markdown. Left as it was.`}ie();var dl=e=>e===1?"":"s";async function Td(e,t,n=1){console.log(""),console.log(`${Z("\u25CF")} ${Qe(t.title)} ${Te(`(${t.path})`)}`);for(let i of t.notes)console.log(V(` \u26A0 ${i}`));let o=_t(t.oldContent,t.newContent,Math.max(40,(process.stdout.columns??80)-1),_r);if(o){console.log(Te(` \u23BF local \u2192 portal: +${o.added} / -${o.removed} line${dl(o.added+o.removed)}`));for(let i of o.lines)console.log(i);o.hidden>0&&console.log(d(` \u2026 +${o.hidden} more diff lines`))}else console.log(d(" Local already matches the portal content \u2014 resolving just advances the sync base."));let r=n>1,s=await e.withPausedInput(()=>Xy({message:`Conflict \u2014 ${t.path}${r?` (${n} left)`:""}`,choices:[{name:"Keep local (publish \u2014 overwrites the portal edit)",value:"local"},{name:"Take portal (overwrites your local edits)",value:"portal"},{name:"Skip (leave it a conflict)",value:"skip"},...r?[{name:`Keep local for ALL ${n} remaining`,value:"local-all"},{name:`Take portal for ALL ${n} remaining`,value:"portal-all"},{name:`Skip ALL ${n} remaining`,value:"skip-all"}]:[]]})).catch(()=>"skip");return s==="portal"?(Qy({cwd:e.cwd,profileName:e.profileName},t),console.log(Q(" \u2713 Took the portal copy (local overwritten, sync base advanced).")),"took-portal"):s==="local"?"keep-local":s==="skip"?(console.log(d(" Skipped \u2014 still a conflict.")),"skipped"):s==="local-all"?"keep-local-all":s==="portal-all"?"took-portal-all":"skipped-all"}async function qC(e,t){console.log(""),console.log(`${Z("\u25CF")} ${Qe(GC(e.cwd,t)??t)} ${Te(`(${t})`)}`);for(let o of ll())console.log(V(` \u26A0 ${o}`));return await e.withPausedInput(()=>Xy({message:cl(),choices:[{name:"Publish your copy over the portal (settles it)",value:"local"},{name:"Skip (leave it a conflict)",value:"skip"}]})).catch(()=>"skip")==="local"?"keep-local":(console.log(d(" Skipped \u2014 still a conflict.")),"skipped")}async function Jy(e,t){let n=Je(t.cwd,t.profileName);if(!n.ok)return console.log(_(n.reason)),{kind:"continue"};let o=e.find(r=>!r.startsWith("--"))?.replace(/\\/g,"/");try{let r=await HC({cwd:t.cwd,profileName:t.profileName}),s=al(t.cwd,t.profileName,r.entries,Uo(r.entries).map(l=>l.path));if(o){if(!s.includes(o))return console.log(Q(`${o} isn't waiting on a decision \u2014 nothing to resolve.`)),{kind:"continue"};s=[o]}if(s.length===0)return console.log(Q("\u2713 No conflicts \u2014 local and Document360 agree on every tracked article.")),{kind:"continue"};let i=[],a=[];for(let[l,c]of s.entries()){let f;try{f=await zy({cwd:t.cwd,profileName:t.profileName,relPath:c})}catch(x){if(!Ky(x))throw x;await qC(t,c)==="keep-local"?i.push(c):a.push(c);continue}let g=await Td(t,f,s.length-l);if(g==="keep-local"&&i.push(c),!g.endsWith("-all"))continue;let y=s.slice(l);if(g==="keep-local-all")i.push(...y),console.log(d(` Keeping local for all ${y.length} \u2014 publishing them over the portal.`));else if(g==="took-portal-all"){let x=0;for(let T of y)try{Qy({cwd:t.cwd,profileName:t.profileName},await zy({cwd:t.cwd,profileName:t.profileName,relPath:T})),x++}catch(C){console.log(_(` \u2717 ${T}: ${C.message}`)),Ky(C)&&a.push(T)}console.log(Q(` \u2713 Took the portal copy of ${x} article${dl(x)} (local overwritten, sync base advanced).`))}else console.log(d(` Skipped all ${y.length} \u2014 still conflict${dl(y.length)}.`));break}for(let l of ul(a))console.log(V(`\u26A0 ${l}`));if(i.length>0)return console.log(d(`Publishing ${i.length} kept-local article${dl(i.length)} over the portal\u2026`)),await Jt(t,i,0,{allowUnpullable:!0})}catch(r){console.log(_(`Resolve failed: ${r.message}`))}return{kind:"continue"}}async function tw(e,t){let n=Je(t.cwd,t.profileName);if(!n.ok)return console.log(_(n.reason)),{kind:"continue"};let o=e.find(r=>r.startsWith("--"));try{if(o==="--status")return await ZC(t),{kind:"continue"};if(o==="--baseline")return await eP(t,e.filter(i=>i!=="--baseline")),{kind:"continue"};if(o==="--compare")return await tP(t),{kind:"continue"};if(o)return console.log(_(`Unknown option: ${o}`)),console.log(d("Usage: /pull pull portal edits + import new portal articles")),console.log(d(" /pull <path> pull one article (or resolve a conflict)")),console.log(d(" /pull --status drift report only (no writes)")),console.log(d(" /pull --baseline set a sync baseline for not-yet-compared articles")),{kind:"continue"};let r=e[0];if(r)return await nP(t,r),{kind:"continue"};console.log(d("Pulling changes from Document360\u2026"));let s=await Ed({cwd:t.cwd,profileName:t.profileName});for(let i of Nd(s))console.log(i.tone==="ok"?Q(i.text):i.tone==="warn"?V(i.text):d(i.text));console.log("")}catch(r){r instanceof JC?console.log(_(r.message)):console.log(_(`Pull failed: ${r.message}`))}return{kind:"continue"}}async function Ed(e){let t=await Zy({cwd:e.cwd,profileName:e.profileName}),n={pulled:[],imported:[],conflicts:[],toPublish:0,failed:[]};for(let o of t.entries)if(o.status==="remote-ahead"&&o.path)try{let r=await ew({cwd:e.cwd,profileName:e.profileName,relPath:o.path});Ad({cwd:e.cwd,profileName:e.profileName},r),n.pulled.push(o.path)}catch(r){n.failed.push({ref:o.path,error:r.message})}else if(o.status==="untracked-remote"&&o.articleId)try{let r=await KC({cwd:e.cwd,profileName:e.profileName,articleId:o.articleId,...o.remoteCategory?{categoryPath:o.remoteCategory.path}:{}});zC({cwd:e.cwd,profileName:e.profileName},r),n.imported.push({title:r.title,path:r.path})}catch(r){n.failed.push({ref:o.title??o.articleId,error:r.message})}else o.status==="conflict"&&o.path?n.conflicts.push(o.path):(o.status==="local-ahead"||o.status==="untracked-local"&&!o.link)&&o.path&&n.toPublish++;return n}var pl=(e,t=5)=>`${e.slice(0,t).join(", ")}${e.length>t?`, +${e.length-t} more`:""}`,Fo=e=>e===1?"":"s";function Nd(e){let t=[];if(e.pulled.length&&t.push({tone:"ok",text:`\u2713 Pulled ${e.pulled.length} portal edit${Fo(e.pulled.length)} into local: ${pl(e.pulled)}`}),e.imported.length&&t.push({tone:"ok",text:`\u2713 Imported ${e.imported.length} new portal article${Fo(e.imported.length)}: ${pl(e.imported.map(n=>n.path))}`}),e.toPublish&&t.push({tone:"info",text:`\u2139 ${e.toPublish} article${Fo(e.toPublish)} changed locally \u2014 /push saves them to Document360.`}),e.conflicts.length&&t.push({tone:"warn",text:`\u26A0 ${e.conflicts.length} article${Fo(e.conflicts.length)} changed both here and on Document360 \u2014 left untouched. Resolve them: /resolve (or /push <path> to keep yours, /pull <path> to take the portal's). (${pl(e.conflicts)})`}),e.failed.length){t.push({tone:"warn",text:`\u26A0 ${e.failed.length} item${Fo(e.failed.length)} could not be imported.`});let n=new Map;for(let o of e.failed){let r=o.error.replace(/^"[^"]*" could not be imported: /,"");n.set(r,[...n.get(r)??[],o.ref])}for(let[o,r]of n)t.push({tone:"warn",text:` ${o}`}),t.push({tone:"warn",text:` ${pl(r)}`})}return t.length===0&&t.push({tone:"ok",text:"\u2713 Already in sync with Document360 \u2014 nothing to pull."}),t}async function ZC(e){console.log(d("Checking Document360 for drift\u2026"));let t=Date.now(),n=await Zy({cwd:e.cwd,profileName:e.profileName});for(let o of il(n,Date.now()-t))console.log(o);console.log("")}async function eP(e,t){let n=t[0]&&t[0]!=="--all"?t[0].replace(/\\/g,"/"):void 0;console.log(d(n?`Setting the sync baseline for ${n}\u2026`:"Setting a sync baseline for tracked-but-not-yet-compared articles\u2026"));let o=await XC({cwd:e.cwd,profileName:e.profileName,relPaths:n?[n]:void 0}),r=n?{rebaselined:[],skipped:[]}:await QC({cwd:e.cwd,profileName:e.profileName});if(o.baselined.length===0&&o.skipped.length===0&&r.rebaselined.length===0){console.log(Q("\u2713 Nothing to baseline \u2014 every tracked article already has a sync baseline."));return}if(o.baselined.length>0&&console.log(Q(`\u2713 Recorded ${o.baselined.length} article(s) as matching Document360 \u2014 they read in-sync until either side changes.`)),r.rebaselined.length>0&&console.log(Q(`\u2713 Refreshed the metadata baseline for ${r.rebaselined.length} article(s) \u2014 cleared false "metadata changed" drift.`)),o.skipped.length>0){console.log(V(`${o.skipped.length} skipped:`));for(let s of o.skipped)console.log(d(` - ${s.path} (${s.reason})`))}}async function tP(e){console.log(d("Comparing the manifest against the per-article path (read-only)\u2026"));let t=await YC({cwd:e.cwd,profileName:e.profileName});console.log(` Legacy listing: ${Qe(String(t.legacyCount))} \xB7 Manifest: ${Qe(String(t.manifestCount))} \xB7 in both: ${t.inBoth}`),t.onlyInLegacy.length===0&&t.onlyInManifest.length===0&&t.mappedMissingFromManifest.length===0?console.log(Q("\u2713 Parity \u2014 the manifest sees exactly the same articles. Safe to make it the default sync source.")):(t.mappedMissingFromManifest.length&&console.log(_(`\u2717 ${t.mappedMissingFromManifest.length} MAPPED article(s) missing from the manifest: ${t.mappedMissingFromManifest.slice(0,5).join(", ")}`)),t.onlyInLegacy.length&&console.log(V(` ${t.onlyInLegacy.length} only in the legacy listing: ${t.onlyInLegacy.slice(0,5).join(", ")}`)),t.onlyInManifest.length&&console.log(V(` ${t.onlyInManifest.length} only in the manifest: ${t.onlyInManifest.slice(0,5).join(", ")}`)),console.log(d(" Send this output \u2014 a mismatch pinpoints where the manifest differs before we flip."))),console.log("")}async function nP(e,t){let n=t.replace(/\\/g,"/"),o=await ew({cwd:e.cwd,profileName:e.profileName,relPath:n});if(o.overwritesLocalChanges){await Td(e,o)==="keep-local"&&(console.log(d(`Publishing ${o.path} over the portal\u2026`)),await Jt(e,[o.path]));return}console.log(""),console.log(`${Z("\u25CF")} ${Qe(o.title)} ${Te(`(${o.path})`)}`);for(let i of o.notes)console.log(V(` \u26A0 ${i}`));let r=_t(o.oldContent,o.newContent,Math.max(40,(process.stdout.columns??80)-1),_r);if(!r){console.log(d(" Local file already matches the remote content \u2014 advancing the sync base only.")),Ad({cwd:e.cwd,profileName:e.profileName},o);return}console.log(Te(` \u23BF Added ${r.added} line${Fo(r.added)}, removed ${r.removed} line${Fo(r.removed)}`));for(let i of r.lines)console.log(i);if(r.hidden>0&&console.log(d(` \u2026 +${r.hidden} more diff lines`)),!await e.withPausedInput(()=>VC({message:`Write ${o.path}?`,default:!o.overwritesLocalChanges}))){console.log(d(" Skipped."));return}Ad({cwd:e.cwd,profileName:e.profileName},o),console.log(Q(` \u2713 Pulled ${o.path} (sync base advanced).`))}ie();import{statSync as hP}from"node:fs";import{resolve as fP}from"node:path";import{estimateBulkCost as mP,planPartitions as gP,readProjectConfig as yP,resolveModelForOperation as wP,runPartitioned as bP,trackedArticlePaths as kP}from"document360-engine";function Xn(e,t){return e.aborted?null:e.blocked?[`\u2717 ${t} didn\u2019t start \u2014 ${e.blocked}.`,"Nothing was changed. Fix that, then re-run."]:e.allEmpty?[`\u2717 ${t} did nothing \u2014 every batch produced no output, so no agent actually ran.`,"Run /doctor \u2014 it runs a real turn with your skills and tools and reports what failed. /logs shows Claude Code\u2019s own error, which was recorded either way, and offers to copy it.","A Claude usage or session limit also does this, and it ends every turn instantly \u2014 if you have just hit one, wait for the reset and re-run."]:null}function nw(e){return!e.bailedEmpty||!e.notRun?null:`\u26A0 Stopped early \u2014 ${e.notRun} batch${e.notRun===1?" was":"es were"} not attempted, because two in a row produced no output (that is systemic, not this work: usually a Claude usage limit). Re-run to finish the rest; anything already done is skipped.`}var Mr=class{activeMs=0;last=0;timer=null;startedAt=0;stopped=null;start(t=Date.now()){this.timer||(this.startedAt=t,this.last=t,this.timer=setInterval(()=>this.tick(),1e3),this.timer.unref?.())}tick(t=Date.now()){let n=t-this.last;this.last=t,!(n<=0)&&(this.activeMs+=Math.min(n,5e3))}stop(t=Date.now()){return this.stopped?this.stopped:(this.timer&&(clearInterval(this.timer),this.timer=null),this.tick(t),this.stopped={activeSeconds:Math.max(0,Math.round(this.activeMs/1e3)),elapsedSeconds:Math.max(0,Math.round((t-this.startedAt)/1e3))},this.stopped)}};function hl(e,t){let n=t-e;return n>=300&&n>=t*.2}function Mt(e,t,n,o=Date.now(),r){let s=t.reduce((a,l)=>a+(l.inputTokens??0),0),i=t.reduce((a,l)=>a+(l.cacheReadTokens??0),0);return{seconds:Math.max(0,Math.round((o-e)/1e3)),outputTokens:t.reduce((a,l)=>a+l.outputTokens,0),inputTokens:s,...s>0?{cachedPct:Math.round(i/s*100)}:{},...r!==void 0?{activeSeconds:r}:{},costUsd:n}}function fl(e){return e.activeSeconds!==void 0&&hl(e.activeSeconds,e.seconds)?`${Dd(e.seconds)} elapsed \xB7 ${Dd(e.activeSeconds)} working`:Dd(e.seconds)}var ow=e=>e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(1)}k`:`${e}`,Dd=e=>{if(e<60)return`${e}s`;let t=Math.floor(e/60);return t<60?`${t}m ${e%60}s`:`${Math.floor(t/60)}h ${t%60}m`};function un(e){let t=e.cachedPct!==void 0?`, ${e.cachedPct}% cached`:"",n=e.costUsd>0?`, this turn ${e.costUsd<.01?e.costUsd.toFixed(4):e.costUsd.toFixed(2)}`:"";return` (took ${fl(e)}, ${ow(e.inputTokens)}\u2192${ow(e.outputTokens)} tokens${t}${n})`}import ve from"picocolors";import Od from"wrap-ansi";import _d from"string-width";ie();import Qn from"picocolors";import gl from"wrap-ansi";import ml from"string-width";var Wo=[{name:"init",usage:"/init",desc:"Pick an environment, scaffold .d360-writer/config.json, and set up the builder hand-off (prints the one-paste prompt)",group:"start"},{name:"login",usage:"/login",desc:"Sign in to Document360 (browser)",group:"start"},{name:"project",usage:"/project [name]",desc:"Choose the Document360 project (picker; lists all you can access)",group:"start"},{name:"devhints",usage:"/devhints",desc:"Re-show/refresh the builder hand-off prompt (set up by /init; run anytime to hand your repo agent the latest)",group:"start"},{name:"scope",usage:"/scope",desc:"Choose which repo folders back the docs (monorepos)",group:"start"},{name:"genre",usage:"/genre [web|cli|service|library|auto]",desc:"Set/show the product genre \u2014 picks the docs craft playbook (usually auto-detected; set it if it guessed wrong)",group:"start"},{name:"mcp",usage:"/mcp [list|add|remove]",desc:"Connect MCP servers (Notion, Linear, \u2026)",group:"start"},{name:"discover",usage:"/discover",desc:"Interview your builder to understand the product (identity, mental model, misconceptions, positioning) \u2192 pins a product brief that grounds the docs; asks focused questions and loops until confident",group:"docs"},{name:"draft",usage:"/draft [<feature>]",desc:"Draft a feature's articles from its PRDs/specs in .d360-writer/input/ + the code",group:"docs"},{name:"document",usage:"/document [<what>] [--push]",desc:"Document a shipped change \u2014 the agent picks the action (new section/article/update); bare: act on builder requests. --push then saves the result to Document360 as drafts, which still needs an editorial review first",group:"docs"},{name:"write",usage:"/write [--preview|--scope <cat>|<path>] [--rewrite] [--concurrency N] [--skip-style]",desc:"Write the planned articles in parallel (starts immediately; --preview shows plan + cost first; --rewrite regenerates already-written articles; --concurrency default 3). Anything that would block publishing is corrected in one automatic pass before the run finishes; --skip-style leaves it to /lint",group:"docs"},{name:"screenshot",usage:"/screenshot [author|capture|annotate|verify|checklist|list] [id|path|--all|--failed|--blocked]",desc:"Screenshots, one family \u2014 bare: pick what to do; author: write capture specs; capture: take the shots (--all re-shoots everything; --failed re-runs the ones that did not work; --blocked re-runs the ones waiting on app data); annotate: draw the callouts on shots already taken, no browser needed (--all --force re-draws after a wording or style change); verify: judge the finished pictures and mark them verified (both run automatically after a capture); checklist: what to stage + dev test-id requests; list: status + what to stage",group:"docs"},{name:"preview",usage:"/preview [path|id]",desc:"Render an article (no arg: pick from tracked)",group:"docs"},{name:"push",usage:"/push [path|--all|--related <path>|[<category>] --screenshots] [--preview] [--skip-review]",desc:"Save your articles to Document360 as drafts \u2014 readers don't see them until /publish (no arg: pick; --all: every candidate; --screenshots: re-save articles with captured shots so images embed, optionally narrowed to one category; --related <path>: also save that article's related articles that were never saved; --preview <path>: show exactly what would be sent, and send nothing). Blocked until each article has had /review --fix \u2014 AI-written text gets an editor first; --skip-review overrides deliberately",group:"publish"},{name:"publish",usage:"/publish [path|<category>|--all] [--skip-review]",desc:"Take the drafts on Document360 LIVE to your readers (the portal's own Publish). Always lists what would become visible and asks first; no arg or a category narrows it. Needs an editorial review \u2014 /review --blocked --fix",group:"publish"},{name:"style-guide",usage:"/style-guide [list | use [<preset>] | keep | sweep [skip] | pull [<n>] | learn | compile | explain <rule>]",desc:"Choose the writing rules your docs are held to. Bare: what is active now; list: every pack you can turn on; use: switch packs, hold every article to them and apply the free text swaps (bare: pick from a list); keep: settle on the rules you have today; sweep: how much of your knowledge base still needs reading against a guide you switched to (skip stops me raising it); pull: bring your Document360 project's own style guide down; learn: turn what your reviewers keep asking for into standing rules; compile: turn its mechanical statements into checks; explain: what one rule means and how to silence it",group:"docs"},{name:"lint",usage:"/lint [<category|path>] [--blocked] [--fix] [--full] [--baselined]",desc:"Lint the docs against the house rules: leaked internals, invented commands, stale facts, AI-tells, em dashes, callout shape (deterministic, no tokens). Every finding names the rule that produced it. Scope to a category/path; --blocked narrows to the errors that block publishing and leaves warnings and suggestions alone; --fix has the agent correct them in place; --full lists every finding instead of a capped summary; --baselined shows the issues that were already in your articles when you chose this style guide, which never block publishing",group:"docs"},{name:"review",usage:"/review [<category|path> | --all | --blocked | --stale] [--fix]",desc:"Independent senior-editor review: style guide, terminology/voice, grammar/clarity, page structure, improvement suggestions. A whole-knowledge-base run also gets one editor on the SHAPE of the docs \u2014 reading order, categories that have grown too big, articles filed in the wrong place, names that no longer fit. Bare: pick a category or everything; --blocked reads only the articles publishing is blocked on; --stale reads the ones the current editor has not read yet, so running it twice costs nothing; --all reviews the whole KB; --fix applies the corrections in place",group:"docs"},{name:"reorder",usage:"/reorder [<category> | --all]",desc:"Reorder articles within a category to the best reading order (renumbers NN- prefixes, fixes references + map); bare: pick a category or everything; new order syncs to Document360 on next /push",group:"docs"},{name:"restructure",usage:"/restructure [<what to change>]",desc:"Reshape the knowledge base: move an article to a different category, split one that has grown too big, merge two, rename one, or nest one inside another. A category left empty by a merge is hidden from readers, never deleted. Bare: reviews how the docs are organized and reports what should change, without touching anything; the changes reach Document360 on the next /push",group:"docs"},{name:"structure",usage:"/structure",desc:"Show the documentation reading order (categories \u2192 articles) as it will appear on Document360",group:"docs"},{name:"status",usage:"/status [--all]",desc:'What stands between your work and your readers, before you spend anything: which articles no editor has read, which break a style rule your config makes blocking, which would show "[Screenshot: \u2026]" text to a reader, which are in conflict with the portal, and which are saved as drafts readers cannot see yet. Each group names the one command that clears it. Reads local files and the last sync \u2014 no network, no tokens. --all lists every article gate by gate',group:"publish"},{name:"retire",usage:"/retire [<path>] [--yes]",desc:"Take an article off your readers on Document360 \u2014 it stops being visible, and nothing offers to publish it again. Nothing is deleted: the article, its version history and every reviewer comment stay. Bare: lists what is worth retiring (articles on Document360 that are no longer in your docs folder). /push puts one back \u2014 reviving is a publish like any other",group:"publish"},{name:"untrack",usage:"/untrack [<path>] [--undo] [--list] [--yes]",desc:"Disown a page that was imported from Document360 but is not one of your docs. /catch-up and /pull import every article they find in your categories; this records the ones you do not want, so they are never imported again \u2014 and the decision travels with the repo, so a teammate\u2019s clone inherits it. Nothing on Document360 changes and nothing is deleted: the markdown moves to .d360-writer/declined/. Bare: lists what this repo has declined. --undo puts one back",group:"publish"},{name:"reuse",usage:'/reuse [pull | add <variable|snippet> <name> "<value>" | audit [<category>] [--create <n\u2026>]]',desc:"The terms, values and blocks your project already maintains on Document360, so your articles reference them instead of repeating them. A variable holds a value like your product name in one place \u2014 change it there and every article changes; type it out by hand and /lint flags it. A snippet does the same for a whole block, and the writer references yours by name instead of writing its own version. A term is defined once and every article is checked for explaining it the first time it appears. Reading is free; pull refreshes from Document360, add creates a new one there, and audit reads every article to work out what belongs in one place",group:"docs"},{name:"eval",usage:"/eval [<category>] [--all]",desc:"How does your whole knowledge base score? Reads every article and reports what no single-article check can see: internal links written as portal URLs, links pointing at headings that no longer exist, link text that names a different article, the command summary line appearing in several shapes, two names for the same section, and how many style findings were accepted as exempt when you chose your style guide. Costs nothing and calls nothing",group:"docs"},{name:"terms",usage:'/terms [<category>] [--all] [--approve "<term>"]',desc:"Is your knowledge base calling the same thing by the same name? Reads every article and reports terms spelled more than one way, short names that may be the long name abbreviated, and article titles out of step with the rest of their kind \u2014 the consistency an editor reading three articles at a time cannot see. Costs nothing and calls nothing. --approve records a spelling as the approved one, so every later run enforces it",group:"docs"},{name:"roadmap",usage:"/roadmap [--all | done [step]]",desc:"Show the docs campaign (the multi-session plan the agent maintains), grouped by the part of the job \u2014 progress + next step; `--all` also lists the steps a later pass replaced; `done` marks the next open step finished (add words from a title to pick another)",group:"docs"},{name:"audit",usage:"/audit [--auto on|off] [--decline <path>]",desc:"Gap analysis: code vs docs vs Document360 (what changed); --auto runs it at launch when code changed; --decline settles a folder or file that needs no article",group:"publish"},{name:"inbox",usage:"/inbox",desc:"Act on messages from the repo's coding agent (requests to document, answers to your questions)",group:"publish"},{name:"ask",usage:"/ask <question>",desc:"Ask your developers something the code cannot tell you: why a feature exists, when to use it, whether it can be undone",group:"publish"},{name:"share",usage:"/share [status|on|off]",desc:"Share your docs work with the team: get your builder's latest, then save + send yours (runs automatically; `off` leaves it to your coding agent)",group:"publish"},{name:"catch-up",usage:"/catch-up [--push] [--include-unfinished]",desc:"Process ALL pending work (builder hints + requests + Document360 portal drift) \u2192 write \u2192 save to Document360 as drafts. --push does that last step for you, which still needs an editorial review first. A note whose code your builder has not committed is left for later, and they are asked whether it is done; --include-unfinished writes those pages anyway. For code changes, that's /audit",group:"publish"},{name:"comments",usage:"/comments [<article>] [--scope <category>] [--answer] [--all] [--resolve] [--answered] [--full]",desc:"Reviewer comments left on passages of your articles in the Document360 portal \u2014 bare: a table by article, pick a number to read its comments in full, then answer or go back; --answer: fix, push the draft, reply (threads stay open unless you add --resolve); --full re-reads every article",group:"publish"},{name:"pull",usage:"/pull [<path> | --status | --baseline]",desc:"Bring Document360 down: pull portal edits + import NEW portal articles. --status: drift report only \xB7 --baseline: set a sync base \xB7 <path>: one article / resolve a conflict",group:"publish"},{name:"resolve",usage:"/resolve [<path>]",desc:"Resolve conflicts (edited both here and on the portal): shows the diff, then keep local (push), take portal (pull), or skip \u2014 per article, or one answer for all remaining. Bare: walk them all",group:"publish"},{name:"convert",usage:"/convert [--scope <folder>] [--run]",desc:"Convert tracked articles to DFM (one-off legacy migration)",group:"publish"},{name:"profile",usage:"/profile [name|add <name> [env]]",desc:"Where this repo publishes \u2014 switch, or connect another environment (picker; s = session)",group:"setup"},{name:"workspace",usage:"/workspace [name]",desc:"Switch the Document360 workspace (picker)",group:"setup"},{name:"auto",usage:"/auto [on|off|status] [--budget <amount>]",desc:"Auto or manual: in auto I run the next step myself \u2014 write, review, check style, save, go live \u2014 instead of waiting for you (shift+tab cycles it; esc stops a run)",group:"setup"},{name:"model",usage:"/model [name|default]",desc:"Set the Claude model for d360-writer",group:"setup"},{name:"verbose",usage:"/verbose [on|off]",desc:"Show the raw transcript \u2014 every tool call with its file path and full result, instead of article-shaped cards with my bookkeeping collapsed (bare: toggle)",group:"setup"},{name:"logout",usage:"/logout [--all-projects]",desc:"Sign out of this project + clear its workspace selection (then /login to re-pick)",group:"setup"},{name:"allow-prod",usage:"/allow-prod",desc:"Authorize taking articles live on a production profile",group:"setup"},{name:"doctor",usage:"/doctor",desc:"Health-check: auth, profile, workspace, map, API, and a real agent turn",group:"setup"},{name:"logs",usage:"/logs",desc:"Show the most recent problem d360-writer recorded, and where the logs are \u2014 press c to copy it for the team",group:"setup"},{name:"update",usage:"/update",desc:"Update d360-writer \u2014 and d360-capture when installed \u2014 to the latest versions",group:"setup"},{name:"feedback",usage:"/feedback",desc:"Copy the repo's open d360-writer tool-feedback to your clipboard, to relay to the d360-writer team",group:"setup"},{name:"reset",usage:"/reset",desc:"[DANGER] Delete all d360-writer files (docs, config, screenshots); types repo name to confirm",group:"setup"},{name:"resume",usage:"/resume [name]",desc:"Resume a session (no arg lists them)",group:"session"},{name:"rename",usage:"/rename [name]",desc:"Name the session (no arg: suggest one)",group:"session"},{name:"clear",usage:"/clear",desc:"Clear the screen + reset the conversation (resumable)",group:"session"},{name:"help",usage:"/help",desc:"Show this help",group:"session"},{name:"exit",usage:"/exit",desc:"Quit",group:"session"}],rw={"/catch-up":"write up everything your builder flagged","/audit":"check the code for anything new or missing in the docs","/discover":"interview your builder so the docs are grounded in the real product","/pull":"pull edits + new articles from Document360","/push":"save your articles to Document360 as drafts","/publish":"take the signed-off drafts live to your readers","/write":"author the planned articles","/draft":"plan articles from PRDs + the code","/login":"sign in to Document360","/workspace":"choose the Document360 workspace","/project":"choose the Document360 project","/scope":"choose which folders back the docs","/genre":"set this repo's product type (picks the docs playbook)","/init":"set up this repo","/screenshot":"work the screenshot pipeline (author specs, take shots, draw the callouts)","/doctor":"run a health check","/logs":"show the last problem, and copy it for the team","/auto":"choose whether I run the next step myself or wait for you","/style-guide":"choose the writing rules your docs are held to","/lint":"check the docs against those rules (costs nothing)"};function sw(e){let t=e.trim().replace(/^\//,"").split(/\s+/)[0]??"",n=`/${t}`;if(rw[n])return rw[n];let o=Wo.find(r=>r.name===t);return o?o.desc:""}function iw(e){if(!e.startsWith("/"))return[];let t=e.slice(1).toLowerCase().split(/\s/)[0]??"";return Wo.filter(n=>n.name.startsWith(t))}function aw(e){let t=0,n="";for(let o of e){if(o==="["){t++;continue}if(o==="]"){t=Math.max(0,t-1);continue}t===0&&(n+=o)}return/<[^>]+>/.test(n)}var oP=new RegExp(`^\\s*/(?:${Wo.map(e=>e.name).join("|")})(?:\\s|$)`),lw=e=>oP.test(e),cw=e=>/^\s*(-{3,}|\*{3,}|_{3,})\s*$/.test(e),uw=e=>/^\s*\|?[\s:|-]*-[\s:|-]*\|?\s*$/.test(e)&&e.includes("-"),dw=e=>e.replace(/^\s*\|/,"").replace(/\|\s*$/,"").split("|").map(t=>t.trim());function rP(e){let t=e.replace(/\r/g,"").split(`
|
|
345
345
|
`),n=[],o=0;for(;o<t.length;){let r=t[o];if(/^\s*```/.test(r)){let a=[];for(o++;o<t.length&&!/^\s*```/.test(t[o]);)a.push(t[o++]);o++,n.push({kind:"code",lines:a});continue}if(cw(r)){n.push({kind:"hr"}),o++;continue}if(r.includes("|")&&o+1<t.length&&uw(t[o+1])){let a=dw(r);o+=2;let l=[];for(;o<t.length&&t[o].includes("|")&&t[o].trim()!=="";)l.push(dw(t[o++]));n.push({kind:"table",header:a,rows:l});continue}let s=r.match(/^(#{1,6})\s+(.*)$/);if(s){n.push({kind:"heading",level:s[1].length,text:s[2]}),o++;continue}if(/^\s*([-*]|\d+\.)\s+/.test(r)){let a=[];for(;o<t.length&&/^\s*([-*]|\d+\.)\s+/.test(t[o]);){let l=t[o++];a.push({ordered:/^\s*\d+\.\s+/.test(l),text:l.replace(/^\s*([-*]|\d+\.)\s+/,"")})}n.push({kind:"list",items:a});continue}if(r.trim()===""){o++;continue}if(lw(r)){n.push({kind:"para",text:r.trim()}),o++;continue}let i=[];for(;o<t.length&&t[o].trim()!==""&&!/^\s*```/.test(t[o])&&!/^(#{1,6})\s/.test(t[o])&&!/^\s*([-*]|\d+\.)\s+/.test(t[o])&&!lw(t[o])&&!cw(t[o])&&!(t[o].includes("|")&&o+1<t.length&&uw(t[o+1]));)i.push(t[o++]);n.push({kind:"para",text:i.join(" ")})}return n}function jd(e){return e.replace(/(\*\*[^*]+\*\*|`[^`]+`|\*[^*]+\*)/g,t=>t.startsWith("**")?Qn.bold(t.slice(2,-2)):t.startsWith("`")?Z(t.slice(1,-1)):Qn.italic(t.slice(1,-1)))}var sP=(e,t)=>e+" ".repeat(Math.max(0,t-ml(e)));function Ks(e,t,n,o={}){return pw(e,t,n,o)}function pw(e,t,n,o={}){let r=e.length,s=P=>Math.max(0,...P.split(`
|
|
346
346
|
`).map(S=>ml(S))),i=e.map((P,S)=>Math.max(ml(P),...t.map(j=>s(j[S]??"")))),a=Math.max(24,n),l=3*r+1,c=[...i],f=()=>c.reduce((P,S)=>P+S,0)+l,g=0;for(;f()>a&&g++<1e4;){let P=-1,S=6;for(let j=0;j<r;j++)c[j]>S&&(S=c[j],P=j);if(P===-1)break;c[P]-=1}let y=(P,S,j)=>Qn.gray(P+c.map(G=>"\u2500".repeat(G+2)).join(S)+j),x=Qn.gray("\u2502"),T=(P,S)=>{let j=c.map((I,oe)=>{let ae=P[oe]??"",ke=S?Qn.bold(ae):o.plain?ae:jd(ae);return ae.includes(`
|
|
347
347
|
`)?ke.split(`
|
|
@@ -355,18 +355,18 @@ ${s}${l}`).join("")}).join(`
|
|
|
355
355
|
`)}case"code":return e.lines.map(n=>Qn.gray(" "+n)).join(`
|
|
356
356
|
`);case"table":return pw(e.header,e.rows,t)}}function Id(e,t){let n=Math.max(20,t);return rP(e).map(o=>iP(o,n)).join(`
|
|
357
357
|
|
|
358
|
-
`)}ie();import aP from"picocolors";var lP=[127,86,217],cP=[22,38,43],hw={T:lP,E:cP},zs=["..TTTTTT....","..TTTTTTT...","..TTTTTTTT..","..TTETTETT..","..TTETTETT..","..TTTTTTTT..","..TTTTTTT...","..TTTTTT....","...T..T....."],Ld=([e,t,n])=>`\x1B[38;2;${e};${t};${n}m`,uP=([e,t,n])=>`\x1B[48;2;${e};${t};${n}m`;function fw(){if(!aP.isColorSupported)return[];let e=zs[0].length,t=[];for(let n=0;n<zs.length;n+=2){let o="";for(let r=0;r<e;r++){let s=hw[zs[n][r]],i=n+1<zs.length?hw[zs[n+1][r]]:void 0;s&&i?o+=`${Ld(s)}${uP(i)}\u2580\x1B[49m\x1B[39m`:s?o+=`${Ld(s)}\u2580\x1B[39m`:i?o+=`${Ld(i)}\u2584\x1B[39m`:o+=" "}t.push(o)}return t}function Xs(e){let t=n=>n.toFixed(1).endsWith(".0")?n.toFixed(0):n.toFixed(1);return e<1e3?String(e):e<1e6?`${t(e/1e3)}k`:`${t(e/1e6)}M`}function Pn(e){return e<60?`${e}s`:`${Math.floor(e/60)}m ${e%60}s`}function gw(e,t,n){let o=e>0&&e<=n?e:t;return Math.max(0,Math.floor((n-o)/1e3))}function xt(e){return e<=0?"$0.00":e<.01?`$${e.toFixed(4)}`:`$${e.toFixed(2)}`}function Fr(e){return e<1e3?`${e} tokens`:e<1e6?`${(e/1e3).toFixed(1)}k tokens`:`${(e/1e6).toFixed(2)}M tokens`}var Ur=(e,t)=>` ${e.padEnd(13)}${t}`;function dP(e,t){let n=[As("\u270E document360-writer")+
|
|
358
|
+
`)}ie();import aP from"picocolors";var lP=[127,86,217],cP=[22,38,43],hw={T:lP,E:cP},zs=["..TTTTTT....","..TTTTTTT...","..TTTTTTTT..","..TTETTETT..","..TTETTETT..","..TTTTTTTT..","..TTTTTTT...","..TTTTTT....","...T..T....."],Ld=([e,t,n])=>`\x1B[38;2;${e};${t};${n}m`,uP=([e,t,n])=>`\x1B[48;2;${e};${t};${n}m`;function fw(){if(!aP.isColorSupported)return[];let e=zs[0].length,t=[];for(let n=0;n<zs.length;n+=2){let o="";for(let r=0;r<e;r++){let s=hw[zs[n][r]],i=n+1<zs.length?hw[zs[n+1][r]]:void 0;s&&i?o+=`${Ld(s)}${uP(i)}\u2580\x1B[49m\x1B[39m`:s?o+=`${Ld(s)}\u2580\x1B[39m`:i?o+=`${Ld(i)}\u2584\x1B[39m`:o+=" "}t.push(o)}return t}function Xs(e){let t=n=>n.toFixed(1).endsWith(".0")?n.toFixed(0):n.toFixed(1);return e<1e3?String(e):e<1e6?`${t(e/1e3)}k`:`${t(e/1e6)}M`}function Pn(e){return e<60?`${e}s`:`${Math.floor(e/60)}m ${e%60}s`}function gw(e,t,n){let o=e>0&&e<=n?e:t;return Math.max(0,Math.floor((n-o)/1e3))}function xt(e){return e<=0?"$0.00":e<.01?`$${e.toFixed(4)}`:`$${e.toFixed(2)}`}function Fr(e){return e<1e3?`${e} tokens`:e<1e6?`${(e/1e3).toFixed(1)}k tokens`:`${(e/1e6).toFixed(2)}M tokens`}var Ur=(e,t)=>` ${e.padEnd(13)}${t}`;function dP(e,t){let n=[As("\u270E document360-writer")+ve.gray(` v${e.version} \xB7 engine v${e.engineVersion}${e.captureVersion?` \xB7 capture v${e.captureVersion}`:""}`),ve.gray(" Reads your code, writes your docs."),"",Ur("Claude:",`${e.claude}${ve.gray(` \xB7 ${e.model}${e.modelSource?` (${e.modelSource})`:""}`)}`),Ur("Document360:",e.configured?e.loggedOut?ve.yellow("not logged in \u2014 run /login"):`${e.who??""}${e.sessionHint?ve.gray(` (${e.sessionHint})`):""}`:ve.yellow("not set up \u2014 run /init")),Ur("Profile:",e.configured?`${e.profile}${ve.gray(` (${e.apiUrl})`)}${e.prod?ve.bold(ve.yellow(" \u26A0 PRODUCTION")):""}`:ve.gray("\u2014 (run /init)")),Ur("Project:",e.configured&&e.workspace===null?`${e.project}${ve.yellow(" \xB7 no workspace yet")}`:`${e.project}${e.workspace?ve.gray(` \xB7 ${e.workspace}`):""}`),Ur("Mode:",ve.gray(e.mode)),Ur("cwd:",ve.gray(e.cwd))],o=fw();if(o.length===0)return n.join(`
|
|
359
359
|
`);let r=2,s=3,i=_d(o[0]);if(!(t>=r+i+s+Math.max(...n.map(f=>_d(f)))))return[...o.map(f=>" "+f),...n].join(`
|
|
360
360
|
`);let l=Math.max(0,Math.floor((n.length-o.length)/2)),c=[];for(let f=0;f<Math.max(o.length+l,n.length);f++){let g=o[f-l]??" ".repeat(i);c.push((" ".repeat(r)+g+" ".repeat(s)+(n[f]??"")).trimEnd())}return c.join(`
|
|
361
|
-
`)}var pP={error:
|
|
361
|
+
`)}var pP={error:ve.red,warn:ve.yellow,ok:ve.green,info:ve.gray},mw=9;function Md(e){return e.length>=mw?`${e} `:e.padEnd(mw)}function yl(e){return e.replace(/^(\s*• |)([^\x00-\x7F][\u{1F000}-\u{1FAFF}️]*) (?=\S)/gmu,"$1$2 ")}function Ud(e,t){let n=Math.max(20,t);switch(e.kind){case"banner":return dP(e.info,n);case"user":{let o="\x1B[48;2;42;42;46m",r="\x1B[49m",s=Math.max(10,n-4),i=50,a=e.text.split(`
|
|
362
362
|
`).flatMap(c=>Od(c,s,{hard:!0}).split(`
|
|
363
|
-
`)),l=Math.max(0,a.length-i);return l>0&&(a=[...a.slice(0,i),
|
|
363
|
+
`)),l=Math.max(0,a.length-i);return l>0&&(a=[...a.slice(0,i),ve.dim(`\u2026 +${l} more lines`)]),`
|
|
364
364
|
`+a.map((c,f)=>o+(f===0?Z(" \u276F "):" ")+c+" ".repeat(Math.max(0,s-_d(c))+1)+r).join(`
|
|
365
365
|
`)}case"assistant":return`
|
|
366
|
-
`+Id(e.text,n);case"tool":{let o=e.arg!==null?
|
|
367
|
-
`+Od(
|
|
368
|
-
`+Od(
|
|
369
|
-
`)}case"diff":{let o=i=>i===1?"":"s",s=[
|
|
366
|
+
`+Id(e.text,n);case"tool":{let o=e.arg!==null?ve.gray(`${e.sep}(${e.arg})`):"";return`
|
|
367
|
+
`+Od(ve.gray("\u25CF ")+e.title+o,n)}case"doc":{let o=[e.category,e.note].filter(s=>!!s).map(s=>ve.gray(` \xB7 ${s}`)),r=e.loud?ve.gray(" \xB7 ")+ve.bold(ve.yellow(e.loud)):"";return`
|
|
368
|
+
`+Od(ve.green("\u25CF ")+Z(ve.bold(Md(e.verb)))+ve.bold(e.title)+o.join("")+r,n)}case"tool-result":{let o=e.isError?ve.red:ve.gray,r=e.lines.map((s,i)=>o((i===0?" \u23BF ":" ")+s));return e.hidden>0&&r.push(ve.dim(` \u2026 +${e.hidden} lines`)),r.join(`
|
|
369
|
+
`)}case"diff":{let o=i=>i===1?"":"s",s=[ve.gray(` \u23BF Added ${e.added} line${o(e.added)}, removed ${e.removed} line${o(e.removed)}`),...e.lines];return e.hidden>0&&s.push(ve.dim(` \u2026 +${e.hidden} more diff lines`)),s.join(`
|
|
370
370
|
`)}case"link":return e.lines.map(o=>Z(Ns(` \u2B95 ${o}`))).join(`
|
|
371
371
|
`);case"preview":return`
|
|
372
372
|
`+As(`\u25A3 Preview \u2014 ${e.name}`)+`
|
|
@@ -374,7 +374,7 @@ ${s}${l}`).join("")}).join(`
|
|
|
374
374
|
`+Id(e.text,n);case"note":return`
|
|
375
375
|
`+pP[e.tone](Ns(yl(e.text)));case"block":return`
|
|
376
376
|
`+e.text;case"done":{let o=e.inTokens&&e.inTokens>0?` \xB7 ${Xs(e.inTokens)} in${e.cachedPct!=null?` (${e.cachedPct}% cached)`:""}`:"",r=e.activeSeconds!==void 0&&hl(e.activeSeconds,e.seconds)?`${Pn(e.seconds)} elapsed \xB7 ${Pn(e.activeSeconds)} working`:Pn(e.seconds),s=e.costUsd>0?` \xB7 this turn ${xt(e.costUsd)}`+(e.sessionCostUsd!==void 0&&e.sessionCostUsd>e.costUsd?` \xB7 session ${xt(e.sessionCostUsd)}`:""):"";return`
|
|
377
|
-
`+(e.ok?
|
|
377
|
+
`+(e.ok?ve.magenta("\u2736 "):ve.red("\u2736 "))+ve.gray(`Cooked for ${r} \xB7 ${Xs(e.tokens)} tokens out${o}${s}`)}}}function yw(e,t,n){let o=e.map(i=>Ud(i,t));if(n===void 0||n<=0)return o.join(`
|
|
378
378
|
`);let r=[],s=0;for(let i=o.length-1;i>=0;i--){let a=o[i].split(`
|
|
379
379
|
`).length;if(r.length>0&&s+a>n)break;r.unshift(o[i]),s+=a}return r.join(`
|
|
380
380
|
`)}var Fd=3;function Wd(e){let t,n=!1;for(let r=0;r<e.length;r++){let s=e[r];s==="--run"||s==="--yes"?n=!0:s==="--scope"?t=e[++r]:s?.startsWith("--scope=")&&(t=s.slice(8))}return{scope:t?.replace(/\\/g,"/").replace(/\/+$/,"")||void 0,run:n}}function Bd(e,t){return t?e.filter(n=>{let o=n.replace(/\\/g,"/");return o===t||o.startsWith(`${t}/`)}):e}function Hd(e,t){return t.map(n=>{let o=0;try{o=hP(fP(e,n)).size}catch{o=0}return{path:n,bytes:o}})}function Gd(e){return`Convert each of these articles to canonical Document360 Flavored Markdown (DFM) and re-publish each as a DRAFT. This is a mechanical syntax conversion, not a rewrite, so be efficient:
|
|
@@ -496,7 +496,7 @@ ${Ce}`)}catch{}let lo=await pf(l,ze,r,c,e,n);if(oe){let Ce=oe==="review"?Yo(e,ae
|
|
|
496
496
|
`),truncated:i>0||l>0}}}return{text:e,truncated:!1}}function Pv(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 Tv(e){let t=!1,n=0,o=0;for(let r of e.split(`
|
|
497
497
|
`)){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 Zo,jsx as K,jsxs as me}from"react/jsx-runtime";var d3={project:".d360-writer/config.json",user:"/model",env:"ANTHROPIC_MODEL",auto:"auto \u2014 right-sized for docs \xB7 /model to change"},Qv=` 1. /init \u2014 pick your Document360 environment & scaffold config
|
|
498
498
|
2. /login \u2014 sign in & pick your Document360 project (workspace auto-selected)
|
|
499
|
-
3. I'll hand you a one-paste prompt for your builder \u2014 then just ask me to write the docs`;function p3(e,t,n,o){let r=Nf(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)",i=Df(e),a=$e(e),l=(a?.docsDir??jf).replace(/\/+$/,""),c=a?.mode==="engineer"?"engineer \xB7 full source access (dogfooding)":`writer \xB7 edits limited to ${l}/ + config`,f={version:t,engineVersion:n0,captureVersion:vt(),claude:s,model:i.model,modelSource:d3[i.source],who:null,sessionHint:null,profile:"\u2014",apiUrl:"\u2014",project:"\u2014",cwd:e,prod:!1,loggedOut:!0,configured:a!==null,mode:c};if(a===null)return f;try{let g=dt(e,o);f.profile=g.name,f.apiUrl=g.connection.apiUrl,f.prod=g.production,f.project=g.project.projectName??g.project.projectId??"(chosen at login)";let y=ao(g.name,g.project.projectId);if(y){let x={...us(y.idToken)??{},...us(y.accessToken)??{}},T=x.email??x.preferred_username??"signed in";er(y)?y.refreshToken&&(f.who=T,f.loggedOut=!1,f.sessionHint="session expired \u2014 refreshing\u2026"):(f.who=T,f.loggedOut=!1,f.sessionHint=`session valid until ${new Date(y.expiresAt).toLocaleString(void 0,{hour:"2-digit",minute:"2-digit",day:"2-digit",month:"short"})}`)}}catch{}return f}var h3=e=>e.mode==="local-only"?e.path:e.plan.path;function f3(e,t){try{let n=dt(e,t),o=ao(n.name,n.project.projectId);if(!o)return{text:`profile "${n.name}" \u2014 not logged in (/login)`,prod:n.production};let r={...us(o.idToken)??{},...us(o.accessToken)??{}},s=r.email??r.preferred_username??"signed in";return er(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 Jv=["Drafting","Composing","Outlining","Researching","Documenting","Structuring","Polishing","Synthesizing","Curating","Distilling","Weaving","Wrangling","Pondering"],ds=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],m3="Ask me to write or update an article\u2026",Zv=2e3,g3=new Set(Wo.map(e=>e.name));function cs({ch:e,dim:t}){let[n,o]=pe(!0);return lt(()=>{let r=setInterval(()=>o(s=>!s),530);return()=>clearInterval(r)},[]),K(z,{inverse:n,color:t&&!n?"gray":void 0,children:e})}function In({rows:e,cursor:t,cap:n,render:o}){let r=yc(e.length,t,n);return me(Zo,{children:[r.above>0?K(z,{color:"gray",children:` \u22EF ${r.above} more above`}):null,e.slice(r.start,r.end).map((s,i)=>o(s,r.start+i,r.start+i===t)),r.below>0?K(z,{color:"gray",children:` \u22EF ${r.below} more below`}):null]})}var e0=/^mcp__document360__d360_(create_article|update_article|fork_article|publish_article)$/;function t0(e){let t=e.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n/);return t?e.slice(t[0].length):e}var y3=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function w3({startTime:e,chars:t}){let[n,o]=pe(0),r=ce(Date.now());lt(()=>{let c=setInterval(()=>o(f=>f+1),120);return()=>clearInterval(c)},[]);let s=ds[n%ds.length],i=Jv[Math.floor(n/16)%Jv.length],a=gw(e,r.current,Date.now()),l=Math.round(t/4);return me(Ie,{children:[K(z,{color:ue,children:` ${s} ${i}\u2026 `}),K(z,{color:"gray",children:`(${Pn(a)} \xB7 ~${l} tokens \xB7 esc to interrupt)`})]})}function b3({step:e,cmd:t,startedAt:n,spentUsd:o,budgetUsd:r}){let[,s]=pe(0);lt(()=>{let l=setInterval(()=>s(c=>c+1),1e3);return()=>clearInterval(l)},[]);let i=Math.max(0,Math.round((Date.now()-n)/1e3)),a=o>0?` \xB7 ${xt(o)} of ${Ke(r)}`:` \xB7 limit ${Ke(r)}`;return me(Ie,{flexDirection:"column",children:[me(Ie,{children:[K(z,{color:ue,children:` \u23F5\u23F5 auto \xB7 step ${e} \xB7 `}),K(z,{color:ue,bold:!0,children:t})]}),K(z,{color:"gray",children:` ${Pn(i)}${a} \xB7 esc stops`})]})}function k3({text:e}){let[t,n]=pe(0);return lt(()=>{let o=setInterval(()=>n(r=>r+1),120);return()=>clearInterval(o)},[]),K(Ie,{paddingX:1,children:K(z,{color:ue,children:`${ds[t%ds.length]} ${e}`})})}var $3=12e4;function v3({p:e}){let[t,n]=pe(0);lt(()=>{let S=setInterval(()=>n(j=>j+1),150);return()=>clearInterval(S)},[]);let o=ds[t%ds.length],r=Math.floor((Date.now()-e.startedAt)/1e3),s=Math.floor((Date.now()-e.lastAt)/1e3),i=Math.round(e.chars/4),a=Date.now()-e.lastAt>=$3,l=e.verb??"Converting",c=e.unit??"article",f=e.rows.reduce((S,j)=>S+j.items,0),g=e.rows.reduce((S,j)=>S+(j.status==="done"?j.items:j.itemsDone??0),0),y=l.toLowerCase(),x=e.rows,T=0;if(x.length>Rc){let S=x.findIndex(G=>G.status==="active"||G.status==="pending"),j=Math.max(0,Math.min(S-2,x.length-Rc));T=x.slice(0,j).filter(G=>G.status==="done").length,x=x.slice(j,j+Rc)}let C=Math.min(40,Math.max(...x.map(S=>vf(S.label).length),0)),P=S=>S==="failed"?"red":S==="active"?ue:void 0;return me(Ie,{flexDirection:"column",children:[me(Ie,{children:[K(z,{color:ue,children:` ${o} ${l} ${f} ${c}${f===1?"":"s"} `}),K(z,{color:"gray",children:e.agentless?`\xB7 ${g}/${f} ${c}${f===1?"":"s"} \xB7 ${Pn(r)} \xB7 esc to stop`:`\xB7 ${g}/${f} ${c}${f===1?"":"s"} \xB7 ${e.done}/${e.total} batch${e.total===1?"":"es"} \xB7 ${e.tools} tool call${e.tools===1?"":"s"} \xB7 ~${i} tokens \xB7 ${Pn(r)} \xB7 esc to stop`})]}),T>0&&K(z,{dimColor:!0,children:` \u2714 +${T} done`}),x.map(S=>me(z,{color:P(S.status),dimColor:S.status==="done"||S.status==="pending",children:[` ${hv[S.status]} ${vf(S.label).padEnd(C)}`,S.status==="active"&&S.itemsDone!==void 0?` (${S.itemsDone}/${S.items} ${c}${S.items===1?"":"s"})`:S.status!=="pending"?` (${S.items} ${c}${S.items===1?"":"s"})`:"",S.status==="active"?` \u2190 ${y}\u2026`:""]},S.label)),a&&K(z,{color:"yellow",children:` \u26A0 no activity for ${Pn(s)} \u2014 press esc to stop if it's wedged`}),a&&e.stalledNote?K(z,{color:"yellow",children:` ${e.stalledNote}`}):null]})}var x3=10800*1e3;function o0({cwd:e,auth:t,profileName:n,version:o}){let{exit:r}=GL(),[s,i]=pe(n),[a,l]=pe(null),[c,f]=pe({text:"",pos:0}),g=c.text,y=se(p=>{f(m=>{let u=typeof p=="function"?p(m.text):p;return{text:u,pos:u.length}})},[]),x=se(p=>{f(m=>({text:m.text.slice(0,m.pos)+p+m.text.slice(m.pos),pos:m.pos+p.length}))},[]),T=se(()=>{f(p=>{if(p.pos===0)return p;let m=p.text.slice(0,p.pos),u=kv(m)??m.slice(0,-1);return{text:u+p.text.slice(p.pos),pos:u.length}})},[]),[C,P]=pe(!1),[S,j]=pe(!1),[G,D]=pe(0),[H,I]=pe(!1),[oe,ae]=pe(null),[ke,Le]=pe(null),[ge,we]=pe([]),ee=ce(!1),Ae=ce(!1),Me=ce(null),ze=ce(!1),[lo,Ce]=pe(!1),Ee=ce(!1),Ze=ce(0),[sn,Ct]=pe(!1),[co,uo]=pe(!1),[tr,Ii]=pe(null),[Li,Wt]=pe(0),Bt=ce(0),[nr,Ln]=pe(0),[Ht,or]=pe(null),[fn,On]=pe(null),rr=ce(new Map),Oi=ce(0),de=ce([]),Ne=ce(null);Ne.current===null&&(Ne.current=Nv({cwd:e,profileName:s,allowProdWrites:!1}));let et=ce({uuid:null,firstPrompt:null,titleFired:!1}),ps=ce(new Map),Mf=ce(null),jc=ce(null),_i=ce(Ov().verboseTranscript===!0),_n=ce(!1),Ic=ce([]),je=ce([]),mn=ce([]),Gt=ce([]),sr=ce(null),[Uf,po]=pe(null),Lc=ce(null),[gn,ho]=pe(null),[qt,ir]=pe(null),[Pt,Mn]=pe(null),[at,ar]=pe(null),[pt,fo]=pe(null),[Tt,lr]=pe(null),[Vt,cr]=pe(null),[Yt,mo]=pe(null),[At,yn]=pe(null),[ct,hs]=pe(null),Ff=ce({value:0,at:0}),Wf=ce({value:0,at:0}),wn=se((p,m)=>{let{index:u,memory:w}=gf(Wf.current,Number(p),m);return Wf.current=w,u},[]),[wt,go]=pe(null),[Bf,yo]=pe(null);lt(()=>On(null),[ge]);let[Et,wo]=pe(null),[ht,ur]=pe(null),[Hf,Oc]=pe([]),bo=ce([]),fs=ce(!1),ms=ce(null),_c=ce(null),[c0,Gf]=pe(!1),Xe=ce(null),[ft,Mi]=pe(null),Ui=ce(!1),ko=ce(!1),Fi=ce(!1),Mc=ce(null),[dr,qf]=pe(null),Uc=ce(null),Be=ce(null),[pr,be]=pe(null),[Vf,Fe]=pe(null),gs=ce(!1),ys=ce(null),Fc=ce(null),$o=ce(null),[Nt,vo]=pe(null),[ws,xo]=pe(null),Un=!!(ft||Pt||at||pt||Tt||Vt||Yt||At||ct||wt||Et||ht||Nt||ws),bs=Pc(()=>{try{return Lv($e(e))}catch{return!1}},[e,G]),Yf=Vt?{verb:"publish live",cancel:"leave as drafts",rows:Vt.rows.length+1}:Tt?{verb:"push as drafts",rows:Tt.rows.length}:wt?{verb:"choose it",rows:wt.rows.length}:Yt?{verb:"run it",rows:Yt.rows.length}:At?{verb:"open it",cancel:"close",rows:At.rows.length-1}:at?{verb:"switch",rows:at.rows.length}:pt?{verb:"switch",rows:pt.rows.length}:Pt?{verb:"use it",rows:Pt.rows.length}:qt?{verb:"switch",rows:qt.rows.length,extra:["s this session only"]}:gn?{verb:"use it",rows:Ut.length}:ht?{verb:"save",multi:!0}:null,[Kf,Ro]=pe(0),[Dt,So]=pe(0),[u0,zf]=pe(0),Wc=Pc(()=>{try{let p=Df(e);return p.source==="auto"?`${p.model} (auto)`:p.model}catch{return null}},[e,u0]),{stdout:tt}=VL(),[,d0]=pe(0),Xf=ce(`${tt.columns??80}x${tt.rows??24}`),Qf=ce(tt.columns??80),nt=se(()=>Math.max(20,(tt.columns??80)-1),[tt]),Wi=se(p=>{de.current.push(p);let m=Ud(p,nt());hr.current+=m.split(`
|
|
499
|
+
3. I'll hand you a one-paste prompt for your builder \u2014 then just ask me to write the docs`;function p3(e,t,n,o){let r=Nf(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)",i=Df(e),a=$e(e),l=(a?.docsDir??jf).replace(/\/+$/,""),c=a?.mode==="engineer"?"engineer \xB7 full source access (dogfooding)":`writer \xB7 edits limited to ${l}/ + config`,f={version:t,engineVersion:n0,captureVersion:vt(),claude:s,model:i.model,modelSource:d3[i.source],who:null,sessionHint:null,profile:"\u2014",apiUrl:"\u2014",project:"\u2014",workspace:null,cwd:e,prod:!1,loggedOut:!0,configured:a!==null,mode:c};if(a===null)return f;try{let g=dt(e,o);f.profile=g.name,f.apiUrl=g.connection.apiUrl,f.prod=g.production,f.project=g.project.projectName??g.project.projectId??"(chosen at login)",f.workspace=g.project.workspaceName??g.project.workspaceId??null;let y=ao(g.name,g.project.projectId);if(y){let x={...us(y.idToken)??{},...us(y.accessToken)??{}},T=x.email??x.preferred_username??"signed in";er(y)?y.refreshToken&&(f.who=T,f.loggedOut=!1,f.sessionHint="session expired \u2014 refreshing\u2026"):(f.who=T,f.loggedOut=!1,f.sessionHint=`session valid until ${new Date(y.expiresAt).toLocaleString(void 0,{hour:"2-digit",minute:"2-digit",day:"2-digit",month:"short"})}`)}}catch{}return f}var h3=e=>e.mode==="local-only"?e.path:e.plan.path;function f3(e,t){try{let n=dt(e,t),o=ao(n.name,n.project.projectId);if(!o)return{text:`profile "${n.name}" \u2014 not logged in (/login)`,prod:n.production};let r={...us(o.idToken)??{},...us(o.accessToken)??{}},s=r.email??r.preferred_username??"signed in";return er(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 Jv=["Drafting","Composing","Outlining","Researching","Documenting","Structuring","Polishing","Synthesizing","Curating","Distilling","Weaving","Wrangling","Pondering"],ds=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],m3="Ask me to write or update an article\u2026",Zv=2e3,g3=new Set(Wo.map(e=>e.name));function cs({ch:e,dim:t}){let[n,o]=pe(!0);return lt(()=>{let r=setInterval(()=>o(s=>!s),530);return()=>clearInterval(r)},[]),K(z,{inverse:n,color:t&&!n?"gray":void 0,children:e})}function In({rows:e,cursor:t,cap:n,render:o}){let r=yc(e.length,t,n);return me(Zo,{children:[r.above>0?K(z,{color:"gray",children:` \u22EF ${r.above} more above`}):null,e.slice(r.start,r.end).map((s,i)=>o(s,r.start+i,r.start+i===t)),r.below>0?K(z,{color:"gray",children:` \u22EF ${r.below} more below`}):null]})}var e0=/^mcp__document360__d360_(create_article|update_article|fork_article|publish_article)$/;function t0(e){let t=e.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n/);return t?e.slice(t[0].length):e}var y3=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function w3({startTime:e,chars:t}){let[n,o]=pe(0),r=ce(Date.now());lt(()=>{let c=setInterval(()=>o(f=>f+1),120);return()=>clearInterval(c)},[]);let s=ds[n%ds.length],i=Jv[Math.floor(n/16)%Jv.length],a=gw(e,r.current,Date.now()),l=Math.round(t/4);return me(Ie,{children:[K(z,{color:ue,children:` ${s} ${i}\u2026 `}),K(z,{color:"gray",children:`(${Pn(a)} \xB7 ~${l} tokens \xB7 esc to interrupt)`})]})}function b3({step:e,cmd:t,startedAt:n,spentUsd:o,budgetUsd:r}){let[,s]=pe(0);lt(()=>{let l=setInterval(()=>s(c=>c+1),1e3);return()=>clearInterval(l)},[]);let i=Math.max(0,Math.round((Date.now()-n)/1e3)),a=o>0?` \xB7 ${xt(o)} of ${Ke(r)}`:` \xB7 limit ${Ke(r)}`;return me(Ie,{flexDirection:"column",children:[me(Ie,{children:[K(z,{color:ue,children:` \u23F5\u23F5 auto \xB7 step ${e} \xB7 `}),K(z,{color:ue,bold:!0,children:t})]}),K(z,{color:"gray",children:` ${Pn(i)}${a} \xB7 esc stops`})]})}function k3({text:e}){let[t,n]=pe(0);return lt(()=>{let o=setInterval(()=>n(r=>r+1),120);return()=>clearInterval(o)},[]),K(Ie,{paddingX:1,children:K(z,{color:ue,children:`${ds[t%ds.length]} ${e}`})})}var $3=12e4;function v3({p:e}){let[t,n]=pe(0);lt(()=>{let S=setInterval(()=>n(j=>j+1),150);return()=>clearInterval(S)},[]);let o=ds[t%ds.length],r=Math.floor((Date.now()-e.startedAt)/1e3),s=Math.floor((Date.now()-e.lastAt)/1e3),i=Math.round(e.chars/4),a=Date.now()-e.lastAt>=$3,l=e.verb??"Converting",c=e.unit??"article",f=e.rows.reduce((S,j)=>S+j.items,0),g=e.rows.reduce((S,j)=>S+(j.status==="done"?j.items:j.itemsDone??0),0),y=l.toLowerCase(),x=e.rows,T=0;if(x.length>Rc){let S=x.findIndex(G=>G.status==="active"||G.status==="pending"),j=Math.max(0,Math.min(S-2,x.length-Rc));T=x.slice(0,j).filter(G=>G.status==="done").length,x=x.slice(j,j+Rc)}let C=Math.min(40,Math.max(...x.map(S=>vf(S.label).length),0)),P=S=>S==="failed"?"red":S==="active"?ue:void 0;return me(Ie,{flexDirection:"column",children:[me(Ie,{children:[K(z,{color:ue,children:` ${o} ${l} ${f} ${c}${f===1?"":"s"} `}),K(z,{color:"gray",children:e.agentless?`\xB7 ${g}/${f} ${c}${f===1?"":"s"} \xB7 ${Pn(r)} \xB7 esc to stop`:`\xB7 ${g}/${f} ${c}${f===1?"":"s"} \xB7 ${e.done}/${e.total} batch${e.total===1?"":"es"} \xB7 ${e.tools} tool call${e.tools===1?"":"s"} \xB7 ~${i} tokens \xB7 ${Pn(r)} \xB7 esc to stop`})]}),T>0&&K(z,{dimColor:!0,children:` \u2714 +${T} done`}),x.map(S=>me(z,{color:P(S.status),dimColor:S.status==="done"||S.status==="pending",children:[` ${hv[S.status]} ${vf(S.label).padEnd(C)}`,S.status==="active"&&S.itemsDone!==void 0?` (${S.itemsDone}/${S.items} ${c}${S.items===1?"":"s"})`:S.status!=="pending"?` (${S.items} ${c}${S.items===1?"":"s"})`:"",S.status==="active"?` \u2190 ${y}\u2026`:""]},S.label)),a&&K(z,{color:"yellow",children:` \u26A0 no activity for ${Pn(s)} \u2014 press esc to stop if it's wedged`}),a&&e.stalledNote?K(z,{color:"yellow",children:` ${e.stalledNote}`}):null]})}var x3=10800*1e3;function o0({cwd:e,auth:t,profileName:n,version:o}){let{exit:r}=GL(),[s,i]=pe(n),[a,l]=pe(null),[c,f]=pe({text:"",pos:0}),g=c.text,y=se(p=>{f(m=>{let u=typeof p=="function"?p(m.text):p;return{text:u,pos:u.length}})},[]),x=se(p=>{f(m=>({text:m.text.slice(0,m.pos)+p+m.text.slice(m.pos),pos:m.pos+p.length}))},[]),T=se(()=>{f(p=>{if(p.pos===0)return p;let m=p.text.slice(0,p.pos),u=kv(m)??m.slice(0,-1);return{text:u+p.text.slice(p.pos),pos:u.length}})},[]),[C,P]=pe(!1),[S,j]=pe(!1),[G,D]=pe(0),[H,I]=pe(!1),[oe,ae]=pe(null),[ke,Le]=pe(null),[ge,we]=pe([]),ee=ce(!1),Ae=ce(!1),Me=ce(null),ze=ce(!1),[lo,Ce]=pe(!1),Ee=ce(!1),Ze=ce(0),[sn,Ct]=pe(!1),[co,uo]=pe(!1),[tr,Ii]=pe(null),[Li,Wt]=pe(0),Bt=ce(0),[nr,Ln]=pe(0),[Ht,or]=pe(null),[fn,On]=pe(null),rr=ce(new Map),Oi=ce(0),de=ce([]),Ne=ce(null);Ne.current===null&&(Ne.current=Nv({cwd:e,profileName:s,allowProdWrites:!1}));let et=ce({uuid:null,firstPrompt:null,titleFired:!1}),ps=ce(new Map),Mf=ce(null),jc=ce(null),_i=ce(Ov().verboseTranscript===!0),_n=ce(!1),Ic=ce([]),je=ce([]),mn=ce([]),Gt=ce([]),sr=ce(null),[Uf,po]=pe(null),Lc=ce(null),[gn,ho]=pe(null),[qt,ir]=pe(null),[Pt,Mn]=pe(null),[at,ar]=pe(null),[pt,fo]=pe(null),[Tt,lr]=pe(null),[Vt,cr]=pe(null),[Yt,mo]=pe(null),[At,yn]=pe(null),[ct,hs]=pe(null),Ff=ce({value:0,at:0}),Wf=ce({value:0,at:0}),wn=se((p,m)=>{let{index:u,memory:w}=gf(Wf.current,Number(p),m);return Wf.current=w,u},[]),[wt,go]=pe(null),[Bf,yo]=pe(null);lt(()=>On(null),[ge]);let[Et,wo]=pe(null),[ht,ur]=pe(null),[Hf,Oc]=pe([]),bo=ce([]),fs=ce(!1),ms=ce(null),_c=ce(null),[c0,Gf]=pe(!1),Xe=ce(null),[ft,Mi]=pe(null),Ui=ce(!1),ko=ce(!1),Fi=ce(!1),Mc=ce(null),[dr,qf]=pe(null),Uc=ce(null),Be=ce(null),[pr,be]=pe(null),[Vf,Fe]=pe(null),gs=ce(!1),ys=ce(null),Fc=ce(null),$o=ce(null),[Nt,vo]=pe(null),[ws,xo]=pe(null),Un=!!(ft||Pt||at||pt||Tt||Vt||Yt||At||ct||wt||Et||ht||Nt||ws),bs=Pc(()=>{try{return Lv($e(e))}catch{return!1}},[e,G]),Yf=Vt?{verb:"publish live",cancel:"leave as drafts",rows:Vt.rows.length+1}:Tt?{verb:"push as drafts",rows:Tt.rows.length}:wt?{verb:"choose it",rows:wt.rows.length}:Yt?{verb:"run it",rows:Yt.rows.length}:At?{verb:"open it",cancel:"close",rows:At.rows.length-1}:at?{verb:"switch",rows:at.rows.length}:pt?{verb:"switch",rows:pt.rows.length}:Pt?{verb:"use it",rows:Pt.rows.length}:qt?{verb:"switch",rows:qt.rows.length,extra:["s this session only"]}:gn?{verb:"use it",rows:Ut.length}:ht?{verb:"save",multi:!0}:null,[Kf,Ro]=pe(0),[Dt,So]=pe(0),[u0,zf]=pe(0),Wc=Pc(()=>{try{let p=Df(e);return p.source==="auto"?`${p.model} (auto)`:p.model}catch{return null}},[e,u0]),{stdout:tt}=VL(),[,d0]=pe(0),Xf=ce(`${tt.columns??80}x${tt.rows??24}`),Qf=ce(tt.columns??80),nt=se(()=>Math.max(20,(tt.columns??80)-1),[tt]),Wi=se(p=>{de.current.push(p);let m=Ud(p,nt());hr.current+=m.split(`
|
|
500
500
|
`).length+1,console.log(m)},[nt]),Bc=ce([]),Bi=se(()=>{let p=Bc.current;if(p.length===0)return;Bc.current=[];let m=Sv(p);m&&Wi({kind:"note",tone:"info",text:`\u25CF Housekeeping ${m}`})},[Wi]),h=se(p=>{Bi(),Wi(p)},[Wi,Bi]),mt=ce({printed:[],armed:[],printedAt:0}),Hi=ce(null),Gi=ce([]),hr=ce(0),qi=se(()=>Xe.current!==null,[]),ot=se((p,m)=>{qi()||(Ze.current++,mt.current.armed=[],ae(p),Le(m??null))},[qi]),Kt=se(()=>{Ze.current++,mt.current.armed=[],ae(null),Le(null)},[]),ks=se(p=>{qi()||(Ze.current++,mt.current.armed=[],ae(null),Le(p))},[qi]),le=se(p=>ot(p,es({kind:"cmd",cmd:p,reason:""})),[ot]),bn=se((p,m)=>{if(!m?.silent&&p.note&&h({kind:"note",tone:m?.tone??p.tone,text:p.note}),!m?.silent&&p.note?.includes(".d360-writer/ \u2014 your articles"))try{let A=KO(e);zO(zL(A)),Ev(A,`${new Date().toISOString()}
|
|
501
501
|
`)}catch{}p.picker&&(we(p.picker.options.map(A=>({cmd:A.cmd,desc:A.desc}))),yo(p.picker.title),Gi.current=p.picker.exclusive?p.picker.options.map(A=>A.cmd.trim()):[]),!m?.silent&&p.note&&(mt.current.printed=p.menu,mt.current.printedAt=hr.current);let w=hr.current-mt.current.printedAt<Math.max(8,(tt.rows??24)-4)&&n$(mt.current.printed,p.menu)?p.menu:[];p.lead?(p.lead.kind==="answer"?ks(es(p.lead,w.length)):ot(p.lead.kind==="cmd"?p.lead.cmd:p.lead.text,es(p.lead,w.length)),!m?.silent&&p.note&&(Hi.current=ns(p.lead))):!p.picker&&(!m?.silent||p.restKind!==null)&&(Kt(),w.length>0&&ks(p.restKind==="delivered"?`1\u2013${w.length} when you want one \xB7 nothing is owed \xB7 or just ask me anything\u2026`:`1\u2013${w.length} to pick a campaign step \xB7 nothing is waiting \xB7 or just ask me anything\u2026`)),mt.current.armed=w},[h,ot,ks,e,tt]),Pe=se((p,m,u="info")=>{mt.current.armed=[],p&&h({kind:"note",tone:u,text:p}),m&&(m.kind==="answer"?ks(es(m)):ot(m.kind==="cmd"?m.cmd:m.text,es(m)),Hi.current=p?ns(m):null)},[h,ot,ks]),kn=se((p,m,u,w="info")=>{if(p.producedNothing){Pe("Nothing was changed \u2014 the agent produced no output, so this command did nothing at all.",{kind:"cmd",cmd:"/doctor",reason:"it checks Claude sign-in + whether the model is reachable from this machine"},"warn");return}p.handedBack||Pe(m,u,w)},[Pe]),[Co,Hc]=pe(null),[p0,Jf]=pe(!1),$n=se((p,m)=>{Hc(p),Jf(!1),h({kind:"note",tone:"info",text:m?.hint??"\u2328 Press c to copy the prompt above to your clipboard (then paste it into your coding agent)."})},[h]),Gc=se(()=>{l3(e,new Date().toISOString());let p=$b(e);p&&(h({kind:"note",tone:"info",text:p.lines.join(`
|
|
502
502
|
`)}),$n(p.prompt,c3(e)?{hint:"\u2328 Press c to copy the request \u2014 only if you want to hand it over now; your builder already has it."}:void 0))},[e,h,$n]),Vi=ce(!1),qc=se(async()=>{try{if(Vi.current||!UO(MO())||$e(e)?.mode==="engineer")return;let p=await Hv({pkg:Vo,currentVersion:o,force:!0});if(!p.updateAvailable||!p.latest)return;if(Vi.current=!0,Ul()){Fl(p.latest),h({kind:"note",tone:"info",text:`\u2B06 Update v${p.latest} is ready to install.
|
|
@@ -505,39 +505,39 @@ ${Ce}`)}catch{}let lo=await pf(l,ze,r,c,e,n);if(oe){let Ce=oe==="review"?Yo(e,ae
|
|
|
505
505
|
Run /doctor anytime to re-check and see the fix. (You can configure Document360 meanwhile \u2014 the agent just needs Claude before it writes.)`}),!$e(e)){h({kind:"note",tone:"info",text:`Welcome! This repo isn't set up for d360-writer yet \u2014 three steps and you're writing docs:
|
|
506
506
|
${Qv}`}),le(w?"/doctor":"/init");return}w&&le("/doctor");try{let b=dt(e,s);b.project.projectId&&iO(b.name,b.project.projectId,b.project.projectName);let $=ao(b.name,b.project.projectId),v=Je(e,s);$&&er($)&&$.refreshToken?tO({profile:b.name,connection:b.connection,projectId:b.project.projectId,projectName:b.project.projectName}).then(()=>{h({kind:"note",tone:"ok",text:"\u2713 Document360 session refreshed."}),D(k=>k+1)}).catch(k=>{if(k instanceof nO&&k.transient){h({kind:"note",tone:"warn",text:"Couldn't reach Document360 to renew your session \u2014 your sign-in is intact, and I'll try again on your next command."});return}h({kind:"note",tone:"warn",text:"Document360 session refresh failed \u2014 sign in again with /login."}),le("/login"),D(R=>R+1)}):v.ok||(h({kind:"note",tone:"warn",text:v.reason}),le("/login"))}catch{}try{let b=$e(e),$=dt(e,s),v=ao($.name,$.project.projectId);b&&v&&!er(v)&&af(e,b.authoritativeSourceFiles)&&!lf(e,s)&&(h({kind:"note",tone:"info",text:"Large repo \u2014 the docs scope isn\u2019t set yet. Run /scope to choose which folders back your docs."}),le("/scope"))}catch{}try{let b=u("resolve-genre",()=>bO(e));b.persisted&&b.appType?h({kind:"note",tone:"ok",text:`\u2713 Detected a ${b.appType} app \u2014 enabled the ${b.appType}-doc-playbook (change with /genre, or edit .d360-writer/config.json).`}):b.source==="unknown"&&$e(e)&&(ze.current=!0)}catch{}let A=Xt(e);try{if(u("reconcile-devhints",()=>ba(e)),A.status!=="current"&&!Sn(e)){let b=ka(e);b.length&&(h({kind:"note",tone:"warn",text:b.join(`
|
|
507
507
|
`)}),$n(jo()),xs.current=!1,fr.current=!1)}}catch{}try{let b=u("reconcile-feedback",()=>FO(e));b.archived>0&&h({kind:"note",tone:"ok",text:`\u2713 Tidied ${b.archived} resolved item${b.archived===1?"":"s"} from your tool feedback (now fixed in d360-writer) \u2192 moved to .d360-writer/tool-feedback-archived.md${b.open>0?`; ${b.open} still open`:""}.`})}catch{}try{let b=u("upgrade-actions",()=>WO(e));b.applied.length>0&&h({kind:"note",tone:"warn",text:ma(b.applied).join(`
|
|
508
|
-
`)}),b.reopenedForRedaction&&h({kind:"note",tone:"warn",text:ga(b.reopenedForRedaction)})}catch{}let U=u("ready-decision",()=>bt(!0));U.kind==="autoAudit"?(h({kind:"note",tone:"info",text:U.note}),qs({cwd:e,profileName:s}).then(b=>{if(b.kind==="audit-blocked"){Pe(b.note,{kind:"cmd",cmd:b.lead.cmd,reason:b.lead.reason},"warn");return}b.kind!=="forward-to-agent"||!b.prompt||
|
|
508
|
+
`)}),b.reopenedForRedaction&&h({kind:"note",tone:"warn",text:ga(b.reopenedForRedaction)})}catch{}let U=u("ready-decision",()=>bt(!0));U.kind==="autoAudit"?(h({kind:"note",tone:"info",text:U.note}),qs({cwd:e,profileName:s}).then(b=>{if(b.kind==="audit-blocked"){Pe(b.note,{kind:"cmd",cmd:b.lead.cmd,reason:b.lead.reason},"warn");return}b.kind!=="forward-to-agent"||!b.prompt||Re(b.prompt,{display:b.display,quietUser:!0}).then($=>{if($.producedNothing)return;let v=nl(e,s);if(v){Pe(v.note,{kind:"cmd",cmd:Or.cmd,reason:Or.reason},"warn"),D(k=>k+1);return}$i(e,s),D(k=>k+1)})})):U.kind==="recommend"&&bn(U.rec);let O=Math.round(performance.now()-p);O>=800&&VO({kind:"slow-launch",level:"transport",message:`Launch blocked the UI for ${O}ms \u2014 input is frozen until this finishes (s-228 class).`,detail:`${m.length?m.join(`
|
|
509
509
|
`):"no single phase \u226580ms"}
|
|
510
|
-
cwd: ${e}`}),qc(),Zf({pull:!0})},[]),lt(()=>{let p=setInterval(()=>{qc()},x3);return()=>clearInterval(p)},[qc]),lt(()=>{let p=setInterval(()=>{try{let m=Date.now(),u=bf(m,Kc.current);if(nm.current+=1,u&&nm.current%Math.round(3e5/6e4)!==0)return;let w=u3(e),A=vs.current,U=w===A.fingerprint&&A.counts?A.counts:Zt(e),O=iv({now:m,lastInputAt:Kc.current,fingerprint:w,counts:U,occupied:C||H||ge.length>0||g!==""||Un||je.current.length>0,prev:A});if(vs.current=O.memory,rm(O.waiting),O.speak&&Xe.current===null){h({kind:"note",tone:"info",text:lv(O.speak)});let b=bt(!1);b.kind==="recommend"&&bn(b.rec,{silent:!0})}}catch{}},6e4);return()=>clearInterval(p)},[e,C,H,ge.length,g,Un,h,bt,bn]),lt(()=>{if(C||H||ee.current||bs&&!ko.current||oe!==null||ge.length>0||g!==""||Co!==null||je.current.length>0||sr.current||Un)return;let p=bt(!1);if(p.kind==="recommend"&&(p.rec.lead||p.rec.picker||p.rec.menu.length>0)){bn(p.rec,{silent:!0});let m=p.rec.lead,u=m?ns(m):p.rec.restKind?`rest:${p.rec.restKind}`:null;u&&u!==Hi.current&&(Hi.current=u,p.rec.settled&&h({kind:"note",tone:"ok",text:p.rec.settled}),m&&h({kind:"note",tone:"info",text:ts(m)}))}Zf({pull:!1})},[C,H,oe,ge,g,Co,Un,G,bt,lo]);let Ss=se((p,m)=>{let u=Xe.current;if(Xe.current=null,Ui.current=!1,Fi.current=!1,!u)return;let w=Math.max(0,Math.round((Date.now()-u.startedAt)/1e3)),A={ran:u.ran,parked:m,spentUsd:Math.max(0,Dt-u.stepStartedSpend),budgetUsd:Ac($e(e)),seconds:w,reason:p};for(let O of z$(A))h({kind:"note",tone:O.tone,text:O.text});try{let O=new Date().toISOString().replace(/[:.]/g,"-"),b=Ei(e,yO,"auto-runs");KL(b,{recursive:!0}),Ev(Ei(b,`${O}.md`),X$(A,new Date().toISOString()),"utf8")}catch{}let U=m.find(O=>O.cmd)?.cmd;U&&le(U),qf(null),D(O=>O+1)},[e,h,Dt,le]);lt(()=>{if(!bs||ko.current||Fi.current||C||H||ge.length>0||g!==""||Co!==null||je.current.length>0||sr.current||Un)return;let p=Xe.current;if(p?.stopping){Ss({kind:"stopped",how:"manual"},p.parked);return}let m=bt(!1);if(m.kind!=="recommend")return;let u=p??{ran:[],parked:[],spentUsd:0,startedAt:Date.now(),stopping:!1,stepStartedSpend:Dt},w=Uc.current,A=nv(m.rec,{ran:u.ran,parked:u.parked,spentUsd:Math.max(0,Dt-u.stepStartedSpend),...u.ran.length>0?{lastLeadKey:u.ran[u.ran.length-1]??null}:{},...w?{lastClosedItsTarget:Qt(w)}:{}},{budgetUsd:Ac($e(e))});if(A.kind==="stop"){if(!p&&A.reason.kind!=="onlyBlocked")return;if(!p){let b=m.rec.lead?ns(m.rec.lead):"blocked";if(Mc.current===b)return;Mc.current=b}Xe.current=u,Ss(A.reason,A.parked);return}if(!Ui.current){let b=Ac($e(e)),$=E=>{try{return E()}catch{return null}},v=A.kind==="run"?A.cmd:`\u201C${A.text}\u201D`,k=A.kind==="run"?$(()=>rk(e,A.cmd)):null,R=A.kind==="run"?$(()=>ov(m.rec,A.cmd,E=>en(e,E)))??[]:[];Mi({firstCmd:v,scale:A.kind==="run"?$(()=>en(e,A.cmd)):null,upcoming:R,budgetUsd:b,overBudget:k!==null&&k>b});return}let U=A.kind==="run"?A.cmd:A.text;u.parked=A.parked,u.ran=[...u.ran,U],Xe.current=u,Uc.current=null,h({kind:"block",text:Z(ne("\u25B6",`Auto mode, step ${u.ran.length}: ${U}`))}),qf({step:u.ran.length,cmd:U,startedAt:u.startedAt,spentUsd:Math.max(0,Dt-u.stepStartedSpend),budgetUsd:Ac($e(e))}),Mc.current=null,Fi.current=!0,I(!0),(A.kind==="run"?Eo(A.cmd):
|
|
510
|
+
cwd: ${e}`}),qc(),Zf({pull:!0})},[]),lt(()=>{let p=setInterval(()=>{qc()},x3);return()=>clearInterval(p)},[qc]),lt(()=>{let p=setInterval(()=>{try{let m=Date.now(),u=bf(m,Kc.current);if(nm.current+=1,u&&nm.current%Math.round(3e5/6e4)!==0)return;let w=u3(e),A=vs.current,U=w===A.fingerprint&&A.counts?A.counts:Zt(e),O=iv({now:m,lastInputAt:Kc.current,fingerprint:w,counts:U,occupied:C||H||ge.length>0||g!==""||Un||je.current.length>0,prev:A});if(vs.current=O.memory,rm(O.waiting),O.speak&&Xe.current===null){h({kind:"note",tone:"info",text:lv(O.speak)});let b=bt(!1);b.kind==="recommend"&&bn(b.rec,{silent:!0})}}catch{}},6e4);return()=>clearInterval(p)},[e,C,H,ge.length,g,Un,h,bt,bn]),lt(()=>{if(C||H||ee.current||bs&&!ko.current||oe!==null||ge.length>0||g!==""||Co!==null||je.current.length>0||sr.current||Un)return;let p=bt(!1);if(p.kind==="recommend"&&(p.rec.lead||p.rec.picker||p.rec.menu.length>0)){bn(p.rec,{silent:!0});let m=p.rec.lead,u=m?ns(m):p.rec.restKind?`rest:${p.rec.restKind}`:null;u&&u!==Hi.current&&(Hi.current=u,p.rec.settled&&h({kind:"note",tone:"ok",text:p.rec.settled}),m&&h({kind:"note",tone:"info",text:ts(m)}))}Zf({pull:!1})},[C,H,oe,ge,g,Co,Un,G,bt,lo]);let Ss=se((p,m)=>{let u=Xe.current;if(Xe.current=null,Ui.current=!1,Fi.current=!1,!u)return;let w=Math.max(0,Math.round((Date.now()-u.startedAt)/1e3)),A={ran:u.ran,parked:m,spentUsd:Math.max(0,Dt-u.stepStartedSpend),budgetUsd:Ac($e(e)),seconds:w,reason:p};for(let O of z$(A))h({kind:"note",tone:O.tone,text:O.text});try{let O=new Date().toISOString().replace(/[:.]/g,"-"),b=Ei(e,yO,"auto-runs");KL(b,{recursive:!0}),Ev(Ei(b,`${O}.md`),X$(A,new Date().toISOString()),"utf8")}catch{}let U=m.find(O=>O.cmd)?.cmd;U&&le(U),qf(null),D(O=>O+1)},[e,h,Dt,le]);lt(()=>{if(!bs||ko.current||Fi.current||C||H||ge.length>0||g!==""||Co!==null||je.current.length>0||sr.current||Un)return;let p=Xe.current;if(p?.stopping){Ss({kind:"stopped",how:"manual"},p.parked);return}let m=bt(!1);if(m.kind!=="recommend")return;let u=p??{ran:[],parked:[],spentUsd:0,startedAt:Date.now(),stopping:!1,stepStartedSpend:Dt},w=Uc.current,A=nv(m.rec,{ran:u.ran,parked:u.parked,spentUsd:Math.max(0,Dt-u.stepStartedSpend),...u.ran.length>0?{lastLeadKey:u.ran[u.ran.length-1]??null}:{},...w?{lastClosedItsTarget:Qt(w)}:{}},{budgetUsd:Ac($e(e))});if(A.kind==="stop"){if(!p&&A.reason.kind!=="onlyBlocked")return;if(!p){let b=m.rec.lead?ns(m.rec.lead):"blocked";if(Mc.current===b)return;Mc.current=b}Xe.current=u,Ss(A.reason,A.parked);return}if(!Ui.current){let b=Ac($e(e)),$=E=>{try{return E()}catch{return null}},v=A.kind==="run"?A.cmd:`\u201C${A.text}\u201D`,k=A.kind==="run"?$(()=>rk(e,A.cmd)):null,R=A.kind==="run"?$(()=>ov(m.rec,A.cmd,E=>en(e,E)))??[]:[];Mi({firstCmd:v,scale:A.kind==="run"?$(()=>en(e,A.cmd)):null,upcoming:R,budgetUsd:b,overBudget:k!==null&&k>b});return}let U=A.kind==="run"?A.cmd:A.text;u.parked=A.parked,u.ran=[...u.ran,U],Xe.current=u,Uc.current=null,h({kind:"block",text:Z(ne("\u25B6",`Auto mode, step ${u.ran.length}: ${U}`))}),qf({step:u.ran.length,cmd:U,startedAt:u.startedAt,spentUsd:Math.max(0,Dt-u.stepStartedSpend),budgetUsd:Ac($e(e))}),Mc.current=null,Fi.current=!0,I(!0),(A.kind==="run"?Eo(A.cmd):Re(A.text,{display:A.text,quietUser:!0}).then(()=>{})).finally(()=>{Fi.current=!1,I(!1)})},[bs,C,H,ge,g,Co,Un,G,bt,Ss]),lt(()=>{let p=null,m=null,u=()=>{p&&clearTimeout(p),p=setTimeout(()=>{p=null;let w=tt.columns??80,A=`${w}x${tt.rows??24}`;if(A===Xf.current)return;let U=w<(Qf.current??w);Xf.current=A,Qf.current=w,d0(O=>O+1),U&&(m&&clearTimeout(m),m=setTimeout(()=>{m=null;let O=Math.max(8,(tt.rows??24)-2);console.log("\x1B[?2026h\x1B[H\x1B[2J"+yw(de.current,nt(),O)+"\x1B[?2026l")},80))},400)};return tt.on("resize",u),()=>{p&&clearTimeout(p),m&&clearTimeout(m),tt.off("resize",u)}},[tt,nt]);let gt=Math.max(20,(tt.columns??80)-1),sm=Pc(()=>f3(e,s),[e,s,G]),zc=(()=>{if(co)return{text:"\u23F3 Update in progress \u2014 please don\u2019t close this window\u2026",isSetup:!0};if(tr!==null)return{text:`Update ready \u2014 run /exit, then relaunch to apply${tr?` v${tr}`:""}\u2026`,isSetup:!0};if(Yf)return{text:U$(Yf),isSetup:!0};if(Yi)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(Co)return p0?{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 \u2014 then paste it into your coding agent\u2026",isSetup:!0};if(om)return{text:uv(om),isSetup:!0};if(je.current.length>0){let w=je.current.length,A=je.current[0].mode;return A==="local-only"?{text:"l publish yours over the portal \xB7 s skip \xB7 anything else cancels\u2026",isSetup:!0}:A==="conflict"?{text:w>1?`l keep local \xB7 p take portal \xB7 s skip \xB7 la/pa/sa for all ${w}\u2026`:"l keep local \xB7 p take portal \xB7 s skip \xB7 anything else cancels\u2026",isSetup:!0}:{text:w>1?`y to write \xB7 n to skip \xB7 ya/na for all ${w}\u2026`:"y to write \xB7 n to skip \xB7 anything else cancels\u2026",isSetup:!0}}let p=$e(e);if(!p)return{text:"Run /init to set up this repo (or /help)\u2026",isSetup:!0};let m=Ei(e,(p.docsDir??jf).replace(/\/+$/,"")),u=(()=>{try{return is(m)&&YL(m).length>0}catch{return!1}})();try{let w=dt(e,s),A=ao(w.name,w.project.projectId);if(!(!!A&&!(er(A)&&!A.refreshToken)))return{text:`Sign in with /login (profile "${w.name}")\u2026`,isSetup:!0};if(!w.project.projectId)return{text:"Sign in to Document360 to pick your project\u2026",isSetup:!0};if(!w.project.workspaceId&&!u)return{text:"Pick a workspace with /workspace\u2026",isSetup:!0}}catch{}return u?{text:m3,isSetup:!1}:{text:"Ask me to write the docs for this repo",isSetup:!1}})(),vn=iw(g),Xc=vn.length>0&&!C,h0=12,an=mf(tt.rows),f0=tt.rows??24,im=mf(tt.rows,{max:h0}),zi=Math.min(nr,Math.max(0,vn.length-1)),To=yc(vn.length,zi,im).start,Qc=vn.slice(To,To+im),Jc=a!==null?Cv(a,8,gt):null,Xi=Et?Et.paths.filter(p=>!Et.query||p.toLowerCase().includes(Et.query.toLowerCase())).slice(0,8):[],Qi=Nt?Nt.sessions.filter(p=>{let m=Nt.query.toLowerCase();return!m||p.name.toLowerCase().includes(m)||p.firstPrompt.toLowerCase().includes(m)}).slice(0,8):[],Ve=se((p,m)=>{Ne.current?.close(),Ne.current=Nv({cwd:e,resume:p,profileName:m??s,allowProdWrites:_n.current}),p||(et.current={uuid:null,firstPrompt:null,titleFired:!1},po(null)),Ro(0),So(0)},[e,s]),am=se((p,m,u)=>{if(e0.test(p))try{let w=dt(e,s),A=typeof m.project_id=="string"&&m.project_id||w.project.projectId,U=pc(m,u),O=[],b=mc(m,u),$=b?`${b} \u2014 `:"",v=hc(u);p.endsWith("publish_article")&&v&&O.push(`${$}Live: ${v}`);let k=fc(u);k?O.push(`${$}Preview: ${k}`):U&&A&&O.push(`${$}Preview: ${dc(w.connection.portalUrl,A,U,w.project.languageCode??"en")}`),O.length>0&&h({kind:"link",lines:O})}catch{}},[e,s,h]),Re=se(async(p,m)=>{Ct(!0),ee.current=!1,Ae.current=!1,Me.current=null,Kt(),we([]),yo(null),Hc(null);let u=Ze.current;if(m?.display?.startsWith("/"))try{let N=qa(e,m.display);N&&(p=`${p}
|
|
511
511
|
|
|
512
512
|
${N}`)}catch{}m?.quietUser||h({kind:"user",text:m?.echoDisplay&&m.display?m.display:p});let w=et.current;w.firstPrompt||(w.firstPrompt=m?.display??p),Bt.current=Date.now(),Wt(0),P(!0),ps.current.clear(),jc.current=null;let A="",U=!1,O=!1,b=!1,$=!1,v="",k={num:0,closed:!1,cueSeen:!1},R=null,E=()=>{R||(R=setTimeout(()=>{R=null,l(v.length>0?v:null)},60))},W=()=>{R&&clearTimeout(R),R=null,l(null)},F=(N=!1)=>{let{head:X,keep:L}=Pv(v,N);X.trim()&&h({kind:"assistant",text:ff(X.trimEnd(),k)}),v=L,W()};try{for await(let N of Ne.current.send(p))if(N.type==="session"){if(!w.uuid){w.uuid=N.sessionId;let X=new Date().toISOString(),L=Lc.current,B=L??uO(w.firstPrompt??"session");dO({uuid:N.sessionId,name:B,renamed:!!L,titled:!1,cwd:e,firstPrompt:w.firstPrompt??"",createdAt:X,updatedAt:X}),po(B),Lc.current=null}}else if(N.type==="text"){v+=N.delta,A+=N.delta;let X=Tv(v);if(X>0){let L=v.slice(0,X).trimEnd();L&&h({kind:"assistant",text:ff(L,k)}),v=v.slice(X)}E(),Wt(L=>L+N.delta.length)}else if(N.type==="tool"){U=!0,e0.test(N.name)&&(O=!0);let L=(ye=>typeof ye=="string"&&ye?ye.replace(/\\/g,"/").toLowerCase():null)(N.input.file_path??N.input.notebook_path),B=["Edit","Write","MultiEdit","NotebookEdit"].includes(N.name),q=N.name==="Read"&&L!==null&&L===Mf.current;Mf.current=B?L:null;let te=_i.current,he=te?null:Rv(N.name,N.input),fe=te?null:ac(N.name,N.input,e,s);if(he)Bc.current.push(he);else if(fe){F();let ye=fe.repeat&&fe.articleKey&&fe.articleKey===jc.current?fe.repeat:fe;jc.current=fe.articleKey??null,h({kind:"doc",verb:fe.verb,title:ye.title,category:ye.category,note:ye.note,...fe.loud?{loud:fe.loud}:{}}),ps.current.set(N.id,{name:N.name,input:N.input,doc:!0,keepResult:fe.keepResult})}else if(q)F(),h({kind:"tool",title:"Read",sep:"",arg:"re-checking the file it just edited"});else{let ye=cc(N.name,N.input,e);ye&&(F(),h({kind:"tool",title:ye.title,sep:ye.sep,arg:ye.arg}),ps.current.set(N.id,{name:N.name,input:N.input}))}}else if(N.type==="article_diff"){let X=_t(N.oldContent,N.newContent,nt());X&&(F(),h({kind:"diff",added:X.added,removed:X.removed,lines:X.lines,hidden:X.hidden}))}else if(N.type==="gate_blocked")Fc.current={path:N.path,commands:N.commands};else if(N.type==="tool_result"){N.isError&&/run \/login|not logged in|session expired|rejected the token/i.test(N.output)&&(gs.current=!0);let X=ps.current.get(N.id);if(X){ps.current.delete(N.id),F();let B=X.doc&&!X.keepResult&&!N.isError?{lines:[],hidden:0}:uc(N.output,4,N.isError||_i.current?void 0:X.name,X.input),q=N.isError&&F$(X.name,N.output);B.lines.length>0&&h({kind:"tool-result",lines:B.lines,hidden:B.hidden,isError:N.isError&&!q}),N.isError||am(X.name,X.input,N.output)}}else if(N.type==="result"){F(!0),Ro(q=>q+N.outputTokens),So(q=>Math.max(q,N.sessionCostUsd));let X=N.inputTokens+N.cacheReadTokens+N.cacheCreationTokens;if(h({kind:"done",seconds:Math.round((Date.now()-Bt.current)/1e3),tokens:N.outputTokens,costUsd:N.costUsd,sessionCostUsd:Math.max(Dt,N.sessionCostUsd),ok:N.ok,...X>0?{inTokens:X,cachedPct:Math.round(N.cacheReadTokens/X*100)}:{}}),!b&&!U&&!A.trim()&&!fs.current&&($=!0,h({kind:"note",tone:"warn",text:"The agent produced no output \u2014 nothing ran this turn. Run /doctor: it runs a real turn with your skills and tools and reports what failed. Claude Code\u2019s own error was written either way \u2014 /logs shows it, and offers to copy it for the team."})),N.ok&&/🛠|📣 Feedback for the d360-writer developers/.test(A))try{let q=qv(e);if(is(q)){let te=Tc(q,"utf8").trim();te&&(Yc(te),h({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{}let L=N.ok?I$(A):null;Ae.current=L!==null,Me.current=L;let B=N.ok?L$(A,g3):null;if(B)Ce(!1),Ee.current=!1,we(B);else if(N.ok&&(k.cueSeen||(L?.options??0)>=2))Ce(!0),Ee.current=!0,we([]);else if(L)we([]);else{let q=N.ok?pv(A,Wo.map(te=>te.name)):[];if(q.length>0)le(q[0]);else if(N.ok&&A.trim()&&!Ae.current){let te=bt(!1);te.kind==="recommend"&&(te.rec.lead||te.rec.picker)||mO(p,A,e).then(he=>{he&&Ze.current===u&&ot(he)}).catch(()=>{})}}if(w.uuid&&(Rf(w.uuid),!w.titleFired)){w.titleFired=!0;let q=w.uuid,te=w.firstPrompt;te&&jv(te,e).then(he=>{he&&(cO(q,he),po(he))}).catch(()=>{})}}else N.type==="error"&&(F(!0),b=!0,N.kind==="auth"&&(gs.current=!0),h(N.kind==="limit"?{kind:"note",text:`\u23F3 ${N.message}`,tone:"warn"}:{kind:"note",text:`agent error: ${N.message}`,tone:"error"}),N.note&&h({kind:"note",text:N.note,tone:"info"}))}finally{if(P(!1),W(),Bi(),O)try{let X=Af(await Tf({cwd:e,profileName:s,onRateLimit:L=>Fe(`Document360 rate limit \u2014 waiting ${Math.ceil(L.waitMs/1e3)}s\u2026`)}));X&&h({kind:"note",tone:X.startsWith("\u26A0")?"warn":"info",text:X})}catch{}finally{Fe(null)}D(X=>X+1),et.current.uuid&&G$(e,et.current.uuid,de.current),gs.current?(gs.current=!1,ys.current=m?.display??p,ot("/login","\u21E5 tab to run /login \u2014 then I\u2019ll re-send your last prompt\u2026")):ys.current=null;let N=Fc.current;if(Fc.current=null,N&&!gs.current&&N.commands.length>0){let X=N.commands[0],L=N.commands.length>1?` \u2014 then ${N.commands[1].split(" ")[0]} for the style errors`:"";ot(X,`\u21E5 tab to run ${X.split(" ")[0]}${L}`)}fs.current&&(fs.current=!1,bo.current.length>0&&(h({kind:"note",tone:"info",text:`(${bo.current.length} queued message(s) discarded)`}),bo.current=[],Oc([])),h({kind:"note",tone:"warn",text:"Interrupted. What do you want to do next?"}))}return{producedNothing:$,handedBack:Me.current!==null}},[e,h,am,nt,ot,le,Kt,bt,Bi]),Ji=se(async()=>{try{Fe("Checking Document360 for new reviewer comments\u2026"),await Ol(e,s,{onProgress:(p,m)=>Fe(`Checking Document360 for new reviewer comments\u2026 ${p}/${m}`)})}catch{}finally{Fe(null)}},[e,s]),kt=se(async(p,m=0,u)=>{let w=Nc(e,p,{allowUnreviewed:u?.skipReview===!0,allowStyleErrors:u?.skipStyle===!0});if(!w.clear){h({kind:"note",tone:"warn",text:Ef(w,p.length,{act:"push"}).join(`
|
|
513
|
-
`)}),le(Bs(w));return}Bt.current=Date.now(),Wt(0),P(!0);let A=new AbortController;Be.current=A;let U={aborted:!1,partitions:0,failed:0,notRun:0,producedNothing:!0},O={retryable:0,stopped:!1,paths:[]},b=async($,v)=>{let k=Sf($),R=new Map;for(let te of k)for(let he of te.paths)R.set(he,te.label);let E=new Map(k.map(te=>[te.label,te.paths.length])),W=new Set,F=Date.now();be({verb:"Pushing",unit:"article",total:$.length,done:0,active:[],rows:Jo(k),tools:0,chars:0,lastAt:Date.now(),startedAt:F,agentless:!0}),h({kind:"note",tone:"info",text:v?`Resolving cross-links in ${$.length} article${$.length===1?"":"s"} whose targets were saved this run (second pass)\u2026 (esc to stop)`:`Pushing ${$.length} article${$.length===1?"":"s"} to Document360 (${TO} at a time)\u2026 (esc to stop)`});let N=await CO({cwd:e,profileName:s,paths:$,...u?.allowUnpullable===!0?{allowUnpullable:!0}:{},signal:A.signal,onEvent:te=>{let he=te.type==="article_done"?te.result.path:te.type==="article_failed"?te.failure.path:te.path,fe=R.get(he);if(!fe)return;if(te.type==="article_start"){be(J=>J&&!J.active.includes(fe)?{...J,active:[...J.active,fe],rows:it(J.rows,fe,"active"),lastAt:Date.now()}:J&&{...J,lastAt:Date.now()});return}te.type==="article_failed"&&W.add(fe);let ye=(E.get(fe)??1)-1;E.set(fe,ye),be(J=>{if(!J)return J;let
|
|
513
|
+
`)}),le(Bs(w));return}Bt.current=Date.now(),Wt(0),P(!0);let A=new AbortController;Be.current=A;let U={aborted:!1,partitions:0,failed:0,notRun:0,producedNothing:!0},O={retryable:0,stopped:!1,paths:[]},b=async($,v)=>{let k=Sf($),R=new Map;for(let te of k)for(let he of te.paths)R.set(he,te.label);let E=new Map(k.map(te=>[te.label,te.paths.length])),W=new Set,F=Date.now();be({verb:"Pushing",unit:"article",total:$.length,done:0,active:[],rows:Jo(k),tools:0,chars:0,lastAt:Date.now(),startedAt:F,agentless:!0}),h({kind:"note",tone:"info",text:v?`Resolving cross-links in ${$.length} article${$.length===1?"":"s"} whose targets were saved this run (second pass)\u2026 (esc to stop)`:`Pushing ${$.length} article${$.length===1?"":"s"} to Document360 (${TO} at a time)\u2026 (esc to stop)`});let N=await CO({cwd:e,profileName:s,paths:$,...u?.allowUnpullable===!0?{allowUnpullable:!0}:{},signal:A.signal,onEvent:te=>{let he=te.type==="article_done"?te.result.path:te.type==="article_failed"?te.failure.path:te.path,fe=R.get(he);if(!fe)return;if(te.type==="article_start"){be(J=>J&&!J.active.includes(fe)?{...J,active:[...J.active,fe],rows:it(J.rows,fe,"active"),lastAt:Date.now()}:J&&{...J,lastAt:Date.now()});return}te.type==="article_failed"&&W.add(fe);let ye=(E.get(fe)??1)-1;E.set(fe,ye),be(J=>{if(!J)return J;let xe={...J,done:J.done+1,rows:xc(J.rows,fe),lastAt:Date.now()};return ye>0?xe:{...xe,active:xe.active.filter(Oe=>Oe!==fe),rows:it(xe.rows,fe,W.has(fe)?"failed":"done")}})}}),X=N.aborted||!!N.stopped,L=ld(e,$,s);v||(U={aborted:X,partitions:k.length,failed:N.failed.length,notRun:N.notRun.length,producedNothing:N.pushed.length===0,verifiedComplete:L>=$.length,leftBehind:m});let B=N.pushed.length>0;h({kind:"note",tone:X||N.failed.length>0||!B?"warn":"ok",text:(X?"Stopped. ":"")+cd(N,{recorded:L,elapsedMs:Date.now()-F,docsRoot:GO(e),...v?{}:{conflictsSkipped:m,unreadableSkipped:u?.unreadableSkipped??0},...u?.scope?{scope:u.scope}:{}}).join(`
|
|
514
514
|
`)}),v||(O={retryable:N.failed.filter(te=>!te.refused).length,stopped:!!N.stopped,paths:N.failed.map(te=>te.path)});let q=ad(e,s);if(q&&h({kind:"note",tone:"info",text:q}),B){let te=Lr(e,s);te&&h({kind:"note",tone:"info",text:te})}if(!X&&B){try{let he=await Tf({cwd:e,profileName:s,onRateLimit:ye=>Fe(`Document360 rate limit \u2014 waiting ${Math.ceil(ye.waitMs/1e3)}s\u2026`)}),fe=Af(he);fe?h({kind:"note",tone:fe.startsWith("\u26A0")?"warn":"info",text:fe}):he.skipped&&h({kind:"note",tone:"warn",text:`Order sync skipped: ${he.skipped}`})}catch(he){h({kind:"note",tone:"warn",text:`Order sync failed: ${he.message}`})}finally{Fe(null)}try{let he=Yv(await Vv({cwd:e,profileName:s}));he&&h({kind:"note",tone:"info",text:he})}catch(he){h({kind:"note",tone:"warn",text:`Related-articles sync failed: ${he.message}`})}let te=await Fs(e,s);te&&h({kind:"note",tone:"info",text:te})}return X};try{let $=await b(p,!1);if(!$){let k=await ud(e,s,p,O.paths);k.length>0&&await b(k,!0),xn(u?.scope?`/push ${u.scope}`:"/push --all",U)}let v=Se("/push",u?.scope?"":"--all",u?.scope);if(O.retryable>0)Pe(null,{kind:"cmd",cmd:v,reason:O.stopped?"Document360 stopped answering \u2014 your articles are unchanged, so this picks up where the run left off":`${O.retryable} article${O.retryable===1?"":"s"} could not be saved \u2014 this retries them`},"warn");else if(!$&&m>0)Pe(null,{kind:"cmd",cmd:"/resolve",reason:`${m} conflict${m===1?"":"s"} still need settling \u2014 they were left untouched`},"warn");else if(!$){let k=id(e,{scope:u?.scope,profileName:s});k&&Pe(null,{kind:"cmd",...k})}}catch($){h({kind:"note",tone:"error",text:`Publish run failed: ${$.message}`})}finally{Be.current=null,be(null),await Ji(),P(!1),D($=>$+1)}},[e,s,h,t,Pe,Ji]),mr=se(p=>h({kind:"block",text:p.join(`
|
|
515
515
|
`)}),[h]),gr=se((p,m,u,w,A)=>{let U=Mt(p,m,u,Date.now(),A);h({kind:"done",seconds:U.seconds,tokens:U.outputTokens,costUsd:U.costUsd,ok:w,...U.activeSeconds!==void 0?{activeSeconds:U.activeSeconds}:{},sessionCostUsd:Dt+u,...U.inputTokens>0?{inTokens:U.inputTokens,...U.cachedPct!==void 0?{cachedPct:U.cachedPct}:{}}:{}})},[h,Dt]),Zi=se(async p=>{let m=ji(e,"standard");Bt.current=Date.now(),Wt(0),P(!0);let u=new AbortController;Be.current=u;let w=Date.now(),A=new Mr;A.start(),be({verb:p.verb,unit:"article",total:p.partitions.length,done:0,active:[],rows:Jo(p.partitions),tools:0,chars:0,lastAt:Date.now(),startedAt:w}),h({kind:"note",tone:"info",text:`${p.headline} on ${m.model}\u2026 (esc to stop)`});let U=!0,O=[],b=!1,$={aborted:!1,partitions:0,failed:0,notRun:0,producedNothing:!0},v=new Set;try{for await(let k of Di({cwd:e,partitions:p.partitions,promptFor:p.promptFor,concurrency:p.concurrency,profileName:s,allowProdWrites:_n.current,model:m.model,signal:u.signal,scope:p.scope}))if(k.type==="partition_status")k.status==="running"?be(R=>R&&{...R,active:[...R.active,k.label],rows:it(R.rows,k.label,"active"),lastAt:Date.now()}):be(R=>R&&{...R,active:R.active.filter(E=>E!==k.label),done:R.done+1,rows:it(R.rows,k.label,k.status==="done"?"done":"failed"),lastAt:Date.now()});else if(k.type==="partition_stalled"){let R=os(k);be(E=>E&&R?{...E,stalledNote:R}:E)}else if(k.type==="partition_event"){let R=null;if(k.event.type==="tool"){let E=$f(k.event.name,k.event.input,p.partitions[k.index]?.paths??[]);E&&!v.has(E)&&(v.add(E),R=k.label)}be(E=>{if(!E)return E;let W={...E,lastAt:Date.now()};return k.event.type==="tool"?W.tools=E.tools+1:k.event.type==="text"&&(W.chars=E.chars+k.event.delta.length),R&&(W.rows=xc(W.rows,R)),W})}else if(k.type==="run_done"){So(F=>F+k.totalCostUsd),Ro(F=>F+k.results.reduce((N,X)=>N+X.outputTokens,0));let R=Cf(p.partitions,k.results);U=!!k.allEmpty||!!k.blocked,b=!!k.aborted,$={aborted:!!k.aborted,partitions:p.partitions.length,failed:R.failed.length,notRun:R.notRun.length,...k.blocked?{blocked:k.blocked}:{},...k.allEmpty?{allEmpty:!0}:{},...k.bailedEmpty?{bailedEmpty:!0}:{}};let E=[`${k.aborted?"Stopped. ":""}${k.results.length-R.failed.length}/${p.partitions.length} batches ${p.doneNoun}.`,...R.failed.length?[`\u26A0 ${R.failed.length} not ${p.doneNoun} \u2014 their articles are unchanged: ${R.failed.map(F=>F.label).join(", ")}`]:[],...R.notRun.length?[`\u26A0 ${R.notRun.length} batch${R.notRun.length===1?"":"es"} never started \u2014 their articles are untouched.`]:[],...k.blocked?[`\u26A0 ${k.blocked}`]:[],...k.allEmpty?[p.emptyNote??"\u26A0 No turn produced any work this run \u2014 nothing was changed."]:[],...p.caveatLines??[]];h({kind:"note",tone:k.aborted||R.failed.length||R.notRun.length||k.blocked||k.allEmpty?"warn":"ok",text:E.join(`
|
|
516
516
|
`)});let W=A.stop();if(gr(w,k.results,k.totalCostUsd,!b&&!U&&!R.failed.length,W.activeSeconds),p.calibrateAs){let F=k.results.filter(N=>N.ok).length;SO(e,p.calibrateAs,{articles:R.covered.length,waves:Math.ceil(F/Math.max(1,p.concurrency)),seconds:W.activeSeconds,costUsd:k.totalCostUsd})}U||(O=R.covered)}}catch(k){h({kind:"note",tone:"error",text:`${p.verb} failed: ${k.message}`})}finally{A.stop(),Be.current=null,be(null),P(!1),D(k=>k+1)}return{producedNothing:U,covered:O,aborted:b,outcome:$}},[e,s,h,gr]),m0=se(async(p,m,u,w=!1)=>{let A=await Zi({partitions:p.partitions,promptFor:p.promptFor,concurrency:ro,verb:"Reviewing",doneNoun:"reviewed",headline:`Reviewing ${p.articles} article${p.articles===1?"":"s"} in ${p.partitions.length} batches (\u2264${ro} editors at once)${m?", applying fixes as they go":", report only"}`+(()=>{let O=Vl(e,"/review",p.articles,p.partitions.length);return O?` \xB7 ${O}`:""})(),caveatLines:Oh(p,w),emptyNote:"\u26A0 No editor produced any work this run \u2014 nothing was reviewed or changed.",calibrateAs:"/review",scope:"review"}),U=null;if(m&&A.covered.length>0&&(U=Yo(e,A.covered,u,s),U.earned.length>0))try{ls(e,U.earned,yt("review"))}catch{}return{producedNothing:A.producedNothing,aborted:A.aborted,outcome:A.outcome,evidence:U}},[e,Zi]),Zc=se(async(p,m)=>{if(p.length===0)return;let u=p.reduce((v,k)=>v+k.threads.length,0),w=new Map,A=p.map(v=>(w.set(v.label,v),{label:v.label,paths:v.path?[v.path]:[]})),U=ji(e,"standard");Bt.current=Date.now(),Wt(0),P(!0);let O=new AbortController;Be.current=O;let b=Date.now(),$=Pf(e);be({verb:"Answering",unit:"article",total:A.length,done:0,active:[],rows:Jo(A),tools:0,chars:0,lastAt:Date.now(),startedAt:b}),h({kind:"note",tone:"info",text:`Answering ${u} comment${u===1?"":"s"} across ${A.length} article${A.length===1?"":"s"} (\u2264${pi} agents at once) on ${U.model}. Each article is fixed, pushed to Document360 as a draft, then answered in the thread${m.resolve?" and resolved":" \u2014 the threads stay open for your reviewers to close"}\u2026 (esc to stop)`});try{for await(let v of Di({cwd:e,partitions:A,promptFor:k=>{let R=w.get(k.label);return nh({articleId:R.articleId,label:R.label,path:R.path,count:R.threads.length,resolve:m.resolve})},concurrency:pi,profileName:s,allowProdWrites:_n.current,allowCommentResolve:m.resolve,model:U.model,signal:O.signal}))if(v.type==="partition_status")v.status==="running"?be(k=>k&&{...k,active:[...k.active,v.label],rows:it(k.rows,v.label,"active"),lastAt:Date.now()}):be(k=>k&&{...k,active:k.active.filter(R=>R!==v.label),done:k.done+1,rows:it(k.rows,v.label,v.status==="done"?"done":"failed"),lastAt:Date.now()});else if(v.type==="partition_stalled"){let k=os(v);be(R=>R&&k?{...R,stalledNote:k}:R)}else if(v.type==="partition_event")be(k=>{if(!k)return k;let R={...k,lastAt:Date.now()};return v.event.type==="tool"?R.tools=k.tools+1:v.event.type==="text"&&(R.chars=k.chars+v.event.delta.length),R});else if(v.type==="run_done"){So(L=>L+v.totalCostUsd),Ro(L=>L+v.results.reduce((B,q)=>B+q.outputTokens,0));let k=Cf(A,v.results),R=v.results.length-k.failed.length,E=v.aborted?"Stopped. ":"",W=[...k.failed.map(L=>L.label),...k.notRun.map(L=>L.label)],F=[`${E}${R}/${A.length} article${A.length===1?"":"s"} answered.`,...W.length?[`\u26A0 ${W.length} left untouched (their threads are still open, which is correct \u2014 nothing was promised to those reviewers): ${W.join(", ")}`]:[],...v.blocked?[`\u26A0 ${v.blocked}`]:[],...v.allEmpty?["\u26A0 No agent produced any work this run \u2014 nothing was changed or answered."]:[]];h({kind:"note",tone:v.aborted||W.length||v.blocked||v.allEmpty?"warn":"ok",text:F.join(`
|
|
517
517
|
`)}),gr(b,v.results,v.totalCostUsd,!v.aborted&&!W.length&&!v.blocked&&!v.allEmpty);let N=new Set(W),X=[...m.rest??[],...p.filter(L=>N.has(L.label))];if(X.length>0&&!v.aborted&&m.report){let L=X.reduce((q,te)=>q+te.threads.length,0);h({kind:"note",tone:"info",text:`${L} comment${L===1?"":"s"} on ${X.length} other article${X.length===1?"":"s"} still open \u2014 pick the next one, or esc to stop here.`});let B={...m.report,threads:X.flatMap(q=>q.threads)};mr(hi(B,{width:nt()})),yn({cursor:0,rows:Ml(X),groups:X,resolve:m.resolve,report:B})}else if(X.length>0&&!v.aborted){let L=X.map(q=>q.path).filter(q=>!!q),B=L.length===X.length?It(L):void 0;le(B?L.length===1?`/comments ${B}`:`/comments --scope ${B}`:"/comments")}xn("/comments --answer",{aborted:v.aborted,partitions:A.length,failed:k.failed.length,notRun:k.notRun.length,...v.blocked?{blocked:v.blocked}:{},...v.allEmpty?{allEmpty:!0}:{},leftBehind:(m.rest??[]).length})}}catch(v){h({kind:"note",tone:"error",text:`Answering comments failed: ${v.message}`})}finally{let k=Go(e,$,"","/comments").changed.length;k>0&&h({kind:"note",tone:"info",text:`${k} article${k===1?"":"s"} changed, and that new text has not been read by an editor \u2014 ${k===1?"it":"they"} can go live once /review has been over ${k===1?"it":"them"}.`}),Be.current=null,be(null),P(!1),D(R=>R+1)}},[e,s,h,Pe,le,mr,nt]),eu=se(async(p,m)=>{if(!(m?.skipReview===!0||m?.skipStyle===!0)){let w=Nc(e,[p],{});if(!w.clear){h({kind:"note",tone:"warn",text:Ef(w,1,{act:"push"}).join(`
|
|
518
|
-
`)}),le(Bs(w,p));return}}await kt([p],0,{skipReview:m?.skipReview,skipStyle:m?.skipStyle,scope:p});try{let w=await Qa(e,s,p.replace(/\\/g,"/"));w.length>0&&(yo(`Related articles not yet saved to Document360 (${w.length}):`),we([`/push --related ${p}`,...w.map(A=>`/push ${A}`)]))}catch{}},[e,s,
|
|
518
|
+
`)}),le(Bs(w,p));return}}await kt([p],0,{skipReview:m?.skipReview,skipStyle:m?.skipStyle,scope:p});try{let w=await Qa(e,s,p.replace(/\\/g,"/"));w.length>0&&(yo(`Related articles not yet saved to Document360 (${w.length}):`),we([`/push --related ${p}`,...w.map(A=>`/push ${A}`)]))}catch{}},[e,s,Re,kt]),lm=se(async(p,m)=>{P(!0);try{let u=await wd(e,s,p,{skipReview:m.skipReview,skipStyle:m.skipStyle,allowProduction:_n.current,onProgress:(A,U,O)=>Fe(`Publishing ${A+1}/${U} \u2014 ${O}\u2026`)}),w=u.blocked.length+u.failed.length;if(h({kind:"note",tone:w>0?"warn":"ok",text:bd(u).join(`
|
|
519
519
|
`)}),u.blocked.length>0){let A=Nc(e,u.blocked.map(O=>O.path),{allowUnreviewed:m.skipReview,allowStyleErrors:m.skipStyle,checkScreenshots:!0}),U=tl(e,A,m.scope);U.cmd?le(U.cmd):Pe(null,null)}else Pe(null,null)}catch(u){h({kind:"note",tone:"error",text:`Publishing failed: ${u.message}`})}finally{Fe(null),await Ji(),P(!1),D(u=>u+1)}},[e,s,h,le,Pe,Ji]),cm=se(async p=>{try{let m=await Ni({cwd:e,profileName:s}),u=new Set(Mo(e,s)),{clean:w,conflicts:A}=_o(m.entries,u);if(w.length===0){h({kind:"note",tone:"info",text:"Nothing new to save \u2014 everything is already up to date on Document360."});return}await kt(w.map(U=>U.path),A.length,{skipReview:p?.skipReview,skipStyle:p?.skipStyle,unreadableSkipped:A.filter(U=>u.has(U.path)).length})}catch(m){h({kind:"note",tone:"warn",text:`Couldn\u2019t check what to save (${m.message}) \u2014 your articles are written; run /push when ready.`})}},[e,s,kt]),xn=se((p,m)=>{if(m&&(Uc.current=m),!(m&&!Qt(m)))for(let u of Ye(e,p))h({kind:"note",tone:"ok",text:u})},[e,h]),um=se(async(p,m)=>{let w=LO(p,3),A=ji(e,"standard"),U=new Map,O=new Map;for(let k of p){let R=Lo(e,k),E=Ou(e,k,R);if(E&&U.set(k,E),R!==null){let W=zu(R);W.length>0&&O.set(k,W)}}Bt.current=Date.now(),Wt(0),P(!0);let b=new AbortController;Be.current=b;let $=Date.now();be({verb:"Authoring",unit:"spec",total:w.length,done:0,active:[],rows:Jo(w),tools:0,chars:0,lastAt:Date.now(),startedAt:$}),h({kind:"note",tone:"info",text:`Authoring ${p.length} screenshot spec${p.length===1?"":"s"} in ${w.length} batch${w.length===1?"":"es"} (\u22646 agents at once) on ${A.model}${A.forced?" (forced)":""}\u2026 (esc to stop)`});let v=!1;try{for await(let k of Di({cwd:e,partitions:w,promptFor:R=>ay(R,U,O),concurrency:6,profileName:s,allowProdWrites:_n.current,model:A.model,signal:b.signal}))if(k.type==="partition_status")k.status==="running"?be(R=>R&&{...R,active:[...R.active,k.label],rows:it(R.rows,k.label,"active"),lastAt:Date.now()}):be(R=>R&&{...R,active:R.active.filter(E=>E!==k.label),done:R.done+1,rows:it(R.rows,k.label,k.status==="done"?"done":"failed"),lastAt:Date.now()});else if(k.type==="partition_stalled"){let R=os(k);be(E=>E&&R?{...E,stalledNote:R}:E)}else if(k.type==="partition_event")be(R=>{if(!R)return R;let E={...R,lastAt:Date.now()};return k.event.type==="tool"?E.tools=R.tools+1:k.event.type==="text"&&(E.chars=R.chars+k.event.delta.length),E});else if(k.type==="run_done"){So(N=>N+k.totalCostUsd),Ro(N=>N+k.results.reduce((X,L)=>X+L.outputTokens,0));let R=k.results.filter(N=>N.ok).length,E=k.aborted?"Stopped. ":"";v=!k.aborted&&m.setup;let W=k.aborted?"":v?" Refreshing the staging checklist\u2026":` Next: /screenshot capture${m.scope?` ${m.scope}`:" --all"} (or /screenshot checklist first for the staging checklist).`,F=Xn(k,"Spec authoring");h({kind:"note",tone:F||k.aborted?"warn":k.ok?"ok":"warn",text:F?F.join(`
|
|
520
|
-
`):`${E}Authored specs \u2014 ${R}/${k.results.length} batches ok.${W}`}),gr($,k.results,k.totalCostUsd,!F&&!k.aborted&&k.ok),F&&(le("/doctor"),v=!1),!k.aborted&&k.ok&&!F&&xn(m.runLine)}}catch(k){h({kind:"note",tone:"error",text:`Spec authoring failed: ${k.message}`})}finally{Be.current=null,be(null),P(!1),D(k=>k+1)}if(v){let k=await Vr();k.kind==="forward-to-agent"&&k.prompt&&await
|
|
520
|
+
`):`${E}Authored specs \u2014 ${R}/${k.results.length} batches ok.${W}`}),gr($,k.results,k.totalCostUsd,!F&&!k.aborted&&k.ok),F&&(le("/doctor"),v=!1),!k.aborted&&k.ok&&!F&&xn(m.runLine)}}catch(k){h({kind:"note",tone:"error",text:`Spec authoring failed: ${k.message}`})}finally{Be.current=null,be(null),P(!1),D(k=>k+1)}if(v){let k=await Vr();k.kind==="forward-to-agent"&&k.prompt&&await Re(k.prompt,{display:k.display,quietUser:!0}),Gc()}},[e,s,h,Re,xn,Gc]),dm=se(async p=>{let m=Ou(e,p,Lo(e,p))??void 0,u=await Ba([p],void 0,{prior:m});u.kind==="forward-to-agent"&&u.prompt&&await Re(u.prompt,{display:u.display,quietUser:!0}),Vg(e,p)&&Pe(`Spec ready \u2014 /screenshot capture ${p} takes the screenshot; bare /screenshot picks the next shot.`,{kind:"cmd",cmd:`/screenshot capture ${p}`,reason:""})},[e,Re,Pe]),ea=se(async(p,m)=>{if(p.length===0)return{drawn:[],undrawn:[],cliMissing:!1};P(!0);let u=new AbortController;Be.current=u,be({verb:"Drawing callouts",unit:"screenshot",total:p.length,done:0,active:[],rows:p.map(R=>({label:R,items:1,status:"pending"})),tools:0,chars:0,lastAt:Date.now(),startedAt:Date.now()});let w=[],A=!1;try{let R=await Lp({cwd:e,ids:p,...m.force?{force:!0}:{},signal:u.signal,onStart:E=>be(W=>W&&{...W,active:[E],rows:it(W.rows,E,"active"),lastAt:Date.now()}),onDone:(E,W)=>be(F=>F&&{...F,active:[],done:F.done+1,rows:it(F.rows,E,W==="failed"?"failed":"done"),lastAt:Date.now()})});w=R.results,A=R.cliMissing??!1}catch(R){h({kind:"note",tone:"error",text:`Drawing the callouts failed: ${R.message}`})}finally{Be.current=null,be(null),P(!1),D(R=>R+1)}if(A)return h({kind:"note",tone:"error",text:"d360-capture is not installed on this machine. Run: npm i -g document360-capture \u2014 then /screenshot annotate."}),{drawn:[],undrawn:p,cliMissing:!0};let U=w.filter(R=>R.outcome==="annotated").map(R=>R.id),O=w.filter(R=>R.outcome==="skipped").map(R=>R.id),b=p.filter(R=>!U.includes(R)&&!O.includes(R));if(h({kind:"note",tone:b.length?"warn":"ok",text:[`\u25C8 ${U.length} drawn`,O.length?`${O.length} already current`:"",b.length?`\u2717 ${b.length} not drawn`:""].filter(Boolean).join(" ")}),b.length>0){let R=Math.max(...b.map(E=>E.length));h({kind:"note",tone:"warn",text:b.map(E=>`\u2717 ${E.padEnd(R)} ${ai(w.find(W=>W.id===E)?.detail)||"no picture was produced"}`).join(`
|
|
521
521
|
`)+`
|
|
522
|
-
${b.length===1?"It has":"They have"} no publishable picture yet. Re-take the shot: /screenshot capture`})}if(m.chainVerify===!1||U.length===0)return{drawn:U,undrawn:b,cliMissing:!1};let $=U.filter(R=>m.verifiedBefore.has(R)).length;if($>El){let R=bb({results:w,voidedVerdicts:$,...m.scope?{scope:m.scope}:{}});return R&&Pe(`${R.glyph} ${R.cmd} ${R.why}`,{kind:"cmd",cmd:R.cmd,reason:""},"warn"),{drawn:U,undrawn:b,cliMissing:!1}}let v=U.filter(R=>!m.verifiedBefore.has(R)||$>0);if(v.length===0)return{drawn:U,undrawn:b,cliMissing:!1};h({kind:"note",tone:"info",text:to(v.length)}),await
|
|
523
|
-
`)}),{drawn:U,undrawn:b,cliMissing:!1}},[e,h,Pe,
|
|
522
|
+
${b.length===1?"It has":"They have"} no publishable picture yet. Re-take the shot: /screenshot capture`})}if(m.chainVerify===!1||U.length===0)return{drawn:U,undrawn:b,cliMissing:!1};let $=U.filter(R=>m.verifiedBefore.has(R)).length;if($>El){let R=bb({results:w,voidedVerdicts:$,...m.scope?{scope:m.scope}:{}});return R&&Pe(`${R.glyph} ${R.cmd} ${R.why}`,{kind:"cmd",cmd:R.cmd,reason:""},"warn"),{drawn:U,undrawn:b,cliMissing:!1}}let v=U.filter(R=>!m.verifiedBefore.has(R)||$>0);if(v.length===0)return{drawn:U,undrawn:b,cliMissing:!1};h({kind:"note",tone:"info",text:to(v.length)}),await Re(pn(e,v),{display:"/screenshot verify",quietUser:!0});let k=Gr(e,v);return k.length>0&&h({kind:"note",tone:"info",text:k.join(`
|
|
523
|
+
`)}),{drawn:U,undrawn:b,cliMissing:!1}},[e,h,Pe,Re]),Cs=se(async(p,m)=>{if(!Ho(e)){await Re(Ep(),{display:"/screenshot capture \u2014 first-time setup",quietUser:!0});return}if(!cb(e)){h({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)"}),P(!0),Fe("Waiting for you to finish logging in (browser window)\u2026");let B=new AbortController;Be.current=B;let q;try{q=await ub({cwd:e,signal:B.signal})}catch(te){h({kind:"note",tone:"error",text:`Could not launch the login: ${te.message}`});return}finally{Be.current=null,Fe(null),P(!1)}if(q.exitCode!==0){Ap(q.exitCode,q.output)?h({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(q.output)?h({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."}):h({kind:"note",tone:B.signal.aborted?"warn":"error",text:B.signal.aborted?"Login cancelled \u2014 nothing saved.":`Login did not complete:
|
|
524
524
|
${Pp(q.output)}`});return}h({kind:"note",tone:"ok",text:"\u2713 Login saved \u2014 continuing with the capture."})}let u=new Map(p.map(B=>[B,Jg(e,B)])),w=0;if(p.length>1){let B=p.filter(q=>u.get(q).mutates);if(B.length>0&&(w=B.length,p=p.filter(q=>!u.get(q).mutates),p.length===0)){h({kind:"note",tone:"warn",text:`\u26A0 All ${B.length} spec${B.length===1?"":"s"} in scope write real data, so a bulk run has nothing to do. Run one deliberately: /screenshot capture ${B[0]}`});return}}else if(p.length===1&&u.get(p[0]).mutates){let B=u.get(p[0]),q=db(e);h({kind:"note",tone:"warn",text:`\u26A0 This capture performs a REAL write in your app${B.what?`: ${Qg(B.what)}`:B.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. `+(q.production?`This profile is PRODUCTION \u2014 the write lands in the LIVE tenant${q.url?` (${q.url})`:""}. If the spec cleans up after itself, confirm it did.`:"Tip: point the capture scope (.d360-writer/capture.json) at a demo/non-production context for shots like this.")})}P(!0);let A=new AbortController;Be.current=A;let U=p.map(B=>({label:B,items:1,status:"pending"}));be({verb:"Capturing",unit:"screenshot",total:p.length,done:0,active:[],rows:U,tools:0,chars:0,lastAt:Date.now(),startedAt:Date.now()});let O=Cn(e),b=Object.keys(O.shots).length>0,$=pb(m?.heldBackBlocked??[],w);h({kind:"note",tone:"info",text:`Capturing ${p.length} screenshot${p.length===1?"":"s"} with d360-capture (a real browser drives your app)\u2026 (esc to stop)`+($?`
|
|
525
525
|
${$}`:"")});let v=Date.now(),k=[];try{for(let B of p){if(A.signal.aborted)break;be(ye=>ye&&{...ye,active:[B],rows:it(ye.rows,B,"active"),lastAt:Date.now()});let{exitCode:q,output:te}=await Tp({cwd:e,id:B,signal:A.signal,stdio:"pipe"});if(Ap(q,te)){h({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 he=A.signal.aborted&&q!==0?"failed":Cp(e,B,q,v),fe=he==="captured"?void 0:he==="skipped"?ab(te)??"a data prerequisite isn't met \u2014 the spec's skip reason names what to stage":lb(te);k.push({id:B,outcome:he,detail:fe}),be(ye=>ye&&{...ye,active:[],done:ye.done+1,rows:it(ye.rows,B,he==="captured"?"done":"failed"),lastAt:Date.now()})}}catch(B){h({kind:"note",tone:"error",text:`Capture run failed: ${B.message}`})}finally{Be.current=null,be(null),P(!1),D(B=>B+1)}if(k.length===0)return;let R=k.filter(B=>B.outcome==="captured"),E=k.filter(B=>B.outcome==="skipped"),W=k.filter(B=>B.outcome==="failed"),F=p.length-k.length;h({kind:"note",tone:W.length?"warn":R.length?"ok":"info",text:[`\u2713 ${R.length} captured`,E.length?`\u2298 ${E.length} blocked`:"",W.length?`\u2717 ${W.length} broken`:"",F?`${F} not run (stopped)`:""].filter(Boolean).join(" ")+` ${k.length} run${w>0?` \xB7 ${w} held back`:""}`+mb(hb(O,k),b).map(B=>`
|
|
526
526
|
${B}`).join("")});let N=fb(E);if(N.length>0&&h({kind:"note",tone:"info",text:N.join(`
|
|
527
527
|
`)}),W.length>0){let B=Math.max(...W.map(q=>q.id.length));h({kind:"note",tone:"error",text:W.map(q=>`\u2717 ${q.id.padEnd(B)} ${ai(q.detail)}`).join(`
|
|
528
|
-
`)})}Ea(e,k.map(B=>({id:B.id,outcome:B.outcome,reason:B.detail,specSource:Lo(e,B.id)})),new Date().toISOString()),k.length===p.length&&W.length===0&&E.length===0&&xn(m?.runLine);let X=[];if(R.length>0&&Al()){let B=await ea(R.map(q=>q.id),{verifiedBefore:new Set});if(B.cliMissing||(X=B.undrawn,B.drawn.length>0))return}else if(R.length>0){h({kind:"note",tone:"info",text:to(R.length)}),await
|
|
529
|
-
`)});return}let L=gb({results:k,ids:p,articles:yb(e,R.map(B=>B.id)),...X.length>0?{unannotated:X}:{}});L&&Pe(L.note,{kind:"cmd",cmd:L.cmd,reason:""},L.tone)},[e,h,Pe,
|
|
528
|
+
`)})}Ea(e,k.map(B=>({id:B.id,outcome:B.outcome,reason:B.detail,specSource:Lo(e,B.id)})),new Date().toISOString()),k.length===p.length&&W.length===0&&E.length===0&&xn(m?.runLine);let X=[];if(R.length>0&&Al()){let B=await ea(R.map(q=>q.id),{verifiedBefore:new Set});if(B.cliMissing||(X=B.undrawn,B.drawn.length>0))return}else if(R.length>0){h({kind:"note",tone:"info",text:to(R.length)}),await Re(pn(e,R.map(q=>q.id)),{display:"/screenshot verify",quietUser:!0});let B=Gr(e,R.map(q=>q.id));B.length>0&&h({kind:"note",tone:"info",text:B.join(`
|
|
529
|
+
`)});return}let L=gb({results:k,ids:p,articles:yb(e,R.map(B=>B.id)),...X.length>0?{unannotated:X}:{}});L&&Pe(L.note,{kind:"cmd",cmd:L.cmd,reason:""},L.tone)},[e,h,Pe,Re,xn,ea]),tu=se(p=>{let m=ao(p);if(!m||er(m)&&!m.refreshToken)return null;let u={...us(m.idToken)??{},...us(m.accessToken)??{}};return u.email??u.preferred_username??"signed in"},[]),ta=se((p,m)=>{if(m){let u=$e(e);u&&(u.defaultProfile=p,Iv(u,e))}i(p),Ve(void 0,p),h({kind:"note",tone:"ok",text:`\u2713 Switched to profile "${p}"${m?" (saved as default)":" (this session only)"} \u2014 agent restarted.`}),tu(p)||(h({kind:"note",tone:"warn",text:`Profile "${p}" is not signed in \u2014 sign in with /login.`}),le("/login")),D(u=>u+1)},[e,h,Ve,tu,le]),nu=se(async(p,m,u,w)=>{let A=async()=>{try{let O=dt(e,p),b=await vO({profile:O.name,connection:O.connection,projectId:m,projectName:O.project.projectName},{cwd:e,profile:O.name,projectId:m,workspaceId:u}),$=xO(b);$.length>0&&h({kind:"note",tone:b.kind==="swept"&&b.hidden==="confirmed"?"ok":"warn",text:$.join(`
|
|
530
530
|
`)})}catch{}},U=u$(e,p);if(U==="fresh"){A(),h({kind:"note",tone:"ok",text:"\u2713 Connected. Configuration is done \u2014 one important step remains."}),h({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:"}),h({kind:"note",tone:"info",text:Pr(Cr(e)).join(`
|
|
531
531
|
`)}),$n(jo()),xs.current=Xt(e).status==="current",fr.current=Sn(e),h({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.`});return}if(U==="unpushed"){let O=p;try{O=dt(e,p).connection.portalUrl}catch{}h({kind:"note",tone:"ok",text:`\u2713 Connected. ${p} \xB7 ${w} \xB7 ${O}`}),Fe("Checking what this portal already holds\u2026"),await A(),Fe("Comparing your articles with this portal\u2026");try{let b=await Ni({cwd:e,profileName:p}),$=$O(e,p).length,v=b.counts["untracked-remote"]??0;h({kind:"note",tone:"ok",text:v===0?`\u2713 This portal has no articles yet.
|
|
532
532
|
Your ${$} ${$===1?"is":"are"} written here and none of them are on it.`:`\u2713 This portal already holds ${v} article${v===1?"":"s"}.
|
|
533
533
|
Nothing is pushed until you decide which of yours are already there.
|
|
534
534
|
/pull --status shows the comparison article by article.`})}catch{}finally{Fe(null),D(b=>b+1)}return}D(O=>O+1)},[e,h,$n]),na=se((p,m,u)=>{ia(e,p,m,u.id,u.name),h({kind:"note",tone:"ok",text:`Switched to workspace "${u.name??u.id}" (agent restarted).`}),Ve(),nu(p,m,u.id,u.name??u.id)},[e,h,Ve,nu]),oa=se(async p=>{let m;try{m=await Es(e,p)}catch{h({kind:"note",tone:"info",text:"Next: pick a workspace \u2014 run /workspace."}),le("/workspace");return}if(m.workspaces.length===0){h({kind:"note",tone:"warn",text:"This project has no workspaces yet. Create one in Document360, then run /workspace."});return}let u=m.workspaces.filter(A=>(A.workspace_type??"").toLowerCase()!=="apidocumentation"),w=u.length>0?u:m.workspaces;if(w.length===1){let A=w[0],U=m.workspaces.length-w.length;h({kind:"note",tone:"ok",text:`Selected the "${A.name??A.id}" workspace${U>0?" (skipped the API-documentation workspace)":""}.`}),na(m.profile,m.projectId,A);return}h({kind:"note",tone:"info",text:`Next: pick the workspace your articles publish to (${w.length} available).`}),le("/workspace")},[e,h,na,le]),ra=se((p,m)=>{aa(e,p,m.id,m.name),h({kind:"note",tone:"ok",text:`Project set to "${m.name??m.id}" (agent restarted).`}),Ve(),oa(p)},[e,h,Ve,oa]),pm=se(async p=>{let m;try{m=dt(e,p)}catch{return}if(m.project.workspaceId){let u=m.project.workspaceName??m.project.workspaceId;m.project.projectId&&await nu(m.name,m.project.projectId,m.project.workspaceId,u);return}if(m.project.projectId){if(!m.project.projectName)try{let{projects:u}=await Rn(e,m.name),w=u.find(A=>A.id===m.project.projectId);w?.name&&qO(e,m.name,{projectName:w.name})}catch{}await oa(m.name);return}try{let{projects:u}=await Rn(e,m.name);if(u.length===1){let w=u[0];h({kind:"note",tone:"ok",text:`Using your Document360 project "${w.name??w.id}".`}),ra(m.name,{id:w.id,name:w.name});return}if(u.length>1){let w=u.map(A=>({id:A.id,name:A.name??A.id,sub:A.sub_domain}));h({kind:"note",tone:"info",text:"Which Document360 project should these docs publish to?"}),fo({cursor:0,current:-1,rows:w,profile:m.name,environment:m.connection.name});return}}catch{}h({kind:"note",tone:"info",text:"Next: pick the Document360 project \u2014 run /project."}),le("/project")},[e,h,oa,ra,le]),yr=se(()=>{let p=je.current[0];if(!p)return;if(Kt(),p.mode==="local-only"){h({kind:"note",tone:"info",text:`\u25CF ${p.title??p.path} (${p.path})`});for(let U of ll())h({kind:"note",tone:"warn",text:`\u26A0 ${U}`});h({kind:"note",tone:"info",text:cl()}),h({kind:"note",tone:"info",text:Yy()});return}let{plan:m,mode:u}=p;h({kind:"note",tone:"info",text:`\u25CF ${m.title} (${m.path})`});for(let U of m.notes)h({kind:"note",tone:"warn",text:`\u26A0 ${U}`});m.overwritesLocalChanges&&u==="pull"&&h({kind:"note",tone:"warn",text:"\u26A0 This OVERWRITES local edits made since the last sync."});let w=_t(m.oldContent,m.newContent,nt(),_r);h(w?{kind:"diff",added:w.added,removed:w.removed,lines:w.lines,hidden:w.hidden}:{kind:"note",tone:"info",text:"Local file already matches the remote content \u2014 applying only advances the sync base."});let A=je.current.length;u==="conflict"?(h({kind:"note",tone:"warn",text:"Conflict \u2014 edited both here and on the portal (diff shows local \u2192 portal)."}),h({kind:"note",tone:"info",text:"Keep local (publish, overwrites portal) \xB7 take Portal (overwrites local) \xB7 Skip?"}),h({kind:"note",tone:"info",text:A>1?`(l/p/s \xB7 la/pa/sa for all ${A} \u2014 anything else stops, keeping your answers so far)`:"(l/p/s \u2014 anything else stops, keeping your answers so far)"})):h({kind:"note",tone:"info",text:A>1?`Write ${m.path}? (y/n \xB7 ya/na for all ${A} \u2014 anything else cancels)`:`Write ${m.path}? (y/n \u2014 anything else cancels)`})},[h,nt,Kt]),Ao=se(()=>{if(je.current.length>0){yr();return}let p=Gt.current;Gt.current=[];let m=mn.current;mn.current=[];for(let u of ul(m))h({kind:"note",tone:"warn",text:`\u26A0 ${u}`});p.length>0?(h({kind:"note",tone:"info",text:`Saving ${p.length} kept-local article${p.length===1?"":"s"} over the portal\u2026`}),kt(p,0,{allowUnpullable:!0})):m.length>0||le("/pull")},[yr,h,kt,le]),ou=se(p=>{let m=gc(p);if(!m)return!1;let u=je.current.splice(0),w=u.length;if(m==="local"){for(let A of u)Gt.current.push(h3(A));h({kind:"note",tone:"info",text:`Keeping local for all ${w} \u2014 will publish ${w===1?"it":"them"} over the portal.`})}else if(m==="portal"){let A=0;for(let U of u){if(U.mode==="local-only"){h({kind:"note",tone:"warn",text:`\u26A0 ${Pd(U.path)}`}),mn.current.push(U.path);continue}try{Ec({cwd:e,profileName:s},U.plan),A++}catch(O){h({kind:"note",tone:"error",text:`Take-portal failed for ${U.plan.path}: ${O.message}`})}}h({kind:"note",tone:"ok",text:`\u2713 Took the portal copy of ${A} article${A===1?"":"s"} (local overwritten, sync base advanced).`})}else{for(let A of u)A.mode==="local-only"&&mn.current.push(A.path);h({kind:"note",tone:"info",text:`Skipped all ${w} \u2014 still conflict${w===1?"":"s"}.`})}return Ao(),!0},[e,s,Ao,h]),hm=se(p=>{if(je.current.length===0)return!1;let m=p.trim(),u=m.toLowerCase(),w=je.current[0];if(w.mode==="local-only"){if(gc(m))return ou(m);if(u==="l"||u==="local"||u==="publish")je.current.shift(),Gt.current.push(w.path),h({kind:"note",tone:"info",text:`Publishing your copy of ${w.path} over the portal.`});else if(u==="s"||u==="skip")je.current.shift(),mn.current.push(w.path),h({kind:"note",tone:"info",text:`Skipped ${w.path} \u2014 still a conflict.`});else{let U=je.current.splice(0),O=Gt.current;return Gt.current=[],mn.current=[],h({kind:"note",tone:"info",text:`Resolve canceled. ${U.length} conflict${U.length===1?"":"s"} left untouched.`+(O.length>0?` Saving the ${O.length} article${O.length===1?"":"s"} you already chose to keep - an answer you gave is not undone by stopping.`:"")}),O.length>0&&kt(O,0,{allowUnpullable:!0}),!0}return Ao(),!0}if(w.mode==="conflict"){if(gc(m))return ou(m);if(u==="l"||u==="local")je.current.shift(),Gt.current.push(w.plan.path),h({kind:"note",tone:"info",text:`Keeping local ${w.plan.path} \u2014 will publish over the portal.`});else if(u==="p"||u==="portal"){je.current.shift();try{Ec({cwd:e,profileName:s},w.plan),h({kind:"note",tone:"ok",text:`\u2713 Took the portal copy of ${w.plan.path} (local overwritten, sync base advanced).`})}catch(U){h({kind:"note",tone:"error",text:`Take-portal failed: ${U.message}`})}}else if(u==="s"||u==="skip"||u==="n")je.current.shift(),h({kind:"note",tone:"info",text:`Skipped ${w.plan.path} \u2014 still a conflict.`});else{let U=je.current.length;je.current=[];let O=Gt.current;return Gt.current=[],mn.current=[],h({kind:"note",tone:"info",text:`Resolve canceled. ${U} conflict${U===1?"":"s"} left untouched.`+(O.length>0?` Saving the ${O.length} article${O.length===1?"":"s"} you already chose to keep - an answer you gave is not undone by stopping.`:"")}),O.length>0&&kt(O,0,{allowUnpullable:!0}),!0}return Ao(),!0}let A=_$(m);if(A){let U=je.current.splice(0);if(A==="skip")h({kind:"note",tone:"info",text:`Skipped all ${U.length} article${U.length===1?"":"s"}.`});else{let O=0;for(let b of U){if(b.mode==="local-only"){h({kind:"note",tone:"warn",text:`\u26A0 ${Pd(b.path)}`});continue}try{Ec({cwd:e,profileName:s},b.plan),O++}catch($){h({kind:"note",tone:"error",text:`Pull failed for ${b.plan.path}: ${$.message}`})}}h({kind:"note",tone:"ok",text:`\u2713 Pulled ${O} article${O===1?"":"s"} (sync base advanced).`})}return Ao(),!0}if(u==="y"||u==="yes"){let{plan:U}=w;je.current.shift();try{Ec({cwd:e,profileName:s},U),h({kind:"note",tone:"ok",text:`\u2713 Pulled ${U.path} (sync base advanced).`})}catch(O){h({kind:"note",tone:"error",text:`Pull failed: ${O.message}`})}}else if(u==="n"||u==="no")je.current.shift(),h({kind:"note",tone:"info",text:`Skipped ${w.plan.path}.`});else{let U=je.current.length;return je.current=[],h({kind:"note",tone:"info",text:`Pull cancelled (${U} article(s) left untouched).`}),!0}return Ao(),!0},[e,s,Ao,h,ou]),ru=se(async p=>{h({kind:"note",tone:"info",text:"Checking Document360 for conflicts\u2026"});try{let m=await Ni({cwd:e,profileName:s}),u=p?.replace(/\\/g,"/"),w=al(e,s,m.entries,Uo(m.entries).map(O=>O.path));if(u){if(!w.includes(u)){h({kind:"note",tone:"ok",text:`${u} isn\u2019t waiting on a decision \u2014 nothing to resolve.`});return}w=[u]}if(w.length===0){h({kind:"note",tone:"ok",text:"\u2713 No conflicts \u2014 local and Document360 agree on every tracked article."});return}let A=[];for(let O of w)try{A.push({plan:await Mv({cwd:e,profileName:s,relPath:O}),mode:"conflict"})}catch(b){Uv(b)?A.push({mode:"local-only",path:O,title:Fv(e,O)}):h({kind:"note",tone:"error",text:`Could not load ${O} from Document360: ${b.message}`})}if(A.length===0)return;let U=A.some(O=>O.mode!=="local-only");h({kind:"note",tone:"info",text:U?`Resolving ${A.length} conflict${A.length===1?"":"s"} \u2014 for each: keep local, take portal, or skip.`:`Resolving ${A.length} conflict${A.length===1?"":"s"} \u2014 for each: publish your copy over the portal, or skip.`}),je.current=A,Gt.current=[],mn.current=[],yr()}catch(m){h({kind:"note",tone:"error",text:`Could not compute sync status: ${m.message}`})}},[e,s,h,yr]),fm=se(p=>{let m=sr.current;if(!m)return!1;if(sr.current=null,Kt(),p.trim()!==m.repoName)return h({kind:"note",tone:"warn",text:"Reset cancelled \u2014 nothing deleted."}),!0;let{removed:u,failed:w}=ch(e,m.targets);h({kind:"note",tone:w.length?"warn":"ok",text:`\u2713 Reset complete \u2014 removed ${u.length} item${u.length===1?"":"s"}. The repo is back to its original state.`});for(let A of w)h({kind:"note",tone:"error",text:` \u2717 ${A.path}: ${A.error}`});return m.hadDevhints&&(h({kind:"note",tone:"warn",text:$a().join(`
|
|
535
535
|
`)}),$n(Su())),h({kind:"note",tone:"info",text:`Clean slate \u2014 set up d360-writer again whenever you're ready:
|
|
536
|
-
${Qv}`}),le("/init"),Ct(!1),!0},[e,h,$n,Kt,le]),Eo=se(async p=>{let m=p.slice(1).trim().split(/\s+/),u=(m[0]??"").toLowerCase(),w=m.slice(1);if(tm.current=p.trim(),Ct(!0),ei(w)&&["write","lint","check","review","reorder"].includes(u)){let b=Zs(e,s);if(b.length===0){h({kind:"note",tone:"warn",text:"No categories yet \u2014 author some articles first, then scope to one."});return}let $=v=>u==="write"?`/write ${Pw(w,v).join(" ")}`.trim():`/${u} ${[v,...w.filter(k=>k!=="--scope")].join(" ")}`.trim();if(b.length===1){Eo($(b[0].token));return}go({cursor:0,rows:b,run:v=>{Eo($(v))}});return}if(eo(u,w)){let b=u==="write"&&ti(w).rewrite,$=u==="write"?up(e,Bv(e),{rewrite:b}):Zs(e,s);if($.length>1){let v=w.filter(q=>q.startsWith("--")).join(" "),k=q=>q==="--blocked"?`/${u} --blocked${v?` ${v}`:""}`.trim():`/${u}${q?` ${u==="write"?`--scope ${q}`:q}`:""}${v?` ${v}`:""} ${q?"":"--all"}`.replace(/\s+/g," ").trim(),R=$.reduce((q,te)=>q+te.count,0),E={token:"",label:u==="review"?`Everything \u2014 all ${R} articles + structure (IA) review`:u==="reorder"?`Everything \u2014 reorder all ${$.length} categories`:b?`Everything \u2014 rewrite all ${R} planned articles`:`Everything \u2014 write all ${R} pending article${R===1?"":"s"}`,count:R},W=u==="review"?Nh(e):[],F=W.length>0&&W.length<R?[{token:"--blocked",label:`Only what needs review \u2014 ${W.length} article${W.length===1?"":"s"} publishing is blocked on`,count:W.length}]:[],N=u==="review"?Dh(e):[],X=N.length>W.length&&N.length<R?[{token:"--stale",label:`Everything the current editor hasn\u2019t read \u2014 ${N.length} article${N.length===1?"":"s"}`,count:N.length}]:[],L=u==="write"?`Write which articles? (counts = ${b?"articles to regenerate":"still to write"})`:u==="review"?"Review what? (counts = articles)":"Reorder what?";go({cursor:0,rows:(q=>u==="review"||u==="write"?q.map(te=>{let he=(()=>{try{return ok(e,k(te.token))}catch{return null}})();return he?{...te,label:`${te.label} \xB7 ${he}`}:te}):q)([...F,...X,E,...$]),run:q=>{Eo(k(q))},title:L});return}}let A=async b=>{let $=xp(b);if(!Ho(e)){await
|
|
536
|
+
${Qv}`}),le("/init"),Ct(!1),!0},[e,h,$n,Kt,le]),Eo=se(async p=>{let m=p.slice(1).trim().split(/\s+/),u=(m[0]??"").toLowerCase(),w=m.slice(1);if(tm.current=p.trim(),Ct(!0),ei(w)&&["write","lint","check","review","reorder"].includes(u)){let b=Zs(e,s);if(b.length===0){h({kind:"note",tone:"warn",text:"No categories yet \u2014 author some articles first, then scope to one."});return}let $=v=>u==="write"?`/write ${Pw(w,v).join(" ")}`.trim():`/${u} ${[v,...w.filter(k=>k!=="--scope")].join(" ")}`.trim();if(b.length===1){Eo($(b[0].token));return}go({cursor:0,rows:b,run:v=>{Eo($(v))}});return}if(eo(u,w)){let b=u==="write"&&ti(w).rewrite,$=u==="write"?up(e,Bv(e),{rewrite:b}):Zs(e,s);if($.length>1){let v=w.filter(q=>q.startsWith("--")).join(" "),k=q=>q==="--blocked"?`/${u} --blocked${v?` ${v}`:""}`.trim():`/${u}${q?` ${u==="write"?`--scope ${q}`:q}`:""}${v?` ${v}`:""} ${q?"":"--all"}`.replace(/\s+/g," ").trim(),R=$.reduce((q,te)=>q+te.count,0),E={token:"",label:u==="review"?`Everything \u2014 all ${R} articles + structure (IA) review`:u==="reorder"?`Everything \u2014 reorder all ${$.length} categories`:b?`Everything \u2014 rewrite all ${R} planned articles`:`Everything \u2014 write all ${R} pending article${R===1?"":"s"}`,count:R},W=u==="review"?Nh(e):[],F=W.length>0&&W.length<R?[{token:"--blocked",label:`Only what needs review \u2014 ${W.length} article${W.length===1?"":"s"} publishing is blocked on`,count:W.length}]:[],N=u==="review"?Dh(e):[],X=N.length>W.length&&N.length<R?[{token:"--stale",label:`Everything the current editor hasn\u2019t read \u2014 ${N.length} article${N.length===1?"":"s"}`,count:N.length}]:[],L=u==="write"?`Write which articles? (counts = ${b?"articles to regenerate":"still to write"})`:u==="review"?"Review what? (counts = articles)":"Reorder what?";go({cursor:0,rows:(q=>u==="review"||u==="write"?q.map(te=>{let he=(()=>{try{return ok(e,k(te.token))}catch{return null}})();return he?{...te,label:`${te.label} \xB7 ${he}`}:te}):q)([...F,...X,E,...$]),run:q=>{Eo(k(q))},title:L});return}}let A=async b=>{let $=xp(b);if(!Ho(e)){await Re(Ep(),{display:"/screenshot capture \u2014 first-time setup",quietUser:!0});return}if($.mode==="single"){await Cs([$.id],{runLine:[`/screenshot capture ${$.id}`,`/capture ${$.id}`]});return}if($.mode==="failed"){let k=Rp(e,li(e).rows);if(k.length===0){h({kind:"note",tone:"info",text:"No broken specs to re-run \u2014 /screenshot list shows where things stand."}),le("/screenshot list");return}await Cs(k);return}if($.mode==="blocked"){let{blocked:k,rows:R}=li(e);if(k.length===0){h({kind:"note",tone:"info",text:"No shots are waiting on app data \u2014 /screenshot list shows where things stand."}),le("/screenshot list");return}let E=_s(R.filter(W=>W.state==="blocked")).length;h({kind:"note",tone:"info",text:`Re-running ${k.length} shot${k.length===1?"":"s"} that skipped on missing data last run.`+(E>0?` If you haven't staged ${E===1?"it":"those "+E+" things"} yet, ${k.length===1?"it":"they"} will skip again \u2014 /screenshot list has the worklist.`:"")}),await Cs(k,{runLine:["/screenshot capture --blocked","/screenshot capture --all --retry-blocked"]});return}if($.mode==="all"||$.mode==="scope"){let k=$.mode==="scope"?$.scope:void 0,{ready:R,captured:E,blocked:W,rows:F}=li(e,k),N=[...R,...E,...$.retryBlocked?W:[]];if(N.length===0&&W.length>0&&!$.retryBlocked){let L=k?`/screenshot capture ${k} --retry-blocked`:"/screenshot capture --blocked";Pe(`All ${W.length} shot${W.length===1?"":"s"} in scope ${W.length===1?"is":"are"} blocked on data that wasn't staged last run \u2014 /screenshot list shows what to stage, then ${L}.`,{kind:"cmd",cmd:"/screenshot list",reason:""},"warn");return}if(N.length===0){h({kind:"note",tone:"info",text:"No authored specs to capture yet \u2014 author them first with /screenshot."}),le("/screenshot");return}if(!$.retryBlocked){let L=k?`/screenshot capture ${k} --retry-blocked`:"/screenshot capture --blocked",B=Sp(F.filter(q=>q.state==="blocked"),L);B&&h({kind:"note",tone:"info",text:B})}let X=$.mode==="scope"?[`/screenshot capture ${$.scope}`,`/capture ${$.scope}`]:["/screenshot capture --all","/capture --all"];await Cs(N,{runLine:X,...$.retryBlocked?{}:{heldBackBlocked:F.filter(L=>L.state==="blocked")}});return}let v=ib(He(e));if(v.length===0){h({kind:"note",tone:"info",text:"No authored specs to capture yet \u2014 author them first with /screenshot."}),le("/screenshot");return}mo({cursor:0,rows:v,title:"Capture screenshots \u2014 pick what to shoot:"})},U=async b=>{let $=Dp(b);if(!Ho(e)){h({kind:"note",tone:"info",text:"Screenshot capture is not set up in this repo yet. Run /screenshot capture and it will walk you through it."}),le("/screenshot capture");return}let v=vt();if(!Al(v)){h({kind:"note",tone:"info",text:Ip(v)}),le("/update");return}let k=$.mode==="scope"?$.scope:void 0,{pending:R,done:E,rows:W}=jp(e,k),F=$.mode==="all"||$.mode==="scope"?$.force:!1,N=$.mode==="single"?[$.id]:F?[...R,...E]:R;if(N.length===0){h({kind:"note",tone:"info",text:E.length>0?`Every shot${k?` under ${k}`:""} is already drawn on. To re-draw them anyway (after a wording or style change): /screenshot annotate --all --force`:"No shots have been taken yet \u2014 /screenshot capture takes them first."}),le(E.length>0?"/screenshot annotate --all --force":"/screenshot capture");return}await ea(N,{force:F,scope:k,verifiedBefore:new Set(W.filter(X=>X.state==="verified").map(X=>X.id))})},O=async()=>{let b=await Vr(w,{cwd:e,profileName:s});b.kind==="forward-to-agent"&&b.prompt&&await Re(b.prompt,{display:b.display,quietUser:!0}),Gc()};switch(u){case"help":xo({tab:0,scroll:0});return;case"exit":case"quit":Ne.current?.close(),r();return;case"clear":Ve(),de.current=[],mt.current={printed:[],armed:[],printedAt:hr.current},Ct(!1),Kt(),console.log("\x1B[?2026h\x1B[H\x1B[2J\x1B[3J\x1B[?2026l"),h({kind:"note",tone:"info",text:"Conversation reset (the previous session is still resumable via /resume)."});return;case"login":{let b;try{b=dt(e,s)}catch($){h({kind:"note",tone:"error",text:$.message});return}h({kind:"note",tone:"info",text:`Profile "${b.name}" \u2192 ${b.connection.name} (${b.connection.apiUrl})${b.production?" \u26A0 PRODUCTION":""}`}),j(!0);try{let $=await JL(b.connection,{promptForRedirect:()=>Promise.reject(new Error("Manual login is CLI-only. Run: d360-writer login --manual"))},R=>h({kind:"note",tone:"info",text:R})),v=ZL(b.name,$);eO(v),await mu(v,b.name,R=>h({kind:"note",tone:"info",text:R}));let k=Dv(v);if(k){let R=dt(e,s).project;await fu(b.name,k,R.projectId===k?R.projectName:void 0)}if(h({kind:"note",tone:"ok",text:`\u2713 Logged in to "${b.name}" as ${ua(ao(b.name,k??void 0)??v)}`}),await pm(s),!$o.current){let R=Je(e,s);R.ok||(h({kind:"note",tone:"warn",text:R.reason}),le("/login"))}if($o.current){let R=$o.current;$o.current=null;let E=Je(e,s);E.ok?(h({kind:"note",tone:"ok",text:`\u2713 Signed into the right project \u2014 tab to run ${R}.`}),le(R)):(h({kind:"note",tone:"warn",text:`${E.reason} (then ${R}.)`}),le("/login"),$o.current=R)}else ys.current&&(ot(ys.current,"\u21E5 tab to re-send your last prompt\u2026"),ys.current=null)}catch($){if(h({kind:"note",tone:"error",text:`Login failed: ${$.message}`}),!Ta(e,s)){let v=$o.current;h({kind:"note",tone:"warn",text:`Your Document360 session still isn't working, so nothing here can reach your portal${v?` \u2014 ${v} is still waiting`:""}. Run /login to try again.`}),le("/login")}}finally{j(!1),D($=>$+1)}return}case"allow-prod":{let b=!1;try{b=dt(e,s).production}catch{}if(!b){h({kind:"note",tone:"info",text:"Current profile is not production \u2014 writes are already allowed."});return}_n.current=!0,Ve(),h({kind:"note",tone:"warn",text:"\u26A0 Production writes authorized for this session."});return}case"rename":{let b=wp(w.join(" ")),$=et.current.uuid;if(!$){if(!b){h({kind:"note",tone:"info",text:"Usage: /rename <name>"});return}Lc.current=b,po(b),rs(`${kc} ${b}`),h({kind:"note",tone:"ok",text:`Session will be named "${b}" once it starts.`});return}if(!b){h({kind:"note",tone:"info",text:"Thinking of a name\u2026"});let v=et.current.firstPrompt??"";jv(v,e).then(k=>{k?(ot(`/rename ${k}`),h({kind:"note",tone:"info",text:`Suggestion: "${k}" \u2014 press tab to accept, or type /rename <your name>.`})):h({kind:"note",tone:"info",text:"Usage: /rename <name>"})}).catch(()=>h({kind:"note",tone:"info",text:"Usage: /rename <name>"}));return}fO($,b),po(b),rs(`${kc} ${b}`),h({kind:"note",tone:"ok",text:`Session renamed to "${b}".`});return}case"profile":{let b=w[0],$=$e(e);if(!b){let v=Object.entries($?.profiles??{});if(v.length===0){h({kind:"note",tone:"info",text:"No profiles. Run /init first."});return}let k=v.map(([F,N])=>{let X=N.connection?.environment,L="";try{L=X?DO(X).portalUrl:N.connection?.portalUrl??""}catch{L=""}return{name:F,env:X??"custom",portalUrl:L,project:N.project?.projectName??N.project?.projectId??null,prod:N.production??jO(X),internal:IO(X),who:tu(F)}}),R=s??$?.defaultProfile,E=Math.max(0,k.findIndex(F=>F.name===R)),W={name:"\uFF0B Connect another Document360 environment",env:"",portalUrl:"",project:null,prod:!1,internal:!1,who:null,connect:!0};ir({cursor:E,current:E,rows:[...k,W]});return}if(b==="add"){if(!w[1]){Mn({cursor:0,mode:"connect",rows:ca()});return}let v=xl(e,w[1],w[2]);if(v){h({kind:"note",tone:"error",text:v});return}let[k,...R]=Rl(w[1],w[2]??w[1]);h({kind:"note",tone:"ok",text:k});for(let E of R)h({kind:"note",tone:"warn",text:E});ta(w[1],!0);return}if(!$?.profiles?.[b]){h({kind:"note",tone:"error",text:`Unknown profile "${b}". Create it: /profile add ${b} <environment>`});return}ta(b,!0);return}case"doctor":await oi(w,{cwd:e,version:o});return;case"logs":{let b=gu();h({kind:"note",tone:"info",text:b.lines.join(`
|
|
537
537
|
`)}),b.copyText&&$n(b.copyText,{hint:b.notable?"\u2328 Press c to copy this problem report \u2014 then send it to the team (Teams / Slack / email).":"\u2328 Press c to copy this entry, if you want to send it to the team."});return}case"style-guide":{let b=bh(e);if(zr(w).sub==="use"){let v=kh(e,zr(w).value);if(v.pick){h({kind:"note",tone:"info",text:`${v.pick.title}
|
|
538
538
|
${v.pick.lead}`}),we(v.pick.options.map(k=>({cmd:k.cmd,desc:k.desc}))),yo(v.pick.title),Gi.current=v.pick.options.map(k=>k.cmd.trim()),D(k=>k+1);return}if(v.lines.length>0&&h({kind:"note",tone:"info",text:v.lines.join(`
|
|
539
539
|
`)}),v.restart&&(Ve(),h({kind:"note",tone:"ok",text:"\u2713 Agent restarted \u2014 it now writes to the new rules."})),v.editorial){let k=en(e,v.editorial.cmd);Pe($h(v.editorial).join(`
|
|
540
|
-
`),{kind:"cmd",cmd:v.editorial.cmd,reason:"it reads every article the new guide flagged and rewrites it",...k?{detail:k}:{}})}D(k=>k+1);return}let $=await ql(w,{cwd:e,profileName:s});if(b&&zr(w).sub==="show"&&Pe(null,{kind:"cmd",cmd:"/style-guide keep",reason:""}),$.kind==="clear")Ve(),h({kind:"note",tone:"ok",text:"\u2713 Agent restarted \u2014 it now writes to the new rules."});else if($.kind==="forward-to-agent"){let v=zr(w).sub==="learn";(await
|
|
540
|
+
`),{kind:"cmd",cmd:v.editorial.cmd,reason:"it reads every article the new guide flagged and rewrites it",...k?{detail:k}:{}})}D(k=>k+1);return}let $=await ql(w,{cwd:e,profileName:s});if(b&&zr(w).sub==="show"&&Pe(null,{kind:"cmd",cmd:"/style-guide keep",reason:""}),$.kind==="clear")Ve(),h({kind:"note",tone:"ok",text:"\u2713 Agent restarted \u2014 it now writes to the new rules."});else if($.kind==="forward-to-agent"){let v=zr(w).sub==="learn";(await Re($.prompt,{display:$.display,quietUser:!0})).producedNothing||(h({kind:"note",tone:"info",text:(v?Qb():Jb()).join(`
|
|
541
541
|
`)}),v&&(Ve(),h({kind:"note",tone:"ok",text:"\u2713 Agent restarted \u2014 it now writes to your house rules."})))}D(v=>v+1);return}case"genre":{let b=$e(e)?.appType??null;(await tc(w,{cwd:e})).kind==="clear"&&($e(e)?.appType??null)!==b&&(Ve(),h({kind:"note",tone:"ok",text:"\u2713 Agent restarted \u2014 the doc-craft playbook for this genre is now loaded."}));try{ze.current=!($e(e)?.appType??_v(e))}catch{}D(v=>v+1);return}case"roadmap":{if(w[0]?.toLowerCase()==="done"){let b=Ir(e,w.slice(1).join(" ").trim());h({kind:"note",tone:b.ok?"ok":"warn",text:b.line}),D($=>$+1);return}h({kind:"block",text:od(e,{width:nt(),all:w.some(b=>b.toLowerCase()==="--all")}).join(`
|
|
542
542
|
`)});return}case"mcp":await Sa(w);return;case"init":{if($e(e)){let b=Wn(e,s);h({kind:"note",tone:"info",text:["This repo is already set up \u2014 /init won\u2019t overwrite it."," Publish somewhere else: /profile, then pick \u201CConnect another Document360 environment\u201D."," The environment lives on the profile, not on your login \u2014 so signing in again never changes it."," Anything else: edit .d360-writer/config.json, or run d360-writer init for the CLI wizard."].join(`
|
|
543
543
|
`)}),b&&h({kind:"note",tone:"warn",text:`\u26A0 ${b.why} \u2014 ${b.cmd} moves this repo.`});return}Mn({cursor:0,mode:"init",rows:ca()});return}case"resume":{let b=w.join(" ").trim(),$=hO(e).filter(R=>R.uuid!==et.current.uuid);if(!b){if(!$.length){h({kind:"note",tone:"info",text:"No saved sessions for this repo yet."});return}vo({query:"",cursor:0,sessions:$});return}let v=pO(e,b);if(!v){h({kind:"note",tone:"error",text:`No session matches "${b}".`});return}Ve(v.uuid),et.current={uuid:v.uuid,firstPrompt:v.firstPrompt,titleFired:!0},po(v.name),Rf(v.uuid),de.current=[],mt.current={printed:[],armed:[],printedAt:hr.current};let k=yf(e,v.uuid);for(let R of k)h(R);h({kind:"note",tone:"ok",text:k.length?`Resumed "${v.name}" \u2014 restored ${k.length} message(s); continue where you left off.`:`Resumed "${v.name}" (agent memory reconnected; no saved transcript to replay).`});return}case"workspace":{let b=w.join(" ").trim(),$;try{$=await Es(e,s)}catch(k){h({kind:"note",tone:"error",text:`Could not list workspaces: ${k.message}`});return}if(!b){let k=$.workspaces.map(E=>({id:E.id,name:E.name??E.id,type:E.workspace_type}));if(k.length===0){h({kind:"note",tone:"info",text:"No workspaces in this project."});return}let R=Math.max(0,k.findIndex(E=>E.id===$.current));ar({cursor:R,current:R,rows:k,profile:$.profile,projectId:$.projectId,projectName:$.projectName,environment:$.environment});return}let v=au($.workspaces,b);if(!v){h({kind:"note",tone:"error",text:`No workspace matches "${b}".`});return}na($.profile,$.projectId,v);return}case"project":{let b=w.join(" ").trim(),$;try{$=await Rn(e,s)}catch(k){h({kind:"note",tone:"error",text:`Could not list projects: ${k.message} (signed in? try /login)`});return}if(!b){let k=$.projects.map(E=>({id:E.id,name:E.name??E.id,sub:E.sub_domain}));if(k.length===0){h({kind:"note",tone:"info",text:"No projects found for this identity."});return}let R=k.findIndex(E=>E.id===$.current);fo({cursor:Math.max(0,R),current:R,rows:k,profile:$.profile,environment:$.environment});return}let v=lu($.projects,b);if(!v){h({kind:"note",tone:"error",text:`No project matches "${b}". Available: ${$.projects.map(k=>k.name??k.id).join(", ")}`});return}ra($.profile,v);return}case"logout":{let b,$,v;try{let F=dt(e,s);b=F.name,$=F.project.projectId,v=F.project.projectName}catch(F){h({kind:"note",tone:"error",text:F.message});return}let k=w.includes("--all-projects"),R=aO(b).filter(F=>{let N=Dv(F);return!!N&&N!==$}).length,E=sO(b,k?void 0:$);lO(e,b),Ve(),D(F=>F+1);let W=k||R===0?"":` Your sign-in for ${R} other project${R===1?"":"s"} is untouched (/logout --all-projects clears those too).`;h({kind:"note",tone:E?"ok":"info",text:E?k?`\u2713 Signed out of every Document360 project for "${b}" and cleared this repo's project/workspace selection.`:`\u2713 Signed out of ${v?`"${v}"`:`"${b}"`} and cleared its project/workspace selection.${W}`:`Profile "${b}" was not signed in. Cleared any project/workspace selection.${W}`}),h({kind:"note",tone:"info",text:"Run /login to sign in and pick a project."}),le("/login");return}case"publish":{if(md(w)){h({kind:"note",tone:"info",text:el()}),await Eo(`/push ${w.join(" ")}`.trim());return}if(!Po(p.trim()))return;let b=w.find(N=>!N.startsWith("--")),$={skipReview:w.includes(Lt),skipStyle:w.includes(Kv),...b?{scope:b}:{}};Fe("Checking what is waiting to go live\u2026");let v;try{v=await gd(e,s,b)}catch(N){h({kind:"note",tone:"error",text:`Could not check Document360: ${N.message}`});return}finally{Fe(null)}if(v.length===0){let N=yd(e,s,b);for(let X of N.lines)h({kind:"note",tone:X.startsWith("\u2713")?"ok":"info",text:X});N.ahead>0&&le(Se("/push","",b));return}let k=Nc(e,v.map(N=>N.path),{allowUnreviewed:$.skipReview,allowStyleErrors:$.skipStyle,checkScreenshots:!0,allowPlaceholders:w.includes(QO)}),R=XO(k),E=v.filter(N=>R.has(N.path)),W=v.filter(N=>!R.has(N.path)),F=tl(e,k,b);if(E.length>0)for(let N of Ef(k,v.length,{scope:b,act:"publish",going:W.length,shotFix:F.shotFix}))h({kind:"note",tone:"warn",text:N});if(W.length===0){F.cmd&&le(F.cmd);return}if(v=W,Xe.current&&Ui.current){h({kind:"note",tone:"info",text:`Taking ${v.length} draft${v.length===1?"":"s"} live \u2014 you approved this run when it started.`}),await lm(v,$);return}h({kind:"note",tone:"info",text:`${v.length} draft${v.length===1?" is":"s are"} ahead of what readers see. Pick what goes live:`}),cr({cursor:0,rows:v,opts:$});return}case"push":{if(!Po(p.trim()))return;let b=w.includes(Lt),$=w.includes(Kv);if(w.includes("--preview")){let E=w.find(W=>!W.startsWith("--"));if(!E){h({kind:"note",tone:"error",text:"Which article? Use /push --preview <path> \u2014 a preview is per article."});return}try{let W=await PO({cwd:e,profileName:s,relPath:E.replace(/\\/g,"/"),preview:!0}),F=W.shots.length>0?` \xB7 ${W.shots.length} screenshot${W.shots.length===1?"":"s"} embedded`:"";h({kind:"note",tone:"info",text:`${W.title}
|
|
@@ -548,22 +548,22 @@ Model: ${F.model}${F.forced?" (forced)":" \u2014 mechanical work; /model to over
|
|
|
548
548
|
`):B+Vd(L.results,R,q).join(`
|
|
549
549
|
`)}),gr(X,L.results,L.totalCostUsd,!te&&!L.aborted&&L.ok),te&&le("/doctor")}}catch(L){h({kind:"note",tone:"error",text:`Convert run failed: ${L.message}`})}finally{Be.current=null,be(null),P(!1),D(L=>L+1)}return}case"write":{if(!$e(e)){h({kind:"note",tone:"error",text:"No d360-writer config here. Run /init first."});return}let b=Bv(e);if(b.length===0){h({kind:"note",tone:"error",text:"No docs plan found (.d360-writer/plan.json). Analyze the repo and propose a structure first \u2014 try: analyze this repo and propose a docs structure."});return}let{scope:$,path:v,preview:k,concurrency:R,rewrite:E}=ti(w);if(!v&&!k&&!($e(e)?.appType??_v(e))){h({kind:"note",tone:"warn",text:`\u26A0 No doc genre is set, so no doc-craft playbook would load \u2014 all ${b.length} articles would be written generically.
|
|
550
550
|
Pick the genre below (one keypress), then run /write again \u2014 it changes structure, titles and depth for every article.
|
|
551
|
-
Not blocked: /write <path> still authors a single article, and /write --preview costs nothing.`}),we(nc.options.map(J=>({cmd:J.cmd,desc:J.desc}))),yo(nc.title);return}let{docsDir:W,targets:F,reason:N,rewriting:X}=$l(e,b,{scope:$,path:v,rewrite:E});if(F.length===0){h({kind:"note",tone:"warn",text:N??"Nothing to write."});return}X&&h({kind:"note",tone:"warn",text:`--rewrite: regenerating ${F.length} already-written article${F.length===1?"":"s"} (overwrites them; git has your baseline).`});let L=Sf(F),B=np(R),q=ji(e,"standard"),te=!!v;if(k&&!te){let J=Wv({files:op(e,b,F),op:"write",model:q.model}),
|
|
551
|
+
Not blocked: /write <path> still authors a single article, and /write --preview costs nothing.`}),we(nc.options.map(J=>({cmd:J.cmd,desc:J.desc}))),yo(nc.title);return}let{docsDir:W,targets:F,reason:N,rewriting:X}=$l(e,b,{scope:$,path:v,rewrite:E});if(F.length===0){h({kind:"note",tone:"warn",text:N??"Nothing to write."});return}X&&h({kind:"note",tone:"warn",text:`--rewrite: regenerating ${F.length} already-written article${F.length===1?"":"s"} (overwrites them; git has your baseline).`});let L=Sf(F),B=np(R),q=ji(e,"standard"),te=!!v;if(k&&!te){let J=Wv({files:op(e,b,F),op:"write",model:q.model}),xe=$?`Scope: ${$} (${F.length} pending)
|
|
552
552
|
`:"",Oe=`
|
|
553
|
-
Model: ${q.model}${q.forced?" (forced)":" \u2014 authoring; /model to override"}`;h({kind:"note",tone:"info",text:
|
|
554
|
-
`)+Oe}),le(`/write${$?` --scope ${$}`:""}`);return}Bt.current=Date.now(),Wt(0),P(!0);let he=new AbortController;Be.current=he;let fe=Date.now();be({verb:"Writing",total:L.length,done:0,active:[],rows:Jo(L),tools:0,chars:0,lastAt:Date.now(),startedAt:fe}),h({kind:"note",tone:"info",text:`Writing ${F.length} article${F.length===1?"":"s"} in ${L.length} batch${L.length===1?"":"es"} (\u2264${B} agents at once) on ${q.model}${q.forced?" (forced)":""}\u2026 (esc to stop)`});let ye=new Set;try{for await(let J of Di({cwd:e,partitions:L,promptFor:
|
|
555
|
-
`):
|
|
553
|
+
Model: ${q.model}${q.forced?" (forced)":" \u2014 authoring; /model to override"}`;h({kind:"note",tone:"info",text:xe+sp(L,J,B).join(`
|
|
554
|
+
`)+Oe}),le(`/write${$?` --scope ${$}`:""}`);return}Bt.current=Date.now(),Wt(0),P(!0);let he=new AbortController;Be.current=he;let fe=Date.now();be({verb:"Writing",total:L.length,done:0,active:[],rows:Jo(L),tools:0,chars:0,lastAt:Date.now(),startedAt:fe}),h({kind:"note",tone:"info",text:`Writing ${F.length} article${F.length===1?"":"s"} in ${L.length} batch${L.length===1?"":"es"} (\u2264${B} agents at once) on ${q.model}${q.forced?" (forced)":""}\u2026 (esc to stop)`});let ye=new Set;try{for await(let J of Di({cwd:e,partitions:L,promptFor:xe=>rp(xe,W,{rewrite:X||te}),concurrency:B,profileName:s,allowProdWrites:_n.current,model:q.model,signal:he.signal}))if(J.type==="partition_status")J.status==="running"?be(xe=>xe&&{...xe,active:[...xe.active,J.label],rows:it(xe.rows,J.label,"active"),lastAt:Date.now()}):be(xe=>xe&&{...xe,active:xe.active.filter(Oe=>Oe!==J.label),done:xe.done+1,rows:it(xe.rows,J.label,J.status==="done"?"done":"failed"),lastAt:Date.now()});else if(J.type==="partition_stalled"){let xe=os(J);be(Oe=>Oe&&xe?{...Oe,stalledNote:xe}:Oe)}else if(J.type==="partition_event"){let xe=null;if(J.event.type==="tool"){let Oe=$f(J.event.name,J.event.input,L[J.index]?.paths??[]);Oe&&!ye.has(Oe)&&(ye.add(Oe),xe=J.label)}be(Oe=>{if(!Oe)return Oe;let zt={...Oe,lastAt:Date.now()};return J.event.type==="tool"?zt.tools=Oe.tools+1:J.event.type==="text"&&(zt.chars=Oe.chars+J.event.delta.length),xe&&(zt.rows=xc(zt.rows,xe)),zt})}else if(J.type==="run_done"){So(ut=>ut+J.totalCostUsd),Ro(ut=>ut+J.results.reduce((jt,Do)=>jt+Do.outputTokens,0));let xe=J.aborted?"Stopped. ":"",Oe=t.kind==="api"?"api":"subscription",zt=Xn(J,"The write");if(h({kind:"note",tone:zt||J.aborted?"warn":J.ok?"ok":"warn",text:zt?zt.join(`
|
|
555
|
+
`):xe+ip(J.results,L,Oe,$??v).join(`
|
|
556
556
|
`)}),gr(fe,J.results,J.totalCostUsd,!zt&&!J.aborted&&J.ok),zt){le("/doctor");continue}let wm=nw(J);if(wm&&h({kind:"note",tone:"warn",text:wm}),!J.aborted&&J.ok)try{let ut=(ye.size>0?[...ye]:F).map(jt=>`${W}/${jt}`);ls(e,ut,{writtenCap:HO,reviewedCap:0,checkedCap:0})}catch{}let No=J.aborted?[]:Hr(e,W,F),bm=ap(No);bm&&h({kind:"note",tone:"warn",text:bm});let br=J.aborted||w.includes("--skip-style")?null:lp(No);if(br){let ut=br.errors.length,jt=await Zi({partitions:br.partitions,promptFor:br.promptFor,concurrency:Wr,verb:"Fixing",doneNoun:"fixed",headline:`${ut} style error${ut===1?"":"s"} in ${br.articles} of the new article${br.articles===1?"":"s"} \u2014 fixing now, one pass`});if(jt.covered.length>0)try{ls(e,jt.covered,yt("lint"))}catch{}No=Hr(e,W,F);let Do=cp(ut,No);h({kind:"note",tone:Do[0].startsWith("\u2713")?"ok":"warn",text:Do.join(`
|
|
557
557
|
`)})}if(!J.aborted&&J.ok){let ut=$??v??"",jt=ut?` ${ut}`:"",Do=No.some(kr=>kr.severity==="error");{let kr=Cf(L,J.results);xn(`/write${$?` --scope ${$}`:""}${w.includes("--rewrite")?" --rewrite":""}`,{aborted:J.aborted,partitions:L.length,failed:kr.failed.length,notRun:kr.notRun.length,leftBehind:No.filter(b0=>b0.severity==="error").length,...J.blocked?{blocked:J.blocked}:{},...J.allEmpty?{allEmpty:!0}:{},...J.bailedEmpty?{bailedEmpty:!0}:{}})}let km=ut?"/push":"/push --all",w0=ut?`/screenshot author ${ut}`:"/screenshot author --all";Pe(Do?`Written, but ${No.filter(kr=>kr.severity==="error").length} style error(s) are still unfixed. /lint${jt} --blocked --fix retries them, then /review${jt} --fix for editorial quality, then ${km}.`:`Written. I\u2019d run /review${jt} --fix next \u2014 editorial review that applies its fixes \u2014 then ${km} saves them to Document360 as drafts (add ${w0} first if you want images).`,{kind:"cmd",cmd:Do?`/lint${jt} --blocked --fix`:`/review${jt} --fix`,reason:""})}}}catch(J){h({kind:"note",tone:"error",text:`Write run failed: ${J.message}`})}finally{Be.current=null,be(null),P(!1),D(J=>J+1)}return}case"draft":{if(!$e(e)){h({kind:"note",tone:"error",text:"No d360-writer config here. Run /init first."});return}let{features:b,looseDocs:$}=dp(e);if(b.length===0&&$===0){h({kind:"note",tone:"info",text:hp});return}let v=w.join(" ").trim();if(!v&&b.length>1){h({kind:"note",tone:"info",text:`Multiple features in .d360-writer/input/ \u2014 draft which one?
|
|
558
558
|
${b.map(E=>` \u2022 ${E.name} (${E.docCount} doc${E.docCount===1?"":"s"})`).join(`
|
|
559
|
-
`)}`}),we(b.map(E=>`/draft ${E.name}`));return}let k="",R;if(v){let E=pp(b,v);E?k=E:R=v}else k=b[0]?.name??"";await
|
|
559
|
+
`)}`}),we(b.map(E=>`/draft ${E.name}`));return}let k="",R;if(v){let E=pp(b,v);E?k=E:R=v}else k=b[0]?.name??"";await Re(vl(k,R),{display:v?`/draft ${v}`:"/draft",quietUser:!0});return}case"pull":{if(!Po(p.trim()))return;let b=w.find($=>$.startsWith("--"));try{if(b==="--status"){h({kind:"note",tone:"info",text:"Checking Document360 for drift\u2026"});let k=Date.now(),R=await Ni({cwd:e,profileName:s});h({kind:"note",tone:"info",text:il(R,Date.now()-k).join(`
|
|
560
560
|
`)});return}if(b==="--baseline"){let k=w.find(F=>!F.startsWith("--")),R=k?k.replace(/\\/g,"/"):void 0;h({kind:"note",tone:"info",text:R?`Setting the sync baseline for ${R}\u2026`:"Setting a sync baseline for tracked-but-not-yet-compared articles\u2026"});let E=await AO({cwd:e,profileName:s,relPaths:R?[R]:void 0}),W=R?{rebaselined:[],skipped:[]}:await EO({cwd:e,profileName:s});if(E.baselined.length===0&&E.skipped.length===0&&W.rebaselined.length===0){h({kind:"note",tone:"ok",text:"\u2713 Nothing to baseline \u2014 every tracked article already has a sync baseline."});return}E.baselined.length>0&&h({kind:"note",tone:"ok",text:`\u2713 Recorded ${E.baselined.length} article${E.baselined.length===1?"":"s"} as matching Document360 \u2014 they read in-sync until either side changes.`}),W.rebaselined.length>0&&h({kind:"note",tone:"ok",text:`\u2713 Refreshed the metadata baseline for ${W.rebaselined.length} article${W.rebaselined.length===1?"":"s"} \u2014 cleared false "metadata changed" drift.`}),E.skipped.length>0&&h({kind:"note",tone:"warn",text:`${E.skipped.length} skipped:
|
|
561
561
|
${E.skipped.map(F=>` - ${F.path} (${F.reason})`).join(`
|
|
562
562
|
`)}`}),D(F=>F+1);return}if(b==="--compare"){h({kind:"note",tone:"info",text:"Comparing the manifest against the per-article path (read-only)\u2026"});let k=await RO({cwd:e,profileName:s}),R=k.onlyInLegacy.length===0&&k.onlyInManifest.length===0&&k.mappedMissingFromManifest.length===0,E=[`Legacy listing: ${k.legacyCount} \xB7 Manifest: ${k.manifestCount} \xB7 in both: ${k.inBoth}`];R?E.push("\u2713 Parity \u2014 the manifest sees exactly the same articles. Safe to make it the default sync source."):(k.mappedMissingFromManifest.length&&E.push(`\u2717 ${k.mappedMissingFromManifest.length} MAPPED article(s) missing from the manifest: ${k.mappedMissingFromManifest.slice(0,5).join(", ")}`),k.onlyInLegacy.length&&E.push(`\u26A0 ${k.onlyInLegacy.length} only in the legacy listing: ${k.onlyInLegacy.slice(0,5).join(", ")}`),k.onlyInManifest.length&&E.push(`\u26A0 ${k.onlyInManifest.length} only in the manifest: ${k.onlyInManifest.slice(0,5).join(", ")}`),E.push("Send me this \u2014 a mismatch pinpoints where the manifest differs before we flip.")),h({kind:"note",tone:R?"ok":"warn",text:E.join(`
|
|
563
563
|
`)});return}if(b){h({kind:"note",tone:"error",text:`Unknown option: ${b} \u2014 use /pull, /pull <path>, /pull --status, or /pull --baseline.`});return}let $=w[0];if($){let k=$.replace(/\\/g,"/"),R;try{let E=await Mv({cwd:e,profileName:s,relPath:k});R={plan:E,mode:E.overwritesLocalChanges?"conflict":"pull"}}catch(E){if(!Uv(E))throw E;R={mode:"local-only",path:k,title:Fv(e,k)}}je.current=[R],Gt.current=[],mn.current=[],yr();return}h({kind:"note",tone:"info",text:"Pulling changes from Document360\u2026"});let v=await Ed({cwd:e,profileName:s});for(let k of Nd(v))h({kind:"note",tone:k.tone,text:k.text});D(k=>k+1)}catch($){h({kind:"note",tone:"error",text:`Pull failed: ${$.message}`})}return}case"scope":{if(!$e(e)){h({kind:"note",tone:"error",text:"No d360-writer config here. Run /init first."});return}let b=NO(e);if(b.length===0){h({kind:"note",tone:"info",text:'No candidate source folders found. Set "authoritativeSourceFiles" in .d360-writer/config.json manually.'});return}ur({cursor:0,rows:b.map($=>({...$,checked:$.recommended}))});return}case"audit":{{let $=ol(e,w);if($){h({kind:"note",tone:$[0].startsWith("\u2713")?"ok":"info",text:$.join(`
|
|
564
|
-
`)});return}}if(w[0]==="--auto"){let $=$e(e);if(!$){h({kind:"note",tone:"error",text:"No d360-writer config here. Run /init first."});return}let v=w[1]!=="off";Iv({...$,autoAudit:v},e),h({kind:"note",tone:"ok",text:v?"\u2713 Auto-audit ON \u2014 when you launch with code changes since the last audit, I\u2019ll run /audit automatically. (/audit --auto off to disable.)":"\u2713 Auto-audit OFF \u2014 I\u2019ll just nudge you to run /audit when code changed."});return}if(!Po(p.trim()))return;let b=await qs({cwd:e,profileName:s});if(b.kind==="audit-blocked"){Pe(b.note,{kind:"cmd",cmd:b.lead.cmd,reason:b.lead.reason},"warn");return}if(b.kind==="forward-to-agent"&&b.prompt){let $=await
|
|
564
|
+
`)});return}}if(w[0]==="--auto"){let $=$e(e);if(!$){h({kind:"note",tone:"error",text:"No d360-writer config here. Run /init first."});return}let v=w[1]!=="off";Iv({...$,autoAudit:v},e),h({kind:"note",tone:"ok",text:v?"\u2713 Auto-audit ON \u2014 when you launch with code changes since the last audit, I\u2019ll run /audit automatically. (/audit --auto off to disable.)":"\u2713 Auto-audit OFF \u2014 I\u2019ll just nudge you to run /audit when code changed."});return}if(!Po(p.trim()))return;let b=await qs({cwd:e,profileName:s});if(b.kind==="audit-blocked"){Pe(b.note,{kind:"cmd",cmd:b.lead.cmd,reason:b.lead.reason},"warn");return}if(b.kind==="forward-to-agent"&&b.prompt){let $=await Re(b.prompt,{display:b.display,quietUser:!0}),v=nl(e,s);if(v){Pe(v.note,{kind:"cmd",cmd:Or.cmd,reason:Or.reason},"warn"),D(k=>k+1);return}$.producedNothing||($i(e,s),D(k=>k+1))}return}case"lint":case"check":{let b=Gs(u);b&&h({kind:"note",tone:"info",text:`\u2192 ${b}`});let $=w.find(E=>!E.startsWith("--")),v=$?` ${$}`:"",k=as({cwd:e,scope:$}),R=An(k,w);if(w.includes("--fix")){let E=Zn(e,w,k);E.lines.length>0&&h({kind:"note",tone:"ok",text:E.lines.join(`
|
|
565
565
|
`)});let W=E.fixed>0?as({cwd:e,scope:$}):k,F=Xd(e,w,W);if(!F){let J=Qd(e,w,W);h({kind:"note",tone:"ok",text:J.lines.join(`
|
|
566
|
-
`)});for(let
|
|
566
|
+
`)});for(let xe of J.closed)h({kind:"note",tone:"ok",text:xe});return}let N={aborted:!1,partitions:0,failed:0,notRun:0};if(F.kind==="partitioned"){let J=await Zi({partitions:F.partitions,promptFor:F.promptFor,concurrency:Wr,verb:"Fixing",doneNoun:"fixed",headline:`Fixing ${F.findings} issue${F.findings===1?"":"s"} across ${F.articles} article${F.articles===1?"":"s"} in ${F.partitions.length} batches (\u2264${Wr} at once)`,calibrateAs:"/lint"});if(N=J.outcome,J.covered.length>0)try{ls(e,J.covered,yt("lint"))}catch{}if(J.aborted){let xe=Hs({cwd:e,after:An(as({cwd:e,scope:$}).filter(Oe=>!Oe.baselined),w),scope:$,aborted:!0,blocked:Tn(w)});Pe(xe.text,{kind:"cmd",cmd:xe.cmd,reason:""},"warn");return}}else{h({kind:"note",tone:"info",text:`Fixing ${F.findings} doc issue${F.findings===1?"":"s"}\u2026`});let J=BO(e,$);await Re(F.prompt,{display:F.display,quietUser:!0});try{ls(e,J,yt("lint"))}catch{}}let X=An(as({cwd:e,scope:$}).filter(J=>!J.baselined),w),L=An(W.filter(J=>!J.baselined),w),B=new Set(X.map(J=>J.fp)),q=new Set(L.map(J=>J.fp)),te=L.filter(J=>!B.has(J.fp)).length,he=X.filter(J=>!q.has(J.fp)).length,fe=he>0?`
|
|
567
567
|
\u26A0 ${he} new ${he===1?"issue":"issues"} appeared in the rewrites \u2014 recasting a sentence is where these come from.`:"";h({kind:"note",tone:X.some(J=>J.severity==="error")?"warn":"ok",text:X.length===0?`\u2713 Lint: all ${L.length} issue${L.length===1?"":"s"} resolved.`:[`\u2713 Lint: fixed ${te} of ${L.length}.${fe}`,"",...Js(X)].join(`
|
|
568
568
|
`)}),xn(Qs(w,$),F.kind==="partitioned"?N:{aborted:!1,partitions:0,failed:0,notRun:0,verifiedComplete:X.length===0});let ye=Hs({cwd:e,after:X,before:F.findings,scope:$,blocked:Tn(w)});Pe(ye.text,{kind:"cmd",cmd:ye.cmd,reason:""});return}if(h({kind:"note",tone:R.length===0?"ok":"warn",text:Js(R,{full:w.includes("--full"),baselined:w.includes("--baselined")}).join(`
|
|
569
569
|
`)}),R.length>0){let E=za(e,$)===0&&Xa(e,$)===0?"; /push --all saves them to Document360 as-is":"",W=`/lint${v}${Tn(w)?" --blocked":""} --fix`,F=en(e,W);Pe(`Found ${R.length} doc issue${R.length===1?"":"s"} above. ${W} has the agent fix them in place${F?` (${F})`:""} \u2014 or /review${v} --fix for the full editorial pass${E}.`,{kind:"cmd",cmd:W,reason:""})}return}case"structure":{let b=Qr(e,s);if(b.length===0){h({kind:"note",tone:"info",text:"No documentation structure yet \u2014 run /init and author some articles first."});return}h({kind:"note",tone:"info",text:["Documentation order (categories \u2192 articles):","",...b].join(`
|
|
@@ -584,23 +584,23 @@ ${E.skipped.map(F=>` - ${F.path} (${F.reason})`).join(`
|
|
|
584
584
|
`)}),h({kind:"block",text:Ql(e,E).lines.join(`
|
|
585
585
|
`)});for(let F of Ye(e,"/reuse audit"))h({kind:"note",tone:"ok",text:F});E.created.length>0&&Ve()})(),ee.current=!0;return}let $=Xl(e,w);if("lines"in $){h({kind:"block",text:$.lines.join(`
|
|
586
586
|
`)});for(let v of Ye(e,"/reuse audit"))h({kind:"note",tone:"ok",text:v});ee.current=!0;return}h({kind:"block",text:zl($.articles,$.families,$.values,0).join(`
|
|
587
|
-
`)}),(async()=>(await
|
|
587
|
+
`)}),(async()=>(await Re($.prompt,{display:$.display,quietUser:!0}),h({kind:"block",text:ec(e,$.display.split(/\s+/)[2]).join(`
|
|
588
588
|
`)})))(),ee.current=!0;return}if(b==="add"){let $=Zh(w);if("error"in $){h({kind:"note",tone:"warn",text:`\u2717 ${$.error}`}),ee.current=!0;return}(async()=>{let v=await Jh(e,$.kind,$.name,$.value,$.description);h({kind:"block",text:v.lines.join(`
|
|
589
589
|
`)}),v.restart&&Ve()})(),ee.current=!0;return}if(b&&b!=="show"){h({kind:"note",tone:"warn",text:`\u2717 I don't know "/reuse ${b}". Use /reuse to see what this project maintains, /reuse pull to refresh it, or /reuse add variable <name> "<value>".`}),ee.current=!0;return}h({kind:"block",text:Qh(e).join(`
|
|
590
|
-
`)}),ee.current=!0;return}case"reorder":{let b=_h(e,w);if(!b){h({kind:"note",tone:"error",text:"No d360-writer config here. Run /init first."});return}let $=await
|
|
590
|
+
`)}),ee.current=!0;return}case"reorder":{let b=_h(e,w);if(!b){h({kind:"note",tone:"error",text:"No d360-writer config here. Run /init first."});return}let $=await Re(b.prompt,{display:b.display,quietUser:!0}),v=Ot(e,{text:"Reordered locally \u2014 /push --all syncs the new order to Document360.",cmd:"/push --all"},"Reordered locally. ");kn($,v.text,{kind:"cmd",cmd:v.cmd,reason:""});return}case"restructure":{let b=Mh(e,w);if(!b){h({kind:"note",tone:"error",text:"No d360-writer config here. Run /init first."});return}let $=await Re(b.prompt,{display:b.display,quietUser:!0});if(w.filter(k=>!k.startsWith("--")).length===0){kn($,null,null);return}let v=Ot(e,{text:"Restructured locally \u2014 /push --all moves it on Document360.",cmd:"/push --all"},"Restructured locally. ");kn($,v.text,{kind:"cmd",cmd:v.cmd,reason:""});return}case"review":{let b=Lh(e,w);if(b){h({kind:"note",tone:"ok",text:b.lines[0]});for(let B of b.closed)h({kind:"note",tone:"ok",text:B});let L=bi(e,w,null);L&&(h({kind:"note",tone:"info",text:"One thing left: no editor has looked at how your docs are organised since it last changed. Checking that now \u2014 it reads the shape, not the articles, so it is one quick pass."}),(await Re(L.prompt,{display:L.display,quietUser:!0})).producedNothing||zv(e,L.fingerprint)),Pe(b.cta.text,{kind:"cmd",cmd:b.cta.cmd,reason:""});return}let $=Ih(e,w);if(!$){h({kind:"note",tone:"error",text:"No d360-writer config here. Run /init first."});return}let v=w.includes("--fix"),k=w.find(L=>!L.startsWith("--")),R=v?Ko(e,w,k):[],E=v?Pf(e):null,W={aborted:!1,partitions:0,failed:0,notRun:0},F,N=null,X=bi(e,w,$);if($.kind==="partitioned"){let L=await m0($,v,E,!!X);W=L.outcome,F=L,N=L.evidence}else F=await Re($.prompt,{display:$.display,quietUser:!0}),W={...W,producedNothing:F.producedNothing};if("aborted"in F&&F.aborted){let L=w.includes("--blocked")?"--blocked --fix":w.includes("--stale")?"--stale --fix":"--fix",B=Se("/review",L,k);Pe(`Stopped \u2014 the batches that finished are saved. ${B} picks up the rest${k||Jr(w)?"":", or narrow it with /review <category> --fix"}.`,{kind:"cmd",cmd:B,reason:""},"warn");return}if(X&&((await Re(X.prompt,{display:X.display,quietUser:!0})).producedNothing||zv(e,X.fingerprint)),$.kind!=="partitioned"&&v&&!F.producedNothing&&(N=Yo(e,R,E,s),N.earned.length>0))try{ls(e,N.earned,yt("review"))}catch{}if(v&&xn(`/review${k?` ${k}`:""}${w.includes("--all")?" --all":""}${w.includes("--blocked")?" --blocked":w.includes("--stale")?" --stale":""} --fix`,W),v){if(v&&!F.producedNothing){let fe=Zn(e,["--fix"],as({cwd:e,scope:k}));fe.lines.length>0&&h({kind:"note",tone:"ok",text:fe.lines.join(`
|
|
591
591
|
`)})}let L=v&&!F.producedNothing?as({cwd:e,scope:k}).filter(fe=>!fe.baselined&&fe.severity==="error"):[],B=N?N.changed.length:-1,q=B<0?"Editor applied its fixes":wi(B),te=Xr(e,N?.unadopted??[],{lead:B===0&&L.length===0});if(te&&h({kind:"note",tone:"warn",text:te.lines.join(`
|
|
592
|
-
`)}),L.length>0){let fe=new Set(L.map(Oe=>Oe.path)).size,ye=`/lint${k?` ${k}`:""} --blocked --fix`,J=en(e,ye),
|
|
593
|
-
`)}),$n(jo()),xs.current=Xt(e).status==="current",fr.current=Sn(e);return}case"discover":{if(!$e(e)){h({kind:"note",tone:"error",text:"No d360-writer config here. Run /init first."});return}await
|
|
592
|
+
`)}),L.length>0){let fe=new Set(L.map(Oe=>Oe.path)).size,ye=`/lint${k?` ${k}`:""} --blocked --fix`,J=en(e,ye),xe=!N||N.changed.length>0;h({kind:"note",tone:"warn",text:xe?`\u26A0 The editor's rewrites left ${L.length} style error${L.length===1?"":"s"} in ${fe} article${fe===1?"":"s"} \u2014 recasting a sentence is where these appear.`:`\u26A0 ${L.length} style error${L.length===1?"":"s"} in ${fe} article${fe===1?"":"s"} still block${L.length===1?"s":""} publishing \u2014 the editor changed nothing, so these were already there.`}),kn(F,`${N?wi(N.changed.length):"Editor applied its fixes"}, but ${L.length} style error${L.length===1?"":"s"} remain. ${ye} clears ${L.length===1?"it":"just those"} \u2014 it costs nothing to find them and one pass to correct${J?` (${J})`:""}. It is the last paid pass before publishing: anything still standing after a second attempt is reported for a person rather than run again.`,{kind:"cmd",cmd:ye,reason:""});return}if(te?.cta){kn(F,te.cta.text,{kind:"cmd",cmd:te.cta.cmd,reason:""});return}let he=Ot(e,{text:`\u2713 ${q} \u2014 your docs are ready. /push${k?"":" --all"} saves them to Document360 as drafts (or /lint${k?` ${k}`:""} for an instant no-token re-scan first).`,cmd:k?"/push":"/push --all"},`\u2713 ${q} in this scope. `,k);kn(F,he.text,{kind:"cmd",cmd:he.cmd,reason:""})}else{let L=k?` ${k}`:"",B=za(e,k)===0&&Xa(e,k)===0?`; /push${k?"":" --all"} saves them to Document360 as-is`:"";kn(F,`The review listed issues above. /review${L} --fix lets the editor apply them all (or /lint${L} --fix for just the deterministic ones${B}).`,{kind:"cmd",cmd:`/review${L} --fix`,reason:""})}return}case"capture-setup":{await O();return}case"devhints":{if(!$e(e)){h({kind:"note",tone:"error",text:"No d360-writer config here. Run /init first."});return}h({kind:"note",tone:"info",text:Pr(Cr(e)).join(`
|
|
593
|
+
`)}),$n(jo()),xs.current=Xt(e).status==="current",fr.current=Sn(e);return}case"discover":{if(!$e(e)){h({kind:"note",tone:"error",text:"No d360-writer config here. Run /init first."});return}await Re(Rd(),{display:"/discover",quietUser:!0});return}case"ask":{if(!$e(e)){h({kind:"note",tone:"error",text:"No d360-writer config here. Run /init first."});return}let b=w.join(" ").trim();if(b===""){h({kind:"block",text:Fp(ZO(e)).join(`
|
|
594
594
|
`)});return}try{let $=JO(e,{question:b});h({kind:"note",tone:"ok",text:Up($.file,ci(e)).join(`
|
|
595
|
-
`)})}catch($){h({kind:"note",tone:"warn",text:`Could not file the question: ${$.message}`})}return}case"inbox":{if(!$e(e)){h({kind:"note",tone:"error",text:"No d360-writer config here. Run /init first."});return}if(_p(e)===0){h({kind:"note",tone:"info",text:"Inbox empty \u2014 no open messages from the builder. (They arrive via the builder's /devhints hand-off.)"});return}await
|
|
595
|
+
`)})}catch($){h({kind:"note",tone:"warn",text:`Could not file the question: ${$.message}`})}return}case"inbox":{if(!$e(e)){h({kind:"note",tone:"error",text:"No d360-writer config here. Run /init first."});return}if(_p(e)===0){h({kind:"note",tone:"info",text:"Inbox empty \u2014 no open messages from the builder. (They arrive via the builder's /devhints hand-off.)"});return}await Re(Mp(),{display:"/inbox",quietUser:!0});return}case"auto":{let b=$e(e);if(!b){h({kind:"note",tone:"error",text:"No d360-writer config here. Run /init first."});return}let $=vd(w);if($.malformed){h({kind:"note",tone:"error",text:`I could not read that spending limit. Give it as an amount, like: /auto --budget 150 (the standard limit is ${Ke(gO)}).`});return}let v=xd(w,$.usd),k=R=>{for(let E of R)h({kind:"note",tone:E.tone,text:E.text});D(E=>E+1)};if($.usd!==void 0&&!v){let R=$d(e,b,$.usd);R&&k(R);return}if(!v||v==="status"){for(let R of Ys(b))h({kind:"note",tone:R.tone,text:R.text});return}if(v==="on"||v==="off"){v==="on"&&(ko.current=!1);let R=$.usd!==void 0?{...b,auto:{...b.auto,enabled:b.auto?.enabled===!0,budgetUsd:$.usd}}:b;k(rl(e,R,v==="on"));return}h({kind:"note",tone:"error",text:`I don't know "${v}". Use: /auto on \xB7 /auto off \xB7 /auto status \xB7 /auto --budget <amount>`});return}case"share":{let b=$e(e);if(!b){h({kind:"note",tone:"error",text:"No d360-writer config here. Run /init first."});return}let $=w[0]?.trim().toLowerCase();if($==="status"){for(let k of Nl(e,b))h({kind:"note",tone:k.tone,text:k.text});return}if($==="on"||$==="off"){for(let k of Wp(e,b,$==="on"))h({kind:"note",tone:k.tone,text:k.text});D(k=>k+1);return}if($){h({kind:"note",tone:"error",text:`Unknown option: ${$} \u2014 use /share, /share status, /share on, or /share off.`});return}h({kind:"note",tone:"info",text:"Exchanging work with your team\u2026"});let v=await Dl(e,b);for(let k of v.lines)h({kind:"note",tone:k.tone,text:k.text});v.changed&&D(k=>k+1);return}case"feedback":{let b=qv(e),$="";try{$=is(b)?Tc(b,"utf8"):""}catch{$=""}if(!/^## /m.test($)){h({kind:"note",tone:"info",text:"No open d360-writer feedback to copy \u2014 nothing logged in .d360-writer/tool-feedback.md yet."});return}let v=await hf($.trim());h({kind:"note",tone:v?"ok":"warn",text:v?"\u2713 Copied the d360-writer feedback to your clipboard \u2014 send it to the team (Teams / Slack / email).":`Couldn\u2019t reach your clipboard \u2014 open ${b} and copy it manually.`}),v&&Yc(null);return}case"document":{if(!$e(e)){h({kind:"note",tone:"error",text:"No d360-writer config here. Run /init first."});return}let{push:b,skipReview:$,what:v}=mp(w);if(!v&&fp(e)===0){h({kind:"note",tone:"info",text:yp});return}let k=`/document${v?` ${v}`:""}${b?" --push":""}`,R=await Re(gp(v),{display:k,quietUser:!0});b&&!R.producedNothing&&await cm({skipReview:$});return}case"catch-up":{if(!$e(e)){h({kind:"note",tone:"error",text:"No d360-writer config here. Run /init first."});return}if(!Po(p.trim()))return;let{push:b,skipReview:$,includeUnfinished:v}=qp(w);for(let q of await Hp(e))h({kind:"note",tone:q.tone,text:q.text});h({kind:"note",tone:"info",text:"Reconciling with Document360 (pulling any portal changes)\u2026"});let k=await Qp({cwd:e,profileName:s});for(let q of Jp(k))h({kind:"note",tone:q.startsWith("\u2713")?"ok":q.startsWith("\u2139")?"info":"warn",text:q});let R=di(e);for(let q of Vp(R))h({kind:"note",tone:q.tone,text:q.text});let E=Yp(e),W=v?[]:E,F=Kp(e,W);for(let q of zp(W,F))h({kind:"note",tone:q.tone,text:q.text});let N=Zt(e);h({kind:"note",tone:"info",text:Gp(N)});let X=Pf(e);Ne.current?.beginTurn({baseline:X,allowUnfinishedHints:v});let L=await Re(Xp(R,{held:E.length,includeUnfinished:v},N.briefMoved),{display:`/catch-up${b?" --push":""}${v?` ${Ll}`:""}`,quietUser:!0});L.producedNothing||e3(e);let B=Go(e,X,"","/catch-up");if(b&&!L.producedNothing)await cm({skipReview:$});else if(B.cta)kn(L,B.cta.text,{kind:"cmd",cmd:B.cta.cmd,reason:""});else{let q=Il({queued:N,hold:R.hold});kn(L,q.note,q.lead?{kind:"cmd",cmd:q.lead.cmd,reason:q.lead.reason}:null,q.tone)}return}case"comments":{if(!$e(e)){h({kind:"note",tone:"error",text:"No d360-writer config here. Run /init first."});return}if(!Po(p.trim()))return;let b=oh(w);Fe("Checking Document360 for reviewer comments\u2026"),P(!0);let $=null;try{$=await Ol(e,s,{...b.scope,onProgress:(k,R)=>Fe(`Checking Document360 for reviewer comments\u2026 ${k}/${R}`)})}catch(k){h({kind:"note",tone:"error",text:`Couldn\u2019t read comments from Document360: ${k.message}`})}finally{P(!1),Fe(null)}if(!$)return;D(k=>k+1);let v=qo($.threads,$.labels);if($.threads.length===0){h({kind:"note",tone:"ok",text:hi($,{showAnswered:b.showAnswered,width:nt()}).join(`
|
|
596
596
|
`)});return}if(b.answer&&(b.all||b.scope.path||b.scope.scope||v.length===1)){mr(th($,{full:!0,showAnswered:b.showAnswered,width:nt()})),await Zc(v,{resolve:b.resolve});return}if(v.length===1){mr(_l(v[0],{width:nt()})),hs({group:v[0],cursor:0,fromList:!1,resolve:b.resolve,rest:[]});return}mr(hi($,{showAnswered:b.showAnswered,width:nt()})),yn({cursor:0,rows:Ml(v),groups:v,resolve:b.resolve,report:$});return}case"update":{if(Ul()){h({kind:"note",tone:"info",text:"Checking for a newer version\u2026"});let $=await Hv({pkg:Vo,currentVersion:o,force:!0}).catch(()=>null);if(!$?.updateAvailable||!$.latest){h({kind:"note",tone:"ok",text:`\u2713 You're on the latest version (v${o}).`});return}Vi.current=!0,Fl($.latest),h({kind:"note",tone:"info",text:`\u2B06 Update v${$.latest} is ready.
|
|
597
597
|
Windows can't replace the files while d360-writer is running, so I'll install it the moment you /exit.`}),le("/exit");return}Vi.current=!0,h({kind:"note",tone:"info",text:"Updating the d360-writer toolchain\u2026 (don\u2019t close this window)"}),uo(!0);let b=await gi().finally(()=>uo(!1));b.ok?(h({kind:"note",tone:"ok",text:`\u2713 Updated ${b.packages.join(" + ")} \u2014 run /exit, then relaunch to apply.`}),le("/exit"),Ii("")):(h({kind:"note",tone:"warn",text:`Update failed. ${mi(b.output)}`}),b.output&&h({kind:"note",tone:"info",text:`${b.output}
|
|
598
598
|
|
|
599
|
-
Full output: ${fi()}`}));return}case"screenshot":{let b=Ls(w),$=Io(e);if($&&b.mode!=="list"&&h({kind:"note",tone:"info",text:$}),b.mode==="capture"){await A(b.rest);return}if(b.mode==="annotate"){await U(b.rest);return}if(b.mode==="checklist"){await O();return}if(b.mode==="verify"){let R=He(e),E=b.rest.find(N=>!N.startsWith("-")),W=E?[E]:ri(R);if(E&&!R.some(N=>N.id===E)){h({kind:"note",tone:"error",text:`No screenshot placeholder called "${E}". /screenshot list shows them all.`});return}if(W.length===0){let N=R.filter(L=>L.state==="rejected").length,X=R.filter(L=>L.state==="verified").length;h({kind:"note",tone:"info",text:N>0?`Nothing left to verify. ${N} shot${N===1?"":"s"} failed verification \u2014 re-capture with /screenshot capture --failed.`:X>0?`All ${X} captured shot${X===1?" is":"s are"} already verified.`:"No captured screenshots to verify yet \u2014 /screenshot capture takes the shots first."});return}h({kind:"note",tone:"info",text:to(W.length)}),await
|
|
599
|
+
Full output: ${fi()}`}));return}case"screenshot":{let b=Ls(w),$=Io(e);if($&&b.mode!=="list"&&h({kind:"note",tone:"info",text:$}),b.mode==="capture"){await A(b.rest);return}if(b.mode==="annotate"){await U(b.rest);return}if(b.mode==="checklist"){await O();return}if(b.mode==="verify"){let R=He(e),E=b.rest.find(N=>!N.startsWith("-")),W=E?[E]:ri(R);if(E&&!R.some(N=>N.id===E)){h({kind:"note",tone:"error",text:`No screenshot placeholder called "${E}". /screenshot list shows them all.`});return}if(W.length===0){let N=R.filter(L=>L.state==="rejected").length,X=R.filter(L=>L.state==="verified").length;h({kind:"note",tone:"info",text:N>0?`Nothing left to verify. ${N} shot${N===1?"":"s"} failed verification \u2014 re-capture with /screenshot capture --failed.`:X>0?`All ${X} captured shot${X===1?" is":"s are"} already verified.`:"No captured screenshots to verify yet \u2014 /screenshot capture takes the shots first."});return}h({kind:"note",tone:"info",text:to(W.length)}),await Re(pn(e,W),{display:E?`/screenshot verify ${E}`:"/screenshot verify",quietUser:!0});let F=Gr(e,W);F.length>0&&h({kind:"note",tone:"info",text:F.join(`
|
|
600
600
|
`)});return}if(b.mode==="list"){if(!$e(e)){h({kind:"note",tone:"error",text:"No d360-writer config here. Run /init first."});return}let R=$e(e)?.docsDir??jf,E=Ju(e,b.scope),W=He(e,E);if(b.picker){if(W.length===0){h({kind:"note",tone:"info",text:"No screenshot placeholders found in the docs."});return}let N=iy(W);if(N.length===0){h({kind:"note",tone:"info",text:Wa(W,{docsDir:R}).join(`
|
|
601
601
|
`)});let X=Se("/push","--screenshots",E),L=Ot(e,{text:`\u2713 All ${W.length} screenshots are captured. ${X} saves them into the articles on Document360; /screenshot capture${E?` ${E}`:""} re-captures.`,cmd:X},`\u2713 All ${W.length} screenshots are captured. `,E);Pe(L.text,{kind:"cmd",cmd:L.cmd,reason:""});return}mo({cursor:0,rows:N,title:"Screenshots \u2014 pick what to author or capture:"});return}h({kind:"note",tone:"info",text:Wa(W,{scope:E,detail:b.detail,docsDir:R}).join(`
|
|
602
602
|
`)}),$&&h({kind:"note",tone:"info",text:$});let F=Qu(W,E);F.key!=="done"&&le(F.cmd);return}if(b.mode==="single"){await dm(b.id);return}if(!$e(e)){h({kind:"note",tone:"error",text:"No d360-writer config here. Run /init first."});return}let v=b.mode==="scope"?b.scope:void 0,k=OO(e,{scope:v}).map(R=>R.id);if(k.length===0){h({kind:"note",tone:"info",text:v?`No screenshot placeholders under ${v}.`:"No screenshot placeholders found in the docs."});return}await um(k,{setup:b.setup,scope:b.mode==="scope"?b.scope:void 0,runLine:b.mode==="scope"?[`/screenshot author ${b.scope}`,`/screenshot ${b.scope}`]:["/screenshot author --all","/screenshot --all"]});return}case"capture":{await A(w);return}case"reset":{let b=lh(e);if(b.length===0){h({kind:"note",tone:"info",text:"Nothing to reset \u2014 no d360-writer files found in this repo."});return}sr.current={repoName:Ai(e),targets:b,hadDevhints:is(Gv(e))},h({kind:"note",tone:"warn",text:uh(e,b).join(`
|
|
603
|
-
`)}),ot(Ai(e));return}default:h({kind:"note",tone:"error",text:`Unknown command: /${u} \u2014 type /help.`})}},[e,r,s,yr,ru,h,Ve,
|
|
603
|
+
`)}),ot(Ai(e));return}default:h({kind:"note",tone:"error",text:`Unknown command: /${u} \u2014 type /help.`})}},[e,r,s,yr,ru,h,Ve,Re,kt,eu,pm]),Ps=se(p=>{let m=(p??g).trim();if(y(""),or(null),Ln(0),m.startsWith("/")){ee.current=!1,Ae.current=!1,Me.current=null,Kt(),yo(null);let w=Gi.current.includes(m);w&&(Gi.current=[]),we(A=>w?[]:mv(A,m))}if(!m||(Ce(!1),Ee.current=!1,(sr.current||je.current.length>0)&&h({kind:"user",text:m}),fm(m))||hm(m))return;Ic.current.push(m);let u=bv(m,rr.current);if(m.startsWith("/"))h({kind:"user",text:m}),I(!0),Eo(u).finally(()=>I(!1));else if(s3(u)){h({kind:"user",text:m});let w=i3(u);if(!w)Re(u,{display:m,quietUser:!0});else{try{a3(e,{title:w,by:"owner (d360-writer terminal)",at:new Date().toISOString()})}catch{}Re(`The owner left this documentation directive (already recorded in .d360-writer/directives.md): "${w}". Action it now: ground it in the sources, write/update the article(s) as DRAFTS (do not publish unless asked), following the active doc-type playbook. When finished, call d360_directive_done with the exact title "${w}" and the article path(s) you wrote.`,{display:m,quietUser:!0})}}else{let w=wy(e,u);Re(w??u,{display:m})}},[g,fm,hm,Eo,Re]),mm=se(p=>{if(p.length>1){if(p.includes("\x1B"))return;let m=gv(p);if(yv(m)){let u=wv(++Oi.current,m);rr.current.set(u,m),x(u)}else x(m);return}x(p)},[x]),wr=Math.max(10,gt-6),gm=Pc(()=>Sc(c.text,wr),[c.text,wr]),su=se(p=>f(m=>({...m,pos:Math.max(0,Math.min(m.text.length,m.pos+p))})),[]),sa=se(p=>f(m=>({...m,pos:$v(Sc(m.text,wr),m.pos,p)})),[wr]),Ts=se(p=>f(m=>({...m,pos:vv(Sc(m.text,wr),m.pos,p)})),[wr]),g0=["\x1B[H","\x1B[1~","\x1BOH"],y0=["\x1B[F","\x1B[4~","\x1BOF"],ym=se((p,m)=>m.leftArrow?(su(-1),!0):m.rightArrow?(su(1),!0):p&&g0.includes(p)?(Ts("start"),!0):p&&y0.includes(p)?(Ts("end"),!0):m.ctrl&&p==="a"?(Ts("start"),!0):m.ctrl&&p==="e"?(Ts("end"),!0):!1,[su,Ts]),iu=se(()=>{if(Be.current){if(!Be.current.signal.aborted){let p=(pr?.verb??"work").toLowerCase(),m=pr?.unit??"article";h({kind:"note",tone:"warn",text:`\u238B Stopping the ${p} run (finishing in-flight ${m}s)\u2026`}),Be.current.abort()}return!0}return fs.current?!1:(fs.current=!0,h({kind:"note",tone:"warn",text:"\u238B Interrupting\u2026"}),Ne.current?.interrupt(),!0)},[pr]);return qL((p,m)=>{if(m.ctrl&&p==="c"){if(ms.current!==null&&Date.now()-ms.current<Zv){ms.current=null,Ne.current?.close(),r();return}ms.current=Date.now(),Gf(!0),_c.current&&clearTimeout(_c.current),_c.current=setTimeout(()=>{ms.current=null,Gf(!1)},Zv),C?iu():g&&y("");return}if(vs.current.awaySince!==null&&Xe.current===null)try{let u=av(vs.current,Date.now(),Zt(e));if(vs.current=u.memory,rm(null),u.kind==="delta"){h({kind:"note",tone:"info",text:cv(u.delta,u.awayMs)});let w=bt(!1);w.kind==="recommend"&&bn(w.rec,{silent:!0})}else if(u.kind==="briefing"){h({kind:"note",tone:"info",text:`\u{1F4E5} ${Pi(u.delta)} arrived while you were away (${kf(u.awayMs)}).`});let w=bt(!1);w.kind==="recommend"&&bn(w.rec)}}catch{}if(Kc.current=Date.now(),!S){if(ft){if(m.return){Mi(null),Ui.current=!0,D(u=>u+1),h({kind:"note",tone:"info",text:"\u25B6 Auto mode starting \u2014 esc stops it at any point, and nothing already done is undone."});return}if(p==="b"||p==="B"){Mi(null),y("/auto --budget ");return}if(m.escape){Mi(null),ko.current=!0,Xe.current=null,h({kind:"note",tone:"info",text:"Left in manual for now \u2014 nothing started. Auto mode is still on: /auto on begins the round trip when you are ready, or /auto off switches it off."}),D(u=>u+1);return}return}if(m.tab&&m.shift&&!Un&&!gn&&!Xc){let u=$e(e);if(!u){h({kind:"note",tone:"info",text:"Set this repo up first with /init \u2014 then shift+tab switches between auto and manual."});return}let w=!Lv(u);rl(e,u,w),w&&(ko.current=!1);let A=!w&&Xe.current!==null;A&&(Xe.current={...Xe.current,stopping:!0}),h({kind:"note",tone:"ok",text:A?"Manual mode \u2014 I'll finish what is running and stop there, then hand back. (shift+tab to change it back.)":`${w?"Auto mode ON":"Manual mode"} \u2014 ${Vs(w)} (shift+tab to change it back.)`}),D(U=>U+1);return}if(C){if(m.escape){if(Xe.current){ko.current=!0;let u=Xe.current;iu(),Ss({kind:"stopped",how:"esc"},u.parked);return}iu();return}if(m.return){let u=g.trim();if(!u)return;bo.current.push(u),Oc([...bo.current]),y("");return}if(ym(p,m))return;if(m.upArrow){sa(-1);return}if(m.downArrow){sa(1);return}if(m.backspace||m.delete){T();return}p&&!m.ctrl&&!m.meta&&mm(p);return}if(gn){if(m.upArrow){ho(u=>u&&{...u,cursor:Math.max(0,u.cursor-1)});return}if(m.downArrow){ho(u=>u&&{...u,cursor:Math.min(Ut.length-1,u.cursor+1)});return}if(p&&/^[0-9]$/.test(p)){let u=wn(p,Ut.length);u!==null&&ho(w=>w&&{...w,cursor:u});return}if(m.return){let u=Ut[gn.cursor];ho(null);let{lines:w,changed:A,effective:U}=ni(e,u.value??"default");for(let O of w)h({kind:"note",tone:O.startsWith("\u26A0")?"warn":O.startsWith("\u2713")?"ok":"info",text:O});A&&(zf(O=>O+1),Ne.current?.setModel(U));return}if(p==="s"){let u=Ut[gn.cursor];ho(null),Ne.current?.setModel(u.value??void 0),h({kind:"note",tone:"ok",text:`\u2713 Using ${u.label} for this session only (your saved default is unchanged).`});return}if(m.escape){ho(null);return}return}if(qt){if(m.upArrow){ir(u=>u&&{...u,cursor:Math.max(0,u.cursor-1)});return}if(m.downArrow){ir(u=>u&&{...u,cursor:Math.min(u.rows.length-1,u.cursor+1)});return}if(p&&/^[0-9]$/.test(p)){let u=wn(p,qt.rows.length);u!==null&&ir(w=>w&&{...w,cursor:u});return}if(m.return||p==="s"){let u=qt.rows[qt.cursor];if(ir(null),u.connect){Mn({cursor:0,mode:"connect",rows:ca()});return}ta(u.name,m.return===!0);return}if(m.escape){ir(null);return}return}if(ws){if(m.escape){xo(null);return}if(m.leftArrow||m.tab&&m.shift){xo(u=>u&&{tab:(u.tab-1+$t.length)%$t.length,scroll:0});return}if(m.rightArrow||m.tab){xo(u=>u&&{tab:(u.tab+1)%$t.length,scroll:0});return}if(p&&/^[1-9]$/.test(p)&&Number(p)<=$t.length){xo({tab:Number(p)-1,scroll:0});return}if(m.upArrow){xo(u=>u&&{...u,scroll:Math.max(0,u.scroll-1)});return}if(m.downArrow){xo(u=>{if(!u)return u;let w=($t[u.tab]??$t[0]).rows.length;return{...u,scroll:Math.min(Math.max(0,w-1),u.scroll+1)}});return}return}if(Pt){if(m.upArrow){Mn(u=>u&&{...u,cursor:Math.max(0,u.cursor-1)});return}if(m.downArrow){Mn(u=>u&&{...u,cursor:Math.min(u.rows.length-1,u.cursor+1)});return}if(p&&/^[0-9]$/.test(p)){let u=wn(p,Pt.rows.length);u!==null&&Mn(w=>w&&{...w,cursor:u});return}if(m.return){let u=Pt.rows[Pt.cursor].name,w=Pt.mode;if(Mn(null),w==="connect"){let O=new Set(Object.keys($e(e)?.profiles??{})),b=u;for(let R=2;O.has(b);R++)b=`${u}-${R}`;let $=xl(e,b,u);if($){h({kind:"note",tone:"error",text:$});return}let[v,...k]=Rl(b,u);h({kind:"note",tone:"ok",text:v}),h({kind:"note",tone:"info",text:`Saved as this repo\u2019s default, so everyone who pulls this repo publishes here.
|
|
604
604
|
Press s in /profile to use a profile for one session only.`});for(let R of k)h({kind:"note",tone:"warn",text:R});ta(b,!0);return}if(!Cu(e,u).created){h({kind:"note",tone:"error",text:"Could not scaffold \u2014 .d360-writer/config.json already exists."});return}h({kind:"note",tone:"ok",text:`\u2713 Wrote .d360-writer/config.json (environment "${u}").`});let U=!1;try{let O=ao(u);U=!!O&&!(er(O)&&!O.refreshToken)}catch{U=!1}h({kind:"note",tone:"info",text:U?`Next: sign in to ${u} and pick your project \u2014 you're already signed in here, so it'll be quick.`:`Next: sign in to Document360 (${u}) and pick your project.`}),le("/login"),D(O=>O+1);return}if(m.escape){Mn(null);return}return}if(at){if(m.upArrow){ar(u=>u&&{...u,cursor:Math.max(0,u.cursor-1)});return}if(m.downArrow){ar(u=>u&&{...u,cursor:Math.min(u.rows.length-1,u.cursor+1)});return}if(p&&/^[0-9]$/.test(p)){let u=wn(p,at.rows.length);u!==null&&ar(w=>w&&{...w,cursor:u});return}if(m.return){let u=at.rows[at.cursor],{profile:w,projectId:A}=at;ar(null),na(w,A,u);return}if(m.escape){ar(null);return}return}if(pt){if(m.upArrow){fo(u=>u&&{...u,cursor:Math.max(0,u.cursor-1)});return}if(m.downArrow){fo(u=>u&&{...u,cursor:Math.min(u.rows.length-1,u.cursor+1)});return}if(p&&/^[0-9]$/.test(p)){let u=wn(p,pt.rows.length);u!==null&&fo(w=>w&&{...w,cursor:u});return}if(m.return){let u=pt.rows[pt.cursor],{profile:w}=pt;fo(null),ra(w,u);return}if(m.escape){fo(null);return}return}if(ht){if(m.upArrow){ur(u=>u&&{...u,cursor:Math.max(0,u.cursor-1)});return}if(m.downArrow){ur(u=>u&&{...u,cursor:Math.min(u.rows.length-1,u.cursor+1)});return}if(p===" "){ur(u=>u&&{...u,rows:u.rows.map((w,A)=>A===u.cursor?{...w,checked:!w.checked}:w)});return}if(m.return){let u=ht.rows.filter(w=>w.checked).map(w=>w.path);if(ur(null),u.length===0){h({kind:"note",tone:"info",text:"Nothing selected \u2014 scope unchanged."});return}Sd(e,u),h({kind:"note",tone:"ok",text:`\u2713 Scoped to ${u.length} folder(s) \u2014 written to .d360-writer/config.json`});for(let w of u)h({kind:"note",tone:"info",text:` ${w}`});h({kind:"note",tone:"info",text:"Next: ask me to analyze these folders and propose a docs structure."});return}if(m.escape){ur(null);return}return}if(wt){if(m.upArrow){go(u=>u&&{...u,cursor:Math.max(0,u.cursor-1)});return}if(m.downArrow){go(u=>u&&{...u,cursor:Math.min(u.rows.length-1,u.cursor+1)});return}if(p&&/^[0-9]$/.test(p)){let u=wn(p,wt.rows.length);u!==null&&go(w=>w&&{...w,cursor:u});return}if(m.return){let u=wt.rows[wt.cursor],w=wt.run;go(null),w(u.token);return}if(m.escape){go(null);return}return}if(Tt){if(m.upArrow){lr(u=>u&&{...u,cursor:Math.max(0,u.cursor-1)});return}if(m.downArrow){lr(u=>u&&{...u,cursor:Math.min(u.rows.length-1,u.cursor+1)});return}if(p&&/^[0-9]$/.test(p)){let u=wn(p,Tt.rows.length);u!==null&&lr(w=>w&&{...w,cursor:u});return}if(m.return){let u=Tt.rows[Tt.cursor],w=Tt.conflicts;lr(null),u.conflict?ru(u.paths[0]):u.kind==="article"?eu(u.paths[0]):kt(u.paths,u.kind==="all"?w:0);return}if(m.escape){lr(null);return}return}if(Vt){if(m.upArrow){cr(u=>u&&{...u,cursor:Math.max(0,u.cursor-1)});return}if(m.downArrow){cr(u=>u&&{...u,cursor:Math.min(u.rows.length,u.cursor+1)});return}if(p&&/^[0-9]$/.test(p)){let u=wn(p,Vt.rows.length+1);u!==null&&cr(w=>w&&{...w,cursor:u});return}if(m.return){let{cursor:u,rows:w,opts:A}=Vt;cr(null);let U=u===0?w:[w[u-1]];lm(U,A);return}if(m.escape){cr(null),h({kind:"note",tone:"info",text:"Left as drafts \u2014 nothing changed. /publish when they have been signed off."});return}return}if(At){let{rows:u}=At;if(m.upArrow){yn(w=>w&&{...w,cursor:Math.max(0,w.cursor-1)});return}if(m.downArrow){yn(w=>w&&{...w,cursor:Math.min(u.length-1,w.cursor+1)});return}if(p&&/^[0-9]$/.test(p)){let w=gf(Ff.current,Number(p),u.length-1);Ff.current=w.memory,w.index!==null&&yn(A=>A&&{...A,cursor:w.index+1});return}if(p==="a"||p==="A"){yn(w=>w&&{...w,cursor:0});return}if(m.return){let w=u[At.cursor],{groups:A,resolve:U,report:O}=At;if(yn(null),w.kind==="all"){Zc(A,{resolve:U,report:O});return}mr(_l(w.group,{width:nt()})),hs({group:w.group,cursor:0,fromList:!0,resolve:U,rest:A.filter(b=>b.articleId!==w.group.articleId),report:O});return}if(m.escape){yn(null),le("/comments");return}return}if(ct){let u=()=>{let{rest:w,group:A,fromList:U,resolve:O,report:b}=ct;if(hs(null),!U||!b){le("/comments");return}let $=[A,...w].sort((v,k)=>v.category.localeCompare(k.category)||v.title.localeCompare(k.title));yn({cursor:0,rows:Ml($),groups:$,resolve:O,report:b})};if(m.upArrow||m.downArrow){hs(w=>w&&{...w,cursor:w.cursor===0?1:0});return}if(p==="y"||p==="Y"||m.return&&ct.cursor===0){let{group:w,resolve:A,rest:U,report:O}=ct;hs(null),Zc([w],{resolve:A,rest:U,...O?{report:O}:{}});return}if(p==="b"||p==="B"||m.escape||m.return){u();return}return}if(Yt){if(m.upArrow){mo(u=>u&&{...u,cursor:Math.max(0,u.cursor-1)});return}if(m.downArrow){mo(u=>u&&{...u,cursor:Math.min(u.rows.length-1,u.cursor+1)});return}if(p&&/^[0-9]$/.test(p)){let u=wn(p,Yt.rows.length);u!==null&&mo(w=>w&&{...w,cursor:u});return}if(m.return){let u=Yt.rows[Yt.cursor];mo(null),u.action==="capture"?Cs(u.ids,{runLine:u.kind==="all"?["/screenshot capture --all","/capture --all"]:void 0}):u.action==="annotate"?ea(u.ids,{verifiedBefore:new Set(He(e).filter(w=>w.state==="verified").map(w=>w.id))}):u.kind==="shot"?dm(u.ids[0]):um(u.ids,{setup:!0,runLine:u.kind==="all"?["/screenshot author --all","/screenshot --all"]:void 0});return}if(m.escape){mo(null);return}return}if(Et){if(m.escape){wo(null);return}if(m.upArrow){wo(u=>u&&{...u,cursor:Math.max(0,u.cursor-1)});return}if(m.downArrow){wo(u=>u&&{...u,cursor:Math.min(Math.max(0,Xi.length-1),u.cursor+1)});return}if(m.return){let u=Xi[Et.cursor];if(u){wo(null);try{h({kind:"preview",name:Ai(u),text:t0(Tc(Ei(e,u),"utf8"))})}catch(w){h({kind:"note",tone:"error",text:`Could not read ${u}: ${w.message}`})}}return}if(m.backspace||m.delete){wo(u=>u&&{...u,query:u.query.slice(0,-1),cursor:0});return}if(p&&!m.ctrl&&!m.meta&&p.length===1){wo(u=>u&&{...u,query:u.query+p,cursor:0});return}return}if(Nt){if(m.escape){vo(null);return}if(m.upArrow){vo(u=>u&&{...u,cursor:Math.max(0,u.cursor-1)});return}if(m.downArrow){vo(u=>u&&{...u,cursor:Math.min(Math.max(0,Qi.length-1),u.cursor+1)});return}if(m.return){let u=Qi[Nt.cursor];if(u){vo(null),Ve(u.uuid),et.current={uuid:u.uuid,firstPrompt:u.firstPrompt,titleFired:!0},po(u.name),Rf(u.uuid),de.current=[],mt.current={printed:[],armed:[],printedAt:hr.current};let w=yf(e,u.uuid);for(let A of w)h(A);h({kind:"note",tone:"ok",text:w.length?`Resumed "${u.name}" \u2014 restored ${w.length} message(s); continue where you left off.`:`Resumed "${u.name}" (agent memory reconnected; no saved transcript to replay).`})}return}if(m.backspace||m.delete){vo(u=>u&&{...u,query:u.query.slice(0,-1),cursor:0});return}if(p&&!m.ctrl&&!m.meta&&p.length===1){vo(u=>u&&{...u,query:u.query+p,cursor:0});return}return}if(m.tab&&!g&&oe){y(oe),Kt();return}if(!g&&ge.length>0&&!Ee.current&&p&&/^[1-9]$/.test(p)){let u=ge[Number(p)-1];if(u){y(io(u));return}}if(!g&&ge.length===0&&!Ee.current&&mt.current.armed.length>0&&p&&/^[1-9]$/.test(p)){let u=mt.current.armed[Number(p)-1];if(u){y(u);return}}if(!g&&(Yi||Co)&&p==="c"&&!m.ctrl&&!m.meta&&!m.return&&!m.tab){let u=Yi!=null;hf(Yi??Co).then(A=>{h({kind:"note",tone:A?"ok":"warn",text:A?u?"\u2713 Copied the feedback to your clipboard \u2014 send it to the d360-writer team (Teams / Slack / email).":"\u2713 Copied the prompt to your clipboard \u2014 paste it into your repo's coding agent.":u?"Couldn't reach your clipboard \u2014 select the feedback above and copy it manually.":"Couldn't reach your clipboard \u2014 select the prompt above and copy it manually."}),A&&u&&Yc(null),A&&!u&&Jf(!0)});return}if(!ym(p,m)){if(Xc){if(m.upArrow){Ln(u=>Math.max(0,u-1));return}if(m.downArrow){Ln(u=>Math.min(vn.length-1,u+1));return}if(m.tab){y("/"+(vn[nr]?.name??"")+" "),Ln(0);return}if(m.return){let u=vn[nr];if(u){let w=g.trim().slice(1).split(/\s+/).slice(1).join(" ");if(aw(u.usage)&&!w){y("/"+u.name+" "),Ln(0);return}Ps("/"+u.name+(w?" "+w:""));return}}}else{if(!g&&ge.length>0&&!Ee.current&&(m.upArrow||m.downArrow)){if(m.downArrow){On(u=>u===null?0:Math.min(ge.length-1,u+1));return}if(fn!==null){On(u=>u===null||u===0?null:u-1);return}}if(m.upArrow){if(g!==""&&Ht===null){sa(-1);return}let u=Ic.current;if(!u.length)return;let w=Ht===null?u.length-1:Math.max(0,Ht-1);or(w),y(u[w]??"");return}if(m.downArrow){if(g!==""&&Ht===null){sa(1);return}let u=Ic.current;if(Ht===null)return;let w=Ht+1;w>=u.length?(or(null),y("")):(or(w),y(u[w]??""));return}}if(m.return){if(fn!==null&&!g&&ge[fn]){let u=io(ge[fn]);On(null),Ps(u);return}Ps();return}if(m.backspace||m.delete){T();return}if(m.escape){Xe.current&&(ko.current=!0,Ss({kind:"stopped",how:"esc"},Xe.current.parked)),y(""),Ln(0),Kt(),we([]),yo(null),On(null),ee.current=!0;return}p&&!m.ctrl&&!m.meta&&mm(p)}}}),lt(()=>{if(C||S)return;let p=bo.current.shift();p!==void 0&&(Oc([...bo.current]),Ps(p))},[C,S,Ps]),me(Ie,{flexDirection:"column",width:gt,children:[Jc!==null&&me(Ie,{marginTop:1,flexDirection:"column",children:[Jc.truncated&&K(z,{dimColor:!0,children:"\u2026"}),K(z,{children:Jc.text})]}),dr?K(b3,{step:dr.step,cmd:dr.cmd,startedAt:dr.startedAt,spentUsd:Math.max(dr.spentUsd,Dt-(Xe.current?.stepStartedSpend??Dt)),budgetUsd:dr.budgetUsd}):null,C&&pr?K(v3,{p:pr}):null,Vf?K(k3,{text:Vf}):C&&!pr?K(w3,{startTime:Bt.current,chars:Li}):null,K(Ie,{borderStyle:"round",borderColor:sm.prod?"yellow":ue,borderTop:!0,borderBottom:!0,borderLeft:!1,borderRight:!1,marginTop:1,flexDirection:"column",children:g?gm.map((p,m)=>{let u=c.text.slice(p.start,p.end),w=m===Cc(gm,c.pos),A=Math.min(c.pos,p.end)-p.start;return me(z,{children:[K(z,{color:ue,children:m===0?"> ":" "}),w?me(Zo,{children:[u.slice(0,A),K(cs,{ch:u[A]??" "}),u.slice(A+1)]}):u||" "]},`${m}-${p.start}`)}):me(z,{children:[K(z,{color:ue,children:"> "}),Un?K(cs,{ch:" "}):ke&&!C?me(Zo,{children:[K(cs,{ch:ke[0],dim:!0}),K(z,{color:"gray",children:ke.slice(1)})]}):oe&&!C?me(Zo,{children:[K(cs,{ch:oe[0],dim:!0}),K(z,{color:"gray",children:oe.slice(1)}),K(z,{dimColor:!0,children:" (tab)"})]}):(zc.isSetup||!sn)&&!C?me(Zo,{children:[K(cs,{ch:zc.text[0],dim:!0}),K(z,{color:"gray",children:zc.text.slice(1)})]}):K(cs,{ch:" "})]})}),Hf.length>0&&K(Ie,{flexDirection:"column",paddingX:1,children:Hf.map((p,m)=>K(z,{color:"gray",children:`\u29D7 queued: ${p}`},`${m}-${p.slice(0,24)}`))}),ft?me(Ie,{flexDirection:"column",paddingX:1,children:[K(z,{color:ue,bold:!0,children:"Auto mode \u2014 I run each step myself"}),K(z,{color:"gray",children:` first step ${ft.firstCmd}${ft.scale?` \u2014 ${ft.scale}`:""}`}),ft.upcoming.length>0?me(Zo,{children:[K(z,{color:"gray",children:" as things stand, after that:"}),ft.upcoming.map(p=>K(z,{color:"gray",children:` ${p.cmd}${p.scale?` ${p.scale}`:""}`},p.cmd)),K(z,{color:"gray",children:" each step changes what the next one finds, so this is not a total"})]}):K(z,{color:"gray",children:" I work down the ladder from there \u2014 reviewing, checking style, saving and going live"}),[ft.scale,...ft.upcoming.map(p=>p.scale)].every(p=>!p?.includes("$"))?K(z,{color:"gray",children:" no cost estimate yet \u2014 this repo has not finished a run for me to measure"}):null,K(z,{color:"gray",children:` spending limit ${Ke(ft.budgetUsd)} \xB7 I stop and report if it is reached`}),K(z,{color:"gray",children:" screenshots are not part of an automatic run"}),ft.overBudget?K(z,{color:"yellow",children:`\u26A0 That is more than your ${Ke(ft.budgetUsd)} limit. I will stop partway and report what is left.`}):null,K(z,{dimColor:!0,children:ft.overBudget?"b raise the limit \xB7 enter start anyway \xB7 esc stay in manual for now":"enter start \xB7 b change the limit \xB7 esc stay in manual for now"})]}):ws?K(dv,{tab:ws.tab,scroll:ws.scroll,rows:f0,width:gt,version:o,engineVersion:n0}):gn?me(Ie,{flexDirection:"column",paddingX:1,children:[K(z,{color:ue,bold:!0,children:"Select model"}),K(z,{color:"gray",children:"Your pick becomes your personal default for new sessions (team .d360-writer/config.json still wins)."}),Ut.map((p,m)=>me(z,{color:m===gn.cursor?ue:void 0,children:[m===gn.cursor?"\u276F ":" ",`${m+1}. ${p.label}${m===gn.current?" \u2714":""}`.padEnd(16),K(z,{color:"gray",children:` ${p.desc}`})]},p.label)),K(z,{dimColor:!0,children:St({verb:"set as default",rows:Ut.length,extra:["s this session only"]})})]}):qt?me(Ie,{flexDirection:"column",paddingX:1,children:[K(z,{color:ue,bold:!0,children:"Switch connection profile"}),K(z,{color:"gray",children:"Where this repo publishes. The environment lives on the profile, not on your login."}),K(In,{rows:qt.rows,cursor:qt.cursor,cap:an,render:(p,m,u)=>me(z,{color:u?ue:void 0,children:[u?"\u276F ":" ",`${m+1}. ${p.name}${m===qt.current?" \u2714":""}`.padEnd(20),p.connect?null:K(z,{color:"gray",children:` ${p.portalUrl||p.env}${p.project?` \xB7 ${p.project}`:""} \xB7 ${p.who??"not signed in"}`}),p.prod?K(z,{color:"yellow",bold:!0,children:" \u26A0 PRODUCTION"}):null,p.internal?K(z,{color:"yellow",children:" \u26A0 internal"}):null]},p.name)}),K(z,{dimColor:!0,children:St({verb:"switch (saved as default)",rows:qt.rows.length,extra:["s this session only"]})})]}):Pt?me(Ie,{flexDirection:"column",paddingX:1,children:[K(z,{color:ue,bold:!0,children:"Pick your Document360 environment"}),K(z,{color:"gray",children:"This is the portal your readers see. Rows marked internal are ours, not a customer's."}),K(In,{rows:Pt.rows,cursor:Pt.cursor,cap:an,render:(p,m,u)=>me(z,{color:u?ue:void 0,children:[u?"\u276F ":" ",`${m+1}. ${p.name}`.padEnd(18),K(z,{color:"gray",children:` ${p.portalUrl.replace(/^https:\/\//,"")}`}),p.note?p.internal||!p.production?K(z,{color:"yellow",children:` \u26A0 ${p.note}`}):K(z,{color:"gray",children:` ${p.note}`}):null]},p.name)}),K(z,{dimColor:!0,children:St({verb:"use it",rows:Pt.rows.length})})]}):at?me(Ie,{flexDirection:"column",paddingX:1,children:[K(z,{color:ue,bold:!0,children:"Switch workspace"}),K(z,{dimColor:!0,children:`environment ${at.environment} \xB7 project ${at.projectName??`${at.projectId.slice(0,8)}\u2026`}`}),K(In,{rows:at.rows,cursor:at.cursor,cap:an,render:(p,m,u)=>me(z,{color:u?ue:void 0,children:[u?"\u276F ":" ",`${m+1}. ${p.name}${m===at.current?" \u2714":""}`.padEnd(30),K(z,{color:"gray",children:p.type?` ${p.type}`:""})]},p.id)}),K(z,{dimColor:!0,children:St({verb:"switch",rows:at.rows.length})})]}):pt?me(Ie,{flexDirection:"column",paddingX:1,children:[K(z,{color:ue,bold:!0,children:"Choose project"}),K(z,{dimColor:!0,children:`environment ${pt.environment}`}),K(In,{rows:pt.rows,cursor:pt.cursor,cap:an,render:(p,m,u)=>me(z,{color:u?ue:void 0,children:[u?"\u276F ":" ",`${m+1}. ${p.name}${m===pt.current?" \u2714":""}`.padEnd(30),K(z,{color:"gray",children:p.sub?` ${p.sub}`:""})]},p.id)}),K(z,{dimColor:!0,children:St({verb:"switch",rows:pt.rows.length})})]}):ht?(()=>{let m=Math.min(Math.max(0,ht.cursor-Math.floor(7)),Math.max(0,ht.rows.length-14)),u=ht.rows.slice(m,m+14),w=ht.rows.filter(A=>A.checked).length;return me(Ie,{flexDirection:"column",paddingX:1,children:[K(z,{color:ue,bold:!0,children:`Which folders back the user docs? (${w} selected of ${ht.rows.length})`}),m>0?K(z,{dimColor:!0,children:` \u2191 ${m} more`}):null,u.map((A,U)=>{let O=m+U;return me(z,{color:O===ht.cursor?ue:void 0,children:[O===ht.cursor?"\u276F ":" ",A.checked?"\u25C9 ":"\u25CB ",A.path.padEnd(Math.min(48,gt-34)),K(z,{color:"gray",children:sl(A)})]},A.path)}),m+14<ht.rows.length?K(z,{dimColor:!0,children:` \u2193 ${ht.rows.length-m-14} more`}):null,K(z,{dimColor:!0,children:St({verb:"save",multi:!0})})]})})():wt?me(Ie,{flexDirection:"column",paddingX:1,children:[K(z,{color:ue,bold:!0,children:wt.title??"Which category? (pick one to scope to)"}),K(In,{rows:wt.rows,cursor:wt.cursor,cap:an,render:(p,m,u)=>me(z,{color:u?ue:void 0,children:[u?"\u276F ":" ",`${`${m+1}.`.padEnd(3)} ${p.label} (${p.count})`.padEnd(Math.min(44,Math.max(20,gt-24))),K(z,{color:"gray",children:` ${p.token}`})]},p.token)}),K(z,{dimColor:!0,children:St({verb:"choose it",rows:wt.rows.length})})]}):Tt?me(Ie,{flexDirection:"column",paddingX:1,children:[K(z,{color:ue,bold:!0,children:"Publish to Document360 \u2014 pick a category (whole) or an article:"}),K(In,{rows:Tt.rows,cursor:Tt.cursor,cap:an,render:(p,m,u)=>me(z,{color:u?ue:void 0,bold:p.kind==="category"&&!u,children:[u?"\u276F ":" ",`${p.indent?" ":" "}${`${m+1}.`.padEnd(3)} ${p.label}`.padEnd(Math.min(56,gt-24)),p.note?K(z,{color:"gray",children:` ${p.note}`}):null]},`${p.kind}:${p.paths.join(",")}`)}),K(z,{dimColor:!0,children:St({verb:"push as drafts",rows:Tt.rows.length})})]}):Vt?me(Ie,{flexDirection:"column",paddingX:1,children:[K(z,{color:ue,bold:!0,children:"Publish live to your readers \u2014 this is what they will see:"}),K(In,{rows:[{path:"",articleId:"",label:`Publish all ${Vt.rows.length} live`,version:void 0},...Vt.rows],cursor:Vt.cursor,cap:an,render:(p,m,u)=>me(z,{color:u?ue:void 0,bold:m===0&&!u,children:[u?"\u276F ":" ",` ${`${m+1}.`.padEnd(3)} ${p.label}`.padEnd(Math.min(56,gt-24)),m>0?K(z,{color:"gray",children:` ${p.version?`draft v${p.version} \u2192 live`:"draft \u2192 live"}`}):null]},`${m}:${p.path||"all"}`)}),K(z,{dimColor:!0,children:St({verb:"publish live",cancel:"leave as drafts",rows:Vt.rows.length+1})})]}):At?me(Ie,{flexDirection:"column",paddingX:1,children:[K(z,{color:ue,bold:!0,children:"Which comments do you want to look at?"}),K(In,{rows:At.rows,cursor:At.cursor,cap:an,render:(p,m,u)=>me(z,{color:u?ue:void 0,bold:p.kind==="all"&&!u,children:[u?"\u276F ":" ",`${(p.kind==="all"?"":`${p.n}.`).padEnd(4)}${p.label}`.padEnd(Math.min(56,Math.max(20,gt-24))),p.kind==="article"?K(z,{color:"gray",children:p.note}):null]},p.kind==="all"?"all":p.group.articleId)}),K(z,{dimColor:!0,children:St({verb:"open it",cancel:"close",rows:At.rows.length-1})}),At.resolve?K(z,{dimColor:!0,children:"--resolve is on: threads you answer will be closed as well as replied to"}):null]}):ct?me(Ie,{flexDirection:"column",paddingX:1,children:[K(z,{color:ue,bold:!0,children:`${ct.group.title} \u2014 ${ct.group.threads.length} comment${ct.group.threads.length===1?"":"s"}`}),me(z,{color:ct.cursor===0?ue:void 0,children:[ct.cursor===0?"\u276F ":" ",ct.resolve?"Yes \u2014 fix them, push the draft, reply and resolve":"Yes \u2014 fix them, push the draft to Document360, then reply"]}),me(z,{color:ct.cursor===1?ue:void 0,children:[ct.cursor===1?"\u276F ":" ",ct.fromList?"No \u2014 back to the list":"No \u2014 leave them for now"]}),K(z,{dimColor:!0,children:St({verb:"open it",cancel:"go back",extra:["y answer"]})})]}):Yt?me(Ie,{flexDirection:"column",paddingX:1,children:[K(z,{color:ue,bold:!0,children:Yt.title}),(()=>{let{rows:p,cursor:m}=Yt,u=yc(p.length,m,an),w=u.start,A=p.slice(u.start,u.end),U=`${p.length}.`.length+1,O=sy(p,Math.max(20,gt-4-U));return me(Zo,{children:[w>0?K(z,{color:"gray",children:` \u2026 ${w} more above`}):null,A.map((b,$)=>{let v=w+$,k=v===m,R=O[v];return me(z,{color:k?ue:void 0,bold:(b.kind==="all"||b.kind==="category")&&!k,children:[k?"\u276F ":" ",`${v+1}.`.padEnd(U),R.head,R.note?K(z,{color:"gray",children:R.note}):null]},`${b.action}:${b.kind}:${b.ids.join(",")}`)}),u.below>0?K(z,{color:"gray",children:` \u2026 ${u.below} more below`}):null]})})(),K(z,{dimColor:!0,children:St({verb:"run it",rows:Yt.rows.length})})]}):Et?me(Ie,{flexDirection:"column",paddingX:1,children:[K(z,{color:ue,bold:!0,children:`Preview article${Et.query?` \u2014 filter: ${Et.query}`:" (type to filter)"}`}),Xi.length===0?K(z,{color:"gray",children:"no articles match"}):Xi.map((p,m)=>me(z,{color:m===Et.cursor?ue:void 0,children:[m===Et.cursor?"\u276F ":" ",p]},p)),K(z,{dimColor:!0,children:"enter preview \xB7 esc cancel"})]}):Nt?me(Ie,{flexDirection:"column",paddingX:1,children:[K(z,{color:ue,bold:!0,children:`Resume session${Nt.query?` \u2014 filter: ${Nt.query}`:" (type to filter)"}`}),Qi.length===0?K(z,{color:"gray",children:"no sessions match"}):Qi.map((p,m)=>me(z,{color:m===Nt.cursor?ue:void 0,children:[m===Nt.cursor?"\u276F ":" ",p.name.slice(0,28).padEnd(30),K(z,{color:m===Nt.cursor?ue:"gray",children:p.firstPrompt.slice(0,Math.max(10,gt-40))})]},p.uuid)),K(z,{dimColor:!0,children:"enter resume \xB7 esc cancel"})]}):Xc?me(Ie,{flexDirection:"column",children:[To>0?K(z,{dimColor:!0,children:` \u22EF ${To} more above`}):null,Qc.map((p,m)=>{let u=To+m,w=p.name==="model"&&Wc?`${p.desc} (currently ${Wc})`:p.desc,A=Math.max(20,gt-4),U=p.usage.length>A?`${p.usage.slice(0,A-1)}\u2026`:p.usage,O=A-Math.max(U.length,22)-1,b=O<=1?"":w.length>O?`${w.slice(0,O-1)}\u2026`:w;return me(z,{color:u===zi?ue:void 0,children:[u===zi?"\u276F ":" ",U.padEnd(22)," ",K(z,{color:u===zi?ue:"gray",children:b})]},p.name)}),To+Qc.length<vn.length?K(z,{dimColor:!0,children:` \u22EF ${vn.length-To-Qc.length} more below`}):null,K(z,{dimColor:!0,children:"\u2191\u2193 choose \xB7 enter run \xB7 tab complete \xB7 esc dismiss"})]}):!C&&!g&&ge.length>0?me(Ie,{flexDirection:"column",paddingX:1,children:[Bf?K(z,{color:ue,children:Bf}):null,K(In,{rows:ge,cursor:fn??0,cap:an,render:(p,m)=>{let u=m===fn,w=io(p),A=w.slice(0,Math.max(20,gt-7)),U=gt-A.length-9,O=U>=12?(fv(p)??sw(w)).slice(0,U):"";return me(z,{color:u?ue:void 0,children:[u?"\u276F ":" ",K(z,{color:ue,children:m+1})," ",A,O?K(z,{color:u?ue:void 0,dimColor:!u,children:` \u2014 ${O}`}):null]},w)}}),K(z,{dimColor:!0,children:`\u2191\u2193 choose \xB7 enter run \xB7 1-${ge.length} fill \xB7 esc cancel`})]}):K(Ie,{paddingX:1,children:me(z,{color:"gray",children:[sm.prod?"\u26A0 PRODUCTION \xB7 ":"",K(z,{color:bs?ue:void 0,children:bs?"\u23F5\u23F5 auto":"\u23F5 manual"}),` \xB7 /help \xB7 ${Wc??"model n/a"}${t.kind==="api"?Dt>0?` \xB7 session ${xt(Dt)}`:"":Kf>0?` \xB7 ${Fr(Kf)}`:""} \xB7 \u2191 history \xB7 shift+tab mode \xB7 ${c0?"press ctrl+c again to exit":"ctrl+c twice to exit"}`]})})]})}ie();import{jsx as C3}from"react/jsx-runtime";async function r0(e=process.cwd(),t="auto",n,o="0.0.0"){let r=S3(t);r.kind==="none"&&(console.error(""),console.error(_("ANTHROPIC_API_KEY is not set (required for --auth api).")),console.error(`Get a key at ${Z("https://console.anthropic.com/settings/keys")}`),console.error(`Or use your Claude subscription instead: ${Z("d360-writer --auth subscription")}`),process.exit(2));let{waitUntilExit:s}=R3(C3(o0,{cwd:e,auth:r,profileName:n,version:o}),{exitOnCtrlC:!1});await s(),await Wl(),process.stdout.write(`
|
|
605
605
|
`),process.exit(0)}ie();import{writeCrashLog as If,isBenignTransportError as P3}from"document360-engine";var s0=!1;function T3(e=process.stdout){return e.destroyed===!0||e.writable===!1}function Lf(e){return e instanceof Error?e.message:typeof e=="string"?e:JSON.stringify(e)}function i0(e){if(s0)return;s0=!0;let t=(n,o)=>{if(P3(o)){If({kind:n,level:"transport",message:Lf(o),stack:o instanceof Error?o.stack:void 0,detail:e}),T3()&&(If({kind:"terminal-gone",level:"transport",message:"stdout is closed \u2014 the terminal this session was rendering to is gone, so it exited rather than spin.",detail:e}),process.exit(0));return}let r=If({kind:n,level:"fatal",message:Lf(o),stack:o instanceof Error?o.stack:void 0,detail:e});process.stderr.write(`
|
|
606
606
|
`+_(`\u2717 d360-writer crashed: ${Lf(o)}`)+`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "document360-writer",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.315",
|
|
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.280",
|
|
38
38
|
"ink": "^5.2.1",
|
|
39
39
|
"picocolors": "^1.1.1",
|
|
40
40
|
"react": "^18.3.1",
|