@mod-computer/mod 0.2.2 → 0.2.3

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/cli.bundle.js +33 -7
  2. package/package.json +1 -1
package/cli.bundle.js CHANGED
@@ -763,7 +763,7 @@ Run without --dry-run to delete.`)}import gst from"fs/promises";import BL from"p
763
763
  `+r:""),l=Math.floor(o.length/s)-this.cursorPos.rows+(r?oAr(r):0);l>0&&(a+=Ist(l)),a+=_st(this.cursorPos.cols),this.write(Cst(this.extraLinesUnderPrompt)+kst(this.height)+a),this.extraLinesUnderPrompt=l,this.height=oAr(a)}checkCursorPos(){let t=this.rl.getCursorPos();t.cols!==this.cursorPos.cols&&(this.write(_st(t.cols)),this.cursorPos=t)}done({clearContent:t}){this.rl.setPrompt("");let r=Cst(this.extraLinesUnderPrompt);r+=t?kst(this.height):`
764
764
  `,r+=nAr,this.write(r),this.rl.close()}};var dke=class extends Promise{static withResolver(){let t,r;return{promise:new Promise((o,i)=>{t=o,r=i}),resolve:t,reject:r}}};function Seo(){let e=Error.prepareStackTrace,t=[];try{Error.prepareStackTrace=(r,n)=>{let o=n.slice(1);return t=o,o},new Error().stack}catch{return t}return Error.prepareStackTrace=e,t}function UL(e){let t=Seo();return(n,o={})=>{let{input:i=process.stdin,signal:s}=o,a=new Set,c=new sAr.default;c.pipe(o.output??process.stdout);let l=iAr.createInterface({terminal:!0,input:i,output:c}),d=new pie(l),{promise:m,resolve:h,reject:A}=dke.withResolver(),b=()=>A(new lke);if(s){let _=()=>A(new cke({cause:s.reason}));if(s.aborted)return _(),Object.assign(m,{cancel:b});s.addEventListener("abort",_),a.add(()=>s.removeEventListener("abort",_))}a.add(K_e((_,D)=>{A(new uie(`User force closed the prompt with ${_} ${D}`))}));let w=()=>A(new uie("User force closed the prompt with SIGINT"));l.on("SIGINT",w),a.add(()=>l.removeListener("SIGINT",w));let v=()=>d.checkCursorPos();return l.input.on("keypress",v),a.add(()=>l.input.removeListener("keypress",v)),zgr(l,_=>{let D=weo.bind(()=>NL.clearAll());return l.on("close",D),a.add(()=>l.removeListener("close",D)),_(()=>{var R;try{let M=e(n,F=>{setImmediate(()=>h(F))});if(M===void 0){let F=(R=t[1])==null?void 0:R.getFileName();throw new Error(`Prompt functions must return a string.
765
765
  at ${F}`)}let[P,U]=typeof M=="string"?[M]:M;d.render(P,U),NL.run()}catch(M){A(M)}}),Object.assign(m.then(R=>(NL.clearAll(),R),R=>{throw NL.clearAll(),R}).finally(()=>{a.forEach(R=>R()),d.done({clearContent:!!o.clearPromptOnDone}),c.end()}).then(()=>m),{cancel:b})})}}import{styleText as Ieo}from"node:util";var mT=class{separator=Ieo("dim",Array.from({length:15}).join(f5.line));type="separator";constructor(t){t&&(this.separator=t)}static isSeparator(t){return!!(t&&typeof t=="object"&&"type"in t&&t.type==="separator")}};function aAr(e,t){let r=t!==!1;return/^(y|yes)/i.test(e)?r=!0:/^(n|no)/i.test(e)&&(r=!1),r}function cAr(e){return e?"Yes":"No"}var Tst=UL((e,t)=>{let{transformer:r=cAr}=e,[n,o]=Up("idle"),[i,s]=Up(""),a=hT(e.theme),c=PL({status:n,theme:a});LL((h,A)=>{if(n==="idle")if(ML(h)){let b=aAr(i,e.default);s(r(b)),o("done"),t(b)}else if(lie(h)){let b=cAr(!aAr(i,e.default));A.clearLine(0),A.write(b),s(b)}else s(A.line)});let l=i,d="";n==="done"?l=a.style.answer(i):d=` ${a.style.defaultAnswer(e.default===!1?"y/N":"Y/n")}`;let m=a.style.message(e.message,n);return`${c} ${m}${d} ${l}`});var Ceo={validationFailureMode:"keep"},Dst=UL((e,t)=>{let{prefill:r="tab"}=e,n=hT(Ceo,e.theme),[o,i]=Up("idle"),[s="",a]=Up(e.default),[c,l]=Up(),[d,m]=Up(""),h=PL({status:o,theme:n});async function A(D){let{required:R,pattern:M,patternError:P="Invalid input"}=e;return R&&!D?"You must provide a value":M&&!M.test(D)?P:typeof e.validate=="function"?await e.validate(D)||"You must provide a valid value":!0}LL(async(D,R)=>{if(o==="idle")if(ML(D)){let M=d||s;i("loading");let P=await A(M);P===!0?(m(M),i("done"),t(M)):(n.validationFailureMode==="clear"?m(""):R.write(d),l(P),i("idle"))}else cie(D)&&!d?a(void 0):lie(D)&&!d?(a(void 0),R.clearLine(0),R.write(s),m(s)):(m(R.line),l(void 0))}),pT(D=>{r==="editable"&&s&&(D.write(s),m(s))},[]);let b=n.style.message(e.message,o),w=d;typeof e.transformer=="function"?w=e.transformer(d,{isFinal:o==="done"}):o==="done"&&(w=n.style.answer(d));let v;s&&o!=="done"&&!d&&(v=n.style.defaultAnswer(s));let _="";return c&&(_=n.style.error(c)),[[h,b,v,w].filter(D=>D!==void 0).join(" "),_]});import{styleText as hie}from"node:util";var _eo={icon:{cursor:f5.pointer},style:{disabled:e=>hie("dim",`- ${e}`),description:e=>hie("cyan",e),keysHelpTip:e=>e.map(([t,r])=>`${hie("bold",t)} ${hie("dim",r)}`).join(hie("dim"," \u2022 "))},indexMode:"hidden",keybindings:[]};function d5(e){return!mT.isSeparator(e)&&!e.disabled}function keo(e){return e.map(t=>{if(mT.isSeparator(t))return t;if(typeof t!="object"||t===null||!("value"in t)){let o=String(t);return{value:t,name:o,short:o,disabled:!1}}let r=t.name??String(t.value),n={value:t.value,name:r,short:t.short??r,disabled:t.disabled??!1};return t.description&&(n.description=t.description),n})}var Rst=UL((e,t)=>{let{loop:r=!0,pageSize:n=7}=e,o=hT(_eo,e.theme),{keybindings:i}=o,[s,a]=Up("idle"),c=PL({status:s,theme:o}),l=QL(),d=!i.includes("vim"),m=fie(()=>keo(e.choices),[e.choices]),h=fie(()=>{let F=m.findIndex(d5),L=m.findLastIndex(d5);if(F===-1)throw new l5("[select prompt] No selectable choices. All choices are disabled.");return{first:F,last:L}},[m]),A=fie(()=>"default"in e?m.findIndex(F=>d5(F)&&F.value===e.default):-1,[e.default,m]),[b,w]=Up(A===-1?h.first:A),v=m[b];LL((F,L)=>{if(clearTimeout(l.current),ML(F))a("done"),t(v.value);else if(aie(F,i)||ake(F,i)){if(L.clearLine(0),r||aie(F,i)&&b!==h.first||ake(F,i)&&b!==h.last){let j=aie(F,i)?-1:1,G=b;do G=(G+j+m.length)%m.length;while(!d5(m[G]));w(G)}}else if(bst(F)&&!Number.isNaN(Number(L.line))){let j=Number(L.line)-1,G=-1,ee=m.findIndex(re=>mT.isSeparator(re)?!1:(G++,G===j)),Z=m[ee];Z!=null&&d5(Z)&&w(ee),l.current=setTimeout(()=>{L.clearLine(0)},700)}else if(cie(F))L.clearLine(0);else if(d){let j=L.line.toLowerCase(),G=m.findIndex(ee=>mT.isSeparator(ee)||!d5(ee)?!1:ee.name.toLowerCase().startsWith(j));G!==-1&&w(G),l.current=setTimeout(()=>{L.clearLine(0)},700)}}),pT(()=>()=>{clearTimeout(l.current)},[]);let _=o.style.message(e.message,s),D=o.style.keysHelpTip([["\u2191\u2193","navigate"],["\u23CE","select"]]),R=0,M=wst({items:m,active:b,renderItem({item:F,isActive:L,index:j}){if(mT.isSeparator(F))return R++,` ${F.separator}`;let G=o.indexMode==="number"?`${j+1-R}. `:"";if(F.disabled){let re=typeof F.disabled=="string"?F.disabled:"(disabled)";return o.style.disabled(`${G}${F.name} ${re}`)}let ee=L?o.style.highlight:re=>re,Z=L?o.icon.cursor:" ";return ee(`${Z} ${G}${F.name}`)},pageSize:n,loop:r});if(s==="done")return[c,_,o.style.answer(v.short)].filter(Boolean).join(" ");let{description:P}=v;return`${[[c,_].filter(Boolean).join(" "),M," ",P?o.style.description(P):"",D].filter(Boolean).join(`
766
- `).trimEnd()}${rAr}`});async function gT(e,t){return await Rst({message:e,choices:t.map(n=>({name:n.label,value:n.value,description:n.description}))})}async function pke(e,t){return await Dst({message:e,default:t==null?void 0:t.default,validate:t!=null&&t.validate?n=>t.validate(n)??!0:void 0})}async function Ost(e,t){return await Tst({message:e,default:(t==null?void 0:t.default)??!0})}function p5(e){return!e||e.trim().length===0?"Workspace name cannot be empty":e.length>100?"Workspace name must be 100 characters or less":/^[a-zA-Z0-9][a-zA-Z0-9-_ ]*[a-zA-Z0-9]$|^[a-zA-Z0-9]$/.test(e.trim())?null:"Workspace name must start and end with alphanumeric characters"}Xu();tie();Xu();async function Bst(e,t){let r=(t==null?void 0:t.timeout)??3e4,n=(t==null?void 0:t.verbose)??!1,o=n?console.log.bind(console):()=>{};o("[fetch] Creating temporary network repo...");let i=E7(),s=nle(),a=new Set;a.add(e);let c=new NM({storage:new R3(i),network:[new CD(s)],sharePolicy:async(l,d)=>d?a.has(d):!1});o("[fetch] Waiting for WebSocket connection..."),await new Promise(l=>setTimeout(l,500));try{o(`[fetch] Fetching workspace document: ${e}`);let l=await c.find(e);await Promise.race([l.whenReady(),new Promise((_,D)=>setTimeout(()=>D(new Error("Timeout waiting for workspace document")),r))]);let d=l.doc();if(!d)throw new Error("Workspace document is empty after fetch");let m=d.title||d.name||"Untitled";o(`[fetch] Workspace loaded: ${m}`);let h=d.fileRefs||[],A=d.folderRefs||[];o(`[fetch] Found ${h.length} files, ${A.length} folders`);let b=h.map(_=>_.id||_.documentId).filter(Boolean),w=A.map(_=>_.id||_.documentId).filter(Boolean);for(let _ of[...b,...w])a.add(_);o(`[fetch] Fetching ${b.length} file documents...`);let v=0;for(let _ of b)try{let D=await c.find(_);await Promise.race([D.whenReady(),new Promise((R,M)=>setTimeout(()=>M(new Error(`Timeout fetching file ${_}`)),1e4))]),v++,n&&v%10===0&&o(`[fetch] Fetched ${v}/${b.length} files...`)}catch(D){o(`[fetch] Warning: Could not fetch file ${_}: ${D}`)}o(`[fetch] Fetching ${w.length} folder documents...`);for(let _ of w)try{let D=await c.find(_);await Promise.race([D.whenReady(),new Promise((R,M)=>setTimeout(()=>M(new Error(`Timeout fetching folder ${_}`)),1e4))])}catch(D){o(`[fetch] Warning: Could not fetch folder ${_}: ${D}`)}return o("[fetch] Waiting for storage flush..."),await new Promise(_=>setTimeout(_,1e3)),TL(e),Jit([...b,...w]),o(`[fetch] Fetch complete: ${v} files, ${w.length} folders`),{workspaceId:e,workspaceName:m,fileCount:v,folderCount:w.length}}finally{o("[fetch] Shutting down temporary repo...");try{await new Promise(l=>setTimeout(l,500))}catch(l){o(`[fetch] Warning during shutdown: ${l}`)}}}async function uAr(e,t){let r=e.includes("--force"),n=process.cwd();if(!qW(qn.AUTH))return lAr(n,r);let o=e.indexOf("--workspace"),i=e.indexOf("--create"),s=o!==-1?e[o+1]:null,a=i!==-1?e[i+1]:null;try{oF();let c=is(n);if(c&&!r){console.log("Already initialized"),console.log(`Workspace: ${c.workspaceName}`),console.log("");let h=await gT("What would you like to do?",[{label:"Resume file import (if interrupted)",value:"resume"},{label:"Start syncing",value:"sync"},{label:"Reinitialize with different workspace",value:"force"}]);if(h==="resume"){console.log("Checking for files to import...");try{await mke(t,{id:c.workspaceId}),console.log("Resume complete")}catch(A){console.error("Resume failed:",A instanceof Error?A.message:A),process.exit(1)}}else h==="sync"?console.log("Run `mod sync start` to begin syncing"):h==="force"&&console.log("Reinitializing...");h!=="force"&&process.exit(0)}let l=sa();Cx.existsSync(y7())||QE(l);let d=!!l.auth;if(!d&&!a&&!s)return lAr(n,r);let m;d?m=await Teo(t,l.auth.email,{workspaceName:s,createName:a}):m=await Deo(t,{createName:a}),m.lastSyncedAt=new Date().toISOString(),w_(n,m),fAr(),console.log("Installed /mod skill to .claude/skills/mod/"),Beo(m,d),process.exit(0)}catch(c){console.error("Initialization failed:",c.message),process.exit(1)}}async function Teo(e,t,r={}){var l,d,m;console.log(`Signed in as ${t}`);let n=sa(),o=[];try{let h=(l=n.auth)==null?void 0:l.userDocId;if(!h)console.warn("User document not found. Creating local workspace.");else{let A=await e.find(h);await A.whenReady();let b=A.doc();if(b){let w=b.workspaceIds||[];for(let v of w)try{let _=await e.find(v);await _.whenReady();let D=_.doc();o.push({id:v,name:(D==null?void 0:D.title)||(D==null?void 0:D.name)||"Untitled"})}catch{o.push({id:v,name:`Workspace ${String(v).slice(0,8)}`})}}}}catch{console.warn("Could not load cloud workspaces. Creating local workspace.")}if(r.createName)return console.log(`Creating workspace: ${r.createName}`),await hke(e,r.createName);if(r.workspaceName){let h=o.find(_=>_.name.toLowerCase()===r.workspaceName.toLowerCase());if(!h)throw new Error(`Workspace "${r.workspaceName}" not found. Available: ${o.map(_=>_.name).join(", ")||"(none)"}`);console.log(`Selected workspace: ${h.name}`),console.log("Fetching workspace from server...");try{let _=await Bst(h.id,{verbose:!1});console.log(`Fetched ${_.fileCount} files, ${_.folderCount} folders`)}catch(_){console.warn(`Warning: Could not fetch workspace from server: ${_ instanceof Error?_.message:_}`),console.warn("Workspace will sync when daemon starts.")}let A={type:"existing",id:h.id,name:h.name};TL(A.id);let b=process.cwd(),w={path:b,workspaceId:A.id,workspaceName:A.name,connectedAt:new Date().toISOString(),lastSyncedAt:new Date().toISOString()};w_(b,w),console.log("Workspace connection saved");let v=sa();if((d=v.auth)!=null&&d.userDocId)try{await Sx(e).addWorkspace(v.auth.userDocId,A.id)}catch{}return await mke(e,{id:A.id}),w}let i=[...o.map(h=>({label:h.name,value:{type:"existing",id:h.id,name:h.name}})),{label:"+ Create new workspace",value:{type:"create",id:"",name:""}}],s=await gT("Select workspace:",i);if(s.type==="create")return await hke(e);console.log("Fetching workspace from server...");try{let h=await Bst(s.id,{verbose:!1});console.log(`Fetched ${h.fileCount} files, ${h.folderCount} folders`)}catch(h){console.warn(`Warning: Could not fetch workspace from server: ${h instanceof Error?h.message:h}`),console.warn("Workspace will sync when daemon starts.")}TL(s.id);let a=process.cwd(),c={path:a,workspaceId:s.id,workspaceName:s.name,connectedAt:new Date().toISOString(),lastSyncedAt:new Date().toISOString()};if(w_(a,c),console.log("Workspace connection saved"),(m=n.auth)!=null&&m.userDocId)try{await Sx(e).addWorkspace(n.auth.userDocId,s.id)}catch{}return await mke(e,{id:s.id}),c}async function Deo(e,t={}){return t.createName?(console.log(`Creating workspace: ${t.createName}`),await hke(e,t.createName)):(await gT("Select option:",[{label:"Create local workspace",value:"create",description:"Work offline, sync later"},{label:"Sign in to sync with team",value:"signin",description:"Access cloud workspaces"}])==="signin"&&(console.log(""),console.log("Please run `mod auth login` to sign in, then run `mod init` again."),process.exit(0)),await hke(e))}async function hke(e,t){var d;let r=$L.basename(process.cwd()),n=process.cwd(),o=r.charAt(0).toUpperCase()+r.slice(1),i;if(t){let m=p5(t);if(m)throw new Error(`Invalid workspace name: ${m}`);i=t}else i=await pke("Workspace name",{default:o,validate:p5});console.log("Creating workspace...");let a=await gs(e).createWorkspace({name:i});TL(a.id);let c={path:n,workspaceId:a.id,workspaceName:a.name,connectedAt:new Date().toISOString(),lastSyncedAt:new Date().toISOString()};w_(n,c),console.log("Workspace connection saved"),console.log("Syncing workspace..."),await new Promise(m=>setTimeout(m,2e3));let l=sa();if((d=l.auth)!=null&&d.userDocId)try{await Sx(e).addWorkspace(l.auth.userDocId,a.id),await new Promise(h=>setTimeout(h,1e3)),console.log("Workspace registered for sync")}catch{console.warn("Note: Could not register workspace for cross-device sync")}return await mke(e,a),console.log("Workspace synced"),c}async function mke(e,t){let n=await new s5(e).execute({paths:["."]},i=>{i.phase==="scanning"?process.stdout.write(`\rScanning... ${i.total} files found`):i.phase==="adding"&&process.stdout.write(`\rImporting... ${i.current}/${i.total} files`)});if(process.stdout.write("\r"+" ".repeat(60)+"\r"),n.summary.totalFiles===0){console.log("No files to import");return}let o=[];n.summary.created>0&&o.push(`${n.summary.created} created`),n.summary.unchanged>0&&o.push(`${n.summary.unchanged} unchanged`),n.summary.skipped>0&&o.push(`${n.summary.skipped} skipped`),n.summary.errors>0&&o.push(`${n.summary.errors} errors`),console.log(`Imported ${n.summary.totalFiles} files (${o.join(", ")})`)}function lAr(e,t){let r=$L.join(e,".mod","traces");!t&&Cx.existsSync(r)&&(console.log("Already initialized for local development."),console.log("Run `mod init --force` to reinitialize."),process.exit(0)),Cx.mkdirSync(r,{recursive:!0});let n=$L.join(e,"glassware.toml");Cx.existsSync(n)||(Cx.writeFileSync(n,`# Trace schema configuration
766
+ `).trimEnd()}${rAr}`});async function gT(e,t){return await Rst({message:e,choices:t.map(n=>({name:n.label,value:n.value,description:n.description}))})}async function pke(e,t){return await Dst({message:e,default:t==null?void 0:t.default,validate:t!=null&&t.validate?n=>t.validate(n)??!0:void 0})}async function Ost(e,t){return await Tst({message:e,default:(t==null?void 0:t.default)??!0})}function p5(e){return!e||e.trim().length===0?"Workspace name cannot be empty":e.length>100?"Workspace name must be 100 characters or less":/^[a-zA-Z0-9][a-zA-Z0-9-_ ]*[a-zA-Z0-9]$|^[a-zA-Z0-9]$/.test(e.trim())?null:"Workspace name must start and end with alphanumeric characters"}Xu();tie();Xu();async function Bst(e,t){let r=(t==null?void 0:t.timeout)??3e4,n=(t==null?void 0:t.verbose)??!1,o=n?console.log.bind(console):()=>{};o("[fetch] Creating temporary network repo...");let i=E7(),s=nle(),a=new Set;a.add(e);let c=new NM({storage:new R3(i),network:[new CD(s)],sharePolicy:async(l,d)=>d?a.has(d):!1});o("[fetch] Waiting for WebSocket connection..."),await new Promise(l=>setTimeout(l,500));try{o(`[fetch] Fetching workspace document: ${e}`);let l=await c.find(e);await Promise.race([l.whenReady(),new Promise((_,D)=>setTimeout(()=>D(new Error("Timeout waiting for workspace document")),r))]);let d=l.doc();if(!d)throw new Error("Workspace document is empty after fetch");let m=d.title||d.name||"Untitled";o(`[fetch] Workspace loaded: ${m}`);let h=d.fileRefs||[],A=d.folderRefs||[];o(`[fetch] Found ${h.length} files, ${A.length} folders`);let b=h.map(_=>_.id||_.documentId).filter(Boolean),w=A.map(_=>_.id||_.documentId).filter(Boolean);for(let _ of[...b,...w])a.add(_);o(`[fetch] Fetching ${b.length} file documents...`);let v=0;for(let _ of b)try{let D=await c.find(_);await Promise.race([D.whenReady(),new Promise((R,M)=>setTimeout(()=>M(new Error(`Timeout fetching file ${_}`)),1e4))]),v++,n&&v%10===0&&o(`[fetch] Fetched ${v}/${b.length} files...`)}catch(D){o(`[fetch] Warning: Could not fetch file ${_}: ${D}`)}o(`[fetch] Fetching ${w.length} folder documents...`);for(let _ of w)try{let D=await c.find(_);await Promise.race([D.whenReady(),new Promise((R,M)=>setTimeout(()=>M(new Error(`Timeout fetching folder ${_}`)),1e4))])}catch(D){o(`[fetch] Warning: Could not fetch folder ${_}: ${D}`)}return o("[fetch] Waiting for storage flush..."),await new Promise(_=>setTimeout(_,1e3)),TL(e),Jit([...b,...w]),o(`[fetch] Fetch complete: ${v} files, ${w.length} folders`),{workspaceId:e,workspaceName:m,fileCount:v,folderCount:w.length}}finally{o("[fetch] Shutting down temporary repo...");try{await new Promise(l=>setTimeout(l,500))}catch(l){o(`[fetch] Warning during shutdown: ${l}`)}}}async function uAr(e,t){let r=e.includes("--force"),n=process.cwd();if(!qW(qn.AUTH))return lAr(n,r);let o=e.indexOf("--workspace"),i=e.indexOf("--create"),s=o!==-1?e[o+1]:null,a=i!==-1?e[i+1]:null;try{oF();let c=is(n);if(c&&!r){console.log("Already initialized"),console.log(`Workspace: ${c.workspaceName}`),console.log("");let h=await gT("What would you like to do?",[{label:"Resume file import (if interrupted)",value:"resume"},{label:"Start syncing",value:"sync"},{label:"Reinitialize with different workspace",value:"force"}]);if(h==="resume"){console.log("Checking for files to import...");try{await mke(t,{id:c.workspaceId}),console.log("Resume complete")}catch(A){console.error("Resume failed:",A instanceof Error?A.message:A),process.exit(1)}}else h==="sync"?console.log("Run `mod sync start` to begin syncing"):h==="force"&&console.log("Reinitializing...");h!=="force"&&process.exit(0)}let l=sa();Cx.existsSync(y7())||QE(l);let d=!!l.auth;if(!d&&!a&&!s)return lAr(n,r);let m;d?m=await Teo(t,l.auth.email,{workspaceName:s,createName:a}):m=await Deo(t,{createName:a}),m.lastSyncedAt=new Date().toISOString(),w_(n,m),fAr(),console.log("Installed mod skill to .claude/skills/mod/"),Beo(m,d),process.exit(0)}catch(c){console.error("Initialization failed:",c.message),process.exit(1)}}async function Teo(e,t,r={}){var l,d,m;console.log(`Signed in as ${t}`);let n=sa(),o=[];try{let h=(l=n.auth)==null?void 0:l.userDocId;if(!h)console.warn("User document not found. Creating local workspace.");else{let A=await e.find(h);await A.whenReady();let b=A.doc();if(b){let w=b.workspaceIds||[];for(let v of w)try{let _=await e.find(v);await _.whenReady();let D=_.doc();o.push({id:v,name:(D==null?void 0:D.title)||(D==null?void 0:D.name)||"Untitled"})}catch{o.push({id:v,name:`Workspace ${String(v).slice(0,8)}`})}}}}catch{console.warn("Could not load cloud workspaces. Creating local workspace.")}if(r.createName)return console.log(`Creating workspace: ${r.createName}`),await hke(e,r.createName);if(r.workspaceName){let h=o.find(_=>_.name.toLowerCase()===r.workspaceName.toLowerCase());if(!h)throw new Error(`Workspace "${r.workspaceName}" not found. Available: ${o.map(_=>_.name).join(", ")||"(none)"}`);console.log(`Selected workspace: ${h.name}`),console.log("Fetching workspace from server...");try{let _=await Bst(h.id,{verbose:!1});console.log(`Fetched ${_.fileCount} files, ${_.folderCount} folders`)}catch(_){console.warn(`Warning: Could not fetch workspace from server: ${_ instanceof Error?_.message:_}`),console.warn("Workspace will sync when daemon starts.")}let A={type:"existing",id:h.id,name:h.name};TL(A.id);let b=process.cwd(),w={path:b,workspaceId:A.id,workspaceName:A.name,connectedAt:new Date().toISOString(),lastSyncedAt:new Date().toISOString()};w_(b,w),console.log("Workspace connection saved");let v=sa();if((d=v.auth)!=null&&d.userDocId)try{await Sx(e).addWorkspace(v.auth.userDocId,A.id)}catch{}return await mke(e,{id:A.id}),w}let i=[...o.map(h=>({label:h.name,value:{type:"existing",id:h.id,name:h.name}})),{label:"+ Create new workspace",value:{type:"create",id:"",name:""}}],s=await gT("Select workspace:",i);if(s.type==="create")return await hke(e);console.log("Fetching workspace from server...");try{let h=await Bst(s.id,{verbose:!1});console.log(`Fetched ${h.fileCount} files, ${h.folderCount} folders`)}catch(h){console.warn(`Warning: Could not fetch workspace from server: ${h instanceof Error?h.message:h}`),console.warn("Workspace will sync when daemon starts.")}TL(s.id);let a=process.cwd(),c={path:a,workspaceId:s.id,workspaceName:s.name,connectedAt:new Date().toISOString(),lastSyncedAt:new Date().toISOString()};if(w_(a,c),console.log("Workspace connection saved"),(m=n.auth)!=null&&m.userDocId)try{await Sx(e).addWorkspace(n.auth.userDocId,s.id)}catch{}return await mke(e,{id:s.id}),c}async function Deo(e,t={}){return t.createName?(console.log(`Creating workspace: ${t.createName}`),await hke(e,t.createName)):(await gT("Select option:",[{label:"Create local workspace",value:"create",description:"Work offline, sync later"},{label:"Sign in to sync with team",value:"signin",description:"Access cloud workspaces"}])==="signin"&&(console.log(""),console.log("Please run `mod auth login` to sign in, then run `mod init` again."),process.exit(0)),await hke(e))}async function hke(e,t){var d;let r=$L.basename(process.cwd()),n=process.cwd(),o=r.charAt(0).toUpperCase()+r.slice(1),i;if(t){let m=p5(t);if(m)throw new Error(`Invalid workspace name: ${m}`);i=t}else i=await pke("Workspace name",{default:o,validate:p5});console.log("Creating workspace...");let a=await gs(e).createWorkspace({name:i});TL(a.id);let c={path:n,workspaceId:a.id,workspaceName:a.name,connectedAt:new Date().toISOString(),lastSyncedAt:new Date().toISOString()};w_(n,c),console.log("Workspace connection saved"),console.log("Syncing workspace..."),await new Promise(m=>setTimeout(m,2e3));let l=sa();if((d=l.auth)!=null&&d.userDocId)try{await Sx(e).addWorkspace(l.auth.userDocId,a.id),await new Promise(h=>setTimeout(h,1e3)),console.log("Workspace registered for sync")}catch{console.warn("Note: Could not register workspace for cross-device sync")}return await mke(e,a),console.log("Workspace synced"),c}async function mke(e,t){let n=await new s5(e).execute({paths:["."]},i=>{i.phase==="scanning"?process.stdout.write(`\rScanning... ${i.total} files found`):i.phase==="adding"&&process.stdout.write(`\rImporting... ${i.current}/${i.total} files`)});if(process.stdout.write("\r"+" ".repeat(60)+"\r"),n.summary.totalFiles===0){console.log("No files to import");return}let o=[];n.summary.created>0&&o.push(`${n.summary.created} created`),n.summary.unchanged>0&&o.push(`${n.summary.unchanged} unchanged`),n.summary.skipped>0&&o.push(`${n.summary.skipped} skipped`),n.summary.errors>0&&o.push(`${n.summary.errors} errors`),console.log(`Imported ${n.summary.totalFiles} files (${o.join(", ")})`)}function lAr(e,t){let r=$L.join(e,".mod","traces");!t&&Cx.existsSync(r)&&(console.log("Already initialized for local development."),console.log("Run `mod init --force` to reinitialize."),process.exit(0)),Cx.mkdirSync(r,{recursive:!0});let n=$L.join(e,"glassware.toml");Cx.existsSync(n)||(Cx.writeFileSync(n,`# Trace schema configuration
767
767
  # See: https://docs.mod.computer/glassware
768
768
 
769
769
  node_types = ["requirement", "specification", "implementation", "test"]
@@ -782,9 +782,9 @@ to = "specification"
782
782
  attribute = "implementations"
783
783
  from = "test"
784
784
  to = "implementation"
785
- `,"utf-8"),console.log("Created glassware.toml")),fAr(),console.log(""),console.log("Initialized for local spec-driven development:"),console.log(" .mod/traces/ \u2014 Local trace storage (committable to git)"),console.log(" glassware.toml \u2014 Trace schema configuration"),console.log(" .claude/skills/ \u2014 /mod skill for AI agents"),console.log(""),console.log("Get started:"),console.log(" mod trace add <file>:<line> --type=implementation"),console.log(" mod trace list"),console.log(" mod trace report <file>"),process.exit(0)}function fAr(){let e=process.cwd(),t=$L.join(e,".claude","skills","mod"),r=$L.join(t,"references");Cx.existsSync(t)||Cx.mkdirSync(t,{recursive:!0}),Cx.existsSync(r)||Cx.mkdirSync(r,{recursive:!0});let n=$L.join(t,"SKILL.md"),o=$L.join(r,"commands.md");Cx.existsSync(n)||Cx.writeFileSync(n,Reo),Cx.existsSync(o)||Cx.writeFileSync(o,Oeo)}var Reo=`---
785
+ `,"utf-8"),console.log("Created glassware.toml")),fAr(),console.log(""),console.log("Initialized for local spec-driven development:"),console.log(" .mod/traces/ Local trace storage (committable to git)"),console.log(" glassware.toml Trace schema configuration"),console.log(" .claude/skills/ Mod skill for AI agents"),console.log(""),console.log("Get started:"),console.log(" mod trace add <file>:<line> --type=implementation"),console.log(" mod trace list"),console.log(" mod trace report <file>"),process.exit(0)}function fAr(){let e=process.cwd(),t=$L.join(e,".claude","skills","mod"),r=$L.join(t,"references");Cx.existsSync(t)||Cx.mkdirSync(t,{recursive:!0}),Cx.existsSync(r)||Cx.mkdirSync(r,{recursive:!0});let n=$L.join(t,"SKILL.md"),o=$L.join(r,"commands.md");Cx.existsSync(n)||Cx.writeFileSync(n,Reo),Cx.existsSync(o)||Cx.writeFileSync(o,Oeo)}var Reo=`---
786
786
  name: mod
787
- description: Spec-driven development with traceability. Implements specs, adds traces, verifies coverage. Use when implementing from specifications, adding requirement traceability, or checking trace coverage before merging.
787
+ description: Spec-driven development with traceability. Implements specs, adds traces, verifies coverage.
788
788
  ---
789
789
 
790
790
  # Mod: Spec-Driven Development
@@ -795,21 +795,27 @@ Use this skill when the user asks to implement from specs, add traceability, or
795
795
 
796
796
  - "implement this spec"
797
797
  - "implement specs/*.md"
798
- - "/mod implement <file>"
798
+ - "implement <file>"
799
799
  - "add tests for this spec"
800
+ - "add traces"
800
801
  - "check trace coverage"
802
+ - "check coverage"
803
+ - "trace report"
801
804
 
802
805
  ## Workflow
803
806
 
804
807
  ### 1. Setup (if needed)
805
808
 
809
+ Check if workspace exists:
810
+ \`\`\`bash
811
+ mod status
812
+ \`\`\`
813
+
806
814
  If not initialized:
807
815
  \`\`\`bash
808
816
  mod init
809
817
  \`\`\`
810
818
 
811
- This creates \`.mod/traces/\` for local trace storage and \`glassware.toml\` for schema configuration. Traces are stored as JSON files committable to git.
812
-
813
819
  ### 2. Read the Spec
814
820
 
815
821
  Parse the spec file for requirements. Requirements may be marked with glassware annotations or be plain markdown sections.
@@ -864,7 +870,7 @@ Only report completion when both commands exit 0 (all files traced, all requirem
864
870
 
865
871
  | Command | Purpose |
866
872
  |---------|---------|
867
- | \`mod init\` | Initialize for local spec-driven development |
873
+ | \`mod init\` | Initialize workspace |
868
874
  | \`mod trace add <file>:<line> --type=<type> [--link=<id>]\` | Add trace |
869
875
  | \`mod trace link <source> <target>\` | Link two traces |
870
876
  | \`mod trace report <file>\` | Show trace coverage for spec |
@@ -880,6 +886,26 @@ Only report completion when both commands exit 0 (all files traced, all requirem
880
886
  - \`test\` - Code that verifies
881
887
  - \`utility\` - Intentionally untraced helpers
882
888
 
889
+ ## Keeping Specs Up to Date
890
+
891
+ Specs are living documents. As you implement, update the spec to reflect reality:
892
+
893
+ - If behavior diverges from the spec during implementation, update the spec to match.
894
+ - If you add new capabilities not covered by the spec, add spec entries with traces.
895
+ - If requirements change or are removed, update the spec accordingly.
896
+ - Run \`mod trace report <spec-file>\` after spec edits to confirm trace integrity.
897
+
898
+ ## Protecting Traced Content
899
+
900
+ Traces reference spec content by file path, line number, and quoted text. Rewriting or restructuring a spec file can orphan traces - the trace still exists in \`.mod/traces/\` but points to content that moved or disappeared.
901
+
902
+ **When editing spec files:**
903
+ - Avoid bulk-rewriting entire spec files. Prefer targeted edits that preserve line structure.
904
+ - If you must restructure a spec, run \`mod trace report <spec-file>\` before and after to confirm no traces were orphaned.
905
+ - When removing a spec item, delete its traces too: \`mod trace delete <trace-id> --force\`.
906
+ - When moving content to a different line, the trace's \`quotedText\` will still match on re-scan, but line numbers will drift. Re-run \`mod trace report\` to verify the chain is intact.
907
+ - If adding new sections to a spec, add corresponding traces with \`mod trace add\`.
908
+
883
909
  ## Important
884
910
 
885
911
  - Always verify coverage before completing
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mod-computer/mod",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "license": "MIT",
5
5
  "description": "Mod CLI - Spec-driven development with traceability",
6
6
  "bin": {