@smartmemory/compose 0.1.9-beta → 0.1.10-beta

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 (63) hide show
  1. package/bin/compose.js +64 -30
  2. package/dist/assets/{_baseUniq-3jW4HAOf.js → _baseUniq-1Y6jTF8t.js} +1 -1
  3. package/dist/assets/{arc-DzzDimyd.js → arc-D6ZzqBFD.js} +1 -1
  4. package/dist/assets/{architectureDiagram-Q4EWVU46-CtAgwORz.js → architectureDiagram-Q4EWVU46-Dz07OB2P.js} +1 -1
  5. package/dist/assets/{blockDiagram-DXYQGD6D-Bryby0c_.js → blockDiagram-DXYQGD6D-CIx_vZA9.js} +1 -1
  6. package/dist/assets/{c4Diagram-AHTNJAMY-C7N9RTJ8.js → c4Diagram-AHTNJAMY-oPCtldKL.js} +1 -1
  7. package/dist/assets/channel-BOEco5yc.js +1 -0
  8. package/dist/assets/{chunk-4BX2VUAB-wijkFgZY.js → chunk-4BX2VUAB-DE1mVs3B.js} +1 -1
  9. package/dist/assets/{chunk-4TB4RGXK-zdSZGRS2.js → chunk-4TB4RGXK-BBOEH_yf.js} +1 -1
  10. package/dist/assets/{chunk-55IACEB6-6zqzTZQQ.js → chunk-55IACEB6-BNnoj3te.js} +1 -1
  11. package/dist/assets/{chunk-EDXVE4YY-frd1Vwf-.js → chunk-EDXVE4YY-BONF1eoC.js} +1 -1
  12. package/dist/assets/{chunk-FMBD7UC4-CdkRK5Hx.js → chunk-FMBD7UC4-ij7VBSva.js} +1 -1
  13. package/dist/assets/{chunk-OYMX7WX6-C6bMB0cf.js → chunk-OYMX7WX6-CzccIkdC.js} +1 -1
  14. package/dist/assets/{chunk-QZHKN3VN-4vsxN3jq.js → chunk-QZHKN3VN-6U3wTZV_.js} +1 -1
  15. package/dist/assets/{chunk-YZCP3GAM-DbNARKip.js → chunk-YZCP3GAM-B0KFbf0L.js} +1 -1
  16. package/dist/assets/classDiagram-6PBFFD2Q-Z--fTQT6.js +1 -0
  17. package/dist/assets/classDiagram-v2-HSJHXN6E-Z--fTQT6.js +1 -0
  18. package/dist/assets/clone-WlHlToLE.js +1 -0
  19. package/dist/assets/{cose-bilkent-S5V4N54A-BpXeV7Vj.js → cose-bilkent-S5V4N54A-CicoNwP8.js} +1 -1
  20. package/dist/assets/{dagre-KV5264BT-DQLu_W8r.js → dagre-KV5264BT-CwIGE26v.js} +1 -1
  21. package/dist/assets/{diagram-5BDNPKRD-skaOoe5A.js → diagram-5BDNPKRD-2VTTGl95.js} +1 -1
  22. package/dist/assets/{diagram-G4DWMVQ6-DezlfFH4.js → diagram-G4DWMVQ6-3Pf96ieo.js} +1 -1
  23. package/dist/assets/{diagram-MMDJMWI5-BUu-v-wT.js → diagram-MMDJMWI5-BMJV4Wvv.js} +1 -1
  24. package/dist/assets/{diagram-TYMM5635-CziQ6LPs.js → diagram-TYMM5635-DekMw47L.js} +1 -1
  25. package/dist/assets/{erDiagram-SMLLAGMA-BsAyOVTI.js → erDiagram-SMLLAGMA-CoDv7HYm.js} +1 -1
  26. package/dist/assets/{flowDiagram-DWJPFMVM-CbYWJOLq.js → flowDiagram-DWJPFMVM-Cnh8oAOL.js} +1 -1
  27. package/dist/assets/{ganttDiagram-T4ZO3ILL-CAwgDkLl.js → ganttDiagram-T4ZO3ILL-4E_ddNnO.js} +1 -1
  28. package/dist/assets/{gitGraphDiagram-UUTBAWPF-DK4RlkjO.js → gitGraphDiagram-UUTBAWPF-BezAbuh1.js} +1 -1
  29. package/dist/assets/{graph-orv1XHGx.js → graph-Dwh_GDZn.js} +1 -1
  30. package/dist/assets/{index-Ceywghsu.js → index-DHdiTAmV.js} +224 -224
  31. package/dist/assets/{infoDiagram-42DDH7IO-DQyA75sK.js → infoDiagram-42DDH7IO-CQG6i5uG.js} +1 -1
  32. package/dist/assets/{ishikawaDiagram-UXIWVN3A-C-F_5q4k.js → ishikawaDiagram-UXIWVN3A-DU-pg-DQ.js} +1 -1
  33. package/dist/assets/{journeyDiagram-VCZTEJTY-Bj8UIvK-.js → journeyDiagram-VCZTEJTY-D16FySlp.js} +1 -1
  34. package/dist/assets/{kanban-definition-6JOO6SKY-DZYr8Dp1.js → kanban-definition-6JOO6SKY-CeBu_tC8.js} +1 -1
  35. package/dist/assets/{layout-CBaTKjpX.js → layout-20cH9PdF.js} +1 -1
  36. package/dist/assets/{linear-j1sI_SiN.js → linear-BJLAYBLR.js} +1 -1
  37. package/dist/assets/{min-DtJISjld.js → min-DpwGyJGd.js} +1 -1
  38. package/dist/assets/{mindmap-definition-QFDTVHPH-Bulb64RS.js → mindmap-definition-QFDTVHPH-Cs8A-hRb.js} +1 -1
  39. package/dist/assets/{pieDiagram-DEJITSTG-D11keQxr.js → pieDiagram-DEJITSTG-B_3gJ-t1.js} +1 -1
  40. package/dist/assets/{quadrantDiagram-34T5L4WZ-BEcWQiEG.js → quadrantDiagram-34T5L4WZ-CWwkmlo7.js} +1 -1
  41. package/dist/assets/{requirementDiagram-MS252O5E-Cbp23uDf.js → requirementDiagram-MS252O5E-B5_FnptK.js} +1 -1
  42. package/dist/assets/{sankeyDiagram-XADWPNL6-Dae1hMc5.js → sankeyDiagram-XADWPNL6-C1V7g3pp.js} +1 -1
  43. package/dist/assets/{sequenceDiagram-FGHM5R23-C16abORi.js → sequenceDiagram-FGHM5R23-Cx40AcDN.js} +1 -1
  44. package/dist/assets/{stateDiagram-FHFEXIEX-CbEtfhbx.js → stateDiagram-FHFEXIEX-MB9EofRm.js} +1 -1
  45. package/dist/assets/stateDiagram-v2-QKLJ7IA2-B2uq4PdS.js +1 -0
  46. package/dist/assets/{timeline-definition-GMOUNBTQ-BV7JTNMI.js → timeline-definition-GMOUNBTQ-BVeH32Lw.js} +1 -1
  47. package/dist/assets/{vennDiagram-DHZGUBPP-DBZiT48j.js → vennDiagram-DHZGUBPP-COiwH6EY.js} +1 -1
  48. package/dist/assets/{wardley-RL74JXVD-Cc8uoiL3.js → wardley-RL74JXVD-BOOKZXAy.js} +1 -1
  49. package/dist/assets/{wardleyDiagram-NUSXRM2D-DEYcWGo5.js → wardleyDiagram-NUSXRM2D-Cjobr6lF.js} +1 -1
  50. package/dist/assets/{xychartDiagram-5P7HB3ND-bFhLXv2b.js → xychartDiagram-5P7HB3ND-Bj1YVrJO.js} +1 -1
  51. package/dist/index.html +1 -1
  52. package/lib/vision-writer.js +7 -1
  53. package/package.json +1 -1
  54. package/server/compose-mcp-tools.js +86 -87
  55. package/server/design-routes.js +5 -2
  56. package/server/index.js +5 -0
  57. package/server/workspace-middleware.js +120 -0
  58. package/server/workspace-routes.js +25 -0
  59. package/dist/assets/channel-DDkv7DUd.js +0 -1
  60. package/dist/assets/classDiagram-6PBFFD2Q-J6ZTeCbW.js +0 -1
  61. package/dist/assets/classDiagram-v2-HSJHXN6E-J6ZTeCbW.js +0 -1
  62. package/dist/assets/clone-5MVZ89iV.js +0 -1
  63. package/dist/assets/stateDiagram-v2-QKLJ7IA2-CyY84hEA.js +0 -1
@@ -1,4 +1,4 @@
1
- import{s as gi,g as xi,q as Xt,p as di,a as fi,b as pi,_ as a,l as Yt,I as mi,e as yi,z as bi,D as _t,i as Ai,F as Nt,G as wi,K as Ci,aH as Si,R as Wt}from"./index-Ceywghsu.js";import{i as _i}from"./init-Gi6I4Gst.js";import{o as ki}from"./ordinal-Cboi1Yqb.js";import{l as zt}from"./linear-j1sI_SiN.js";import"./defaultLocale-DX6XiGOO.js";function Ri(e,t,i){e=+e,t=+t,i=(n=arguments.length)<2?(t=e,e=0,1):n<3?1:+i;for(var s=-1,n=Math.max(0,Math.ceil((t-e)/i))|0,o=new Array(n);++s<n;)o[s]=e+s*i;return o}function bt(){var e=ki().unknown(void 0),t=e.domain,i=e.range,s=0,n=1,o,u,p=!1,f=0,T=0,P=.5;delete e.unknown;function _(){var y=t().length,E=n<s,v=E?n:s,L=E?s:n;o=(L-v)/Math.max(1,y-f+T*2),p&&(o=Math.floor(o)),v+=(L-v-o*(y-f))*P,u=o*(1-f),p&&(v=Math.round(v),u=Math.round(u));var I=Ri(y).map(function(m){return v+o*m});return i(E?I.reverse():I)}return e.domain=function(y){return arguments.length?(t(y),_()):t()},e.range=function(y){return arguments.length?([s,n]=y,s=+s,n=+n,_()):[s,n]},e.rangeRound=function(y){return[s,n]=y,s=+s,n=+n,p=!0,_()},e.bandwidth=function(){return u},e.step=function(){return o},e.round=function(y){return arguments.length?(p=!!y,_()):p},e.padding=function(y){return arguments.length?(f=Math.min(1,T=+y),_()):f},e.paddingInner=function(y){return arguments.length?(f=Math.min(1,y),_()):f},e.paddingOuter=function(y){return arguments.length?(T=+y,_()):T},e.align=function(y){return arguments.length?(P=Math.max(0,Math.min(1,y)),_()):P},e.copy=function(){return bt(t(),[s,n]).round(p).paddingInner(f).paddingOuter(T).align(P)},_i.apply(_(),arguments)}var At=(function(){var e=a(function(F,h,c,g){for(c=c||{},g=F.length;g--;c[F[g]]=h);return c},"o"),t=[1,10,12,14,16,18,19,21,23],i=[2,6],s=[1,3],n=[1,5],o=[1,6],u=[1,7],p=[1,5,10,12,14,16,18,19,21,23,34,35,36],f=[1,25],T=[1,26],P=[1,28],_=[1,29],y=[1,30],E=[1,31],v=[1,32],L=[1,33],I=[1,34],m=[1,35],R=[1,36],l=[1,37],W=[1,43],O=[1,42],X=[1,47],Y=[1,50],S=[1,10,12,14,16,18,19,21,23,34,35,36],U=[1,10,12,14,16,18,19,21,23,24,26,27,28,34,35,36],b=[1,10,12,14,16,18,19,21,23,24,26,27,28,34,35,36,41,42,43,44,45,46,47,48,49,50],w=[1,64],V={trace:a(function(){},"trace"),yy:{},symbols_:{error:2,start:3,eol:4,XYCHART:5,chartConfig:6,document:7,CHART_ORIENTATION:8,statement:9,title:10,text:11,X_AXIS:12,parseXAxis:13,Y_AXIS:14,parseYAxis:15,LINE:16,plotData:17,BAR:18,acc_title:19,acc_title_value:20,acc_descr:21,acc_descr_value:22,acc_descr_multiline_value:23,SQUARE_BRACES_START:24,commaSeparatedNumbers:25,SQUARE_BRACES_END:26,NUMBER_WITH_DECIMAL:27,COMMA:28,xAxisData:29,bandData:30,ARROW_DELIMITER:31,commaSeparatedTexts:32,yAxisData:33,NEWLINE:34,SEMI:35,EOF:36,alphaNum:37,STR:38,MD_STR:39,alphaNumToken:40,AMP:41,NUM:42,ALPHA:43,PLUS:44,EQUALS:45,MULT:46,DOT:47,BRKT:48,MINUS:49,UNDERSCORE:50,$accept:0,$end:1},terminals_:{2:"error",5:"XYCHART",8:"CHART_ORIENTATION",10:"title",12:"X_AXIS",14:"Y_AXIS",16:"LINE",18:"BAR",19:"acc_title",20:"acc_title_value",21:"acc_descr",22:"acc_descr_value",23:"acc_descr_multiline_value",24:"SQUARE_BRACES_START",26:"SQUARE_BRACES_END",27:"NUMBER_WITH_DECIMAL",28:"COMMA",31:"ARROW_DELIMITER",34:"NEWLINE",35:"SEMI",36:"EOF",38:"STR",39:"MD_STR",41:"AMP",42:"NUM",43:"ALPHA",44:"PLUS",45:"EQUALS",46:"MULT",47:"DOT",48:"BRKT",49:"MINUS",50:"UNDERSCORE"},productions_:[0,[3,2],[3,3],[3,2],[3,1],[6,1],[7,0],[7,2],[9,2],[9,2],[9,2],[9,2],[9,2],[9,3],[9,2],[9,3],[9,2],[9,2],[9,1],[17,3],[25,3],[25,1],[13,1],[13,2],[13,1],[29,1],[29,3],[30,3],[32,3],[32,1],[15,1],[15,2],[15,1],[33,3],[4,1],[4,1],[4,1],[11,1],[11,1],[11,1],[37,1],[37,2],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1]],performAction:a(function(h,c,g,x,C,r,rt){var d=r.length-1;switch(C){case 5:x.setOrientation(r[d]);break;case 9:x.setDiagramTitle(r[d].text.trim());break;case 12:x.setLineData({text:"",type:"text"},r[d]);break;case 13:x.setLineData(r[d-1],r[d]);break;case 14:x.setBarData({text:"",type:"text"},r[d]);break;case 15:x.setBarData(r[d-1],r[d]);break;case 16:this.$=r[d].trim(),x.setAccTitle(this.$);break;case 17:case 18:this.$=r[d].trim(),x.setAccDescription(this.$);break;case 19:this.$=r[d-1];break;case 20:this.$=[Number(r[d-2]),...r[d]];break;case 21:this.$=[Number(r[d])];break;case 22:x.setXAxisTitle(r[d]);break;case 23:x.setXAxisTitle(r[d-1]);break;case 24:x.setXAxisTitle({type:"text",text:""});break;case 25:x.setXAxisBand(r[d]);break;case 26:x.setXAxisRangeData(Number(r[d-2]),Number(r[d]));break;case 27:this.$=r[d-1];break;case 28:this.$=[r[d-2],...r[d]];break;case 29:this.$=[r[d]];break;case 30:x.setYAxisTitle(r[d]);break;case 31:x.setYAxisTitle(r[d-1]);break;case 32:x.setYAxisTitle({type:"text",text:""});break;case 33:x.setYAxisRangeData(Number(r[d-2]),Number(r[d]));break;case 37:this.$={text:r[d],type:"text"};break;case 38:this.$={text:r[d],type:"text"};break;case 39:this.$={text:r[d],type:"markdown"};break;case 40:this.$=r[d];break;case 41:this.$=r[d-1]+""+r[d];break}},"anonymous"),table:[e(t,i,{3:1,4:2,7:4,5:s,34:n,35:o,36:u}),{1:[3]},e(t,i,{4:2,7:4,3:8,5:s,34:n,35:o,36:u}),e(t,i,{4:2,7:4,6:9,3:10,5:s,8:[1,11],34:n,35:o,36:u}),{1:[2,4],9:12,10:[1,13],12:[1,14],14:[1,15],16:[1,16],18:[1,17],19:[1,18],21:[1,19],23:[1,20]},e(p,[2,34]),e(p,[2,35]),e(p,[2,36]),{1:[2,1]},e(t,i,{4:2,7:4,3:21,5:s,34:n,35:o,36:u}),{1:[2,3]},e(p,[2,5]),e(t,[2,7],{4:22,34:n,35:o,36:u}),{11:23,37:24,38:f,39:T,40:27,41:P,42:_,43:y,44:E,45:v,46:L,47:I,48:m,49:R,50:l},{11:39,13:38,24:W,27:O,29:40,30:41,37:24,38:f,39:T,40:27,41:P,42:_,43:y,44:E,45:v,46:L,47:I,48:m,49:R,50:l},{11:45,15:44,27:X,33:46,37:24,38:f,39:T,40:27,41:P,42:_,43:y,44:E,45:v,46:L,47:I,48:m,49:R,50:l},{11:49,17:48,24:Y,37:24,38:f,39:T,40:27,41:P,42:_,43:y,44:E,45:v,46:L,47:I,48:m,49:R,50:l},{11:52,17:51,24:Y,37:24,38:f,39:T,40:27,41:P,42:_,43:y,44:E,45:v,46:L,47:I,48:m,49:R,50:l},{20:[1,53]},{22:[1,54]},e(S,[2,18]),{1:[2,2]},e(S,[2,8]),e(S,[2,9]),e(U,[2,37],{40:55,41:P,42:_,43:y,44:E,45:v,46:L,47:I,48:m,49:R,50:l}),e(U,[2,38]),e(U,[2,39]),e(b,[2,40]),e(b,[2,42]),e(b,[2,43]),e(b,[2,44]),e(b,[2,45]),e(b,[2,46]),e(b,[2,47]),e(b,[2,48]),e(b,[2,49]),e(b,[2,50]),e(b,[2,51]),e(S,[2,10]),e(S,[2,22],{30:41,29:56,24:W,27:O}),e(S,[2,24]),e(S,[2,25]),{31:[1,57]},{11:59,32:58,37:24,38:f,39:T,40:27,41:P,42:_,43:y,44:E,45:v,46:L,47:I,48:m,49:R,50:l},e(S,[2,11]),e(S,[2,30],{33:60,27:X}),e(S,[2,32]),{31:[1,61]},e(S,[2,12]),{17:62,24:Y},{25:63,27:w},e(S,[2,14]),{17:65,24:Y},e(S,[2,16]),e(S,[2,17]),e(b,[2,41]),e(S,[2,23]),{27:[1,66]},{26:[1,67]},{26:[2,29],28:[1,68]},e(S,[2,31]),{27:[1,69]},e(S,[2,13]),{26:[1,70]},{26:[2,21],28:[1,71]},e(S,[2,15]),e(S,[2,26]),e(S,[2,27]),{11:59,32:72,37:24,38:f,39:T,40:27,41:P,42:_,43:y,44:E,45:v,46:L,47:I,48:m,49:R,50:l},e(S,[2,33]),e(S,[2,19]),{25:73,27:w},{26:[2,28]},{26:[2,20]}],defaultActions:{8:[2,1],10:[2,3],21:[2,2],72:[2,28],73:[2,20]},parseError:a(function(h,c){if(c.recoverable)this.trace(h);else{var g=new Error(h);throw g.hash=c,g}},"parseError"),parse:a(function(h){var c=this,g=[0],x=[],C=[null],r=[],rt=this.table,d="",ct=0,It=0,hi=2,Mt=1,li=r.slice.call(arguments,1),D=Object.create(this.lexer),$={yy:{}};for(var ft in this.yy)Object.prototype.hasOwnProperty.call(this.yy,ft)&&($.yy[ft]=this.yy[ft]);D.setInput(h,$.yy),$.yy.lexer=D,$.yy.parser=this,typeof D.yylloc>"u"&&(D.yylloc={});var pt=D.yylloc;r.push(pt);var ci=D.options&&D.options.ranges;typeof $.yy.parseError=="function"?this.parseError=$.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function ui(B){g.length=g.length-2*B,C.length=C.length-B,r.length=r.length-B}a(ui,"popStack");function Vt(){var B;return B=x.pop()||D.lex()||Mt,typeof B!="number"&&(B instanceof Array&&(x=B,B=x.pop()),B=c.symbols_[B]||B),B}a(Vt,"lex");for(var M,q,z,mt,G={},ut,N,Bt,gt;;){if(q=g[g.length-1],this.defaultActions[q]?z=this.defaultActions[q]:((M===null||typeof M>"u")&&(M=Vt()),z=rt[q]&&rt[q][M]),typeof z>"u"||!z.length||!z[0]){var yt="";gt=[];for(ut in rt[q])this.terminals_[ut]&&ut>hi&&gt.push("'"+this.terminals_[ut]+"'");D.showPosition?yt="Parse error on line "+(ct+1)+`:
1
+ import{s as gi,g as xi,q as Xt,p as di,a as fi,b as pi,_ as a,l as Yt,I as mi,e as yi,z as bi,D as _t,i as Ai,F as Nt,G as wi,K as Ci,aH as Si,R as Wt}from"./index-DHdiTAmV.js";import{i as _i}from"./init-Gi6I4Gst.js";import{o as ki}from"./ordinal-Cboi1Yqb.js";import{l as zt}from"./linear-BJLAYBLR.js";import"./defaultLocale-DX6XiGOO.js";function Ri(e,t,i){e=+e,t=+t,i=(n=arguments.length)<2?(t=e,e=0,1):n<3?1:+i;for(var s=-1,n=Math.max(0,Math.ceil((t-e)/i))|0,o=new Array(n);++s<n;)o[s]=e+s*i;return o}function bt(){var e=ki().unknown(void 0),t=e.domain,i=e.range,s=0,n=1,o,u,p=!1,f=0,T=0,P=.5;delete e.unknown;function _(){var y=t().length,E=n<s,v=E?n:s,L=E?s:n;o=(L-v)/Math.max(1,y-f+T*2),p&&(o=Math.floor(o)),v+=(L-v-o*(y-f))*P,u=o*(1-f),p&&(v=Math.round(v),u=Math.round(u));var I=Ri(y).map(function(m){return v+o*m});return i(E?I.reverse():I)}return e.domain=function(y){return arguments.length?(t(y),_()):t()},e.range=function(y){return arguments.length?([s,n]=y,s=+s,n=+n,_()):[s,n]},e.rangeRound=function(y){return[s,n]=y,s=+s,n=+n,p=!0,_()},e.bandwidth=function(){return u},e.step=function(){return o},e.round=function(y){return arguments.length?(p=!!y,_()):p},e.padding=function(y){return arguments.length?(f=Math.min(1,T=+y),_()):f},e.paddingInner=function(y){return arguments.length?(f=Math.min(1,y),_()):f},e.paddingOuter=function(y){return arguments.length?(T=+y,_()):T},e.align=function(y){return arguments.length?(P=Math.max(0,Math.min(1,y)),_()):P},e.copy=function(){return bt(t(),[s,n]).round(p).paddingInner(f).paddingOuter(T).align(P)},_i.apply(_(),arguments)}var At=(function(){var e=a(function(F,h,c,g){for(c=c||{},g=F.length;g--;c[F[g]]=h);return c},"o"),t=[1,10,12,14,16,18,19,21,23],i=[2,6],s=[1,3],n=[1,5],o=[1,6],u=[1,7],p=[1,5,10,12,14,16,18,19,21,23,34,35,36],f=[1,25],T=[1,26],P=[1,28],_=[1,29],y=[1,30],E=[1,31],v=[1,32],L=[1,33],I=[1,34],m=[1,35],R=[1,36],l=[1,37],W=[1,43],O=[1,42],X=[1,47],Y=[1,50],S=[1,10,12,14,16,18,19,21,23,34,35,36],U=[1,10,12,14,16,18,19,21,23,24,26,27,28,34,35,36],b=[1,10,12,14,16,18,19,21,23,24,26,27,28,34,35,36,41,42,43,44,45,46,47,48,49,50],w=[1,64],V={trace:a(function(){},"trace"),yy:{},symbols_:{error:2,start:3,eol:4,XYCHART:5,chartConfig:6,document:7,CHART_ORIENTATION:8,statement:9,title:10,text:11,X_AXIS:12,parseXAxis:13,Y_AXIS:14,parseYAxis:15,LINE:16,plotData:17,BAR:18,acc_title:19,acc_title_value:20,acc_descr:21,acc_descr_value:22,acc_descr_multiline_value:23,SQUARE_BRACES_START:24,commaSeparatedNumbers:25,SQUARE_BRACES_END:26,NUMBER_WITH_DECIMAL:27,COMMA:28,xAxisData:29,bandData:30,ARROW_DELIMITER:31,commaSeparatedTexts:32,yAxisData:33,NEWLINE:34,SEMI:35,EOF:36,alphaNum:37,STR:38,MD_STR:39,alphaNumToken:40,AMP:41,NUM:42,ALPHA:43,PLUS:44,EQUALS:45,MULT:46,DOT:47,BRKT:48,MINUS:49,UNDERSCORE:50,$accept:0,$end:1},terminals_:{2:"error",5:"XYCHART",8:"CHART_ORIENTATION",10:"title",12:"X_AXIS",14:"Y_AXIS",16:"LINE",18:"BAR",19:"acc_title",20:"acc_title_value",21:"acc_descr",22:"acc_descr_value",23:"acc_descr_multiline_value",24:"SQUARE_BRACES_START",26:"SQUARE_BRACES_END",27:"NUMBER_WITH_DECIMAL",28:"COMMA",31:"ARROW_DELIMITER",34:"NEWLINE",35:"SEMI",36:"EOF",38:"STR",39:"MD_STR",41:"AMP",42:"NUM",43:"ALPHA",44:"PLUS",45:"EQUALS",46:"MULT",47:"DOT",48:"BRKT",49:"MINUS",50:"UNDERSCORE"},productions_:[0,[3,2],[3,3],[3,2],[3,1],[6,1],[7,0],[7,2],[9,2],[9,2],[9,2],[9,2],[9,2],[9,3],[9,2],[9,3],[9,2],[9,2],[9,1],[17,3],[25,3],[25,1],[13,1],[13,2],[13,1],[29,1],[29,3],[30,3],[32,3],[32,1],[15,1],[15,2],[15,1],[33,3],[4,1],[4,1],[4,1],[11,1],[11,1],[11,1],[37,1],[37,2],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1]],performAction:a(function(h,c,g,x,C,r,rt){var d=r.length-1;switch(C){case 5:x.setOrientation(r[d]);break;case 9:x.setDiagramTitle(r[d].text.trim());break;case 12:x.setLineData({text:"",type:"text"},r[d]);break;case 13:x.setLineData(r[d-1],r[d]);break;case 14:x.setBarData({text:"",type:"text"},r[d]);break;case 15:x.setBarData(r[d-1],r[d]);break;case 16:this.$=r[d].trim(),x.setAccTitle(this.$);break;case 17:case 18:this.$=r[d].trim(),x.setAccDescription(this.$);break;case 19:this.$=r[d-1];break;case 20:this.$=[Number(r[d-2]),...r[d]];break;case 21:this.$=[Number(r[d])];break;case 22:x.setXAxisTitle(r[d]);break;case 23:x.setXAxisTitle(r[d-1]);break;case 24:x.setXAxisTitle({type:"text",text:""});break;case 25:x.setXAxisBand(r[d]);break;case 26:x.setXAxisRangeData(Number(r[d-2]),Number(r[d]));break;case 27:this.$=r[d-1];break;case 28:this.$=[r[d-2],...r[d]];break;case 29:this.$=[r[d]];break;case 30:x.setYAxisTitle(r[d]);break;case 31:x.setYAxisTitle(r[d-1]);break;case 32:x.setYAxisTitle({type:"text",text:""});break;case 33:x.setYAxisRangeData(Number(r[d-2]),Number(r[d]));break;case 37:this.$={text:r[d],type:"text"};break;case 38:this.$={text:r[d],type:"text"};break;case 39:this.$={text:r[d],type:"markdown"};break;case 40:this.$=r[d];break;case 41:this.$=r[d-1]+""+r[d];break}},"anonymous"),table:[e(t,i,{3:1,4:2,7:4,5:s,34:n,35:o,36:u}),{1:[3]},e(t,i,{4:2,7:4,3:8,5:s,34:n,35:o,36:u}),e(t,i,{4:2,7:4,6:9,3:10,5:s,8:[1,11],34:n,35:o,36:u}),{1:[2,4],9:12,10:[1,13],12:[1,14],14:[1,15],16:[1,16],18:[1,17],19:[1,18],21:[1,19],23:[1,20]},e(p,[2,34]),e(p,[2,35]),e(p,[2,36]),{1:[2,1]},e(t,i,{4:2,7:4,3:21,5:s,34:n,35:o,36:u}),{1:[2,3]},e(p,[2,5]),e(t,[2,7],{4:22,34:n,35:o,36:u}),{11:23,37:24,38:f,39:T,40:27,41:P,42:_,43:y,44:E,45:v,46:L,47:I,48:m,49:R,50:l},{11:39,13:38,24:W,27:O,29:40,30:41,37:24,38:f,39:T,40:27,41:P,42:_,43:y,44:E,45:v,46:L,47:I,48:m,49:R,50:l},{11:45,15:44,27:X,33:46,37:24,38:f,39:T,40:27,41:P,42:_,43:y,44:E,45:v,46:L,47:I,48:m,49:R,50:l},{11:49,17:48,24:Y,37:24,38:f,39:T,40:27,41:P,42:_,43:y,44:E,45:v,46:L,47:I,48:m,49:R,50:l},{11:52,17:51,24:Y,37:24,38:f,39:T,40:27,41:P,42:_,43:y,44:E,45:v,46:L,47:I,48:m,49:R,50:l},{20:[1,53]},{22:[1,54]},e(S,[2,18]),{1:[2,2]},e(S,[2,8]),e(S,[2,9]),e(U,[2,37],{40:55,41:P,42:_,43:y,44:E,45:v,46:L,47:I,48:m,49:R,50:l}),e(U,[2,38]),e(U,[2,39]),e(b,[2,40]),e(b,[2,42]),e(b,[2,43]),e(b,[2,44]),e(b,[2,45]),e(b,[2,46]),e(b,[2,47]),e(b,[2,48]),e(b,[2,49]),e(b,[2,50]),e(b,[2,51]),e(S,[2,10]),e(S,[2,22],{30:41,29:56,24:W,27:O}),e(S,[2,24]),e(S,[2,25]),{31:[1,57]},{11:59,32:58,37:24,38:f,39:T,40:27,41:P,42:_,43:y,44:E,45:v,46:L,47:I,48:m,49:R,50:l},e(S,[2,11]),e(S,[2,30],{33:60,27:X}),e(S,[2,32]),{31:[1,61]},e(S,[2,12]),{17:62,24:Y},{25:63,27:w},e(S,[2,14]),{17:65,24:Y},e(S,[2,16]),e(S,[2,17]),e(b,[2,41]),e(S,[2,23]),{27:[1,66]},{26:[1,67]},{26:[2,29],28:[1,68]},e(S,[2,31]),{27:[1,69]},e(S,[2,13]),{26:[1,70]},{26:[2,21],28:[1,71]},e(S,[2,15]),e(S,[2,26]),e(S,[2,27]),{11:59,32:72,37:24,38:f,39:T,40:27,41:P,42:_,43:y,44:E,45:v,46:L,47:I,48:m,49:R,50:l},e(S,[2,33]),e(S,[2,19]),{25:73,27:w},{26:[2,28]},{26:[2,20]}],defaultActions:{8:[2,1],10:[2,3],21:[2,2],72:[2,28],73:[2,20]},parseError:a(function(h,c){if(c.recoverable)this.trace(h);else{var g=new Error(h);throw g.hash=c,g}},"parseError"),parse:a(function(h){var c=this,g=[0],x=[],C=[null],r=[],rt=this.table,d="",ct=0,It=0,hi=2,Mt=1,li=r.slice.call(arguments,1),D=Object.create(this.lexer),$={yy:{}};for(var ft in this.yy)Object.prototype.hasOwnProperty.call(this.yy,ft)&&($.yy[ft]=this.yy[ft]);D.setInput(h,$.yy),$.yy.lexer=D,$.yy.parser=this,typeof D.yylloc>"u"&&(D.yylloc={});var pt=D.yylloc;r.push(pt);var ci=D.options&&D.options.ranges;typeof $.yy.parseError=="function"?this.parseError=$.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function ui(B){g.length=g.length-2*B,C.length=C.length-B,r.length=r.length-B}a(ui,"popStack");function Vt(){var B;return B=x.pop()||D.lex()||Mt,typeof B!="number"&&(B instanceof Array&&(x=B,B=x.pop()),B=c.symbols_[B]||B),B}a(Vt,"lex");for(var M,q,z,mt,G={},ut,N,Bt,gt;;){if(q=g[g.length-1],this.defaultActions[q]?z=this.defaultActions[q]:((M===null||typeof M>"u")&&(M=Vt()),z=rt[q]&&rt[q][M]),typeof z>"u"||!z.length||!z[0]){var yt="";gt=[];for(ut in rt[q])this.terminals_[ut]&&ut>hi&&gt.push("'"+this.terminals_[ut]+"'");D.showPosition?yt="Parse error on line "+(ct+1)+`:
2
2
  `+D.showPosition()+`
3
3
  Expecting `+gt.join(", ")+", got '"+(this.terminals_[M]||M)+"'":yt="Parse error on line "+(ct+1)+": Unexpected "+(M==Mt?"end of input":"'"+(this.terminals_[M]||M)+"'"),this.parseError(yt,{text:D.match,token:this.terminals_[M]||M,line:D.yylineno,loc:pt,expected:gt})}if(z[0]instanceof Array&&z.length>1)throw new Error("Parse Error: multiple actions possible at state: "+q+", token: "+M);switch(z[0]){case 1:g.push(M),C.push(D.yytext),r.push(D.yylloc),g.push(z[1]),M=null,It=D.yyleng,d=D.yytext,ct=D.yylineno,pt=D.yylloc;break;case 2:if(N=this.productions_[z[1]][1],G.$=C[C.length-N],G._$={first_line:r[r.length-(N||1)].first_line,last_line:r[r.length-1].last_line,first_column:r[r.length-(N||1)].first_column,last_column:r[r.length-1].last_column},ci&&(G._$.range=[r[r.length-(N||1)].range[0],r[r.length-1].range[1]]),mt=this.performAction.apply(G,[d,It,ct,$.yy,z[1],C,r].concat(li)),typeof mt<"u")return mt;N&&(g=g.slice(0,-1*N*2),C=C.slice(0,-1*N),r=r.slice(0,-1*N)),g.push(this.productions_[z[1]][0]),C.push(G.$),r.push(G._$),Bt=rt[g[g.length-2]][g[g.length-1]],g.push(Bt);break;case 3:return!0}}return!0},"parse")},k=(function(){var F={EOF:1,parseError:a(function(c,g){if(this.yy.parser)this.yy.parser.parseError(c,g);else throw new Error(c)},"parseError"),setInput:a(function(h,c){return this.yy=c||this.yy||{},this._input=h,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:a(function(){var h=this._input[0];this.yytext+=h,this.yyleng++,this.offset++,this.match+=h,this.matched+=h;var c=h.match(/(?:\r\n?|\n).*/g);return c?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),h},"input"),unput:a(function(h){var c=h.length,g=h.split(/(?:\r\n?|\n)/g);this._input=h+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-c),this.offset-=c;var x=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),g.length-1&&(this.yylineno-=g.length-1);var C=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:g?(g.length===x.length?this.yylloc.first_column:0)+x[x.length-g.length].length-g[0].length:this.yylloc.first_column-c},this.options.ranges&&(this.yylloc.range=[C[0],C[0]+this.yyleng-c]),this.yyleng=this.yytext.length,this},"unput"),more:a(function(){return this._more=!0,this},"more"),reject:a(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).
4
4
  `+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:a(function(h){this.unput(this.match.slice(h))},"less"),pastInput:a(function(){var h=this.matched.substr(0,this.matched.length-this.match.length);return(h.length>20?"...":"")+h.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:a(function(){var h=this.match;return h.length<20&&(h+=this._input.substr(0,20-h.length)),(h.substr(0,20)+(h.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:a(function(){var h=this.pastInput(),c=new Array(h.length+1).join("-");return h+this.upcomingInput()+`
package/dist/index.html CHANGED
@@ -4,7 +4,7 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>Compose</title>
7
- <script type="module" crossorigin src="/assets/index-Ceywghsu.js"></script>
7
+ <script type="module" crossorigin src="/assets/index-DHdiTAmV.js"></script>
8
8
  <link rel="stylesheet" crossorigin href="/assets/index-DKBsEUJ-.css">
9
9
  </head>
10
10
  <body>
@@ -34,11 +34,13 @@ export class VisionWriter {
34
34
  * @param {string} dataDir Path to the data directory (e.g. `.compose/data/`)
35
35
  * @param {object} [opts]
36
36
  * @param {number} [opts.port] Server port (default: resolvePort())
37
+ * @param {string} [opts.workspaceId] Workspace id; when set, sent as `X-Compose-Workspace-Id` on REST calls
37
38
  */
38
39
  constructor(dataDir, opts = {}) {
39
40
  this.filePath = path.join(dataDir, 'vision-state.json');
40
41
  this._dataDir = dataDir;
41
42
  this._port = opts.port ?? resolvePort();
43
+ this.workspaceId = opts.workspaceId;
42
44
  }
43
45
 
44
46
  // ---------------------------------------------------------------------------
@@ -126,7 +128,11 @@ export class VisionWriter {
126
128
  const res = await fetch(`${this._baseUrl}${urlPath}`, {
127
129
  ...opts,
128
130
  signal: controller.signal,
129
- headers: { 'Content-Type': 'application/json', ...opts.headers },
131
+ headers: {
132
+ 'Content-Type': 'application/json',
133
+ ...(this.workspaceId ? { 'X-Compose-Workspace-Id': this.workspaceId } : {}),
134
+ ...opts.headers,
135
+ },
130
136
  });
131
137
  if (!res.ok) {
132
138
  const body = await res.text().catch(() => '');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smartmemory/compose",
3
- "version": "0.1.9-beta",
3
+ "version": "0.1.10-beta",
4
4
  "description": "Structured AI dev pipeline — goal-to-product orchestration with gates, iteration loops, and feature lifecycle management.",
5
5
  "author": "SmartMemory",
6
6
  "license": "MIT",
@@ -154,21 +154,13 @@ export function toolGetBlockedItems() {
154
154
  export async function toolGetCurrentSession({ featureCode } = {}) {
155
155
  if (featureCode) {
156
156
  // Delegate to REST API for live session + lifecycle context
157
- return new Promise((resolve, reject) => {
158
- const url = new URL(`${_getComposeApi()}/api/session/current?featureCode=${encodeURIComponent(featureCode)}`);
159
- const req = http.request(
160
- { hostname: url.hostname, port: url.port, path: `${url.pathname}${url.search}`, method: 'GET' },
161
- (res) => {
162
- let buf = '';
163
- res.on('data', chunk => buf += chunk);
164
- res.on('end', () => {
165
- try { resolve(JSON.parse(buf)); } catch { resolve({ session: null }); }
166
- });
167
- },
168
- );
169
- req.on('error', () => resolve({ session: null }));
170
- req.end();
171
- });
157
+ try {
158
+ const { body } = await _httpRequest('GET',
159
+ `/api/session/current?featureCode=${encodeURIComponent(featureCode)}`);
160
+ return typeof body === 'object' && body !== null ? body : { session: null };
161
+ } catch {
162
+ return { session: null };
163
+ }
172
164
  }
173
165
  // Existing disk-read path (keep as-is)
174
166
  const sessions = loadSessions();
@@ -307,30 +299,20 @@ export async function toolValidateProject(args = {}) {
307
299
  }
308
300
 
309
301
  export async function toolBindSession({ featureCode }) {
310
- const postData = JSON.stringify({ featureCode });
311
- return new Promise((resolve, reject) => {
312
- const url = new URL(`${_getComposeApi()}/api/session/bind`);
313
- const req = http.request(
314
- { hostname: url.hostname, port: url.port, path: url.pathname, method: 'POST',
315
- headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(postData) } },
316
- (res) => {
317
- let buf = '';
318
- res.on('data', chunk => buf += chunk);
319
- res.on('end', () => {
320
- let parsed;
321
- try { parsed = JSON.parse(buf); } catch { parsed = { error: buf }; }
322
- if (res.statusCode >= 400) {
323
- reject(new Error(parsed.error || `HTTP ${res.statusCode}: ${buf}`));
324
- } else {
325
- resolve(parsed);
326
- }
327
- });
328
- },
329
- );
330
- req.on('error', (err) => reject(new Error(`Compose server unreachable: ${err.message}`)));
331
- req.write(postData);
332
- req.end();
333
- });
302
+ let result;
303
+ try {
304
+ result = await _httpRequest('POST', '/api/session/bind', { featureCode });
305
+ } catch (err) {
306
+ throw new Error(`Compose server unreachable: ${err.message}`);
307
+ }
308
+ const { status, body } = result;
309
+ if (status >= 400) {
310
+ const errMsg = (body && typeof body === 'object' && body.error)
311
+ ? body.error
312
+ : `HTTP ${status}: ${typeof body === 'string' ? body : JSON.stringify(body)}`;
313
+ throw new Error(errMsg);
314
+ }
315
+ return body;
334
316
  }
335
317
 
336
318
  // ---------------------------------------------------------------------------
@@ -341,57 +323,67 @@ function _getComposeApi() {
341
323
  return `http://127.0.0.1:${process.env.COMPOSE_PORT || process.env.PORT || 3001}`;
342
324
  }
343
325
 
344
- function _postLifecycle(itemId, action, body) {
345
- return new Promise((resolve, reject) => {
346
- const data = JSON.stringify(body);
347
- const url = new URL(`${_getComposeApi()}/api/vision/items/${itemId}/lifecycle/${action}`);
348
- const req = http.request(
349
- { hostname: url.hostname, port: url.port, path: url.pathname, method: 'POST',
350
- headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data) } },
351
- (res) => {
352
- let buf = '';
353
- res.on('data', (chunk) => buf += chunk);
354
- res.on('end', () => {
355
- let parsed;
356
- try { parsed = JSON.parse(buf); }
357
- catch { parsed = { error: buf }; }
358
- if (res.statusCode >= 400) {
359
- reject(new Error(parsed.error || `HTTP ${res.statusCode}: ${buf}`));
360
- } else {
361
- resolve(parsed);
362
- }
363
- });
364
- },
365
- );
366
- req.on('error', (err) => reject(new Error(`Compose server unreachable: ${err.message}`)));
367
- req.end(data);
368
- });
326
+ async function _postLifecycle(itemId, action, body) {
327
+ let result;
328
+ try {
329
+ result = await _httpRequest('POST', `/api/vision/items/${itemId}/lifecycle/${action}`, body);
330
+ } catch (err) {
331
+ throw new Error(`Compose server unreachable: ${err.message}`);
332
+ }
333
+ const { status, body: respBody } = result;
334
+ if (status >= 400) {
335
+ const errMsg = (respBody && typeof respBody === 'object' && respBody.error)
336
+ ? respBody.error
337
+ : `HTTP ${status}: ${typeof respBody === 'string' ? respBody : JSON.stringify(respBody)}`;
338
+ throw new Error(errMsg);
339
+ }
340
+ return respBody;
341
+ }
342
+
343
+ async function _postGate(gateId, action, body) {
344
+ let result;
345
+ try {
346
+ result = await _httpRequest('POST', `/api/vision/gates/${gateId}/${action}`, body);
347
+ } catch (err) {
348
+ throw new Error(`Compose server unreachable: ${err.message}`);
349
+ }
350
+ const { status, body: respBody } = result;
351
+ if (status >= 400) {
352
+ const errMsg = (respBody && typeof respBody === 'object' && respBody.error)
353
+ ? respBody.error
354
+ : `HTTP ${status}: ${typeof respBody === 'string' ? respBody : JSON.stringify(respBody)}`;
355
+ throw new Error(errMsg);
356
+ }
357
+ return respBody;
369
358
  }
370
359
 
371
- function _postGate(gateId, action, body) {
360
+ /**
361
+ * Centralized http.request wrapper for Compose REST calls from the MCP layer.
362
+ * Injects X-Compose-Workspace-Id from the current session binding when set.
363
+ * COMP-WORKSPACE-HTTP T5.
364
+ */
365
+ async function _httpRequest(method, urlPath, body = null) {
366
+ const port = process.env.COMPOSE_PORT || process.env.PORT || 3001;
367
+ const headers = { 'Content-Type': 'application/json' };
368
+ if (_binding?.id) headers['X-Compose-Workspace-Id'] = _binding.id;
369
+ let payload = null;
370
+ if (body !== null && body !== undefined) {
371
+ payload = JSON.stringify(body);
372
+ headers['Content-Length'] = Buffer.byteLength(payload);
373
+ }
374
+ const opts = { hostname: '127.0.0.1', port, path: urlPath, method, headers };
372
375
  return new Promise((resolve, reject) => {
373
- const data = JSON.stringify(body);
374
- const url = new URL(`${_getComposeApi()}/api/vision/gates/${gateId}/${action}`);
375
- const req = http.request(
376
- { hostname: url.hostname, port: url.port, path: url.pathname, method: 'POST',
377
- headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data) } },
378
- (res) => {
379
- let buf = '';
380
- res.on('data', (chunk) => buf += chunk);
381
- res.on('end', () => {
382
- let parsed;
383
- try { parsed = JSON.parse(buf); }
384
- catch { parsed = { error: buf }; }
385
- if (res.statusCode >= 400) {
386
- reject(new Error(parsed.error || `HTTP ${res.statusCode}: ${buf}`));
387
- } else {
388
- resolve(parsed);
389
- }
390
- });
391
- },
392
- );
393
- req.on('error', (err) => reject(new Error(`Compose server unreachable: ${err.message}`)));
394
- req.end(data);
376
+ const req = http.request(opts, (res) => {
377
+ let data = '';
378
+ res.on('data', (chunk) => { data += chunk; });
379
+ res.on('end', () => {
380
+ try { resolve({ status: res.statusCode, body: JSON.parse(data) }); }
381
+ catch { resolve({ status: res.statusCode, body: data }); }
382
+ });
383
+ });
384
+ req.on('error', reject);
385
+ if (payload !== null) req.write(payload);
386
+ req.end();
395
387
  });
396
388
  }
397
389
 
@@ -470,6 +462,13 @@ export function toolGetPendingGates({ itemId }) {
470
462
  // ---------------------------------------------------------------------------
471
463
  // Workspace binding (MCP session-scoped)
472
464
  // ---------------------------------------------------------------------------
465
+ //
466
+ // `_binding` is process-global by intent. Claude Code spawns ONE stdio MCP
467
+ // child per Claude session; the child's lifetime IS the session's lifetime.
468
+ // "Session-scoped" therefore equals "process-scoped" in this architecture.
469
+ // COMP-WORKSPACE-ID Decision 5 documents this. The HTTP server (port 4001)
470
+ // is the shared-across-sessions process, NOT this module — it gets the
471
+ // workspace per request via the X-Compose-Workspace-Id header instead.
473
472
 
474
473
  let _binding = null;
475
474
 
@@ -468,15 +468,18 @@ Output ONLY the Markdown content, no code fences.`;
468
468
  if (scope === 'feature' && featureCode) {
469
469
  try {
470
470
  const apiBase = `http://127.0.0.1:${process.env.PORT || 4001}`;
471
+ const wsHeaders = req.workspace?.id ? { 'X-Compose-Workspace-Id': req.workspace.id } : {};
471
472
  // Look up the vision item for this featureCode
472
- const itemsRes = await fetch(`${apiBase}/api/vision/items?keyword=${encodeURIComponent(featureCode)}`);
473
+ const itemsRes = await fetch(`${apiBase}/api/vision/items?keyword=${encodeURIComponent(featureCode)}`, {
474
+ headers: { ...wsHeaders },
475
+ });
473
476
  const itemsData = await itemsRes.json();
474
477
  const featureItem = itemsData.items?.find(i =>
475
478
  i.title?.startsWith(featureCode) || i.lifecycle?.featureCode === featureCode || i.featureCode === featureCode
476
479
  );
477
480
  await fetch(`${apiBase}/api/vision/gates`, {
478
481
  method: 'POST',
479
- headers: { 'Content-Type': 'application/json' },
482
+ headers: { 'Content-Type': 'application/json', ...wsHeaders },
480
483
  body: JSON.stringify({
481
484
  flowId: `design-${featureCode}`,
482
485
  stepId: 'design_gate',
package/server/index.js CHANGED
@@ -9,6 +9,8 @@ import { VisionServer } from './vision-server.js';
9
9
  import { SessionManager } from './session-manager.js';
10
10
  import { scanFeatures, seedFeatures, scanSubPackages, seedSubPackages, seedFromRoadmapGraph } from './feature-scan.js';
11
11
  import { attachGraphExportRoutes } from './graph-export.js';
12
+ import { attachWorkspaceRoutes } from './workspace-routes.js';
13
+ import { createWorkspaceMiddleware } from './workspace-middleware.js';
12
14
  import { getTargetRoot, getDataDir, ensureDataDir, loadProjectConfig, resolveProjectPath, switchProject } from './project-root.js';
13
15
 
14
16
  // Load project config and verify stratum capability matches reality
@@ -47,6 +49,9 @@ const app = express();
47
49
  app.use(cors({ origin: /^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?$/ }));
48
50
  app.use(express.json());
49
51
 
52
+ attachWorkspaceRoutes(app);
53
+ app.use(createWorkspaceMiddleware());
54
+
50
55
  app.get('/api/health', (_req, res) => res.json({ ok: true }));
51
56
  app.get('/api/status', (_req, res) => res.json({ session: 2, phase: '0.4-brainstorm', upSince: new Date().toISOString() }));
52
57
 
@@ -0,0 +1,120 @@
1
+ /**
2
+ * workspace-middleware.js — Express middleware that resolves the per-request
3
+ * workspace from the `X-Compose-Workspace-Id` header and attaches it as
4
+ * `req.workspace = { id, root, source, configPath? }`.
5
+ *
6
+ * Behavior (v1):
7
+ * - Exempt paths (/api/workspace, /api/project/switch, /api/health) bypass
8
+ * resolution entirely and get `req.workspace.source = 'exempt'`.
9
+ * - Header present + valid → resolveWorkspace() result, source carried through.
10
+ * - Header absent + soft fallback enabled → fallback to getTargetRoot() with
11
+ * the `X-Compose-Workspace-Fallback: true` response header. Applies to
12
+ * ALL methods in v1 (GET and POST alike).
13
+ * - Resolver errors map to HTTP via mapResolverErrorToResponse:
14
+ * WorkspaceUnknown → 400 { error, code, id }
15
+ * WorkspaceAmbiguous → 409 { error, code, candidates }
16
+ * WorkspaceIdCollision → 409 { error, code, roots }
17
+ * WorkspaceDiscoveryTooBroad → 400 { error, code }
18
+ * (anything else) → 500 { error: '...' }
19
+ *
20
+ * Roadmap: COMP-WORKSPACE-HTTP T3.
21
+ */
22
+ import { getTargetRoot } from './project-root.js';
23
+ import {
24
+ resolveWorkspace,
25
+ WorkspaceUnknown,
26
+ WorkspaceAmbiguous,
27
+ WorkspaceIdCollision,
28
+ } from '../lib/resolve-workspace.js';
29
+
30
+ const EXEMPT_PATHS = new Set([
31
+ '/api/workspace',
32
+ '/api/project/switch',
33
+ '/api/health',
34
+ ]);
35
+
36
+ /**
37
+ * Factory for the Express middleware.
38
+ *
39
+ * @param {object} [opts]
40
+ * @param {boolean} [opts.allowGetFallback=true] — when true, requests without
41
+ * the workspace header soft-fallback to the target root with a hint header.
42
+ * When false, missing header surfaces as a WorkspaceUnknown(null) → 400.
43
+ */
44
+ export function createWorkspaceMiddleware({ allowGetFallback = true } = {}) {
45
+ return function workspaceMiddleware(req, res, next) {
46
+ if (EXEMPT_PATHS.has(req.path)) {
47
+ req.workspace = { id: null, root: getTargetRoot(), source: 'exempt' };
48
+ return next();
49
+ }
50
+
51
+ const headerId = req.headers['x-compose-workspace-id'];
52
+
53
+ try {
54
+ if (!headerId) {
55
+ if (allowGetFallback) {
56
+ req.workspace = { id: null, root: getTargetRoot(), source: 'fallback' };
57
+ res.setHeader('X-Compose-Workspace-Fallback', 'true');
58
+ return next();
59
+ }
60
+ // Hard-fail mode: missing header is treated as an unknown id.
61
+ throw new WorkspaceUnknown(null);
62
+ }
63
+ const resolved = resolveWorkspace({
64
+ workspaceId: headerId,
65
+ cwd: getTargetRoot(),
66
+ });
67
+ req.workspace = resolved;
68
+ next();
69
+ } catch (err) {
70
+ mapResolverErrorToResponse(err, res, next);
71
+ }
72
+ };
73
+ }
74
+
75
+ /**
76
+ * Translate a resolver error into a JSON HTTP response.
77
+ *
78
+ * @param {Error} err
79
+ * @param {import('express').Response} res
80
+ * @param {import('express').NextFunction} [next] — optional; if provided,
81
+ * truly unknown errors are forwarded to it instead of generating a 500.
82
+ */
83
+ export function mapResolverErrorToResponse(err, res, next) {
84
+ if (err instanceof WorkspaceUnknown || err?.code === 'WorkspaceUnknown') {
85
+ return res.status(400).json({
86
+ error: err.message,
87
+ code: 'WorkspaceUnknown',
88
+ id: err.id ?? null,
89
+ });
90
+ }
91
+ if (err instanceof WorkspaceAmbiguous || err?.code === 'WorkspaceAmbiguous') {
92
+ return res.status(409).json({
93
+ error: err.message,
94
+ code: 'WorkspaceAmbiguous',
95
+ candidates: err.candidates ?? [],
96
+ });
97
+ }
98
+ if (err instanceof WorkspaceIdCollision || err?.code === 'WorkspaceIdCollision') {
99
+ return res.status(409).json({
100
+ error: err.message,
101
+ code: 'WorkspaceIdCollision',
102
+ roots: err.roots ?? [],
103
+ });
104
+ }
105
+ if (err?.code === 'WorkspaceDiscoveryTooBroad') {
106
+ return res.status(400).json({
107
+ error: err.message,
108
+ code: 'WorkspaceDiscoveryTooBroad',
109
+ });
110
+ }
111
+ if (typeof next === 'function') {
112
+ return next(err);
113
+ }
114
+ return res.status(500).json({
115
+ error: err?.message || 'Internal workspace resolver error',
116
+ code: err?.code || 'WorkspaceResolverInternalError',
117
+ });
118
+ }
119
+
120
+ export { EXEMPT_PATHS };
@@ -0,0 +1,25 @@
1
+ /**
2
+ * workspace-routes.js — boot-deterministic workspace identity endpoint.
3
+ *
4
+ * Mounts GET /api/workspace, returning the workspace resolved at boot time.
5
+ * No descendant discovery, no ambiguity check, no resolveWorkspace() call —
6
+ * this is the bootstrap endpoint the frontend hits BEFORE it has a workspace
7
+ * id to send (per design SD-2 of COMP-WORKSPACE-HTTP).
8
+ *
9
+ * Response shape: { id, root, source: 'boot' }
10
+ * - id — string, derived via deriveId({ root }).id
11
+ * - root — absolute path of the boot target
12
+ * - source — always 'boot' for this endpoint
13
+ */
14
+ import { getTargetRoot } from './project-root.js';
15
+ import { deriveId } from '../lib/discover-workspaces.js';
16
+
17
+ export function attachWorkspaceRoutes(app) {
18
+ app.get('/api/workspace', (req, res) => {
19
+ const root = getTargetRoot();
20
+ // deriveId returns { id, root, configPath } — destructure the id only.
21
+ // The route deliberately does NOT leak configPath to the client.
22
+ const { id } = deriveId({ root });
23
+ res.json({ id, root, source: 'boot' });
24
+ });
25
+ }
@@ -1 +0,0 @@
1
- import{aq as o,ar as n}from"./index-Ceywghsu.js";const t=(r,a)=>o.lang.round(n.parse(r)[a]);export{t as c};
@@ -1 +0,0 @@
1
- import{s as a,c as s,a as e,C as t}from"./chunk-4TB4RGXK-zdSZGRS2.js";import{_ as i}from"./index-Ceywghsu.js";import"./chunk-FMBD7UC4-CdkRK5Hx.js";import"./chunk-YZCP3GAM-DbNARKip.js";import"./chunk-55IACEB6-6zqzTZQQ.js";import"./chunk-EDXVE4YY-frd1Vwf-.js";var u={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{u as diagram};
@@ -1 +0,0 @@
1
- import{s as a,c as s,a as e,C as t}from"./chunk-4TB4RGXK-zdSZGRS2.js";import{_ as i}from"./index-Ceywghsu.js";import"./chunk-FMBD7UC4-CdkRK5Hx.js";import"./chunk-YZCP3GAM-DbNARKip.js";import"./chunk-55IACEB6-6zqzTZQQ.js";import"./chunk-EDXVE4YY-frd1Vwf-.js";var u={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{u as diagram};
@@ -1 +0,0 @@
1
- import{b as r}from"./graph-orv1XHGx.js";var e=4;function a(o){return r(o,e)}export{a as c};
@@ -1 +0,0 @@
1
- import{s as t,b as r,a,S as s}from"./chunk-OYMX7WX6-C6bMB0cf.js";import{_ as i}from"./index-Ceywghsu.js";import"./chunk-55IACEB6-6zqzTZQQ.js";import"./chunk-EDXVE4YY-frd1Vwf-.js";var l={parser:a,get db(){return new s(2)},renderer:r,styles:t,init:i(e=>{e.state||(e.state={}),e.state.arrowMarkerAbsolute=e.arrowMarkerAbsolute},"init")};export{l as diagram};