toolnet-memory 0.3.12 → 0.3.13

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.
@@ -1,3 +1,3 @@
1
- import"dotenv/config";import{createReadStream as ue}from"node:fs";import{createServer as ut}from"node:http";import{dirname as mt,resolve as Q}from"node:path";import{fileURLToPath as ft}from"node:url";import{existsSync as ge,readFileSync as he}from"node:fs";import{homedir as ye}from"node:os";import{join as Se}from"node:path";function be(n){let t=n.trim();return t.length>=2&&t.startsWith('"')&&t.endsWith('"')?(t=t.slice(1,-1),t.replace(/\\n/g,`
2
- `).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):t.length>=2&&t.startsWith("'")&&t.endsWith("'")?t.slice(1,-1):t}function xe(){let n=process.env.TOOLNET_GLOBAL_ENV??Se(ye(),".config","toolnet-memory",".env");if(!ge(n))return;let t=he(n,"utf8");for(let e of t.split(/\r?\n/)){let s=e.trim();if(!s||s.startsWith("#"))continue;s.startsWith("export ")&&(s=s.slice(7));let a=s.indexOf("=");if(a<=0)continue;let c=s.slice(0,a).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(c)&&process.env[c]===void 0&&(process.env[c]=be(s.slice(a+1)))}}xe();function N(n,t){return n===void 0?t:["1","true","yes","on"].includes(n.toLowerCase())}function L(n,t){if(!n)return t;let e=Number(n);return Number.isFinite(e)?e:t}function te(){return{memory:{autoCapture:N(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:N(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:N(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:N(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:L(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:L(process.env.MEMORY_RERANK_TOP,10),finalContext:L(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:L(process.env.MEMORY_TOKEN_BUDGET,2e3)},storage:{provider:process.env.MEMORY_STORAGE_PROVIDER??"huggingface",r2:{accountId:process.env.R2_ACCOUNT_ID,bucket:process.env.R2_BUCKET,accessKeyId:process.env.R2_ACCESS_KEY_ID,secretAccessKey:process.env.R2_SECRET_ACCESS_KEY},s3:{endpoint:process.env.S3_ENDPOINT,region:process.env.S3_REGION,bucket:process.env.S3_BUCKET,accessKeyId:process.env.S3_ACCESS_KEY_ID,secretAccessKey:process.env.S3_SECRET_ACCESS_KEY,forcePathStyle:N(process.env.S3_FORCE_PATH_STYLE,!1)},huggingface:{namespace:process.env.HF_NAMESPACE,bucket:process.env.HF_BUCKET,accessKeyId:process.env.HF_S3_ACCESS_KEY_ID,secretAccessKey:process.env.HF_S3_SECRET_ACCESS_KEY},localRoot:process.env.MEMORY_LOCAL_STORAGE_PATH},cache:{maxMb:L(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as Ce}from"node:crypto";import{existsSync as J,mkdirSync as we,readFileSync as Ee,renameSync as ve,writeFileSync as Pe}from"node:fs";import{basename as ke,dirname as K,join as B,parse as oe,resolve as I}from"node:path";var ne=".toolnet",je="project.json";function Re(n){return Ce("sha256").update(n).digest("hex").slice(0,16)}function Y(n){return B(n,ne,je)}function Te(n){return J(Y(n))}function Ae(n,t){let e=I(n),s=oe(e).root;for(;;){if(Te(e))return e;if(e===s||t&&e===I(t))break;let a=K(e);if(a===e)break;e=a}return null}function Me(n){let t=I(n),e=oe(t).root,s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(s.some(c=>J(B(t,c))))return t;if(t===e)break;let a=K(t);if(a===t)break;t=a}return I(n)}function Ne(n){let t;try{t=JSON.parse(Ee(n,"utf8"))}catch(a){throw new Error(`Invalid ToolNet project manifest: ${n}: ${a instanceof Error?a.message:String(a)}`)}if(!t||typeof t!="object")throw new Error(`Invalid ToolNet project manifest: ${n}`);let e=t;if(typeof e.id!="string"||!e.id.trim())throw new Error(`ToolNet project manifest is missing id: ${n}`);if(typeof e.name!="string"||!e.name.trim())throw new Error(`ToolNet project manifest is missing name: ${n}`);let s=new Date().toISOString();return{version:1,id:e.id,name:e.name,remote:typeof e.remote=="string"&&e.remote.trim()?e.remote:e.name,rootPath:typeof e.rootPath=="string"?e.rootPath:K(K(n)),createdAt:typeof e.createdAt=="string"?e.createdAt:s,updatedAt:typeof e.updatedAt=="string"?e.updatedAt:s,graphVersion:typeof e.graphVersion=="number"?e.graphVersion:0,memoryVersion:typeof e.memoryVersion=="number"?e.memoryVersion:0,metadata:e.metadata&&typeof e.metadata=="object"?e.metadata:void 0}}function re(n,t){let e=B(n,ne);we(e,{recursive:!0});let s=Y(n),a=`${s}.tmp-${process.pid}`;Pe(a,JSON.stringify(t,null,2)+`
3
- `,{encoding:"utf8",mode:384}),ve(a,s)}function se(n,t){return{id:n.id,name:n.name,remote:n.remote,rootPath:t,createdAt:n.createdAt,updatedAt:n.updatedAt,graphVersion:n.graphVersion,memoryVersion:n.memoryVersion,metadata:n.metadata}}var z=class{detect(t=process.cwd()){let e=I(t),s=Me(e),c=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(v=>J(B(s,v))),p=Ae(e,c?s:void 0);if(p){let v=Y(p),j=Ne(v);return j.rootPath!==p&&(j.rootPath=p,j.updatedAt=new Date().toISOString(),re(p,j)),se(j,p)}let k=new Date().toISOString(),b=ke(s),E={version:1,id:Re(s),name:b,remote:b,rootPath:s,createdAt:k,updatedAt:k,graphVersion:0,memoryVersion:0};return re(s,E),se(E,s)}};import*as w from"typescript";var A=class{symbols=new Map;edges=new Map;addSymbol(t){this.symbols.set(t.id,t)}addEdge(t){this.edges.set(t.id,t)}getSymbol(t){return this.symbols.get(t)}findByName(t,e){return[...this.symbols.values()].filter(s=>s.projectId===t&&(s.name===e||s.qualifiedName===e))}allSymbols(t){return[...this.symbols.values()].filter(e=>e.projectId===t)}allEdges(t){return[...this.edges.values()].filter(e=>e.projectId===t)}import(t,e){for(let s of t)this.addSymbol(s);for(let s of e)this.addEdge(s)}clearProject(t){for(let[e,s]of this.symbols)s.projectId===t&&this.symbols.delete(e);for(let[e,s]of this.edges)s.projectId===t&&this.edges.delete(e)}};import{execFile as $e}from"node:child_process";import{promisify as Fe}from"node:util";var kr=Fe($e);import*as P from"typescript";import*as m from"typescript";var Gs=new Set([m.SyntaxKind.EqualsToken,m.SyntaxKind.PlusEqualsToken,m.SyntaxKind.MinusEqualsToken,m.SyntaxKind.AsteriskEqualsToken,m.SyntaxKind.SlashEqualsToken,m.SyntaxKind.PercentEqualsToken,m.SyntaxKind.QuestionQuestionEqualsToken,m.SyntaxKind.AmpersandAmpersandEqualsToken,m.SyntaxKind.BarBarEqualsToken]);var H=class{constructor(t){this.graph=t}graph;build(t,e,s,a){let c=this.graph.allSymbols(t),p=this.graph.allEdges(t),k=new Map(e?.layers.map(o=>[o.filePath,o.layer])??[]),b=new Map;for(let o of e?.clusters??[])for(let r of o.files)b.set(r,{id:o.id,label:o.label});let E=new Map(e?.hotspots.map(o=>[o.filePath,o.score])??[]),v=new Map(s?.deadCode.map(o=>[o.symbolId,{confidence:o.confidence,score:o.score}])??[]),j=new Map,M=new Map;for(let o of p)M.set(o.from,(M.get(o.from)??0)+1),j.set(o.to,(j.get(o.to)??0)+1);let R=[];for(let o=0;o<c.length;o++){let r=c[o],l=b.get(r.filePath);R.push({id:r.id,name:r.name,qualifiedName:r.qualifiedName,type:r.type,filePath:r.filePath,layer:k.get(r.filePath),cluster:l?.id,clusterLabel:l?.label,hotspotScore:E.get(r.filePath),deadCode:v.get(r.id),incoming:j.get(r.id)??0,outgoing:M.get(r.id)??0}),((o+1)%100===0||o===c.length-1)&&a?.({current:o+1,total:c.length,phase:"nodes"})}let F=new Set(R.map(o=>o.id)),T=p.filter(o=>F.has(o.from)&&F.has(o.to)),D=[];for(let o=0;o<T.length;o++){let r=T[o];D.push({source:r.from,target:r.to,type:r.type,confidence:typeof r.metadata?.confidence=="string"?r.metadata.confidence:void 0,resolver:typeof r.metadata?.resolver=="string"?r.metadata.resolver:void 0}),((o+1)%100===0||o===T.length-1)&&a?.({current:o+1,total:T.length,phase:"links"})}return{version:1,projectId:t,generatedAt:new Date().toISOString(),summary:{nodes:R.length,links:D.length,files:new Set(R.map(o=>o.filePath)).size,clusters:new Set(R.map(o=>o.cluster).filter(Boolean)).size},nodes:R,links:D}}};import{homedir as ct}from"node:os";import{join as lt}from"node:path";import{DeleteObjectCommand as Ge,GetObjectCommand as Ke,HeadObjectCommand as ze,ListObjectsV2Command as Be,PutObjectCommand as He,S3Client as Ve}from"@aws-sdk/client-s3";import{getSignedUrl as Ue}from"@aws-sdk/s3-request-presigner";var V=class{name="huggingface";client;bucket;constructor(t){this.bucket=t.bucket,this.client=new Ve({region:"us-east-1",endpoint:`https://s3.hf.co/${t.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}})}async put(t,e,s="application/octet-stream"){let a=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new He({Bucket:this.bucket,Key:t,Body:a,ContentType:s}))}async get(t){let e=await Ue(this.client,new Ke({Bucket:this.bucket,Key:t}),{expiresIn:60}),s=await fetch(e,{redirect:"follow"});if(s.status===404)return null;if(!s.ok)throw new Error(`HF download failed: ${s.status} ${s.statusText}`);return new Uint8Array(await s.arrayBuffer())}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await this.client.send(new ze({Bucket:this.bucket,Key:t})),!0}catch(e){if(typeof e=="object"&&e!==null&&"$metadata"in e&&e.$metadata?.httpStatusCode===404)return!1;throw e}}async delete(t){await this.client.send(new Ge({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],s;do{let a=await this.client.send(new Be({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:s}));for(let c of a.Contents??[])c.Key&&e.push({key:c.Key,size:c.Size,updatedAt:c.LastModified?.toISOString()});s=a.IsTruncated?a.NextContinuationToken:void 0}while(s);return e}};import{access as ie,mkdir as We,readFile as Je,readdir as Ye,rm as qe,stat as ae,writeFile as Qe}from"node:fs/promises";import{dirname as Xe,join as Ze,relative as ce,resolve as et}from"node:path";var O=class{constructor(t){this.root=t}root;name="local";path(t){let e=t.replace(/^\/+/,"");return et(this.root,e)}async put(t,e){let s=this.path(t);await We(Xe(s),{recursive:!0}),await Qe(s,e)}async get(t){try{return await Je(this.path(t))}catch(e){if(typeof e=="object"&&e!==null&&"code"in e&&e.code==="ENOENT")return null;throw e}}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await ie(this.path(t)),!0}catch{return!1}}async delete(t){await qe(this.path(t),{force:!0})}async list(t=""){let e=this.path(t),s=[];try{await ie(e)}catch{return s}let a=async p=>{let k=await Ye(p,{withFileTypes:!0});for(let b of k){let E=Ze(p,b.name);if(b.isDirectory()){await a(E);continue}let v=await ae(E);s.push({key:ce(this.root,E),size:v.size,updatedAt:v.mtime.toISOString()})}},c=await ae(e);return c.isDirectory()?await a(e):s.push({key:ce(this.root,e),size:c.size,updatedAt:c.mtime.toISOString()}),s}};import{DeleteObjectCommand as tt,GetObjectCommand as rt,HeadObjectCommand as st,ListObjectsV2Command as ot,PutObjectCommand as nt,S3Client as it}from"@aws-sdk/client-s3";import{getSignedUrl as at}from"@aws-sdk/s3-request-presigner";var _=class{name;client;bucket;constructor(t){this.name=t.name??"s3",this.bucket=t.bucket,this.client=new it({region:t.region??"us-east-1",endpoint:t.endpoint||void 0,forcePathStyle:t.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}})}async put(t,e,s="application/octet-stream"){let a=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new nt({Bucket:this.bucket,Key:t,Body:a,ContentType:s}))}async get(t){let e=await at(this.client,new rt({Bucket:this.bucket,Key:t}),{expiresIn:60}),s=await fetch(e,{redirect:"follow"});if(s.status===404)return null;if(!s.ok)throw new Error(`${this.name} download failed: ${s.status} ${s.statusText}`);return new Uint8Array(await s.arrayBuffer())}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await this.client.send(new st({Bucket:this.bucket,Key:t})),!0}catch(e){if(typeof e=="object"&&e!==null&&"$metadata"in e&&e.$metadata?.httpStatusCode===404)return!1;throw e}}async delete(t){await this.client.send(new tt({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],s;do{let a=await this.client.send(new ot({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:s}));for(let c of a.Contents??[])c.Key&&e.push({key:c.Key,size:c.Size,updatedAt:c.LastModified?.toISOString()});s=a.IsTruncated?a.NextContinuationToken:void 0}while(s);return e}};function q(n,t){return console.warn(t),new O(n)}function le(n){let t=n.localRoot??lt(ct(),".toolnet-memory","storage");if(n.provider==="r2"){let e=n.r2;return e?.accountId&&e.bucket&&e.accessKeyId&&e.secretAccessKey?new _({name:"r2",endpoint:`https://${e.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:e.bucket,forcePathStyle:!0,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):q(t,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(n.provider==="s3"){let e=n.s3;return e?.bucket&&e.accessKeyId&&e.secretAccessKey?new _({name:"s3",endpoint:e.endpoint,region:e.region??"us-east-1",bucket:e.bucket,forcePathStyle:e.forcePathStyle??!1,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):q(t,"[storage] S3 credentials missing. Using local fallback.")}if(n.provider==="huggingface"){let e=n.huggingface;return e?.namespace&&e.bucket&&e.accessKeyId&&e.secretAccessKey?new V({namespace:e.namespace,bucket:e.bucket,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):q(t,"[storage] Hugging Face credentials missing. Using local fallback.")}return new O(t)}function dt(n){return new Promise(t=>setTimeout(t,n))}async function de(n,t={}){let e=Math.max(1,t.attempts??3),s=t.baseDelayMs??150,a=t.maxDelayMs??2e3,c;for(let p=1;p<=e;p++)try{return await n()}catch(k){if(c=k,p>=e)break;let b=Math.min(a,s*2**(p-1)),E=Math.floor(Math.random()*Math.max(1,b*.2));await dt(b+E)}throw c}var pt=new Set(["put","get","getText","delete","list"]);function pe(n,t={}){return new Proxy(n,{get(e,s){let a=Reflect.get(e,s,e);return typeof a!="function"?a:pt.has(s)?(...c)=>de(()=>Promise.resolve(a.apply(e,c)),t):a.bind(e)}})}var me=Number(process.env.TOOLNET_GRAPH_PORT??9749),fe=process.env.TOOLNET_GRAPH_HOST??"127.0.0.1";function gt(n){if(typeof n=="string")return n;if(!n||typeof n!="object")return null;let t=n;for(let e of["key","Key","path","name"])if(typeof t[e]=="string")return t[e];return null}async function ht(n,t){return(await n.list(t)).map(gt).filter(s=>!!s)}async function $(n,t){let e=await n.getText(t);if(!e)return null;try{return JSON.parse(e)}catch{return null}}async function yt(n){let t=await ht(n,"projects/"),e=[...new Set(t.filter(c=>/^projects\/[^/]+\/project\.json$/.test(c)).map(c=>c.split("/")[1]))],s=new Set(t),a=[];for(let c of e){let p=await $(n,`projects/${c}/project.json`);!p||typeof p.id!="string"||a.push({id:p.id,name:typeof p.name=="string"?p.name:c,remote:c,hasGraph:s.has(`projects/${c}/code/graph/current.json`),hasArchitecture:s.has(`projects/${c}/code/architecture/current.json`),hasAnalysis:s.has(`projects/${c}/code/analysis/current.json`),hasVisualization:s.has(`projects/${c}/code/visualization/graph.json`)})}return a.sort((c,p)=>c.name.localeCompare(p.name))}async function St(){let n=te(),t=new z().detect(),e=pe(le({provider:n.storage.provider,huggingface:n.storage.huggingface,localRoot:n.storage.localRoot}),{attempts:3});e.name!=="huggingface"&&console.warn("[graph-ui] Remote Hugging Face storage is not active.");let s=mt(ft(import.meta.url)),a=Q(s,"public"),c=Q(a,"vendor","3d-force-graph.min.js");async function p(){let o=await yt(e),r=t.remote??t.name,l=o.find(f=>f.remote===r&&f.hasGraph),S=o.find(f=>f.hasGraph);return{projects:o,defaultProject:l?.remote??S?.remote??null}}let k=new Map;function b(o){return String(typeof o=="object"&&o!==null?o.id:o)}function E(o){return o.clusterLabel||o.cluster||o.layer||"Unclustered"}function v(o,r={}){return{id:o.id,name:o.name,remote:o.remote,hasArchitecture:o.hasArchitecture,hasAnalysis:o.hasAnalysis,...r}}async function j(o){let r=`projects/${o.remote}`,l=await $(e,`${r}/code/graph/current.json`);if(!l||!Array.isArray(l.symbols)||!Array.isArray(l.edges))throw new Error(`Code graph missing for project ${o.name}`);let S=await $(e,`${r}/code/architecture/current.json`),f=await $(e,`${r}/code/analysis/current.json`),y=new A;y.import(l.symbols,l.edges);let x=new H(y).build(o.id,S,f);return await e.put(`${r}/code/visualization/graph.json`,JSON.stringify(x),"application/json"),{...x,dashboard:v(o)}}async function M(o,r=!1){r&&k.delete(o.remote);let l=k.get(o.remote);if(l)return l;let S=`projects/${o.remote}`,f=await $(e,`${S}/code/visualization/graph.json`);if(f&&Array.isArray(f.nodes)&&Array.isArray(f.links)){let x={...f,dashboard:v(o)};return k.set(o.remote,x),x}let y=await j(o);return k.set(o.remote,y),y}function R(o,r){let l=new Map,S=new Map;for(let d of r.nodes){let u=E(d),g=`cluster:${u}`;S.set(d.id,g);let i=l.get(g);i||(i={id:g,name:u,qualifiedName:u,type:"subsystem",filePath:"",clusterLabel:u,incoming:0,outgoing:0,memberCount:0,files:new Set,viewKind:"cluster"},l.set(g,i)),i.memberCount+=1,d.filePath&&i.files.add(d.filePath)}let f=new Map;for(let d of r.links){let u=S.get(b(d.source)),g=S.get(b(d.target));if(!u||!g||u===g)continue;let i=`${u}\0${g}`,h=f.get(i);h||(h={source:u,target:g,type:"SUBSYSTEM_DEPENDENCY",count:0},f.set(i,h)),h.count+=1}for(let d of f.values()){let u=l.get(d.source),g=l.get(d.target);u&&(u.outgoing+=d.count),g&&(g.incoming+=d.count)}let y=[...l.values()].map(d=>({...d,filePath:`${d.files.size} files \xB7 ${d.memberCount} symbols`,files:void 0})),x=[...f.values()];return{version:2,projectId:r.projectId,generatedAt:r.generatedAt,summary:{nodes:y.length,links:x.length,files:r.summary?.files??0,clusters:y.length},nodes:y,links:x,dashboard:v(o,{viewMode:"overview",totalNodes:r.nodes.length,totalLinks:r.links.length})}}function F(o,r,l){let S=r.nodes.filter(i=>!l||E(i)===l),f=new Set(S.map(i=>i.id)),y=new Map,x=new Map;for(let i of S){let h=i.filePath||"(unknown)";y.set(i.id,h);let C=x.get(h);C||(C={id:`file:${h}`,name:h.split("/").pop()||h,qualifiedName:h,type:"file",filePath:h,clusterLabel:E(i),incoming:0,outgoing:0,memberCount:0,viewKind:"file"},x.set(h,C)),C.memberCount+=1}let d=new Map;for(let i of r.links){let h=b(i.source),C=b(i.target);if(!f.has(h)||!f.has(C))continue;let U=y.get(h),W=y.get(C);if(!U||!W||U===W)continue;let X=`file:${U}`,Z=`file:${W}`,ee=`${X}\0${Z}`,G=d.get(ee);G||(G={source:X,target:Z,type:"FILE_DEPENDENCY",count:0},d.set(ee,G)),G.count+=1}for(let i of d.values()){let h=x.get(String(i.source).slice(5)),C=x.get(String(i.target).slice(5));h&&(h.outgoing+=i.count),C&&(C.incoming+=i.count)}let u=[...x.values()],g=[...d.values()];return{version:2,projectId:r.projectId,generatedAt:r.generatedAt,summary:{nodes:u.length,links:g.length,files:u.length,clusters:l?1:0},nodes:u,links:g,dashboard:v(o,{viewMode:"files",selection:l,totalNodes:r.nodes.length,totalLinks:r.links.length})}}function T(o,r,l){let y=r.nodes.filter(i=>i.filePath===l),x=new Set(y.map(i=>i.id)),d=new Set;for(let i of y.slice(0,1800))d.add(i.id);for(let i of r.links){if(d.size>=1800)break;let h=b(i.source),C=b(i.target);(x.has(h)||x.has(C))&&(d.add(h),d.size<1800&&d.add(C))}let u=r.nodes.filter(i=>d.has(i.id)).map(i=>({...i,viewKind:"symbol"})),g=[];for(let i of r.links){if(g.length>=12e3)break;let h=b(i.source),C=b(i.target);d.has(h)&&d.has(C)&&g.push({...i,source:h,target:C})}return{version:2,projectId:r.projectId,generatedAt:r.generatedAt,summary:{nodes:u.length,links:g.length,files:new Set(u.map(i=>i.filePath)).size,clusters:new Set(u.map(i=>E(i))).size},nodes:u,links:g,dashboard:v(o,{viewMode:"symbols",selection:l,focused:!0,truncated:u.length>=1800||g.length>=12e3,totalNodes:r.nodes.length,totalLinks:r.links.length})}}ut(async(o,r)=>{try{let l=new URL(o.url??"/",`http://${o.headers.host??"localhost"}`);if(l.pathname==="/api/health"){let S=await p();r.setHeader("content-type","application/json; charset=utf-8"),r.setHeader("cache-control","no-store"),r.end(JSON.stringify({ok:!0,mode:"multi-project",projects:S.projects.length,indexedProjects:S.projects.filter(f=>f.hasGraph).length,defaultProject:S.defaultProject}));return}if(l.pathname==="/api/projects"){let S=await p();r.setHeader("content-type","application/json; charset=utf-8"),r.setHeader("cache-control","no-store"),r.end(JSON.stringify(S));return}if(l.pathname==="/api/graph"){let S=await p(),f=l.searchParams.get("project")??S.defaultProject;if(!f){r.statusCode=404,r.setHeader("content-type","application/json; charset=utf-8"),r.end(JSON.stringify({error:"No indexed ToolNet project found."}));return}let y=S.projects.find(i=>i.remote===f);if(!y){r.statusCode=404,r.setHeader("content-type","application/json; charset=utf-8"),r.end(JSON.stringify({error:"Project not found.",project:f}));return}if(!y.hasGraph){r.statusCode=409,r.setHeader("content-type","application/json; charset=utf-8"),r.end(JSON.stringify({error:"Project is registered but has not been indexed yet.",project:y}));return}let x=l.searchParams.get("refresh")==="1",d=await M(y,x),u=l.searchParams.get("mode")??"full",g=d;if(u==="overview")g=R(y,d);else if(u==="files")g=F(y,d,l.searchParams.get("cluster")??"");else if(u==="symbols"){let i=l.searchParams.get("file");if(!i){r.statusCode=400,r.setHeader("content-type","application/json; charset=utf-8"),r.end(JSON.stringify({error:"file is required for symbols mode."}));return}g=T(y,d,i)}else if(u!=="full"){r.statusCode=400,r.setHeader("content-type","application/json; charset=utf-8"),r.end(JSON.stringify({error:`Unknown graph mode: ${u}`}));return}r.setHeader("content-type","application/json; charset=utf-8"),r.setHeader("cache-control","no-store"),r.end(JSON.stringify(g));return}if(l.pathname==="/vendor/3d-force-graph.min.js"){r.setHeader("content-type","application/javascript; charset=utf-8"),ue(c).pipe(r);return}if(l.pathname==="/"||l.pathname==="/index.html"){r.setHeader("content-type","text/html; charset=utf-8"),r.setHeader("cache-control","no-store"),ue(Q(a,"index.html")).pipe(r);return}r.statusCode=404,r.end("Not found")}catch(l){console.error(l),r.statusCode=500,r.setHeader("content-type","application/json; charset=utf-8"),r.end(JSON.stringify({error:l instanceof Error?l.message:String(l)}))}}).listen(me,fe,()=>{console.log("ToolNet Multi-Project 3D Graph"),console.log(`Listening: http://${fe}:${me}`)})}St().catch(n=>{console.error(n),process.exit(1)});
1
+ import"dotenv/config";import{createReadStream as ue}from"node:fs";import{createServer as ut}from"node:http";import{dirname as ft,resolve as Q}from"node:path";import{fileURLToPath as mt}from"node:url";import{existsSync as ge,readFileSync as he}from"node:fs";import{homedir as ye}from"node:os";import{join as Se}from"node:path";function be(o){let t=o.trim();return t.length>=2&&t.startsWith('"')&&t.endsWith('"')?(t=t.slice(1,-1),t.replace(/\\n/g,`
2
+ `).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):t.length>=2&&t.startsWith("'")&&t.endsWith("'")?t.slice(1,-1):t}function Ce(){let o=process.env.TOOLNET_GLOBAL_ENV??Se(ye(),".config","toolnet-memory",".env");if(!ge(o))return;let t=he(o,"utf8");for(let e of t.split(/\r?\n/)){let s=e.trim();if(!s||s.startsWith("#"))continue;s.startsWith("export ")&&(s=s.slice(7));let a=s.indexOf("=");if(a<=0)continue;let c=s.slice(0,a).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(c)&&process.env[c]===void 0&&(process.env[c]=be(s.slice(a+1)))}}Ce();function M(o,t){return o===void 0?t:["1","true","yes","on"].includes(o.toLowerCase())}function L(o,t){if(!o)return t;let e=Number(o);return Number.isFinite(e)?e:t}function te(){return{memory:{autoCapture:M(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:M(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:M(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:M(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:L(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:L(process.env.MEMORY_RERANK_TOP,10),finalContext:L(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:L(process.env.MEMORY_TOKEN_BUDGET,2e3)},storage:{provider:process.env.MEMORY_STORAGE_PROVIDER??"huggingface",r2:{accountId:process.env.R2_ACCOUNT_ID,bucket:process.env.R2_BUCKET,accessKeyId:process.env.R2_ACCESS_KEY_ID,secretAccessKey:process.env.R2_SECRET_ACCESS_KEY},s3:{endpoint:process.env.S3_ENDPOINT,region:process.env.S3_REGION,bucket:process.env.S3_BUCKET,accessKeyId:process.env.S3_ACCESS_KEY_ID,secretAccessKey:process.env.S3_SECRET_ACCESS_KEY,forcePathStyle:M(process.env.S3_FORCE_PATH_STYLE,!1)},huggingface:{namespace:process.env.HF_NAMESPACE,bucket:process.env.HF_BUCKET,accessKeyId:process.env.HF_S3_ACCESS_KEY_ID,secretAccessKey:process.env.HF_S3_SECRET_ACCESS_KEY},localRoot:process.env.MEMORY_LOCAL_STORAGE_PATH},cache:{maxMb:L(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as xe}from"node:crypto";import{existsSync as J,mkdirSync as we,readFileSync as Ee,renameSync as ve,writeFileSync as ke}from"node:fs";import{basename as Pe,dirname as K,join as B,parse as ne,resolve as I}from"node:path";var oe=".toolnet",Re="project.json";function je(o){return xe("sha256").update(o).digest("hex").slice(0,16)}function Y(o){return B(o,oe,Re)}function Te(o){return J(Y(o))}function Ae(o,t){let e=I(o),s=ne(e).root;for(;;){if(Te(e))return e;if(e===s||t&&e===I(t))break;let a=K(e);if(a===e)break;e=a}return null}function Ne(o){let t=I(o),e=ne(t).root,s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(s.some(c=>J(B(t,c))))return t;if(t===e)break;let a=K(t);if(a===t)break;t=a}return I(o)}function Me(o){let t;try{t=JSON.parse(Ee(o,"utf8"))}catch(a){throw new Error(`Invalid ToolNet project manifest: ${o}: ${a instanceof Error?a.message:String(a)}`)}if(!t||typeof t!="object")throw new Error(`Invalid ToolNet project manifest: ${o}`);let e=t;if(typeof e.id!="string"||!e.id.trim())throw new Error(`ToolNet project manifest is missing id: ${o}`);if(typeof e.name!="string"||!e.name.trim())throw new Error(`ToolNet project manifest is missing name: ${o}`);let s=new Date().toISOString();return{version:1,id:e.id,name:e.name,remote:typeof e.remote=="string"&&e.remote.trim()?e.remote:e.name,rootPath:typeof e.rootPath=="string"?e.rootPath:K(K(o)),createdAt:typeof e.createdAt=="string"?e.createdAt:s,updatedAt:typeof e.updatedAt=="string"?e.updatedAt:s,graphVersion:typeof e.graphVersion=="number"?e.graphVersion:0,memoryVersion:typeof e.memoryVersion=="number"?e.memoryVersion:0,metadata:e.metadata&&typeof e.metadata=="object"?e.metadata:void 0}}function re(o,t){let e=B(o,oe);we(e,{recursive:!0});let s=Y(o),a=`${s}.tmp-${process.pid}`;ke(a,JSON.stringify(t,null,2)+`
3
+ `,{encoding:"utf8",mode:384}),ve(a,s)}function se(o,t){return{id:o.id,name:o.name,remote:o.remote,rootPath:t,createdAt:o.createdAt,updatedAt:o.updatedAt,graphVersion:o.graphVersion,memoryVersion:o.memoryVersion,metadata:o.metadata}}var z=class{detect(t=process.cwd()){let e=I(t),s=Ne(e),c=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(v=>J(B(s,v))),p=Ae(e,c?s:void 0);if(p){let v=Y(p),R=Me(v);return R.rootPath!==p&&(R.rootPath=p,R.updatedAt=new Date().toISOString(),re(p,R)),se(R,p)}let P=new Date().toISOString(),b=Pe(s),E={version:1,id:je(s),name:b,remote:b,rootPath:s,createdAt:P,updatedAt:P,graphVersion:0,memoryVersion:0};return re(s,E),se(E,s)}};import*as w from"typescript";var A=class{symbols=new Map;edges=new Map;addSymbol(t){this.symbols.set(t.id,t)}addEdge(t){this.edges.set(t.id,t)}getSymbol(t){return this.symbols.get(t)}findByName(t,e){return[...this.symbols.values()].filter(s=>s.projectId===t&&(s.name===e||s.qualifiedName===e))}allSymbols(t){return[...this.symbols.values()].filter(e=>e.projectId===t)}allEdges(t){return[...this.edges.values()].filter(e=>e.projectId===t)}import(t,e){for(let s of t)this.addSymbol(s);for(let s of e)this.addEdge(s)}clearProject(t){for(let[e,s]of this.symbols)s.projectId===t&&this.symbols.delete(e);for(let[e,s]of this.edges)s.projectId===t&&this.edges.delete(e)}};import{execFile as $e}from"node:child_process";import{promisify as Fe}from"node:util";var Pr=Fe($e);import{DatabaseSync as gs}from"node:sqlite";import*as k from"typescript";import*as f from"typescript";var _s=new Set([f.SyntaxKind.EqualsToken,f.SyntaxKind.PlusEqualsToken,f.SyntaxKind.MinusEqualsToken,f.SyntaxKind.AsteriskEqualsToken,f.SyntaxKind.SlashEqualsToken,f.SyntaxKind.PercentEqualsToken,f.SyntaxKind.QuestionQuestionEqualsToken,f.SyntaxKind.AmpersandAmpersandEqualsToken,f.SyntaxKind.BarBarEqualsToken]);var H=class{constructor(t){this.graph=t}graph;build(t,e,s,a){let c=this.graph.allSymbols(t),p=this.graph.allEdges(t),P=new Map(e?.layers.map(n=>[n.filePath,n.layer])??[]),b=new Map;for(let n of e?.clusters??[])for(let r of n.files)b.set(r,{id:n.id,label:n.label});let E=new Map(e?.hotspots.map(n=>[n.filePath,n.score])??[]),v=new Map(s?.deadCode.map(n=>[n.symbolId,{confidence:n.confidence,score:n.score}])??[]),R=new Map,N=new Map;for(let n of p)N.set(n.from,(N.get(n.from)??0)+1),R.set(n.to,(R.get(n.to)??0)+1);let j=[];for(let n=0;n<c.length;n++){let r=c[n],l=b.get(r.filePath);j.push({id:r.id,name:r.name,qualifiedName:r.qualifiedName,type:r.type,filePath:r.filePath,layer:P.get(r.filePath),cluster:l?.id,clusterLabel:l?.label,hotspotScore:E.get(r.filePath),deadCode:v.get(r.id),incoming:R.get(r.id)??0,outgoing:N.get(r.id)??0}),((n+1)%100===0||n===c.length-1)&&a?.({current:n+1,total:c.length,phase:"nodes"})}let F=new Set(j.map(n=>n.id)),T=p.filter(n=>F.has(n.from)&&F.has(n.to)),D=[];for(let n=0;n<T.length;n++){let r=T[n];D.push({source:r.from,target:r.to,type:r.type,confidence:typeof r.metadata?.confidence=="string"?r.metadata.confidence:void 0,resolver:typeof r.metadata?.resolver=="string"?r.metadata.resolver:void 0}),((n+1)%100===0||n===T.length-1)&&a?.({current:n+1,total:T.length,phase:"links"})}return{version:1,projectId:t,generatedAt:new Date().toISOString(),summary:{nodes:j.length,links:D.length,files:new Set(j.map(n=>n.filePath)).size,clusters:new Set(j.map(n=>n.cluster).filter(Boolean)).size},nodes:j,links:D}}};import{homedir as ct}from"node:os";import{join as lt}from"node:path";import{DeleteObjectCommand as Ge,GetObjectCommand as Ke,HeadObjectCommand as ze,ListObjectsV2Command as Be,PutObjectCommand as He,S3Client as Ue}from"@aws-sdk/client-s3";import{getSignedUrl as We}from"@aws-sdk/s3-request-presigner";var U=class{name="huggingface";client;bucket;constructor(t){this.bucket=t.bucket,this.client=new Ue({region:"us-east-1",endpoint:`https://s3.hf.co/${t.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}})}async put(t,e,s="application/octet-stream"){let a=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new He({Bucket:this.bucket,Key:t,Body:a,ContentType:s}))}async get(t){let e=await We(this.client,new Ke({Bucket:this.bucket,Key:t}),{expiresIn:60}),s=await fetch(e,{redirect:"follow"});if(s.status===404)return null;if(!s.ok)throw new Error(`HF download failed: ${s.status} ${s.statusText}`);return new Uint8Array(await s.arrayBuffer())}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await this.client.send(new ze({Bucket:this.bucket,Key:t})),!0}catch(e){if(typeof e=="object"&&e!==null&&"$metadata"in e&&e.$metadata?.httpStatusCode===404)return!1;throw e}}async delete(t){await this.client.send(new Ge({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],s;do{let a=await this.client.send(new Be({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:s}));for(let c of a.Contents??[])c.Key&&e.push({key:c.Key,size:c.Size,updatedAt:c.LastModified?.toISOString()});s=a.IsTruncated?a.NextContinuationToken:void 0}while(s);return e}};import{access as ie,mkdir as Ve,readFile as Je,readdir as Ye,rm as qe,stat as ae,writeFile as Qe}from"node:fs/promises";import{dirname as Xe,join as Ze,relative as ce,resolve as et}from"node:path";var O=class{constructor(t){this.root=t}root;name="local";path(t){let e=t.replace(/^\/+/,"");return et(this.root,e)}async put(t,e){let s=this.path(t);await Ve(Xe(s),{recursive:!0}),await Qe(s,e)}async get(t){try{return await Je(this.path(t))}catch(e){if(typeof e=="object"&&e!==null&&"code"in e&&e.code==="ENOENT")return null;throw e}}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await ie(this.path(t)),!0}catch{return!1}}async delete(t){await qe(this.path(t),{force:!0})}async list(t=""){let e=this.path(t),s=[];try{await ie(e)}catch{return s}let a=async p=>{let P=await Ye(p,{withFileTypes:!0});for(let b of P){let E=Ze(p,b.name);if(b.isDirectory()){await a(E);continue}let v=await ae(E);s.push({key:ce(this.root,E),size:v.size,updatedAt:v.mtime.toISOString()})}},c=await ae(e);return c.isDirectory()?await a(e):s.push({key:ce(this.root,e),size:c.size,updatedAt:c.mtime.toISOString()}),s}};import{DeleteObjectCommand as tt,GetObjectCommand as rt,HeadObjectCommand as st,ListObjectsV2Command as nt,PutObjectCommand as ot,S3Client as it}from"@aws-sdk/client-s3";import{getSignedUrl as at}from"@aws-sdk/s3-request-presigner";var _=class{name;client;bucket;constructor(t){this.name=t.name??"s3",this.bucket=t.bucket,this.client=new it({region:t.region??"us-east-1",endpoint:t.endpoint||void 0,forcePathStyle:t.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}})}async put(t,e,s="application/octet-stream"){let a=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new ot({Bucket:this.bucket,Key:t,Body:a,ContentType:s}))}async get(t){let e=await at(this.client,new rt({Bucket:this.bucket,Key:t}),{expiresIn:60}),s=await fetch(e,{redirect:"follow"});if(s.status===404)return null;if(!s.ok)throw new Error(`${this.name} download failed: ${s.status} ${s.statusText}`);return new Uint8Array(await s.arrayBuffer())}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await this.client.send(new st({Bucket:this.bucket,Key:t})),!0}catch(e){if(typeof e=="object"&&e!==null&&"$metadata"in e&&e.$metadata?.httpStatusCode===404)return!1;throw e}}async delete(t){await this.client.send(new tt({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],s;do{let a=await this.client.send(new nt({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:s}));for(let c of a.Contents??[])c.Key&&e.push({key:c.Key,size:c.Size,updatedAt:c.LastModified?.toISOString()});s=a.IsTruncated?a.NextContinuationToken:void 0}while(s);return e}};function q(o,t){return console.warn(t),new O(o)}function le(o){let t=o.localRoot??lt(ct(),".toolnet-memory","storage");if(o.provider==="r2"){let e=o.r2;return e?.accountId&&e.bucket&&e.accessKeyId&&e.secretAccessKey?new _({name:"r2",endpoint:`https://${e.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:e.bucket,forcePathStyle:!0,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):q(t,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(o.provider==="s3"){let e=o.s3;return e?.bucket&&e.accessKeyId&&e.secretAccessKey?new _({name:"s3",endpoint:e.endpoint,region:e.region??"us-east-1",bucket:e.bucket,forcePathStyle:e.forcePathStyle??!1,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):q(t,"[storage] S3 credentials missing. Using local fallback.")}if(o.provider==="huggingface"){let e=o.huggingface;return e?.namespace&&e.bucket&&e.accessKeyId&&e.secretAccessKey?new U({namespace:e.namespace,bucket:e.bucket,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):q(t,"[storage] Hugging Face credentials missing. Using local fallback.")}return new O(t)}function dt(o){return new Promise(t=>setTimeout(t,o))}async function de(o,t={}){let e=Math.max(1,t.attempts??3),s=t.baseDelayMs??150,a=t.maxDelayMs??2e3,c;for(let p=1;p<=e;p++)try{return await o()}catch(P){if(c=P,p>=e)break;let b=Math.min(a,s*2**(p-1)),E=Math.floor(Math.random()*Math.max(1,b*.2));await dt(b+E)}throw c}var pt=new Set(["put","get","getText","delete","list"]);function pe(o,t={}){return new Proxy(o,{get(e,s){let a=Reflect.get(e,s,e);return typeof a!="function"?a:pt.has(s)?(...c)=>de(()=>Promise.resolve(a.apply(e,c)),t):a.bind(e)}})}var fe=Number(process.env.TOOLNET_GRAPH_PORT??9749),me=process.env.TOOLNET_GRAPH_HOST??"127.0.0.1";function gt(o){if(typeof o=="string")return o;if(!o||typeof o!="object")return null;let t=o;for(let e of["key","Key","path","name"])if(typeof t[e]=="string")return t[e];return null}async function ht(o,t){return(await o.list(t)).map(gt).filter(s=>!!s)}async function $(o,t){let e=await o.getText(t);if(!e)return null;try{return JSON.parse(e)}catch{return null}}async function yt(o){let t=await ht(o,"projects/"),e=[...new Set(t.filter(c=>/^projects\/[^/]+\/project\.json$/.test(c)).map(c=>c.split("/")[1]))],s=new Set(t),a=[];for(let c of e){let p=await $(o,`projects/${c}/project.json`);!p||typeof p.id!="string"||a.push({id:p.id,name:typeof p.name=="string"?p.name:c,remote:c,hasGraph:s.has(`projects/${c}/code/graph/current.json`),hasArchitecture:s.has(`projects/${c}/code/architecture/current.json`),hasAnalysis:s.has(`projects/${c}/code/analysis/current.json`),hasVisualization:s.has(`projects/${c}/code/visualization/graph.json`)})}return a.sort((c,p)=>c.name.localeCompare(p.name))}async function St(){let o=te(),t=new z().detect(),e=pe(le({provider:o.storage.provider,huggingface:o.storage.huggingface,localRoot:o.storage.localRoot}),{attempts:3});e.name!=="huggingface"&&console.warn("[graph-ui] Remote Hugging Face storage is not active.");let s=ft(mt(import.meta.url)),a=Q(s,"public"),c=Q(a,"vendor","3d-force-graph.min.js");async function p(){let n=await yt(e),r=t.remote??t.name,l=n.find(m=>m.remote===r&&m.hasGraph),S=n.find(m=>m.hasGraph);return{projects:n,defaultProject:l?.remote??S?.remote??null}}let P=new Map;function b(n){return String(typeof n=="object"&&n!==null?n.id:n)}function E(n){return n.clusterLabel||n.cluster||n.layer||"Unclustered"}function v(n,r={}){return{id:n.id,name:n.name,remote:n.remote,hasArchitecture:n.hasArchitecture,hasAnalysis:n.hasAnalysis,...r}}async function R(n){let r=`projects/${n.remote}`,l=await $(e,`${r}/code/graph/current.json`);if(!l||!Array.isArray(l.symbols)||!Array.isArray(l.edges))throw new Error(`Code graph missing for project ${n.name}`);let S=await $(e,`${r}/code/architecture/current.json`),m=await $(e,`${r}/code/analysis/current.json`),y=new A;y.import(l.symbols,l.edges);let C=new H(y).build(n.id,S,m);return await e.put(`${r}/code/visualization/graph.json`,JSON.stringify(C),"application/json"),{...C,dashboard:v(n)}}async function N(n,r=!1){r&&P.delete(n.remote);let l=P.get(n.remote);if(l)return l;let S=`projects/${n.remote}`,m=await $(e,`${S}/code/visualization/graph.json`);if(m&&Array.isArray(m.nodes)&&Array.isArray(m.links)){let C={...m,dashboard:v(n)};return P.set(n.remote,C),C}let y=await R(n);return P.set(n.remote,y),y}function j(n,r){let l=new Map,S=new Map;for(let d of r.nodes){let u=E(d),g=`cluster:${u}`;S.set(d.id,g);let i=l.get(g);i||(i={id:g,name:u,qualifiedName:u,type:"subsystem",filePath:"",clusterLabel:u,incoming:0,outgoing:0,memberCount:0,files:new Set,viewKind:"cluster"},l.set(g,i)),i.memberCount+=1,d.filePath&&i.files.add(d.filePath)}let m=new Map;for(let d of r.links){let u=S.get(b(d.source)),g=S.get(b(d.target));if(!u||!g||u===g)continue;let i=`${u}\0${g}`,h=m.get(i);h||(h={source:u,target:g,type:"SUBSYSTEM_DEPENDENCY",count:0},m.set(i,h)),h.count+=1}for(let d of m.values()){let u=l.get(d.source),g=l.get(d.target);u&&(u.outgoing+=d.count),g&&(g.incoming+=d.count)}let y=[...l.values()].map(d=>({...d,filePath:`${d.files.size} files \xB7 ${d.memberCount} symbols`,files:void 0})),C=[...m.values()];return{version:2,projectId:r.projectId,generatedAt:r.generatedAt,summary:{nodes:y.length,links:C.length,files:r.summary?.files??0,clusters:y.length},nodes:y,links:C,dashboard:v(n,{viewMode:"overview",totalNodes:r.nodes.length,totalLinks:r.links.length})}}function F(n,r,l){let S=r.nodes.filter(i=>!l||E(i)===l),m=new Set(S.map(i=>i.id)),y=new Map,C=new Map;for(let i of S){let h=i.filePath||"(unknown)";y.set(i.id,h);let x=C.get(h);x||(x={id:`file:${h}`,name:h.split("/").pop()||h,qualifiedName:h,type:"file",filePath:h,clusterLabel:E(i),incoming:0,outgoing:0,memberCount:0,viewKind:"file"},C.set(h,x)),x.memberCount+=1}let d=new Map;for(let i of r.links){let h=b(i.source),x=b(i.target);if(!m.has(h)||!m.has(x))continue;let W=y.get(h),V=y.get(x);if(!W||!V||W===V)continue;let X=`file:${W}`,Z=`file:${V}`,ee=`${X}\0${Z}`,G=d.get(ee);G||(G={source:X,target:Z,type:"FILE_DEPENDENCY",count:0},d.set(ee,G)),G.count+=1}for(let i of d.values()){let h=C.get(String(i.source).slice(5)),x=C.get(String(i.target).slice(5));h&&(h.outgoing+=i.count),x&&(x.incoming+=i.count)}let u=[...C.values()],g=[...d.values()];return{version:2,projectId:r.projectId,generatedAt:r.generatedAt,summary:{nodes:u.length,links:g.length,files:u.length,clusters:l?1:0},nodes:u,links:g,dashboard:v(n,{viewMode:"files",selection:l,totalNodes:r.nodes.length,totalLinks:r.links.length})}}function T(n,r,l){let y=r.nodes.filter(i=>i.filePath===l),C=new Set(y.map(i=>i.id)),d=new Set;for(let i of y.slice(0,1800))d.add(i.id);for(let i of r.links){if(d.size>=1800)break;let h=b(i.source),x=b(i.target);(C.has(h)||C.has(x))&&(d.add(h),d.size<1800&&d.add(x))}let u=r.nodes.filter(i=>d.has(i.id)).map(i=>({...i,viewKind:"symbol"})),g=[];for(let i of r.links){if(g.length>=12e3)break;let h=b(i.source),x=b(i.target);d.has(h)&&d.has(x)&&g.push({...i,source:h,target:x})}return{version:2,projectId:r.projectId,generatedAt:r.generatedAt,summary:{nodes:u.length,links:g.length,files:new Set(u.map(i=>i.filePath)).size,clusters:new Set(u.map(i=>E(i))).size},nodes:u,links:g,dashboard:v(n,{viewMode:"symbols",selection:l,focused:!0,truncated:u.length>=1800||g.length>=12e3,totalNodes:r.nodes.length,totalLinks:r.links.length})}}ut(async(n,r)=>{try{let l=new URL(n.url??"/",`http://${n.headers.host??"localhost"}`);if(l.pathname==="/api/health"){let S=await p();r.setHeader("content-type","application/json; charset=utf-8"),r.setHeader("cache-control","no-store"),r.end(JSON.stringify({ok:!0,mode:"multi-project",projects:S.projects.length,indexedProjects:S.projects.filter(m=>m.hasGraph).length,defaultProject:S.defaultProject}));return}if(l.pathname==="/api/projects"){let S=await p();r.setHeader("content-type","application/json; charset=utf-8"),r.setHeader("cache-control","no-store"),r.end(JSON.stringify(S));return}if(l.pathname==="/api/graph"){let S=await p(),m=l.searchParams.get("project")??S.defaultProject;if(!m){r.statusCode=404,r.setHeader("content-type","application/json; charset=utf-8"),r.end(JSON.stringify({error:"No indexed ToolNet project found."}));return}let y=S.projects.find(i=>i.remote===m);if(!y){r.statusCode=404,r.setHeader("content-type","application/json; charset=utf-8"),r.end(JSON.stringify({error:"Project not found.",project:m}));return}if(!y.hasGraph){r.statusCode=409,r.setHeader("content-type","application/json; charset=utf-8"),r.end(JSON.stringify({error:"Project is registered but has not been indexed yet.",project:y}));return}let C=l.searchParams.get("refresh")==="1",d=await N(y,C),u=l.searchParams.get("mode")??"full",g=d;if(u==="overview")g=j(y,d);else if(u==="files")g=F(y,d,l.searchParams.get("cluster")??"");else if(u==="symbols"){let i=l.searchParams.get("file");if(!i){r.statusCode=400,r.setHeader("content-type","application/json; charset=utf-8"),r.end(JSON.stringify({error:"file is required for symbols mode."}));return}g=T(y,d,i)}else if(u!=="full"){r.statusCode=400,r.setHeader("content-type","application/json; charset=utf-8"),r.end(JSON.stringify({error:`Unknown graph mode: ${u}`}));return}r.setHeader("content-type","application/json; charset=utf-8"),r.setHeader("cache-control","no-store"),r.end(JSON.stringify(g));return}if(l.pathname==="/vendor/3d-force-graph.min.js"){r.setHeader("content-type","application/javascript; charset=utf-8"),ue(c).pipe(r);return}if(l.pathname==="/"||l.pathname==="/index.html"){r.setHeader("content-type","text/html; charset=utf-8"),r.setHeader("cache-control","no-store"),ue(Q(a,"index.html")).pipe(r);return}r.statusCode=404,r.end("Not found")}catch(l){console.error(l),r.statusCode=500,r.setHeader("content-type","application/json; charset=utf-8"),r.end(JSON.stringify({error:l instanceof Error?l.message:String(l)}))}}).listen(fe,me,()=>{console.log("ToolNet Multi-Project 3D Graph"),console.log(`Listening: http://${me}:${fe}`)})}St().catch(o=>{console.error(o),process.exit(1)});
@@ -1,4 +1,4 @@
1
- import{readFileSync as N}from"node:fs";import{dirname as M,resolve as w}from"node:path";import{fileURLToPath as D}from"node:url";var E={spinner:["\xB7","\u2722","\u2733","\u2736","\u273B","\u273D"],barFilled:"\u2588",barEmpty:"\u2591",phaseDone:"\u25C6",phaseActive:"\u25C7",rail:"\u2502",branch:"\u251C",corner:"\u2514",dash:"\u2014"},R={spinner:[".","*","+","x","*","+"],barFilled:"#",barEmpty:"-",phaseDone:"[OK]",phaseActive:"[>>]",rail:"|",branch:"|",corner:"\\",dash:"-"};function U(){if(process.env.TOOLNET_ASCII==="1")return!1;if(process.env.TOOLNET_UNICODE==="1")return!0;if(process.platform==="win32"){let o=process.env.TERM_PROGRAM,e=process.env.WT_SESSION,n=process.env.VSCODE_PID,t=process.env.ConEmuPID;return!!(e||n||t||o==="vscode")}return process.env.TERM!=="linux"}var h;function v(){return h||(h=U()?E:R),h}var m={reset:"\x1B[0m",green:"\x1B[32m",cyan:"\x1B[36m",red:"\x1B[31m",yellow:"\x1B[33m",amber:"\x1B[38;5;214m",white:"\x1B[97m",dim:"\x1B[2m",bold:"\x1B[1m"};function A(o){return o.noColor||process.env.NO_COLOR?!1:o.tty??process.stdout.isTTY??!1}function f(o,e,n){return A(n)?`${e}${o}${m.reset}`:o}function T(o,e){return f(o,m.dim,e)}function G(o,e){return f(o,m.bold,e)}function k(o,e){return f(o,m.cyan,e)}function d(o,e,n){let t=v(),i=e?` ${T(`v${e}`,n)}`:"";return`${k(t.phaseDone,n)} ${o}${i}`}function a(o,e){return G(o,e)}function l(o,e,n,t){let i=" ".repeat(Math.max(0,n-o.length));return` ${k(o,t)}${i} ${e}`}var g=[{name:"setup",description:"Configure ToolNet",category:"core"},{name:"init",description:"Initialize current project",usage:"toolnet-memory init [path]",category:"core"},{name:"doctor",description:"Check system health",category:"core"},{name:"ask",description:"Ask project memory",usage:"toolnet-memory ask <question>",category:"memory"},{name:"context",description:"Show current project context",category:"memory",aliases:["context:print"]},{name:"work",description:"Show current work state",category:"memory",aliases:["work:status"]},{name:"index",description:"Build code intelligence",category:"code",pipeline:["Scanning files","Parsing code","Type Resolution","Rich Graph","Semantic Code Index","Architecture Intelligence","Graph Analysis","3D Visualization Dataset"]},{name:"semantic",description:"Semantic code search",usage:"toolnet-memory semantic <query>",category:"code"},{name:"impact",description:"Analyze change impact",usage:"toolnet-memory impact <file>",category:"code"},{name:"graph",description:"Open code graph UI",category:"code"},{name:"graph:ui",description:"Open code graph UI",category:"code",hidden:!0},{name:"model",description:"View/change AI model",category:"ai"},{name:"provider",description:"Manage AI providers",category:"ai",aliases:["provider:status"]},{name:"status",description:"Show ToolNet status",category:"system"},{name:"update",description:"Update ToolNet Memory",category:"system"},{name:"context:print",description:"Print fast project context",category:"context",hidden:!0},{name:"context:sync",description:"Show context hash/size",category:"context"},{name:"context:refresh",description:"Rebuild deep startup brief cache",category:"context"},{name:"brief",description:"Show full project brief",category:"context"},{name:"brief:json",description:"Show brief as JSON",category:"context"},{name:"profile:show",description:"Show .toolnet/profile.md",category:"context"},{name:"profile:sync",description:"Sync profile to agent files",category:"context"},{name:"work:status",description:"Show current work state",category:"work",hidden:!0},{name:"work:json",description:"Show work state as JSON",category:"work"},{name:"work:reconcile",description:"Reconcile work continuity",category:"work"},{name:"handoff:latest",description:"Show latest session handoff",category:"work"},{name:"handoff:refresh",description:"Refresh handoff data",category:"work"},{name:"index:graph",description:"Build graph index only",category:"code"},{name:"incremental",description:"Incremental code indexing",category:"code"},{name:"config",description:"Manage configuration",usage:"toolnet-memory config <get|set|list|open>",category:"ai"},{name:"provider:list",description:"List available AI providers",category:"ai"},{name:"provider:status",description:"Show AI provider status",category:"ai",hidden:!0},{name:"provider:test",description:"Test AI provider connection",category:"ai"},{name:"integrate:detect",description:"Detect agent integrations",category:"integration"},{name:"integrate:auto",description:"Auto-enable integrations with safe project scope policy",usage:"toolnet-memory integrate:auto [--all] [--scope global|project|both] [--project <path>] [--json]",category:"integration",examples:["toolnet-memory integrate:auto","toolnet-memory integrate:auto --project /path/to/toolnet-project","toolnet-memory integrate:auto --scope global","toolnet-memory integrate:auto --scope both --project /path/to/project"]},{name:"integrate:agy",description:"Install Agy hooks",category:"integration"},{name:"integrate:codex",description:"Install Codex integration",category:"integration"},{name:"integrate:opencode",description:"Install OpenCode plugin",category:"integration"},{name:"integrate:claude",description:"Install Claude Code integration",category:"integration"},{name:"integrate:kiro",description:"Install Kiro CLI integration",usage:"toolnet-memory integrate:kiro [--status] [--json]",category:"integration"},{name:"integrate:cursor",description:"Install Cursor CLI integration",usage:"toolnet-memory integrate:cursor [--scope global|project|both] [--project <path>] [--status] [--json]",category:"integration",examples:["toolnet-memory integrate:cursor --scope global","toolnet-memory integrate:cursor --scope project --project /path/to/project","toolnet-memory integrate:cursor --scope both --project /path/to/project"]},{name:"integrate:copilot",description:"Install GitHub Copilot CLI integration",usage:"toolnet-memory integrate:copilot [--scope global|project|both] [--project <path>] [--status] [--json]",category:"integration",examples:["toolnet-memory integrate:copilot --scope global","toolnet-memory integrate:copilot --scope project --project /path/to/project","toolnet-memory integrate:copilot --scope both --project /path/to/project"]},{name:"integrate:grok",description:"Install Grok Build integration",usage:"toolnet-memory integrate:grok [--scope global|project|both] [--project <path>] [--status] [--json]",category:"integration",examples:["toolnet-memory integrate:grok --scope global","toolnet-memory integrate:grok --scope project --project /path/to/project","toolnet-memory integrate:grok --scope both --project /path/to/project"]},{name:"integrate:status",description:"Show unified Cursor/Copilot/Grok scoped integration status",usage:"toolnet-memory integrate:status [--scope global|project|both] [--project <path>] [--agent cursor|copilot|grok] [--json]",category:"integration",examples:["toolnet-memory integrate:status --scope global","toolnet-memory integrate:status --scope both --project /path/to/project","toolnet-memory integrate:status --scope both --project /path/to/project --agent cursor"]},{name:"session:agy-sync",description:"Sync Agy session",category:"session"},{name:"session:agy-recover",description:"Recover Agy sessions",category:"session"},{name:"session:agy-hook",description:"Agy session hook",category:"session",hidden:!0},{name:"session:codex-sync",description:"Sync Codex session",category:"session"},{name:"session:codex-recover",description:"Recover Codex sessions",category:"session"},{name:"session:codex-notify",description:"Codex notification hook",category:"session",hidden:!0},{name:"session:codex-context",description:"Codex context hook",category:"session",hidden:!0},{name:"session:opencode-sync",description:"Sync OpenCode session",category:"session"},{name:"session:opencode-recover",description:"Recover OpenCode sessions",category:"session"},{name:"session:claude-hook",description:"Claude session hook",category:"session",hidden:!0},{name:"session:kiro-hook",description:"Kiro lifecycle hook",category:"session",hidden:!0},{name:"session:cursor-hook",description:"Cursor lifecycle hook",category:"session",hidden:!0},{name:"session:copilot-hook",description:"Copilot lifecycle hook",category:"session",hidden:!0},{name:"session:grok-hook",description:"Grok lifecycle hook",category:"session",hidden:!0},{name:"memory:review",description:"Review memory entries",category:"session"},{name:"memory:reconcile",description:"Reconcile memory state",category:"session"},{name:"guard:check",description:"Check project rules",category:"guard"},{name:"guard:explain",description:"Show rules and evidence",category:"guard"},{name:"guard:json",description:"Output guard result as JSON",category:"guard",hidden:!0},{name:"project:manual-init",description:"Initialize project manual",category:"project"},{name:"project:manual-show",description:"Show project manual",category:"project"},{name:"project:manual-sync",description:"Sync project manual",category:"project"},{name:"snapshot:list",description:"List snapshots",category:"snapshot"},{name:"snapshot:create",description:"Create snapshot",category:"snapshot"},{name:"snapshot:restore",description:"Restore snapshot",category:"snapshot"},{name:"recover",description:"Recover latest snapshot",category:"snapshot"},{name:"service",description:"Run daemon in foreground",category:"service"},{name:"service:install",description:"Install/start daemon",category:"service"},{name:"service:start",description:"Start daemon",category:"service"},{name:"service:stop",description:"Stop daemon",category:"service"},{name:"service:restart",description:"Restart daemon",category:"service"},{name:"service:status",description:"Show daemon status",category:"service"},{name:"service:remove",description:"Remove daemon",category:"service"},{name:"mcp",description:"Run MCP server",category:"advanced"},{name:"api",description:"Run API server",category:"advanced"},{name:"run",description:"Run ToolNet runtime",category:"advanced",hidden:!0},{name:"production:certify",description:"Verify production runtime",category:"advanced"},{name:"certify:continuity",description:"Certify work continuity",category:"advanced"},{name:"ask-ai",description:"Ask AI agent (advanced)",category:"advanced",hidden:!0}];function x(o){return{tty:o.tty,noColor:o.noColor}}function b(o){let e=[],n=o.version??"0.0.0",t=x(o);e.push(""),e.push(d("ToolNet Memory",n,t)),e.push(" Persistent memory & code intelligence for AI coding agents"),e.push(""),e.push(a("USAGE",t)),e.push(" toolnet-memory <command> [options]"),e.push("");let i=[{key:"core",title:"GET STARTED"},{key:"memory",title:"MEMORY"},{key:"code",title:"CODE"},{key:"ai",title:"AI"},{key:"system",title:"SYSTEM"}];for(let{key:s,title:u}of i){let c=g.filter(r=>r.category===s&&!r.hidden);if(c.length===0)continue;e.push(a(u,t));let y=Math.max(...c.map(r=>r.name.length));for(let r of c)e.push(l(r.name,r.description,y,t));e.push("")}return e.push(a("MORE",t)),e.push(l("help --all","Show all advanced commands",15,t)),e.push(l("help <command>","Show command details",15,t)),e.push(""),e.join(`
2
- `)}function O(o){let e=[],n=o.version??"0.0.0",t=x(o);e.push(""),e.push(d("ToolNet Memory",n,t)),e.push(" Persistent memory & code intelligence for AI coding agents"),e.push(""),e.push(a("USAGE",t)),e.push(" toolnet-memory <command> [options]"),e.push("");let i=[{key:"core",title:"PROJECT SETUP"},{key:"memory",title:"MEMORY"},{key:"code",title:"CODE INTELLIGENCE"},{key:"ai",title:"AI / PROVIDERS"},{key:"system",title:"SYSTEM"},{key:"context",title:"CONTEXT & MEMORY"},{key:"work",title:"WORK CONTINUITY"},{key:"integration",title:"AGENT INTEGRATIONS"},{key:"session",title:"SESSIONS"},{key:"guard",title:"GUARD"},{key:"project",title:"PROJECT MANUAL"},{key:"snapshot",title:"SNAPSHOTS & RECOVERY"},{key:"service",title:"BACKGROUND SERVICE"},{key:"advanced",title:"DEVELOPER / PRODUCTION"}];for(let{key:s,title:u}of i){let c=g.filter(r=>r.category===s&&!r.hidden);if(c.length===0)continue;e.push(a(u,t));let y=Math.max(...c.map(r=>r.name.length));for(let r of c)e.push(l(r.name,r.description,y,t));e.push("")}return e.join(`
3
- `)}function S(o,e){let n=g.find(s=>s.name===o||s.aliases?.includes(o));if(!n)return null;let t=[],i=x(e);if(t.push(""),t.push(d(n.name,void 0,i)),t.push(""),t.push(n.description),t.push(""),n.usage&&(t.push(a("USAGE",i)),t.push(` ${n.usage}`),t.push("")),n.aliases&&n.aliases.length>0){t.push(a("ALIASES",i));for(let s of n.aliases)t.push(` ${s}`);t.push("")}if(n.pipeline&&n.pipeline.length>0){t.push(a("PIPELINE",i));for(let s of n.pipeline)t.push(` ${s}`);t.push("")}if(n.examples&&n.examples.length>0){t.push(a("EXAMPLES",i));for(let s of n.examples)t.push(` ${s}`);t.push("")}return t.join(`
4
- `)}function j(o){return g.flatMap(t=>[t.name,...t.aliases??[]]).filter(t=>t===o?!1:!!(t.startsWith(o)||o.length>=3&&t.includes(o))).slice(0,3)}function $(){let o=M(D(import.meta.url)),e=[w(o,"../package.json"),w(o,"../../package.json")];for(let n of e)try{let t=JSON.parse(N(n,"utf8"));if(t.version)return t.version}catch{}return process.env.npm_package_version??"unknown"}var p=process.argv.slice(2),C={version:$(),tty:!!process.stdout.isTTY};function I(o){let e=j(o);if(console.error(""),console.error(`\u2715 Unknown command: ${o}`),e.length>0){console.error(""),console.error(" Did you mean:");for(let n of e)console.error(` ${n}`)}console.error(""),console.error(" Run `toolnet-memory help` to see available commands."),console.error("")}if(p[0]==="--unknown")I(p[1]??""),process.exitCode=1;else if(p[0]==="--all")console.log(O(C));else if(p[0]){let o=S(p[0],C);o?console.log(o):(I(p[0]),process.exitCode=1)}else console.log(b(C));
1
+ import{readFileSync as A}from"node:fs";import{dirname as M,resolve as w}from"node:path";import{fileURLToPath as D}from"node:url";var I={spinner:["\xB7","\u2722","\u2733","\u2736","\u273B","\u273D"],barFilled:"\u2588",barEmpty:"\u2591",phaseDone:"\u25C6",phaseActive:"\u25C7",rail:"\u2502",branch:"\u251C",corner:"\u2514",dash:"\u2014"},R={spinner:[".","*","+","x","*","+"],barFilled:"#",barEmpty:"-",phaseDone:"[OK]",phaseActive:"[>>]",rail:"|",branch:"|",corner:"\\",dash:"-"};function U(){if(process.env.TOOLNET_ASCII==="1")return!1;if(process.env.TOOLNET_UNICODE==="1")return!0;if(process.platform==="win32"){let o=process.env.TERM_PROGRAM,e=process.env.WT_SESSION,n=process.env.VSCODE_PID,t=process.env.ConEmuPID;return!!(e||n||t||o==="vscode")}return process.env.TERM!=="linux"}var h;function k(){return h||(h=U()?I:R),h}var m={reset:"\x1B[0m",green:"\x1B[32m",cyan:"\x1B[36m",red:"\x1B[31m",yellow:"\x1B[33m",amber:"\x1B[38;5;214m",white:"\x1B[97m",dim:"\x1B[2m",bold:"\x1B[1m"};function T(o){return o.noColor||process.env.NO_COLOR?!1:o.tty??process.stdout.isTTY??!1}function f(o,e,n){return T(n)?`${e}${o}${m.reset}`:o}function G(o,e){return f(o,m.dim,e)}function N(o,e){return f(o,m.bold,e)}function b(o,e){return f(o,m.cyan,e)}function g(o,e,n){let t=k(),i=e?` ${G(`v${e}`,n)}`:"";return`${b(t.phaseDone,n)} ${o}${i}`}function a(o,e){return N(o,e)}function l(o,e,n,t){let i=" ".repeat(Math.max(0,n-o.length));return` ${b(o,t)}${i} ${e}`}var u=[{name:"init",description:"Initialize current project",usage:"toolnet-memory init [path]",category:"core"},{name:"doctor",description:"Check system health",category:"core"},{name:"ask",description:"Ask project memory",usage:"toolnet-memory ask <question>",category:"memory"},{name:"context",description:"Show current project context",category:"memory",aliases:["context:print"]},{name:"work",description:"Show current work state",category:"memory",aliases:["work:status"]},{name:"index",description:"Build code intelligence",category:"code",pipeline:["Scanning files","Parsing code","Type Resolution","Rich Graph","Semantic Code Index","Architecture Intelligence","Graph Analysis","3D Visualization Dataset"]},{name:"semantic",description:"Semantic code search",usage:"toolnet-memory semantic <query>",category:"code"},{name:"impact",description:"Analyze change impact",usage:"toolnet-memory impact <file>",category:"code"},{name:"graph",description:"Open code graph UI",category:"code"},{name:"graph:ui",description:"Open code graph UI",category:"code",hidden:!0},{name:"status",description:"Show ToolNet status",category:"system"},{name:"update",description:"Update ToolNet Memory",category:"system"},{name:"context:print",description:"Print fast project context",category:"context",hidden:!0},{name:"context:sync",description:"Show context hash/size",category:"context"},{name:"context:refresh",description:"Rebuild deep startup brief cache",category:"context"},{name:"brief",description:"Show full project brief",category:"context"},{name:"brief:json",description:"Show brief as JSON",category:"context"},{name:"profile:show",description:"Show .toolnet/profile.md",category:"context"},{name:"profile:sync",description:"Sync profile to agent files",category:"context"},{name:"work:status",description:"Show current work state",category:"work",hidden:!0},{name:"work:json",description:"Show work state as JSON",category:"work"},{name:"work:reconcile",description:"Reconcile work continuity",category:"work"},{name:"handoff:latest",description:"Show latest session handoff",category:"work"},{name:"handoff:refresh",description:"Refresh handoff data",category:"work"},{name:"index:graph",description:"Build graph index only",category:"code"},{name:"incremental",description:"Incremental code indexing",category:"code"},{name:"config",description:"Manage configuration",usage:"toolnet-memory config <get|set|list|open>",category:"system"},{name:"integrate:detect",description:"Detect agent integrations",category:"integration"},{name:"integrate:auto",description:"Auto-enable integrations with safe project scope policy",usage:"toolnet-memory integrate:auto [--all] [--scope global|project|both] [--project <path>] [--json]",category:"integration",examples:["toolnet-memory integrate:auto","toolnet-memory integrate:auto --project /path/to/toolnet-project","toolnet-memory integrate:auto --scope global","toolnet-memory integrate:auto --scope both --project /path/to/project"]},{name:"integrate:agy",description:"Install Agy hooks",category:"integration"},{name:"integrate:codex",description:"Install Codex integration",category:"integration"},{name:"integrate:opencode",description:"Install OpenCode plugin",category:"integration"},{name:"integrate:claude",description:"Install Claude Code integration",category:"integration"},{name:"integrate:kiro",description:"Install Kiro CLI integration",usage:"toolnet-memory integrate:kiro [--status] [--json]",category:"integration"},{name:"integrate:cursor",description:"Install Cursor CLI integration",usage:"toolnet-memory integrate:cursor [--scope global|project|both] [--project <path>] [--status] [--json]",category:"integration",examples:["toolnet-memory integrate:cursor --scope global","toolnet-memory integrate:cursor --scope project --project /path/to/project","toolnet-memory integrate:cursor --scope both --project /path/to/project"]},{name:"integrate:copilot",description:"Install GitHub Copilot CLI integration",usage:"toolnet-memory integrate:copilot [--scope global|project|both] [--project <path>] [--status] [--json]",category:"integration",examples:["toolnet-memory integrate:copilot --scope global","toolnet-memory integrate:copilot --scope project --project /path/to/project","toolnet-memory integrate:copilot --scope both --project /path/to/project"]},{name:"integrate:grok",description:"Install Grok Build integration",usage:"toolnet-memory integrate:grok [--scope global|project|both] [--project <path>] [--status] [--json]",category:"integration",examples:["toolnet-memory integrate:grok --scope global","toolnet-memory integrate:grok --scope project --project /path/to/project","toolnet-memory integrate:grok --scope both --project /path/to/project"]},{name:"integrate:toolnet-cli",description:"Install ToolNet CLI MCP integration",usage:"toolnet-memory integrate:toolnet-cli [--status] [--json] [--force]",category:"integration",examples:["toolnet-memory integrate:toolnet-cli","toolnet-memory integrate:toolnet-cli --status"]},{name:"integrate:kilo",description:"Install Kilo CLI MCP integration",usage:"toolnet-memory integrate:kilo [--status] [--json] [--force]",category:"integration",examples:["toolnet-memory integrate:kilo","toolnet-memory integrate:kilo --status"]},{name:"integrate:all",description:"Auto-enable all detected integrations",usage:"toolnet-memory integrate:all [--json]",category:"integration"},{name:"integrate:status",description:"Show unified Cursor/Copilot/Grok scoped integration status",usage:"toolnet-memory integrate:status [--scope global|project|both] [--project <path>] [--agent cursor|copilot|grok] [--json]",category:"integration",examples:["toolnet-memory integrate:status --scope global","toolnet-memory integrate:status --scope both --project /path/to/project","toolnet-memory integrate:status --scope both --project /path/to/project --agent cursor"]},{name:"session:agy-sync",description:"Sync Agy session",category:"session"},{name:"session:agy-recover",description:"Recover Agy sessions",category:"session"},{name:"session:agy-hook",description:"Agy session hook",category:"session",hidden:!0},{name:"session:codex-sync",description:"Sync Codex session",category:"session"},{name:"session:codex-recover",description:"Recover Codex sessions",category:"session"},{name:"session:codex-notify",description:"Codex notification hook",category:"session",hidden:!0},{name:"session:codex-context",description:"Codex context hook",category:"session",hidden:!0},{name:"session:opencode-sync",description:"Sync OpenCode session",category:"session"},{name:"session:opencode-recover",description:"Recover OpenCode sessions",category:"session"},{name:"session:claude-hook",description:"Claude session hook",category:"session",hidden:!0},{name:"session:kiro-hook",description:"Kiro lifecycle hook",category:"session",hidden:!0},{name:"session:cursor-hook",description:"Cursor lifecycle hook",category:"session",hidden:!0},{name:"session:copilot-hook",description:"Copilot lifecycle hook",category:"session",hidden:!0},{name:"session:grok-hook",description:"Grok lifecycle hook",category:"session",hidden:!0},{name:"memory:review",description:"Review memory entries",category:"session"},{name:"memory:reconcile",description:"Reconcile memory state",category:"session"},{name:"guard:check",description:"Check project rules",category:"guard"},{name:"guard:explain",description:"Show rules and evidence",category:"guard"},{name:"guard:json",description:"Output guard result as JSON",category:"guard",hidden:!0},{name:"project:manual-init",description:"Initialize project manual",category:"project"},{name:"project:manual-show",description:"Show project manual",category:"project"},{name:"project:manual-sync",description:"Sync project manual",category:"project"},{name:"snapshot:list",description:"List snapshots",category:"snapshot"},{name:"snapshot:create",description:"Create snapshot",category:"snapshot"},{name:"snapshot:restore",description:"Restore snapshot",category:"snapshot"},{name:"recover",description:"Recover latest snapshot",category:"snapshot"},{name:"service",description:"Run daemon in foreground",category:"service"},{name:"service:install",description:"Install/start daemon",category:"service"},{name:"service:start",description:"Start daemon",category:"service"},{name:"service:stop",description:"Stop daemon",category:"service"},{name:"service:restart",description:"Restart daemon",category:"service"},{name:"service:status",description:"Show daemon status",category:"service"},{name:"service:remove",description:"Remove daemon",category:"service"},{name:"mcp",description:"Run MCP server",category:"advanced"},{name:"api",description:"Run API server",category:"advanced"},{name:"run",description:"Run ToolNet runtime",category:"advanced",hidden:!0},{name:"production:certify",description:"Verify production runtime",category:"advanced"},{name:"certify:continuity",description:"Certify work continuity",category:"advanced"}];function x(o){return{tty:o.tty,noColor:o.noColor}}function O(o){let e=[],n=o.version??"0.0.0",t=x(o);e.push(""),e.push(g("ToolNet Memory",n,t)),e.push(" Persistent memory & code intelligence for AI coding agents"),e.push(""),e.push(a("USAGE",t)),e.push(" toolnet-memory <command> [options]"),e.push("");let i=[{key:"core",title:"GET STARTED"},{key:"memory",title:"MEMORY"},{key:"code",title:"CODE"},{key:"system",title:"SYSTEM"}];for(let{key:s,title:d}of i){let c=u.filter(r=>r.category===s&&!r.hidden);if(c.length===0)continue;e.push(a(d,t));let y=Math.max(...c.map(r=>r.name.length));for(let r of c)e.push(l(r.name,r.description,y,t));e.push("")}return e.push(a("MORE",t)),e.push(l("help --all","Show all advanced commands",15,t)),e.push(l("help <command>","Show command details",15,t)),e.push(""),e.join(`
2
+ `)}function v(o){let e=[],n=o.version??"0.0.0",t=x(o);e.push(""),e.push(g("ToolNet Memory",n,t)),e.push(" Persistent memory & code intelligence for AI coding agents"),e.push(""),e.push(a("USAGE",t)),e.push(" toolnet-memory <command> [options]"),e.push("");let i=[{key:"core",title:"PROJECT SETUP"},{key:"memory",title:"MEMORY"},{key:"code",title:"CODE INTELLIGENCE"},{key:"system",title:"SYSTEM"},{key:"context",title:"CONTEXT & MEMORY"},{key:"work",title:"WORK CONTINUITY"},{key:"integration",title:"AGENT INTEGRATIONS"},{key:"session",title:"SESSIONS"},{key:"guard",title:"GUARD"},{key:"project",title:"PROJECT MANUAL"},{key:"snapshot",title:"SNAPSHOTS & RECOVERY"},{key:"service",title:"BACKGROUND SERVICE"},{key:"advanced",title:"DEVELOPER / PRODUCTION"}];for(let{key:s,title:d}of i){let c=u.filter(r=>r.category===s&&!r.hidden);if(c.length===0)continue;e.push(a(d,t));let y=Math.max(...c.map(r=>r.name.length));for(let r of c)e.push(l(r.name,r.description,y,t));e.push("")}return e.join(`
3
+ `)}function S(o,e){let n=u.find(s=>s.name===o||s.aliases?.includes(o));if(!n)return null;let t=[],i=x(e);if(t.push(""),t.push(g(n.name,void 0,i)),t.push(""),t.push(n.description),t.push(""),n.usage&&(t.push(a("USAGE",i)),t.push(` ${n.usage}`),t.push("")),n.aliases&&n.aliases.length>0){t.push(a("ALIASES",i));for(let s of n.aliases)t.push(` ${s}`);t.push("")}if(n.pipeline&&n.pipeline.length>0){t.push(a("PIPELINE",i));for(let s of n.pipeline)t.push(` ${s}`);t.push("")}if(n.examples&&n.examples.length>0){t.push(a("EXAMPLES",i));for(let s of n.examples)t.push(` ${s}`);t.push("")}return t.join(`
4
+ `)}function j(o){return u.flatMap(t=>[t.name,...t.aliases??[]]).filter(t=>t===o?!1:!!(t.startsWith(o)||o.length>=3&&t.includes(o))).slice(0,3)}function P(){let o=M(D(import.meta.url)),e=[w(o,"../package.json"),w(o,"../../package.json")];for(let n of e)try{let t=JSON.parse(A(n,"utf8"));if(t.version)return t.version}catch{}return process.env.npm_package_version??"unknown"}var p=process.argv.slice(2),C={version:P(),tty:!!process.stdout.isTTY};function E(o){let e=j(o);if(console.error(""),console.error(`\u2715 Unknown command: ${o}`),e.length>0){console.error(""),console.error(" Did you mean:");for(let n of e)console.error(` ${n}`)}console.error(""),console.error(" Run `toolnet-memory help` to see available commands."),console.error("")}if(p[0]==="--unknown")E(p[1]??""),process.exitCode=1;else if(p[0]==="--all")console.log(v(C));else if(p[0]){let o=S(p[0],C);o?console.log(o):(E(p[0]),process.exitCode=1)}else console.log(O(C));
package/bundle/impact.js CHANGED
@@ -1,4 +1,4 @@
1
1
  import"dotenv/config";import{existsSync as se,readFileSync as oe}from"node:fs";import{homedir as ne}from"node:os";import{join as ie}from"node:path";function ae(s){let e=s.trim();return e.length>=2&&e.startsWith('"')&&e.endsWith('"')?(e=e.slice(1,-1),e.replace(/\\n/g,`
2
- `).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):e.length>=2&&e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function ce(){let s=process.env.TOOLNET_GLOBAL_ENV??ie(ne(),".config","toolnet-memory",".env");if(!se(s))return;let e=oe(s,"utf8");for(let t of e.split(/\r?\n/)){let r=t.trim();if(!r||r.startsWith("#"))continue;r.startsWith("export ")&&(r=r.slice(7));let o=r.indexOf("=");if(o<=0)continue;let n=r.slice(0,o).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(n)&&process.env[n]===void 0&&(process.env[n]=ae(r.slice(o+1)))}}ce();function y(s,e){return s===void 0?e:["1","true","yes","on"].includes(s.toLowerCase())}function S(s,e){if(!s)return e;let t=Number(s);return Number.isFinite(t)?t:e}function D(){return{memory:{autoCapture:y(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:y(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:y(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:y(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:S(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:S(process.env.MEMORY_RERANK_TOP,10),finalContext:S(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:S(process.env.MEMORY_TOKEN_BUDGET,2e3)},storage:{provider:process.env.MEMORY_STORAGE_PROVIDER??"huggingface",r2:{accountId:process.env.R2_ACCOUNT_ID,bucket:process.env.R2_BUCKET,accessKeyId:process.env.R2_ACCESS_KEY_ID,secretAccessKey:process.env.R2_SECRET_ACCESS_KEY},s3:{endpoint:process.env.S3_ENDPOINT,region:process.env.S3_REGION,bucket:process.env.S3_BUCKET,accessKeyId:process.env.S3_ACCESS_KEY_ID,secretAccessKey:process.env.S3_SECRET_ACCESS_KEY,forcePathStyle:y(process.env.S3_FORCE_PATH_STYLE,!1)},huggingface:{namespace:process.env.HF_NAMESPACE,bucket:process.env.HF_BUCKET,accessKeyId:process.env.HF_S3_ACCESS_KEY_ID,secretAccessKey:process.env.HF_S3_SECRET_ACCESS_KEY},localRoot:process.env.MEMORY_LOCAL_STORAGE_PATH},cache:{maxMb:S(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as le}from"node:crypto";import{existsSync as O,mkdirSync as de,readFileSync as pe,renameSync as ue,writeFileSync as me}from"node:fs";import{basename as fe,dirname as v,join as k,parse as z,resolve as b}from"node:path";var B=".toolnet",ge="project.json";function he(s){return le("sha256").update(s).digest("hex").slice(0,16)}function _(s){return k(s,B,ge)}function ye(s){return O(_(s))}function Se(s,e){let t=b(s),r=z(t).root;for(;;){if(ye(t))return t;if(t===r||e&&t===b(e))break;let o=v(t);if(o===t)break;t=o}return null}function be(s){let e=b(s),t=z(e).root,r=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(r.some(n=>O(k(e,n))))return e;if(e===t)break;let o=v(e);if(o===e)break;e=o}return b(s)}function xe(s){let e;try{e=JSON.parse(pe(s,"utf8"))}catch(o){throw new Error(`Invalid ToolNet project manifest: ${s}: ${o instanceof Error?o.message:String(o)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${s}`);let t=e;if(typeof t.id!="string"||!t.id.trim())throw new Error(`ToolNet project manifest is missing id: ${s}`);if(typeof t.name!="string"||!t.name.trim())throw new Error(`ToolNet project manifest is missing name: ${s}`);let r=new Date().toISOString();return{version:1,id:t.id,name:t.name,remote:typeof t.remote=="string"&&t.remote.trim()?t.remote:t.name,rootPath:typeof t.rootPath=="string"?t.rootPath:v(v(s)),createdAt:typeof t.createdAt=="string"?t.createdAt:r,updatedAt:typeof t.updatedAt=="string"?t.updatedAt:r,graphVersion:typeof t.graphVersion=="number"?t.graphVersion:0,memoryVersion:typeof t.memoryVersion=="number"?t.memoryVersion:0,metadata:t.metadata&&typeof t.metadata=="object"?t.metadata:void 0}}function G(s,e){let t=k(s,B);de(t,{recursive:!0});let r=_(s),o=`${r}.tmp-${process.pid}`;me(o,JSON.stringify(e,null,2)+`
3
- `,{encoding:"utf8",mode:384}),ue(o,r)}function K(s,e){return{id:s.id,name:s.name,remote:s.remote,rootPath:e,createdAt:s.createdAt,updatedAt:s.updatedAt,graphVersion:s.graphVersion,memoryVersion:s.memoryVersion,metadata:s.metadata}}var P=class{detect(e=process.cwd()){let t=b(e),r=be(t),n=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(d=>O(k(r,d))),i=Se(t,n?r:void 0);if(i){let d=_(i),f=xe(d);return f.rootPath!==i&&(f.rootPath=i,f.updatedAt=new Date().toISOString(),G(i,f)),K(f,i)}let p=new Date().toISOString(),l=fe(r),c={version:1,id:he(r),name:l,remote:l,rootPath:r,createdAt:p,updatedAt:p,graphVersion:0,memoryVersion:0};return G(r,c),K(c,r)}};import{homedir as We}from"node:os";import{join as He}from"node:path";import{DeleteObjectCommand as we,GetObjectCommand as Ee,HeadObjectCommand as ve,ListObjectsV2Command as Pe,PutObjectCommand as ke,S3Client as Re}from"@aws-sdk/client-s3";import{getSignedUrl as Te}from"@aws-sdk/s3-request-presigner";var R=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new Re({region:"us-east-1",endpoint:`https://s3.hf.co/${e.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,r="application/octet-stream"){let o=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new ke({Bucket:this.bucket,Key:e,Body:o,ContentType:r}))}async get(e){let t=await Te(this.client,new Ee({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(t,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`HF download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new ve({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new we({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let o=await this.client.send(new Pe({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let n of o.Contents??[])n.Key&&t.push({key:n.Key,size:n.Size,updatedAt:n.LastModified?.toISOString()});r=o.IsTruncated?o.NextContinuationToken:void 0}while(r);return t}};import{access as W,mkdir as je,readFile as Ae,readdir as Me,rm as Le,stat as H,writeFile as Ne}from"node:fs/promises";import{dirname as Ie,join as Oe,relative as U,resolve as _e}from"node:path";var x=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return _e(this.root,t)}async put(e,t){let r=this.path(e);await je(Ie(r),{recursive:!0}),await Ne(r,t)}async get(e){try{return await Ae(this.path(e))}catch(t){if(typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT")return null;throw t}}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await W(this.path(e)),!0}catch{return!1}}async delete(e){await Le(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),r=[];try{await W(t)}catch{return r}let o=async i=>{let p=await Me(i,{withFileTypes:!0});for(let l of p){let c=Oe(i,l.name);if(l.isDirectory()){await o(c);continue}let d=await H(c);r.push({key:U(this.root,c),size:d.size,updatedAt:d.mtime.toISOString()})}},n=await H(t);return n.isDirectory()?await o(t):r.push({key:U(this.root,t),size:n.size,updatedAt:n.mtime.toISOString()}),r}};import{DeleteObjectCommand as Fe,GetObjectCommand as $e,HeadObjectCommand as De,ListObjectsV2Command as Ge,PutObjectCommand as Ke,S3Client as ze}from"@aws-sdk/client-s3";import{getSignedUrl as Be}from"@aws-sdk/s3-request-presigner";var C=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new ze({region:e.region??"us-east-1",endpoint:e.endpoint||void 0,forcePathStyle:e.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,r="application/octet-stream"){let o=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new Ke({Bucket:this.bucket,Key:e,Body:o,ContentType:r}))}async get(e){let t=await Be(this.client,new $e({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(t,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`${this.name} download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new De({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new Fe({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let o=await this.client.send(new Ge({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let n of o.Contents??[])n.Key&&t.push({key:n.Key,size:n.Size,updatedAt:n.LastModified?.toISOString()});r=o.IsTruncated?o.NextContinuationToken:void 0}while(r);return t}};function F(s,e){return console.warn(e),new x(s)}function V(s){let e=s.localRoot??He(We(),".toolnet-memory","storage");if(s.provider==="r2"){let t=s.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new C({name:"r2",endpoint:`https://${t.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:t.bucket,forcePathStyle:!0,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):F(e,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(s.provider==="s3"){let t=s.s3;return t?.bucket&&t.accessKeyId&&t.secretAccessKey?new C({name:"s3",endpoint:t.endpoint,region:t.region??"us-east-1",bucket:t.bucket,forcePathStyle:t.forcePathStyle??!1,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):F(e,"[storage] S3 credentials missing. Using local fallback.")}if(s.provider==="huggingface"){let t=s.huggingface;return t?.namespace&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new R({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):F(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new x(e)}var w=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"graph","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));return t?JSON.parse(t):null}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};function Ue(s){return new Promise(e=>setTimeout(e,s))}async function Y(s,e={}){let t=Math.max(1,e.attempts??3),r=e.baseDelayMs??150,o=e.maxDelayMs??2e3,n;for(let i=1;i<=t;i++)try{return await s()}catch(p){if(n=p,i>=t)break;let l=Math.min(o,r*2**(i-1)),c=Math.floor(Math.random()*Math.max(1,l*.2));await Ue(l+c)}throw n}var Ve=new Set(["put","get","getText","delete","list"]);function q(s,e={}){return new Proxy(s,{get(t,r){let o=Reflect.get(t,r,t);return typeof o!="function"?o:Ve.has(r)?(...n)=>Y(()=>Promise.resolve(o.apply(t,n)),e):o.bind(t)}})}function J(s){let e=s.trim().replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,"").slice(0,100);if(!e||e==="."||e==="..")throw new Error("Invalid project storage folder");return e}function Q(s){let e=s.replace(/^\/+/,"");if(e.startsWith("memories/"))return"memory/records/"+e.slice(9);if(e.startsWith("vectors/"))return"memory/vectors/"+e.slice(8);if(e.startsWith("graph/"))return"code/graph/"+e.slice(6);let t=e.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(t)return`${t[1]}memory/records/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),t)return`${t[1]}memory/vectors/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),t)return`${t[1]}code/graph/${t[2]}`;let r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(r)return`${r[1]}memory/records/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),r)return`${r[1]}memory/vectors/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),r)return`${r[1]}code/graph/${r[2]}`;let o=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return o?`${o[1]}memory/records/${o[2]}`:(o=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),o?`${o[1]}memory/vectors/${o[2]}`:(o=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),o?`${o[1]}code/graph/${o[2]}`:e))}var T=class{constructor(e,t,r,o){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=r,this.folder=J(o??r),this.sourcePrefix=`projects/${t}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let e=`${this.targetPrefix}/project.json`,t=new Date().toISOString(),r=t,o=await this.provider.getText(e);if(o){let i;try{i=JSON.parse(o)}catch(p){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${p instanceof Error?p.message:String(p)}`)}if(typeof i.id=="string"&&i.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${i.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof i.createdAt=="string"&&(r=i.createdAt)}let n={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:r,updatedAt:t};await this.provider.put(e,JSON.stringify(n,null,2)+`
4
- `,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=Q(e),e===this.sourcePrefix)return this.targetPrefix;if(e.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+e.slice(this.sourcePrefix.length);if(e===this.targetPrefix||e.startsWith(`${this.targetPrefix}/`))return e;if(e.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${e}`].join(" "));return e}async put(e,t,r){return await this.ensureRegistered(),this.provider.put(this.key(e),t,r)}async get(e){return await this.ensureRegistered(),this.provider.get(this.key(e))}async getText(e){return await this.ensureRegistered(),this.provider.getText(this.key(e))}async delete(e){return await this.ensureRegistered(),this.provider.delete(this.key(e))}async exists(e){return await this.ensureRegistered(),this.provider.exists(this.key(e))}async list(e){return await this.ensureRegistered(),this.provider.list(this.key(e))}};import*as u from"typescript";var h=class{symbols=new Map;edges=new Map;addSymbol(e){this.symbols.set(e.id,e)}addEdge(e){this.edges.set(e.id,e)}getSymbol(e){return this.symbols.get(e)}findByName(e,t){return[...this.symbols.values()].filter(r=>r.projectId===e&&(r.name===t||r.qualifiedName===t))}allSymbols(e){return[...this.symbols.values()].filter(t=>t.projectId===e)}allEdges(e){return[...this.edges.values()].filter(t=>t.projectId===e)}import(e,t){for(let r of e)this.addSymbol(r);for(let r of t)this.addEdge(r)}clearProject(e){for(let[t,r]of this.symbols)r.projectId===e&&this.symbols.delete(t);for(let[t,r]of this.edges)r.projectId===e&&this.edges.delete(t)}};function Qe(s,e){return e.ranges.length===0?!0:typeof s.startLine!="number"||typeof s.endLine!="number"?!1:e.ranges.some(t=>s.startLine<=t.endLine&&s.endLine>=t.startLine)}var j=class{constructor(e){this.graph=e}graph;map(e,t){let r=this.graph.allSymbols(e),o=[];for(let n of t){let i=r.filter(c=>c.filePath===n.filePath),l=i.filter(c=>c.type!=="file").filter(c=>Qe(c,n));if(l.length===0){let c=i.find(d=>d.type==="file");c&&o.push({symbol:c,fileStatus:n.status,direct:!0});continue}for(let c of l)o.push({symbol:c,fileStatus:n.status,direct:!0})}return o}mapFile(e,t){return this.map(e,[{filePath:t,status:"modified",ranges:[]}])}};var Ze={CALLS:4,CALL_REFERENCE:6,USES_TYPE:4,TESTS:2,ROUTE:4,WRITES:3,IMPORTS:3,INHERITS:5,IMPLEMENTS:5,DEFINES:1};function Xe(s){return s>=30?"CRITICAL":s>=16?"HIGH":s>=6?"MEDIUM":"LOW"}function Z(s){let e=s.toLowerCase().replaceAll("\\","/");return e.startsWith("tests/")||e.includes("/tests/")||e.includes("__tests__")||/\.(test|spec)\.[^.]+$/.test(e)}var A=class{constructor(e){this.graph=e}graph;analyze(e,t,r=4){let o=this.graph.allEdges(e),n=new Map,i=0;for(let d of t){d.fileStatus==="deleted"?i+=8:d.fileStatus==="renamed"?i+=5:i+=2,d.symbol.type==="interface"&&(i+=5),d.symbol.type==="class"&&(i+=3);let f=new Set([d.symbol.id]),L=[{id:d.symbol.id,depth:0}];for(;L.length;){let N=L.shift();if(N.depth>=r)continue;let ee=o.filter(g=>g.to===N.id&&["CALLS","CALL_REFERENCE","USES_TYPE","TESTS","ROUTE","WRITES","IMPORTS","INHERITS","IMPLEMENTS","DEFINES"].includes(g.type));for(let g of ee){if(f.has(g.from))continue;f.add(g.from);let E=this.graph.getSymbol(g.from);if(!E)continue;let I=N.depth+1,te=`${d.symbol.id}:${E.id}`;n.set(te,{symbol:E,relation:g.type,depth:I,sourceSymbolId:d.symbol.id});let re=Ze[g.type]??1;i+=Math.max(1,re-(I-1)),L.push({id:E.id,depth:I})}}}let p=[...n.values()].sort((d,f)=>d.depth-f.depth),l=[...new Set(p.map(d=>d.symbol.filePath))],c=l.filter(Z);return c.length===0&&c.push(...l.filter(d=>!Z(d)).slice(0,10)),{risk:Xe(i),riskScore:i,changedSymbols:t,impacted:p,impactedFiles:l,suggestedTests:c}}};import{execFile as et}from"node:child_process";import{promisify as tt}from"node:util";var $=tt(et);function rt(s){let e=[],t=/^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@/gm;for(let r of s.matchAll(t)){let o=Number(r[1]),n=Number(r[2]??1);e.push({startLine:o,endLine:Math.max(o,o+n-1)})}return e}function st(s){return s.startsWith("A")?"added":s.startsWith("D")?"deleted":s.startsWith("R")?"renamed":"modified"}async function X(s){let e="";try{e=(await $("git",["diff","HEAD","--name-status","--no-renames"],{cwd:s,maxBuffer:10485760})).stdout}catch{return[]}let t=[];for(let r of e.split(/\r?\n/).filter(Boolean)){let[o,...n]=r.split(" "),i=n.at(-1);if(!i)continue;let p="";try{p=(await $("git",["diff","HEAD","--unified=0","--",i],{cwd:s,maxBuffer:20971520})).stdout}catch{p=""}t.push({filePath:i,status:st(o),ranges:rt(p)})}try{let r=await $("git",["ls-files","--others","--exclude-standard"],{cwd:s});for(let o of r.stdout.split(/\r?\n/).filter(Boolean))t.some(n=>n.filePath===o)||t.push({filePath:o,status:"added",ranges:[]})}catch{}return t}var M=class{constructor(e){this.graph=e;this.mapper=new j(e),this.blast=new A(e)}graph;mapper;blast;async analyzeGitDiff(e,t,r={}){let o=await X(t),n=this.mapper.map(e,o),i=this.blast.analyze(e,n,r.maxDepth??4);return{mode:"git_diff",changes:o,...i}}analyzeFile(e,t,r={}){let o=this.mapper.mapFile(e,t);return{mode:"planned_file",filePath:t,...this.blast.analyze(e,o,r.maxDepth??4)}}};import*as m from"typescript";import*as a from"typescript";var ko=new Set([a.SyntaxKind.EqualsToken,a.SyntaxKind.PlusEqualsToken,a.SyntaxKind.MinusEqualsToken,a.SyntaxKind.AsteriskEqualsToken,a.SyntaxKind.SlashEqualsToken,a.SyntaxKind.PercentEqualsToken,a.SyntaxKind.QuestionQuestionEqualsToken,a.SyntaxKind.AmpersandAmpersandEqualsToken,a.SyntaxKind.BarBarEqualsToken]);async function nt(){let s=D(),e=new P().detect(),t=q(V({provider:s.storage.provider,huggingface:s.storage.huggingface,localRoot:s.storage.localRoot}),{attempts:3}),r=new T(t,e.id,e.name,e.remote??e.name),o=await new w(r).load(e.id);if(!o)throw new Error("Code graph missing");let n=new h;n.import(o.symbols,o.edges);let i=new M(n),p=process.argv[2],l=p?i.analyzeFile(e.id,p):await i.analyzeGitDiff(e.id,e.rootPath);console.log({risk:l.risk,riskScore:l.riskScore,changedSymbols:l.changedSymbols.map(c=>({symbol:c.symbol.qualifiedName,file:c.symbol.filePath,type:c.symbol.type})),impactedFiles:l.impactedFiles,suggestedTests:l.suggestedTests,impactedCount:l.impacted.length})}nt().catch(s=>{console.error(s),process.exit(1)});
2
+ `).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):e.length>=2&&e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function ce(){let s=process.env.TOOLNET_GLOBAL_ENV??ie(ne(),".config","toolnet-memory",".env");if(!se(s))return;let e=oe(s,"utf8");for(let t of e.split(/\r?\n/)){let r=t.trim();if(!r||r.startsWith("#"))continue;r.startsWith("export ")&&(r=r.slice(7));let o=r.indexOf("=");if(o<=0)continue;let n=r.slice(0,o).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(n)&&process.env[n]===void 0&&(process.env[n]=ae(r.slice(o+1)))}}ce();function y(s,e){return s===void 0?e:["1","true","yes","on"].includes(s.toLowerCase())}function S(s,e){if(!s)return e;let t=Number(s);return Number.isFinite(t)?t:e}function $(){return{memory:{autoCapture:y(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:y(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:y(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:y(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:S(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:S(process.env.MEMORY_RERANK_TOP,10),finalContext:S(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:S(process.env.MEMORY_TOKEN_BUDGET,2e3)},storage:{provider:process.env.MEMORY_STORAGE_PROVIDER??"huggingface",r2:{accountId:process.env.R2_ACCOUNT_ID,bucket:process.env.R2_BUCKET,accessKeyId:process.env.R2_ACCESS_KEY_ID,secretAccessKey:process.env.R2_SECRET_ACCESS_KEY},s3:{endpoint:process.env.S3_ENDPOINT,region:process.env.S3_REGION,bucket:process.env.S3_BUCKET,accessKeyId:process.env.S3_ACCESS_KEY_ID,secretAccessKey:process.env.S3_SECRET_ACCESS_KEY,forcePathStyle:y(process.env.S3_FORCE_PATH_STYLE,!1)},huggingface:{namespace:process.env.HF_NAMESPACE,bucket:process.env.HF_BUCKET,accessKeyId:process.env.HF_S3_ACCESS_KEY_ID,secretAccessKey:process.env.HF_S3_SECRET_ACCESS_KEY},localRoot:process.env.MEMORY_LOCAL_STORAGE_PATH},cache:{maxMb:S(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as le}from"node:crypto";import{existsSync as O,mkdirSync as de,readFileSync as pe,renameSync as ue,writeFileSync as me}from"node:fs";import{basename as fe,dirname as v,join as P,parse as z,resolve as b}from"node:path";var B=".toolnet",ge="project.json";function he(s){return le("sha256").update(s).digest("hex").slice(0,16)}function _(s){return P(s,B,ge)}function ye(s){return O(_(s))}function Se(s,e){let t=b(s),r=z(t).root;for(;;){if(ye(t))return t;if(t===r||e&&t===b(e))break;let o=v(t);if(o===t)break;t=o}return null}function be(s){let e=b(s),t=z(e).root,r=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(r.some(n=>O(P(e,n))))return e;if(e===t)break;let o=v(e);if(o===e)break;e=o}return b(s)}function Ce(s){let e;try{e=JSON.parse(pe(s,"utf8"))}catch(o){throw new Error(`Invalid ToolNet project manifest: ${s}: ${o instanceof Error?o.message:String(o)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${s}`);let t=e;if(typeof t.id!="string"||!t.id.trim())throw new Error(`ToolNet project manifest is missing id: ${s}`);if(typeof t.name!="string"||!t.name.trim())throw new Error(`ToolNet project manifest is missing name: ${s}`);let r=new Date().toISOString();return{version:1,id:t.id,name:t.name,remote:typeof t.remote=="string"&&t.remote.trim()?t.remote:t.name,rootPath:typeof t.rootPath=="string"?t.rootPath:v(v(s)),createdAt:typeof t.createdAt=="string"?t.createdAt:r,updatedAt:typeof t.updatedAt=="string"?t.updatedAt:r,graphVersion:typeof t.graphVersion=="number"?t.graphVersion:0,memoryVersion:typeof t.memoryVersion=="number"?t.memoryVersion:0,metadata:t.metadata&&typeof t.metadata=="object"?t.metadata:void 0}}function G(s,e){let t=P(s,B);de(t,{recursive:!0});let r=_(s),o=`${r}.tmp-${process.pid}`;me(o,JSON.stringify(e,null,2)+`
3
+ `,{encoding:"utf8",mode:384}),ue(o,r)}function K(s,e){return{id:s.id,name:s.name,remote:s.remote,rootPath:e,createdAt:s.createdAt,updatedAt:s.updatedAt,graphVersion:s.graphVersion,memoryVersion:s.memoryVersion,metadata:s.metadata}}var k=class{detect(e=process.cwd()){let t=b(e),r=be(t),n=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(d=>O(P(r,d))),i=Se(t,n?r:void 0);if(i){let d=_(i),f=Ce(d);return f.rootPath!==i&&(f.rootPath=i,f.updatedAt=new Date().toISOString(),G(i,f)),K(f,i)}let p=new Date().toISOString(),l=fe(r),c={version:1,id:he(r),name:l,remote:l,rootPath:r,createdAt:p,updatedAt:p,graphVersion:0,memoryVersion:0};return G(r,c),K(c,r)}};import{homedir as We}from"node:os";import{join as Ue}from"node:path";import{DeleteObjectCommand as Ee,GetObjectCommand as we,HeadObjectCommand as ve,ListObjectsV2Command as ke,PutObjectCommand as Pe,S3Client as Re}from"@aws-sdk/client-s3";import{getSignedUrl as Te}from"@aws-sdk/s3-request-presigner";var R=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new Re({region:"us-east-1",endpoint:`https://s3.hf.co/${e.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,r="application/octet-stream"){let o=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new Pe({Bucket:this.bucket,Key:e,Body:o,ContentType:r}))}async get(e){let t=await Te(this.client,new we({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(t,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`HF download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new ve({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new Ee({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let o=await this.client.send(new ke({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let n of o.Contents??[])n.Key&&t.push({key:n.Key,size:n.Size,updatedAt:n.LastModified?.toISOString()});r=o.IsTruncated?o.NextContinuationToken:void 0}while(r);return t}};import{access as W,mkdir as Ae,readFile as je,readdir as Le,rm as Me,stat as U,writeFile as Ne}from"node:fs/promises";import{dirname as Ie,join as Oe,relative as H,resolve as _e}from"node:path";var C=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return _e(this.root,t)}async put(e,t){let r=this.path(e);await Ae(Ie(r),{recursive:!0}),await Ne(r,t)}async get(e){try{return await je(this.path(e))}catch(t){if(typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT")return null;throw t}}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await W(this.path(e)),!0}catch{return!1}}async delete(e){await Me(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),r=[];try{await W(t)}catch{return r}let o=async i=>{let p=await Le(i,{withFileTypes:!0});for(let l of p){let c=Oe(i,l.name);if(l.isDirectory()){await o(c);continue}let d=await U(c);r.push({key:H(this.root,c),size:d.size,updatedAt:d.mtime.toISOString()})}},n=await U(t);return n.isDirectory()?await o(t):r.push({key:H(this.root,t),size:n.size,updatedAt:n.mtime.toISOString()}),r}};import{DeleteObjectCommand as Fe,GetObjectCommand as De,HeadObjectCommand as $e,ListObjectsV2Command as Ge,PutObjectCommand as Ke,S3Client as ze}from"@aws-sdk/client-s3";import{getSignedUrl as Be}from"@aws-sdk/s3-request-presigner";var x=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new ze({region:e.region??"us-east-1",endpoint:e.endpoint||void 0,forcePathStyle:e.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,r="application/octet-stream"){let o=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new Ke({Bucket:this.bucket,Key:e,Body:o,ContentType:r}))}async get(e){let t=await Be(this.client,new De({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(t,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`${this.name} download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new $e({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new Fe({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let o=await this.client.send(new Ge({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let n of o.Contents??[])n.Key&&t.push({key:n.Key,size:n.Size,updatedAt:n.LastModified?.toISOString()});r=o.IsTruncated?o.NextContinuationToken:void 0}while(r);return t}};function F(s,e){return console.warn(e),new C(s)}function V(s){let e=s.localRoot??Ue(We(),".toolnet-memory","storage");if(s.provider==="r2"){let t=s.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new x({name:"r2",endpoint:`https://${t.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:t.bucket,forcePathStyle:!0,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):F(e,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(s.provider==="s3"){let t=s.s3;return t?.bucket&&t.accessKeyId&&t.secretAccessKey?new x({name:"s3",endpoint:t.endpoint,region:t.region??"us-east-1",bucket:t.bucket,forcePathStyle:t.forcePathStyle??!1,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):F(e,"[storage] S3 credentials missing. Using local fallback.")}if(s.provider==="huggingface"){let t=s.huggingface;return t?.namespace&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new R({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):F(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new C(e)}var E=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"graph","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));return t?JSON.parse(t):null}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};function He(s){return new Promise(e=>setTimeout(e,s))}async function Y(s,e={}){let t=Math.max(1,e.attempts??3),r=e.baseDelayMs??150,o=e.maxDelayMs??2e3,n;for(let i=1;i<=t;i++)try{return await s()}catch(p){if(n=p,i>=t)break;let l=Math.min(o,r*2**(i-1)),c=Math.floor(Math.random()*Math.max(1,l*.2));await He(l+c)}throw n}var Ve=new Set(["put","get","getText","delete","list"]);function J(s,e={}){return new Proxy(s,{get(t,r){let o=Reflect.get(t,r,t);return typeof o!="function"?o:Ve.has(r)?(...n)=>Y(()=>Promise.resolve(o.apply(t,n)),e):o.bind(t)}})}function q(s){let e=s.trim().replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,"").slice(0,100);if(!e||e==="."||e==="..")throw new Error("Invalid project storage folder");return e}function Q(s){let e=s.replace(/^\/+/,"");if(e.startsWith("memories/"))return"memory/records/"+e.slice(9);if(e.startsWith("vectors/"))return"memory/vectors/"+e.slice(8);if(e.startsWith("graph/"))return"code/graph/"+e.slice(6);let t=e.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(t)return`${t[1]}memory/records/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),t)return`${t[1]}memory/vectors/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),t)return`${t[1]}code/graph/${t[2]}`;let r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(r)return`${r[1]}memory/records/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),r)return`${r[1]}memory/vectors/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),r)return`${r[1]}code/graph/${r[2]}`;let o=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return o?`${o[1]}memory/records/${o[2]}`:(o=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),o?`${o[1]}memory/vectors/${o[2]}`:(o=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),o?`${o[1]}code/graph/${o[2]}`:e))}var T=class{constructor(e,t,r,o){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=r,this.folder=q(o??r),this.sourcePrefix=`projects/${t}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let e=`${this.targetPrefix}/project.json`,t=new Date().toISOString(),r=t,o=await this.provider.getText(e);if(o){let i;try{i=JSON.parse(o)}catch(p){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${p instanceof Error?p.message:String(p)}`)}if(typeof i.id=="string"&&i.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${i.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof i.createdAt=="string"&&(r=i.createdAt)}let n={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:r,updatedAt:t};await this.provider.put(e,JSON.stringify(n,null,2)+`
4
+ `,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=Q(e),e===this.sourcePrefix)return this.targetPrefix;if(e.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+e.slice(this.sourcePrefix.length);if(e===this.targetPrefix||e.startsWith(`${this.targetPrefix}/`))return e;if(e.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${e}`].join(" "));return e}async put(e,t,r){return await this.ensureRegistered(),this.provider.put(this.key(e),t,r)}async get(e){return await this.ensureRegistered(),this.provider.get(this.key(e))}async getText(e){return await this.ensureRegistered(),this.provider.getText(this.key(e))}async delete(e){return await this.ensureRegistered(),this.provider.delete(this.key(e))}async exists(e){return await this.ensureRegistered(),this.provider.exists(this.key(e))}async list(e){return await this.ensureRegistered(),this.provider.list(this.key(e))}};import*as u from"typescript";var h=class{symbols=new Map;edges=new Map;addSymbol(e){this.symbols.set(e.id,e)}addEdge(e){this.edges.set(e.id,e)}getSymbol(e){return this.symbols.get(e)}findByName(e,t){return[...this.symbols.values()].filter(r=>r.projectId===e&&(r.name===t||r.qualifiedName===t))}allSymbols(e){return[...this.symbols.values()].filter(t=>t.projectId===e)}allEdges(e){return[...this.edges.values()].filter(t=>t.projectId===e)}import(e,t){for(let r of e)this.addSymbol(r);for(let r of t)this.addEdge(r)}clearProject(e){for(let[t,r]of this.symbols)r.projectId===e&&this.symbols.delete(t);for(let[t,r]of this.edges)r.projectId===e&&this.edges.delete(t)}};function Qe(s,e){return e.ranges.length===0?!0:typeof s.startLine!="number"||typeof s.endLine!="number"?!1:e.ranges.some(t=>s.startLine<=t.endLine&&s.endLine>=t.startLine)}var A=class{constructor(e){this.graph=e}graph;map(e,t){let r=this.graph.allSymbols(e),o=[];for(let n of t){let i=r.filter(c=>c.filePath===n.filePath),l=i.filter(c=>c.type!=="file").filter(c=>Qe(c,n));if(l.length===0){let c=i.find(d=>d.type==="file");c&&o.push({symbol:c,fileStatus:n.status,direct:!0});continue}for(let c of l)o.push({symbol:c,fileStatus:n.status,direct:!0})}return o}mapFile(e,t){return this.map(e,[{filePath:t,status:"modified",ranges:[]}])}};var Ze={CALLS:4,CALL_REFERENCE:6,USES_TYPE:4,TESTS:2,ROUTE:4,WRITES:3,IMPORTS:3,INHERITS:5,IMPLEMENTS:5,DEFINES:1};function Xe(s){return s>=30?"CRITICAL":s>=16?"HIGH":s>=6?"MEDIUM":"LOW"}function Z(s){let e=s.toLowerCase().replaceAll("\\","/");return e.startsWith("tests/")||e.includes("/tests/")||e.includes("__tests__")||/\.(test|spec)\.[^.]+$/.test(e)}var j=class{constructor(e){this.graph=e}graph;analyze(e,t,r=4){let o=this.graph.allEdges(e),n=new Map,i=0;for(let d of t){d.fileStatus==="deleted"?i+=8:d.fileStatus==="renamed"?i+=5:i+=2,d.symbol.type==="interface"&&(i+=5),d.symbol.type==="class"&&(i+=3);let f=new Set([d.symbol.id]),M=[{id:d.symbol.id,depth:0}];for(;M.length;){let N=M.shift();if(N.depth>=r)continue;let ee=o.filter(g=>g.to===N.id&&["CALLS","CALL_REFERENCE","USES_TYPE","TESTS","ROUTE","WRITES","IMPORTS","INHERITS","IMPLEMENTS","DEFINES"].includes(g.type));for(let g of ee){if(f.has(g.from))continue;f.add(g.from);let w=this.graph.getSymbol(g.from);if(!w)continue;let I=N.depth+1,te=`${d.symbol.id}:${w.id}`;n.set(te,{symbol:w,relation:g.type,depth:I,sourceSymbolId:d.symbol.id});let re=Ze[g.type]??1;i+=Math.max(1,re-(I-1)),M.push({id:w.id,depth:I})}}}let p=[...n.values()].sort((d,f)=>d.depth-f.depth),l=[...new Set(p.map(d=>d.symbol.filePath))],c=l.filter(Z);return c.length===0&&c.push(...l.filter(d=>!Z(d)).slice(0,10)),{risk:Xe(i),riskScore:i,changedSymbols:t,impacted:p,impactedFiles:l,suggestedTests:c}}};import{execFile as et}from"node:child_process";import{promisify as tt}from"node:util";var D=tt(et);function rt(s){let e=[],t=/^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@/gm;for(let r of s.matchAll(t)){let o=Number(r[1]),n=Number(r[2]??1);e.push({startLine:o,endLine:Math.max(o,o+n-1)})}return e}function st(s){return s.startsWith("A")?"added":s.startsWith("D")?"deleted":s.startsWith("R")?"renamed":"modified"}async function X(s){let e="";try{e=(await D("git",["diff","HEAD","--name-status","--no-renames"],{cwd:s,maxBuffer:10485760})).stdout}catch{return[]}let t=[];for(let r of e.split(/\r?\n/).filter(Boolean)){let[o,...n]=r.split(" "),i=n.at(-1);if(!i)continue;let p="";try{p=(await D("git",["diff","HEAD","--unified=0","--",i],{cwd:s,maxBuffer:20971520})).stdout}catch{p=""}t.push({filePath:i,status:st(o),ranges:rt(p)})}try{let r=await D("git",["ls-files","--others","--exclude-standard"],{cwd:s});for(let o of r.stdout.split(/\r?\n/).filter(Boolean))t.some(n=>n.filePath===o)||t.push({filePath:o,status:"added",ranges:[]})}catch{}return t}var L=class{constructor(e){this.graph=e;this.mapper=new A(e),this.blast=new j(e)}graph;mapper;blast;async analyzeGitDiff(e,t,r={}){let o=await X(t),n=this.mapper.map(e,o),i=this.blast.analyze(e,n,r.maxDepth??4);return{mode:"git_diff",changes:o,...i}}analyzeFile(e,t,r={}){let o=this.mapper.mapFile(e,t);return{mode:"planned_file",filePath:t,...this.blast.analyze(e,o,r.maxDepth??4)}}};import{DatabaseSync as to}from"node:sqlite";import*as m from"typescript";import*as a from"typescript";var wo=new Set([a.SyntaxKind.EqualsToken,a.SyntaxKind.PlusEqualsToken,a.SyntaxKind.MinusEqualsToken,a.SyntaxKind.AsteriskEqualsToken,a.SyntaxKind.SlashEqualsToken,a.SyntaxKind.PercentEqualsToken,a.SyntaxKind.QuestionQuestionEqualsToken,a.SyntaxKind.AmpersandAmpersandEqualsToken,a.SyntaxKind.BarBarEqualsToken]);async function nt(){let s=$(),e=new k().detect(),t=J(V({provider:s.storage.provider,huggingface:s.storage.huggingface,localRoot:s.storage.localRoot}),{attempts:3}),r=new T(t,e.id,e.name,e.remote??e.name),o=await new E(r).load(e.id);if(!o)throw new Error("Code graph missing");let n=new h;n.import(o.symbols,o.edges);let i=new L(n),p=process.argv[2],l=p?i.analyzeFile(e.id,p):await i.analyzeGitDiff(e.id,e.rootPath);console.log({risk:l.risk,riskScore:l.riskScore,changedSymbols:l.changedSymbols.map(c=>({symbol:c.symbol.qualifiedName,file:c.symbol.filePath,type:c.symbol.type})),impactedFiles:l.impactedFiles,suggestedTests:l.suggestedTests,impactedCount:l.impacted.length})}nt().catch(s=>{console.error(s),process.exit(1)});
@@ -1,4 +1,4 @@
1
1
  import"dotenv/config";import{existsSync as de,readFileSync as pe}from"node:fs";import{homedir as le}from"node:os";import{join as ue}from"node:path";function me(s){let e=s.trim();return e.length>=2&&e.startsWith('"')&&e.endsWith('"')?(e=e.slice(1,-1),e.replace(/\\n/g,`
2
- `).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):e.length>=2&&e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function ge(){let s=process.env.TOOLNET_GLOBAL_ENV??ue(le(),".config","toolnet-memory",".env");if(!de(s))return;let e=pe(s,"utf8");for(let t of e.split(/\r?\n/)){let r=t.trim();if(!r||r.startsWith("#"))continue;r.startsWith("export ")&&(r=r.slice(7));let o=r.indexOf("=");if(o<=0)continue;let i=r.slice(0,o).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(i)&&process.env[i]===void 0&&(process.env[i]=me(r.slice(o+1)))}}ge();function P(s,e){return s===void 0?e:["1","true","yes","on"].includes(s.toLowerCase())}function C(s,e){if(!s)return e;let t=Number(s);return Number.isFinite(t)?t:e}function G(){return{memory:{autoCapture:P(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:P(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:P(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:P(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:C(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:C(process.env.MEMORY_RERANK_TOP,10),finalContext:C(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:C(process.env.MEMORY_TOKEN_BUDGET,2e3)},storage:{provider:process.env.MEMORY_STORAGE_PROVIDER??"huggingface",r2:{accountId:process.env.R2_ACCOUNT_ID,bucket:process.env.R2_BUCKET,accessKeyId:process.env.R2_ACCESS_KEY_ID,secretAccessKey:process.env.R2_SECRET_ACCESS_KEY},s3:{endpoint:process.env.S3_ENDPOINT,region:process.env.S3_REGION,bucket:process.env.S3_BUCKET,accessKeyId:process.env.S3_ACCESS_KEY_ID,secretAccessKey:process.env.S3_SECRET_ACCESS_KEY,forcePathStyle:P(process.env.S3_FORCE_PATH_STYLE,!1)},huggingface:{namespace:process.env.HF_NAMESPACE,bucket:process.env.HF_BUCKET,accessKeyId:process.env.HF_S3_ACCESS_KEY_ID,secretAccessKey:process.env.HF_S3_SECRET_ACCESS_KEY},localRoot:process.env.MEMORY_LOCAL_STORAGE_PATH},cache:{maxMb:C(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as fe}from"node:crypto";import{existsSync as L,mkdirSync as ye,readFileSync as he,renameSync as Se,writeFileSync as be}from"node:fs";import{basename as ve,dirname as E,join as T,parse as J,resolve as M}from"node:path";var Y=".toolnet",xe="project.json";function we(s){return fe("sha256").update(s).digest("hex").slice(0,16)}function F(s){return T(s,Y,xe)}function je(s){return L(F(s))}function ke(s,e){let t=M(s),r=J(t).root;for(;;){if(je(t))return t;if(t===r||e&&t===M(e))break;let o=E(t);if(o===t)break;t=o}return null}function Pe(s){let e=M(s),t=J(e).root,r=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(r.some(i=>L(T(e,i))))return e;if(e===t)break;let o=E(e);if(o===e)break;e=o}return M(s)}function Ce(s){let e;try{e=JSON.parse(he(s,"utf8"))}catch(o){throw new Error(`Invalid ToolNet project manifest: ${s}: ${o instanceof Error?o.message:String(o)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${s}`);let t=e;if(typeof t.id!="string"||!t.id.trim())throw new Error(`ToolNet project manifest is missing id: ${s}`);if(typeof t.name!="string"||!t.name.trim())throw new Error(`ToolNet project manifest is missing name: ${s}`);let r=new Date().toISOString();return{version:1,id:t.id,name:t.name,remote:typeof t.remote=="string"&&t.remote.trim()?t.remote:t.name,rootPath:typeof t.rootPath=="string"?t.rootPath:E(E(s)),createdAt:typeof t.createdAt=="string"?t.createdAt:r,updatedAt:typeof t.updatedAt=="string"?t.updatedAt:r,graphVersion:typeof t.graphVersion=="number"?t.graphVersion:0,memoryVersion:typeof t.memoryVersion=="number"?t.memoryVersion:0,metadata:t.metadata&&typeof t.metadata=="object"?t.metadata:void 0}}function U(s,e){let t=T(s,Y);ye(t,{recursive:!0});let r=F(s),o=`${r}.tmp-${process.pid}`;be(o,JSON.stringify(e,null,2)+`
3
- `,{encoding:"utf8",mode:384}),Se(o,r)}function H(s,e){return{id:s.id,name:s.name,remote:s.remote,rootPath:e,createdAt:s.createdAt,updatedAt:s.updatedAt,graphVersion:s.graphVersion,memoryVersion:s.memoryVersion,metadata:s.metadata}}var I=class{detect(e=process.cwd()){let t=M(e),r=Pe(t),i=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(l=>L(T(r,l))),n=ke(t,i?r:void 0);if(n){let l=F(n),m=Ce(l);return m.rootPath!==n&&(m.rootPath=n,m.updatedAt=new Date().toISOString(),U(n,m)),H(m,n)}let u=new Date().toISOString(),d=ve(r),c={version:1,id:we(r),name:d,remote:d,rootPath:r,createdAt:u,updatedAt:u,graphVersion:0,memoryVersion:0};return U(r,c),H(c,r)}};import{homedir as We}from"node:os";import{join as Qe}from"node:path";import{DeleteObjectCommand as Me,GetObjectCommand as Re,HeadObjectCommand as Ae,ListObjectsV2Command as Ee,PutObjectCommand as Ie,S3Client as Te}from"@aws-sdk/client-s3";import{getSignedUrl as Oe}from"@aws-sdk/s3-request-presigner";var O=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new Te({region:"us-east-1",endpoint:`https://s3.hf.co/${e.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,r="application/octet-stream"){let o=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new Ie({Bucket:this.bucket,Key:e,Body:o,ContentType:r}))}async get(e){let t=await Oe(this.client,new Re({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(t,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`HF download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new Ae({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new Me({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let o=await this.client.send(new Ee({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let i of o.Contents??[])i.Key&&t.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});r=o.IsTruncated?o.NextContinuationToken:void 0}while(r);return t}};import{access as W,mkdir as Ne,readFile as $e,readdir as _e,rm as Ke,stat as Q,writeFile as De}from"node:fs/promises";import{dirname as Le,join as Fe,relative as Z,resolve as Be}from"node:path";var R=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return Be(this.root,t)}async put(e,t){let r=this.path(e);await Ne(Le(r),{recursive:!0}),await De(r,t)}async get(e){try{return await $e(this.path(e))}catch(t){if(typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT")return null;throw t}}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await W(this.path(e)),!0}catch{return!1}}async delete(e){await Ke(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),r=[];try{await W(t)}catch{return r}let o=async n=>{let u=await _e(n,{withFileTypes:!0});for(let d of u){let c=Fe(n,d.name);if(d.isDirectory()){await o(c);continue}let l=await Q(c);r.push({key:Z(this.root,c),size:l.size,updatedAt:l.mtime.toISOString()})}},i=await Q(t);return i.isDirectory()?await o(t):r.push({key:Z(this.root,t),size:i.size,updatedAt:i.mtime.toISOString()}),r}};import{DeleteObjectCommand as ze,GetObjectCommand as Ve,HeadObjectCommand as Ge,ListObjectsV2Command as Ue,PutObjectCommand as He,S3Client as Je}from"@aws-sdk/client-s3";import{getSignedUrl as Ye}from"@aws-sdk/s3-request-presigner";var A=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new Je({region:e.region??"us-east-1",endpoint:e.endpoint||void 0,forcePathStyle:e.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,r="application/octet-stream"){let o=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new He({Bucket:this.bucket,Key:e,Body:o,ContentType:r}))}async get(e){let t=await Ye(this.client,new Ve({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(t,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`${this.name} download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new Ge({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new ze({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let o=await this.client.send(new Ue({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let i of o.Contents??[])i.Key&&t.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});r=o.IsTruncated?o.NextContinuationToken:void 0}while(r);return t}};function B(s,e){return console.warn(e),new R(s)}function q(s){let e=s.localRoot??Qe(We(),".toolnet-memory","storage");if(s.provider==="r2"){let t=s.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new A({name:"r2",endpoint:`https://${t.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:t.bucket,forcePathStyle:!0,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):B(e,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(s.provider==="s3"){let t=s.s3;return t?.bucket&&t.accessKeyId&&t.secretAccessKey?new A({name:"s3",endpoint:t.endpoint,region:t.region??"us-east-1",bucket:t.bucket,forcePathStyle:t.forcePathStyle??!1,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):B(e,"[storage] S3 credentials missing. Using local fallback.")}if(s.provider==="huggingface"){let t=s.huggingface;return t?.namespace&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new O({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):B(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new R(e)}var N=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"graph","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));return t?JSON.parse(t):null}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};var $=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"graph","manifest.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));return t?JSON.parse(t):null}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};function X(s){let e=s.trim().replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,"").slice(0,100);if(!e||e==="."||e==="..")throw new Error("Invalid project storage folder");return e}function ee(s){let e=s.replace(/^\/+/,"");if(e.startsWith("memories/"))return"memory/records/"+e.slice(9);if(e.startsWith("vectors/"))return"memory/vectors/"+e.slice(8);if(e.startsWith("graph/"))return"code/graph/"+e.slice(6);let t=e.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(t)return`${t[1]}memory/records/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),t)return`${t[1]}memory/vectors/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),t)return`${t[1]}code/graph/${t[2]}`;let r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(r)return`${r[1]}memory/records/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),r)return`${r[1]}memory/vectors/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),r)return`${r[1]}code/graph/${r[2]}`;let o=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return o?`${o[1]}memory/records/${o[2]}`:(o=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),o?`${o[1]}memory/vectors/${o[2]}`:(o=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),o?`${o[1]}code/graph/${o[2]}`:e))}var _=class{constructor(e,t,r,o){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=r,this.folder=X(o??r),this.sourcePrefix=`projects/${t}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let e=`${this.targetPrefix}/project.json`,t=new Date().toISOString(),r=t,o=await this.provider.getText(e);if(o){let n;try{n=JSON.parse(o)}catch(u){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${u instanceof Error?u.message:String(u)}`)}if(typeof n.id=="string"&&n.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${n.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof n.createdAt=="string"&&(r=n.createdAt)}let i={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:r,updatedAt:t};await this.provider.put(e,JSON.stringify(i,null,2)+`
4
- `,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=ee(e),e===this.sourcePrefix)return this.targetPrefix;if(e.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+e.slice(this.sourcePrefix.length);if(e===this.targetPrefix||e.startsWith(`${this.targetPrefix}/`))return e;if(e.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${e}`].join(" "));return e}async put(e,t,r){return await this.ensureRegistered(),this.provider.put(this.key(e),t,r)}async get(e){return await this.ensureRegistered(),this.provider.get(this.key(e))}async getText(e){return await this.ensureRegistered(),this.provider.getText(this.key(e))}async delete(e){return await this.ensureRegistered(),this.provider.delete(this.key(e))}async exists(e){return await this.ensureRegistered(),this.provider.exists(this.key(e))}async list(e){return await this.ensureRegistered(),this.provider.list(this.key(e))}};import{join as ct}from"node:path";import{readdir as et}from"node:fs/promises";import{extname as tt,join as rt,relative as st}from"node:path";import{basename as Ze}from"node:path";var qe=new Set([".env",".env.local",".env.production",".env.development","id_rsa","id_ed25519","credentials","credentials.json","service-account.json"]),Xe=new Set([".pem",".key",".p12",".pfx"]);function te(s){let e=Ze(s).toLowerCase();if(qe.has(e))return!0;for(let t of Xe)if(e.endsWith(t))return!0;return!1}var ot=new Set([".ts",".tsx",".js",".jsx",".mjs",".cjs",".mts",".cts"]),nt=new Set(["node_modules",".git","dist","build","coverage",".next",".cache",".toolnet-memory"]);async function re(s){let e=[];async function t(r){let o=await et(r,{withFileTypes:!0});for(let i of o){if(nt.has(i.name))continue;let n=rt(r,i.name);if(i.isDirectory()){await t(n);continue}ot.has(tt(i.name))&&(te(n)||e.push(st(s,n)))}}return await t(s),e.sort()}import{createHash as it}from"node:crypto";import{readFile as at}from"node:fs/promises";async function se(s){let e=await at(s);return it("sha256").update(e).digest("hex")}async function oe(s,e){let t=await re(e),r={};for(let o of t)r[o]={path:o,hash:await se(ct(e,o))};return{version:1,projectId:s,updatedAt:new Date().toISOString(),files:r}}function ne(s,e){let t=s?.files??{},r=e.files,o=[],i=[],n=[],u=[];for(let[d,c]of Object.entries(r)){let l=t[d];l?l.hash!==c.hash?i.push(d):u.push(d):o.push(d)}for(let d of Object.keys(t))r[d]||n.push(d);return{added:o.sort(),modified:i.sort(),deleted:n.sort(),unchanged:u.sort()}}import{readFile as dt}from"node:fs/promises";import{join as pt}from"node:path";import{createHash as lt}from"node:crypto";import*as p from"typescript";function ie(s,e){return lt("sha256").update(`${s}:${e}`).digest("hex").slice(0,24)}function z(s,e){return s.getLineAndCharacterOfPosition(e.getStart(s)).line+1}async function ae(s,e,t){let r=pt(e,t),o=await dt(r,"utf8"),i=p.createSourceFile(t,o,p.ScriptTarget.Latest,!0,t.endsWith(".tsx")||t.endsWith(".jsx")?p.ScriptKind.TSX:p.ScriptKind.TS),n=[],u=[],d=[],c=[],l={id:ie(s,`file:${t}`),projectId:s,name:t,qualifiedName:t,type:"file",filePath:t,startLine:1,endLine:i.getLineAndCharacterOfPosition(i.end).line+1};n.push(l);let m=[],b=[];function y(a,S,h,f){let x=ie(s,`${t}:${h}:${f??S}:${a.pos}`);return n.push({id:x,projectId:s,name:S,qualifiedName:f??S,type:h,filePath:t,startLine:z(i,a),endLine:i.getLineAndCharacterOfPosition(a.end).line+1}),x}function j(a){if(!p.isStringLiteral(a.moduleSpecifier))return;let S=[],h=a.importClause;if(h?.name&&S.push({localName:h.name.text,importedName:"default",kind:"default"}),h?.namedBindings&&p.isNamedImports(h.namedBindings))for(let f of h.namedBindings.elements)S.push({localName:f.name.text,importedName:f.propertyName?.text??f.name.text,kind:"named"});h?.namedBindings&&p.isNamespaceImport(h.namedBindings)&&S.push({localName:h.namedBindings.name.text,importedName:"*",kind:"namespace"}),u.push({source:a.moduleSpecifier.text,bindings:S})}function v(a,S){for(let h of a.heritageClauses??[])for(let f of h.types){let x=f.expression.getText(i).split(".").at(-1)??"";x&&c.push({fromId:S,targetName:x,type:h.token===p.SyntaxKind.ExtendsKeyword?"INHERITS":"IMPLEMENTS"})}}function g(a){let S=!1,h=!1;if(p.isImportDeclaration(a)&&j(a),p.isInterfaceDeclaration(a)&&y(a,a.name.text,"interface",a.name.text),p.isClassDeclaration(a)&&a.name){let f=y(a,a.name.text,"class",a.name.text);v(a,f),b.push({id:f,name:a.name.text}),h=!0}if(p.isFunctionDeclaration(a)&&a.name){let f=y(a,a.name.text,"function",a.name.text);m.push(f),S=!0}if(p.isMethodDeclaration(a)&&a.name){let f=a.name.getText(i),x=b.at(-1)?.name,ce=y(a,f,"method",x?`${x}.${f}`:f);m.push(ce),S=!0}if(p.isCallExpression(a)&&(p.isIdentifier(a.expression)&&d.push({callerId:m.at(-1),calleeName:a.expression.text,line:z(i,a)}),p.isPropertyAccessExpression(a.expression))){let f=a.expression;d.push({callerId:m.at(-1),qualifier:f.expression.getText(i),calleeName:f.name.text,line:z(i,a)})}p.forEachChild(a,g),S&&m.pop(),h&&b.pop()}return g(i),{filePath:t,symbols:n,imports:u,calls:d,heritage:c}}import{createHash as ut}from"node:crypto";import{dirname as mt,extname as gt,normalize as ft,posix as yt}from"node:path";var k=class{symbols=new Map;edges=new Map;addSymbol(e){this.symbols.set(e.id,e)}addEdge(e){this.edges.set(e.id,e)}getSymbol(e){return this.symbols.get(e)}findByName(e,t){return[...this.symbols.values()].filter(r=>r.projectId===e&&(r.name===t||r.qualifiedName===t))}allSymbols(e){return[...this.symbols.values()].filter(t=>t.projectId===e)}allEdges(e){return[...this.edges.values()].filter(t=>t.projectId===e)}import(e,t){for(let r of e)this.addSymbol(r);for(let r of t)this.addEdge(r)}clearProject(e){for(let[t,r]of this.symbols)r.projectId===e&&this.symbols.delete(t);for(let[t,r]of this.edges)r.projectId===e&&this.edges.delete(t)}};function ht(s,e,t,r){return ut("sha256").update(`${s}:${e}:${t}:${r}`).digest("hex").slice(0,24)}function w(s){return ft(s).replaceAll("\\","/")}function V(s,e,t){if(!e.startsWith("."))return;let r=w(yt.join(mt(s),e)),o=gt(r),i;if(o===".js"){let n=r.slice(0,-3);i=[`${n}.ts`,`${n}.tsx`,`${n}.js`,`${n}.jsx`]}else if(o===".mjs"){let n=r.slice(0,-4);i=[`${n}.mts`,`${n}.ts`,`${n}.mjs`]}else if(o===".cjs"){let n=r.slice(0,-4);i=[`${n}.cts`,`${n}.ts`,`${n}.cjs`]}else o?i=[r]:i=[`${r}.ts`,`${r}.tsx`,`${r}.mts`,`${r}.cts`,`${r}.js`,`${r}.jsx`,`${r}.mjs`,`${r}.cjs`,`${r}/index.ts`,`${r}/index.tsx`,`${r}/index.js`,`${r}/index.jsx`];return i.find(n=>t.has(w(n)))}var K=class{build(e,t){let r=new k,o=new Map;for(let n of t)for(let u of n.symbols)r.addSymbol(u),u.type==="file"&&o.set(w(n.filePath),u);let i=new Set(o.keys());for(let n of t){let u=w(n.filePath),d=o.get(u);if(d){for(let c of n.symbols)c.id!==d.id&&r.addEdge(this.edge(e,d.id,"DEFINES",c.id));for(let c of n.imports){let l=V(u,c.source,i);if(!l)continue;let m=o.get(l);m&&r.addEdge(this.edge(e,d.id,"IMPORTS",m.id,{source:c.source}))}for(let c of n.heritage){let l=r.findByName(e,c.targetName).filter(m=>m.type==="class"||m.type==="interface");for(let m of l)r.addEdge(this.edge(e,c.fromId,c.type,m.id))}for(let c of n.calls){if(!c.callerId)continue;let l=this.resolveCallTargets(e,n,c.calleeName,c.qualifier,t,r,i);for(let m of l)m.id!==c.callerId&&r.addEdge(this.edge(e,c.callerId,"CALLS",m.id,{line:c.line,qualifier:c.qualifier}))}}}return r}resolveCallTargets(e,t,r,o,i,n,u){let d=w(t.filePath),c=n.findByName(e,r).filter(l=>w(l.filePath)===d);if(!o&&c.length)return c;if(o)for(let l of t.imports){if(!l.bindings.find(y=>y.kind==="namespace"&&y.localName===o))continue;let b=V(d,l.source,u);if(b)return n.findByName(e,r).filter(y=>w(y.filePath)===b)}for(let l of t.imports){let m=l.bindings.find(v=>v.localName===r);if(!m)continue;let b=V(d,l.source,u);if(!b)continue;let y=m.importedName==="default"?r:m.importedName,j=n.findByName(e,y).filter(v=>w(v.filePath)===b);if(j.length)return j}return n.findByName(e,r).filter(l=>l.type==="function"||l.type==="method")}edge(e,t,r,o,i){return{id:ht(e,t,r,o),projectId:e,from:t,to:o,type:r,metadata:i}}};var D=class{constructor(e){this.storage=e}storage;async index(e,t){let r=new $(this.storage),o=new N(this.storage),i=await r.load(e),n=await o.load(e),u=await oe(e,t),d=ne(i,u),c=[...d.added,...d.modified];if(i&&n&&c.length===0&&d.deleted.length===0)return{firstRun:!1,scanned:Object.keys(u.files).length,parsed:0,added:0,modified:0,deleted:0,unchanged:d.unchanged.length,symbols:n.symbols.length,edges:n.edges.length,saved:!1};let l=n?.symbols.filter(g=>d.unchanged.includes(g.filePath))??[],m=[];for(let g of c)m.push(await ae(e,t,g));let b=new K().build(e,m),y=new k;for(let g of l)y.addSymbol(g);for(let g of b.allSymbols(e))y.addSymbol(g);let j=new Set(y.allSymbols(e).map(g=>g.id));for(let g of n?.edges??[])j.has(g.from)&&j.has(g.to)&&!c.includes(n.symbols.find(a=>a.id===g.from)?.filePath??"")&&!c.includes(n.symbols.find(a=>a.id===g.to)?.filePath??"")&&y.addEdge(g);for(let g of b.allEdges(e))y.addEdge(g);let v={version:1,projectId:e,updatedAt:new Date().toISOString(),files:Object.keys(u.files).length,symbols:y.allSymbols(e),edges:y.allEdges(e)};return await o.save(v),await r.save(u),{firstRun:i===null,scanned:Object.keys(u.files).length,parsed:c.length,added:d.added.length,modified:d.modified.length,deleted:d.deleted.length,unchanged:d.unchanged.length,symbols:v.symbols.length,edges:v.edges.length,saved:!0}}};async function St(){let s=G(),e=new I().detect(),t=q({provider:s.storage.provider,huggingface:s.storage.huggingface,localRoot:s.storage.localRoot}),r=new _(t,e.id,e.name,e.remote??e.name),o=await new D(r).index(e.id,e.rootPath);console.log({ok:!0,storage:r.name,...o})}St().catch(s=>{console.error(s),process.exit(1)});
2
+ `).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):e.length>=2&&e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function ge(){let s=process.env.TOOLNET_GLOBAL_ENV??ue(le(),".config","toolnet-memory",".env");if(!de(s))return;let e=pe(s,"utf8");for(let t of e.split(/\r?\n/)){let r=t.trim();if(!r||r.startsWith("#"))continue;r.startsWith("export ")&&(r=r.slice(7));let n=r.indexOf("=");if(n<=0)continue;let i=r.slice(0,n).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(i)&&process.env[i]===void 0&&(process.env[i]=me(r.slice(n+1)))}}ge();function P(s,e){return s===void 0?e:["1","true","yes","on"].includes(s.toLowerCase())}function C(s,e){if(!s)return e;let t=Number(s);return Number.isFinite(t)?t:e}function G(){return{memory:{autoCapture:P(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:P(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:P(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:P(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:C(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:C(process.env.MEMORY_RERANK_TOP,10),finalContext:C(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:C(process.env.MEMORY_TOKEN_BUDGET,2e3)},storage:{provider:process.env.MEMORY_STORAGE_PROVIDER??"huggingface",r2:{accountId:process.env.R2_ACCOUNT_ID,bucket:process.env.R2_BUCKET,accessKeyId:process.env.R2_ACCESS_KEY_ID,secretAccessKey:process.env.R2_SECRET_ACCESS_KEY},s3:{endpoint:process.env.S3_ENDPOINT,region:process.env.S3_REGION,bucket:process.env.S3_BUCKET,accessKeyId:process.env.S3_ACCESS_KEY_ID,secretAccessKey:process.env.S3_SECRET_ACCESS_KEY,forcePathStyle:P(process.env.S3_FORCE_PATH_STYLE,!1)},huggingface:{namespace:process.env.HF_NAMESPACE,bucket:process.env.HF_BUCKET,accessKeyId:process.env.HF_S3_ACCESS_KEY_ID,secretAccessKey:process.env.HF_S3_SECRET_ACCESS_KEY},localRoot:process.env.MEMORY_LOCAL_STORAGE_PATH},cache:{maxMb:C(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as fe}from"node:crypto";import{existsSync as L,mkdirSync as ye,readFileSync as he,renameSync as Se,writeFileSync as be}from"node:fs";import{basename as ve,dirname as E,join as T,parse as J,resolve as R}from"node:path";var Y=".toolnet",xe="project.json";function we(s){return fe("sha256").update(s).digest("hex").slice(0,16)}function F(s){return T(s,Y,xe)}function je(s){return L(F(s))}function ke(s,e){let t=R(s),r=J(t).root;for(;;){if(je(t))return t;if(t===r||e&&t===R(e))break;let n=E(t);if(n===t)break;t=n}return null}function Pe(s){let e=R(s),t=J(e).root,r=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(r.some(i=>L(T(e,i))))return e;if(e===t)break;let n=E(e);if(n===e)break;e=n}return R(s)}function Ce(s){let e;try{e=JSON.parse(he(s,"utf8"))}catch(n){throw new Error(`Invalid ToolNet project manifest: ${s}: ${n instanceof Error?n.message:String(n)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${s}`);let t=e;if(typeof t.id!="string"||!t.id.trim())throw new Error(`ToolNet project manifest is missing id: ${s}`);if(typeof t.name!="string"||!t.name.trim())throw new Error(`ToolNet project manifest is missing name: ${s}`);let r=new Date().toISOString();return{version:1,id:t.id,name:t.name,remote:typeof t.remote=="string"&&t.remote.trim()?t.remote:t.name,rootPath:typeof t.rootPath=="string"?t.rootPath:E(E(s)),createdAt:typeof t.createdAt=="string"?t.createdAt:r,updatedAt:typeof t.updatedAt=="string"?t.updatedAt:r,graphVersion:typeof t.graphVersion=="number"?t.graphVersion:0,memoryVersion:typeof t.memoryVersion=="number"?t.memoryVersion:0,metadata:t.metadata&&typeof t.metadata=="object"?t.metadata:void 0}}function U(s,e){let t=T(s,Y);ye(t,{recursive:!0});let r=F(s),n=`${r}.tmp-${process.pid}`;be(n,JSON.stringify(e,null,2)+`
3
+ `,{encoding:"utf8",mode:384}),Se(n,r)}function H(s,e){return{id:s.id,name:s.name,remote:s.remote,rootPath:e,createdAt:s.createdAt,updatedAt:s.updatedAt,graphVersion:s.graphVersion,memoryVersion:s.memoryVersion,metadata:s.metadata}}var I=class{detect(e=process.cwd()){let t=R(e),r=Pe(t),i=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(l=>L(T(r,l))),o=ke(t,i?r:void 0);if(o){let l=F(o),m=Ce(l);return m.rootPath!==o&&(m.rootPath=o,m.updatedAt=new Date().toISOString(),U(o,m)),H(m,o)}let u=new Date().toISOString(),d=ve(r),c={version:1,id:we(r),name:d,remote:d,rootPath:r,createdAt:u,updatedAt:u,graphVersion:0,memoryVersion:0};return U(r,c),H(c,r)}};import{homedir as We}from"node:os";import{join as Qe}from"node:path";import{DeleteObjectCommand as Re,GetObjectCommand as Me,HeadObjectCommand as Ae,ListObjectsV2Command as Ee,PutObjectCommand as Ie,S3Client as Te}from"@aws-sdk/client-s3";import{getSignedUrl as Oe}from"@aws-sdk/s3-request-presigner";var O=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new Te({region:"us-east-1",endpoint:`https://s3.hf.co/${e.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,r="application/octet-stream"){let n=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new Ie({Bucket:this.bucket,Key:e,Body:n,ContentType:r}))}async get(e){let t=await Oe(this.client,new Me({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(t,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`HF download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new Ae({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new Re({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let n=await this.client.send(new Ee({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let i of n.Contents??[])i.Key&&t.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});r=n.IsTruncated?n.NextContinuationToken:void 0}while(r);return t}};import{access as W,mkdir as Ne,readFile as $e,readdir as _e,rm as Ke,stat as Q,writeFile as De}from"node:fs/promises";import{dirname as Le,join as Fe,relative as Z,resolve as Be}from"node:path";var M=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return Be(this.root,t)}async put(e,t){let r=this.path(e);await Ne(Le(r),{recursive:!0}),await De(r,t)}async get(e){try{return await $e(this.path(e))}catch(t){if(typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT")return null;throw t}}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await W(this.path(e)),!0}catch{return!1}}async delete(e){await Ke(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),r=[];try{await W(t)}catch{return r}let n=async o=>{let u=await _e(o,{withFileTypes:!0});for(let d of u){let c=Fe(o,d.name);if(d.isDirectory()){await n(c);continue}let l=await Q(c);r.push({key:Z(this.root,c),size:l.size,updatedAt:l.mtime.toISOString()})}},i=await Q(t);return i.isDirectory()?await n(t):r.push({key:Z(this.root,t),size:i.size,updatedAt:i.mtime.toISOString()}),r}};import{DeleteObjectCommand as ze,GetObjectCommand as Ve,HeadObjectCommand as Ge,ListObjectsV2Command as Ue,PutObjectCommand as He,S3Client as Je}from"@aws-sdk/client-s3";import{getSignedUrl as Ye}from"@aws-sdk/s3-request-presigner";var A=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new Je({region:e.region??"us-east-1",endpoint:e.endpoint||void 0,forcePathStyle:e.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,r="application/octet-stream"){let n=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new He({Bucket:this.bucket,Key:e,Body:n,ContentType:r}))}async get(e){let t=await Ye(this.client,new Ve({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(t,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`${this.name} download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new Ge({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new ze({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let n=await this.client.send(new Ue({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let i of n.Contents??[])i.Key&&t.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});r=n.IsTruncated?n.NextContinuationToken:void 0}while(r);return t}};function B(s,e){return console.warn(e),new M(s)}function q(s){let e=s.localRoot??Qe(We(),".toolnet-memory","storage");if(s.provider==="r2"){let t=s.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new A({name:"r2",endpoint:`https://${t.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:t.bucket,forcePathStyle:!0,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):B(e,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(s.provider==="s3"){let t=s.s3;return t?.bucket&&t.accessKeyId&&t.secretAccessKey?new A({name:"s3",endpoint:t.endpoint,region:t.region??"us-east-1",bucket:t.bucket,forcePathStyle:t.forcePathStyle??!1,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):B(e,"[storage] S3 credentials missing. Using local fallback.")}if(s.provider==="huggingface"){let t=s.huggingface;return t?.namespace&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new O({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):B(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new M(e)}var N=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"graph","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));return t?JSON.parse(t):null}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};var $=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"graph","manifest.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));return t?JSON.parse(t):null}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};function X(s){let e=s.trim().replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,"").slice(0,100);if(!e||e==="."||e==="..")throw new Error("Invalid project storage folder");return e}function ee(s){let e=s.replace(/^\/+/,"");if(e.startsWith("memories/"))return"memory/records/"+e.slice(9);if(e.startsWith("vectors/"))return"memory/vectors/"+e.slice(8);if(e.startsWith("graph/"))return"code/graph/"+e.slice(6);let t=e.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(t)return`${t[1]}memory/records/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),t)return`${t[1]}memory/vectors/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),t)return`${t[1]}code/graph/${t[2]}`;let r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(r)return`${r[1]}memory/records/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),r)return`${r[1]}memory/vectors/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),r)return`${r[1]}code/graph/${r[2]}`;let n=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return n?`${n[1]}memory/records/${n[2]}`:(n=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),n?`${n[1]}memory/vectors/${n[2]}`:(n=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),n?`${n[1]}code/graph/${n[2]}`:e))}var _=class{constructor(e,t,r,n){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=r,this.folder=X(n??r),this.sourcePrefix=`projects/${t}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let e=`${this.targetPrefix}/project.json`,t=new Date().toISOString(),r=t,n=await this.provider.getText(e);if(n){let o;try{o=JSON.parse(n)}catch(u){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${u instanceof Error?u.message:String(u)}`)}if(typeof o.id=="string"&&o.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${o.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof o.createdAt=="string"&&(r=o.createdAt)}let i={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:r,updatedAt:t};await this.provider.put(e,JSON.stringify(i,null,2)+`
4
+ `,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=ee(e),e===this.sourcePrefix)return this.targetPrefix;if(e.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+e.slice(this.sourcePrefix.length);if(e===this.targetPrefix||e.startsWith(`${this.targetPrefix}/`))return e;if(e.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${e}`].join(" "));return e}async put(e,t,r){return await this.ensureRegistered(),this.provider.put(this.key(e),t,r)}async get(e){return await this.ensureRegistered(),this.provider.get(this.key(e))}async getText(e){return await this.ensureRegistered(),this.provider.getText(this.key(e))}async delete(e){return await this.ensureRegistered(),this.provider.delete(this.key(e))}async exists(e){return await this.ensureRegistered(),this.provider.exists(this.key(e))}async list(e){return await this.ensureRegistered(),this.provider.list(this.key(e))}};import{join as ct}from"node:path";import{readdir as et}from"node:fs/promises";import{extname as tt,join as rt,relative as st}from"node:path";import{basename as Ze}from"node:path";var qe=new Set([".env",".env.local",".env.production",".env.development","id_rsa","id_ed25519","credentials","credentials.json","service-account.json"]),Xe=new Set([".pem",".key",".p12",".pfx"]);function te(s){let e=Ze(s).toLowerCase();if(qe.has(e))return!0;for(let t of Xe)if(e.endsWith(t))return!0;return!1}var nt=new Set([".ts",".tsx",".js",".jsx",".mjs",".cjs",".mts",".cts"]),ot=new Set(["node_modules",".git","dist","build","coverage",".next",".cache",".toolnet-memory"]);async function re(s){let e=[];async function t(r){let n=await et(r,{withFileTypes:!0});for(let i of n){if(ot.has(i.name))continue;let o=rt(r,i.name);if(i.isDirectory()){await t(o);continue}nt.has(tt(i.name))&&(te(o)||e.push(st(s,o)))}}return await t(s),e.sort()}import{createHash as it}from"node:crypto";import{readFile as at}from"node:fs/promises";async function se(s){let e=await at(s);return it("sha256").update(e).digest("hex")}async function ne(s,e){let t=await re(e),r={};for(let n of t)r[n]={path:n,hash:await se(ct(e,n))};return{version:1,projectId:s,updatedAt:new Date().toISOString(),files:r}}function oe(s,e){let t=s?.files??{},r=e.files,n=[],i=[],o=[],u=[];for(let[d,c]of Object.entries(r)){let l=t[d];l?l.hash!==c.hash?i.push(d):u.push(d):n.push(d)}for(let d of Object.keys(t))r[d]||o.push(d);return{added:n.sort(),modified:i.sort(),deleted:o.sort(),unchanged:u.sort()}}import{readFile as dt}from"node:fs/promises";import{join as pt}from"node:path";import{createHash as lt}from"node:crypto";import*as p from"typescript";function ie(s,e){return lt("sha256").update(`${s}:${e}`).digest("hex").slice(0,24)}function z(s,e){return s.getLineAndCharacterOfPosition(e.getStart(s)).line+1}async function ae(s,e,t){let r=pt(e,t),n=await dt(r,"utf8"),i=p.createSourceFile(t,n,p.ScriptTarget.Latest,!0,t.endsWith(".tsx")||t.endsWith(".jsx")?p.ScriptKind.TSX:p.ScriptKind.TS),o=[],u=[],d=[],c=[],l={id:ie(s,`file:${t}`),projectId:s,name:t,qualifiedName:t,type:"file",filePath:t,startLine:1,endLine:i.getLineAndCharacterOfPosition(i.end).line+1};o.push(l);let m=[],b=[];function y(a,S,h,f){let x=ie(s,`${t}:${h}:${f??S}:${a.pos}`);return o.push({id:x,projectId:s,name:S,qualifiedName:f??S,type:h,filePath:t,startLine:z(i,a),endLine:i.getLineAndCharacterOfPosition(a.end).line+1}),x}function j(a){if(!p.isStringLiteral(a.moduleSpecifier))return;let S=[],h=a.importClause;if(h?.name&&S.push({localName:h.name.text,importedName:"default",kind:"default"}),h?.namedBindings&&p.isNamedImports(h.namedBindings))for(let f of h.namedBindings.elements)S.push({localName:f.name.text,importedName:f.propertyName?.text??f.name.text,kind:"named"});h?.namedBindings&&p.isNamespaceImport(h.namedBindings)&&S.push({localName:h.namedBindings.name.text,importedName:"*",kind:"namespace"}),u.push({source:a.moduleSpecifier.text,bindings:S})}function v(a,S){for(let h of a.heritageClauses??[])for(let f of h.types){let x=f.expression.getText(i).split(".").at(-1)??"";x&&c.push({fromId:S,targetName:x,type:h.token===p.SyntaxKind.ExtendsKeyword?"INHERITS":"IMPLEMENTS"})}}function g(a){let S=!1,h=!1;if(p.isImportDeclaration(a)&&j(a),p.isInterfaceDeclaration(a)&&y(a,a.name.text,"interface",a.name.text),p.isClassDeclaration(a)&&a.name){let f=y(a,a.name.text,"class",a.name.text);v(a,f),b.push({id:f,name:a.name.text}),h=!0}if(p.isFunctionDeclaration(a)&&a.name){let f=y(a,a.name.text,"function",a.name.text);m.push(f),S=!0}if(p.isMethodDeclaration(a)&&a.name){let f=a.name.getText(i),x=b.at(-1)?.name,ce=y(a,f,"method",x?`${x}.${f}`:f);m.push(ce),S=!0}if(p.isCallExpression(a)&&(p.isIdentifier(a.expression)&&d.push({callerId:m.at(-1),calleeName:a.expression.text,line:z(i,a)}),p.isPropertyAccessExpression(a.expression))){let f=a.expression;d.push({callerId:m.at(-1),qualifier:f.expression.getText(i),calleeName:f.name.text,line:z(i,a)})}p.forEachChild(a,g),S&&m.pop(),h&&b.pop()}return g(i),{filePath:t,symbols:o,imports:u,calls:d,heritage:c}}import{createHash as ut}from"node:crypto";import{dirname as mt,extname as gt,normalize as ft,posix as yt}from"node:path";var k=class{symbols=new Map;edges=new Map;addSymbol(e){this.symbols.set(e.id,e)}addEdge(e){this.edges.set(e.id,e)}getSymbol(e){return this.symbols.get(e)}findByName(e,t){return[...this.symbols.values()].filter(r=>r.projectId===e&&(r.name===t||r.qualifiedName===t))}allSymbols(e){return[...this.symbols.values()].filter(t=>t.projectId===e)}allEdges(e){return[...this.edges.values()].filter(t=>t.projectId===e)}import(e,t){for(let r of e)this.addSymbol(r);for(let r of t)this.addEdge(r)}clearProject(e){for(let[t,r]of this.symbols)r.projectId===e&&this.symbols.delete(t);for(let[t,r]of this.edges)r.projectId===e&&this.edges.delete(t)}};function ht(s,e,t,r){return ut("sha256").update(`${s}:${e}:${t}:${r}`).digest("hex").slice(0,24)}function w(s){return ft(s).replaceAll("\\","/")}function V(s,e,t){if(!e.startsWith("."))return;let r=w(yt.join(mt(s),e)),n=gt(r),i;if(n===".js"){let o=r.slice(0,-3);i=[`${o}.ts`,`${o}.tsx`,`${o}.js`,`${o}.jsx`]}else if(n===".mjs"){let o=r.slice(0,-4);i=[`${o}.mts`,`${o}.ts`,`${o}.mjs`]}else if(n===".cjs"){let o=r.slice(0,-4);i=[`${o}.cts`,`${o}.ts`,`${o}.cjs`]}else n?i=[r]:i=[`${r}.ts`,`${r}.tsx`,`${r}.mts`,`${r}.cts`,`${r}.js`,`${r}.jsx`,`${r}.mjs`,`${r}.cjs`,`${r}/index.ts`,`${r}/index.tsx`,`${r}/index.js`,`${r}/index.jsx`];return i.find(o=>t.has(w(o)))}var K=class{build(e,t){let r=new k,n=new Map;for(let o of t)for(let u of o.symbols)r.addSymbol(u),u.type==="file"&&n.set(w(o.filePath),u);let i=new Set(n.keys());for(let o of t){let u=w(o.filePath),d=n.get(u);if(d){for(let c of o.symbols)c.id!==d.id&&r.addEdge(this.edge(e,d.id,"DEFINES",c.id));for(let c of o.imports){let l=V(u,c.source,i);if(!l)continue;let m=n.get(l);m&&r.addEdge(this.edge(e,d.id,"IMPORTS",m.id,{source:c.source}))}for(let c of o.heritage){let l=r.findByName(e,c.targetName).filter(m=>m.type==="class"||m.type==="interface");for(let m of l)r.addEdge(this.edge(e,c.fromId,c.type,m.id))}for(let c of o.calls){if(!c.callerId)continue;let l=this.resolveCallTargets(e,o,c.calleeName,c.qualifier,t,r,i);for(let m of l)m.id!==c.callerId&&r.addEdge(this.edge(e,c.callerId,"CALLS",m.id,{line:c.line,qualifier:c.qualifier}))}}}return r}resolveCallTargets(e,t,r,n,i,o,u){let d=w(t.filePath),c=o.findByName(e,r).filter(l=>w(l.filePath)===d);if(!n&&c.length)return c;if(n)for(let l of t.imports){if(!l.bindings.find(y=>y.kind==="namespace"&&y.localName===n))continue;let b=V(d,l.source,u);if(b)return o.findByName(e,r).filter(y=>w(y.filePath)===b)}for(let l of t.imports){let m=l.bindings.find(v=>v.localName===r);if(!m)continue;let b=V(d,l.source,u);if(!b)continue;let y=m.importedName==="default"?r:m.importedName,j=o.findByName(e,y).filter(v=>w(v.filePath)===b);if(j.length)return j}return o.findByName(e,r).filter(l=>l.type==="function"||l.type==="method")}edge(e,t,r,n,i){return{id:ht(e,t,r,n),projectId:e,from:t,to:n,type:r,metadata:i}}};var D=class{constructor(e){this.storage=e}storage;async index(e,t){let r=new $(this.storage),n=new N(this.storage),i=await r.load(e),o=await n.load(e),u=await ne(e,t),d=oe(i,u),c=[...d.added,...d.modified];if(i&&o&&c.length===0&&d.deleted.length===0)return{firstRun:!1,scanned:Object.keys(u.files).length,parsed:0,added:0,modified:0,deleted:0,unchanged:d.unchanged.length,symbols:o.symbols.length,edges:o.edges.length,saved:!1};let l=o?.symbols.filter(g=>d.unchanged.includes(g.filePath))??[],m=[];for(let g of c)m.push(await ae(e,t,g));let b=new K().build(e,m),y=new k;for(let g of l)y.addSymbol(g);for(let g of b.allSymbols(e))y.addSymbol(g);let j=new Set(y.allSymbols(e).map(g=>g.id));for(let g of o?.edges??[])j.has(g.from)&&j.has(g.to)&&!c.includes(o.symbols.find(a=>a.id===g.from)?.filePath??"")&&!c.includes(o.symbols.find(a=>a.id===g.to)?.filePath??"")&&y.addEdge(g);for(let g of b.allEdges(e))y.addEdge(g);let v={version:1,projectId:e,updatedAt:new Date().toISOString(),files:Object.keys(u.files).length,symbols:y.allSymbols(e),edges:y.allEdges(e)};return await n.save(v),await r.save(u),{firstRun:i===null,scanned:Object.keys(u.files).length,parsed:c.length,added:d.added.length,modified:d.modified.length,deleted:d.deleted.length,unchanged:d.unchanged.length,symbols:v.symbols.length,edges:v.edges.length,saved:!0}}};async function St(){let s=G(),e=new I().detect(),t=q({provider:s.storage.provider,huggingface:s.storage.huggingface,localRoot:s.storage.localRoot}),r=new _(t,e.id,e.name,e.remote??e.name),n=await new D(r).index(e.id,e.rootPath);console.log({ok:!0,storage:r.name,...n})}St().catch(s=>{console.error(s),process.exit(1)});