document360-writer 0.5.53 → 0.5.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -244,7 +244,7 @@ Articles:
|
|
|
244
244
|
`),s=n.mode==="scope"?`/screenshot ${n.scope}`:`/screenshot${e[0]?` ${e[0]}`:""}`;return{kind:"forward-to-agent",prompt:r,display:s}}import{existsSync as pp,readFileSync as fp}from"node:fs";import{isAbsolute as mp,join as hp,resolve as gp}from"node:path";import{readProjectConfig as wp,DEFAULT_CAPTURE_DIR as yp}from"document360-engine";async function un(){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(`
|
|
245
245
|
`),display:"/capture-setup"}}var Wa="-".repeat(64);function os(e){let t=yp;try{let x=wp(e);x?.captureDir&&(t=x.captureDir.replace(/\\/g,"/").replace(/\/+$/,""))}catch{}let n=mp(t)?hp(t,"CAPTURE-TESTID-REQUESTS.json"):gp(e,t,"CAPTURE-TESTID-REQUESTS.json");if(!pp(n))return null;let o;try{let x=JSON.parse(fp(n,"utf8").replace(/^/,""));o=Array.isArray(x?.requests)?x.requests:[]}catch{return null}if(o.length===0)return null;let r=new Set(o.map(x=>x.file).filter(Boolean)).size,s=o.length,c=`${s} data-testid attribute${s===1?"":"s"}`,p=`${r} source file${r===1?"":"s"}`,g=`${t}/CAPTURE-TESTID-REQUESTS.md`,k=`${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.","",Wa,`Read ${g} and ${k}. 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.",Wa]}ct();W();import{listOpenForDocs as kp,readProjectConfig as bp}from"document360-engine";function rs(e){try{return kp(e).length}catch{return 0}}function ss(){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(`
|
|
246
246
|
`)}async function Fa(e,t){let n=t?.cwd??process.cwd();return bp(n)?rs(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:ss(),display:"/inbox"}:(console.log($("No d360-writer config here. Run /init first.")),{kind:"continue"})}W();import{listOpenHints as xp,listOpenForDocs as $p,readProjectConfig as vp,computeSyncStatus as Cp,planPull as Pp,applyPull as Rp}from"document360-engine";function Ot(e){let t=0,n=0,o=0;try{t=xp(e).length}catch{}try{let r=$p(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 Ut(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 as(e){return{publish:e.includes("--publish")}}function ls({publish:e}){let t=["Catch the documentation up: process ALL the pending work the builder has queued, end to end.","","1. The portal was already reconciled before this run: clean portal edits were pulled into local, and any conflicts / new-portal-articles were surfaced to the user. Do NOT re-pull. Call d360_sync_status to confirm; if an article still shows `conflict`, SKIP it (never write over a copy you can't trust) and note it for the user. Otherwise proceed.","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.","8. Cross-link second pass: any article you wrote that links to ANOTHER article in this same batch had that inline link degrade to plain text (its target had no live URL yet when the source was written). Now that every target exists, re-update the affected articles so those inline links resolve to live URLs \u2014 only the ones that had such forward references need it. (The structured Related Articles sidebars are already two-way linked; this is just inline body links.)"];return t.push(e?"9. 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(`
|
|
247
|
-
`)}var Tp={status:Cp,plan:Pp,apply:Rp};async function cs(e,t=Tp){let n={pulled:[],conflicts:[],untrackedRemote:[],failed:[]},o;try{o=await t.status({cwd:e.cwd,profileName:e.profileName})}catch(r){return n.error=r.message,n}for(let r of o.entries)if(r.status==="conflict"&&r.path)n.conflicts.push(r.path);else if(r.status==="untracked-remote")n.untrackedRemote.push(r.title??r.articleId??"(untitled)");else if(r.status==="remote-ahead"&&r.path)try{let s=await t.plan({cwd:e.cwd,profileName:e.profileName,relPath:r.path});t.apply({cwd:e.cwd,profileName:e.profileName},s),n.pulled.push(r.path)}catch(s){n.failed.push({path:r.path,error:s.message})}return n}var is=(e,t=5)=>`${e.slice(0,t).join(", ")}${e.length>t?`, +${e.length-t} more`:""}`,ao=e=>e===1?"":"s";function us(e){if(e.error)return[`\u26A0 Couldn't check Document360 for portal changes (${e.error.slice(0,100)}) \u2014 proceeding; the agent will re-check.`];let t=[];return e.pulled.length&&t.push(`\u2713 Pulled ${e.pulled.length} portal edit${ao(e.pulled.length)} into local: ${is(e.pulled)}`),e.conflicts.length&&t.push(`\u26A0 ${e.conflicts.length} article${ao(e.conflicts.length)} changed both locally and in the portal \u2014 left untouched (resolve: /pull <path>): ${is(e.conflicts)}`),e.untrackedRemote.length&&t.push(`\u2139 ${e.untrackedRemote.length} new article${ao(e.untrackedRemote.length)} in the portal aren't local yet \u2014 run /pull to bring them in: ${is(e.untrackedRemote)}`),e.failed.length&&t.push(`\u26A0 ${e.failed.length} pull${ao(e.failed.length)} failed \u2014 left for /pull.`),t}async function Ha(e,t){let n=t?.cwd??process.cwd();if(!vp(n))return console.log($("No d360-writer config here. Run /init first.")),{kind:"continue"};let o=Le(n,t?.profileName);if(!o.ok)return console.log($(o.reason)),{kind:"continue"};let{publish:r}=as(e);console.log(f("Reconciling with Document360 (pulling any portal changes)\u2026"));let s=await cs({cwd:n,profileName:t?.profileName});for(let p of us(s))console.log(p.startsWith("\u2713")?j(p):p.startsWith("\u2139")?f(p):M(p));let c=Ut(Ot(n));return console.log(f(c?`Catching up on ${c}\u2026`:"Checking for code changes the builder didn't hint\u2026")),{kind:"forward-to-agent",prompt:ls({publish:r}),display:`/catch-up${r?" --publish":""}`}}W();import{spawn as Sp}from"node:child_process";var pt="document360-writer",lo=`npm i -g ${pt}@latest`;async function co(){let e=process.platform==="win32";return new Promise(t=>{try{let n=Sp(`npm i -g ${pt}@latest`,{stdio:"ignore",shell:!0,detached:!e,windowsHide:!0});n.on("error",o=>t({ok:!1,output:String(o)})),n.on("close",o=>t({ok:o===0,output:""})),e||n.unref()}catch(n){t({ok:!1,output:String(n)})}})}async function qa(){return console.log(f(`Updating ${pt}\u2026`)),(await co()).ok?console.log(j("\u2713 Updated. Exit (/exit) and relaunch d360-writer to apply.")):console.log($(`Update failed \u2014 run manually: ${lo}`)),{kind:"continue"}}W();ct();import{existsSync as ds,rmSync as Dp}from"node:fs";import{basename as Ba,resolve as ps}from"node:path";import{execFileSync as jp}from"node:child_process";import{input as Ap}from"@inquirer/prompts";import{readProjectConfig as Ep,devHintsGuidePath as Ip}from"document360-engine";var Np=[".d360-writer.json",".d360-writer","d360-category-map.json",".d360-capture.json",".d360-capture-cache"],_p=new Set([".d360-writer.json","d360-category-map.json",".d360-capture.json",".d360-capture-cache"]),uo=e=>(e??"").replace(/\\/g,"/").replace(/\/+$/,""),Ga=e=>e===".d360-writer"||e.startsWith(".d360-writer/");function Ya(e){let t=uo(e);return t===""||t.startsWith("..")||t.startsWith("/")||/^[a-zA-Z]:/.test(t)?!1:Ga(t)?!0:_p.has(t)}function fs(e){let t=null;try{t=Ep(e)}catch{t=null}let n=new Set(Np);for(let o of[uo(t?.docsDir),uo(t?.captureDir),uo(t?.outputDir)])o&&Ga(o)&&n.add(o);return[...n].filter(o=>Ya(o)&&ds(ps(e,o)))}function ms(e,t){let n=[],o=[];for(let r of t){if(!Ya(r)){o.push({path:r,error:"refused \u2014 outside .d360-writer/ (protected)"});continue}try{Dp(ps(e,r),{recursive:!0,force:!0}),n.push(r)}catch(s){o.push({path:r,error:s.message})}}return{removed:n,failed:o}}function Va(e,t){try{return jp("git",["status","--porcelain","--",t],{cwd:e,encoding:"utf8",stdio:["ignore","pipe","ignore"]}).split(`
|
|
247
|
+
`)}var Tp={status:Cp,plan:Pp,apply:Rp};async function cs(e,t=Tp){let n={pulled:[],conflicts:[],untrackedRemote:[],failed:[]},o;try{o=await t.status({cwd:e.cwd,profileName:e.profileName})}catch(r){return n.error=r.message,n}for(let r of o.entries)if(r.status==="conflict"&&r.path)n.conflicts.push(r.path);else if(r.status==="untracked-remote")n.untrackedRemote.push(r.title??r.articleId??"(untitled)");else if(r.status==="remote-ahead"&&r.path)try{let s=await t.plan({cwd:e.cwd,profileName:e.profileName,relPath:r.path});t.apply({cwd:e.cwd,profileName:e.profileName},s),n.pulled.push(r.path)}catch(s){n.failed.push({path:r.path,error:s.message})}return n}var is=(e,t=5)=>`${e.slice(0,t).join(", ")}${e.length>t?`, +${e.length-t} more`:""}`,ao=e=>e===1?"":"s";function us(e){if(e.error)return[`\u26A0 Couldn't check Document360 for portal changes (${e.error.slice(0,100)}) \u2014 proceeding; the agent will re-check.`];let t=[];return e.pulled.length&&t.push(`\u2713 Pulled ${e.pulled.length} portal edit${ao(e.pulled.length)} into local: ${is(e.pulled)}`),e.conflicts.length&&t.push(`\u26A0 ${e.conflicts.length} article${ao(e.conflicts.length)} changed both locally and in the portal \u2014 left untouched (resolve: /pull <path>): ${is(e.conflicts)}`),e.untrackedRemote.length&&t.push(`\u2139 ${e.untrackedRemote.length} new article${ao(e.untrackedRemote.length)} in the portal ${e.untrackedRemote.length===1?"isn't":"aren't"} local yet \u2014 run /pull to bring ${e.untrackedRemote.length===1?"it":"them"} in: ${is(e.untrackedRemote)}`),e.failed.length&&t.push(`\u26A0 ${e.failed.length} pull${ao(e.failed.length)} failed \u2014 left for /pull.`),t}async function Ha(e,t){let n=t?.cwd??process.cwd();if(!vp(n))return console.log($("No d360-writer config here. Run /init first.")),{kind:"continue"};let o=Le(n,t?.profileName);if(!o.ok)return console.log($(o.reason)),{kind:"continue"};let{publish:r}=as(e);console.log(f("Reconciling with Document360 (pulling any portal changes)\u2026"));let s=await cs({cwd:n,profileName:t?.profileName});for(let p of us(s))console.log(p.startsWith("\u2713")?j(p):p.startsWith("\u2139")?f(p):M(p));let c=Ut(Ot(n));return console.log(f(c?`Catching up on ${c}\u2026`:"Checking for code changes the builder didn't hint\u2026")),{kind:"forward-to-agent",prompt:ls({publish:r}),display:`/catch-up${r?" --publish":""}`}}W();import{spawn as Sp}from"node:child_process";var pt="document360-writer",lo=`npm i -g ${pt}@latest`;async function co(){let e=process.platform==="win32";return new Promise(t=>{try{let n=Sp(`npm i -g ${pt}@latest`,{stdio:"ignore",shell:!0,detached:!e,windowsHide:!0});n.on("error",o=>t({ok:!1,output:String(o)})),n.on("close",o=>t({ok:o===0,output:""})),e||n.unref()}catch(n){t({ok:!1,output:String(n)})}})}async function qa(){return console.log(f(`Updating ${pt}\u2026`)),(await co()).ok?console.log(j("\u2713 Updated. Exit (/exit) and relaunch d360-writer to apply.")):console.log($(`Update failed \u2014 run manually: ${lo}`)),{kind:"continue"}}W();ct();import{existsSync as ds,rmSync as Dp}from"node:fs";import{basename as Ba,resolve as ps}from"node:path";import{execFileSync as jp}from"node:child_process";import{input as Ap}from"@inquirer/prompts";import{readProjectConfig as Ep,devHintsGuidePath as Ip}from"document360-engine";var Np=[".d360-writer.json",".d360-writer","d360-category-map.json",".d360-capture.json",".d360-capture-cache"],_p=new Set([".d360-writer.json","d360-category-map.json",".d360-capture.json",".d360-capture-cache"]),uo=e=>(e??"").replace(/\\/g,"/").replace(/\/+$/,""),Ga=e=>e===".d360-writer"||e.startsWith(".d360-writer/");function Ya(e){let t=uo(e);return t===""||t.startsWith("..")||t.startsWith("/")||/^[a-zA-Z]:/.test(t)?!1:Ga(t)?!0:_p.has(t)}function fs(e){let t=null;try{t=Ep(e)}catch{t=null}let n=new Set(Np);for(let o of[uo(t?.docsDir),uo(t?.captureDir),uo(t?.outputDir)])o&&Ga(o)&&n.add(o);return[...n].filter(o=>Ya(o)&&ds(ps(e,o)))}function ms(e,t){let n=[],o=[];for(let r of t){if(!Ya(r)){o.push({path:r,error:"refused \u2014 outside .d360-writer/ (protected)"});continue}try{Dp(ps(e,r),{recursive:!0,force:!0}),n.push(r)}catch(s){o.push({path:r,error:s.message})}}return{removed:n,failed:o}}function Va(e,t){try{return jp("git",["status","--porcelain","--",t],{cwd:e,encoding:"utf8",stdio:["ignore","pipe","ignore"]}).split(`
|
|
248
248
|
`).filter(o=>o.trim()&&/\.md\s*$/.test(o)).length}catch{return 0}}function Mp(e){return Va(e,".d360-writer/hints")}function Lp(e){return Va(e,".d360-writer/messages")}function hs(e,t){if(t.length===0)return["Nothing to reset \u2014 no d360-writer files found in this repo."];let n=Mp(e),o=Lp(e),r=ds(ps(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?["Note: a repo-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: ${Ba(e)}`]}async function za(e,t){let n=fs(t.cwd);for(let g of hs(t.cwd,n))console.log(n.length===0?f(g):g);if(n.length===0)return{kind:"continue"};let o=Ba(t.cwd);if((await t.withPausedInput(()=>Ap({message:`Type "${o}" to delete (anything else cancels):`}).catch(()=>""))).trim()!==o)return console.log(M("Reset cancelled \u2014 nothing deleted.")),{kind:"continue"};let s=ds(Ip(t.cwd)),{removed:c,failed:p}=ms(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 g of p)console.log($(` \u2717 ${g.path}: ${g.error}`));if(s){for(let g of Vn())console.log(g);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"}}W();import{lintDocs as Op}from"document360-engine";function po(e){if(e.length===0)return["\u2713 No issues found \u2014 no leaked internals, unknown commands, or stale version facts in the docs."];let t=new Map;for(let o of e){let r=t.get(o.path)??[];r.push(o),t.set(o.path,r)}let n=[`\u26A0 ${e.length} doc issue${e.length===1?"":"s"} in ${t.size} article${t.size===1?"":"s"} (leaked internals / unknown commands / stale facts):`];for(let[o,r]of t){n.push(` ${o}`);for(let s of r)n.push(` L${s.line}: "${s.term}" \u2014 ${s.message}`)}return n}function gs(e){let t=new Map;for(let o of e){let r=t.get(o.path)??[];r.push(o),t.set(o.path,r)}return["The documentation quality check found issues in already-written articles. FIX each one by EDITING the","article in place \u2014 do not rewrite whole articles, just resolve the flagged problems. Ground every change","in the actual product source (the authoritativeSourceFiles); never invent.","","How to fix, by issue type:","- internal-leak (an agent codename, an internal skill name, or a d360_* tool name): this is the"," documentation engine's own machinery and must NOT appear in user docs. Reword the sentence to describe"," the behavior in plain user terms, or delete the internal aside. Do NOT swap it for another internal term.","- unknown-command: the named /command or --flag does not exist in the product source. Find the correct"," command/flag in the source and use it; if there is no real equivalent, remove the claim.","- stale-fact: the version contradicts the product's package.json engines \u2014 correct it to the real value.","","Rules: edit ONLY the listed articles; do not touch others; do not publish; introduce no new internal","nouns, invented commands, or wrong facts. When done, every listed issue should be gone.","","Issues:",[...t.entries()].map(([o,r])=>[o,...r.map(s=>` - L${s.line} [${s.kind}]: "${s.term}" \u2014 ${s.message}`)].join(`
|
|
249
249
|
`)).join(`
|
|
250
250
|
`)].join(`
|
package/package.json
CHANGED