pando-ai 0.1.5 → 0.2.0
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 +106 -106
- package/dist/watcher-process.js +401 -401
- package/dist/workers/snapshot-worker.mjs +4 -4
- package/package.json +1 -1
- package/resources/tools/generated_pando-tools.json +95 -1
- package/tools/clojure-editor/lib/pando-clojure-editor-standalone.jar +0 -0
- package/tools/clojure-indexer/lib/pando-clojure-indexer-standalone.jar +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import{g as P,h as J}from"./chunk-Z7LW7C4Z.mjs";import{a as V,b as
|
|
1
|
+
import{g as P,h as J}from"./chunk-Z7LW7C4Z.mjs";import{a as V,b as st,c as K,e as R}from"./chunk-ODST7O2H.mjs";import{a as Q,b as Et}from"./chunk-Z5RGEDVQ.mjs";import{a as Tt,c as ot}from"./chunk-AYCBSZ56.mjs";import{parentPort as $}from"worker_threads";import nt from"path";import*as o from"fs";import*as I from"path";import*as g from"isomorphic-git";function xt(){return new Date().toISOString()}function U(r,t,e){let n=`[${xt()}] [${r.toUpperCase()}] ${t}`;if(!e)return n;try{return n+" "+JSON.stringify(e)}catch{return n}}var B={debug:(r,t)=>{try{process.stderr.write(U("debug",r,t)+`
|
|
2
2
|
`)}catch{}},info:(r,t)=>{try{process.stderr.write(U("info",r,t)+`
|
|
3
3
|
`)}catch{}},warn:(r,t)=>{try{process.stderr.write(U("warn",r,t)+`
|
|
4
4
|
`)}catch{}},error:(r,t)=>{try{process.stderr.write(U("error",r,t)+`
|
|
5
|
-
`)}catch{}}};import{execFileSync as v}from"child_process";K();import*as Z from"path";function
|
|
5
|
+
`)}catch{}}};import{execFileSync as v}from"child_process";K();import*as Z from"path";function at(r){let t=V(r);return{root:t,historyGit:Z.join(t,"history.git"),workdir:Z.join(t,"workdir")}}function f(r,t){try{B.info(r,t)}catch{}}function ct(r,t){try{B.warn(r,t)}catch{}}var F=class{constructor(t){this.repoDir=t;this.stack=[{name:"",entries:[],dirNames:new Set,fileNames:new Set}]}currentDirs(){return this.stack.slice(1).map(t=>t.name)}lcpLen(t,e){let n=0;for(;n<t.length&&n<e.length&&t[n]===e[n];)n++;return n}async flushTop(){if(this.stack.length<=1)return;let t=this.stack.pop();t.entries.sort((i,s)=>i.path.localeCompare(s.path));let e=await g.writeTree({fs:o,dir:this.repoDir,tree:t.entries}),n=this.stack[this.stack.length-1];n.entries.push({mode:"040000",path:t.name,oid:e,type:"tree"}),n.dirNames.add(t.name)}async addFile(t,e){let n=t.replace(/^\.\//,"").replace(/\\/g,"/"),i=n.split("/").filter(Boolean);if(!i.length)return;let s=i.slice(0,-1),a=i[i.length-1],p=this.currentDirs(),m=this.lcpLen(p,s);for(let b=p.length;b>m;b--)await this.flushTop();for(let b=m;b<s.length;b++){let h=s[b];if(this.stack[this.stack.length-1].fileNames.has(h))throw new Error(`Path conflict: file exists where directory expected: ${s.slice(0,b+1).join("/")}`);this.stack.push({name:h,entries:[],dirNames:new Set,fileNames:new Set})}let u=this.stack[this.stack.length-1];if(u.dirNames.has(a))throw new Error(`Path conflict: directory exists where file expected: ${n}`);u.entries.push({mode:"100644",path:a,oid:e,type:"blob"}),u.fileNames.add(a)}async finish(){for(;this.stack.length>1;)await this.flushTop();let t=this.stack[0];return t.entries.sort((e,n)=>e.path.localeCompare(n.path)),g.writeTree({fs:o,dir:this.repoDir,tree:t.entries})}},L=class{constructor(t){this.baseDir=t.baseDir;let e=at(t.shadowDir??t.baseDir);this.repoDir=I.join(e.root,"history")}async ensureInitialized(){o.mkdirSync(this.repoDir,{recursive:!0});let t=I.join(this.repoDir,".git");if(o.existsSync(t)){await this.ensureDefaultGitignore();return}let e=I.join(this.repoDir,".init.lock"),n=s=>new Promise(a=>setTimeout(a,s)),i=null;try{try{i=o.openSync(e,"wx"),o.writeFileSync(i,String(Date.now()))}catch{let s=Date.now(),a=3e4;for(;!o.existsSync(t);){try{let p=o.statSync(e);if(Date.now()-p.mtimeMs>a){try{o.unlinkSync(e)}catch{}break}}catch{}if(Date.now()-s>a)break;await n(200)}if(o.existsSync(t))return;i=o.openSync(e,"wx"),o.writeFileSync(i,String(Date.now()))}if(o.existsSync(t))return;f(`[${new Date().toISOString()}] [GitHistoryStore] Initializing history repo at ${this.repoDir}`),await g.init({fs:o,dir:this.repoDir}),f(`[${new Date().toISOString()}] [GitHistoryStore] git.init OK`),await g.setConfig({fs:o,dir:this.repoDir,path:"user.name",value:"ast-db"}),await g.setConfig({fs:o,dir:this.repoDir,path:"user.email",value:"ast-db@example.com"}),f(`[${new Date().toISOString()}] [GitHistoryStore] git config set (user.name/user.email)`),o.writeFileSync(I.join(this.repoDir,".gitignore"),`# ast history repo
|
|
6
6
|
`),f(`[${new Date().toISOString()}] [GitHistoryStore] wrote .gitignore`)}finally{try{i!=null&&o.closeSync(i)}catch{}try{o.existsSync(e)&&o.unlinkSync(e)}catch{}}await this.ensureDefaultGitignore()}async ensureDefaultGitignore(){let t=I.join(this.repoDir,".gitignore");if(!o.existsSync(t))try{o.mkdirSync(I.dirname(t),{recursive:!0}),o.writeFileSync(t,`# ast history repo
|
|
7
|
-
`,"utf8");try{await g.add({fs:o,dir:this.repoDir,filepath:".gitignore"})}catch{}f(`[GitHistoryStore] restored missing .gitignore at ${t}`)}catch(e){at(`[GitHistoryStore] failed to recreate .gitignore: ${e?.message||e}`)}}normalizeLogicalPath(t){return String(t||"").replace(/\\/g,"/").replace(/^(?:\.\/)+/,"").replace(/^\/+/,"")}gitExists(){try{return v("git",["--version"],{cwd:this.repoDir,stdio:"ignore"}),!0}catch{return!1}}createSnapshotNative(t,e,n,i){let a=(typeof n=="string"?n.match(/^AST-Restore-Trace-Id:\s*(.+)$/m):null)?.[1]?.trim()||null,p=e;if(!p)try{p=v("git",["rev-parse","HEAD"],{cwd:this.repoDir,encoding:"utf8"}).trim()}catch{p=void 0}let m=new Map,u=Date.now();try{f("[GitHistoryStore] native snapshot start",{traceId:a,repoDir:this.repoDir,memberCount:t.length,parentOid:p||null})}catch{}for(let h=0;h<t.length;h++){let y=t[h];if(y.content==null)throw new Error(`Direct snapshot requires content for ${y.logicalPath}`);let S=Buffer.isBuffer(y.content)?y.content:Buffer.from(y.content),c=this.normalizeLogicalPath(y.logicalPath);if(!c)throw new Error("Direct snapshot requires non-empty logicalPath");let d=Date.now();try{f("[GitHistoryStore] native hash-object start",{traceId:a,repoDir:this.repoDir,index:h+1,total:t.length,logicalPath:c,bytes:S.length})}catch{}let D;try{D=v("git",["hash-object","-w","--stdin"],{cwd:this.repoDir,input:S}).toString("utf8").trim()}catch(T){try{at("[GitHistoryStore] native hash-object failed",{traceId:a,repoDir:this.repoDir,index:h+1,total:t.length,logicalPath:c,bytes:S.length,durationMs:Date.now()-d,message:T?.message||String(T),status:T?.status??null,signal:T?.signal??null,stdout:T?.stdout?String(T.stdout).slice(0,500):null,stderr:T?.stderr?String(T.stderr).slice(0,500):null})}catch{}throw T}let E=Date.now()-d;try{(E>1e3||h<3||h===t.length-1)&&f("[GitHistoryStore] native hash-object done",{traceId:a,repoDir:this.repoDir,index:h+1,total:t.length,logicalPath:c,bytes:S.length,durationMs:E,oid:D})}catch{}m.set(c,D)}try{f("[GitHistoryStore] native hash-object phase complete",{traceId:a,repoDir:this.repoDir,memberCount:t.length,durationMs:Date.now()-u})}catch{}let b=I.join(this.repoDir,`.pando-index-${process.pid}-${Date.now()}`),l={...process.env,GIT_INDEX_FILE:b};try{let h=Date.now();try{f("[GitHistoryStore] native read-tree begin",{traceId:a,repoDir:this.repoDir,tmpIndex:b})}catch{}v("git",["read-tree","--empty"],{cwd:this.repoDir,env:l});try{f("[GitHistoryStore] native read-tree complete",{traceId:a,repoDir:this.repoDir,durationMs:Date.now()-h})}catch{}let y=Array.from(m.keys()).sort((k,O)=>k.localeCompare(O)),S=Date.now();for(let k of y){let O=m.get(k);v("git",["update-index","--add","--cacheinfo","100644",O,k],{cwd:this.repoDir,env:l})}try{f("[GitHistoryStore] native update-index complete",{traceId:a,repoDir:this.repoDir,entryCount:y.length,durationMs:Date.now()-S})}catch{}let c=Date.now();try{f("[GitHistoryStore] native write-tree begin",{traceId:a,repoDir:this.repoDir,entryCount:y.length})}catch{}let d=v("git",["write-tree"],{cwd:this.repoDir,env:l}).toString("utf8").trim();try{f("[GitHistoryStore] native write-tree complete",{traceId:a,repoDir:this.repoDir,treeOid:d,durationMs:Date.now()-c})}catch{}let D=["commit-tree",d];p&&D.push("-p",p),n&&D.push("-m",n);let E=Date.now();try{f("[GitHistoryStore] native commit-tree begin",{traceId:a,repoDir:this.repoDir,treeOid:d,parentOid:p||null})}catch{}let T=v("git",D,{cwd:this.repoDir}).toString("utf8").trim();try{f("[GitHistoryStore] native commit-tree complete",{traceId:a,repoDir:this.repoDir,commitOid:T,durationMs:Date.now()-E})}catch{}let j=Date.now();try{f("[GitHistoryStore] native update-ref begin",{traceId:a,repoDir:this.repoDir,commitOid:T})}catch{}v("git",["update-ref","refs/heads/master",T],{cwd:this.repoDir});try{f("[GitHistoryStore] native update-ref complete",{traceId:a,repoDir:this.repoDir,commitOid:T,durationMs:Date.now()-j})}catch{}return{snapshotId:T,parent:p}}finally{try{o.unlinkSync(b)}catch{}try{o.unlinkSync(`${b}.lock`)}catch{}}}async readObject(t){let{object:e}=await g.readObject({fs:o,dir:this.repoDir,oid:t});return Buffer.from(e)}async hasObject(t){try{return await g.readObject({fs:o,dir:this.repoDir,oid:t}),!0}catch{return!1}}async createSnapshot(t,e){P(e,"Snapshot cancelled"),await this.ensureInitialized(),f(`[${new Date().toISOString()}] [GitHistoryStore] Creating snapshot (streaming) with ${t.members.length} members`);let n=[...t.members].map(w=>({...w,logicalPath:this.normalizeLogicalPath(w.logicalPath)})).filter(w=>!!w.logicalPath).sort((w,x)=>w.logicalPath.localeCompare(x.logicalPath)),i=this.gitExists()&&!e;try{i?_.info("[GitHistoryStore] Using native git snapshot path",{members:n.length}):_.info("[GitHistoryStore] Using JS snapshot path",{members:n.length,git:!1,cancellable:!!e})}catch{}if(i)try{return this.createSnapshotNative(n,t.parent,t.message,t.when).snapshotId}catch(w){try{_.warn(`[GitHistoryStore] Native git path failed; falling back to JS path: ${w?.message||w}`)}catch{}}let s=Date.now(),a=w=>w.toString("hex"),p=w=>Tt("crypto").createHash("sha1").update(w).digest(),m=w=>{let x=Buffer.from(`blob ${w.length}\0`,"utf8"),A=Buffer.concat([x,w]);return a(p(A))},u=Math.max(1,Math.min(64,Number(process.env.SNAPSHOT_BLOB_CONCURRENCY||16))),b=String(process.env.SNAPSHOT_PRECOMPUTE_OIDS||"1")!=="0";try{_.info("[GitHistoryStore] JS snapshot settings",{SNAPSHOT_BLOB_CONCURRENCY:u,SNAPSHOT_PRECOMPUTE_OIDS:b})}catch{}let l=new Map,h=0,y=0,S=0,c=n.map(w=>({path:w.logicalPath,buf:Buffer.isBuffer(w.content)?w.content:Buffer.from(w.content)})),d=async()=>{for(;;){P(e,"Snapshot cancelled");let w;if(h<c.length)w=c[h++];else break;let x;if(b)try{let A=m(w.buf);await this.hasObject(A)&&(x=A,S++)}catch{}if(x||(x=await g.writeBlob({fs:o,dir:this.repoDir,blob:w.buf}),y++),P(e,"Snapshot cancelled"),!x)throw new Error("Failed to compute blob object id");l.set(w.path,x)}},D=Array.from({length:Math.min(u,c.length)},()=>d());await Promise.all(D),P(e,"Snapshot cancelled"),f(`[GitHistoryStore] Blobs ready wrote=${y} skipped=${S} in ${Date.now()-s}ms`);let E=new F(this.repoDir),T=Array.from(l.keys()).sort((w,x)=>w.localeCompare(x));for(let w of T)P(e,"Snapshot cancelled"),await E.addFile(w,l.get(w));P(e,"Snapshot cancelled");let j=Date.now(),k=await E.finish();f(`[GitHistoryStore] Wrote tree(s). root=${k} in ${Date.now()-j}ms`);let O=t.parent;if(!O)try{O=await g.resolveRef({fs:o,dir:this.repoDir,ref:"HEAD"})}catch{}P(e,"Snapshot cancelled");let N=t.when?Math.floor(t.when/1e3):Math.floor(Date.now()/1e3),G=new Date(N*1e3).getTimezoneOffset(),B=await g.commit({fs:o,dir:this.repoDir,message:t.message,parent:O?[O]:[],tree:k,author:{name:"ast-db",email:"ast-db@example.com",timestamp:N,timezoneOffset:G},committer:{name:"ast-db",email:"ast-db@example.com",timestamp:N,timezoneOffset:G}});return await g.writeRef({fs:o,dir:this.repoDir,ref:"refs/heads/master",value:B,force:!0}),f(`[GitHistoryStore] Snapshot commit oid ${B}`),B}async listSnapshotMembers(t){await this.ensureInitialized();let e=s=>Math.round((s||0)/(1024*1024)),n=()=>{let s=process.memoryUsage();return`rss=${e(s.rss)}MB heapUsed=${e(s.heapUsed)}MB heapTotal=${e(s.heapTotal)}MB`},i=[];return f(`[${new Date().toISOString()}] [GitHistoryStore] listSnapshotMembers(${t}) start (${n()})`),await g.walk({fs:o,dir:this.repoDir,trees:[g.TREE({ref:t})],map:async(s,[a])=>{if(!a)return;if(await a.type()==="blob"){let m=await a.oid();i.push({logicalPath:s,objectId:m})}}}),f(`[${new Date().toISOString()}] [GitHistoryStore] listSnapshotMembers(${t}) -> ${i.length} blobs`),i.filter(s=>s.logicalPath&&s.logicalPath!==".").map(s=>({logicalPath:s.logicalPath.replace(/^\.\//,""),objectId:s.objectId}))}async readCommit(t){let{commit:e}=await g.readCommit({fs:o,dir:this.repoDir,oid:t});return{message:e.message,committer:e.committer}}async readCommitParent(t){let{commit:e}=await g.readCommit({fs:o,dir:this.repoDir,oid:t});return Array.isArray(e.parent)&&e.parent.length>0?String(e.parent[0]):void 0}async createSnapshotFromDisk(t,e){P(e,"Snapshot cancelled"),await this.ensureInitialized(),f(`[${new Date().toISOString()}] [GitHistoryStore] Creating snapshot from disk (streaming) with ${t.relPaths.length} members`);let n=[...t.relPaths].map(y=>this.normalizeLogicalPath(y)).filter(Boolean).sort((y,S)=>y.localeCompare(S)),i=Date.now(),s=new F(this.repoDir),a=0;for(let y of n){P(e,"Snapshot cancelled");let S=I.join(this.baseDir,y);try{let c=o.readFileSync(S),d=await g.writeBlob({fs:o,dir:this.repoDir,blob:c});await s.addFile(y,d),a++}catch{}}f(`[GitHistoryStore] Wrote ${a} blobs (disk) in ${Date.now()-i}ms`),P(e,"Snapshot cancelled");let p=Date.now(),m=await s.finish();f(`[GitHistoryStore] Wrote tree(s) (disk). root=${m} in ${Date.now()-p}ms`);let u=t.parent;if(!u)try{u=await g.resolveRef({fs:o,dir:this.repoDir,ref:"HEAD"})}catch{}P(e,"Snapshot cancelled");let b=t.when?Math.floor(t.when/1e3):Math.floor(Date.now()/1e3),l=new Date(b*1e3).getTimezoneOffset(),h=await g.commit({fs:o,dir:this.repoDir,message:t.message,parent:u?[u]:[],tree:m,author:{name:"ast-db",email:"ast-db@example.com",timestamp:b,timezoneOffset:l},committer:{name:"ast-db",email:"ast-db@example.com",timestamp:b,timezoneOffset:l}});return await g.writeRef({fs:o,dir:this.repoDir,ref:"refs/heads/master",value:h,force:!0}),f(`[GitHistoryStore] Snapshot commit oid ${h}`),h}async createSnapshotFromDiskAndCapturedIfChanged(t){await this.ensureInitialized();let e=new Map;for(let c of t.capturedMembers||[]){let d=this.normalizeLogicalPath(c.logicalPath);!d||c.content==null||e.has(d)||e.set(d,Buffer.isBuffer(c.content)?c.content:Buffer.from(c.content))}f(`[${new Date().toISOString()}] [GitHistoryStore] Creating snapshot from disk+captured (diff) with ${t.relPaths.length} members, captured=${e.size}`);let n=[...t.relPaths].map(c=>this.normalizeLogicalPath(c)).filter(Boolean).sort((c,d)=>c.localeCompare(d)),i=Date.now(),s=new F(this.repoDir),a=0,p=0,m=0;for(let c of n)try{let d=e.get(c),D=d??o.readFileSync(I.join(this.baseDir,c)),E=await g.writeBlob({fs:o,dir:this.repoDir,blob:D});await s.addFile(c,E),a++,d?p++:m++}catch{}f(`[GitHistoryStore] Wrote ${a} blobs (captured=${p}, disk=${m}) in ${Date.now()-i}ms`);let u=Date.now(),b=await s.finish();f(`[GitHistoryStore] Wrote tree(s) (disk+captured). root=${b} in ${Date.now()-u}ms`);let l=t.parent;if(!l)try{l=await g.resolveRef({fs:o,dir:this.repoDir,ref:"HEAD"})}catch{}if(l)try{let{commit:c}=await g.readCommit({fs:o,dir:this.repoDir,oid:l});if(c?.tree===b){f("[GitHistoryStore] Snapshot identical to parent; skipping commit",{parentOid:l,tree:b});let d=await this.readCommitParent(l);return{snapshotId:l,identical:!0,parent:d}}}catch(c){f("[GitHistoryStore] Parent read failed; proceeding with commit",{error:c?.message||String(c)})}let h=t.when?Math.floor(t.when/1e3):Math.floor(Date.now()/1e3),y=new Date(h*1e3).getTimezoneOffset(),S=await g.commit({fs:o,dir:this.repoDir,message:t.message,parent:l?[l]:[],tree:b,author:{name:"ast-db",email:"ast-db@example.com",timestamp:h,timezoneOffset:y},committer:{name:"ast-db",email:"ast-db@example.com",timestamp:h,timezoneOffset:y}});return await g.writeRef({fs:o,dir:this.repoDir,ref:"refs/heads/master",value:S,force:!0}),f(`[GitHistoryStore] Snapshot commit oid ${S}`),{snapshotId:S,identical:!1,parent:l}}async createSnapshotFromDiskIfChanged(t){await this.ensureInitialized(),f(`[${new Date().toISOString()}] [GitHistoryStore] Creating snapshot from disk (diff) with ${t.relPaths.length} members`);let e=[...t.relPaths].map(h=>this.normalizeLogicalPath(h)).filter(Boolean).sort((h,y)=>h.localeCompare(y)),n=Date.now(),i=new F(this.repoDir),s=0;for(let h of e){let y=I.join(this.baseDir,h);try{let S=o.readFileSync(y),c=await g.writeBlob({fs:o,dir:this.repoDir,blob:S});await i.addFile(h,c),s++}catch{}}f(`[GitHistoryStore] Wrote ${s} blobs (disk) in ${Date.now()-n}ms`);let a=Date.now(),p=await i.finish();f(`[GitHistoryStore] Wrote tree(s) (disk). root=${p} in ${Date.now()-a}ms`);let m=t.parent;if(!m)try{m=await g.resolveRef({fs:o,dir:this.repoDir,ref:"HEAD"})}catch{}if(m)try{let{commit:h}=await g.readCommit({fs:o,dir:this.repoDir,oid:m});if(h?.tree===p){f("[GitHistoryStore] Snapshot identical to parent; skipping commit",{parentOid:m,tree:p});let y=await this.readCommitParent(m);return{snapshotId:m,identical:!0,parent:y}}}catch(h){f("[GitHistoryStore] Parent read failed; proceeding with commit",{error:h?.message||String(h)})}let u=t.when?Math.floor(t.when/1e3):Math.floor(Date.now()/1e3),b=new Date(u*1e3).getTimezoneOffset(),l=await g.commit({fs:o,dir:this.repoDir,message:t.message,parent:m?[m]:[],tree:p,author:{name:"ast-db",email:"ast-db@example.com",timestamp:u,timezoneOffset:b},committer:{name:"ast-db",email:"ast-db@example.com",timestamp:u,timezoneOffset:b}});return await g.writeRef({fs:o,dir:this.repoDir,ref:"refs/heads/master",value:l,force:!0}),f(`[GitHistoryStore] Snapshot commit oid ${l}`),{snapshotId:l,identical:!1,parent:m}}};function q(r){let t={},e=r.split(/\r?\n/);for(let n of e){let i=n.match(/^([A-Za-z0-9-]+):\s*(.*)$/);i&&(t[i[1]]=i[2])}return t}import Pt from"fs";import H from"path";import ct from"ignore";import{Project as Kt}from"ts-morph";var It=new Set(["node_modules",".git",".pando-data",".clj-kondo"]);function ht(r,t){if(!t?.length)return null;let e=t.map(i=>i.replace(/^\/+/,"").replace(/\/+$/,"")).filter(Boolean).map(i=>`${i}/`);if(!e.length)return null;let n=i=>i.replace(/\\/g,"/");return i=>{let s=n(H.relative(r,i));if(!s||s.startsWith(".."))return!1;let a=s.endsWith("/")?s:`${s}/`;return e.some(p=>a.startsWith(p))}}function pt(r,t){return Mt(r,t,null)}function Mt(r,t,e){let n=t?.shouldExclude||null,i=[],s=H.resolve(t?.gitignoreRoot??r),a=J(s,""),p=ct().add(a),m=[{dir:r,relDir:"",rules:a,ig:p}];for(;m.length;){let u=m.pop(),b=[];try{b=Pt.readdirSync(u.dir,{withFileTypes:!0})}catch{continue}for(let l of b){let h=H.join(u.dir,l.name);if(!(typeof l.isSymbolicLink=="function"&&l.isSymbolicLink())){if(l.isDirectory()){if(It.has(l.name)||n?.(h))continue;let S=u.relDir?`${u.relDir}/${l.name}`:l.name,c=lt(S);if(u.ig.ignores(c))continue;let d=u.rules.slice(),D=J(h,c);D.length&&d.push(...D);let E=D.length?ct().add(d):u.ig;m.push({dir:h,relDir:c,rules:d,ig:E})}else if(l.isFile()){if(n?.(h))continue;let S=lt(H.relative(r,h));if(u.ig.ignores(S))continue;if(e===null)i.push(h);else{let c=H.extname(l.name).toLowerCase();e.has(c)&&i.push(h)}}}}}return i}function lt(r){return r.replace(/\\/g,"/")}K();Et();K();import tt from"fs";import Ot from"path";function dt(r){r.pragma("journal_mode = WAL"),r.pragma("synchronous = NORMAL"),r.pragma("busy_timeout = 10000");try{r.pragma("temp_store = MEMORY")}catch{}r.exec(`
|
|
7
|
+
`,"utf8");try{await g.add({fs:o,dir:this.repoDir,filepath:".gitignore"})}catch{}f(`[GitHistoryStore] restored missing .gitignore at ${t}`)}catch(e){ct(`[GitHistoryStore] failed to recreate .gitignore: ${e?.message||e}`)}}normalizeLogicalPath(t){return String(t||"").replace(/\\/g,"/").replace(/^(?:\.\/)+/,"").replace(/^\/+/,"")}gitExists(){try{return v("git",["--version"],{cwd:this.repoDir,stdio:"ignore"}),!0}catch{return!1}}createSnapshotNative(t,e,n,i){let a=(typeof n=="string"?n.match(/^AST-Restore-Trace-Id:\s*(.+)$/m):null)?.[1]?.trim()||null,p=e;if(!p)try{p=v("git",["rev-parse","HEAD"],{cwd:this.repoDir,encoding:"utf8"}).trim()}catch{p=void 0}let m=new Map,u=Date.now();try{f("[GitHistoryStore] native snapshot start",{traceId:a,repoDir:this.repoDir,memberCount:t.length,parentOid:p||null})}catch{}for(let l=0;l<t.length;l++){let y=t[l];if(y.content==null)throw new Error(`Direct snapshot requires content for ${y.logicalPath}`);let S=Buffer.isBuffer(y.content)?y.content:Buffer.from(y.content),c=this.normalizeLogicalPath(y.logicalPath);if(!c)throw new Error("Direct snapshot requires non-empty logicalPath");let d=Date.now();try{f("[GitHistoryStore] native hash-object start",{traceId:a,repoDir:this.repoDir,index:l+1,total:t.length,logicalPath:c,bytes:S.length})}catch{}let D;try{D=v("git",["hash-object","-w","--stdin"],{cwd:this.repoDir,input:S}).toString("utf8").trim()}catch(T){try{ct("[GitHistoryStore] native hash-object failed",{traceId:a,repoDir:this.repoDir,index:l+1,total:t.length,logicalPath:c,bytes:S.length,durationMs:Date.now()-d,message:T?.message||String(T),status:T?.status??null,signal:T?.signal??null,stdout:T?.stdout?String(T.stdout).slice(0,500):null,stderr:T?.stderr?String(T.stderr).slice(0,500):null})}catch{}throw T}let E=Date.now()-d;try{(E>1e3||l<3||l===t.length-1)&&f("[GitHistoryStore] native hash-object done",{traceId:a,repoDir:this.repoDir,index:l+1,total:t.length,logicalPath:c,bytes:S.length,durationMs:E,oid:D})}catch{}m.set(c,D)}try{f("[GitHistoryStore] native hash-object phase complete",{traceId:a,repoDir:this.repoDir,memberCount:t.length,durationMs:Date.now()-u})}catch{}let b=I.join(this.repoDir,`.pando-index-${process.pid}-${Date.now()}`),h={...process.env,GIT_INDEX_FILE:b};try{let l=Date.now();try{f("[GitHistoryStore] native read-tree begin",{traceId:a,repoDir:this.repoDir,tmpIndex:b})}catch{}v("git",["read-tree","--empty"],{cwd:this.repoDir,env:h});try{f("[GitHistoryStore] native read-tree complete",{traceId:a,repoDir:this.repoDir,durationMs:Date.now()-l})}catch{}let y=Array.from(m.keys()).sort((k,O)=>k.localeCompare(O)),S=Date.now();for(let k of y){let O=m.get(k);v("git",["update-index","--add","--cacheinfo","100644",O,k],{cwd:this.repoDir,env:h})}try{f("[GitHistoryStore] native update-index complete",{traceId:a,repoDir:this.repoDir,entryCount:y.length,durationMs:Date.now()-S})}catch{}let c=Date.now();try{f("[GitHistoryStore] native write-tree begin",{traceId:a,repoDir:this.repoDir,entryCount:y.length})}catch{}let d=v("git",["write-tree"],{cwd:this.repoDir,env:h}).toString("utf8").trim();try{f("[GitHistoryStore] native write-tree complete",{traceId:a,repoDir:this.repoDir,treeOid:d,durationMs:Date.now()-c})}catch{}let D=["commit-tree",d];p&&D.push("-p",p),n&&D.push("-m",n);let E=Date.now();try{f("[GitHistoryStore] native commit-tree begin",{traceId:a,repoDir:this.repoDir,treeOid:d,parentOid:p||null})}catch{}let T=v("git",D,{cwd:this.repoDir}).toString("utf8").trim();try{f("[GitHistoryStore] native commit-tree complete",{traceId:a,repoDir:this.repoDir,commitOid:T,durationMs:Date.now()-E})}catch{}let j=Date.now();try{f("[GitHistoryStore] native update-ref begin",{traceId:a,repoDir:this.repoDir,commitOid:T})}catch{}v("git",["update-ref","refs/heads/master",T],{cwd:this.repoDir});try{f("[GitHistoryStore] native update-ref complete",{traceId:a,repoDir:this.repoDir,commitOid:T,durationMs:Date.now()-j})}catch{}return{snapshotId:T,parent:p}}finally{try{o.unlinkSync(b)}catch{}try{o.unlinkSync(`${b}.lock`)}catch{}}}async readObject(t){let{object:e}=await g.readObject({fs:o,dir:this.repoDir,oid:t});return Buffer.from(e)}async hasObject(t){try{return await g.readObject({fs:o,dir:this.repoDir,oid:t}),!0}catch{return!1}}async createSnapshot(t,e){P(e,"Snapshot cancelled"),await this.ensureInitialized(),f(`[${new Date().toISOString()}] [GitHistoryStore] Creating snapshot (streaming) with ${t.members.length} members`);let n=[...t.members].map(w=>({...w,logicalPath:this.normalizeLogicalPath(w.logicalPath)})).filter(w=>!!w.logicalPath).sort((w,x)=>w.logicalPath.localeCompare(x.logicalPath)),i=this.gitExists()&&!e;try{i?B.info("[GitHistoryStore] Using native git snapshot path",{members:n.length}):B.info("[GitHistoryStore] Using JS snapshot path",{members:n.length,git:!1,cancellable:!!e})}catch{}if(i)try{return this.createSnapshotNative(n,t.parent,t.message,t.when).snapshotId}catch(w){try{B.warn(`[GitHistoryStore] Native git path failed; falling back to JS path: ${w?.message||w}`)}catch{}}let s=Date.now(),a=w=>w.toString("hex"),p=w=>Tt("crypto").createHash("sha1").update(w).digest(),m=w=>{let x=Buffer.from(`blob ${w.length}\0`,"utf8"),A=Buffer.concat([x,w]);return a(p(A))},u=Math.max(1,Math.min(64,Number(process.env.SNAPSHOT_BLOB_CONCURRENCY||16))),b=String(process.env.SNAPSHOT_PRECOMPUTE_OIDS||"1")!=="0";try{B.info("[GitHistoryStore] JS snapshot settings",{SNAPSHOT_BLOB_CONCURRENCY:u,SNAPSHOT_PRECOMPUTE_OIDS:b})}catch{}let h=new Map,l=0,y=0,S=0,c=n.map(w=>({path:w.logicalPath,buf:Buffer.isBuffer(w.content)?w.content:Buffer.from(w.content)})),d=async()=>{for(;;){P(e,"Snapshot cancelled");let w;if(l<c.length)w=c[l++];else break;let x;if(b)try{let A=m(w.buf);await this.hasObject(A)&&(x=A,S++)}catch{}if(x||(x=await g.writeBlob({fs:o,dir:this.repoDir,blob:w.buf}),y++),P(e,"Snapshot cancelled"),!x)throw new Error("Failed to compute blob object id");h.set(w.path,x)}},D=Array.from({length:Math.min(u,c.length)},()=>d());await Promise.all(D),P(e,"Snapshot cancelled"),f(`[GitHistoryStore] Blobs ready wrote=${y} skipped=${S} in ${Date.now()-s}ms`);let E=new F(this.repoDir),T=Array.from(h.keys()).sort((w,x)=>w.localeCompare(x));for(let w of T)P(e,"Snapshot cancelled"),await E.addFile(w,h.get(w));P(e,"Snapshot cancelled");let j=Date.now(),k=await E.finish();f(`[GitHistoryStore] Wrote tree(s). root=${k} in ${Date.now()-j}ms`);let O=t.parent;if(!O)try{O=await g.resolveRef({fs:o,dir:this.repoDir,ref:"HEAD"})}catch{}P(e,"Snapshot cancelled");let N=t.when?Math.floor(t.when/1e3):Math.floor(Date.now()/1e3),G=new Date(N*1e3).getTimezoneOffset(),_=await g.commit({fs:o,dir:this.repoDir,message:t.message,parent:O?[O]:[],tree:k,author:{name:"ast-db",email:"ast-db@example.com",timestamp:N,timezoneOffset:G},committer:{name:"ast-db",email:"ast-db@example.com",timestamp:N,timezoneOffset:G}});return await g.writeRef({fs:o,dir:this.repoDir,ref:"refs/heads/master",value:_,force:!0}),f(`[GitHistoryStore] Snapshot commit oid ${_}`),_}async listSnapshotMembers(t){await this.ensureInitialized();let e=s=>Math.round((s||0)/(1024*1024)),n=()=>{let s=process.memoryUsage();return`rss=${e(s.rss)}MB heapUsed=${e(s.heapUsed)}MB heapTotal=${e(s.heapTotal)}MB`},i=[];return f(`[${new Date().toISOString()}] [GitHistoryStore] listSnapshotMembers(${t}) start (${n()})`),await g.walk({fs:o,dir:this.repoDir,trees:[g.TREE({ref:t})],map:async(s,[a])=>{if(!a)return;if(await a.type()==="blob"){let m=await a.oid();i.push({logicalPath:s,objectId:m})}}}),f(`[${new Date().toISOString()}] [GitHistoryStore] listSnapshotMembers(${t}) -> ${i.length} blobs`),i.filter(s=>s.logicalPath&&s.logicalPath!==".").map(s=>({logicalPath:s.logicalPath.replace(/^\.\//,""),objectId:s.objectId}))}async readCommit(t){let{commit:e}=await g.readCommit({fs:o,dir:this.repoDir,oid:t});return{message:e.message,committer:e.committer}}async readCommitParent(t){let{commit:e}=await g.readCommit({fs:o,dir:this.repoDir,oid:t});return Array.isArray(e.parent)&&e.parent.length>0?String(e.parent[0]):void 0}async createSnapshotFromDisk(t,e){P(e,"Snapshot cancelled"),await this.ensureInitialized(),f(`[${new Date().toISOString()}] [GitHistoryStore] Creating snapshot from disk (streaming) with ${t.relPaths.length} members`);let n=[...t.relPaths].map(y=>this.normalizeLogicalPath(y)).filter(Boolean).sort((y,S)=>y.localeCompare(S)),i=Date.now(),s=new F(this.repoDir),a=0;for(let y of n){P(e,"Snapshot cancelled");let S=I.join(this.baseDir,y);try{let c=o.readFileSync(S),d=await g.writeBlob({fs:o,dir:this.repoDir,blob:c});await s.addFile(y,d),a++}catch{}}f(`[GitHistoryStore] Wrote ${a} blobs (disk) in ${Date.now()-i}ms`),P(e,"Snapshot cancelled");let p=Date.now(),m=await s.finish();f(`[GitHistoryStore] Wrote tree(s) (disk). root=${m} in ${Date.now()-p}ms`);let u=t.parent;if(!u)try{u=await g.resolveRef({fs:o,dir:this.repoDir,ref:"HEAD"})}catch{}P(e,"Snapshot cancelled");let b=t.when?Math.floor(t.when/1e3):Math.floor(Date.now()/1e3),h=new Date(b*1e3).getTimezoneOffset(),l=await g.commit({fs:o,dir:this.repoDir,message:t.message,parent:u?[u]:[],tree:m,author:{name:"ast-db",email:"ast-db@example.com",timestamp:b,timezoneOffset:h},committer:{name:"ast-db",email:"ast-db@example.com",timestamp:b,timezoneOffset:h}});return await g.writeRef({fs:o,dir:this.repoDir,ref:"refs/heads/master",value:l,force:!0}),f(`[GitHistoryStore] Snapshot commit oid ${l}`),l}async createSnapshotFromDiskAndCapturedIfChanged(t){await this.ensureInitialized();let e=new Map;for(let c of t.capturedMembers||[]){let d=this.normalizeLogicalPath(c.logicalPath);!d||c.content==null||e.has(d)||e.set(d,Buffer.isBuffer(c.content)?c.content:Buffer.from(c.content))}f(`[${new Date().toISOString()}] [GitHistoryStore] Creating snapshot from disk+captured (diff) with ${t.relPaths.length} members, captured=${e.size}`);let n=[...t.relPaths].map(c=>this.normalizeLogicalPath(c)).filter(Boolean).sort((c,d)=>c.localeCompare(d)),i=Date.now(),s=new F(this.repoDir),a=0,p=0,m=0;for(let c of n)try{let d=e.get(c),D=d??o.readFileSync(I.join(this.baseDir,c)),E=await g.writeBlob({fs:o,dir:this.repoDir,blob:D});await s.addFile(c,E),a++,d?p++:m++}catch{}f(`[GitHistoryStore] Wrote ${a} blobs (captured=${p}, disk=${m}) in ${Date.now()-i}ms`);let u=Date.now(),b=await s.finish();f(`[GitHistoryStore] Wrote tree(s) (disk+captured). root=${b} in ${Date.now()-u}ms`);let h=t.parent;if(!h)try{h=await g.resolveRef({fs:o,dir:this.repoDir,ref:"HEAD"})}catch{}if(h)try{let{commit:c}=await g.readCommit({fs:o,dir:this.repoDir,oid:h});if(c?.tree===b){f("[GitHistoryStore] Snapshot identical to parent; skipping commit",{parentOid:h,tree:b});let d=await this.readCommitParent(h);return{snapshotId:h,identical:!0,parent:d}}}catch(c){f("[GitHistoryStore] Parent read failed; proceeding with commit",{error:c?.message||String(c)})}let l=t.when?Math.floor(t.when/1e3):Math.floor(Date.now()/1e3),y=new Date(l*1e3).getTimezoneOffset(),S=await g.commit({fs:o,dir:this.repoDir,message:t.message,parent:h?[h]:[],tree:b,author:{name:"ast-db",email:"ast-db@example.com",timestamp:l,timezoneOffset:y},committer:{name:"ast-db",email:"ast-db@example.com",timestamp:l,timezoneOffset:y}});return await g.writeRef({fs:o,dir:this.repoDir,ref:"refs/heads/master",value:S,force:!0}),f(`[GitHistoryStore] Snapshot commit oid ${S}`),{snapshotId:S,identical:!1,parent:h}}async createSnapshotFromDiskIfChanged(t){await this.ensureInitialized(),f(`[${new Date().toISOString()}] [GitHistoryStore] Creating snapshot from disk (diff) with ${t.relPaths.length} members`);let e=[...t.relPaths].map(l=>this.normalizeLogicalPath(l)).filter(Boolean).sort((l,y)=>l.localeCompare(y)),n=Date.now(),i=new F(this.repoDir),s=0;for(let l of e){let y=I.join(this.baseDir,l);try{let S=o.readFileSync(y),c=await g.writeBlob({fs:o,dir:this.repoDir,blob:S});await i.addFile(l,c),s++}catch{}}f(`[GitHistoryStore] Wrote ${s} blobs (disk) in ${Date.now()-n}ms`);let a=Date.now(),p=await i.finish();f(`[GitHistoryStore] Wrote tree(s) (disk). root=${p} in ${Date.now()-a}ms`);let m=t.parent;if(!m)try{m=await g.resolveRef({fs:o,dir:this.repoDir,ref:"HEAD"})}catch{}if(m)try{let{commit:l}=await g.readCommit({fs:o,dir:this.repoDir,oid:m});if(l?.tree===p){f("[GitHistoryStore] Snapshot identical to parent; skipping commit",{parentOid:m,tree:p});let y=await this.readCommitParent(m);return{snapshotId:m,identical:!0,parent:y}}}catch(l){f("[GitHistoryStore] Parent read failed; proceeding with commit",{error:l?.message||String(l)})}let u=t.when?Math.floor(t.when/1e3):Math.floor(Date.now()/1e3),b=new Date(u*1e3).getTimezoneOffset(),h=await g.commit({fs:o,dir:this.repoDir,message:t.message,parent:m?[m]:[],tree:p,author:{name:"ast-db",email:"ast-db@example.com",timestamp:u,timezoneOffset:b},committer:{name:"ast-db",email:"ast-db@example.com",timestamp:u,timezoneOffset:b}});return await g.writeRef({fs:o,dir:this.repoDir,ref:"refs/heads/master",value:h,force:!0}),f(`[GitHistoryStore] Snapshot commit oid ${h}`),{snapshotId:h,identical:!1,parent:m}}};function q(r){let t={},e=r.split(/\r?\n/);for(let n of e){let i=n.match(/^([A-Za-z0-9-]+):\s*(.*)$/);i&&(t[i[1]]=i[2])}return t}import Pt from"fs";import H from"path";import lt from"ignore";import{Project as Jt}from"ts-morph";var It=new Set(["node_modules",".git",".pando-data"]);function Mt(r){let t=tt(r).split("/").filter(Boolean);for(let e=0;e<t.length;e+=1)if(It.has(t[e])||t[e]===".clj-kondo"&&t[e+1]===".cache")return!0;return!1}function ht(r,t){if(!t?.length)return null;let e=t.map(i=>i.replace(/^\/+/,"").replace(/\/+$/,"")).filter(Boolean).map(i=>`${i}/`);if(!e.length)return null;let n=i=>i.replace(/\\/g,"/");return i=>{let s=n(H.relative(r,i));if(!s||s.startsWith(".."))return!1;let a=s.endsWith("/")?s:`${s}/`;return e.some(p=>a.startsWith(p))}}function pt(r,t){return Ot(r,t,null)}function Ot(r,t,e){let n=t?.shouldExclude||null,i=[],s=H.resolve(t?.gitignoreRoot??r),a=J(s,""),p=lt().add(a),m=[{dir:r,relDir:"",rules:a,ig:p}];for(;m.length;){let u=m.pop(),b=[];try{b=Pt.readdirSync(u.dir,{withFileTypes:!0})}catch{continue}for(let h of b){let l=H.join(u.dir,h.name);if(!(typeof h.isSymbolicLink=="function"&&h.isSymbolicLink())&&!Mt(l)){if(h.isDirectory()){if(n?.(l))continue;let S=u.relDir?`${u.relDir}/${h.name}`:h.name,c=tt(S);if(u.ig.ignores(c))continue;let d=u.rules.slice(),D=J(l,c);D.length&&d.push(...D);let E=D.length?lt().add(d):u.ig;m.push({dir:l,relDir:c,rules:d,ig:E})}else if(h.isFile()){if(n?.(l))continue;let S=tt(H.relative(r,l));if(u.ig.ignores(S))continue;if(e===null)i.push(l);else{let c=H.extname(h.name).toLowerCase();e.has(c)&&i.push(l)}}}}}return i}function tt(r){return r.replace(/\\/g,"/")}K();Et();K();import et from"fs";import Ct from"path";function dt(r){r.pragma("journal_mode = WAL"),r.pragma("synchronous = NORMAL"),r.pragma("busy_timeout = 10000");try{r.pragma("temp_store = MEMORY")}catch{}r.exec(`
|
|
8
8
|
CREATE TABLE IF NOT EXISTS objects (
|
|
9
9
|
object_id TEXT PRIMARY KEY,
|
|
10
10
|
kind TEXT DEFAULT 'blob',
|
|
@@ -42,4 +42,4 @@ import{g as P,h as J}from"./chunk-Z7LW7C4Z.mjs";import{a as V,b as nt,c as K,e a
|
|
|
42
42
|
|
|
43
43
|
CREATE INDEX IF NOT EXISTS idx_snapshots_timestamp ON snapshots(timestamp);
|
|
44
44
|
CREATE INDEX IF NOT EXISTS idx_snapshot_meta_kv ON snapshot_meta(key, value);
|
|
45
|
-
`)}function
|
|
45
|
+
`)}function rt(r,t){let e=st(r),n=Ct.dirname(e);et.existsSync(n)||et.mkdirSync(n,{recursive:!0});let i=!!t?.readonly;if(i&&!et.existsSync(e)){let a=new Q(e,{});try{dt(a)}finally{try{a.close()}catch{}}}let s=new Q(e,i?{readonly:!0}:{});if(!i)dt(s);else try{s.pragma("busy_timeout = 10000")}catch{}return s}var C="",it=[],St=null;ot();function gt(r,t){if(Array.isArray(t)){it=t.slice(),St=ht(r,it);try{R("SnapshotWorker","exclude state updated",{excludeDirs:it})}catch{}}}function kt(r){let t=pt(r,{shouldExclude:St??void 0,gitignoreRoot:r});try{R("SnapshotWorker","gatherSnapshotFiles",{count:t.length,sample:t.slice(0,20).map(e=>nt.relative(r,e).replace(/\\/g,"/"))})}catch{}return t}function mt(r){try{return r.prepare("SELECT snapshot_id FROM snapshots ORDER BY timestamp DESC, rowid DESC LIMIT 1").get()?.snapshot_id}catch{return}}function ft(r,t,e,n,i){let s=i||null;r.prepare("INSERT OR REPLACE INTO snapshots (snapshot_id, parents, timestamp, message) VALUES (?, ?, ?, ?)").run(t,s,Math.floor(e/1e3),n)}async function ut(r,t,e){try{return await r.readCommitParent(t)||e}catch{return e}}function X(r,t){let e=new Map;try{let n=r.prepare("SELECT logical_path, object_id FROM snapshot_members WHERE snapshot_id = ?").all(t);for(let i of n)e.set(i.logical_path,i.object_id)}catch{}return e}function yt(r,t,e){r.transaction(i=>{let s=r.prepare("INSERT OR REPLACE INTO snapshot_members (snapshot_id, logical_path, object_id, lang, encoding) VALUES (?, ?, ?, ?, ?)");for(let a of i)s.run(t,a.logical_path,a.object_id,a.lang??null,a.encoding??null)})(e)}$.on("message",async r=>{if(r.cmd==="init"){C=r.projectRoot,gt(C,r.excludeDirs),$.postMessage({type:"ready"});return}if(r.excludeDirs&>(C,r.excludeDirs),r.cmd==="snapshot"){let t=rt(C);try{let e=Date.now(),n=r.payload?.when||Date.now(),i=r.payload?.message||"ast: snapshot",s=r.payload?.parent||mt(t),a=Date.now(),p=r.payload?.files&&r.payload.files.length?r.payload.files:kt(C);try{R("SnapshotWorker","snapshot start",{when:n,message:i,parent:s,fileCount:p.length,filesPreview:p.slice(0,50).map(D=>nt.relative(C,D).replace(/\\/g,"/"))})}catch{}let m=Date.now()-a,u=p.map(D=>nt.relative(C,D).replace(/\\/g,"/")),b=(r.payload?.captured||[]).map(D=>({logicalPath:D.logicalPath,objectId:"unknown",content:Buffer.from(D.contentB64,"base64")})),h=new L({baseDir:C});await h.ensureInitialized();let l=Date.now(),y=b.length>0?await h.createSnapshotFromDiskAndCapturedIfChanged({message:i,when:n,relPaths:u,capturedMembers:b,parent:s}):await h.createSnapshotFromDiskIfChanged({message:i,when:n,relPaths:u,parent:s}),S=y.snapshotId,c=await ut(h,S,y.parent||s),d=Date.now()-l;if(y.identical){let D=Date.now()-e;try{R("SnapshotWorker","snapshot skipped (identical)",{snapshotId:S,parent:c,files:u.length,timingsMs:{list:m,snapshot:d,total:D}})}catch{}$.postMessage({type:"result",snapshotId:S,parent:c,files:u.length,diff:{added:0,changed:0,deleted:0}})}else{let D=Date.now();ft(t,S,n,i,c);try{let M=q(i||""),W=t.prepare("INSERT OR REPLACE INTO snapshot_meta (snapshot_id, key, value) VALUES (?, ?, ?)");t.transaction(()=>{for(let[wt,Dt]of Object.entries(M))W.run(S,wt,Dt)})()}catch{}let E=Date.now()-D,T=Date.now(),j=await h.listSnapshotMembers(S),k=Date.now()-T,O=j.map(M=>({logical_path:M.logicalPath,object_id:M.objectId})),N=Date.now();yt(t,S,O);let G=Date.now()-N,_=c?X(t,c):new Map,w=X(t,S),x=[],A=[],z=[];for(let[M,W]of w){let Y=_.get(M);Y?Y!==W&&A.push(M):x.push(M)}for(let[M]of _)w.has(M)||z.push(M);let bt=Date.now()-e;try{R("SnapshotWorker","snapshot complete",{snapshotId:S,parent:c,files:u.length,timingsMs:{list:m,snapshot:d,metadata:E,listMembers:k,membersWrite:G,total:bt},diff:{added:x.length,changed:A.length,deleted:z.length}})}catch{}$.postMessage({type:"result",snapshotId:S,parent:c,files:u.length,diff:{added:x.length,changed:A.length,deleted:z.length}})}}catch(e){$.postMessage({type:"error",message:e?.message||String(e)})}finally{t.close()}return}if(r.cmd==="snapshot-captured"){let t=rt(C);try{let e=r.payload?.when||Date.now(),n=r.payload?.message||"ast: snapshot",i=r.payload?.parent||mt(t);try{R("SnapshotWorker","snapshot-captured start",{when:e,message:n,parent:i,members:r.payload.members.length})}catch{}let s=new L({baseDir:C});await s.ensureInitialized();let a=(r.payload.members||[]).map(d=>({logicalPath:d.logicalPath,objectId:"unknown",content:Buffer.from(d.contentB64,"base64")})),p=await s.createSnapshot({message:n,when:e,members:a}),m=await ut(s,p,i);ft(t,p,e,n,m);try{let d=q(n||""),D=t.prepare("INSERT OR REPLACE INTO snapshot_meta (snapshot_id, key, value) VALUES (?, ?, ?)");t.transaction(()=>{for(let[T,j]of Object.entries(d))D.run(p,T,j)})()}catch{}let u=await s.listSnapshotMembers(p),b=u.map(d=>({logical_path:d.logicalPath,object_id:d.objectId}));yt(t,p,b);let h=m?X(t,m):new Map,l=X(t,p),y=[],S=[],c=[];for(let[d,D]of l){let E=h.get(d);E?E!==D&&S.push(d):y.push(d)}for(let[d]of h)l.has(d)||c.push(d);try{R("SnapshotWorker","snapshot-captured complete",{snapshotId:p,parent:m,members:u.length,diff:{added:y.length,changed:S.length,deleted:c.length}})}catch{}$.postMessage({type:"result",snapshotId:p,parent:m,files:u.length,diff:{added:y.length,changed:S.length,deleted:c.length}})}catch(e){$.postMessage({type:"error",message:e?.message||String(e)})}finally{t.close()}}r.cmd==="close"&&$.postMessage({type:"closed"})});
|
package/package.json
CHANGED
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
"delete(path, expectedHashes, forceDeleteReferencesMayBreakSyntax, page)",
|
|
45
45
|
"insert(to, code, replaceExistingNodeAtPath, expectedHash, createFileIfMissing, newFileInitialContent, forceInsertWillBreakSyntax)",
|
|
46
46
|
"replace(path, expectedHash, with, scope, forceReplaceWillBreakSyntax)",
|
|
47
|
+
"edit-file-text(file, expectedSha256, content, replacements, edits)",
|
|
47
48
|
"replace-body(path, expectedHash, with, forceReplaceWillBreakSyntax)",
|
|
48
49
|
"change-signature(path, expectedHash, parameters, returnType, makeAsync, makeSyncFromAsync, forceChangeWillBreakSyntax, boundary, batchSaves)",
|
|
49
50
|
"filter-map-reduce(query, transforms, apply)",
|
|
@@ -63,7 +64,7 @@
|
|
|
63
64
|
"name": "read-this-first",
|
|
64
65
|
"category": "pando",
|
|
65
66
|
"signature": "read-this-first()",
|
|
66
|
-
"description": "Pando is an AST-aware code navigation and editing layer for large repos. It keeps a light index of supported source files, uses that index to narrow candidate files, then parses exact AST details at query or edit time.\n\nUse Pando for source-code search, navigation, references, callers, exports, renames, inserts, replacements, and deletes. Use shell/text tools for builds, tests, generated files, non-code files, and unsupported languages.\n\nRead next:\n- workspace-overview: quick inventory of indexed files and symbols.\n- find-nodes: search by language, scope, and Datalog; request include.self/topN when you need paths and hashes for edits.\n- get-content: read files or node paths returned by search tools.\n- list-exports, find-references, and find-callers: follow symbols before changing them.\n- Writers: rename, replace, replace-body, insert, and
|
|
67
|
+
"description": "Pando is an AST-aware code navigation and editing layer for large repos. It keeps a light index of supported source files, uses that index to narrow candidate files, then parses exact AST details at query or edit time.\n\nUse Pando for source-code search, navigation, references, callers, exports, renames, inserts, replacements, and deletes. Use shell/text tools for builds, tests, generated files, non-code files, and unsupported languages.\n\nRead next:\n- workspace-overview: quick inventory of indexed files and symbols.\n- find-nodes: search by language, scope, and Datalog; request include.self/topN when you need paths and hashes for edits.\n- get-content: read files or node paths returned by search tools.\n- list-exports, find-references, and find-callers: follow symbols before changing them.\n- Writers: rename, replace, replace-body, insert, delete, and edit-file-text for .clj-kondo config/hook files. Re-run find-nodes first if a path or hash is stale.\n\nUnnamed nodes can and should still be edited with Pando. Statements, returns, calls, arguments, branches, literals, and other unnamed syntax are normal AST nodes; find them by starting from a named entity or file, then narrowing by kind, text, role, parent, or child index.\n\nExamples:\n- In one find-nodes call, search for a function by name within the workspace and request include.self/topN to get the exact node path and hash.\n- To edit a statement inside a named function, find the function with include.self, then call find-nodes again with scope.node set to { path, expectedHash } for that function. Use predicates such as :node/kind \"ReturnStatement\" or :node/text \"oldValue\", then pass the returned statement path and hash without @ to replace or insert; delete uses expectedHashes.\n- For scripts or top-level code, scope find-nodes to the file path instead of a named node, find the statement by kind/text/role/index, then edit that returned node with a writer.\n- Insert code is literal: include the newline and indentation you want when inserting before or after a statement anchor.",
|
|
67
68
|
"parameters": {
|
|
68
69
|
"type": "object",
|
|
69
70
|
"properties": {},
|
|
@@ -1530,6 +1531,99 @@
|
|
|
1530
1531
|
}
|
|
1531
1532
|
]
|
|
1532
1533
|
},
|
|
1534
|
+
{
|
|
1535
|
+
"name": "edit-file-text",
|
|
1536
|
+
"category": "pando",
|
|
1537
|
+
"signature": "edit-file-text(file, expectedSha256, content, replacements, edits)",
|
|
1538
|
+
"description": "Text-only editor for .clj-kondo config and hook files that are intentionally excluded from semantic Clojure indexing. Requires expectedSha256 of the current file text. Refuses .clj-kondo/.cache. Provide exactly one of content, replacements, or edits. Use this for .clj-kondo/config.edn or .clj-kondo hook files when a Clojure change also needs lint config updates.",
|
|
1539
|
+
"parameters": {
|
|
1540
|
+
"type": "object",
|
|
1541
|
+
"properties": {
|
|
1542
|
+
"file": {
|
|
1543
|
+
"type": "string",
|
|
1544
|
+
"description": "Workspace-relative .clj-kondo file path."
|
|
1545
|
+
},
|
|
1546
|
+
"expectedSha256": {
|
|
1547
|
+
"type": "string",
|
|
1548
|
+
"description": "SHA-256 of the current file text, 64 lowercase hex characters."
|
|
1549
|
+
},
|
|
1550
|
+
"content": {
|
|
1551
|
+
"type": "string",
|
|
1552
|
+
"description": "Full replacement content. Mutually exclusive with replacements and edits."
|
|
1553
|
+
},
|
|
1554
|
+
"replacements": {
|
|
1555
|
+
"type": "array",
|
|
1556
|
+
"description": "Exact old/new string replacements. Mutually exclusive with content and edits.",
|
|
1557
|
+
"items": {
|
|
1558
|
+
"type": "object",
|
|
1559
|
+
"properties": {
|
|
1560
|
+
"old": {
|
|
1561
|
+
"type": "string",
|
|
1562
|
+
"description": "Exact current text to replace."
|
|
1563
|
+
},
|
|
1564
|
+
"new": {
|
|
1565
|
+
"type": "string",
|
|
1566
|
+
"description": "Replacement text."
|
|
1567
|
+
},
|
|
1568
|
+
"replaceAll": {
|
|
1569
|
+
"type": "boolean",
|
|
1570
|
+
"description": "Replace every occurrence instead of requiring exactly one match."
|
|
1571
|
+
}
|
|
1572
|
+
},
|
|
1573
|
+
"required": [
|
|
1574
|
+
"old",
|
|
1575
|
+
"new"
|
|
1576
|
+
],
|
|
1577
|
+
"additionalProperties": false
|
|
1578
|
+
}
|
|
1579
|
+
},
|
|
1580
|
+
"edits": {
|
|
1581
|
+
"type": "array",
|
|
1582
|
+
"description": "Character-offset edits applied from bottom to top. Mutually exclusive with content and replacements.",
|
|
1583
|
+
"items": {
|
|
1584
|
+
"type": "object",
|
|
1585
|
+
"properties": {
|
|
1586
|
+
"start": {
|
|
1587
|
+
"type": "number",
|
|
1588
|
+
"description": "Start character offset."
|
|
1589
|
+
},
|
|
1590
|
+
"end": {
|
|
1591
|
+
"type": "number",
|
|
1592
|
+
"description": "End character offset."
|
|
1593
|
+
},
|
|
1594
|
+
"with": {
|
|
1595
|
+
"type": "string",
|
|
1596
|
+
"description": "Replacement text."
|
|
1597
|
+
}
|
|
1598
|
+
},
|
|
1599
|
+
"required": [
|
|
1600
|
+
"start",
|
|
1601
|
+
"end",
|
|
1602
|
+
"with"
|
|
1603
|
+
],
|
|
1604
|
+
"additionalProperties": false
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
},
|
|
1608
|
+
"required": [
|
|
1609
|
+
"file",
|
|
1610
|
+
"expectedSha256"
|
|
1611
|
+
],
|
|
1612
|
+
"additionalProperties": false
|
|
1613
|
+
},
|
|
1614
|
+
"examples": [
|
|
1615
|
+
{
|
|
1616
|
+
"file": ".clj-kondo/config.edn",
|
|
1617
|
+
"expectedSha256": "0000000000000000000000000000000000000000000000000000000000000000",
|
|
1618
|
+
"replacements": [
|
|
1619
|
+
{
|
|
1620
|
+
"old": ":old/ns",
|
|
1621
|
+
"new": ":new/ns"
|
|
1622
|
+
}
|
|
1623
|
+
]
|
|
1624
|
+
}
|
|
1625
|
+
]
|
|
1626
|
+
},
|
|
1533
1627
|
{
|
|
1534
1628
|
"name": "replace-body",
|
|
1535
1629
|
"category": "pando",
|
|
Binary file
|
|
Binary file
|