pando-ai 0.2.1 → 0.2.2
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/README.md +139 -42
- package/dist/cli.js +297 -140
- package/dist/watcher-process.js +368 -368
- package/dist/workers/chunk-A6FOFRZ6.mjs +518 -0
- package/dist/workers/indexer-worker.mjs +1 -1
- package/dist/workers/snapshot-worker.mjs +5 -5
- package/package.json +4 -3
- package/resources/tools/generated_pando-tools.json +46 -128
- package/tools/clang-indexer/bin/pando-clang-indexer +0 -0
- package/tools/clojure-editor/lib/pando-clojure-editor-standalone.jar +0 -0
- package/tools/clojure-indexer/lib/pando-clojure-indexer-standalone.jar +0 -0
- package/dist/workers/chunk-Z7LW7C4Z.mjs +0 -518
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as B,b as
|
|
1
|
+
import{a as B,b as L,c as q,d as A,e as C,f as z,i as F,j as N}from"./chunk-A6FOFRZ6.mjs";import"./chunk-ODST7O2H.mjs";import{c as X}from"./chunk-AYCBSZ56.mjs";import{parentPort as l,threadId as ne}from"worker_threads";import K from"fs";import _ from"path";import{promises as G}from"fs";import H from"ignore";import{createHash as se}from"crypto";var te=Math.max(1,Number(process.env.PANDO_BATCH_INDEX_MAX_FILES||"64")),re=Math.max(1,Number(process.env.PANDO_BATCH_INDEX_MAX_BYTES||`${1024*1024}`)),ce=Math.max(1,Number(process.env.PANDO_BATCH_INDEX_MAX_WAIT_MS||"15")),le=Math.max(te,Number(process.env.PANDO_BATCH_INDEX_MAX_PENDING_FILES||"256")),he=Math.max(re,Number(process.env.PANDO_BATCH_INDEX_MAX_PENDING_BYTES||`${8*1024*1024}`));var be=1*1024*1024,We=Math.max(3e4,Number(process.env.PANDO_WORKER_INIT_TIMEOUT_MS||12e4)),ie=Math.max(1e3,Number(process.env.PANDO_ACTIVE_CONTEXT_STALL_LOG_MS||1e4)),Me=Math.max(ie,Number(process.env.PANDO_ACTIVE_CONTEXT_STALL_FAIL_MS||6e5)),Be=q();function U(e,t){return Buffer.from(e).toString(t)}function D(e,t,o,r,d,u){let m=Buffer.from(e),n=se("sha1").update(m).digest("hex"),i=u??m.toString(d),c=i?i.split(/\r?\n/).length:0;return{relPath:t,size:o,mtime:r,objectId:n,lineCount:c,encoding:d}}if(!l)throw new Error("Indexer worker must be spawned as a worker thread");X();function h(e,t){try{if(t===void 0){console.error(`[${new Date().toISOString()}] [IndexerWorker] ${e}`);return}console.error(`[${new Date().toISOString()}] [IndexerWorker] ${e} ${JSON.stringify({pid:process.pid,threadId:ne,...t&&typeof t=="object"&&!Array.isArray(t)?t:{details:t}})}`)}catch{}}h("module loaded",{cwd:process.cwd(),hasParentPort:!!l,envWorkerRoot:process.env.PANDO_WORKER_SCRIPT_DIR??null,envRuntimeRoot:process.env.PANDO_RUNTIME_ROOT??null});var J=new Map,E="",T="index",b=new Map,v=new Set;function Y(e,t){return _.relative(e,t).replace(/\\/g,"/")}function ae(e,t){let o=(t||[]).map(r=>typeof r=="string"?r.trim():"").filter(Boolean).map(r=>r.replace(/^[\\/]+/,"").replace(/[\\/]+$/,"")).map(r=>r.endsWith("/")?r:`${r}/`);return o.length?r=>{let d=Y(e,r);if(!d||d.startsWith(".."))return!1;let u=d.endsWith("/")?d:`${d}/`;return o.some(m=>u.startsWith(m))}:null}async function oe(e){await new Promise(t=>{b.set(e,t)})}l.on("message",async e=>{if(e.cmd==="scan-ack"){let t=b.get(e.requestId);if(t){b.delete(e.requestId);try{t()}catch{}}return}if(e.cmd==="scan-cancel"){v.add(e.requestId);let t=b.get(e.requestId);if(t){b.delete(e.requestId);try{t()}catch{}}return}if(e.cmd==="init"){try{h("init received",{mode:e.mode??"index",projectRoot:typeof e.projectRoot=="string"?e.projectRoot:null}),typeof e.projectRoot=="string"&&(E=e.projectRoot),T=e.mode??"index",h("init completed",{mode:T,projectRoot:E}),l.postMessage({type:"ready"})}catch(t){h("init failed",{error:t?.message||String(t),stack:t?.stack??null}),l.postMessage({type:"error",message:`init failed: ${t?.message||t}`,stack:t?.stack})}return}if(e.cmd==="scan"){let t=e.requestId,o=typeof e.projectRoot=="string"?e.projectRoot:E;if(h("scan received",{requestId:t,projectRoot:o||null,batchSize:e.batchSize??null,maxFileBytes:e.maxFileBytes??null,excludeDirs:Array.isArray(e.excludeDirs)?e.excludeDirs.slice(0,20):[]}),!o){l.postMessage({type:"error",requestId:t,message:"worker not initialized"});return}let r=Math.max(1,Math.min(500,Number(e.batchSize||100))),d=Math.max(1,Number(e.maxFileBytes||1*1024*1024)),u=e.languageSupport,m=z(u),n=ae(o,e.excludeDirs??[]),i=N(o),c=new Set(["node_modules",".git",".pando-data"]),S=a=>a.replace(/\\/g,"/"),w=await F(o,""),g=H().add(w),f=[{dir:o,relDir:"",rules:w,ig:g}],y=0,s=0,p=[],x=async()=>{p.length&&(l.postMessage({type:"scan:batch",requestId:t,files:p}),p=[],await oe(t))};try{for(;f.length&&!v.has(t);){let a=f.pop(),$=[];try{$=await G.readdir(a.dir,{withFileTypes:!0})}catch{s+=1;continue}for(let k of $){if(v.has(t))break;let I=_.join(a.dir,k.name);if(k.isSymbolicLink?.()){s+=1;continue}if(k.isDirectory()){if(c.has(k.name)){s+=1;continue}if(n?.(I)){s+=1;continue}let Q=a.relDir?`${a.relDir}/${k.name}`:k.name,M=S(Q);if(a.ig.ignores(M)&&!i?.hasDescendant(M)){s+=1;continue}let R=a.rules.slice(),j=await F(I,M);j.length&&R.push(...j);let Z=H().add(R);f.push({dir:I,relDir:M,rules:R,ig:Z});continue}if(!k.isFile()){s+=1;continue}if(n?.(I)){s+=1;continue}let P=Y(o,I);if(!P||P.startsWith("..")){s+=1;continue}if(a.ig.ignores(P)&&!i?.hasFile(P)){s+=1;continue}let V=_.extname(k.name).toLowerCase();if(!m.has(V)){s+=1;continue}let O=A(P);if(!O){s+=1;continue}let W;try{W=await G.stat(I)}catch{s+=1;continue}if(W.size>d){s+=1;continue}p.push({relPath:P,absPath:I,mtimeMs:Math.floor(W.mtimeMs),size:W.size,lang:O}),y+=1,p.length>=r&&await x()}}v.has(t)||await x(),l.postMessage({type:"scan:done",requestId:t,discovered:y,skipped:s,cancelled:v.has(t)}),h("scan complete",{requestId:t,discovered:y,skipped:s,cancelled:v.has(t)})}catch(a){h("scan failed",{requestId:t,error:a?.message||String(a),stack:a?.stack??null}),l.postMessage({type:"error",requestId:t,message:a?.message||String(a),stack:a?.stack})}finally{v.delete(t),b.delete(t)}return}if(e.cmd==="process"){let t=e.role??T,o=typeof e.projectRoot=="string"?e.projectRoot:E;if(!o){l.postMessage({type:"error",requestId:e.requestId,message:"worker not initialized"});return}let{file:r}=e,d=r.encoding??"utf8",u=r.sharedBuffer,m={size:r.size,mtime:r.mtime};try{let n=r.lang??"ts",i,c,S=()=>{if(c&&typeof i=="string")return{content:i,sharedBuffer:c};if(u instanceof SharedArrayBuffer)return c=u,i=U(u,d),{content:i,sharedBuffer:c};if(u instanceof ArrayBuffer){let x=Buffer.from(u),a=new SharedArrayBuffer(x.byteLength);return new Uint8Array(a).set(x),c=a,i=x.toString(d),{content:i,sharedBuffer:c}}let s=K.readFileSync(r.absPath),p=new SharedArrayBuffer(s.byteLength);return new Uint8Array(p).set(s),c=p,i=s.toString(d),{content:i,sharedBuffer:c}},w=()=>{let s=B(n,r.relPath,m),p=S();l.postMessage({type:"result",requestId:e.requestId,role:"index",relPath:r.relPath,result:{payload:s.payload,meta:{size:s.fileSize,mtime:s.mtime},snapshotSummary:D(p.sharedBuffer,r.relPath,r.size,r.mtime,d,p.content)}})},g=`${o}:${n}`,f=J.get(g);if(!f)try{h("creating language indexer",{role:t,requestId:e.requestId,lang:n,projectRoot:o});let s=C(n);if(!L(s))throw new Error(`${n} is not a worker-indexed language`);f=s.createIndexer(o),J.set(g,f),h("language indexer ready",{role:t,requestId:e.requestId,lang:n,projectRoot:o})}catch(s){h("language indexer create failed",{role:t,requestId:e.requestId,lang:n,error:s?.message||String(s),stack:s?.stack??null}),w();return}({content:i,sharedBuffer:c}=S());let y;try{y=await f.process({absPath:r.absPath,relPath:r.relPath,content:i,meta:m,preDeleted:!!r.preDeleted,lang:n})}catch(s){h("process failed",{role:t,requestId:e.requestId,lang:n,relPath:r.relPath,error:s?.message||String(s),stack:s?.stack??null}),w();return}l.postMessage({type:"result",requestId:e.requestId,role:"index",relPath:r.relPath,result:{payload:y.payload,meta:{size:y.fileSize,mtime:y.mtime},snapshotSummary:D(c,r.relPath,r.size,r.mtime,d,i)}})}catch(n){h("process wrapper failed",{role:t,requestId:e.requestId,relPath:r.relPath,error:n?.message||String(n),stack:n?.stack??null});try{let i=r.lang??"ts",c=K.readFileSync(r.absPath),S=new SharedArrayBuffer(c.byteLength);new Uint8Array(S).set(c);let w=c.toString(d),g=B(i,r.relPath,m);l.postMessage({type:"result",requestId:e.requestId,role:"index",relPath:r.relPath,result:{payload:g.payload,meta:{size:g.fileSize,mtime:g.mtime},snapshotSummary:D(S,r.relPath,r.size,r.mtime,d,w)}})}catch(i){l.postMessage({type:"error",requestId:e.requestId,message:i?.message||n?.message||String(i??n),stack:i?.stack??n?.stack})}}return}e.cmd==="close"&&l.postMessage({type:"closed"})});process.on("uncaughtException",e=>{try{l.postMessage({type:"error",message:e?.message||String(e),stack:e?.stack})}catch{}});process.on("unhandledRejection",e=>{try{l.postMessage({type:"error",message:String(e)})}catch{}});
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import{g as P,h as J}from"./chunk-
|
|
1
|
+
import{g as P,h as J,j as ot}from"./chunk-A6FOFRZ6.mjs";import{a as V,b as st,c as K,e as R}from"./chunk-ODST7O2H.mjs";import{a as Q,b as xt}from"./chunk-Z5RGEDVQ.mjs";import{a as Et,c as at}from"./chunk-AYCBSZ56.mjs";import{parentPort as $}from"worker_threads";import it from"path";import*as o from"fs";import*as I from"path";import*as g from"isomorphic-git";function Pt(){return new Date().toISOString()}function U(r,t,e){let i=`[${Pt()}] [${r.toUpperCase()}] ${t}`;if(!e)return i;try{return i+" "+JSON.stringify(e)}catch{return i}}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
|
|
6
|
-
`),
|
|
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(`
|
|
5
|
+
`)}catch{}}};import{execFileSync as v}from"child_process";K();import*as Z from"path";function ct(r){let t=V(r);return{root:t,historyGit:Z.join(t,"history.git"),workdir:Z.join(t,"workdir")}}function u(r,t){try{B.info(r,t)}catch{}}function lt(r,t){try{B.warn(r,t)}catch{}}var _=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 i=0;for(;i<t.length&&i<e.length&&t[i]===e[i];)i++;return i}async flushTop(){if(this.stack.length<=1)return;let t=this.stack.pop();t.entries.sort((n,s)=>n.path.localeCompare(s.path));let e=await g.writeTree({fs:o,dir:this.repoDir,tree:t.entries}),i=this.stack[this.stack.length-1];i.entries.push({mode:"040000",path:t.name,oid:e,type:"tree"}),i.dirNames.add(t.name)}async addFile(t,e){let i=t.replace(/^\.\//,"").replace(/\\/g,"/"),n=i.split("/").filter(Boolean);if(!n.length)return;let s=n.slice(0,-1),a=n[n.length-1],p=this.currentDirs(),y=this.lcpLen(p,s);for(let m=p.length;m>y;m--)await this.flushTop();for(let m=y;m<s.length;m++){let f=s[m];if(this.stack[this.stack.length-1].fileNames.has(f))throw new Error(`Path conflict: file exists where directory expected: ${s.slice(0,m+1).join("/")}`);this.stack.push({name:f,entries:[],dirNames:new Set,fileNames:new Set})}let w=this.stack[this.stack.length-1];if(w.dirNames.has(a))throw new Error(`Path conflict: directory exists where file expected: ${i}`);w.entries.push({mode:"100644",path:a,oid:e,type:"blob"}),w.fileNames.add(a)}async finish(){for(;this.stack.length>1;)await this.flushTop();let t=this.stack[0];return t.entries.sort((e,i)=>e.path.localeCompare(i.path)),g.writeTree({fs:o,dir:this.repoDir,tree:t.entries})}},L=class{constructor(t){this.baseDir=t.baseDir;let e=ct(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"),i=s=>new Promise(a=>setTimeout(a,s)),n=null;try{try{n=o.openSync(e,"wx"),o.writeFileSync(n,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 i(200)}if(o.existsSync(t))return;n=o.openSync(e,"wx"),o.writeFileSync(n,String(Date.now()))}if(o.existsSync(t))return;u(`[${new Date().toISOString()}] [GitHistoryStore] Initializing history repo at ${this.repoDir}`),await g.init({fs:o,dir:this.repoDir}),u(`[${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"}),u(`[${new Date().toISOString()}] [GitHistoryStore] git config set (user.name/user.email)`),o.writeFileSync(I.join(this.repoDir,".gitignore"),`# ast history repo
|
|
6
|
+
`),u(`[${new Date().toISOString()}] [GitHistoryStore] wrote .gitignore`)}finally{try{n!=null&&o.closeSync(n)}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{}u(`[GitHistoryStore] restored missing .gitignore at ${t}`)}catch(e){lt(`[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,i,n){let a=(typeof i=="string"?i.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 y=new Map,w=Date.now();try{u("[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 h=t[l];if(h.content==null)throw new Error(`Direct snapshot requires content for ${h.logicalPath}`);let S=Buffer.isBuffer(h.content)?h.content:Buffer.from(h.content),c=this.normalizeLogicalPath(h.logicalPath);if(!c)throw new Error("Direct snapshot requires non-empty logicalPath");let d=Date.now();try{u("[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{lt("[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)&&u("[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{}y.set(c,D)}try{u("[GitHistoryStore] native hash-object phase complete",{traceId:a,repoDir:this.repoDir,memberCount:t.length,durationMs:Date.now()-w})}catch{}let m=I.join(this.repoDir,`.pando-index-${process.pid}-${Date.now()}`),f={...process.env,GIT_INDEX_FILE:m};try{let l=Date.now();try{u("[GitHistoryStore] native read-tree begin",{traceId:a,repoDir:this.repoDir,tmpIndex:m})}catch{}v("git",["read-tree","--empty"],{cwd:this.repoDir,env:f});try{u("[GitHistoryStore] native read-tree complete",{traceId:a,repoDir:this.repoDir,durationMs:Date.now()-l})}catch{}let h=Array.from(y.keys()).sort((C,k)=>C.localeCompare(k)),S=Date.now();for(let C of h){let k=y.get(C);v("git",["update-index","--add","--cacheinfo","100644",k,C],{cwd:this.repoDir,env:f})}try{u("[GitHistoryStore] native update-index complete",{traceId:a,repoDir:this.repoDir,entryCount:h.length,durationMs:Date.now()-S})}catch{}let c=Date.now();try{u("[GitHistoryStore] native write-tree begin",{traceId:a,repoDir:this.repoDir,entryCount:h.length})}catch{}let d=v("git",["write-tree"],{cwd:this.repoDir,env:f}).toString("utf8").trim();try{u("[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),i&&D.push("-m",i);let E=Date.now();try{u("[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{u("[GitHistoryStore] native commit-tree complete",{traceId:a,repoDir:this.repoDir,commitOid:T,durationMs:Date.now()-E})}catch{}let j=Date.now();try{u("[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{u("[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(m)}catch{}try{o.unlinkSync(`${m}.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(),u(`[${new Date().toISOString()}] [GitHistoryStore] Creating snapshot (streaming) with ${t.members.length} members`);let i=[...t.members].map(b=>({...b,logicalPath:this.normalizeLogicalPath(b.logicalPath)})).filter(b=>!!b.logicalPath).sort((b,x)=>b.logicalPath.localeCompare(x.logicalPath)),n=this.gitExists()&&!e;try{n?B.info("[GitHistoryStore] Using native git snapshot path",{members:i.length}):B.info("[GitHistoryStore] Using JS snapshot path",{members:i.length,git:!1,cancellable:!!e})}catch{}if(n)try{return this.createSnapshotNative(i,t.parent,t.message,t.when).snapshotId}catch(b){try{B.warn(`[GitHistoryStore] Native git path failed; falling back to JS path: ${b?.message||b}`)}catch{}}let s=Date.now(),a=b=>b.toString("hex"),p=b=>Et("crypto").createHash("sha1").update(b).digest(),y=b=>{let x=Buffer.from(`blob ${b.length}\0`,"utf8"),A=Buffer.concat([x,b]);return a(p(A))},w=Math.max(1,Math.min(64,Number(process.env.SNAPSHOT_BLOB_CONCURRENCY||16))),m=String(process.env.SNAPSHOT_PRECOMPUTE_OIDS||"1")!=="0";try{B.info("[GitHistoryStore] JS snapshot settings",{SNAPSHOT_BLOB_CONCURRENCY:w,SNAPSHOT_PRECOMPUTE_OIDS:m})}catch{}let f=new Map,l=0,h=0,S=0,c=i.map(b=>({path:b.logicalPath,buf:Buffer.isBuffer(b.content)?b.content:Buffer.from(b.content)})),d=async()=>{for(;;){P(e,"Snapshot cancelled");let b;if(l<c.length)b=c[l++];else break;let x;if(m)try{let A=y(b.buf);await this.hasObject(A)&&(x=A,S++)}catch{}if(x||(x=await g.writeBlob({fs:o,dir:this.repoDir,blob:b.buf}),h++),P(e,"Snapshot cancelled"),!x)throw new Error("Failed to compute blob object id");f.set(b.path,x)}},D=Array.from({length:Math.min(w,c.length)},()=>d());await Promise.all(D),P(e,"Snapshot cancelled"),u(`[GitHistoryStore] Blobs ready wrote=${h} skipped=${S} in ${Date.now()-s}ms`);let E=new _(this.repoDir),T=Array.from(f.keys()).sort((b,x)=>b.localeCompare(x));for(let b of T)P(e,"Snapshot cancelled"),await E.addFile(b,f.get(b));P(e,"Snapshot cancelled");let j=Date.now(),C=await E.finish();u(`[GitHistoryStore] Wrote tree(s). root=${C} in ${Date.now()-j}ms`);let k=t.parent;if(!k)try{k=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(),F=await g.commit({fs:o,dir:this.repoDir,message:t.message,parent:k?[k]:[],tree:C,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:F,force:!0}),u(`[GitHistoryStore] Snapshot commit oid ${F}`),F}async listSnapshotMembers(t){await this.ensureInitialized();let e=s=>Math.round((s||0)/(1024*1024)),i=()=>{let s=process.memoryUsage();return`rss=${e(s.rss)}MB heapUsed=${e(s.heapUsed)}MB heapTotal=${e(s.heapTotal)}MB`},n=[];return u(`[${new Date().toISOString()}] [GitHistoryStore] listSnapshotMembers(${t}) start (${i()})`),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 y=await a.oid();n.push({logicalPath:s,objectId:y})}}}),u(`[${new Date().toISOString()}] [GitHistoryStore] listSnapshotMembers(${t}) -> ${n.length} blobs`),n.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(),u(`[${new Date().toISOString()}] [GitHistoryStore] Creating snapshot from disk (streaming) with ${t.relPaths.length} members`);let i=[...t.relPaths].map(h=>this.normalizeLogicalPath(h)).filter(Boolean).sort((h,S)=>h.localeCompare(S)),n=Date.now(),s=new _(this.repoDir),a=0;for(let h of i){P(e,"Snapshot cancelled");let S=I.join(this.baseDir,h);try{let c=o.readFileSync(S),d=await g.writeBlob({fs:o,dir:this.repoDir,blob:c});await s.addFile(h,d),a++}catch{}}u(`[GitHistoryStore] Wrote ${a} blobs (disk) in ${Date.now()-n}ms`),P(e,"Snapshot cancelled");let p=Date.now(),y=await s.finish();u(`[GitHistoryStore] Wrote tree(s) (disk). root=${y} in ${Date.now()-p}ms`);let w=t.parent;if(!w)try{w=await g.resolveRef({fs:o,dir:this.repoDir,ref:"HEAD"})}catch{}P(e,"Snapshot cancelled");let m=t.when?Math.floor(t.when/1e3):Math.floor(Date.now()/1e3),f=new Date(m*1e3).getTimezoneOffset(),l=await g.commit({fs:o,dir:this.repoDir,message:t.message,parent:w?[w]:[],tree:y,author:{name:"ast-db",email:"ast-db@example.com",timestamp:m,timezoneOffset:f},committer:{name:"ast-db",email:"ast-db@example.com",timestamp:m,timezoneOffset:f}});return await g.writeRef({fs:o,dir:this.repoDir,ref:"refs/heads/master",value:l,force:!0}),u(`[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))}u(`[${new Date().toISOString()}] [GitHistoryStore] Creating snapshot from disk+captured (diff) with ${t.relPaths.length} members, captured=${e.size}`);let i=[...t.relPaths].map(c=>this.normalizeLogicalPath(c)).filter(Boolean).sort((c,d)=>c.localeCompare(d)),n=Date.now(),s=new _(this.repoDir),a=0,p=0,y=0;for(let c of i)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++:y++}catch{}u(`[GitHistoryStore] Wrote ${a} blobs (captured=${p}, disk=${y}) in ${Date.now()-n}ms`);let w=Date.now(),m=await s.finish();u(`[GitHistoryStore] Wrote tree(s) (disk+captured). root=${m} in ${Date.now()-w}ms`);let f=t.parent;if(!f)try{f=await g.resolveRef({fs:o,dir:this.repoDir,ref:"HEAD"})}catch{}if(f)try{let{commit:c}=await g.readCommit({fs:o,dir:this.repoDir,oid:f});if(c?.tree===m){u("[GitHistoryStore] Snapshot identical to parent; skipping commit",{parentOid:f,tree:m});let d=await this.readCommitParent(f);return{snapshotId:f,identical:!0,parent:d}}}catch(c){u("[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),h=new Date(l*1e3).getTimezoneOffset(),S=await g.commit({fs:o,dir:this.repoDir,message:t.message,parent:f?[f]:[],tree:m,author:{name:"ast-db",email:"ast-db@example.com",timestamp:l,timezoneOffset:h},committer:{name:"ast-db",email:"ast-db@example.com",timestamp:l,timezoneOffset:h}});return await g.writeRef({fs:o,dir:this.repoDir,ref:"refs/heads/master",value:S,force:!0}),u(`[GitHistoryStore] Snapshot commit oid ${S}`),{snapshotId:S,identical:!1,parent:f}}async createSnapshotFromDiskIfChanged(t){await this.ensureInitialized(),u(`[${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,h)=>l.localeCompare(h)),i=Date.now(),n=new _(this.repoDir),s=0;for(let l of e){let h=I.join(this.baseDir,l);try{let S=o.readFileSync(h),c=await g.writeBlob({fs:o,dir:this.repoDir,blob:S});await n.addFile(l,c),s++}catch{}}u(`[GitHistoryStore] Wrote ${s} blobs (disk) in ${Date.now()-i}ms`);let a=Date.now(),p=await n.finish();u(`[GitHistoryStore] Wrote tree(s) (disk). root=${p} in ${Date.now()-a}ms`);let y=t.parent;if(!y)try{y=await g.resolveRef({fs:o,dir:this.repoDir,ref:"HEAD"})}catch{}if(y)try{let{commit:l}=await g.readCommit({fs:o,dir:this.repoDir,oid:y});if(l?.tree===p){u("[GitHistoryStore] Snapshot identical to parent; skipping commit",{parentOid:y,tree:p});let h=await this.readCommitParent(y);return{snapshotId:y,identical:!0,parent:h}}}catch(l){u("[GitHistoryStore] Parent read failed; proceeding with commit",{error:l?.message||String(l)})}let w=t.when?Math.floor(t.when/1e3):Math.floor(Date.now()/1e3),m=new Date(w*1e3).getTimezoneOffset(),f=await g.commit({fs:o,dir:this.repoDir,message:t.message,parent:y?[y]:[],tree:p,author:{name:"ast-db",email:"ast-db@example.com",timestamp:w,timezoneOffset:m},committer:{name:"ast-db",email:"ast-db@example.com",timestamp:w,timezoneOffset:m}});return await g.writeRef({fs:o,dir:this.repoDir,ref:"refs/heads/master",value:f,force:!0}),u(`[GitHistoryStore] Snapshot commit oid ${f}`),{snapshotId:f,identical:!1,parent:y}}};function q(r){let t={},e=r.split(/\r?\n/);for(let i of e){let n=i.match(/^([A-Za-z0-9-]+):\s*(.*)$/);n&&(t[n[1]]=n[2])}return t}import It from"fs";import H from"path";import ht from"ignore";import{Project as Qt}from"ts-morph";var Mt=new Set(["node_modules",".git",".pando-data"]);function kt(r){let t=tt(r).split("/").filter(Boolean);for(let e=0;e<t.length;e+=1)if(Mt.has(t[e])||t[e]===".clj-kondo"&&t[e+1]===".cache")return!0;return!1}function dt(r,t){if(!t?.length)return null;let e=t.map(n=>n.replace(/^\/+/,"").replace(/\/+$/,"")).filter(Boolean).map(n=>`${n}/`);if(!e.length)return null;let i=n=>n.replace(/\\/g,"/");return n=>{let s=i(H.relative(r,n));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 i=t?.shouldExclude||null,n=[],s=H.resolve(t?.gitignoreRoot??r),a=ot(s),p=J(s,""),y=ht().add(p),w=[{dir:r,relDir:"",rules:p,ig:y}];for(;w.length;){let m=w.pop(),f=[];try{f=It.readdirSync(m.dir,{withFileTypes:!0})}catch{continue}for(let l of f){let h=H.join(m.dir,l.name);if(!(typeof l.isSymbolicLink=="function"&&l.isSymbolicLink())&&!kt(h)){if(l.isDirectory()){if(i?.(h))continue;let c=m.relDir?`${m.relDir}/${l.name}`:l.name,d=tt(c);if(m.ig.ignores(d)&&!a?.hasDescendant(d))continue;let D=m.rules.slice(),E=J(h,d);E.length&&D.push(...E);let T=E.length?ht().add(D):m.ig;w.push({dir:h,relDir:d,rules:D,ig:T})}else if(l.isFile()){if(i?.(h))continue;let c=tt(H.relative(r,h));if(m.ig.ignores(c)&&!a?.hasFile(c))continue;if(e===null)n.push(h);else{let d=H.extname(l.name).toLowerCase();e.has(d)&&n.push(h)}}}}}return n}function tt(r){return r.replace(/\\/g,"/")}K();xt();K();import et from"fs";import Ct from"path";function gt(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 st,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 rt(r,t){let e=st(r),
|
|
45
|
+
`)}function rt(r,t){let e=st(r),i=Ct.dirname(e);et.existsSync(i)||et.mkdirSync(i,{recursive:!0});let n=!!t?.readonly;if(n&&!et.existsSync(e)){let a=new Q(e,{});try{gt(a)}finally{try{a.close()}catch{}}}let s=new Q(e,n?{readonly:!0}:{});if(!n)gt(s);else try{s.pragma("busy_timeout = 10000")}catch{}return s}var O="",nt=[],bt=null;at();function mt(r,t){if(Array.isArray(t)){nt=t.slice(),bt=dt(r,nt);try{R("SnapshotWorker","exclude state updated",{excludeDirs:nt})}catch{}}}function At(r){let t=pt(r,{shouldExclude:bt??void 0,gitignoreRoot:r});try{R("SnapshotWorker","gatherSnapshotFiles",{count:t.length,sample:t.slice(0,20).map(e=>it.relative(r,e).replace(/\\/g,"/"))})}catch{}return t}function ft(r){try{return r.prepare("SELECT snapshot_id FROM snapshots ORDER BY timestamp DESC, rowid DESC LIMIT 1").get()?.snapshot_id}catch{return}}function ut(r,t,e,i,n){let s=n||null;r.prepare("INSERT OR REPLACE INTO snapshots (snapshot_id, parents, timestamp, message) VALUES (?, ?, ?, ?)").run(t,s,Math.floor(e/1e3),i)}async function yt(r,t,e){try{return await r.readCommitParent(t)||e}catch{return e}}function X(r,t){let e=new Map;try{let i=r.prepare("SELECT logical_path, object_id FROM snapshot_members WHERE snapshot_id = ?").all(t);for(let n of i)e.set(n.logical_path,n.object_id)}catch{}return e}function St(r,t,e){r.transaction(n=>{let s=r.prepare("INSERT OR REPLACE INTO snapshot_members (snapshot_id, logical_path, object_id, lang, encoding) VALUES (?, ?, ?, ?, ?)");for(let a of n)s.run(t,a.logical_path,a.object_id,a.lang??null,a.encoding??null)})(e)}$.on("message",async r=>{if(r.cmd==="init"){O=r.projectRoot,mt(O,r.excludeDirs),$.postMessage({type:"ready"});return}if(r.excludeDirs&&mt(O,r.excludeDirs),r.cmd==="snapshot"){let t=rt(O);try{let e=Date.now(),i=r.payload?.when||Date.now(),n=r.payload?.message||"ast: snapshot",s=r.payload?.parent||ft(t),a=Date.now(),p=r.payload?.files&&r.payload.files.length?r.payload.files:At(O);try{R("SnapshotWorker","snapshot start",{when:i,message:n,parent:s,fileCount:p.length,filesPreview:p.slice(0,50).map(D=>it.relative(O,D).replace(/\\/g,"/"))})}catch{}let y=Date.now()-a,w=p.map(D=>it.relative(O,D).replace(/\\/g,"/")),m=(r.payload?.captured||[]).map(D=>({logicalPath:D.logicalPath,objectId:"unknown",content:Buffer.from(D.contentB64,"base64")})),f=new L({baseDir:O});await f.ensureInitialized();let l=Date.now(),h=m.length>0?await f.createSnapshotFromDiskAndCapturedIfChanged({message:n,when:i,relPaths:w,capturedMembers:m,parent:s}):await f.createSnapshotFromDiskIfChanged({message:n,when:i,relPaths:w,parent:s}),S=h.snapshotId,c=await yt(f,S,h.parent||s),d=Date.now()-l;if(h.identical){let D=Date.now()-e;try{R("SnapshotWorker","snapshot skipped (identical)",{snapshotId:S,parent:c,files:w.length,timingsMs:{list:y,snapshot:d,total:D}})}catch{}$.postMessage({type:"result",snapshotId:S,parent:c,files:w.length,diff:{added:0,changed:0,deleted:0}})}else{let D=Date.now();ut(t,S,i,n,c);try{let M=q(n||""),W=t.prepare("INSERT OR REPLACE INTO snapshot_meta (snapshot_id, key, value) VALUES (?, ?, ?)");t.transaction(()=>{for(let[Dt,Tt]of Object.entries(M))W.run(S,Dt,Tt)})()}catch{}let E=Date.now()-D,T=Date.now(),j=await f.listSnapshotMembers(S),C=Date.now()-T,k=j.map(M=>({logical_path:M.logicalPath,object_id:M.objectId})),N=Date.now();St(t,S,k);let G=Date.now()-N,F=c?X(t,c):new Map,b=X(t,S),x=[],A=[],z=[];for(let[M,W]of b){let Y=F.get(M);Y?Y!==W&&A.push(M):x.push(M)}for(let[M]of F)b.has(M)||z.push(M);let wt=Date.now()-e;try{R("SnapshotWorker","snapshot complete",{snapshotId:S,parent:c,files:w.length,timingsMs:{list:y,snapshot:d,metadata:E,listMembers:C,membersWrite:G,total:wt},diff:{added:x.length,changed:A.length,deleted:z.length}})}catch{}$.postMessage({type:"result",snapshotId:S,parent:c,files:w.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(O);try{let e=r.payload?.when||Date.now(),i=r.payload?.message||"ast: snapshot",n=r.payload?.parent||ft(t);try{R("SnapshotWorker","snapshot-captured start",{when:e,message:i,parent:n,members:r.payload.members.length})}catch{}let s=new L({baseDir:O});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:i,when:e,members:a}),y=await yt(s,p,n);ut(t,p,e,i,y);try{let d=q(i||""),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 w=await s.listSnapshotMembers(p),m=w.map(d=>({logical_path:d.logicalPath,object_id:d.objectId}));St(t,p,m);let f=y?X(t,y):new Map,l=X(t,p),h=[],S=[],c=[];for(let[d,D]of l){let E=f.get(d);E?E!==D&&S.push(d):h.push(d)}for(let[d]of f)l.has(d)||c.push(d);try{R("SnapshotWorker","snapshot-captured complete",{snapshotId:p,parent:y,members:w.length,diff:{added:h.length,changed:S.length,deleted:c.length}})}catch{}$.postMessage({type:"result",snapshotId:p,parent:y,files:w.length,diff:{added:h.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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pando-ai",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.2",
|
|
4
|
+
"description": "Pando — an AI coding firewall. Supervises codex and claude transparently: reduces the agent to the Pando MCP tool, intercepts/rewrites everything else, and keeps code local.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"pando-ai": "bin/pando-ai.js"
|
|
7
7
|
},
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
"tools/csharp-indexer/CSharpIndexer.csproj",
|
|
18
18
|
"tools/csharp-indexer/src/",
|
|
19
19
|
"tools/csharp-indexer/bin/pando-csharp-indexer",
|
|
20
|
-
"tools/csharp-indexer/bin/pando-csharp-indexer.cmd"
|
|
20
|
+
"tools/csharp-indexer/bin/pando-csharp-indexer.cmd",
|
|
21
|
+
"tools/clang-indexer/bin/pando-clang-indexer"
|
|
21
22
|
],
|
|
22
23
|
"engines": {
|
|
23
24
|
"node": ">=22.5.0"
|
|
@@ -41,10 +41,9 @@
|
|
|
41
41
|
"move-clojure-namespace(namespace, newFile)",
|
|
42
42
|
"rename-clojure-namespace(oldNamespace, newNamespace)",
|
|
43
43
|
"rename(path, newName, expectedHash, boundary, requireConfirmation, confirmed, includeStrings, includeComments, page, reachabilityDepth, reachabilityOptions)",
|
|
44
|
-
"delete(path, expectedHashes, forceDeleteReferencesMayBreakSyntax
|
|
44
|
+
"delete(path, expectedHashes, forceDeleteReferencesMayBreakSyntax)",
|
|
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)",
|
|
48
47
|
"replace-body(path, expectedHash, with, forceReplaceWillBreakSyntax)",
|
|
49
48
|
"change-signature(path, expectedHash, parameters, returnType, makeAsync, makeSyncFromAsync, forceChangeWillBreakSyntax, boundary, batchSaves)",
|
|
50
49
|
"filter-map-reduce(query, transforms, apply)",
|
|
@@ -64,7 +63,7 @@
|
|
|
64
63
|
"name": "read-this-first",
|
|
65
64
|
"category": "pando",
|
|
66
65
|
"signature": "read-this-first()",
|
|
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,
|
|
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 delete. 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.",
|
|
68
67
|
"parameters": {
|
|
69
68
|
"type": "object",
|
|
70
69
|
"properties": {},
|
|
@@ -147,7 +146,7 @@
|
|
|
147
146
|
"name": "find-nodes",
|
|
148
147
|
"category": "pando",
|
|
149
148
|
"signature": "find-nodes(lang, snapshot, scope, datalog, prefilter, include, page)",
|
|
150
|
-
"description": "Search code nodes with per-file lazy Datalog execution. Candidate files are processed in deterministic order
|
|
149
|
+
"description": "Search code nodes with per-file lazy Datalog execution. Candidate files are discovered from indexed file/module metadata, then processed in deterministic order one file at a time; exact AST facts are materialized per candidate file as needed, and execution stops as soon as page.limit items have been emitted. Pagination is cursor-only via page.cursor. page.limit defaults to 5 when omitted and caps at 20. Optional prefilter.fts narrows candidate files before Datalog execution. This operation is per-file structural search only; cross-file Datalog joins are not supported here. AST nodes do not need names. For arbitrary unnamed nodes such as if/return/call/argument/etc., first find a stable enclosing node such as a function/class, then re-run find-nodes with scope.node and use :node/kind, :node/text, :node/parent, :node/role, :node/index-in-parent, include.self, and include.parents to disambiguate the exact child node before editing.",
|
|
151
150
|
"parameters": {
|
|
152
151
|
"type": "object",
|
|
153
152
|
"properties": {
|
|
@@ -287,7 +286,7 @@
|
|
|
287
286
|
"properties": {
|
|
288
287
|
"fts": {
|
|
289
288
|
"type": "string",
|
|
290
|
-
"description": "SQLite
|
|
289
|
+
"description": "SQLite FTS5 MATCH query passed through unchanged after trimming; used only to narrow candidate files."
|
|
291
290
|
}
|
|
292
291
|
},
|
|
293
292
|
"additionalProperties": false
|
|
@@ -409,6 +408,45 @@
|
|
|
409
408
|
"limit": 5,
|
|
410
409
|
"cursor": null
|
|
411
410
|
}
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
"lang": [
|
|
414
|
+
"ts",
|
|
415
|
+
"js"
|
|
416
|
+
],
|
|
417
|
+
"scope": {
|
|
418
|
+
"node": {
|
|
419
|
+
"path": "src/service.ts#120-520:FunctionDeclaration",
|
|
420
|
+
"expectedHash": "p123:c456"
|
|
421
|
+
}
|
|
422
|
+
},
|
|
423
|
+
"datalog": {
|
|
424
|
+
"query": "[:find ?n ?parent ?role ?idx :where [?n :node/kind \"IfStatement\"] [?n :node/parent ?parent] [?n :node/role ?role] [?n :node/index-in-parent ?idx]]",
|
|
425
|
+
"bindings": [
|
|
426
|
+
"?n",
|
|
427
|
+
"?parent",
|
|
428
|
+
"?role",
|
|
429
|
+
"?idx"
|
|
430
|
+
],
|
|
431
|
+
"result": {
|
|
432
|
+
"nodeVar": "?n",
|
|
433
|
+
"columns": {
|
|
434
|
+
"parent": "?parent",
|
|
435
|
+
"role": "?role",
|
|
436
|
+
"index": "?idx"
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
},
|
|
440
|
+
"include": {
|
|
441
|
+
"self": true,
|
|
442
|
+
"parents": true,
|
|
443
|
+
"topN": 5,
|
|
444
|
+
"maxChars": 4000
|
|
445
|
+
},
|
|
446
|
+
"page": {
|
|
447
|
+
"limit": 10,
|
|
448
|
+
"cursor": null
|
|
449
|
+
}
|
|
412
450
|
}
|
|
413
451
|
]
|
|
414
452
|
},
|
|
@@ -700,7 +738,7 @@
|
|
|
700
738
|
"name": "query-db",
|
|
701
739
|
"category": "pando",
|
|
702
740
|
"signature": "query-db(sql, maxRows)",
|
|
703
|
-
"description": "Execute
|
|
741
|
+
"description": "Execute passthrough SQL against all index chunks (readonly), then globally merge and apply ORDER BY/LIMIT/OFFSET semantics in code. Chunk identity is intentionally hidden from results. Use get-db-schema first when exploring. For relationship mining, inspect node_references plus dict_kinds; non-TS find-references/find-callers often resolve at query time via target_symbol_key or target_node_id rather than a generic relationships table. Grouped ORDER BY queries must include an explicit LIMIT. Example: { sql:'SELECT * FROM nodes LIMIT 20' }",
|
|
704
742
|
"parameters": {
|
|
705
743
|
"type": "object",
|
|
706
744
|
"properties": {
|
|
@@ -1311,7 +1349,7 @@
|
|
|
1311
1349
|
{
|
|
1312
1350
|
"name": "delete",
|
|
1313
1351
|
"category": "pando",
|
|
1314
|
-
"signature": "delete(path, expectedHashes, forceDeleteReferencesMayBreakSyntax
|
|
1352
|
+
"signature": "delete(path, expectedHashes, forceDeleteReferencesMayBreakSyntax)",
|
|
1315
1353
|
"description": "Delete code targets and their references. expectedHashes must align with path list and omit '@'. Requires forceDeleteReferencesMayBreakSyntax=true. Example: { path:'src/app.ts#FD:0', expectedHashes:['p123:c456'], forceDeleteReferencesMayBreakSyntax:true }",
|
|
1316
1354
|
"parameters": {
|
|
1317
1355
|
"type": "object",
|
|
@@ -1338,34 +1376,7 @@
|
|
|
1338
1376
|
},
|
|
1339
1377
|
"forceDeleteReferencesMayBreakSyntax": {
|
|
1340
1378
|
"type": "boolean",
|
|
1341
|
-
"description": "Must be true to proceed."
|
|
1342
|
-
},
|
|
1343
|
-
"page": {
|
|
1344
|
-
"type": "object",
|
|
1345
|
-
"description": "Pagination for changedFiles in response (limit 1..100).",
|
|
1346
|
-
"properties": {
|
|
1347
|
-
"offset": {
|
|
1348
|
-
"anyOf": [
|
|
1349
|
-
{
|
|
1350
|
-
"type": "number"
|
|
1351
|
-
},
|
|
1352
|
-
{
|
|
1353
|
-
"type": "string"
|
|
1354
|
-
}
|
|
1355
|
-
]
|
|
1356
|
-
},
|
|
1357
|
-
"limit": {
|
|
1358
|
-
"anyOf": [
|
|
1359
|
-
{
|
|
1360
|
-
"type": "number"
|
|
1361
|
-
},
|
|
1362
|
-
{
|
|
1363
|
-
"type": "string"
|
|
1364
|
-
}
|
|
1365
|
-
]
|
|
1366
|
-
}
|
|
1367
|
-
},
|
|
1368
|
-
"additionalProperties": false
|
|
1379
|
+
"description": "Must be true to proceed. Delete always applies to all resolved references and returns all changed files without pagination."
|
|
1369
1380
|
}
|
|
1370
1381
|
},
|
|
1371
1382
|
"required": [
|
|
@@ -1531,99 +1542,6 @@
|
|
|
1531
1542
|
}
|
|
1532
1543
|
]
|
|
1533
1544
|
},
|
|
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
|
-
},
|
|
1627
1545
|
{
|
|
1628
1546
|
"name": "replace-body",
|
|
1629
1547
|
"category": "pando",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|