@nanonets/graft 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +228 -0
  3. package/dist/ai/crux.d.ts +31 -0
  4. package/dist/ai/crux.d.ts.map +1 -0
  5. package/dist/ai/crux.js +91 -0
  6. package/dist/ai/crux.js.map +1 -0
  7. package/dist/ai/providers.d.ts +37 -0
  8. package/dist/ai/providers.d.ts.map +1 -0
  9. package/dist/ai/providers.js +17 -0
  10. package/dist/ai/providers.js.map +1 -0
  11. package/dist/ai/summarize.d.ts +25 -0
  12. package/dist/ai/summarize.d.ts.map +1 -0
  13. package/dist/ai/summarize.js +41 -0
  14. package/dist/ai/summarize.js.map +1 -0
  15. package/dist/ai/synthesize.d.ts +32 -0
  16. package/dist/ai/synthesize.d.ts.map +1 -0
  17. package/dist/ai/synthesize.js +132 -0
  18. package/dist/ai/synthesize.js.map +1 -0
  19. package/dist/cli.d.ts +13 -0
  20. package/dist/cli.d.ts.map +1 -0
  21. package/dist/cli.js +131 -0
  22. package/dist/cli.js.map +1 -0
  23. package/dist/context/build.d.ts +33 -0
  24. package/dist/context/build.d.ts.map +1 -0
  25. package/dist/context/build.js +265 -0
  26. package/dist/context/build.js.map +1 -0
  27. package/dist/context/check.d.ts +21 -0
  28. package/dist/context/check.d.ts.map +1 -0
  29. package/dist/context/check.js +127 -0
  30. package/dist/context/check.js.map +1 -0
  31. package/dist/context/node-file.d.ts +80 -0
  32. package/dist/context/node-file.d.ts.map +1 -0
  33. package/dist/context/node-file.js +165 -0
  34. package/dist/context/node-file.js.map +1 -0
  35. package/dist/engine.d.ts +58 -0
  36. package/dist/engine.d.ts.map +1 -0
  37. package/dist/engine.js +87 -0
  38. package/dist/engine.js.map +1 -0
  39. package/dist/graph/build.d.ts +33 -0
  40. package/dist/graph/build.d.ts.map +1 -0
  41. package/dist/graph/build.js +95 -0
  42. package/dist/graph/build.js.map +1 -0
  43. package/dist/graph/check.d.ts +18 -0
  44. package/dist/graph/check.d.ts.map +1 -0
  45. package/dist/graph/check.js +126 -0
  46. package/dist/graph/check.js.map +1 -0
  47. package/dist/graph/enrich.d.ts +42 -0
  48. package/dist/graph/enrich.d.ts.map +1 -0
  49. package/dist/graph/enrich.js +141 -0
  50. package/dist/graph/enrich.js.map +1 -0
  51. package/dist/graph/extract.d.ts +23 -0
  52. package/dist/graph/extract.d.ts.map +1 -0
  53. package/dist/graph/extract.js +249 -0
  54. package/dist/graph/extract.js.map +1 -0
  55. package/dist/graph/resolve.d.ts +4 -0
  56. package/dist/graph/resolve.d.ts.map +1 -0
  57. package/dist/graph/resolve.js +0 -0
  58. package/dist/graph/resolve.js.map +1 -0
  59. package/dist/graph/types.d.ts +56 -0
  60. package/dist/graph/types.d.ts.map +1 -0
  61. package/dist/graph/types.js +14 -0
  62. package/dist/graph/types.js.map +1 -0
  63. package/dist/graph/write.d.ts +9 -0
  64. package/dist/graph/write.d.ts.map +1 -0
  65. package/dist/graph/write.js +39 -0
  66. package/dist/graph/write.js.map +1 -0
  67. package/dist/index.d.ts +33 -0
  68. package/dist/index.d.ts.map +1 -0
  69. package/dist/index.js +26 -0
  70. package/dist/index.js.map +1 -0
  71. package/dist/ingest/fs.d.ts +10 -0
  72. package/dist/ingest/fs.d.ts.map +1 -0
  73. package/dist/ingest/fs.js +48 -0
  74. package/dist/ingest/fs.js.map +1 -0
  75. package/dist/util/id.d.ts +7 -0
  76. package/dist/util/id.d.ts.map +1 -0
  77. package/dist/util/id.js +14 -0
  78. package/dist/util/id.js.map +1 -0
  79. package/dist/viewer/app.js +1 -0
  80. package/dist/viewer/index.html +48 -0
  81. package/dist/viewer/style.css +119 -0
  82. package/dist/viz/assemble.d.ts +33 -0
  83. package/dist/viz/assemble.d.ts.map +1 -0
  84. package/dist/viz/assemble.js +101 -0
  85. package/dist/viz/assemble.js.map +1 -0
  86. package/dist/viz/serve.d.ts +12 -0
  87. package/dist/viz/serve.d.ts.map +1 -0
  88. package/dist/viz/serve.js +128 -0
  89. package/dist/viz/serve.js.map +1 -0
  90. package/package.json +72 -0
@@ -0,0 +1 @@
1
+ var se={part_of:"structure",contains:"structure",uses:"dependency",depends_on:"dependency",calls:"dependency",imports:"dependency",produces:"dependency",configures:"dependency",validates:"dependency",extends:"contract",implements:"contract",references:"association"};function Y(t){return se[t]??"association"}var kt={structure:85,dependency:145,contract:150,association:185};function J(t){return Y(t)==="structure"?"part of":t==="calls"||t==="uses"||t==="depends_on"||t==="imports"?"uses":t.replace(/_/g," ")}var bt={"part of":"where does this live? (contains, part of)",uses:"what breaks if the target changes? (calls, uses, depends on, imports)",produces:"where does this output come from?",configures:"what changes its behavior without a code change?",validates:"what checks or judges this? (tests, drift checks, scoring)",extends:"what contract must this honor? (inheritance)",implements:"what contract must this honor? (interface)",references:"mentioned but never called \u2014 possible dead coupling"},ae={system:"--sys",concept:"--con",file:"--fil",api:"--api"},le={file:"--k-file",class:"--k-class",function:"--k-fn",method:"--k-method",interface:"--k-iface",type:"--k-type",enum:"--k-enum"};function D(t,e){return(t==="code"?le:ae)[e]??"--edge"}function M(t){return getComputedStyle(document.documentElement).getPropertyValue(t).trim()}async function _t(){return await(await fetch("/api/context-graph")).json()}async function Et(){let t=await fetch("/api/code-graph");if(!t.ok)return null;let e=await t.json(),n=e.nodes.map(o=>({id:o.id,name:o.name,type:o.kind,summary:o.summary??o.signature??"",sources:[`${o.path} \xB7 ${o.span}`]})),i=new Set(n.map(o=>o.id)),r=e.edges.filter(o=>i.has(o.source)&&i.has(o.target)).map(o=>({source:o.source,target:o.target,relation:o.relation,confidence:o.confidence}));return{meta:{nodeCount:n.length,edgeCount:r.length},nodes:n,edges:r}}function Mt(t){let e=new EventSource("/events");e.onmessage=n=>{n.data==="change"&&t()}}function ot(t,e){var n,i=1;t==null&&(t=0),e==null&&(e=0);function r(){var o,a=n.length,p,l=0,s=0;for(o=0;o<a;++o)p=n[o],l+=p.x,s+=p.y;for(l=(l/a-t)*i,s=(s/a-e)*i,o=0;o<a;++o)p=n[o],p.x-=l,p.y-=s}return r.initialize=function(o){n=o},r.x=function(o){return arguments.length?(t=+o,r):t},r.y=function(o){return arguments.length?(e=+o,r):e},r.strength=function(o){return arguments.length?(i=+o,r):i},r}function Ct(t){let e=+this._x.call(null,t),n=+this._y.call(null,t);return Nt(this.cover(e,n),e,n,t)}function Nt(t,e,n,i){if(isNaN(e)||isNaN(n))return t;var r,o=t._root,a={data:i},p=t._x0,l=t._y0,s=t._x1,c=t._y1,m,u,f,v,h,d,g,y;if(!o)return t._root=a,t;for(;o.length;)if((h=e>=(m=(p+s)/2))?p=m:s=m,(d=n>=(u=(l+c)/2))?l=u:c=u,r=o,!(o=o[g=d<<1|h]))return r[g]=a,t;if(f=+t._x.call(null,o.data),v=+t._y.call(null,o.data),e===f&&n===v)return a.next=o,r?r[g]=a:t._root=a,t;do r=r?r[g]=new Array(4):t._root=new Array(4),(h=e>=(m=(p+s)/2))?p=m:s=m,(d=n>=(u=(l+c)/2))?l=u:c=u;while((g=d<<1|h)===(y=(v>=u)<<1|f>=m));return r[y]=o,r[g]=a,t}function St(t){var e,n,i=t.length,r,o,a=new Array(i),p=new Array(i),l=1/0,s=1/0,c=-1/0,m=-1/0;for(n=0;n<i;++n)isNaN(r=+this._x.call(null,e=t[n]))||isNaN(o=+this._y.call(null,e))||(a[n]=r,p[n]=o,r<l&&(l=r),r>c&&(c=r),o<s&&(s=o),o>m&&(m=o));if(l>c||s>m)return this;for(this.cover(l,s).cover(c,m),n=0;n<i;++n)Nt(this,a[n],p[n],t[n]);return this}function Lt(t,e){if(isNaN(t=+t)||isNaN(e=+e))return this;var n=this._x0,i=this._y0,r=this._x1,o=this._y1;if(isNaN(n))r=(n=Math.floor(t))+1,o=(i=Math.floor(e))+1;else{for(var a=r-n||1,p=this._root,l,s;n>t||t>=r||i>e||e>=o;)switch(s=(e<i)<<1|t<n,l=new Array(4),l[s]=p,p=l,a*=2,s){case 0:r=n+a,o=i+a;break;case 1:n=r-a,o=i+a;break;case 2:r=n+a,i=o-a;break;case 3:n=r-a,i=o-a;break}this._root&&this._root.length&&(this._root=p)}return this._x0=n,this._y0=i,this._x1=r,this._y1=o,this}function Tt(){var t=[];return this.visit(function(e){if(!e.length)do t.push(e.data);while(e=e.next)}),t}function At(t){return arguments.length?this.cover(+t[0][0],+t[0][1]).cover(+t[1][0],+t[1][1]):isNaN(this._x0)?void 0:[[this._x0,this._y0],[this._x1,this._y1]]}function S(t,e,n,i,r){this.node=t,this.x0=e,this.y0=n,this.x1=i,this.y1=r}function Vt(t,e,n){var i,r=this._x0,o=this._y0,a,p,l,s,c=this._x1,m=this._y1,u=[],f=this._root,v,h;for(f&&u.push(new S(f,r,o,c,m)),n==null?n=1/0:(r=t-n,o=e-n,c=t+n,m=e+n,n*=n);v=u.pop();)if(!(!(f=v.node)||(a=v.x0)>c||(p=v.y0)>m||(l=v.x1)<r||(s=v.y1)<o))if(f.length){var d=(a+l)/2,g=(p+s)/2;u.push(new S(f[3],d,g,l,s),new S(f[2],a,g,d,s),new S(f[1],d,p,l,g),new S(f[0],a,p,d,g)),(h=(e>=g)<<1|t>=d)&&(v=u[u.length-1],u[u.length-1]=u[u.length-1-h],u[u.length-1-h]=v)}else{var y=t-+this._x.call(null,f.data),w=e-+this._y.call(null,f.data),x=y*y+w*w;if(x<n){var _=Math.sqrt(n=x);r=t-_,o=e-_,c=t+_,m=e+_,i=f.data}}return i}function $t(t){if(isNaN(c=+this._x.call(null,t))||isNaN(m=+this._y.call(null,t)))return this;var e,n=this._root,i,r,o,a=this._x0,p=this._y0,l=this._x1,s=this._y1,c,m,u,f,v,h,d,g;if(!n)return this;if(n.length)for(;;){if((v=c>=(u=(a+l)/2))?a=u:l=u,(h=m>=(f=(p+s)/2))?p=f:s=f,e=n,!(n=n[d=h<<1|v]))return this;if(!n.length)break;(e[d+1&3]||e[d+2&3]||e[d+3&3])&&(i=e,g=d)}for(;n.data!==t;)if(r=n,!(n=n.next))return this;return(o=n.next)&&delete n.next,r?(o?r.next=o:delete r.next,this):e?(o?e[d]=o:delete e[d],(n=e[0]||e[1]||e[2]||e[3])&&n===(e[3]||e[2]||e[1]||e[0])&&!n.length&&(i?i[g]=n:this._root=n),this):(this._root=o,this)}function zt(t){for(var e=0,n=t.length;e<n;++e)this.remove(t[e]);return this}function Gt(){return this._root}function Ht(){var t=0;return this.visit(function(e){if(!e.length)do++t;while(e=e.next)}),t}function It(t){var e=[],n,i=this._root,r,o,a,p,l;for(i&&e.push(new S(i,this._x0,this._y0,this._x1,this._y1));n=e.pop();)if(!t(i=n.node,o=n.x0,a=n.y0,p=n.x1,l=n.y1)&&i.length){var s=(o+p)/2,c=(a+l)/2;(r=i[3])&&e.push(new S(r,s,c,p,l)),(r=i[2])&&e.push(new S(r,o,c,s,l)),(r=i[1])&&e.push(new S(r,s,a,p,c)),(r=i[0])&&e.push(new S(r,o,a,s,c))}return this}function Rt(t){var e=[],n=[],i;for(this._root&&e.push(new S(this._root,this._x0,this._y0,this._x1,this._y1));i=e.pop();){var r=i.node;if(r.length){var o,a=i.x0,p=i.y0,l=i.x1,s=i.y1,c=(a+l)/2,m=(p+s)/2;(o=r[0])&&e.push(new S(o,a,p,c,m)),(o=r[1])&&e.push(new S(o,c,p,l,m)),(o=r[2])&&e.push(new S(o,a,m,c,s)),(o=r[3])&&e.push(new S(o,c,m,l,s))}n.push(i)}for(;i=n.pop();)t(i.node,i.x0,i.y0,i.x1,i.y1);return this}function Dt(t){return t[0]}function Ot(t){return arguments.length?(this._x=t,this):this._x}function Bt(t){return t[1]}function jt(t){return arguments.length?(this._y=t,this):this._y}function B(t,e,n){var i=new st(e??Dt,n??Bt,NaN,NaN,NaN,NaN);return t==null?i:i.addAll(t)}function st(t,e,n,i,r,o){this._x=t,this._y=e,this._x0=n,this._y0=i,this._x1=r,this._y1=o,this._root=void 0}function Pt(t){for(var e={data:t.data},n=e;t=t.next;)n=n.next={data:t.data};return e}var A=B.prototype=st.prototype;A.copy=function(){var t=new st(this._x,this._y,this._x0,this._y0,this._x1,this._y1),e=this._root,n,i;if(!e)return t;if(!e.length)return t._root=Pt(e),t;for(n=[{source:e,target:t._root=new Array(4)}];e=n.pop();)for(var r=0;r<4;++r)(i=e.source[r])&&(i.length?n.push({source:i,target:e.target[r]=new Array(4)}):e.target[r]=Pt(i));return t};A.add=Ct;A.addAll=St;A.cover=Lt;A.data=Tt;A.extent=At;A.find=Vt;A.remove=$t;A.removeAll=zt;A.root=Gt;A.size=Ht;A.visit=It;A.visitAfter=Rt;A.x=Ot;A.y=jt;function G(t){return function(){return t}}function z(t){return(t()-.5)*1e-6}function ce(t){return t.x+t.vx}function ue(t){return t.y+t.vy}function at(t){var e,n,i,r=1,o=1;typeof t!="function"&&(t=G(t==null?1:+t));function a(){for(var s,c=e.length,m,u,f,v,h,d,g=0;g<o;++g)for(m=B(e,ce,ue).visitAfter(p),s=0;s<c;++s)u=e[s],h=n[u.index],d=h*h,f=u.x+u.vx,v=u.y+u.vy,m.visit(y);function y(w,x,_,T,V){var C=w.data,$=w.r,N=h+$;if(C){if(C.index>u.index){var I=f-C.x-C.vx,R=v-C.y-C.vy,H=I*I+R*R;H<N*N&&(I===0&&(I=z(i),H+=I*I),R===0&&(R=z(i),H+=R*R),H=(N-(H=Math.sqrt(H)))/H*r,u.vx+=(I*=H)*(N=($*=$)/(d+$)),u.vy+=(R*=H)*N,C.vx-=I*(N=1-N),C.vy-=R*N)}return}return x>f+N||T<f-N||_>v+N||V<v-N}}function p(s){if(s.data)return s.r=n[s.data.index];for(var c=s.r=0;c<4;++c)s[c]&&s[c].r>s.r&&(s.r=s[c].r)}function l(){if(e){var s,c=e.length,m;for(n=new Array(c),s=0;s<c;++s)m=e[s],n[m.index]=+t(m,s,e)}}return a.initialize=function(s,c){e=s,i=c,l()},a.iterations=function(s){return arguments.length?(o=+s,a):o},a.strength=function(s){return arguments.length?(r=+s,a):r},a.radius=function(s){return arguments.length?(t=typeof s=="function"?s:G(+s),l(),a):t},a}function de(t){return t.index}function Yt(t,e){var n=t.get(e);if(!n)throw new Error("node not found: "+e);return n}function lt(t){var e=de,n=m,i,r=G(30),o,a,p,l,s,c=1;t==null&&(t=[]);function m(d){return 1/Math.min(p[d.source.index],p[d.target.index])}function u(d){for(var g=0,y=t.length;g<c;++g)for(var w=0,x,_,T,V,C,$,N;w<y;++w)x=t[w],_=x.source,T=x.target,V=T.x+T.vx-_.x-_.vx||z(s),C=T.y+T.vy-_.y-_.vy||z(s),$=Math.sqrt(V*V+C*C),$=($-o[w])/$*d*i[w],V*=$,C*=$,T.vx-=V*(N=l[w]),T.vy-=C*N,_.vx+=V*(N=1-N),_.vy+=C*N}function f(){if(a){var d,g=a.length,y=t.length,w=new Map(a.map((_,T)=>[e(_,T,a),_])),x;for(d=0,p=new Array(g);d<y;++d)x=t[d],x.index=d,typeof x.source!="object"&&(x.source=Yt(w,x.source)),typeof x.target!="object"&&(x.target=Yt(w,x.target)),p[x.source.index]=(p[x.source.index]||0)+1,p[x.target.index]=(p[x.target.index]||0)+1;for(d=0,l=new Array(y);d<y;++d)x=t[d],l[d]=p[x.source.index]/(p[x.source.index]+p[x.target.index]);i=new Array(y),v(),o=new Array(y),h()}}function v(){if(a)for(var d=0,g=t.length;d<g;++d)i[d]=+n(t[d],d,t)}function h(){if(a)for(var d=0,g=t.length;d<g;++d)o[d]=+r(t[d],d,t)}return u.initialize=function(d,g){a=d,s=g,f()},u.links=function(d){return arguments.length?(t=d,f(),u):t},u.id=function(d){return arguments.length?(e=d,u):e},u.iterations=function(d){return arguments.length?(c=+d,u):c},u.strength=function(d){return arguments.length?(n=typeof d=="function"?d:G(+d),v(),u):n},u.distance=function(d){return arguments.length?(r=typeof d=="function"?d:G(+d),h(),u):r},u}var fe={value:()=>{}};function qt(){for(var t=0,e=arguments.length,n={},i;t<e;++t){if(!(i=arguments[t]+"")||i in n||/[\s.]/.test(i))throw new Error("illegal type: "+i);n[i]=[]}return new U(n)}function U(t){this._=t}function he(t,e){return t.trim().split(/^|\s+/).map(function(n){var i="",r=n.indexOf(".");if(r>=0&&(i=n.slice(r+1),n=n.slice(0,r)),n&&!e.hasOwnProperty(n))throw new Error("unknown type: "+n);return{type:n,name:i}})}U.prototype=qt.prototype={constructor:U,on:function(t,e){var n=this._,i=he(t+"",n),r,o=-1,a=i.length;if(arguments.length<2){for(;++o<a;)if((r=(t=i[o]).type)&&(r=pe(n[r],t.name)))return r;return}if(e!=null&&typeof e!="function")throw new Error("invalid callback: "+e);for(;++o<a;)if(r=(t=i[o]).type)n[r]=Ft(n[r],t.name,e);else if(e==null)for(r in n)n[r]=Ft(n[r],t.name,null);return this},copy:function(){var t={},e=this._;for(var n in e)t[n]=e[n].slice();return new U(t)},call:function(t,e){if((r=arguments.length-2)>0)for(var n=new Array(r),i=0,r,o;i<r;++i)n[i]=arguments[i+2];if(!this._.hasOwnProperty(t))throw new Error("unknown type: "+t);for(o=this._[t],i=0,r=o.length;i<r;++i)o[i].value.apply(e,n)},apply:function(t,e,n){if(!this._.hasOwnProperty(t))throw new Error("unknown type: "+t);for(var i=this._[t],r=0,o=i.length;r<o;++r)i[r].value.apply(e,n)}};function pe(t,e){for(var n=0,i=t.length,r;n<i;++n)if((r=t[n]).name===e)return r.value}function Ft(t,e,n){for(var i=0,r=t.length;i<r;++i)if(t[i].name===e){t[i]=fe,t=t.slice(0,i).concat(t.slice(i+1));break}return n!=null&&t.push({name:e,value:n}),t}var ct=qt;var F=0,X=0,W=0,Xt=1e3,tt,K,et=0,j=0,nt=0,Q=typeof performance=="object"&&performance.now?performance:Date,Kt=typeof window=="object"&&window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(t){setTimeout(t,17)};function ft(){return j||(Kt(ge),j=Q.now()+nt)}function ge(){j=0}function ut(){this._call=this._time=this._next=null}ut.prototype=rt.prototype={constructor:ut,restart:function(t,e,n){if(typeof t!="function")throw new TypeError("callback is not a function");n=(n==null?ft():+n)+(e==null?0:+e),!this._next&&K!==this&&(K?K._next=this:tt=this,K=this),this._call=t,this._time=n,dt()},stop:function(){this._call&&(this._call=null,this._time=1/0,dt())}};function rt(t,e,n){var i=new ut;return i.restart(t,e,n),i}function Qt(){ft(),++F;for(var t=tt,e;t;)(e=j-t._time)>=0&&t._call.call(void 0,e),t=t._next;--F}function Wt(){j=(et=Q.now())+nt,F=X=0;try{Qt()}finally{F=0,ve(),j=0}}function me(){var t=Q.now(),e=t-et;e>Xt&&(nt-=e,et=t)}function ve(){for(var t,e=tt,n,i=1/0;e;)e._call?(i>e._time&&(i=e._time),t=e,e=e._next):(n=e._next,e._next=null,e=t?t._next=n:tt=n);K=t,dt(i)}function dt(t){if(!F){X&&(X=clearTimeout(X));var e=t-j;e>24?(t<1/0&&(X=setTimeout(Wt,t-Q.now()-nt)),W&&(W=clearInterval(W))):(W||(et=Q.now(),W=setInterval(me,Xt)),F=1,Kt(Wt))}}function Zt(){let t=1;return()=>(t=(1664525*t+1013904223)%4294967296)/4294967296}function Jt(t){return t.x}function Ut(t){return t.y}var ye=10,xe=Math.PI*(3-Math.sqrt(5));function ht(t){var e,n=1,i=.001,r=1-Math.pow(i,1/300),o=0,a=.6,p=new Map,l=rt(m),s=ct("tick","end"),c=Zt();t==null&&(t=[]);function m(){u(),s.call("tick",e),n<i&&(l.stop(),s.call("end",e))}function u(h){var d,g=t.length,y;h===void 0&&(h=1);for(var w=0;w<h;++w)for(n+=(o-n)*r,p.forEach(function(x){x(n)}),d=0;d<g;++d)y=t[d],y.fx==null?y.x+=y.vx*=a:(y.x=y.fx,y.vx=0),y.fy==null?y.y+=y.vy*=a:(y.y=y.fy,y.vy=0);return e}function f(){for(var h=0,d=t.length,g;h<d;++h){if(g=t[h],g.index=h,g.fx!=null&&(g.x=g.fx),g.fy!=null&&(g.y=g.fy),isNaN(g.x)||isNaN(g.y)){var y=ye*Math.sqrt(.5+h),w=h*xe;g.x=y*Math.cos(w),g.y=y*Math.sin(w)}(isNaN(g.vx)||isNaN(g.vy))&&(g.vx=g.vy=0)}}function v(h){return h.initialize&&h.initialize(t,c),h}return f(),e={tick:u,restart:function(){return l.restart(m),e},stop:function(){return l.stop(),e},nodes:function(h){return arguments.length?(t=h,f(),p.forEach(v),e):t},alpha:function(h){return arguments.length?(n=+h,e):n},alphaMin:function(h){return arguments.length?(i=+h,e):i},alphaDecay:function(h){return arguments.length?(r=+h,e):+r},alphaTarget:function(h){return arguments.length?(o=+h,e):o},velocityDecay:function(h){return arguments.length?(a=1-h,e):1-a},randomSource:function(h){return arguments.length?(c=h,p.forEach(v),e):c},force:function(h,d){return arguments.length>1?(d==null?p.delete(h):p.set(h,v(d)),e):p.get(h)},find:function(h,d,g){var y=0,w=t.length,x,_,T,V,C;for(g==null?g=1/0:g*=g,y=0;y<w;++y)V=t[y],x=h-V.x,_=d-V.y,T=x*x+_*_,T<g&&(C=V,g=T);return C},on:function(h,d){return arguments.length>1?(s.on(h,d),e):s.on(h)}}}function pt(){var t,e,n,i,r=G(-30),o,a=1,p=1/0,l=.81;function s(f){var v,h=t.length,d=B(t,Jt,Ut).visitAfter(m);for(i=f,v=0;v<h;++v)e=t[v],d.visit(u)}function c(){if(t){var f,v=t.length,h;for(o=new Array(v),f=0;f<v;++f)h=t[f],o[h.index]=+r(h,f,t)}}function m(f){var v=0,h,d,g=0,y,w,x;if(f.length){for(y=w=x=0;x<4;++x)(h=f[x])&&(d=Math.abs(h.value))&&(v+=h.value,g+=d,y+=d*h.x,w+=d*h.y);f.x=y/g,f.y=w/g}else{h=f,h.x=h.data.x,h.y=h.data.y;do v+=o[h.data.index];while(h=h.next)}f.value=v}function u(f,v,h,d){if(!f.value)return!0;var g=f.x-e.x,y=f.y-e.y,w=d-v,x=g*g+y*y;if(w*w/l<x)return x<p&&(g===0&&(g=z(n),x+=g*g),y===0&&(y=z(n),x+=y*y),x<a&&(x=Math.sqrt(a*x)),e.vx+=g*f.value*i/x,e.vy+=y*f.value*i/x),!0;if(f.length||x>=p)return;(f.data!==e||f.next)&&(g===0&&(g=z(n),x+=g*g),y===0&&(y=z(n),x+=y*y),x<a&&(x=Math.sqrt(a*x)));do f.data!==e&&(w=o[f.data.index]*i/x,e.vx+=g*w,e.vy+=y*w);while(f=f.next)}return s.initialize=function(f,v){t=f,n=v,c()},s.strength=function(f){return arguments.length?(r=typeof f=="function"?f:G(+f),c(),s):r},s.distanceMin=function(f){return arguments.length?(a=f*f,s):Math.sqrt(a)},s.distanceMax=function(f){return arguments.length?(p=f*f,s):Math.sqrt(p)},s.theta=function(f){return arguments.length?(l=f*f,s):Math.sqrt(l)},s}var we="http://www.w3.org/2000/svg";function L(t,e){let n=document.createElementNS(we,t);for(let[i,r]of Object.entries(e))n.setAttribute(i,String(r));return n}var it=class{svg;defs;viewport;sim=null;nodes=[];edges=[];edgeEls=[];nodeEls=[];view={x:0,y:0,k:1};tab="context";selected=null;query="";hiddenRels={};hiddenTypes={};onSelect=()=>{};constructor(e){this.svg=e,this.buildChrome(),this.bindPanZoom()}buildChrome(){this.svg.textContent="",this.defs=L("defs",{}),this.svg.appendChild(this.defs),this.viewport=L("g",{}),this.svg.appendChild(this.viewport),this.svg.addEventListener("click",()=>this.select(null))}ensureMarkers(){this.defs.textContent="";let e=L("marker",{id:"arr",viewBox:"0 0 10 10",refX:8,refY:5,markerWidth:4.5,markerHeight:4.5,orient:"auto-start-reverse"});e.appendChild(L("path",{d:"M2,1.5L8.5,5L2,8.5",fill:"none",stroke:M("--edge"),"stroke-width":1.8,"stroke-linecap":"round","stroke-linejoin":"round",opacity:.75}));let n=L("marker",{id:"arrH",viewBox:"0 0 12 12",refX:10,refY:6,markerWidth:6.5,markerHeight:6.5,orient:"auto-start-reverse"});n.appendChild(L("path",{d:"M1.5,1.5L10.5,6L1.5,10.5z",fill:M("--canvas"),stroke:M("--edge"),"stroke-width":1.4}));let i=L("marker",{id:"arrOut",viewBox:"0 0 10 10",refX:8,refY:5,markerWidth:5,markerHeight:5,orient:"auto-start-reverse"});i.appendChild(L("path",{d:"M1,1L9,5L1,9z",fill:M("--fil")}));let r=L("marker",{id:"arrIn",viewBox:"0 0 10 10",refX:8,refY:5,markerWidth:5,markerHeight:5,orient:"auto-start-reverse"});r.appendChild(L("path",{d:"M1,1L9,5L1,9z",fill:M("--accent")}));for(let o of[e,n,i,r])this.defs.appendChild(o)}setData(e,n){this.tab=n;let i=new Map(this.nodes.map(l=>[l.id,l])),r={};for(let l of e.edges)r[l.source]=(r[l.source]??0)+1,r[l.target]=(r[l.target]??0)+1;let o=this.svg.clientWidth||800,a=this.svg.clientHeight||600;this.nodes=e.nodes.map((l,s)=>{let c=i.get(l.id),m=s/Math.max(1,e.nodes.length)*Math.PI*2,u=r[l.id]??0;return{id:l.id,name:l.name,type:l.type,deg:u,r:11+Math.min(13,u*2.6),x:c?.x??o/2+Math.cos(m)*Math.min(o,a)/4,y:c?.y??a/2+Math.sin(m)*Math.min(o,a)/4,vx:c?.vx??0,vy:c?.vy??0}});let p=new Map(this.nodes.map(l=>[l.id,l]));this.edges=e.edges.map(l=>({source:p.get(l.source),target:p.get(l.target),relation:l.relation,description:l.description,confidence:l.confidence})).filter(l=>l.source&&l.target),this.sim?.stop(),this.sim=ht(this.nodes).force("charge",pt().strength(-220)).force("link",lt(this.edges).distance(l=>kt[Y(l.relation)]).strength(.5)).force("center",ot(o/2,a/2)).force("collide",at().radius(l=>l.r+6)).on("tick",()=>this.tick()),this.buildElements(),this.restyle()}buildElements(){this.ensureMarkers(),this.viewport.textContent="",this.edgeEls=this.edges.map(e=>{let n=L("path",{fill:"none","stroke-linecap":"round"}),i=L("title",{});i.textContent=e.relation.replace(/_/g," ")+(e.description?` \u2014 ${e.description}`:""),n.appendChild(i);let r=L("text",{"text-anchor":"middle","font-size":9.5,"font-weight":700,"stroke-width":3,"paint-order":"stroke",display:"none"});return r.textContent=e.relation.replace(/_/g," "),this.viewport.appendChild(n),this.viewport.appendChild(r),{path:n,label:r,edge:e}}),this.nodeEls=this.nodes.map(e=>{let n=L("g",{cursor:"pointer"}),i=L("circle",{fill:"none","stroke-width":1.6,opacity:0,r:e.r+5}),r=L("circle",{r:e.r,opacity:.92}),o=L("text",{y:e.r+15,"text-anchor":"middle","font-size":11.5,"font-weight":600});return o.textContent=e.name,n.append(i,r,o),n.addEventListener("click",a=>{a.stopPropagation(),this.select(e.id)}),this.bindDrag(n,e),this.viewport.appendChild(n),{group:n,circle:r,ring:i,label:o,node:e}})}tick(){for(let{path:e,label:n,edge:i}of this.edgeEls){let r=i.source,o=i.target,a=(o.x??0)-(r.x??0),p=(o.y??0)-(r.y??0),l=Math.sqrt(a*a+p*p)||1,s=a/l,c=p/l,m=(r.x??0)+s*(r.r+2),u=(r.y??0)+c*(r.r+2),f=(o.x??0)-s*(o.r+6),v=(o.y??0)-c*(o.r+6),h=(m+f)/2-c*10,d=(u+v)/2+s*10;e.setAttribute("d",`M${m},${u} Q${h},${d} ${f},${v}`),n.setAttribute("x",String((m+f)/2-c*14)),n.setAttribute("y",String((u+v)/2+s*14))}for(let{group:e,node:n}of this.nodeEls)e.setAttribute("transform",`translate(${n.x??0},${n.y??0})`)}restyle(){this.ensureMarkers();let e=new Set;if(this.selected)for(let r of this.edges)r.source.id===this.selected&&e.add(r.target.id),r.target.id===this.selected&&e.add(r.source.id);let n=this.query.toLowerCase(),i=r=>!this.hiddenTypes[r.type];for(let{path:r,label:o,edge:a}of this.edgeEls){let p=i(a.source)&&i(a.target)&&!this.hiddenRels[J(a.relation)];if(r.style.display=p?"":"none",o.setAttribute("display","none"),!p)continue;let l=Y(a.relation),s=M("--edge"),c=1.3,m="none",u=.5,f="url(#arr)";l==="structure"?(c=2.4,u=.32,f="none"):l==="dependency"?(c=1.5,u=.55):l==="contract"?(u=.55,f="url(#arrH)"):(m="2 5",c=1.2,u=.28,f="none"),a.confidence==="inferred"&&(m="5 4"),n&&(u=.12);let v=this.selected?a.source.id===this.selected?"out":a.target.id===this.selected?"in":"far":"none";v==="out"?(s=M("--fil"),u=.95,c+=.7,f="url(#arrOut)"):v==="in"?(s=M("--accent"),u=.95,c+=.7,f="url(#arrIn)"):v==="far"&&(u=.06),r.setAttribute("stroke",s),r.setAttribute("stroke-width",String(c)),r.setAttribute("stroke-dasharray",m),r.setAttribute("opacity",String(u)),f==="none"?r.removeAttribute("marker-end"):r.setAttribute("marker-end",f),(v==="out"||v==="in")&&(o.removeAttribute("display"),o.setAttribute("fill",s),o.setAttribute("stroke",M("--canvas")))}for(let{group:r,circle:o,ring:a,label:p,node:l}of this.nodeEls){let s=i(l);if(r.style.display=s?"":"none",!s)continue;let m=!n||l.name.toLowerCase().includes(n)?1:.22;this.selected&&l.id!==this.selected&&!e.has(l.id)&&(m=Math.min(m,.2)),r.setAttribute("opacity",String(m));let u=M(D(this.tab,l.type));o.setAttribute("fill",u),a.setAttribute("stroke",u),a.setAttribute("opacity",l.id===this.selected?"0.55":"0"),p.setAttribute("fill",M("--ink"))}}select(e){this.selected=e,this.restyle(),this.onSelect(e)}focus(e){let n=this.nodes.find(o=>o.id===e);if(!n)return;let i=this.svg.clientWidth,r=this.svg.clientHeight;this.view.k=Math.max(this.view.k,1.4),this.view.x=i/2-(n.x??0)*this.view.k,this.view.y=r/2-(n.y??0)*this.view.k,this.applyView(),this.select(e)}firstMatch(){let e=this.query.toLowerCase();return this.nodes.find(n=>!this.hiddenTypes[n.type]&&n.name.toLowerCase().includes(e))}applyView(){this.viewport.setAttribute("transform",`translate(${this.view.x},${this.view.y}) scale(${this.view.k})`)}zoomBy(e){let n=this.svg.clientWidth/2,i=this.svg.clientHeight/2,r=Math.max(.35,Math.min(3,this.view.k*e));this.view.x=n-(n-this.view.x)*(r/this.view.k),this.view.y=i-(i-this.view.y)*(r/this.view.k),this.view.k=r,this.applyView()}resetView(){this.view={x:0,y:0,k:1},this.applyView()}reheat(){this.sim?.alpha(.6).restart()}bindDrag(e,n){e.addEventListener("pointerdown",i=>{i.stopPropagation(),e.setPointerCapture(i.pointerId);let r=a=>{let p=this.svg.getBoundingClientRect();n.fx=(a.clientX-p.left-this.view.x)/this.view.k,n.fy=(a.clientY-p.top-this.view.y)/this.view.k,this.sim?.alphaTarget(.25).restart()},o=()=>{n.fx=null,n.fy=null,this.sim?.alphaTarget(0),e.removeEventListener("pointermove",r),e.removeEventListener("pointerup",o)};e.addEventListener("pointermove",r),e.addEventListener("pointerup",o)})}bindPanZoom(){let e=null;this.svg.addEventListener("pointerdown",n=>{e={x:n.clientX-this.view.x,y:n.clientY-this.view.y}}),this.svg.addEventListener("pointermove",n=>{e&&(this.view.x=n.clientX-e.x,this.view.y=n.clientY-e.y,this.applyView())}),window.addEventListener("pointerup",()=>{e=null}),this.svg.addEventListener("wheel",n=>{n.preventDefault();let i=this.svg.getBoundingClientRect(),r=n.clientX-i.left,o=n.clientY-i.top,a=Math.max(.35,Math.min(3,this.view.k*Math.exp(-n.deltaY*.0016)));this.view.x=r-(r-this.view.x)*(a/this.view.k),this.view.y=o-(o-this.view.y)*(a/this.view.k),this.view.k=a,this.applyView()},{passive:!1})}};function O(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;")}function te(t,e,n,i,r){if(!i||!e){t.innerHTML='<div class="empty">Select a node to view details</div>';return}let o=e.nodes.find(u=>u.id===i);if(!o){t.innerHTML='<div class="empty">Select a node to view details</div>';return}let a=M(D(n,o.type)),p=u=>e.nodes.find(f=>f.id===u)?.name??u,l=e.edges.filter(u=>u.source===i),s=e.edges.filter(u=>u.target===i),c=`<span class="d-type"><span class="sw" style="background:${a}"></span>${O(o.type)}</span>`;if(c+=`<h3>${O(o.name)}</h3>`,o.summary&&(c+=`<p class="summary">${O(o.summary)}</p>`),o.sources.length){c+=`<div class="d-label">Sources \xB7 ${o.sources.length}</div>`;for(let u of o.sources)c+=`<div class="src mono">${O(u)}</div>`}let m=(u,f,v,h)=>{if(u.length){c+=`<div class="d-label"><span class="dot" style="background:${f}"></span>${v} \xB7 ${u.length}</div>`;for(let d of u)c+=`<button class="lnk" data-go="${O(h(d))}"><span class="rel">${O(d.relation.replace(/_/g," "))}</span> <span class="to">${O(p(h(d)))}</span>`+(d.description?`<div class="desc">${O(d.description)}</div>`:"")+"</button>"}};m(l,M("--fil"),"Depends on",u=>u.target),m(s,M("--accent"),"Depended on by",u=>u.source),t.innerHTML=c,t.querySelectorAll("[data-go]").forEach(u=>{u.addEventListener("click",()=>r(u.dataset.go))})}function gt(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;")}function q(t,e,n,i,r){let o=s=>e.edges.filter(c=>c.relation==="contains"&&c.source===s).map(c=>e.nodes.find(m=>m.id===c.target)).filter(c=>!!c),a=s=>{let c=o(s);return c.length+c.reduce((m,u)=>m+a(u.id),0)},p=e.nodes.filter(s=>s.type==="file"),l=s=>{let c=o(s.id),m=i[s.id]!==!1,u=`<button class="tree-row${m?" open":""}${n===s.id?" sel":""}" data-id="${gt(s.id)}" data-haskids="${c.length?1:0}"><span class="chev">${c.length?"\u25B6":""}</span><span class="k" style="background:${M(D("code",s.type))}"></span><span class="t${s.type==="file"?" mono":""}">${gt(s.name)}</span><span class="badge">${c.length?a(s.id)+" sym":gt(s.type)}</span></button>`;return c.length&&m&&(u+=`<div class="tree-kids">${c.map(l).join("")}</div>`),u};t.innerHTML='<div class="tree-tools"><button data-tool="expand">Expand all</button><button data-tool="collapse">Collapse all</button></div>'+p.map(l).join(""),t.querySelectorAll(".tree-row").forEach(s=>{s.addEventListener("click",()=>{let c=s.dataset.id;s.dataset.haskids==="1"&&(i[c]=i[c]===!1,q(t,e,c,i,r)),r(c)})}),t.querySelector('[data-tool="expand"]')?.addEventListener("click",()=>{for(let s of Object.keys(i))delete i[s];q(t,e,n,i,r)}),t.querySelector('[data-tool="collapse"]')?.addEventListener("click",()=>{for(let s of e.nodes)i[s.id]=!1;q(t,e,n,i,r)})}var E=t=>document.getElementById(t),b={tab:"context",context:null,code:null,outlineOpen:{}},k=new it(E("graphSvg"));function Z(){return b.tab==="context"?b.context:b.code}function vt(){return b.tab==="code"||b.tab==="outline"?"code":"context"}function yt(){let t=E("edgeChips");t.innerHTML='<span class="cap">Edges</span>';let e=Z();if(!e)return;let n=new Map;for(let r of e.edges){let o=J(r.relation);n.set(o,(n.get(o)??0)+1)}let i=[...n.keys()].sort((r,o)=>{let a=p=>p==="part of"?0:p==="uses"?1:2;return a(r)-a(o)||r.localeCompare(o)});for(let r of i){let o=document.createElement("button");o.type="button",o.className="echip"+(k.hiddenRels[r]?"":" on"),o.innerHTML=`${ke(r)} ${r} <span style="opacity:.55">${n.get(r)}</span>`,o.title=(bt[r]??`"${r}" edges`)+" \u2014 click to "+(k.hiddenRels[r]?"show":"hide"),o.addEventListener("click",()=>{k.hiddenRels[r]=!k.hiddenRels[r],yt(),k.restyle(),wt()}),t.appendChild(o)}}function ke(t){let e=t==="part of"?"structure":t==="uses"?"dependency":Y(t.replace(/ /g,"_"));return{structure:'<svg width="20" height="8" aria-hidden="true"><line x1="1" y1="4" x2="19" y2="4" stroke="currentColor" stroke-width="3" opacity=".5"/></svg>',dependency:'<svg width="20" height="8" aria-hidden="true"><line x1="1" y1="4" x2="14" y2="4" stroke="currentColor" stroke-width="1.6"/><path d="M14,1 L19,4 L14,7 z" fill="currentColor"/></svg>',contract:'<svg width="20" height="8" aria-hidden="true"><line x1="1" y1="4" x2="13" y2="4" stroke="currentColor" stroke-width="1.4"/><path d="M13,1 L19,4 L13,7 z" fill="none" stroke="currentColor" stroke-width="1.2"/></svg>',association:'<svg width="20" height="8" aria-hidden="true"><line x1="1" y1="4" x2="19" y2="4" stroke="currentColor" stroke-width="1.4" stroke-dasharray="2 4" opacity=".7"/></svg>'}[e]}function xt(){let t=E("legend");t.innerHTML="";let e=Z();if(!e)return;let n=new Map;for(let i of e.nodes)n.set(i.type,(n.get(i.type)??0)+1);for(let[i,r]of n){let o=document.createElement("button");o.type="button",o.className="lchip"+(k.hiddenTypes[i]?" off":""),o.innerHTML=`<span class="sw" style="background:${M(D(vt(),i))}"></span>${i} <span style="color:var(--muted);font-weight:500">${r}</span>`,o.addEventListener("click",()=>{k.hiddenTypes[i]=!k.hiddenTypes[i],xt(),k.restyle(),wt()}),t.appendChild(o)}}function wt(){let t=Z();if(!t){E("lcount").textContent="";return}let e=t.nodes.filter(n=>!k.hiddenTypes[n.type]).length;E("lcount").textContent=`${e} / ${t.nodes.length} nodes shown`}function be(){let t=E("counts");if(b.tab==="outline"&&b.code){let e=b.code.nodes.filter(n=>n.type==="file").length;t.textContent=`${e} files \xB7 ${b.code.nodes.length} symbols`}else{let e=Z();t.textContent=e?`${e.meta.nodeCount} nodes \xB7 ${e.meta.edgeCount} links`:""}}function P(t){te(E("detail"),b.tab==="context"?b.context:b.code,vt(),t,e=>{b.tab==="outline"?(P(e),q(E("tree"),b.code,e,b.outlineOpen,P)):k.focus(e)})}k.onSelect=t=>P(t);function re(t){b.tab=t,document.querySelectorAll(".tab").forEach(i=>{i.setAttribute("aria-selected",i.dataset.tab===t?"true":"false")});let e=t==="outline";E("canvasWrap").hidden=e,E("outlineView").hidden=!e,k.hiddenRels={},k.hiddenTypes={},k.selected=null,P(null);let n=E("graphEmpty");if(t==="outline")b.code?q(E("tree"),b.code,null,b.outlineOpen,P):(E("outlineView").hidden=!0,E("canvasWrap").hidden=!1,ee('No code graph yet \u2014 run <code>graft graph</code> to generate <span class="mono">graph.json</span>.'));else{let i=Z();i?(n.hidden=!0,k.resetView(),k.setData(i,vt()),k.reheat()):ee(t==="code"?'No code graph yet \u2014 run <code>graft graph</code> to generate <span class="mono">graph.json</span>.':"No context graph \u2014 run <code>graft init</code> first.")}yt(),xt(),wt(),be()}function ee(t){let e=E("graphEmpty");e.innerHTML=t,e.hidden=!1}document.querySelectorAll(".tab").forEach(t=>{t.addEventListener("click",()=>re(t.dataset.tab))});var mt=E("search");mt.addEventListener("input",()=>{k.query=mt.value.trim(),k.restyle()});mt.addEventListener("keydown",t=>{if(t.key==="Enter"&&k.query){let e=k.firstMatch();e&&k.focus(e.id)}});E("zin").addEventListener("click",()=>k.zoomBy(1.25));E("zout").addEventListener("click",()=>k.zoomBy(1/1.25));E("zreset").addEventListener("click",()=>k.resetView());var ie="graft-viz-theme",ne=localStorage.getItem(ie);ne&&document.documentElement.setAttribute("data-theme",ne);E("themeBtn").addEventListener("click",()=>{let t=document.documentElement,e=t.getAttribute("data-theme"),n=window.matchMedia("(prefers-color-scheme: dark)").matches,r=(e?e==="dark":n)?"light":"dark";t.setAttribute("data-theme",r),localStorage.setItem(ie,r),k.restyle(),yt(),xt(),P(k.selected)});async function oe(){let[t,e]=await Promise.all([_t(),Et()]);b.context=t,b.code=e,E("repoName").textContent=t.meta.repoName??"",document.title=`graft viz \u2014 ${t.meta.repoName??""}`,re(b.tab)}Mt(()=>{let t=k.selected;oe().then(()=>{t&&(k.selected=t,k.restyle(),P(t))})});oe();
@@ -0,0 +1,48 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>graft viz</title>
7
+ <link rel="stylesheet" href="/style.css">
8
+ </head>
9
+ <body>
10
+ <header class="appbar">
11
+ <div class="brand"><b>graft</b><span id="repoName"></span></div>
12
+ <div class="counts" id="counts"></div>
13
+ <nav class="tabs" role="tablist" id="tabs">
14
+ <button class="tab" role="tab" aria-selected="true" data-tab="context">Context</button>
15
+ <button class="tab" role="tab" aria-selected="false" data-tab="code">Code</button>
16
+ <button class="tab" role="tab" aria-selected="false" data-tab="outline">Outline</button>
17
+ </nav>
18
+ <div class="searchbox">
19
+ <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.3-4.3"/></svg>
20
+ <input id="search" type="search" placeholder="Search nodes…" aria-label="Search nodes">
21
+ </div>
22
+ <button class="iconbtn" id="themeBtn" title="Toggle theme" aria-label="Toggle theme">
23
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M21 12.8A9 9 0 1 1 11.2 3 7 7 0 0 0 21 12.8z"/></svg>
24
+ </button>
25
+ </header>
26
+
27
+ <main class="stage" id="stage">
28
+ <div class="canvas-wrap" id="canvasWrap">
29
+ <svg class="graph" id="graphSvg" role="img" aria-label="Force-directed graph"></svg>
30
+ <div class="edgestyle" id="edgeChips"><span class="cap">Edges</span></div>
31
+ <div class="lcount" id="lcount"></div>
32
+ <div class="legend" id="legend"></div>
33
+ <div class="zoomctl">
34
+ <button class="iconbtn" id="zin" aria-label="Zoom in">+</button>
35
+ <button class="iconbtn" id="zout" aria-label="Zoom out">−</button>
36
+ <button class="iconbtn" id="zreset" aria-label="Reset view">⟲</button>
37
+ </div>
38
+ <div class="emptytab" id="graphEmpty" hidden></div>
39
+ </div>
40
+ <div class="outline" id="outlineView" hidden>
41
+ <div class="tree" id="tree"></div>
42
+ </div>
43
+ <aside class="detail" id="detail"><div class="empty">Select a node to view details</div></aside>
44
+ </main>
45
+
46
+ <script type="module" src="/app.js"></script>
47
+ </body>
48
+ </html>
@@ -0,0 +1,119 @@
1
+ /* graft viz — design tokens + app styles.
2
+ Visual reference of record: docs/superpowers/specs/2026-07-15-graft-viz-design.md */
3
+ :root{
4
+ --bg:#F4F6F6; --panel:#FFFFFF; --panel2:#FBFCFC; --ink:#17191B; --muted:#6B7278;
5
+ --border:#E2E6E7; --border2:#EDF0F1; --accent:#0B7A69; --accent-ink:#0B7A69;
6
+ --chip:#F0F3F3;
7
+ --sys:#4A7DF0; --con:#A06BDB; --fil:#D98E2B; --api:#D65C7E;
8
+ --k-file:#D98E2B; --k-class:#4A7DF0; --k-fn:#0FA38A; --k-method:#3AA7C9; --k-iface:#A06BDB; --k-type:#D65C7E; --k-enum:#6BAA3F;
9
+ --edge:#9AA4A9; --canvas:#F8FAFA;
10
+ }
11
+ @media (prefers-color-scheme: dark){
12
+ :root{
13
+ --bg:#0E1012; --panel:#15181B; --panel2:#111417; --ink:#E7E9EA; --muted:#8B9298;
14
+ --border:#252B30; --border2:#1D2226; --accent:#2FBFA4; --accent-ink:#2FBFA4;
15
+ --chip:#1C2125;
16
+ --sys:#6B94F5; --con:#B487E0; --fil:#E0A04A; --api:#E07A97;
17
+ --k-file:#E0A04A; --k-class:#6B94F5; --k-fn:#2FBFA4; --k-method:#56B9D8; --k-iface:#B487E0; --k-type:#E07A97; --k-enum:#8CC45E;
18
+ --edge:#4A545B; --canvas:#101315;
19
+ }
20
+ }
21
+ :root[data-theme="dark"]{
22
+ --bg:#0E1012; --panel:#15181B; --panel2:#111417; --ink:#E7E9EA; --muted:#8B9298;
23
+ --border:#252B30; --border2:#1D2226; --accent:#2FBFA4; --accent-ink:#2FBFA4;
24
+ --chip:#1C2125;
25
+ --sys:#6B94F5; --con:#B487E0; --fil:#E0A04A; --api:#E07A97;
26
+ --k-file:#E0A04A; --k-class:#6B94F5; --k-fn:#2FBFA4; --k-method:#56B9D8; --k-iface:#B487E0; --k-type:#E07A97; --k-enum:#8CC45E;
27
+ --edge:#4A545B; --canvas:#101315;
28
+ }
29
+ :root[data-theme="light"]{
30
+ --bg:#F4F6F6; --panel:#FFFFFF; --panel2:#FBFCFC; --ink:#17191B; --muted:#6B7278;
31
+ --border:#E2E6E7; --border2:#EDF0F1; --accent:#0B7A69; --accent-ink:#0B7A69;
32
+ --chip:#F0F3F3;
33
+ --sys:#4A7DF0; --con:#A06BDB; --fil:#D98E2B; --api:#D65C7E;
34
+ --k-file:#D98E2B; --k-class:#4A7DF0; --k-fn:#0FA38A; --k-method:#3AA7C9; --k-iface:#A06BDB; --k-type:#D65C7E; --k-enum:#6BAA3F;
35
+ --edge:#9AA4A9; --canvas:#F8FAFA;
36
+ }
37
+
38
+ *{box-sizing:border-box}
39
+ [hidden]{display:none !important}
40
+ html,body{margin:0;padding:0;height:100%}
41
+ body{
42
+ display:flex;flex-direction:column;
43
+ background:var(--bg);color:var(--ink);
44
+ font-family:"Avenir Next","Segoe UI",system-ui,-apple-system,sans-serif;
45
+ font-size:15px;line-height:1.55;-webkit-font-smoothing:antialiased;
46
+ }
47
+ .mono{font-family:ui-monospace,"SF Mono",SFMono-Regular,Menlo,Consolas,monospace}
48
+ button:focus-visible,input:focus-visible,.tree-row:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
49
+
50
+ .appbar{display:flex;align-items:center;gap:14px;padding:10px 16px;border-bottom:1px solid var(--border);background:var(--panel);flex:none}
51
+ .brand{display:flex;align-items:baseline;gap:8px;flex:none}
52
+ .brand b{font-weight:700;letter-spacing:-.01em}
53
+ .brand span{color:var(--muted);font-size:13px}
54
+ .counts{color:var(--muted);font-size:12.5px;font-variant-numeric:tabular-nums;flex:none}
55
+ .tabs{display:flex;background:var(--chip);border-radius:9px;padding:3px;gap:2px;margin-left:auto;flex:none}
56
+ .tab{border:0;background:transparent;color:var(--muted);font:inherit;font-size:13px;font-weight:600;padding:5px 14px;border-radius:7px;cursor:pointer}
57
+ .tab:hover{color:var(--ink)}
58
+ .tab[aria-selected="true"]{background:var(--panel);color:var(--ink);box-shadow:0 1px 3px rgba(0,0,0,.12)}
59
+ .searchbox{position:relative;flex:none}
60
+ .searchbox input{width:190px;padding:6px 10px 6px 30px;border:1px solid var(--border);border-radius:8px;background:var(--panel2);color:var(--ink);font:inherit;font-size:13px;outline:none}
61
+ .searchbox input:focus{border-color:var(--accent)}
62
+ .searchbox svg{position:absolute;left:9px;top:50%;transform:translateY(-50%);opacity:.45}
63
+ .iconbtn{border:1px solid var(--border);background:var(--panel2);color:var(--ink);border-radius:8px;width:32px;height:32px;cursor:pointer;display:grid;place-items:center;flex:none;font:inherit;font-size:14px}
64
+ .iconbtn:hover{border-color:var(--muted)}
65
+
66
+ .stage{display:flex;flex:1;min-height:0;background:var(--canvas)}
67
+ .canvas-wrap{position:relative;flex:1;min-width:0}
68
+ svg.graph{display:block;width:100%;height:100%;cursor:grab}
69
+ svg.graph:active{cursor:grabbing}
70
+
71
+ .edgestyle{position:absolute;top:12px;left:14px;display:flex;align-items:center;gap:5px;flex-wrap:wrap;max-width:75%}
72
+ .edgestyle .cap{font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);font-weight:700;margin-right:3px}
73
+ .echip{display:inline-flex;align-items:center;gap:5px;border:1px solid var(--border);background:var(--panel);color:var(--muted);border-radius:16px;padding:3px 10px;font:inherit;font-size:11.5px;font-weight:600;cursor:pointer}
74
+ .echip:hover{color:var(--ink)}
75
+ .echip.on{color:var(--ink)}
76
+ .echip:not(.on){opacity:.45}
77
+
78
+ .legend{position:absolute;left:14px;bottom:14px;display:flex;gap:6px;flex-wrap:wrap;max-width:70%}
79
+ .lchip{display:flex;align-items:center;gap:6px;font-size:12px;font-weight:600;color:var(--ink);background:var(--panel);border:1px solid var(--border);border-radius:20px;padding:4px 11px;cursor:pointer;user-select:none;font-family:inherit}
80
+ .lchip .sw{width:9px;height:9px;border-radius:50%}
81
+ .lchip.off{opacity:.38}
82
+ .lcount{position:absolute;left:14px;bottom:52px;font-size:11.5px;color:var(--muted);font-variant-numeric:tabular-nums}
83
+ .zoomctl{position:absolute;right:14px;bottom:14px;display:flex;flex-direction:column;gap:6px}
84
+ .zoomctl .iconbtn{background:var(--panel)}
85
+
86
+ .detail{width:300px;flex:none;border-left:1px solid var(--border);background:var(--panel);overflow-y:auto;padding:18px 18px 24px}
87
+ .detail .empty{color:var(--muted);font-size:13.5px;text-align:center;margin-top:200px}
88
+ .d-type{display:inline-flex;align-items:center;gap:6px;font-size:11px;letter-spacing:.1em;text-transform:uppercase;font-weight:700;padding:3px 10px;border-radius:20px;background:var(--chip)}
89
+ .d-type .sw{width:8px;height:8px;border-radius:50%}
90
+ .detail h3{margin:10px 0 6px;font-size:17px;letter-spacing:-.01em}
91
+ .detail p.summary{font-size:13.5px;color:var(--ink);margin:0 0 16px;white-space:pre-line}
92
+ .d-label{font-size:10.5px;letter-spacing:.13em;text-transform:uppercase;color:var(--muted);font-weight:700;margin:16px 0 7px;display:flex;align-items:center;gap:5px}
93
+ .d-label .dot{display:inline-block;width:7px;height:7px;border-radius:50%}
94
+ .src{font-size:12px;color:var(--muted);padding:3px 0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
95
+ .lnk{display:block;width:100%;text-align:left;border:1px solid var(--border2);background:var(--panel2);border-radius:8px;padding:7px 10px;margin-bottom:6px;cursor:pointer;font:inherit;color:var(--ink)}
96
+ .lnk:hover{border-color:var(--accent)}
97
+ .lnk .rel{font-size:10.5px;letter-spacing:.09em;text-transform:uppercase;color:var(--accent-ink);font-weight:700}
98
+ .lnk .to{font-size:13px;font-weight:600}
99
+ .lnk .desc{font-size:12px;color:var(--muted)}
100
+
101
+ .outline{flex:1;display:flex;min-width:0}
102
+ .tree{flex:1;overflow-y:auto;padding:14px 8px 20px 14px;background:var(--panel)}
103
+ .tree-tools{display:flex;gap:14px;font-size:12.5px;color:var(--muted);padding:2px 8px 10px}
104
+ .tree-tools button{border:0;background:none;color:var(--accent-ink);font:inherit;font-size:12.5px;cursor:pointer;padding:0}
105
+ .tree-row{display:flex;align-items:center;gap:7px;padding:4px 8px;border-radius:7px;cursor:pointer;border:0;background:none;width:100%;text-align:left;font:inherit;color:var(--ink)}
106
+ .tree-row:hover{background:var(--chip)}
107
+ .tree-row.sel{background:var(--chip)}
108
+ .tree-row .chev{width:14px;flex:none;color:var(--muted);font-size:10px;transition:transform .12s}
109
+ .tree-row.open .chev{transform:rotate(90deg)}
110
+ .tree-row .k{width:9px;height:9px;border-radius:3px;flex:none}
111
+ .tree-row .t{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13.5px}
112
+ .tree-row .badge{margin-left:auto;font-size:11px;color:var(--muted);font-variant-numeric:tabular-nums;flex:none}
113
+ .tree-kids{margin-left:19px;border-left:1px solid var(--border2);padding-left:4px}
114
+
115
+ .emptytab{position:absolute;inset:0;display:grid;place-items:center;color:var(--muted);font-size:14px;text-align:center;padding:30px;background:var(--canvas)}
116
+ .emptytab code{background:var(--chip);padding:2px 8px;border-radius:6px;font-family:ui-monospace,"SF Mono",Menlo,Consolas,monospace;font-size:.9em}
117
+
118
+ @media (prefers-reduced-motion:reduce){ .tree-row .chev{transition:none} }
119
+ @media (max-width:760px){ .detail{display:none} .searchbox input{width:120px} .counts{display:none} }
@@ -0,0 +1,33 @@
1
+ export interface VizNode {
2
+ id: string;
3
+ name: string;
4
+ type: string;
5
+ summary: string;
6
+ sources: string[];
7
+ }
8
+ export interface VizEdge {
9
+ source: string;
10
+ target: string;
11
+ relation: string;
12
+ description?: string;
13
+ }
14
+ export interface VizGraph {
15
+ meta: {
16
+ nodeCount: number;
17
+ edgeCount: number;
18
+ skippedFiles: number;
19
+ droppedEdges: number;
20
+ };
21
+ nodes: VizNode[];
22
+ edges: VizEdge[];
23
+ }
24
+ /**
25
+ * Legacy vague verbs → the concrete verb they usually mean. Keeps old
26
+ * `.context/` folders readable without regeneration; the synthesizer itself
27
+ * is constrained to the closed vocabulary going forward.
28
+ */
29
+ export declare const NORMALIZE_RELATION: Record<string, string>;
30
+ export declare function normalizeRelation(rel: string): string;
31
+ /** Read a context dir and assemble the render-ready graph document. */
32
+ export declare function assembleContextGraph(contextDir: string): VizGraph;
33
+ //# sourceMappingURL=assemble.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assemble.d.ts","sourceRoot":"","sources":["../../src/viz/assemble.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE;QACJ,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,KAAK,EAAE,OAAO,EAAE,CAAC;CAClB;AAED;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAKrD,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAErD;AAoBD,uEAAuE;AACvE,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ,CAmDjE"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Assembles the viz-facing JSON for the context graph: reads every `.md` node
3
+ * file in a context dir (frontmatter = node + edges) and produces a single
4
+ * `{meta, nodes, edges}` document the viewer can render directly.
5
+ *
6
+ * Design rules (see docs/superpowers/specs/2026-07-15-graft-viz-design.md):
7
+ * - relations are normalized into the closed verb vocabulary — vague LLM
8
+ * softeners map to the concrete verb they usually mean
9
+ * - edges whose target slug doesn't exist are dropped (LLM output can dangle)
10
+ * - a node file with unparseable frontmatter is skipped, never fatal
11
+ * - both drop counts are reported in `meta` so the UI can say so
12
+ */
13
+ import { readFileSync, readdirSync, existsSync } from "node:fs";
14
+ import { join } from "node:path";
15
+ import matter from "gray-matter";
16
+ /**
17
+ * Legacy vague verbs → the concrete verb they usually mean. Keeps old
18
+ * `.context/` folders readable without regeneration; the synthesizer itself
19
+ * is constrained to the closed vocabulary going forward.
20
+ */
21
+ export const NORMALIZE_RELATION = {
22
+ influences: "configures",
23
+ supports: "validates",
24
+ defines: "validates",
25
+ measures: "validates",
26
+ };
27
+ export function normalizeRelation(rel) {
28
+ return NORMALIZE_RELATION[rel] ?? rel;
29
+ }
30
+ const GEN_START = "<!-- context:generated:start -->";
31
+ const GEN_END = "<!-- context:generated:end -->";
32
+ const SUMMARY_MAX = 500;
33
+ /** Pull the prose summary out of a node body's generated block. */
34
+ function extractSummary(body) {
35
+ let text = body;
36
+ const start = text.indexOf(GEN_START);
37
+ const end = text.indexOf(GEN_END);
38
+ if (start >= 0 && end > start)
39
+ text = text.slice(start + GEN_START.length, end);
40
+ // Keep only the Summary section if headings are present.
41
+ const related = text.indexOf("## Related");
42
+ if (related >= 0)
43
+ text = text.slice(0, related);
44
+ text = text.replace(/^##\s+Summary\s*$/m, "");
45
+ text = text.trim();
46
+ return text.length > SUMMARY_MAX ? text.slice(0, SUMMARY_MAX).trimEnd() + "…" : text;
47
+ }
48
+ /** Read a context dir and assemble the render-ready graph document. */
49
+ export function assembleContextGraph(contextDir) {
50
+ const nodes = [];
51
+ const rawEdges = [];
52
+ let skippedFiles = 0;
53
+ if (existsSync(contextDir)) {
54
+ for (const entry of readdirSync(contextDir).sort()) {
55
+ if (!entry.endsWith(".md"))
56
+ continue;
57
+ let parsed;
58
+ try {
59
+ parsed = matter(readFileSync(join(contextDir, entry), "utf8"));
60
+ }
61
+ catch {
62
+ skippedFiles++;
63
+ continue;
64
+ }
65
+ const fm = parsed.data;
66
+ const id = String(fm.slug ?? entry.replace(/\.md$/, ""));
67
+ const sources = Array.isArray(fm.sources) ? fm.sources : [];
68
+ const links = Array.isArray(fm.links) ? fm.links : [];
69
+ nodes.push({
70
+ id,
71
+ name: String(fm.name ?? id),
72
+ type: String(fm.type ?? "concept"),
73
+ summary: extractSummary(parsed.content),
74
+ sources: sources.map((s) => s.path),
75
+ });
76
+ for (const link of links) {
77
+ if (!link || typeof link.to !== "string")
78
+ continue;
79
+ rawEdges.push({
80
+ source: id,
81
+ target: link.to,
82
+ relation: normalizeRelation(String(link.relation ?? "uses")),
83
+ ...(link.description ? { description: link.description } : {}),
84
+ });
85
+ }
86
+ }
87
+ }
88
+ const known = new Set(nodes.map((n) => n.id));
89
+ const edges = rawEdges.filter((e) => known.has(e.source) && known.has(e.target));
90
+ return {
91
+ meta: {
92
+ nodeCount: nodes.length,
93
+ edgeCount: edges.length,
94
+ skippedFiles,
95
+ droppedEdges: rawEdges.length - edges.length,
96
+ },
97
+ nodes,
98
+ edges,
99
+ };
100
+ }
101
+ //# sourceMappingURL=assemble.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assemble.js","sourceRoot":"","sources":["../../src/viz/assemble.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export interface VizServerOptions {
2
+ contextDir: string;
3
+ viewerDir: string;
4
+ port: number;
5
+ repoName: string;
6
+ }
7
+ export interface VizServer {
8
+ url: string;
9
+ close(): Promise<void>;
10
+ }
11
+ export declare function startVizServer(opts: VizServerOptions): Promise<VizServer>;
12
+ //# sourceMappingURL=serve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/viz/serve.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AA+BD,wBAAsB,cAAc,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,SAAS,CAAC,CA2F/E"}
@@ -0,0 +1,128 @@
1
+ /**
2
+ * The `graft viz` local server. Zero runtime dependencies: node:http serves
3
+ * the prebuilt viewer bundle, two JSON endpoints, and an SSE channel that
4
+ * pings the browser whenever the context dir changes on disk.
5
+ *
6
+ * GET / viewer (index.html, app.js, style.css from viewerDir)
7
+ * GET /api/context-graph assembled from .context/*.md on every request
8
+ * GET /api/code-graph .context/graph.json passthrough (404 until generated)
9
+ * GET /events SSE; fs.watch on the context dir, debounced 300ms
10
+ */
11
+ import { createServer } from "node:http";
12
+ import { readFileSync, existsSync, watch } from "node:fs";
13
+ import { join } from "node:path";
14
+ import { assembleContextGraph } from "./assemble.js";
15
+ const PORT_ATTEMPTS = 10;
16
+ const WATCH_DEBOUNCE_MS = 300;
17
+ const STATIC_FILES = {
18
+ "/": { file: "index.html", type: "text/html; charset=utf-8" },
19
+ "/index.html": { file: "index.html", type: "text/html; charset=utf-8" },
20
+ "/app.js": { file: "app.js", type: "text/javascript; charset=utf-8" },
21
+ "/style.css": { file: "style.css", type: "text/css; charset=utf-8" },
22
+ };
23
+ function sendJson(res, status, body) {
24
+ res.writeHead(status, { "content-type": "application/json; charset=utf-8" });
25
+ res.end(JSON.stringify(body));
26
+ }
27
+ function listen(server, port) {
28
+ return new Promise((resolve, reject) => {
29
+ const onError = (err) => {
30
+ if (err.code === "EADDRINUSE")
31
+ resolve(false);
32
+ else
33
+ reject(err);
34
+ };
35
+ server.once("error", onError);
36
+ server.listen(port, "127.0.0.1", () => {
37
+ server.removeListener("error", onError);
38
+ resolve(true);
39
+ });
40
+ });
41
+ }
42
+ export async function startVizServer(opts) {
43
+ const sseClients = new Set();
44
+ const server = createServer((req, res) => {
45
+ const path = (req.url ?? "/").split("?")[0];
46
+ const asset = STATIC_FILES[path];
47
+ if (asset) {
48
+ const file = join(opts.viewerDir, asset.file);
49
+ if (!existsSync(file)) {
50
+ res.writeHead(404, { "content-type": "text/plain" });
51
+ res.end("viewer bundle missing — run the package build");
52
+ return;
53
+ }
54
+ res.writeHead(200, { "content-type": asset.type });
55
+ res.end(readFileSync(file));
56
+ return;
57
+ }
58
+ if (path === "/api/context-graph") {
59
+ const graph = assembleContextGraph(opts.contextDir);
60
+ sendJson(res, 200, { ...graph, meta: { ...graph.meta, repoName: opts.repoName } });
61
+ return;
62
+ }
63
+ if (path === "/api/code-graph") {
64
+ const file = join(opts.contextDir, "graph.json");
65
+ if (!existsSync(file)) {
66
+ sendJson(res, 404, { error: "no graph.json in this context dir — run `graft graph` first" });
67
+ return;
68
+ }
69
+ try {
70
+ const parsed = JSON.parse(readFileSync(file, "utf8"));
71
+ if (parsed?.meta?.version !== 1) {
72
+ sendJson(res, 404, { error: "graph.json has an unsupported version — regenerate with `graft graph`" });
73
+ return;
74
+ }
75
+ sendJson(res, 200, parsed);
76
+ }
77
+ catch {
78
+ sendJson(res, 404, { error: "graph.json is unreadable — regenerate with `graft graph`" });
79
+ }
80
+ return;
81
+ }
82
+ if (path === "/events") {
83
+ res.writeHead(200, {
84
+ "content-type": "text/event-stream",
85
+ "cache-control": "no-cache",
86
+ connection: "keep-alive",
87
+ });
88
+ res.write(": connected\n\n");
89
+ sseClients.add(res);
90
+ req.on("close", () => sseClients.delete(res));
91
+ return;
92
+ }
93
+ res.writeHead(404, { "content-type": "text/plain" });
94
+ res.end("not found");
95
+ });
96
+ let port = opts.port;
97
+ let bound = false;
98
+ for (let i = 0; i < PORT_ATTEMPTS && !bound; i++) {
99
+ port = opts.port + i;
100
+ bound = await listen(server, port);
101
+ }
102
+ if (!bound) {
103
+ throw new Error(`no free port in ${opts.port}–${opts.port + PORT_ATTEMPTS - 1}`);
104
+ }
105
+ let watcher;
106
+ let debounce;
107
+ if (existsSync(opts.contextDir)) {
108
+ watcher = watch(opts.contextDir, () => {
109
+ clearTimeout(debounce);
110
+ debounce = setTimeout(() => {
111
+ for (const client of sseClients)
112
+ client.write("data: change\n\n");
113
+ }, WATCH_DEBOUNCE_MS);
114
+ });
115
+ }
116
+ return {
117
+ url: `http://127.0.0.1:${port}`,
118
+ close() {
119
+ clearTimeout(debounce);
120
+ watcher?.close();
121
+ for (const client of sseClients)
122
+ client.end();
123
+ sseClients.clear();
124
+ return new Promise((resolve, reject) => server.close((err) => (err ? reject(err) : resolve())));
125
+ },
126
+ };
127
+ }
128
+ //# sourceMappingURL=serve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve.js","sourceRoot":"","sources":["../../src/viz/serve.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "@nanonets/graft",
3
+ "version": "0.1.0",
4
+ "description": "Build a repo's context graph as a folder of linked markdown files that live in the repo and stay in sync with the code through git.",
5
+ "keywords": [
6
+ "ai",
7
+ "agents",
8
+ "knowledge-graph",
9
+ "context",
10
+ "codebase",
11
+ "llm",
12
+ "graft"
13
+ ],
14
+ "license": "MIT",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/NanoNets/context-graph-engine.git"
18
+ },
19
+ "homepage": "https://github.com/NanoNets/context-graph-engine#readme",
20
+ "bugs": {
21
+ "url": "https://github.com/NanoNets/context-graph-engine/issues"
22
+ },
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "type": "module",
27
+ "main": "./dist/index.js",
28
+ "types": "./dist/index.d.ts",
29
+ "exports": {
30
+ ".": {
31
+ "types": "./dist/index.d.ts",
32
+ "import": "./dist/index.js"
33
+ }
34
+ },
35
+ "bin": {
36
+ "graft": "dist/cli.js"
37
+ },
38
+ "files": [
39
+ "dist",
40
+ "README.md",
41
+ "LICENSE"
42
+ ],
43
+ "scripts": {
44
+ "build": "tsc -p tsconfig.json && tsc -p viewer/tsconfig.json && node scripts/build-viewer.mjs",
45
+ "dev": "tsc -p tsconfig.json --watch",
46
+ "cli": "tsx src/cli.ts",
47
+ "bench": "tsx bench/run.ts",
48
+ "test": "node --import tsx --test test/*.test.ts",
49
+ "prepare": "npm run build",
50
+ "prepublishOnly": "npm run build"
51
+ },
52
+ "engines": {
53
+ "node": ">=20"
54
+ },
55
+ "dependencies": {
56
+ "commander": "^15.0.0",
57
+ "dotenv": "^17.4.2",
58
+ "gray-matter": "^4.0.3",
59
+ "openai": "^6.45.0",
60
+ "tree-sitter": "^0.21.1",
61
+ "tree-sitter-python": "^0.21.0",
62
+ "tree-sitter-typescript": "^0.23.2"
63
+ },
64
+ "devDependencies": {
65
+ "@types/d3-force": "^3.0.10",
66
+ "@types/node": "^26.1.0",
67
+ "d3-force": "^3.0.0",
68
+ "esbuild": "^0.28.1",
69
+ "tsx": "^4.22.4",
70
+ "typescript": "^6.0.3"
71
+ }
72
+ }