document360-writer 0.4.15 → 0.4.16

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
@@ -40,7 +40,11 @@ No MCP servers registered. Add one with /mcp add <name> <type> <ref>
40
40
  `+bo(e.text,o);case"note":return`
41
41
  `+fi[e.tone](tt(e.text));case"done":return`
42
42
  `+(e.ok?N.magenta("\u2736 "):N.red("\u2736 "))+N.gray(`Cooked for ${it(e.seconds)} \xB7 ${e.tokens} tokens`+(e.costUsd>0?` \xB7 ${Me(e.costUsd)}`:""))}}function Yn(e,t){return e.map(o=>Po(o,t)).join(`
43
- `)}var To=3;function So(e){let t,o=!1;for(let r=0;r<e.length;r++){let s=e[r];s==="--run"||s==="--yes"?o=!0:s==="--scope"?t=e[++r]:s?.startsWith("--scope=")&&(t=s.slice(8))}return{scope:t?.replace(/\\/g,"/").replace(/\/+$/,"")||void 0,run:o}}function Ro(e,t){return t?e.filter(o=>{let n=o.replace(/\\/g,"/");return n===t||n.startsWith(`${t}/`)}):e}function jo(e,t){return t.map(o=>{let n=0;try{n=gi(hi(e,o)).size}catch{n=0}return{path:o,bytes:n}})}function Ao(e){return`Convert each of these articles to canonical Document360 Flavored Markdown (DFM) \u2014 wrap callouts, FAQs, tabs, accordions, and media embeds per the d360-markdown skill \u2014 then re-publish each as a DRAFT to Document360. Work ONLY on the files in this partition; do not read, touch, or convert any article outside this list:
43
+ `)}var To=3;function So(e){let t,o=!1;for(let r=0;r<e.length;r++){let s=e[r];s==="--run"||s==="--yes"?o=!0:s==="--scope"?t=e[++r]:s?.startsWith("--scope=")&&(t=s.slice(8))}return{scope:t?.replace(/\\/g,"/").replace(/\/+$/,"")||void 0,run:o}}function Ro(e,t){return t?e.filter(o=>{let n=o.replace(/\\/g,"/");return n===t||n.startsWith(`${t}/`)}):e}function jo(e,t){return t.map(o=>{let n=0;try{n=gi(hi(e,o)).size}catch{n=0}return{path:o,bytes:n}})}function Ao(e){return`Convert each of these articles to canonical Document360 Flavored Markdown (DFM) and re-publish each as a DRAFT. This is a mechanical syntax conversion, not a rewrite \u2014 be efficient:
44
+ - Read ONLY the article files listed below. Do NOT read source code, other articles, or fetch the live portal \u2014 you already have everything you need.
45
+ - Convert in place: wrap callouts, FAQs, tabs, accordions, and media embeds per the d360-markdown skill. Preserve all wording, headings, and structure; change only GFM\u2192DFM syntax.
46
+ - Publish each as a draft with a single update call. Do NOT re-read, re-verify, or polish after converting, and skip articles already in DFM.
47
+ - Work only on these files:
44
48
  `+e.paths.map(t=>`- ${t}`).join(`
45
49
  `)}function Io(e,t,o){let n=e.reduce((p,h)=>p+h.paths.length,0),[r,s]=t.usd;return[`Convert ${n} article${n===1?"":"s"} to DFM across ${e.length} partition${e.length===1?"":"s"} (\u2264${o} agents at once):`,...e.map(p=>` \u2022 ${p.label} \u2014 ${p.paths.length} article${p.paths.length===1?"":"s"}`),"",`Estimated cost: ${Me(r)}\u2013${Me(s)}. ${t.note}`,"","Each article is rewritten and re-published as a DRAFT. Run /convert --run to start."]}function Do(e,t){let o=e.filter(s=>s.ok),n=e.filter(s=>!s.ok),r=[`Converted ${o.length}/${e.length} partition${e.length===1?"":"s"} \xB7 ${Me(t)} total.`];if(n.length>0){r.push(`${n.length} failed \u2014 re-run /convert to retry:`);for(let s of n)r.push(` \u2717 ${s.label}${s.error?` \u2014 ${s.error}`:""}`)}return r}async function Xn(e,t){if(!yi(t.cwd))return console.log($("No .d360-writer.json here. Run /init first.")),{kind:"continue"};let{scope:o,run:n}=So(e),r=bi(t.cwd,t.profileName);if(r.length===0)return console.log($("No tracked articles in d360-category-map.json. Publish some first (/publish), then /convert.")),{kind:"continue"};let s=Ro(r,o);if(s.length===0)return console.log($(`No tracked articles under "${o}". (${r.length} are tracked overall.)`)),{kind:"continue"};let u=wi(s),p=`/convert${o?` --scope ${o}`:""} --run`,{model:h,forced:f}=xi(t.cwd,"light");if(!n){let x=ki({files:jo(t.cwd,s),op:"convert",model:h});o&&console.log(c(`Scope: ${o} (${s.length} of ${r.length} tracked articles).`));for(let T of Io(u,x,To))console.log(T);return console.log(c(`Model: ${h}${f?" (forced)":" \u2014 mechanical work; /model to override"}.`)),console.log(c(`Run ${p} to start.`)),console.log(""),{kind:"continue"}}console.log(c(`Converting ${s.length} articles across ${u.length} partitions (\u2264${To} agents at once) on ${h}\u2026`)),console.log(c(" (mid-run abort is TUI-only \u2014 Ctrl+C exits the REPL.)"));try{for await(let x of $i({cwd:t.cwd,partitions:u,promptFor:Ao,concurrency:To,profileName:t.profileName,allowProdWrites:t.allowProdWrites(),model:h}))if(x.type==="partition_status")x.status==="running"?console.log(c(` \u25B8 ${x.label} \u2014 converting\u2026`)):x.status==="done"?console.log(S(` \u2713 ${x.label}`)):console.log($(` \u2717 ${x.label}`));else if(x.type==="run_done"){console.log("");for(let T of Do(x.results,x.totalCostUsd))console.log(x.ok?S(T):D(T))}}catch(x){console.log($(`Convert run failed: ${x.message}`))}return console.log(""),{kind:"continue"}}L();import{search as vi}from"@inquirer/prompts";import{findByName as Ci,getSession as Pi,listSessions as Ti,relativeTime as Kn}from"document360-engine";async function Qn(e,t){let o=Ti(t.cwd).filter(n=>n.uuid!==t.currentUuid());if(o.length===0)return console.log(c("No saved sessions for this repo yet \u2014 sessions auto-save as you work.")),{kind:"continue"};if(e.length>0){let n=e.join(" "),r=Ci(t.cwd,n);return r?{kind:"resume",uuid:r.uuid,name:r.name}:(console.log($(`No session matches "${n}".`)),Jn(o),{kind:"continue"})}if(!process.stdin.isTTY)return Jn(o),console.log(c("Run: /resume <name>")),{kind:"continue"};try{let n=await t.withPausedInput(()=>vi({message:"Resume session (type to filter, \u2191\u2193 to navigate):",source:async s=>{let u=(s??"").toLowerCase();return o.filter(p=>!u||p.name.toLowerCase().includes(u)||p.firstPrompt.toLowerCase().includes(u)).map(p=>({name:`${p.name} ${Kn(p.updatedAt)}`,value:p.uuid,description:p.firstPrompt.slice(0,100)}))}})),r=Pi(n);return r?{kind:"resume",uuid:r.uuid,name:r.name}:{kind:"continue"}}catch{return console.log(""),{kind:"continue"}}}function Jn(e){console.log("");for(let t of e.slice(0,15))console.log(` ${y(t.name)} ${c(Kn(t.updatedAt))}`),console.log(` ${c(t.firstPrompt.slice(0,80))}`);console.log("")}L();import{renameSession as Si}from"document360-engine";async function Zn(e,t){let o=e.join(" ").trim();if(!o)return console.log($("Usage: /rename <new name>")),{kind:"continue"};let n=t.currentUuid();return n?(Si(n,o)?console.log(S(`\u2713 Session renamed to "${o}"`)):console.log($("Could not find the current session record.")),{kind:"continue"}):(console.log($("Nothing to rename yet \u2014 send a message first; sessions auto-save once the agent replies.")),{kind:"continue"})}import{knownEnvironments as er,readProjectConfig as Ri,writeProjectConfig as ji}from"document360-engine";L();function Eo(e,t,o){if(!t)return"Usage: /profile add <name> [environment]";let n=Ri(e);if(!n)return"No .d360-writer.json \u2014 run /init first.";if(n.profiles?.[t])return`Profile "${t}" already exists.`;let r=o??t;return er().includes(r)?(n.profiles={...n.profiles,[t]:{connection:{environment:r},production:!1}},ji(n,e),null):`Unknown environment "${r}". Known: ${er().join(", ")} (or add the profile with explicit URLs in .d360-writer.json).`}async function tr(e,t){let o=e[0];if(!o)return St(t.cwd),{kind:"continue"};if(o==="add"){let n=Eo(t.cwd,e[1],e[2]);return n?(console.log($(n)),{kind:"continue"}):(console.log(S(`\u2713 Profile "${e[1]}" created (environment: ${e[2]??e[1]}).`)),console.log(` Switch + sign in: ${y(`/profile ${e[1]}`)} then ${y("/login")}`),{kind:"continue"})}return Rt(t.cwd,o),jt(t.cwd,o),console.log(c(" Restarting agent for the new profile\u2026")),{kind:"clear"}}L();import{select as Ai}from"@inquirer/prompts";import{readProjectConfig as Ii,readUserConfig as or,resolveModelSetting as Mo,writeUserConfig as nr}from"document360-engine";var ce=[{value:null,label:"Auto",desc:"Engine right-sizes per task \u2014 Sonnet for routine work, Opus for analysis (recommended)"},{value:"claude-fable-5",label:"Fable",desc:"Fable 5 \xB7 most capable, for the hardest and longest-running tasks"},{value:"opus",label:"Opus",desc:"Opus 4.8 \xB7 best for everyday complex tasks"},{value:"sonnet",label:"Sonnet",desc:"Sonnet 4.6 \xB7 efficient for routine tasks"},{value:"haiku",label:"Haiku",desc:"Haiku 4.5 \xB7 fastest for quick answers"}];function Lt(e){if(e.model===null||e.source==="claude-settings")return 0;let t=e.model.toLowerCase(),o=ce.findIndex(n=>n.value!==null&&(n.value===t||n.label.toLowerCase()===t||t.includes(n.label.toLowerCase())));return o>=0?o:0}function Di(e){switch(e.source){case"project":return".d360-writer.json defaultModel (team setting)";case"user":return"~/.document360-writer/config.json (your /model setting)";case"env":return"ANTHROPIC_MODEL environment variable";case"claude-settings":return"Claude Code's own settings (~/.claude/settings.json)";case"claude-default":return"Claude Code default (no override configured)"}}function lt(e,t){let o=()=>{let s=Mo(e);return s.source==="project"||s.source==="user"||s.source==="env"?s.model??void 0:void 0};if(t==="default"){let s=or();return s.defaultModel?(delete s.defaultModel,nr(s),{lines:[`\u2713 Personal model override cleared \u2014 now: ${Mo(e).model??"Claude Code default"} (applies from your next message)`],changed:!0,effective:o()}):{lines:["No personal model override set \u2014 nothing to clear."],changed:!1,effective:void 0}}nr({...or(),defaultModel:t});let n=[`\u2713 Personal model set to "${t}" (applies from your next message \u2014 conversation continues)`],r=Ii(e)?.defaultModel;return r&&n.push(`\u26A0 .d360-writer.json sets defaultModel "${r}" \u2014 the team setting overrides yours until it is removed.`),{lines:n,changed:!0,effective:o()}}async function rr(e,t){let o=e[0]?.trim();if(!o){let u=Mo(t.cwd);if(!process.stdin.isTTY)return console.log(`${re("Model:")} ${y(u.model??"Claude Code default")}`),console.log(c(` source: ${Di(u)}`)),console.log(c(" change: /model <haiku|sonnet|opus|full-model-id> \xB7 reset: /model default")),{kind:"continue"};let p=Lt(u),h;try{h=await t.withPausedInput(()=>Ai({message:`Select model (current: ${u.model??"Claude Code default"})`,default:ce[p].value,choices:ce.map((v,j)=>({name:`${v.label}${j===p?" \u2714":""}`,value:v.value,description:v.desc}))}))}catch{return console.log(c("Cancelled.")),{kind:"continue"}}let{lines:f,changed:x,effective:T}=lt(t.cwd,h??"default");for(let v of f)console.log(v.startsWith("\u26A0")?D(v):v.startsWith("\u2713")?S(v):c(v));return x&&await t.setModel(T),{kind:"continue"}}let{lines:n,changed:r,effective:s}=lt(t.cwd,o);for(let u of n)console.log(u.startsWith("\u26A0")?D(u):u.startsWith("\u2713")?S(u):c(u));return r&&await t.setModel(s),{kind:"continue"}}Nt();async function cr(e,t){return await t.withPausedInput(()=>qe(t.cwd)),{kind:"clear"}}L();import{resolveActiveProfile as Hi}from"document360-engine";async function ur(e,t){let o=!1;try{o=Hi(t.cwd).production}catch{}return o?(console.log(D("\u26A0 Authorizing writes to the PRODUCTION profile for this session.")),{kind:"allow-prod"}):(console.log(c("Current profile is not a production profile \u2014 writes are already allowed.")),{kind:"continue"})}L();var dr=async(e,t)=>{try{await t.withPausedInput(()=>Tt({}))}catch(o){console.log($(`Login failed: ${o.message}`))}return{kind:"continue"}};L();async function Ot(e){let t=e[0];return t?{kind:"forward-to-agent",prompt:[`Run the emit-screenshot-spec skill for placeholder id: ${t}`,"","Steps you must follow:","1. Locate the SCREENSHOT HTML comment block with this id across user-docs/**/*.md.","2. Translate the placeholder.steps into Playwright actions using STABLE selectors only (data-testid > aria-label > role+name > visible text). If no stable selector is available for a required action, write `test.skip(...)` plus a TODO comment naming the React component that needs a data-testid.",'3. Write the spec file to <captureDir>/<id>.spec.ts. Use `import { test } from "@playwright/test"` and `import { waitPastLogin, dumpAnnotations, type Placeholder } from "document360-capture/helpers"`.',"4. Use process.env.CAPTURE_START_URL and process.env.CAPTURE_AUTH_BOUNDARY (these are injected at run time by document360-capture; do not hardcode them).","5. Report the path of the generated spec and any TODOs from missing selectors."].join(`
46
50
  `),display:`/screenshot ${t}`}:(console.log($("Usage: /screenshot <placeholder-id>")),{kind:"continue"})}var pr={help:mo,"?":mo,clear:$n,exit:fo,quit:fo,init:Tn,mcp:At,publish:Dn,audit:Et,scope:Mn,sync:Fn,convert:Xn,resume:Qn,rename:Zn,profile:tr,model:rr,doctor:at,workspace:cr,"allow-prod":ur,login:dr,screenshot:Ot};function mr(e){let t=e.trim();if(!t.startsWith("/"))return null;let o=t.slice(1).split(/\s+/),n=(o[0]??"").toLowerCase();return n?{name:n,args:o.slice(1)}:null}L();var qi={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"},gr=160,No=200,hr=40;function Z(e,t){let o=e.replace(/\s+/g," ").trim();return o.length<=t?o:o.slice(0,t-1)+"\u2026"}function fr(e){let t=Object.entries(e).filter(([,n])=>n!=null&&n!=="");if(t.length===0)return null;let o=t.slice(0,4).map(([n,r])=>`${n}: ${Z(typeof r=="string"?r:JSON.stringify(r),hr)}`);return t.length>4&&o.push("\u2026"),Z(o.join(", "),gr)}var ve=e=>typeof e=="string"&&e?e:null,Lo=e=>typeof e=="string"&&e.length>=8?e.slice(0,8):null;function zi(e){let t=ve(e)?.replace(/\\/g,"/");if(!t)return null;let o=t.split("/").filter(Boolean);if(o.length<2)return null;let n=o[o.length-2],r=n.replace(/^\d+[-_.]/,"").split(/[-_]/).filter(Boolean);return r.length===0?n:r.map(s=>s.charAt(0).toUpperCase()+s.slice(1)).join(" ")}function Gi(e,t){let o=(n,r)=>({title:`Document360: ${n}`,sep:" ",arg:r});switch(e){case"d360_create_article":{let n=ve(t.title);if(!n)return null;let r=zi(t.local_path);return o("Create article",`"${Z(n,60)}"${r?` in ${r}`:""}`)}case"d360_update_article":{let n=ve(t.title),r=Lo(t.article_id);return o("Update article",n?`"${Z(n,60)}"`:r?`id ${r}\u2026`:null)}case"d360_fork_article":return o("Fork article (new draft)",Lo(t.article_id)?`id ${Lo(t.article_id)}\u2026`:null);case"d360_publish_article":{let n=t.version_number;return o("Publish article LIVE",typeof n=="number"?`v${n}`:null)}case"d360_unpublish_article":return o("Unpublish article",null);case"d360_create_category":return o("Create category",ve(t.name)?`"${Z(ve(t.name),60)}"`:null);case"d360_upload_drive_file":{let n=ve(t.file_path);return o("Upload image",n?Z(n.replace(/\\/g,"/").split("/").pop()??n,60):null)}case"d360_sync_status":return o("Check sync status",null);default:return null}}function Ut(e,t){if(e==="ToolSearch")return null;if(e.startsWith("mcp__")){let[,r="",...s]=e.split("__"),u=s.join("__");if(r==="document360"){let f=Gi(u,t);if(f)return f}let p=u.replace(/^d360_/,"").replace(/_/g," ");return{title:`${r==="document360"?"Document360":r.charAt(0).toUpperCase()+r.slice(1)}: ${p}`,sep:" ",arg:fr(t)}}let o=qi[e],n=o?t[o]:void 0;return typeof n=="string"&&n?{title:e,sep:"",arg:Z(n,gr)}:{title:e,sep:"",arg:fr(t)}}function Vi(e){if(e===null||typeof e!="object")return typeof e=="string"?e:null;let t=e;for(let o of["name","title","slug","id"])if(typeof t[o]=="string"&&t[o])return t[o];return null}function Yi(e){if(!/^[[{]/.test(e))return null;let t;try{t=JSON.parse(e)}catch{return null}if(Array.isArray(t)){let o=t.map(Vi).filter(r=>r!==null),n=`${t.length} item${t.length===1?"":"s"}`;return o.length===0?[n]:[n,...o.map(r=>Z(r,No))]}if(t!==null&&typeof t=="object"){let o=Object.entries(t).filter(([,n])=>n!==null&&(typeof n=="string"||typeof n=="number"||typeof n=="boolean")).slice(0,6).map(([n,r])=>`${n}: ${Z(String(r),hr)}`);return o.length>0?[Z(o.join(" \xB7 "),No)]:null}return null}function Xi(e){let t=ve(e)?.replace(/\\/g,"/");if(!t)return null;let o=t.split("/").filter(Boolean);return o.length>1?o.slice(1).join("/"):t}function Ji(e,t,o){switch(e){case"d360_create_article":{let n=Xi(t?.local_path);return[`\u2713 draft created${n?` \xB7 ${n}`:""}`]}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 n=ve(t?.name);return[`\u2713 category created${n?` \xB7 "${n}"`:""}`]}case"d360_upload_drive_file":{let n=o.match(/https?:\/\/\S+/);return[`\u2713 uploaded${n?` \xB7 ${Z(n[0],120)}`:""}`]}default:return null}}function Wt(e,t=4,o,n){let r=e.replace(/\r\n/g,`
@@ -13,7 +13,8 @@ export declare function sizeTargets(cwd: string, paths: string[]): {
13
13
  path: string;
14
14
  bytes: number;
15
15
  }[];
16
- /** The agent prompt for one partition. Scopes the agent strictly to this slice. */
16
+ /** The agent prompt for one partition. Mechanical + scoped + efficient keep the agent from
17
+ over-exploring (the main driver of slow convert runs): no source reads, no re-verification. */
17
18
  export declare function buildConvertPrompt(partition: Partition): string;
18
19
  /** Preview lines for bare `/convert`: the partition plan + the cost band, before any spend. */
19
20
  export declare function previewLines(partitions: Partition[], estimate: CostEstimate, concurrency: number): string[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "document360-writer",
3
- "version": "0.4.15",
3
+ "version": "0.4.16",
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.8",
37
+ "document360-engine": "^0.2.9",
38
38
  "ink": "^5.2.1",
39
39
  "picocolors": "^1.1.1",
40
40
  "react": "^18.3.1",