document360-writer 0.4.89 → 0.4.91

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 CHANGED
@@ -225,7 +225,7 @@ Articles:
225
225
  `),s=n.mode==="scope"?`/screenshot ${n.scope}`:`/screenshot${e[0]?` ${e[0]}`:""}`;return{kind:"forward-to-agent",prompt:r,display:s}}import{existsSync as qu,readFileSync as Bu}from"node:fs";import{isAbsolute as Gu,join as Vu,resolve as Yu}from"node:path";import{readProjectConfig as zu,DEFAULT_CAPTURE_DIR as Xu}from"document360-engine";async function Bt(){return{kind:"forward-to-agent",prompt:["Run the capture-setup-checklist skill.","","Scan every <!-- SCREENSHOT --> block across the docs tree (the configured docsDir), read each block\u2019s prerequisites,","anchor them to the capture scope keys in .d360-capture.json, then synthesize a deduped, grouped","\u201Cstage this data\u201D checklist and write it to <captureDir>/CAPTURE-SETUP.md. Separate transient/manual","states, flag any spec whose prerequisites are vague, and report the path when done."].join(`
226
226
  `),display:"/capture-setup"}}var Yi="-".repeat(64);function gr(e){let t=Xu;try{let k=zu(e);k?.captureDir&&(t=k.captureDir.replace(/\\/g,"/").replace(/\/+$/,""))}catch{}let n=Gu(t)?Vu(t,"CAPTURE-TESTID-REQUESTS.json"):Yu(e,t,"CAPTURE-TESTID-REQUESTS.json");if(!qu(n))return null;let o;try{let k=JSON.parse(Bu(n,"utf8").replace(/^/,""));o=Array.isArray(k?.requests)?k.requests:[]}catch{return null}if(o.length===0)return null;let r=new Set(o.map(k=>k.file).filter(Boolean)).size,s=o.length,c=`${s} data-testid attribute${s===1?"":"s"}`,p=`${r} source file${r===1?"":"s"}`,h=`${t}/CAPTURE-TESTID-REQUESTS.md`,b=`${t}/CAPTURE-TESTID-REQUESTS.json`;return[`\u{1F4CB} Developer hand-off \u2014 ${c} requested across ${p}.`,"\u26A0 Do NOT run this prompt here. d360-writer documents code; it does not change it \u2014"," these edits belong in your code repo. Open a SEPARATE terminal in:",` ${e}`," start Claude Code (or your coding agent), and paste everything between the lines."," Tip: run it in plan mode first \u2014 these are a hypothesis to verify, not a verified patch.","",Yi,`Read ${h} and ${b}. They list ${c} that d360-writer THINKS this repo needs for screenshot automation. Treat them as a hypothesis to VERIFY against the source \u2014 not a verified patch list.`,"","For EACH request in the JSON:",'1. Locate the element in THIS repo. Trace it from the route/page, not the feature name. If "anchorVerified" is false, find it by the "element"/"reason" description; otherwise confirm the "anchor" snippet still exists in "file".',"2. If the element already has a data-testid, skip it and note the existing one.","3. Confirm the attribute will reach the DOM: the rendering component must forward unknown props (a {...rest} spread or explicit pass-through). If it is a shared wrapper (e.g. Modal, ContextMenu) that destructures a fixed prop list, a data-testid is silently dropped \u2014 thread it through the component, or report it as needing a code change instead of forcing it.",'4. Only then add data-testid="<testid>" to the verified element. Add nothing else; do not rename, restructure, reformat, or change behaviour.',"","When done, report: which testids you added (file + element), which were already present, which requests were wrong (wrong file/anchor/page) with the correction, and which need threading through a shared component. Never force a request that does not match the source \u2014 flag it instead.",Yi]}ze();M();import{listOpenForDocs as Ku,readProjectConfig as Ju}from"document360-engine";function wr(e){try{return Ku(e).length}catch{return 0}}function yr(){return["Process the two-way message inbox from the source repo's coding agent (the builder).","","1. Call d360_inbox to list the OPEN messages addressed to you.","2. For each message, act by its kind:",' \u2022 request ("document the X we shipped") \u2192 treat it as a session hand-off: run the document-handoff'," skill \u2014 read what changed, ground it in the cited sources + the architecture brief, then decide and"," apply the RIGHT doc action (new section, new article, update existing, or retire). No fixed shape."," \u2022 answer (a reply to a question you asked; `re` = your question id) \u2192 fold the fact into the affected"," article(s), and into `.d360-writer/architecture/` if it is durable product knowledge.","3. After acting on a message, mark it resolved: edit its file in `.d360-writer/messages/` and set the"," frontmatter `status: done` (add `done_at` with the current UTC time). Never delete the file.","4. Drafts only \u2014 do not publish unless explicitly asked. End with a one-line summary of what you did per"," message (and, for any request you could not fully ground, what you still need)."].join(`
227
227
  `)}async function zi(e,t){let n=t?.cwd??process.cwd();return Ju(n)?wr(n)===0?(console.log(f("Inbox empty \u2014 no open messages from the builder. (They arrive via the builder's /devhints hand-off.)")),{kind:"continue"}):{kind:"forward-to-agent",prompt:yr(),display:"/inbox"}:(console.log(x("No d360-writer config here. Run /init first.")),{kind:"continue"})}M();import{listOpenHints as Qu,listOpenForDocs as Zu,readProjectConfig as ed}from"document360-engine";function $t(e){let t=0,n=0,o=0;try{t=Qu(e).length}catch{}try{let r=Zu(e);n=r.filter(s=>s.kind==="request").length,o=r.filter(s=>s.kind==="answer").length}catch{}return{hints:t,requests:n,answers:o}}function vt(e){let t=[];return e.hints&&t.push(`${e.hints} hint${e.hints===1?"":"s"}`),e.requests&&t.push(`${e.requests} request${e.requests===1?"":"s"}`),e.answers&&t.push(`${e.answers} answer${e.answers===1?"":"s"}`),t.length?t.join(", "):null}function kr(e){return{publish:e.includes("--publish")}}function br({publish:e}){let t=["Catch the documentation up: process ALL the pending work the builder has queued, end to end.","","1. Trust the inventory first \u2014 call d360_sync_status. If anything is in conflict or remote-ahead, STOP and tell me what to resolve (never write over a copy you can't trust).","2. Gather the pending work: d360_pending_hints (the builder's per-change hints) AND d360_inbox (open requests + answers).","3. Decide the right doc actions across all of it: gap-analysis for the hints (verify each against its sources + commit), document-handoff for each request (no fixed shape \u2014 new section / new article / update / retire), and fold any answers into the affected article(s) + the architecture brief.","4. If that means writing more than one article, call d360_estimate_cost and show me the count + cost band first (the bulk gate), then proceed.","5. Write/update the articles as drafts, grounded in the cited sources.","6. Close the loop: stamp consumed hints, and mark handled messages status: done with resulting_articles.","7. Push the drafts to Document360."];return t.push(e?"8. Then publish the new/updated articles live (publish-to-d360 skill), respecting the production write-guard.":"Leave everything as DRAFTS for review \u2014 do not publish live (run /publish when you want them live)."),t.push("","End with a one-line-per-item summary of what you wrote/updated, and anything you couldn't ground (plus the d360_ask_builder question you left for it)."),t.join(`
228
- `)}async function Xi(e,t){let n=t?.cwd??process.cwd();if(!ed(n))return console.log(x("No d360-writer config here. Run /init first.")),{kind:"continue"};let o=Se(n,t?.profileName);if(!o.ok)return console.log(x(o.reason)),{kind:"continue"};let{publish:r}=kr(e),s=vt($t(n));return console.log(f(s?`Catching up on ${s} (+ checking Document360 for drift)\u2026`:"No local queue \u2014 checking for code changes and drift\u2026")),{kind:"forward-to-agent",prompt:br({publish:r}),display:`/catch-up${r?" --publish":""}`}}M();import{spawn as td}from"node:child_process";var Ke="document360-writer",On=`npm i -g ${Ke}@latest`;async function Ln(){return new Promise(e=>{let t="";try{let n=td(`npm i -g ${Ke}@latest`,{stdio:["ignore","pipe","pipe"],shell:!0});n.stdout?.on("data",o=>t+=String(o)),n.stderr?.on("data",o=>t+=String(o)),n.on("error",o=>e({ok:!1,output:String(o)})),n.on("close",o=>e({ok:o===0,output:t.trim()}))}catch(n){e({ok:!1,output:String(n)})}})}async function Ki(){return console.log(f(`Updating ${Ke}\u2026`)),(await Ln()).ok?console.log(j("\u2713 Updated. Exit (/exit) and relaunch d360-writer to apply.")):console.log(x(`Update failed \u2014 run manually: ${On}`)),{kind:"continue"}}M();ze();import{existsSync as xr,rmSync as nd}from"node:fs";import{basename as Ji,resolve as $r}from"node:path";import{execFileSync as od}from"node:child_process";import{input as rd}from"@inquirer/prompts";import{readProjectConfig as sd,devHintsGuidePath as id}from"document360-engine";var ad=[".d360-writer.json",".d360-writer","d360-category-map.json",".d360-capture.json",".d360-capture-cache"],ld=new Set([".d360-writer.json","d360-category-map.json",".d360-capture.json",".d360-capture-cache"]),Mn=e=>(e??"").replace(/\\/g,"/").replace(/\/+$/,""),Qi=e=>e===".d360-writer"||e.startsWith(".d360-writer/");function Zi(e){let t=Mn(e);return t===""||t.startsWith("..")||t.startsWith("/")||/^[a-zA-Z]:/.test(t)?!1:Qi(t)?!0:ld.has(t)}function vr(e){let t=null;try{t=sd(e)}catch{t=null}let n=new Set(ad);for(let o of[Mn(t?.docsDir),Mn(t?.captureDir),Mn(t?.outputDir)])o&&Qi(o)&&n.add(o);return[...n].filter(o=>Zi(o)&&xr($r(e,o)))}function Cr(e,t){let n=[],o=[];for(let r of t){if(!Zi(r)){o.push({path:r,error:"refused \u2014 outside .d360-writer/ (protected)"});continue}try{nd($r(e,r),{recursive:!0,force:!0}),n.push(r)}catch(s){o.push({path:r,error:s.message})}}return{removed:n,failed:o}}function ea(e,t){try{return od("git",["status","--porcelain","--",t],{cwd:e,encoding:"utf8",stdio:["ignore","pipe","ignore"]}).split(`
228
+ `)}async function Xi(e,t){let n=t?.cwd??process.cwd();if(!ed(n))return console.log(x("No d360-writer config here. Run /init first.")),{kind:"continue"};let o=Se(n,t?.profileName);if(!o.ok)return console.log(x(o.reason)),{kind:"continue"};let{publish:r}=kr(e),s=vt($t(n));return console.log(f(s?`Catching up on ${s} (+ checking Document360 for drift)\u2026`:"No local queue \u2014 checking for code changes and drift\u2026")),{kind:"forward-to-agent",prompt:br({publish:r}),display:`/catch-up${r?" --publish":""}`}}M();import{spawn as td}from"node:child_process";var Ke="document360-writer",On=`npm i -g ${Ke}@latest`;async function Ln(){return new Promise(e=>{try{let t=td(`npm i -g ${Ke}@latest`,{stdio:"ignore",shell:!0,detached:!0,windowsHide:!0});t.on("error",n=>e({ok:!1,output:String(n)})),t.on("close",n=>e({ok:n===0,output:""})),t.unref()}catch(t){e({ok:!1,output:String(t)})}})}async function Ki(){return console.log(f(`Updating ${Ke}\u2026`)),(await Ln()).ok?console.log(j("\u2713 Updated. Exit (/exit) and relaunch d360-writer to apply.")):console.log(x(`Update failed \u2014 run manually: ${On}`)),{kind:"continue"}}M();ze();import{existsSync as xr,rmSync as nd}from"node:fs";import{basename as Ji,resolve as $r}from"node:path";import{execFileSync as od}from"node:child_process";import{input as rd}from"@inquirer/prompts";import{readProjectConfig as sd,devHintsGuidePath as id}from"document360-engine";var ad=[".d360-writer.json",".d360-writer","d360-category-map.json",".d360-capture.json",".d360-capture-cache"],ld=new Set([".d360-writer.json","d360-category-map.json",".d360-capture.json",".d360-capture-cache"]),Mn=e=>(e??"").replace(/\\/g,"/").replace(/\/+$/,""),Qi=e=>e===".d360-writer"||e.startsWith(".d360-writer/");function Zi(e){let t=Mn(e);return t===""||t.startsWith("..")||t.startsWith("/")||/^[a-zA-Z]:/.test(t)?!1:Qi(t)?!0:ld.has(t)}function vr(e){let t=null;try{t=sd(e)}catch{t=null}let n=new Set(ad);for(let o of[Mn(t?.docsDir),Mn(t?.captureDir),Mn(t?.outputDir)])o&&Qi(o)&&n.add(o);return[...n].filter(o=>Zi(o)&&xr($r(e,o)))}function Cr(e,t){let n=[],o=[];for(let r of t){if(!Zi(r)){o.push({path:r,error:"refused \u2014 outside .d360-writer/ (protected)"});continue}try{nd($r(e,r),{recursive:!0,force:!0}),n.push(r)}catch(s){o.push({path:r,error:s.message})}}return{removed:n,failed:o}}function ea(e,t){try{return od("git",["status","--porcelain","--",t],{cwd:e,encoding:"utf8",stdio:["ignore","pipe","ignore"]}).split(`
229
229
  `).filter(o=>o.trim()&&/\.md\s*$/.test(o)).length}catch{return 0}}function cd(e){return ea(e,".d360-writer/hints")}function ud(e){return ea(e,".d360-writer/messages")}function Pr(e,t){if(t.length===0)return["Nothing to reset \u2014 no d360-writer files found in this repo."];let n=cd(e),o=ud(e),r=xr($r(e,"user-docs"));return["\u26A0 This permanently DELETES everything d360-writer created here:",...t.map(s=>` \u2022 ${s}`),"",...n>0?[`\u26A0 ${n} doc hint(s) under .d360-writer/hints/ are not committed \u2014 commit them first or they're gone.`]:[],...o>0?[`\u26A0 ${o} message(s) under .d360-writer/messages/ are not committed \u2014 commit them first or they're gone.`]:[],...r?["Protected: root user-docs/ is NOT touched (only .d360-writer/ is removed)."]:[],"Undo = git: committed files are restorable; untracked ones (most screenshots) are gone.",`To confirm, type the repo name: ${Ji(e)}`]}async function ta(e,t){let n=vr(t.cwd);for(let h of Pr(t.cwd,n))console.log(n.length===0?f(h):h);if(n.length===0)return{kind:"continue"};let o=Ji(t.cwd);if((await t.withPausedInput(()=>rd({message:`Type "${o}" to delete (anything else cancels):`}).catch(()=>""))).trim()!==o)return console.log(L("Reset cancelled \u2014 nothing deleted.")),{kind:"continue"};let s=xr(id(t.cwd)),{removed:c,failed:p}=Cr(t.cwd,n);console.log(j(`\u2713 Reset complete \u2014 removed ${c.length} item${c.length===1?"":"s"}. The repo is back to its original state.`));for(let h of p)console.log(x(` \u2717 ${h.path}: ${h.error}`));if(s){for(let h of Pn())console.log(h);console.log(f("(Select and copy the prompt above to paste into your coding agent.)"))}return console.log(f("Set up d360-writer again with /init (then /login, /workspace).")),{kind:"continue"}}var na={help:vo,"?":vo,clear:Ps,exit:Co,quit:Co,init:qs,mcp:Sn,publish:oi,audit:jn,scope:si,sync:pi,convert:fi,write:wi,draft:$i,document:vi,resume:Ti,rename:Si,profile:Di,model:Ei,doctor:qt,workspace:Mi,project:Wi,"allow-prod":Fi,login:Hi,logout:qi,screenshot:Un,"capture-setup":Bt,devhints:Es,inbox:zi,"catch-up":Xi,update:Ki,reset:ta};function oa(e){let t=e.trim();if(!t.startsWith("/"))return null;let n=t.slice(1).split(/\s+/),o=(n[0]??"").toLowerCase();return o?{name:o,args:n.slice(1)}:null}ze();M();var dd={Bash:"command",PowerShell:"command",Read:"file_path",Write:"file_path",Edit:"file_path",NotebookEdit:"notebook_path",Glob:"pattern",Grep:"pattern",WebFetch:"url",WebSearch:"query",Agent:"description",Task:"description",Skill:"skill"},sa=160,Sr=200,ia=40;function pe(e,t){let n=e.replace(/\s+/g," ").trim();return n.length<=t?n:n.slice(0,t-1)+"\u2026"}function ra(e){let t=Object.entries(e).filter(([,o])=>o!=null&&o!=="");if(t.length===0)return null;let n=t.slice(0,4).map(([o,r])=>`${o}: ${pe(typeof r=="string"?r:JSON.stringify(r),ia)}`);return t.length>4&&n.push("\u2026"),pe(n.join(", "),sa)}var Oe=e=>typeof e=="string"&&e?e:null,Tr=e=>typeof e=="string"&&e.length>=8?e.slice(0,8):null;function pd(e){let t=Oe(e)?.replace(/\\/g,"/");if(!t)return null;let n=t.split("/").filter(Boolean);if(n.length<2)return null;let o=n[n.length-2],r=o.replace(/^\d+[-_.]/,"").split(/[-_]/).filter(Boolean);return r.length===0?o:r.map(s=>s.charAt(0).toUpperCase()+s.slice(1)).join(" ")}function fd(e,t){let n=(o,r)=>({title:`Document360: ${o}`,sep:" ",arg:r});switch(e){case"d360_create_article":{let o=Oe(t.title);if(!o)return null;let r=pd(t.local_path);return n("Create article",`"${pe(o,60)}"${r?` in ${r}`:""}`)}case"d360_update_article":{let o=Oe(t.title),r=Tr(t.article_id);return n("Update article",o?`"${pe(o,60)}"`:r?`id ${r}\u2026`:null)}case"d360_fork_article":return n("Fork article (new draft)",Tr(t.article_id)?`id ${Tr(t.article_id)}\u2026`:null);case"d360_publish_article":{let o=t.version_number;return n("Publish article LIVE",typeof o=="number"?`v${o}`:null)}case"d360_unpublish_article":return n("Unpublish article",null);case"d360_create_category":return n("Create category",Oe(t.name)?`"${pe(Oe(t.name),60)}"`:null);case"d360_upload_drive_file":{let o=Oe(t.file_path);return n("Upload image",o?pe(o.replace(/\\/g,"/").split("/").pop()??o,60):null)}case"d360_sync_status":return n("Check sync status",null);default:return null}}function Wn(e,t){if(e==="ToolSearch")return null;if(e.startsWith("mcp__")){let[,r="",...s]=e.split("__"),c=s.join("__");if(r==="document360"){let b=fd(c,t);if(b)return b}let p=c.replace(/^d360_/,"").replace(/_/g," ");return{title:`${r==="document360"?"Document360":r.charAt(0).toUpperCase()+r.slice(1)}: ${p}`,sep:" ",arg:ra(t)}}let n=dd[e],o=n?t[n]:void 0;return typeof o=="string"&&o?{title:e,sep:"",arg:pe(o,sa)}:{title:e,sep:"",arg:ra(t)}}function md(e){if(e===null||typeof e!="object")return typeof e=="string"?e:null;let t=e;for(let n of["name","title","slug","id"])if(typeof t[n]=="string"&&t[n])return t[n];return null}function hd(e){if(!/^[[{]/.test(e))return null;let t;try{t=JSON.parse(e)}catch{return null}if(Array.isArray(t)){let n=t.map(md).filter(r=>r!==null),o=`${t.length} item${t.length===1?"":"s"}`;return n.length===0?[o]:[o,...n.map(r=>pe(r,Sr))]}if(t!==null&&typeof t=="object"){let n=Object.entries(t).filter(([,o])=>o!==null&&(typeof o=="string"||typeof o=="number"||typeof o=="boolean")).slice(0,6).map(([o,r])=>`${o}: ${pe(String(r),ia)}`);return n.length>0?[pe(n.join(" \xB7 "),Sr)]:null}return null}function gd(e){let t=Oe(e)?.replace(/\\/g,"/");if(!t)return null;let n=t.split("/").filter(Boolean);return n.length>1?n.slice(1).join("/"):t}function wd(e,t,n){switch(e){case"d360_create_article":{let o=gd(t?.local_path);return[`\u2713 draft created${o?` \xB7 ${o}`:""}`]}case"d360_update_article":return["\u2713 draft updated"];case"d360_fork_article":return["\u2713 forked to a new draft"];case"d360_publish_article":return["\u2713 PUBLISHED LIVE \u2014 visible to readers"];case"d360_unpublish_article":return["\u2713 reverted to draft (removed from readers)"];case"d360_create_category":{let o=Oe(t?.name);return[`\u2713 category created${o?` \xB7 "${o}"`:""}`]}case"d360_upload_drive_file":{let o=n.match(/https?:\/\/\S+/);return[`\u2713 uploaded${o?` \xB7 ${pe(o[0],120)}`:""}`]}default:return null}}function Fn(e,t=4,n,o){let r=e.replace(/\r\n/g,`
230
230
  `).trimEnd();if(!r)return{lines:["(no output)"],hidden:0};if(n?.startsWith("mcp__document360__")){let c=wd(n.slice(18),o,r);if(c)return{lines:c,hidden:0}}let s=hd(r)??r.split(`
231
231
  `);return{lines:s.slice(0,t).map(c=>pe(c,Sr)),hidden:Math.max(0,s.length-t)}}function Hn(e,t,n,o="en"){return`${e.replace(/\/$/,"")}/${t}/document/v1/${o}/${n}`}function qn(e,t){if(typeof e.article_id=="string"&&e.article_id)return e.article_id;try{let n=JSON.parse(t),r=(Array.isArray(n)?n[0]:n)?.id;return typeof r=="string"&&r?r:null}catch{return null}}function Bn(e){try{let t=JSON.parse(e),o=(Array.isArray(t)?t[0]:t)?.url;return typeof o=="string"&&/^https?:\/\//.test(o)?o:null}catch{return null}}var Dd=/^mcp__document360__d360_(create_article|update_article|fork_article|publish_article)$/;function jd(e,t,n,o){if(Dd.test(e))try{let r=ua(o),s=typeof t.project_id=="string"&&t.project_id||r.project.projectId,c=qn(t,n),p=Bn(n);e.endsWith("publish_article")&&p&&console.log(E(` \u2B95 Live: ${p}`)),c&&s&&console.log(E(` \u2B95 Preview: ${Hn(r.connection.portalUrl,s,c,r.project.languageCode??"en")}`))}catch{}}async function da(e=process.cwd(),t="auto",n){let o=kd(t);o.kind==="none"&&(console.error(""),console.error(x("ANTHROPIC_API_KEY is not set (required for --auth api).")),console.error(""),console.error(` ${E("export ANTHROPIC_API_KEY=sk-ant-...")} (macOS / Linux)`),console.error(` ${E('$env:ANTHROPIC_API_KEY="sk-ant-..."')} (PowerShell)`),console.error(""),console.error(`Get a key at ${E("https://console.anthropic.com/settings/keys")}`),console.error(`Or use your Claude subscription instead: ${E("d360-writer --auth subscription")}`),console.error(""),process.exit(2)),Ad(e,n),o.kind==="subscription"&&(o.stored?console.log(f(" Using your Claude subscription (no API key set).")):console.log(f(" No API key or stored Claude Code login found \u2014 trying your Claude session anyway.")),console.log(""));let r=!1,s=Rr({cwd:e,profileName:n,allowProdWrites:r}),c={uuid:null,firstPrompt:null,titleFired:!1},p=yd({input:process.stdin,output:process.stdout}),h=[],b=null,k=!1;p.on("line",v=>{if(b){let C=b;b=null,C(v)}else h.push(v)}),p.on("close",()=>{if(k=!0,b){let v=b;b=null,v(null)}});function D(){return h.length>0?Promise.resolve(h.shift()):k?Promise.resolve(null):(process.stdout.write(E("> ")),new Promise(v=>{b=v}))}let w={cwd:e,profileName:n,allowProdWrites:()=>r,restartAgent:()=>{s.close(),s=Rr({cwd:e,profileName:n,allowProdWrites:r}),c={uuid:null,firstPrompt:null,titleFired:!1}},currentUuid:()=>c.uuid,setModel:async v=>s.setModel(v),withPausedInput:async v=>{p.pause();try{return await v()}finally{p.resume()}}};try{for(;;){let v=await D();if(v===null)break;let C=v.trim();if(C){if(C.startsWith("/")){let N=oa(C);if(!N)continue;let U=na[N.name];if(!U){console.log(x(`Unknown command: /${N.name}`)),console.log(f("Type /help for the list."));continue}let H=await U(N.args,w);if(H.kind==="exit")break;if(H.kind==="clear"){w.restartAgent();continue}if(H.kind==="allow-prod"){r=!0,w.restartAgent(),console.log(j("\u2713 Production writes authorized for this session.")),console.log("");continue}if(H.kind==="resume"){s.close(),s=Rr({cwd:e,resume:H.uuid,profileName:n,allowProdWrites:r});let ee=bd(H.uuid);c={uuid:H.uuid,firstPrompt:ee?.firstPrompt??null,titleFired:!0},ca(H.uuid),console.log(j(`\u2713 Resumed "${H.name}"`)),console.log("");continue}H.kind==="forward-to-agent"&&(c.firstPrompt||(c.firstPrompt=H.display??H.prompt),await la(s,H.prompt,o,c,e));continue}c.firstPrompt||(c.firstPrompt=C),await la(s,C,o,c,e)}}}finally{s.close(),p.close()}}function Ad(e,t){console.log(""),console.log(Et("document360-writer")),console.log(f(` cwd: ${e}`));let n=Pd(e);if(console.log(f(` model: ${n.model??"auto (engine right-sizes per task)"}${n.model?` (${n.source})`:""}`)),console.log(Ed(e,t)),!Td(e))console.log(L(" First run: /init (scaffolds config + prints the builder hand-off prompt) \u2192 /login, then ask me to write the docs."));else{try{vn(e);for(let o of Cn(e))console.log(L(` ${o}`))}catch{}try{let o=vt($t(e));o&&console.log(L(` \u{1F4CB} ${o} from the repo's coding agent \u2014 run /catch-up.`))}catch{}}console.log(f(" Type a prompt, or /help for slash commands. /exit to quit.")),console.log("")}function Ed(e,t){try{let n=ua(e,t),o=n.production?L(" \u26A0 PRODUCTION"):"",r=Rd(n.name);if(!r)return f(` Document360: profile "${n.name}"${o} \u2014 not logged in (d360-writer login)`);let s={...aa(r.idToken)??{},...aa(r.accessToken)??{}},c=s.email??s.preferred_username??"signed in";return Sd(r)&&!r.refreshToken?L(` Document360: profile "${n.name}"${o} \u2014 session expired (d360-writer login)`):f(` Document360: ${c} \xB7 profile "${n.name}"${o}`)}catch(n){return f(` Document360: ${n.message.split(".")[0]}`)}}function Id(){console.error(""),console.error(`Sign in with your Claude subscription: run ${E("claude")} once, then retry.`),console.error(` (No Claude Code? ${E("npm install -g @anthropic-ai/claude-code")})`),console.error(`Or set an API key: ${E("https://console.anthropic.com/settings/keys")}`)}function Nd(e,t,n){e.uuid=t;let o=new Date().toISOString();vd({uuid:t,name:$d(e.firstPrompt??"session"),renamed:!1,titled:!1,cwd:n,firstPrompt:e.firstPrompt??"",createdAt:o,updatedAt:o})}function _d(e,t){e.titleFired=!0;let n=e.uuid,o=e.firstPrompt;!n||!o||Cd(o,t).then(r=>{r&&xd(n,r)}).catch(()=>{})}async function la(e,t,n,o,r){let s=new Map;for await(let c of e.send(t))Ud(c,o,r,n,s)}function Ud(e,t,n,o,r){switch(e.type){case"session":t.uuid||Nd(t,e.sessionId,n);break;case"text":process.stdout.write(e.delta);break;case"tool":{let s=Wn(e.name,e.input);s&&(process.stdout.write(`
@@ -241,7 +241,7 @@ Articles:
241
241
  `),truncated:c>0||h>0}}}return{text:e,truncated:!1}}function ja(e){let t=!1,n=0,o=0;for(let r of e.split(`
242
242
  `)){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 Lr,jsx as R,jsxs as F}from"react/jsx-runtime";var Dp={project:".d360-writer.json",user:"/model",env:"ANTHROPIC_MODEL","claude-settings":"Claude Code settings","claude-default":""},Ua=` 1. /init \u2014 pick your Document360 environment & scaffold config
243
243
  2. /login \u2014 sign in & pick your Document360 project (workspace auto-selected)
244
- 3. I'll hand you a one-paste prompt for your builder \u2014 then just ask me to write the docs`;function jp(e,t,n,o){let r=Fa(n),s=r.state==="ready"?r.kind==="api"?"API key":"subscription":r.state==="maybe"?"subscription":"not signed in \u2014 run /doctor (needs Claude Code, not Claude Desktop)",c=Mr(e),p=Z(e),h=(p?.docsDir??Ha).replace(/\/+$/,""),b=p?.mode==="engineer"?"engineer \xB7 full source access (dogfooding)":`writer \xB7 edits limited to ${h}/ + config`,k={version:t,claude:s,model:c.model??"Claude Code default model",modelSource:Dp[c.source],who:null,sessionHint:null,profile:"\u2014",apiUrl:"\u2014",project:"\u2014",cwd:e,prod:!1,loggedOut:!0,configured:p!==null,mode:b};if(p===null)return k;try{let D=fe(e,o);k.profile=D.name,k.apiUrl=D.connection.apiUrl,k.prod=D.production,k.project=D.project.projectName??D.project.projectId??"(chosen at login)";let w=Je(D.name);if(w){let v={...Ct(w.idToken)??{},...Ct(w.accessToken)??{}},C=v.email??v.preferred_username??"signed in";Le(w)?w.refreshToken&&(k.who=C,k.loggedOut=!1,k.sessionHint="session expired \u2014 refreshing\u2026"):(k.who=C,k.loggedOut=!1,k.sessionHint=`session valid until ${new Date(w.expiresAt).toLocaleString(void 0,{hour:"2-digit",minute:"2-digit",day:"2-digit",month:"short"})}`)}}catch{}return k}function Ap(e,t){try{let n=fe(e,t),o=Je(n.name);if(!o)return{text:`profile "${n.name}" \u2014 not logged in (/login)`,prod:n.production};let r={...Ct(o.idToken)??{},...Ct(o.accessToken)??{}},s=r.email??r.preferred_username??"signed in";return Le(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 Oa=["Drafting","Composing","Outlining","Researching","Documenting","Structuring","Polishing","Synthesizing","Curating","Distilling","Weaving","Wrangling","Pondering"],Zn=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],Ep="Ask me to write or update an article\u2026";function Qn({ch:e,dim:t}){let[n,o]=W(!0);return $e(()=>{let r=setInterval(()=>o(s=>!s),530);return()=>clearInterval(r)},[]),R(S,{inverse:n,color:t&&!n?"gray":void 0,children:e})}var Ip=/^mcp__document360__d360_(create_article|update_article|fork_article|publish_article)$/;function La(e){let t=e.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n/);return t?e.slice(t[0].length):e}var Np=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function Ma(e){try{return Wa(e,{withFileTypes:!0}).filter(t=>t.isDirectory()&&!t.name.startsWith(".")).length>6}catch{return!1}}function _p(e,t){let n=t??[];return n.length===0?["src","api","services","packages","modules"].some(o=>Ma(Xt(e,o))):n.some(o=>!o.includes("/")&&!o.endsWith(".md")&&Ma(Xt(e,o)))}function Up({startTime:e,chars:t}){let[n,o]=W(0);$e(()=>{let h=setInterval(()=>o(b=>b+1),120);return()=>clearInterval(h)},[]);let r=Zn[n%Zn.length],s=Oa[Math.floor(n/16)%Oa.length],c=Math.floor((Date.now()-e)/1e3),p=Math.round(t/4);return F(Q,{children:[R(S,{color:q,children:` ${r} ${s}\u2026 `}),R(S,{color:"gray",children:`(${Mt(c)} \xB7 ~${p} tokens \xB7 esc to interrupt)`})]})}var Op=12e4;function Lp({p:e}){let[t,n]=W(0);$e(()=>{let U=setInterval(()=>n(H=>H+1),150);return()=>clearInterval(U)},[]);let o=Zn[t%Zn.length],r=Math.floor((Date.now()-e.startedAt)/1e3),s=Math.floor((Date.now()-e.lastAt)/1e3),c=Math.round(e.chars/4),p=Date.now()-e.lastAt>=Op,h=e.verb??"Converting",b=e.unit??"article",k=e.rows.reduce((U,H)=>U+H.items,0),D=h.toLowerCase(),w=e.rows,v=0;if(w.length>Yn){let U=w.findIndex(ee=>ee.status==="active"||ee.status==="pending"),H=Math.max(0,Math.min(U-2,w.length-Yn));v=w.slice(0,H).filter(ee=>ee.status==="done").length,w=w.slice(H,H+Yn)}let C=Math.min(40,Math.max(...w.map(U=>Ar(U.label).length),0)),N=U=>U==="failed"?"red":U==="active"?q:void 0;return F(Q,{flexDirection:"column",children:[F(Q,{children:[R(S,{color:q,children:` ${o} ${h} ${k} ${b}${k===1?"":"s"} `}),R(S,{color:"gray",children:`\xB7 ${e.done}/${e.total} \xB7 ${e.tools} tool call${e.tools===1?"":"s"} \xB7 ~${c} tokens \xB7 ${Mt(r)} \xB7 esc to stop`})]}),v>0&&R(S,{dimColor:!0,children:` \u2714 +${v} done`}),w.map(U=>F(S,{color:N(U.status),dimColor:U.status==="done"||U.status==="pending",children:[` ${ba[U.status]} ${Ar(U.label).padEnd(C)}`,U.status!=="pending"?` (${U.items} ${b}${U.items===1?"":"s"})`:"",U.status==="active"?` \u2190 ${D}\u2026`:""]},U.label)),p&&R(S,{color:"yellow",children:` \u26A0 no activity for ${Mt(s)} \u2014 press esc to stop if it's wedged`})]})}var Mp=10800*1e3;function qa({cwd:e,auth:t,profileName:n,version:o}){let{exit:r}=zd(),[s,c]=W(n),[p,h]=W(null),[b,k]=W({text:"",pos:0}),D=b.text,w=G(l=>{k(i=>{let a=typeof l=="function"?l(i.text):l;return{text:a,pos:a.length}})},[]),v=G(l=>{k(i=>({text:i.text.slice(0,i.pos)+l+i.text.slice(i.pos),pos:i.pos+l.length}))},[]),C=G(()=>{k(l=>{if(l.pos===0)return l;let i=l.text.slice(0,l.pos),a=Ta(i)??i.slice(0,-1);return{text:a+l.text.slice(l.pos),pos:a.length}})},[]),[N,U]=W(!1),[H,ee]=W(!1),[ve,oe]=W(0),[ke,se]=W(null),[Pt,B]=W([]),Qe=X(0),[Ya,Kt]=W(!1),[za,Ze]=W(0),Me=X(0),[Tt,et]=W(0),[tt,Jt]=W(null),Hr=X(new Map),Xa=X(0),nt=X([]),me=X(null);me.current===null&&(me.current=Aa({cwd:e,profileName:s,allowProdWrites:!1}));let Ce=X({uuid:null,firstPrompt:null,titleFired:!1}),Qt=X(new Map),ot=X(!1),eo=X([]),je=X([]),to=X(null),[qr,rt]=W(null),[We,Fe]=W(null),[Pe,st]=W(null),[Ae,it]=W(null),[ie,at]=W(null),[he,lt]=W(null),[He,ct]=W(null),[Br,Zt]=W(null),[ge,qe]=W(null),[ce,ut]=W(null),[Gr,no]=W([]),Be=X([]),St=X(!1),we=X(null),[Vr,K]=W(null),en=X(!1),tn=X(null),Rt=X(null),[ye,Ge]=W(null),[Yr,dt]=W(0),[zr,pt]=W(0),[Ka,Xr]=W(0),oo=Ir(()=>{try{return Mr(e).model??"auto"}catch{return null}},[e,Ka]),{stdout:be}=Kd(),[,Ja]=W(0),Kr=X(`${be.columns??80}x${be.rows??24}`),Ee=G(()=>Math.max(20,(be.columns??80)-1),[be]),u=G(l=>{nt.current.push(l),console.log(_o(l,Ee()))},[Ee]),[ro,so]=W(null),ft=G(l=>{so(l),u({kind:"note",tone:"info",text:"\u2328 Press c to copy the prompt above to your clipboard (then paste it into your coding agent)."})},[u]),io=X(!1),ao=G(async()=>{try{if(io.current||!Tp(Pp())||Z(e)?.mode==="engineer")return;let l=await Cp({pkg:Ke,currentVersion:o,force:!0});if(!l.updateAvailable||!l.latest)return;io.current=!0,u({kind:"note",tone:"info",text:`\u2B06 Updating ${Ke} ${l.current} \u2192 ${l.latest}\u2026`}),(await Ln()).ok?(u({kind:"note",tone:"ok",text:`\u2713 Updated to v${l.latest} \u2014 run /exit, then relaunch to apply.`}),B(a=>a.includes("/exit")?a:[...a,"/exit"])):u({kind:"note",tone:"warn",text:`Update available (v${l.latest}) but auto-update failed \u2014 run: ${On}`})}catch{}},[e,o,u]),lo=G(l=>{let i=Se(e,s);return i.ok?!0:(u({kind:"note",tone:"warn",text:i.reason}),Rt.current=l,B(["/login"]),!1)},[e,s,u]),[co,uo]=W(null),Dt=X(!0),nn=X(0);$e(()=>{let l=setInterval(()=>{if(N||Dt.current){nn.current=0;return}try{if(!Yt(_a(e)))return;if(Ne(e).status==="current"){Dt.current=!0,nn.current=0,u({kind:"note",tone:"ok",text:"\u2713 Your builder finished the hand-off \u2014 the architecture brief is in. I can read this repo now and write accurate docs."}),so(null),se("write the docs for this repo");return}nn.current+=1;let i=nn.current;(i===1||i%7===0)&&u({kind:"note",tone:"info",text:Ns(e)?"\u{1F440} Your builder is working on the architecture brief \u2014 I'll continue automatically when it finishes.":"\u23F3 Waiting for your builder to run the prompt \u2014 paste it into your coding agent; I'll pick up automatically when it's done."})}catch{}},4e3);return()=>clearInterval(l)},[e,N,u]),$e(()=>{let l=`d360-writer \xB7 ${qr??zt(e)}`;if(!N){Gn(0),Gt(`${jr} ${l}`);return}Gn(3);let i=0;Gt(`${Vn[0]} ${l}`);let a=setInterval(()=>{i=(i+1)%Vn.length,Gt(`${Vn[i]} ${l}`)},400);return()=>clearInterval(a)},[N,e,qr]),$e(()=>()=>Gn(0),[]),$e(()=>{u({kind:"banner",info:jp(e,o,t,s)});let l=Fa(t).state==="missing";if(l&&u({kind:"note",tone:"warn",text:`\u26A0 Set up Claude first \u2014 d360-writer's agent runs on Claude Code, and it's not signed in on this machine yet.
244
+ 3. I'll hand you a one-paste prompt for your builder \u2014 then just ask me to write the docs`;function jp(e,t,n,o){let r=Fa(n),s=r.state==="ready"?r.kind==="api"?"API key":"subscription":r.state==="maybe"?"subscription":"not signed in \u2014 run /doctor (needs Claude Code, not Claude Desktop)",c=Mr(e),p=Z(e),h=(p?.docsDir??Ha).replace(/\/+$/,""),b=p?.mode==="engineer"?"engineer \xB7 full source access (dogfooding)":`writer \xB7 edits limited to ${h}/ + config`,k={version:t,claude:s,model:c.model??"Claude Code default model",modelSource:Dp[c.source],who:null,sessionHint:null,profile:"\u2014",apiUrl:"\u2014",project:"\u2014",cwd:e,prod:!1,loggedOut:!0,configured:p!==null,mode:b};if(p===null)return k;try{let D=fe(e,o);k.profile=D.name,k.apiUrl=D.connection.apiUrl,k.prod=D.production,k.project=D.project.projectName??D.project.projectId??"(chosen at login)";let w=Je(D.name);if(w){let v={...Ct(w.idToken)??{},...Ct(w.accessToken)??{}},C=v.email??v.preferred_username??"signed in";Le(w)?w.refreshToken&&(k.who=C,k.loggedOut=!1,k.sessionHint="session expired \u2014 refreshing\u2026"):(k.who=C,k.loggedOut=!1,k.sessionHint=`session valid until ${new Date(w.expiresAt).toLocaleString(void 0,{hour:"2-digit",minute:"2-digit",day:"2-digit",month:"short"})}`)}}catch{}return k}function Ap(e,t){try{let n=fe(e,t),o=Je(n.name);if(!o)return{text:`profile "${n.name}" \u2014 not logged in (/login)`,prod:n.production};let r={...Ct(o.idToken)??{},...Ct(o.accessToken)??{}},s=r.email??r.preferred_username??"signed in";return Le(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 Oa=["Drafting","Composing","Outlining","Researching","Documenting","Structuring","Polishing","Synthesizing","Curating","Distilling","Weaving","Wrangling","Pondering"],Zn=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],Ep="Ask me to write or update an article\u2026";function Qn({ch:e,dim:t}){let[n,o]=W(!0);return $e(()=>{let r=setInterval(()=>o(s=>!s),530);return()=>clearInterval(r)},[]),R(S,{inverse:n,color:t&&!n?"gray":void 0,children:e})}var Ip=/^mcp__document360__d360_(create_article|update_article|fork_article|publish_article)$/;function La(e){let t=e.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n/);return t?e.slice(t[0].length):e}var Np=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function Ma(e){try{return Wa(e,{withFileTypes:!0}).filter(t=>t.isDirectory()&&!t.name.startsWith(".")).length>6}catch{return!1}}function _p(e,t){let n=t??[];return n.length===0?["src","api","services","packages","modules"].some(o=>Ma(Xt(e,o))):n.some(o=>!o.includes("/")&&!o.endsWith(".md")&&Ma(Xt(e,o)))}function Up({startTime:e,chars:t}){let[n,o]=W(0);$e(()=>{let h=setInterval(()=>o(b=>b+1),120);return()=>clearInterval(h)},[]);let r=Zn[n%Zn.length],s=Oa[Math.floor(n/16)%Oa.length],c=Math.floor((Date.now()-e)/1e3),p=Math.round(t/4);return F(Q,{children:[R(S,{color:q,children:` ${r} ${s}\u2026 `}),R(S,{color:"gray",children:`(${Mt(c)} \xB7 ~${p} tokens \xB7 esc to interrupt)`})]})}var Op=12e4;function Lp({p:e}){let[t,n]=W(0);$e(()=>{let U=setInterval(()=>n(H=>H+1),150);return()=>clearInterval(U)},[]);let o=Zn[t%Zn.length],r=Math.floor((Date.now()-e.startedAt)/1e3),s=Math.floor((Date.now()-e.lastAt)/1e3),c=Math.round(e.chars/4),p=Date.now()-e.lastAt>=Op,h=e.verb??"Converting",b=e.unit??"article",k=e.rows.reduce((U,H)=>U+H.items,0),D=h.toLowerCase(),w=e.rows,v=0;if(w.length>Yn){let U=w.findIndex(ee=>ee.status==="active"||ee.status==="pending"),H=Math.max(0,Math.min(U-2,w.length-Yn));v=w.slice(0,H).filter(ee=>ee.status==="done").length,w=w.slice(H,H+Yn)}let C=Math.min(40,Math.max(...w.map(U=>Ar(U.label).length),0)),N=U=>U==="failed"?"red":U==="active"?q:void 0;return F(Q,{flexDirection:"column",children:[F(Q,{children:[R(S,{color:q,children:` ${o} ${h} ${k} ${b}${k===1?"":"s"} `}),R(S,{color:"gray",children:`\xB7 ${e.done}/${e.total} \xB7 ${e.tools} tool call${e.tools===1?"":"s"} \xB7 ~${c} tokens \xB7 ${Mt(r)} \xB7 esc to stop`})]}),v>0&&R(S,{dimColor:!0,children:` \u2714 +${v} done`}),w.map(U=>F(S,{color:N(U.status),dimColor:U.status==="done"||U.status==="pending",children:[` ${ba[U.status]} ${Ar(U.label).padEnd(C)}`,U.status!=="pending"?` (${U.items} ${b}${U.items===1?"":"s"})`:"",U.status==="active"?` \u2190 ${D}\u2026`:""]},U.label)),p&&R(S,{color:"yellow",children:` \u26A0 no activity for ${Mt(s)} \u2014 press esc to stop if it's wedged`})]})}var Mp=10800*1e3;function qa({cwd:e,auth:t,profileName:n,version:o}){let{exit:r}=zd(),[s,c]=W(n),[p,h]=W(null),[b,k]=W({text:"",pos:0}),D=b.text,w=G(l=>{k(i=>{let a=typeof l=="function"?l(i.text):l;return{text:a,pos:a.length}})},[]),v=G(l=>{k(i=>({text:i.text.slice(0,i.pos)+l+i.text.slice(i.pos),pos:i.pos+l.length}))},[]),C=G(()=>{k(l=>{if(l.pos===0)return l;let i=l.text.slice(0,l.pos),a=Ta(i)??i.slice(0,-1);return{text:a+l.text.slice(l.pos),pos:a.length}})},[]),[N,U]=W(!1),[H,ee]=W(!1),[ve,oe]=W(0),[ke,se]=W(null),[Pt,B]=W([]),Qe=X(0),[Ya,Kt]=W(!1),[za,Ze]=W(0),Me=X(0),[Tt,et]=W(0),[tt,Jt]=W(null),Hr=X(new Map),Xa=X(0),nt=X([]),me=X(null);me.current===null&&(me.current=Aa({cwd:e,profileName:s,allowProdWrites:!1}));let Ce=X({uuid:null,firstPrompt:null,titleFired:!1}),Qt=X(new Map),ot=X(!1),eo=X([]),je=X([]),to=X(null),[qr,rt]=W(null),[We,Fe]=W(null),[Pe,st]=W(null),[Ae,it]=W(null),[ie,at]=W(null),[he,lt]=W(null),[He,ct]=W(null),[Br,Zt]=W(null),[ge,qe]=W(null),[ce,ut]=W(null),[Gr,no]=W([]),Be=X([]),St=X(!1),we=X(null),[Vr,K]=W(null),en=X(!1),tn=X(null),Rt=X(null),[ye,Ge]=W(null),[Yr,dt]=W(0),[zr,pt]=W(0),[Ka,Xr]=W(0),oo=Ir(()=>{try{return Mr(e).model??"auto"}catch{return null}},[e,Ka]),{stdout:be}=Kd(),[,Ja]=W(0),Kr=X(`${be.columns??80}x${be.rows??24}`),Ee=G(()=>Math.max(20,(be.columns??80)-1),[be]),u=G(l=>{nt.current.push(l),console.log(_o(l,Ee()))},[Ee]),[ro,so]=W(null),ft=G(l=>{so(l),u({kind:"note",tone:"info",text:"\u2328 Press c to copy the prompt above to your clipboard (then paste it into your coding agent)."})},[u]),io=X(!1),ao=G(async()=>{try{if(io.current||!Tp(Pp())||Z(e)?.mode==="engineer")return;let l=await Cp({pkg:Ke,currentVersion:o,force:!0});if(!l.updateAvailable||!l.latest)return;io.current=!0,u({kind:"note",tone:"info",text:`\u2B06 Updating ${Ke} ${l.current} \u2192 ${l.latest}\u2026 (wait for the \u2713 before you /exit)`}),(await Ln()).ok?(u({kind:"note",tone:"ok",text:`\u2713 Updated to v${l.latest} \u2014 run /exit, then relaunch to apply.`}),B(a=>a.includes("/exit")?a:[...a,"/exit"])):u({kind:"note",tone:"warn",text:`Update available (v${l.latest}) but auto-update failed \u2014 run: ${On}`})}catch{}},[e,o,u]),lo=G(l=>{let i=Se(e,s);return i.ok?!0:(u({kind:"note",tone:"warn",text:i.reason}),Rt.current=l,B(["/login"]),!1)},[e,s,u]),[co,uo]=W(null),Dt=X(!0),nn=X(0);$e(()=>{let l=setInterval(()=>{if(N||Dt.current){nn.current=0;return}try{if(!Yt(_a(e)))return;if(Ne(e).status==="current"){Dt.current=!0,nn.current=0,u({kind:"note",tone:"ok",text:"\u2713 Your builder finished the hand-off \u2014 the architecture brief is in. I can read this repo now and write accurate docs."}),so(null),se("write the docs for this repo");return}nn.current+=1;let i=nn.current;(i===1||i%7===0)&&u({kind:"note",tone:"info",text:Ns(e)?"\u{1F440} Your builder is working on the architecture brief \u2014 I'll continue automatically when it finishes.":"\u23F3 Waiting for your builder to run the prompt \u2014 paste it into your coding agent; I'll pick up automatically when it's done."})}catch{}},4e3);return()=>clearInterval(l)},[e,N,u]),$e(()=>{let l=`d360-writer \xB7 ${qr??zt(e)}`;if(!N){Gn(0),Gt(`${jr} ${l}`);return}Gn(3);let i=0;Gt(`${Vn[0]} ${l}`);let a=setInterval(()=>{i=(i+1)%Vn.length,Gt(`${Vn[i]} ${l}`)},400);return()=>clearInterval(a)},[N,e,qr]),$e(()=>()=>Gn(0),[]),$e(()=>{u({kind:"banner",info:jp(e,o,t,s)});let l=Fa(t).state==="missing";if(l&&u({kind:"note",tone:"warn",text:`\u26A0 Set up Claude first \u2014 d360-writer's agent runs on Claude Code, and it's not signed in on this machine yet.
245
245
  ${Qd}
246
246
  Run /doctor anytime to re-check and see the fix. (You can configure Document360 meanwhile \u2014 the agent just needs Claude before it writes.)`}),!Z(e)){u({kind:"note",tone:"info",text:`Welcome! This repo isn't set up for d360-writer yet \u2014 three steps and you're writing docs:
247
247
  ${Ua}`}),B(l?["/doctor","/init"]:["/init"]);return}l&&B(["/doctor"]);try{let i=fe(e,s),a=Je(i.name);a&&Le(a)&&a.refreshToken?np({profile:i.name,connection:i.connection}).then(()=>{u({kind:"note",tone:"ok",text:"\u2713 Document360 session refreshed."}),oe(g=>g+1)}).catch(()=>{u({kind:"note",tone:"warn",text:"Document360 session refresh failed \u2014 do you want to log in now? (press 1)"}),B(["/login"])}):(!a||Le(a))&&(u({kind:"note",tone:"warn",text:`Profile "${i.name}" is not signed in \u2014 do you want to log in now? (press 1)`}),B(["/login"]))}catch{}try{let i=Z(e),a=fe(e,s),g=Je(a.name);i&&g&&!Le(g)&&_p(e,i.authoritativeSourceFiles)&&(u({kind:"note",tone:"info",text:"Large repo \u2014 the docs scope isn\u2019t set yet. Run /scope to choose which folders back your docs."}),B(["/scope"]))}catch{}try{vn(e);let i=Cn(e);i.length&&(u({kind:"note",tone:"warn",text:i.join(`
@@ -1,11 +1,13 @@
1
1
  import type { SlashCommandResult } from './index.js';
2
2
  export declare const SELF_UPDATE_PKG = "document360-writer";
3
3
  export declare const SELF_UPDATE_HINT = "npm i -g document360-writer@latest";
4
- /** Run the global install. Best-effort: resolves {ok,output}, never throws. We need a shell (to resolve
5
- `npm`/`npm.cmd` cross-platform), but `spawn(file, argsArray, { shell:true })` triggers Node's DEP0190
6
- deprecation warning which leaked into the clean TUI (dogfood, s-197). Passing the whole command as a
7
- STRING with `shell:true` avoids DEP0190 entirely; the only interpolated value is a fixed package name,
8
- so there's no injection surface. */
4
+ /** Run the global install. Best-effort: resolves {ok,output}, never throws.
5
+ - shell:true (as a STRING command, not an args array) resolves `npm`/`npm.cmd` cross-platform without
6
+ tripping Node's DEP0190 (dogfood s-197); the only interpolated value is a fixed package name.
7
+ - detached + unref: `npm i -g` rewrites the global command shims (delete-then-recreate); if the app is
8
+ exited mid-install the install must still COMPLETE so the `d360-writer` shim is never left missing
9
+ (dogfood s-201: a relaunch during the rewrite window hit "command not recognized"). Detaching lets it
10
+ finish independently of the parent. windowsHide keeps the spawned shell from flashing a console. */
9
11
  export declare function runSelfUpdate(): Promise<{
10
12
  ok: boolean;
11
13
  output: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "document360-writer",
3
- "version": "0.4.89",
3
+ "version": "0.4.91",
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.47",
37
+ "document360-engine": "^0.2.48",
38
38
  "ink": "^5.2.1",
39
39
  "picocolors": "^1.1.1",
40
40
  "react": "^18.3.1",