document360-writer 0.5.110 → 0.5.112

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +8 -6
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -116,7 +116,9 @@ either stalls or ships thin. Answer promptly and thoroughly. For each open messa
116
116
  \`re: <the question's id>\`).
117
117
  - If it's a **request**: do what it asks (a "re-run onboarding" request usually embeds the exact prompt to
118
118
  run \u2014 run it).
119
- - Then mark the message resolved: set its frontmatter \`status: done\`.
119
+ - Then flip the message YOU received (the writer's question/request) to \`status: done\`. **Leave your own
120
+ answer file at \`status: open\`** \u2014 the writer's inbox only surfaces open messages; it folds your answer
121
+ into the docs and closes it itself. An answer you pre-close is a dead letter the writer never sees.
120
122
 
121
123
  **Product-discovery questions (subject starts \`[discovery]\`) are the most important to answer well.**
122
124
  Before writing a repo's docs, the writer interviews YOU to understand the product the way a senior
@@ -153,7 +155,7 @@ re: <question id> # for an answer; leave empty for a request
153
155
  sources:
154
156
  - path/to/file.ts
155
157
  subject: "<one line>"
156
- status: open # the side that acts flips this to 'done'
158
+ status: open # ALWAYS 'open' when you write it \u2014 the RECIPIENT closes it after processing
157
159
  ---
158
160
  <the answer or request, in plain words>
159
161
  \`\`\`
@@ -235,7 +237,7 @@ Anything only a human knows that would help document this product. For example:
235
237
  `)}function Ur(e){try{let t=Iu(Ta(e),"utf8"),n=JSON.parse(t);return n&&typeof n=="object"?n:null}catch{return null}}function ja(e,t){let o={...Ur(e)??{},emittedVersion:le,emittedAt:t};nt(Ta(e),`${JSON.stringify(o,null,2)}
236
238
  `)}function ze(e){try{return _u(Pa(e)).some(t=>/\.md$/i.test(t)&&t.toLowerCase()!=="creator-notes.md")}catch{return!1}}function Ee(e){let n=Ur(e)?.completedVersion;return typeof n!="number"?{status:"unset"}:{status:n<le?"stale":"current",completedVersion:n}}function ko(e,t=new Date().toISOString()){let n={guideRefreshed:!1,notifiedBuilder:!1,toVersion:le};try{if(!Gt(yo(e)))return n;let o=Ur(e),r=o?.emittedVersion??0;n.completedVersion=o?.completedVersion;let s=o?.completedVersion??0;le>r&&(bn(Mr(e)),nt(yo(e),Sa()),ja(e,t),n.guideRefreshed=!0),le>s&&(Uu(e).some(p=>p.kind==="request"&&p.subject.startsWith(Ra))||(Mu(e,{id:`onb${le}`,created:t,from:"docs",kind:"request",re:"",sources:[],subject:`${Ra}${le})`,status:"open",body:[`The dev\u2194docs onboarding protocol moved to v${le}${s?` (you last completed v${s})`:""}.`,"Re-run the hand-off so the docs agent and this repo stay in step \u2014 run this prompt here:","",kt()].join(`
237
239
  `)}),n.notifiedBuilder=!0))}catch{}return n}function bo(e){if(!Gt(yo(e)))return[];let t=Ee(e);return t.status==="current"?[]:[`\u{1F514} Your builder agent ${t.completedVersion?`is on v${t.completedVersion}`:"hasn't completed the hand-off yet"} \u2014 d360-writer's dev\u2194docs protocol is now v${le}.`,"Please run this prompt in your repository's coding agent (Claude Code / Codex / Cursor) for:",` ${e}`,Yt,kt(),Yt,"(Also saved in .d360-writer/messages/ \u2014 or run /devhints to re-show. This stops once your builder runs it.)"]}function Wr(){return['document360-writer (its "docs agent") has been removed from this repository. Please undo its dev\u2192docs',"hand-off setup and restore this repo to how it was before \u2014 in order:","","1. In this repo's agent-memory file (CLAUDE.md, or AGENTS.md \u2014 whichever it edited), REMOVE the",' "Documentation hand-off" section that was added (the rule about dropping hint files in'," `.d360-writer/hints/`, keeping `.d360-writer/architecture/` current, checking"," `.d360-writer/messages/`, and relaying `@d360-writer` directives to `.d360-writer/directives.md`)."," Leave everything else in that file untouched.","2. The `.d360-writer/` folder is already deleted on disk. If it was committed, remove it from version"," control too \u2014 `git rm -r --cached .d360-writer` (if it still resolves) \u2014 and commit the deletions.","3. Do NOT recreate any of those files or follow the hand-off protocol anymore.","","That's it \u2014 this repo is back to its pre-docs-agent state."].join(`
238
- `)}function vo(){return['\u{1F9F9} One leftover: your builder still has the "Documentation hand-off" rule in its CLAUDE.md / AGENTS.md'," (reset only clears d360-writer's own .d360-writer/ folder \u2014 never your repo's files)."," To restore it, paste this prompt into your repo's coding agent (Claude Code / Codex / Cursor):",Yt,Wr(),Yt]}function Kt(e){return[...e?.status==="stale"?[`\u26A0 Your builder last completed devhints v${e.completedVersion}; this hand-off is v${le}.`," Have them run the prompt below again \u2014 it now sets up two-way messages + on-demand docs.",""]:[],"\u2713 Wrote .d360-writer/DEV-HINTS-GUIDE.md + the .d360-writer/architecture/ (brief), .d360-writer/hints/, and .d360-writer/messages/ inboxes.","","\u{1F4CB} Copy this one prompt and run it in your repo's coding agent (Claude Code / Codex / Cursor):",Yt,kt(),Yt,"","That one run writes the architecture brief, adds the ongoing rule to your agent memory (CLAUDE.md / AGENTS.md)","and commits it so teammates inherit it, answers any open questions the writer left, and asks you for tribal","knowledge \u2014 no manual file edits. It also sets up the two-way channel: the writer can ask you questions","(`/inbox` on its side), and you can ask it to document a session on demand (Part 4 of the guide).","","When it finishes, come back to THIS terminal and run:"," write the docs for this repo"]}var Yt,le,Wu,Ra,ot=ao(()=>{"use strict";Yt="-".repeat(64),le=9,Wu=`#!/usr/bin/env node
240
+ `)}function vo(){return['\u{1F9F9} One leftover: your builder still has the "Documentation hand-off" rule in its CLAUDE.md / AGENTS.md'," (reset only clears d360-writer's own .d360-writer/ folder \u2014 never your repo's files)."," To restore it, paste this prompt into your repo's coding agent (Claude Code / Codex / Cursor):",Yt,Wr(),Yt]}function Kt(e){return[...e?.status==="stale"?[`\u26A0 Your builder last completed devhints v${e.completedVersion}; this hand-off is v${le}.`," Have them run the prompt below again \u2014 it now sets up two-way messages + on-demand docs.",""]:[],"\u2713 Wrote .d360-writer/DEV-HINTS-GUIDE.md + the .d360-writer/architecture/ (brief), .d360-writer/hints/, and .d360-writer/messages/ inboxes.","","\u{1F4CB} Copy this one prompt and run it in your repo's coding agent (Claude Code / Codex / Cursor):",Yt,kt(),Yt,"","That one run writes the architecture brief, adds the ongoing rule to your agent memory (CLAUDE.md / AGENTS.md)","and commits it so teammates inherit it, answers any open questions the writer left, and asks you for tribal","knowledge \u2014 no manual file edits. It also sets up the two-way channel: the writer can ask you questions","(`/inbox` on its side), and you can ask it to document a session on demand (Part 4 of the guide).","","When it finishes, come back to THIS terminal and run:"," write the docs for this repo"]}var Yt,le,Wu,Ra,ot=ao(()=>{"use strict";Yt="-".repeat(64),le=10,Wu=`#!/usr/bin/env node
239
241
  // d360-writer \u2014 session-start nudge for the builder ("John"). Surfaces open questions the docs agent
240
242
  // ("Julie") left in .d360-writer/messages/ so they get answered, not forgotten. Self-contained (no deps);
241
243
  // wired into your coding agent's session-start trigger by /devhints. See .d360-writer/DEV-HINTS-GUIDE.md.
@@ -267,7 +269,7 @@ try {
267
269
  : '';
268
270
  process.stdout.write(
269
271
  '\u{1F4CB} d360-writer: ' + open + ' open question' + (open === 1 ? '' : 's') + ' from the docs agent await your answer' + d +
270
- '. Answer them in .d360-writer/messages/ and set each to status: done \u2014 an open docs question blocks the documentation. See .d360-writer/DEV-HINTS-GUIDE.md.\\n',
272
+ '. Reply with an answer file (kind: answer, status: open \u2014 the writer closes it after folding), flip only the QUESTION to status: done. An open docs question blocks the documentation. See .d360-writer/DEV-HINTS-GUIDE.md.\\n',
271
273
  );
272
274
  }
273
275
  } catch {
@@ -347,8 +349,8 @@ Articles:
347
349
  `)].join(`
348
350
  `)}async function ac(e,t){let n=e.includes("--fix"),o=e.find(l=>!l.startsWith("--")),r=o?` ${o}`:"",s=Vh({cwd:t.cwd,scope:o});if(n)return s.length===0?(console.log(D("\u2713 Nothing to fix \u2014 the docs are already clean.")),{kind:"continue"}):(console.log(f(`Fixing ${s.length} doc issue${s.length===1?"":"s"} (run /check again after to confirm)\u2026`)),{kind:"forward-to-agent",prompt:Zs(s),display:`/check${r} --fix`});console.log(f("Linting the docs (engine internals, unknown commands, stale facts, AI-tells)\u2026"));for(let l of qo(s))console.log(s.length===0?D(l):L(l));return s.length>0&&console.log(f("Run /check --fix to have the agent correct these.")),console.log(""),{kind:"continue"}}W();import{lintDocs as Qh,loadStyleGuide as Jh,buildReviewPrompt as Zh,readProjectConfig as em,DEFAULT_DOCS_DIR as tm}from"document360-engine";W();import{readProjectConfig as Gh,loadProfileMap as Yh,resolveActiveProfile as zh,desiredCategoryOrder as Kh,desiredArticleOrder as Xh}from"document360-engine";function lc(e){let t=e.replace(/\.(md|markdown)$/i,"").replace(/^\d+[-_]/,"").replace(/[-_]+/g," ").trim();return t?t.charAt(0).toUpperCase()+t.slice(1):e}var cc=e=>e.replace(/\\/g,"/").split("/").pop()??e;function Nn(e,t){try{if(!Gh(e))return[];let n=zh(e,t),o=Yh(e,n.name);if(!o)return[];let r=new Map;for(let l of Xh(o)){let p=r.get(l.categoryPath);p?p.push(l):r.set(l.categoryPath,[l])}let s=[];for(let l of Kh(o)){s.push(lc(cc(l.path)));for(let p of(r.get(l.path)??[]).sort((m,k)=>m.order-k.order))s.push(` ${lc(cc(p.path))}`)}return s}catch{return[]}}async function dc(e,t){let n=Nn(t.cwd,t.profileName);if(n.length===0)return console.log(f("No documentation structure yet \u2014 run /init and author some articles first.")),{kind:"continue"};console.log(O("Documentation order (categories \u2192 articles):"));for(let o of n)console.log(o);return console.log(""),{kind:"continue"}}function ei(e,t){let n=em(e);if(!n)return null;let o=(n.docsDir??tm).replace(/\\/g,"/").replace(/\/+$/,""),r=t.includes("--fix"),s=t.find(g=>!g.startsWith("--"))?.replace(/\\/g,"/").replace(/\/+$/,""),l=Qh({cwd:e}),p=s?l.filter(g=>g.path===`${o}/${s}`||g.path.startsWith(`${o}/${s}/`)):l,m=s?"":Nn(e).join(`
349
351
  `);return{prompt:Zh({docsRoot:o,scope:s,styleGuide:Jh(e),deterministicFindings:p,fix:r,structure:m||void 0}),display:`/review${s?` ${s}`:""}${r?" --fix":""}`}}async function uc(e,t){if(it("review",e)){let o=Jt(t.cwd,"review",e,t.profileName);if(o.length>0){for(let r of o)console.log(f(r));return{kind:"continue"}}}let n=ei(t.cwd,e);return n?{kind:"forward-to-agent",prompt:n.prompt,display:n.display}:(console.log(R("No d360-writer config here. Run /init first.")),{kind:"continue"})}W();import{readProjectConfig as nm}from"document360-engine";function ti(e,t){if(!nm(e))return null;let n=t.find(r=>!r.startsWith("--"));return{prompt:[`Reorder the articles within ${n?`the "${n}" category`:"each category"} to the best reading order.`,"Follow the manage-structure skill: decide the ideal order with senior-editor judgment per the doc-genre","playbook (overview / mental-model first; reference and troubleshooting/FAQ last), then APPLY it with the","d360_reorder_articles tool \u2014 one category at a time, all paths in the same category folder.",n?"":"If the CATEGORY order itself reads wrong too, fix that first with the d360_reorder_categories tool (folder paths under one parent, in the desired order).","Do not rename files or folders or edit the map by hand; the tools handle it. Report the new order, and","remind me it reaches Document360 on the next /publish."].filter(Boolean).join(`
350
- `),display:`/reorder${n?` ${n}`:""}`}}async function pc(e,t){if(it("reorder",e)){let o=Jt(t.cwd,"reorder",e,t.profileName);if(o.length>0){for(let r of o)console.log(f(r));return{kind:"continue"}}}let n=ti(t.cwd,e);return n?{kind:"forward-to-agent",prompt:n.prompt,display:n.display}:(console.log(R("No d360-writer config here. Run /init first.")),{kind:"continue"})}W();import{readProjectConfig as om,writeProjectConfig as fc,isAppType as rm,playbookSkillName as sm,detectAppType as hc}from"document360-engine";var mc=["web","cli","service","library"],im=new Set(["web","cli"]);function ni(e){return im.has(e)?`${sm(e)} will load`:`no ${e}-doc-playbook exists yet \u2014 generic skills apply`}async function Bo(e,t){let n=t.cwd,o=om(n);if(!o)return console.log(R("No d360-writer config here. Run /init first.")),{kind:"continue"};let r=e[0]?.trim().toLowerCase();if(!r){let s=o.appType??hc(n);if(s){let l=o.appType?"set in config":"auto-detected";console.log(`${O("Genre:")} ${s} (${l}) \u2014 ${ni(s)}`)}else console.log(L("Genre: unclassified \u2014 no doc-type playbook is loading."));return console.log(f(`Set it: /genre <${mc.join("|")}> \xB7 revert to auto-detect: /genre auto`)),{kind:"continue"}}if(r==="auto"||r==="clear"){if(!o.appType)return console.log(f("Already on auto-detect (no genre set in config).")),{kind:"continue"};let{appType:s,...l}=o;fc(l,n);let p=hc(n);return console.log(D(`\u2713 Genre reset to auto-detect \u2014 now: ${p?`${p} (${ni(p)})`:"unclassified (no playbook)"} (applies from your next command).`)),{kind:"continue"}}return rm(r)?(fc({...o,appType:r},n),console.log(D(`\u2713 Genre set to ${r} \u2014 ${ni(r)} (applies from your next command).`)),{kind:"continue"}):(console.log(R(`Unknown genre "${r}". Choose one of: ${mc.join(", ")} (or "auto" to revert to detection).`)),{kind:"continue"})}W();import{listRoadmap as gc,roadmapProgress as wc,stampRoadmapDone as am}from"document360-engine";function oi(e){let t=gc(e);if(t.length===0)return["No docs roadmap yet \u2014 the docs agent seeds .d360-writer/roadmap.md when it pins a structure,","and adds steps as the app changes. You can also write steps by hand (## [open] <step>), or ask:",'"propose the docs roadmap for this repo".'];let n=wc(e),o=[`Docs roadmap \u2014 ${n.done} of ${n.total} step${n.total===1?"":"s"} done:`,""];for(let r of t)o.push(` ${r.status==="done"?"\u2713":"\u25CB"} ${r.title}${r.run?` \xB7 ${r.run}`:""}`);return o.push(""),o.push(n.next?`Next: ${n.next.title}${n.next.run?` \u2014 ${n.next.run}`:""}`:"Campaign complete \u2014 the docs agent adds new steps as the app changes."),o.push("(Edit .d360-writer/roadmap.md freely \xB7 /roadmap done <step> marks one you finished yourself.)"),o}function ri(e,t){if(!t.trim())return{ok:!1,line:"Usage: /roadmap done <step title>"};if(!am(e,t,{doneAt:new Date().toISOString()})){let r=gc(e).filter(s=>s.status==="open");return{ok:!1,line:r.length?`No open step titled "${t}". Open steps: ${r.map(s=>`"${s.title}"`).join(", ")}`:`No open step titled "${t}" \u2014 the roadmap has no open steps.`}}let o=wc(e)?.next;return{ok:!0,line:`\u2713 Marked done: ${t}${o?` \u2014 next up: ${o.title}${o.run?` (${o.run})`:""}`:" \u2014 that was the last step: campaign complete."}`}}async function yc(e,t){let n=t.cwd;if(e[0]?.toLowerCase()==="done"){let r=ri(n,e.slice(1).join(" ").trim());return console.log(r.ok?D(r.line):L(r.line)),{kind:"continue"}}let o=oi(n);console.log(O(o[0]??""));for(let r of o.slice(1))console.log(r.startsWith("(")?f(r):r);return{kind:"continue"}}var kc={help:Or,"?":Or,clear:ya,exit:Lr,quit:Lr,init:Hr,mcp:Co,publish:nl,audit:Pn,discover:ol,scope:sl,pull:gl,resolve:fl,convert:wl,write:xl,draft:Sl,document:Dl,resume:El,rename:Nl,profile:_l,model:Ml,doctor:Dn,workspace:Bl,project:Vl,"allow-prod":Gl,login:Yl,logout:zl,screenshot:Mo,"capture-setup":jn,devhints:Da,inbox:Zl,"catch-up":ec,update:tc,reset:ic,check:ac,review:uc,reorder:pc,structure:dc,genre:Bo,roadmap:yc};function bc(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}ot();var lm="Or just tell me what to write or update.",cm='\u{1F4ED} No docs yet for this repo. Tell me "write the docs for this repo" and I\u2019ll read the code and draft a structure \u2014 or /audit to see what the docs should cover.';function fe(e){return e===1?"":"s"}function Vo(e){return e.kind==="cmd"?`\u21E5 tab to run ${e.cmd} \xB7 or just ask me anything\u2026`:null}function In(e){return e.kind==="cmd"?`I\u2019d start with ${e.cmd} \u2014 ${e.reason}.`:`Just tell me: \u201C${e.text}\u201D.`}var dm={title:"What kind of product is this repo? (sets the doc-craft playbook \u2014 otherwise the docs stay generic)",options:[{cmd:"/genre web",desc:"web app / SaaS UI"},{cmd:"/genre cli",desc:"command-line tool"},{cmd:"/genre service",desc:"API / backend service"},{cmd:"/genre library",desc:"SDK / library"}]};function um(e){let t=[];if(e.devhints!=="current"&&e.builderStarted){let n=e.devhintsToVersion?`v${e.devhintsToVersion}`:"a newer version",o=e.devhints==="stale"&&e.devhintsCompletedVersion?` (your builder is on v${e.devhintsCompletedVersion})`:"";t.push({key:"devhints",bullet:`dev\u2194docs protocol moved to ${n}${o} \u2014 your docs aren\u2019t blocked`,lead:{kind:"cmd",cmd:"/devhints",reason:`one paste updates your builder to ${n}`},picker:null,follow:"Also run /devhints when you get a second \u2014 your builder\u2019s protocol is behind.",tone:"warn"})}if(e.genreUnknown&&t.push({key:"genre",bullet:"the doc genre isn\u2019t set \u2014 I can\u2019t load the right doc-craft playbook yet",lead:null,picker:dm,follow:"Then pick the doc genre below (it sets the playbook I write with)."}),e.discovery&&e.discovery.status!=="confident"){let n=e.discovery.openQuestions;t.push(e.discovery.status==="pending"?{key:"discovery",bullet:"your existing docs aren\u2019t grounded in a product brief yet",lead:{kind:"cmd",cmd:"/discover",reason:"it interviews your builder and grounds the docs before you rewrite or publish them"},picker:null,follow:"Then /discover grounds your docs in a product brief (do this before the rewrite/publish steps)."}:n>0?{key:"discovery",bullet:`product discovery is waiting on ${n} answer${fe(n)} from your builder \u2014 nudge them`,lead:null,picker:null,follow:"Discovery is waiting on your builder\u2019s answers \u2014 nudge them, then /catch-up folds the answers in once they reply."}:{key:"discovery",bullet:"product discovery is ready to continue",lead:{kind:"cmd",cmd:"/discover",reason:"it continues interviewing your builder to ground the docs"},picker:null,follow:"Then /discover continues building the product brief."})}if(e.hasDocs&&e.pendingWrites>0){let n=e.pendingWrites;t.push({key:"write",bullet:`${n} planned article${fe(n)} still unwritten`,lead:{kind:"cmd",cmd:"/write",reason:"it finishes the articles already planned"},picker:null,follow:`Then /write finishes the ${n} planned article${fe(n)}.`})}if(e.directives>0||e.pendingText){let n=e.directives>0&&e.pendingText?"it actions your directives and clears the builder\u2019s queue":e.directives>0?"it actions the directives you left":"it clears everything your builder queued",o=[];e.directives>0&&o.push(`${e.directives} owner directive${fe(e.directives)} waiting`),e.pendingText&&o.push(`your builder left ${e.pendingText}`),t.push({key:"catchup",bullet:o.join(`
351
- \u2022 `),lead:{kind:"cmd",cmd:"/catch-up",reason:n},picker:null,follow:"Then /catch-up clears what\u2019s queued for the docs."})}if(e.localAhead>0){let n=e.localAhead;t.push({key:"publish",bullet:`${n} article${fe(n)} changed locally \u2014 ready to publish`,lead:{kind:"cmd",cmd:"/publish",reason:`it pushes your ${n} local edit${fe(n)} up to Document360`},picker:null,follow:`Then /publish pushes your ${n} local edit${fe(n)} up.`})}if(e.conflicts>0){let n=e.conflicts;t.push({key:"resolve",bullet:`${n} article${fe(n)} changed both here and on Document360 (conflicts) \u2014 /publish skips these`,lead:{kind:"cmd",cmd:"/resolve",reason:"it walks each conflict \u2014 keep yours, take the portal's, or skip"},picker:null,follow:`Then /resolve settles the ${n} conflict${fe(n)} (keep yours / take the portal's / skip).`})}if(e.hasDocs&&e.roadmap?.next){let n=e.roadmap,o=n.next;t.push({key:"roadmap",bullet:`docs campaign: ${n.done} of ${n.total} step${fe(n.total)} done \u2014 next: ${o.title}`,lead:o.run?{kind:"cmd",cmd:o.run,reason:"it\u2019s the next step of your docs campaign"}:{kind:"prompt",text:o.title,reason:"it\u2019s the next step of your docs campaign"},picker:null,follow:`Then the campaign continues: ${o.title} (/roadmap shows all ${n.total} steps).`})}if(e.behind>0){let n=e.behind;t.push({key:"audit",bullet:`${n} code change${fe(n)} since your last docs check`,lead:{kind:"cmd",cmd:"/audit",reason:"it shows what the latest code changes touch (or /audit --auto on to run it for you)"},picker:null,follow:`After that, /audit covers the ${n} newer commit${fe(n)}.`})}return t}function vc(e){return e.find(t=>t.lead||t.picker)??null}function si(e,t){let n=t.filter(l=>l.bullet).map(l=>` \u2022 ${l.bullet}`),o=vc(t),r=[];o?.lead?r.push(In(o.lead)):o?.picker&&r.push("Pick the genre below \u2014 one keypress, and it shapes everything I write.");for(let l of t.filter(p=>p!==o).slice(0,2))l.follow&&r.push(l.follow);let s=[[e,...n].join(`
352
+ `),display:`/reorder${n?` ${n}`:""}`}}async function pc(e,t){if(it("reorder",e)){let o=Jt(t.cwd,"reorder",e,t.profileName);if(o.length>0){for(let r of o)console.log(f(r));return{kind:"continue"}}}let n=ti(t.cwd,e);return n?{kind:"forward-to-agent",prompt:n.prompt,display:n.display}:(console.log(R("No d360-writer config here. Run /init first.")),{kind:"continue"})}W();import{readProjectConfig as om,writeProjectConfig as fc,isAppType as rm,playbookSkillName as sm,detectAppType as hc}from"document360-engine";var mc=["web","cli","service","library"],im=new Set(["web","cli"]);function ni(e){return im.has(e)?`${sm(e)} will load`:`no ${e}-doc-playbook exists yet \u2014 generic skills apply`}async function Bo(e,t){let n=t.cwd,o=om(n);if(!o)return console.log(R("No d360-writer config here. Run /init first.")),{kind:"continue"};let r=e[0]?.trim().toLowerCase();if(!r){let s=o.appType??hc(n);if(s){let l=o.appType?"set in config":"auto-detected";console.log(`${O("Genre:")} ${s} (${l}) \u2014 ${ni(s)}`)}else console.log(L("Genre: unclassified \u2014 no doc-type playbook is loading."));return console.log(f(`Set it: /genre <${mc.join("|")}> \xB7 revert to auto-detect: /genre auto`)),{kind:"continue"}}if(r==="auto"||r==="clear"){if(!o.appType)return console.log(f("Already on auto-detect (no genre set in config).")),{kind:"continue"};let{appType:s,...l}=o;fc(l,n);let p=hc(n);return console.log(D(`\u2713 Genre reset to auto-detect \u2014 now: ${p?`${p} (${ni(p)})`:"unclassified (no playbook)"} (applies from your next command).`)),{kind:"continue"}}return rm(r)?(fc({...o,appType:r},n),console.log(D(`\u2713 Genre set to ${r} \u2014 ${ni(r)} (applies from your next command).`)),{kind:"continue"}):(console.log(R(`Unknown genre "${r}". Choose one of: ${mc.join(", ")} (or "auto" to revert to detection).`)),{kind:"continue"})}W();import{listRoadmap as gc,roadmapProgress as wc,stampRoadmapDone as am}from"document360-engine";function oi(e){let t=gc(e);if(t.length===0)return["No docs roadmap yet \u2014 the docs agent seeds .d360-writer/roadmap.md when it pins a structure,","and adds steps as the app changes. You can also write steps by hand (## [open] <step>), or ask:",'"propose the docs roadmap for this repo".'];let n=wc(e),o=[`Docs roadmap \u2014 ${n.done} of ${n.total} step${n.total===1?"":"s"} done:`,""];for(let r of t)o.push(` ${r.status==="done"?"\u2713":"\u25CB"} ${r.title}${r.run?` \xB7 ${r.run}`:""}`);return o.push(""),o.push(n.next?`Next: ${n.next.title}${n.next.run?` \u2014 ${n.next.run}`:""}`:"Campaign complete \u2014 the docs agent adds new steps as the app changes."),o.push("(Edit .d360-writer/roadmap.md freely \xB7 /roadmap done <step> marks one you finished yourself.)"),o}function ri(e,t){if(!t.trim())return{ok:!1,line:"Usage: /roadmap done <step title>"};if(!am(e,t,{doneAt:new Date().toISOString()})){let r=gc(e).filter(s=>s.status==="open");return{ok:!1,line:r.length?`No open step titled "${t}". Open steps: ${r.map(s=>`"${s.title}"`).join(", ")}`:`No open step titled "${t}" \u2014 the roadmap has no open steps.`}}let o=wc(e)?.next;return{ok:!0,line:`\u2713 Marked done: ${t}${o?` \u2014 next up: ${o.title}${o.run?` (${o.run})`:""}`:" \u2014 that was the last step: campaign complete."}`}}async function yc(e,t){let n=t.cwd;if(e[0]?.toLowerCase()==="done"){let r=ri(n,e.slice(1).join(" ").trim());return console.log(r.ok?D(r.line):L(r.line)),{kind:"continue"}}let o=oi(n);console.log(O(o[0]??""));for(let r of o.slice(1))console.log(r.startsWith("(")?f(r):r);return{kind:"continue"}}var kc={help:Or,"?":Or,clear:ya,exit:Lr,quit:Lr,init:Hr,mcp:Co,publish:nl,audit:Pn,discover:ol,scope:sl,pull:gl,resolve:fl,convert:wl,write:xl,draft:Sl,document:Dl,resume:El,rename:Nl,profile:_l,model:Ml,doctor:Dn,workspace:Bl,project:Vl,"allow-prod":Gl,login:Yl,logout:zl,screenshot:Mo,"capture-setup":jn,devhints:Da,inbox:Zl,"catch-up":ec,update:tc,reset:ic,check:ac,review:uc,reorder:pc,structure:dc,genre:Bo,roadmap:yc};function bc(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}ot();var lm="Or just tell me what to write or update.",cm='\u{1F4ED} No docs yet for this repo. Tell me "write the docs for this repo" and I\u2019ll read the code and draft a structure \u2014 or /audit to see what the docs should cover.';function fe(e){return e===1?"":"s"}function Vo(e){return e.kind==="cmd"?`\u21E5 tab to run ${e.cmd} \xB7 or just ask me anything\u2026`:null}function In(e){return e.kind==="cmd"?`I\u2019d start with ${e.cmd} \u2014 ${e.reason}.`:`Just tell me: \u201C${e.text}\u201D.`}var dm={title:"What kind of product is this repo? (sets the doc-craft playbook \u2014 otherwise the docs stay generic)",options:[{cmd:"/genre web",desc:"web app / SaaS UI"},{cmd:"/genre cli",desc:"command-line tool"},{cmd:"/genre service",desc:"API / backend service"},{cmd:"/genre library",desc:"SDK / library"}]};function um(e){let t=[];if(e.devhints!=="current"&&e.builderStarted){let n=e.devhintsToVersion?`v${e.devhintsToVersion}`:"a newer version",o=e.devhints==="stale"&&e.devhintsCompletedVersion?` (your builder is on v${e.devhintsCompletedVersion})`:"";t.push({key:"devhints",bullet:`dev\u2194docs protocol moved to ${n}${o} \u2014 your docs aren\u2019t blocked`,lead:{kind:"cmd",cmd:"/devhints",reason:`one paste updates your builder to ${n}`},picker:null,follow:"Also run /devhints when you get a second \u2014 your builder\u2019s protocol is behind.",tone:"warn"})}if(e.genreUnknown&&t.push({key:"genre",bullet:"the doc genre isn\u2019t set \u2014 I can\u2019t load the right doc-craft playbook yet",lead:null,picker:dm,follow:"Then pick the doc genre below (it sets the playbook I write with)."}),e.discovery&&e.discovery.status!=="confident"){let n=e.discovery.openQuestions;t.push(e.discovery.status==="pending"?{key:"discovery",bullet:"your existing docs aren\u2019t grounded in a product brief yet",lead:{kind:"cmd",cmd:"/discover",reason:"it interviews your builder and grounds the docs before you rewrite or publish them"},picker:null,follow:"Then /discover grounds your docs in a product brief (do this before the rewrite/publish steps)."}:n>0?{key:"discovery",bullet:`product discovery is waiting on ${n} answer${fe(n)} from your builder \u2014 nudge them`,lead:null,picker:null,follow:"Discovery is waiting on your builder\u2019s answers \u2014 nudge them, then /catch-up folds the answers in once they reply."}:{key:"discovery",bullet:"product discovery is ready to continue",lead:{kind:"cmd",cmd:"/discover",reason:"it continues interviewing your builder to ground the docs"},picker:null,follow:"Then /discover continues building the product brief."})}if(e.directives>0||e.pendingText){let n=e.directives>0&&e.pendingText?"it actions your directives and clears the builder\u2019s queue":e.directives>0?"it actions the directives you left":"it clears everything your builder queued",o=[];e.directives>0&&o.push(`${e.directives} owner directive${fe(e.directives)} waiting`),e.pendingText&&o.push(`your builder left ${e.pendingText}`),t.push({key:"catchup",bullet:o.join(`
353
+ \u2022 `),lead:{kind:"cmd",cmd:"/catch-up",reason:n},picker:null,follow:"Then /catch-up clears what\u2019s queued for the docs."})}if(e.hasDocs&&e.pendingWrites>0){let n=e.pendingWrites;t.push({key:"write",bullet:`${n} planned article${fe(n)} still unwritten`,lead:{kind:"cmd",cmd:"/write",reason:"it finishes the articles already planned"},picker:null,follow:`Then /write finishes the ${n} planned article${fe(n)}.`})}if(e.localAhead>0){let n=e.localAhead;t.push({key:"publish",bullet:`${n} article${fe(n)} changed locally \u2014 ready to publish`,lead:{kind:"cmd",cmd:"/publish",reason:`it pushes your ${n} local edit${fe(n)} up to Document360`},picker:null,follow:`Then /publish pushes your ${n} local edit${fe(n)} up.`})}if(e.conflicts>0){let n=e.conflicts;t.push({key:"resolve",bullet:`${n} article${fe(n)} changed both here and on Document360 (conflicts) \u2014 /publish skips these`,lead:{kind:"cmd",cmd:"/resolve",reason:"it walks each conflict \u2014 keep yours, take the portal's, or skip"},picker:null,follow:`Then /resolve settles the ${n} conflict${fe(n)} (keep yours / take the portal's / skip).`})}if(e.hasDocs&&e.roadmap?.next){let n=e.roadmap,o=n.next;t.push({key:"roadmap",bullet:`docs campaign: ${n.done} of ${n.total} step${fe(n.total)} done \u2014 next: ${o.title}`,lead:o.run?{kind:"cmd",cmd:o.run,reason:"it\u2019s the next step of your docs campaign"}:{kind:"prompt",text:o.title,reason:"it\u2019s the next step of your docs campaign"},picker:null,follow:`Then the campaign continues: ${o.title} (/roadmap shows all ${n.total} steps).`})}if(e.behind>0){let n=e.behind;t.push({key:"audit",bullet:`${n} code change${fe(n)} since your last docs check`,lead:{kind:"cmd",cmd:"/audit",reason:"it shows what the latest code changes touch (or /audit --auto on to run it for you)"},picker:null,follow:`After that, /audit covers the ${n} newer commit${fe(n)}.`})}return t}function vc(e){return e.find(t=>t.lead||t.picker)??null}function si(e,t){let n=t.filter(l=>l.bullet).map(l=>` \u2022 ${l.bullet}`),o=vc(t),r=[];o?.lead?r.push(In(o.lead)):o?.picker&&r.push("Pick the genre below \u2014 one keypress, and it shapes everything I write.");for(let l of t.filter(p=>p!==o).slice(0,2))l.follow&&r.push(l.follow);let s=[[e,...n].join(`
352
354
  `)];return r.length&&s.push(r.join(" ")),s.push(lm),s.join(`
353
355
 
354
356
  `)}function _n(e){if(!e.ready)return{kind:"idle"};let t=um(e);if(!e.hasDocs){let s=vc(t);return s&&(s.lead?.kind==="cmd"||s.picker)?{kind:"recommend",rec:{note:si("\u{1F4ED} No docs yet for this repo \u2014 here\u2019s where things stand:",t),tone:s.tone??"info",lead:s.lead,picker:s.picker}}:{kind:"recommend",rec:{note:cm,tone:"info",lead:{kind:"prompt",text:"write the docs for this repo",reason:"I\u2019ll read the code and draft a structure"},picker:null}}}if(t.length===0){let s=e.roadmap&&!e.roadmap.next?` Docs campaign complete \u2014 all ${e.roadmap.total} step${fe(e.roadmap.total)} done (/roadmap).`:"";return{kind:"recommend",rec:{note:`Welcome back. Your ${e.tracked} tracked article${fe(e.tracked)} for \u201C${e.profileName}\u201D are all in sync \u2014 /audit re-checks the code, /pull brings down portal edits.${s}`,tone:"info",lead:null,picker:null}}}let n=t.filter(s=>s.lead||s.picker),o=n[0]??null;return o?(o.key==="audit"||o.key==="roadmap"&&n[1]?.key==="audit")&&e.behind>0&&e.autoAudit&&e.allowAutoAudit?{kind:"autoAudit",note:`\u{1F4CB} ${e.behind} code change${fe(e.behind)} since your last docs check \u2014 running /audit automatically (auto-audit is on)\u2026`}:{kind:"recommend",rec:{note:si("Welcome back \u2014 where your docs stand:",t),tone:o.tone??"info",lead:o.lead,picker:o.picker}}:{kind:"recommend",rec:{note:si("Welcome back \u2014 where your docs stand:",t),tone:"info",lead:null,picker:null}}}import{existsSync as ii,readdirSync as li}from"node:fs";import{spawnSync as pm}from"node:child_process";import{join as Go}from"node:path";import{readProjectConfig as xc,resolveActiveProfile as Cc,loadTokens as fm,isExpired as hm,loadProfileMap as Rc,localAheadPaths as mm,loadKnownConflicts as gm,readDocsPlan as wm,devHintsGuidePath as ym,DEFAULT_DOCS_DIR as ai,roadmapProgress as km,listRoadmap as bm,readProductBriefState as vm}from"document360-engine";ot();function $c(e){try{return li(e,{withFileTypes:!0}).filter(t=>t.isDirectory()&&!t.name.startsWith(".")).length>6}catch{return!1}}function ci(e,t){let n=t??[];return n.length===0?["src","api","services","packages","modules"].some(o=>$c(Go(e,o))):n.some(o=>!o.includes("/")&&!o.endsWith(".md")&&$c(Go(e,o)))}function di(e,t){try{let n=xc(e);if(!n)return!1;let o=Cc(e,t),r=Rc(e,o.name);if(r&&Object.keys(r.articles).length>0)return!0;let s=Go(e,(n.docsDir??ai).replace(/\/+$/,""));return!!(ii(s)&&li(s).length>0||ii(ym(e)))}catch{return!1}}function On(e,t,n){try{let o=xc(e);if(!o)return null;let r=Cc(e,t),s=fm(r.name),l=!!s&&(!hm(s)||!!s.refreshToken),p=di(e,t)||!ci(e,o.authoritativeSourceFiles),m=Rc(e,r.name),k=m?Object.keys(m.articles).length:0,g=Go(e,(o.docsDir??ai).replace(/\/+$/,"")),T=!1;try{T=ii(g)&&li(g).length>0}catch{}let v=0;if(m?.lastAnalyzedCommit)try{let Se=pm("git",["rev-list","--count",`${m.lastAnalyzedCommit}..HEAD`],{cwd:e,encoding:"utf8",timeout:1200});Se.status===0&&(v=parseInt((Se.stdout||"").trim(),10)||0)}catch{}let _=0;try{let Se=wm(e);if(Se.length>0){let be=(o.docsDir??ai).replace(/\/+$/,"");_=ks(e,be,Se.map(He=>He.path)).length}}catch{}let P=An(e),N=En({...P,directives:0}),S=mm(e,t),M=new Set(gm(e,r.name)),B=S.filter(Se=>M.has(Se)).length,V=S.length-B,Y=Ee(e),ne=km(e),_e=vm(e),Fe=_e?{status:_e.status,openQuestions:_e.openQuestions}:null;return!Fe&&T&&bm(e).some(be=>be.status==="open"&&(be.run??"").trim()==="/discover")&&(Fe={status:"pending",openQuestions:0}),{ready:l&&p&&!n.claudeMissing,hasDocs:T,tracked:k,pendingText:N,directives:P.directives,behind:v,pendingWrites:_,localAhead:V,conflicts:B,autoAudit:!!o.autoAudit,profileName:r.name,allowAutoAudit:n.allowAutoAudit,devhints:Y.status,devhintsCompletedVersion:Y.completedVersion,devhintsToVersion:le,builderStarted:ze(e),genreUnknown:n.genreUnknown,roadmap:ne,discovery:Fe}}catch{return null}}function Pc(e,t){let n=[],o="";for(let r of e.split(/\s+/).filter(Boolean))for(o.length?o.length+1+r.length<=t?o+=` ${r}`:(n.push(o),o=r):o=r;o.length>t;)n.push(o.slice(0,t)),o=o.slice(t);return o&&n.push(o),n}function Yo(e){if(e.length===0)return[];let t=(s,l)=>`${s}${"\u2500".repeat(78)}${l}`,n=(s="")=>`\u2502 ${s.padEnd(76)} \u2502`,o=[`\u2B06 d360-writer upgraded \u2014 ${e.length===1?"one improvement affects":`${e.length} improvements affect`} the docs you've already written:`,"",t("\u250C","\u2510")];e.forEach((s,l)=>{l>0&&o.push(t("\u251C","\u2524")),o.push(n());for(let p of Pc(`${l+1}. ${s.feature}`,76))o.push(n(p));o.push(n());for(let p of Pc(s.why,73))o.push(n(` ${p}`));o.push(n())}),o.push(t("\u2514","\u2518")),o.push("");let r=e.length>1;return o.push(`\u2713 ${r?"These are":"This is"} now on your docs roadmap \u2014 each launch shows your next step below (/roadmap shows the full campaign).`),o}W();var $m={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"},Sc=160,pi=200,Dc=40;function Re(e,t){let n=e.replace(/\s+/g," ").trim();return n.length<=t?n:n.slice(0,t-1)+"\u2026"}function Tc(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}: ${Re(typeof r=="string"?r:JSON.stringify(r),Dc)}`);return t.length>4&&n.push("\u2026"),Re(n.join(", "),Sc)}var at=e=>typeof e=="string"&&e?e:null,ui=e=>typeof e=="string"&&e.length>=8?e.slice(0,8):null;function xm(e){let t=at(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 Cm(e,t){let n=(o,r)=>({title:`Document360: ${o}`,sep:" ",arg:r});switch(e){case"d360_create_article":{let o=at(t.title);if(!o)return null;let r=xm(t.local_path);return n("Create article",`"${Re(o,60)}"${r?` in ${r}`:""}`)}case"d360_update_article":{let o=at(t.title),r=ui(t.article_id);return n("Update article",o?`"${Re(o,60)}"`:r?`id ${r}\u2026`:null)}case"d360_fork_article":return n("Fork article (new draft)",ui(t.article_id)?`id ${ui(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",at(t.name)?`"${Re(at(t.name),60)}"`:null);case"d360_upload_drive_file":{let o=at(t.file_path);return n("Upload image",o?Re(o.replace(/\\/g,"/").split("/").pop()??o,60):null)}case"d360_sync_status":return n("Check sync status",null);default:return null}}function zo(e,t){if(e==="ToolSearch")return null;if(e.startsWith("mcp__")){let[,r="",...s]=e.split("__"),l=s.join("__");if(r==="document360"){let k=Cm(l,t);if(k)return k}let p=l.replace(/^d360_/,"").replace(/_/g," ");return{title:`${r==="document360"?"Document360":r.charAt(0).toUpperCase()+r.slice(1)}: ${p}`,sep:" ",arg:Tc(t)}}let n=$m[e],o=n?t[n]:void 0;return typeof o=="string"&&o?{title:e,sep:"",arg:Re(o,Sc)}:{title:e,sep:"",arg:Tc(t)}}function Rm(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 Pm(e){if(!/^[[{]/.test(e))return null;let t;try{t=JSON.parse(e)}catch{return null}if(Array.isArray(t)){let n=t.map(Rm).filter(r=>r!==null),o=`${t.length} item${t.length===1?"":"s"}`;return n.length===0?[o]:[o,...n.map(r=>Re(r,pi))]}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}: ${Re(String(r),Dc)}`);return n.length>0?[Re(n.join(" \xB7 "),pi)]:null}return null}function Tm(e){let t=at(e)?.replace(/\\/g,"/");if(!t)return null;let n=t.split("/").filter(Boolean);return n.length>1?n.slice(1).join("/"):t}function Sm(e,t,n){switch(e){case"d360_create_article":{let o=Tm(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=at(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 ${Re(o[0],120)}`:""}`]}default:return null}}function Ko(e,t=4,n,o){let r=e.replace(/\r\n/g,`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "document360-writer",
3
- "version": "0.5.110",
3
+ "version": "0.5.112",
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.122",
37
+ "document360-engine": "^0.2.123",
38
38
  "ink": "^5.2.1",
39
39
  "picocolors": "^1.1.1",
40
40
  "react": "^18.3.1",