@smartmemory/compose 0.1.7-beta → 0.1.9-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.
- package/README.md +32 -5
- package/bin/compose.js +294 -34
- package/bin/git-hooks/post-commit.template +2 -1
- package/bin/git-hooks/pre-push.template +2 -1
- package/dist/assets/{_baseUniq-D-avYfn5.js → _baseUniq-3jW4HAOf.js} +1 -1
- package/dist/assets/{arc-BC4dfQ-X.js → arc-DzzDimyd.js} +1 -1
- package/dist/assets/{architectureDiagram-Q4EWVU46-BZmFXnGI.js → architectureDiagram-Q4EWVU46-CtAgwORz.js} +1 -1
- package/dist/assets/{blockDiagram-DXYQGD6D-DlfWSuux.js → blockDiagram-DXYQGD6D-Bryby0c_.js} +1 -1
- package/dist/assets/{c4Diagram-AHTNJAMY-Y__uJrRx.js → c4Diagram-AHTNJAMY-C7N9RTJ8.js} +1 -1
- package/dist/assets/channel-DDkv7DUd.js +1 -0
- package/dist/assets/{chunk-4BX2VUAB-BfMePfTp.js → chunk-4BX2VUAB-wijkFgZY.js} +1 -1
- package/dist/assets/{chunk-4TB4RGXK-BdlMSdEA.js → chunk-4TB4RGXK-zdSZGRS2.js} +1 -1
- package/dist/assets/{chunk-55IACEB6-vrQHZTdv.js → chunk-55IACEB6-6zqzTZQQ.js} +1 -1
- package/dist/assets/{chunk-EDXVE4YY-B8wioVlW.js → chunk-EDXVE4YY-frd1Vwf-.js} +1 -1
- package/dist/assets/{chunk-FMBD7UC4-Cd6Hrux2.js → chunk-FMBD7UC4-CdkRK5Hx.js} +1 -1
- package/dist/assets/{chunk-OYMX7WX6-CfrhdQXY.js → chunk-OYMX7WX6-C6bMB0cf.js} +1 -1
- package/dist/assets/{chunk-QZHKN3VN-B9JQerOU.js → chunk-QZHKN3VN-4vsxN3jq.js} +1 -1
- package/dist/assets/{chunk-YZCP3GAM-DFN9X99H.js → chunk-YZCP3GAM-DbNARKip.js} +1 -1
- package/dist/assets/classDiagram-6PBFFD2Q-J6ZTeCbW.js +1 -0
- package/dist/assets/classDiagram-v2-HSJHXN6E-J6ZTeCbW.js +1 -0
- package/dist/assets/clone-5MVZ89iV.js +1 -0
- package/dist/assets/{cose-bilkent-S5V4N54A-BAn0ap_E.js → cose-bilkent-S5V4N54A-BpXeV7Vj.js} +1 -1
- package/dist/assets/{dagre-KV5264BT-DyxnVq1g.js → dagre-KV5264BT-DQLu_W8r.js} +1 -1
- package/dist/assets/{diagram-5BDNPKRD-XCrzqski.js → diagram-5BDNPKRD-skaOoe5A.js} +1 -1
- package/dist/assets/{diagram-G4DWMVQ6-MBCAXft_.js → diagram-G4DWMVQ6-DezlfFH4.js} +1 -1
- package/dist/assets/{diagram-MMDJMWI5-DbtB2yS6.js → diagram-MMDJMWI5-BUu-v-wT.js} +1 -1
- package/dist/assets/{diagram-TYMM5635-Bb5NzX61.js → diagram-TYMM5635-CziQ6LPs.js} +1 -1
- package/dist/assets/{erDiagram-SMLLAGMA-CpIeCOh2.js → erDiagram-SMLLAGMA-BsAyOVTI.js} +1 -1
- package/dist/assets/{flowDiagram-DWJPFMVM-CHyoKnhW.js → flowDiagram-DWJPFMVM-CbYWJOLq.js} +1 -1
- package/dist/assets/{ganttDiagram-T4ZO3ILL-DErKteO_.js → ganttDiagram-T4ZO3ILL-CAwgDkLl.js} +1 -1
- package/dist/assets/{gitGraphDiagram-UUTBAWPF-KFVAtj2F.js → gitGraphDiagram-UUTBAWPF-DK4RlkjO.js} +1 -1
- package/dist/assets/{graph-CRnO_ifT.js → graph-orv1XHGx.js} +1 -1
- package/dist/assets/{index-DkRKLuNr.js → index-Ceywghsu.js} +143 -143
- package/dist/assets/{infoDiagram-42DDH7IO-BZFnuSp5.js → infoDiagram-42DDH7IO-DQyA75sK.js} +1 -1
- package/dist/assets/{ishikawaDiagram-UXIWVN3A-4Xe2Szde.js → ishikawaDiagram-UXIWVN3A-C-F_5q4k.js} +1 -1
- package/dist/assets/{journeyDiagram-VCZTEJTY-CZRByfS-.js → journeyDiagram-VCZTEJTY-Bj8UIvK-.js} +1 -1
- package/dist/assets/{kanban-definition-6JOO6SKY-B95sk6Fk.js → kanban-definition-6JOO6SKY-DZYr8Dp1.js} +1 -1
- package/dist/assets/{layout-BqNQzxWT.js → layout-CBaTKjpX.js} +1 -1
- package/dist/assets/{linear-CUh7qb64.js → linear-j1sI_SiN.js} +1 -1
- package/dist/assets/{min-wXgOS3ig.js → min-DtJISjld.js} +1 -1
- package/dist/assets/{mindmap-definition-QFDTVHPH-DB6iaAbO.js → mindmap-definition-QFDTVHPH-Bulb64RS.js} +1 -1
- package/dist/assets/{pieDiagram-DEJITSTG-CHkZHrTW.js → pieDiagram-DEJITSTG-D11keQxr.js} +1 -1
- package/dist/assets/{quadrantDiagram-34T5L4WZ-DoTEO8e3.js → quadrantDiagram-34T5L4WZ-BEcWQiEG.js} +1 -1
- package/dist/assets/{requirementDiagram-MS252O5E-Dn8peXYp.js → requirementDiagram-MS252O5E-Cbp23uDf.js} +1 -1
- package/dist/assets/{sankeyDiagram-XADWPNL6-DRXs6Ipb.js → sankeyDiagram-XADWPNL6-Dae1hMc5.js} +1 -1
- package/dist/assets/{sequenceDiagram-FGHM5R23-wBBYZ0aq.js → sequenceDiagram-FGHM5R23-C16abORi.js} +1 -1
- package/dist/assets/{stateDiagram-FHFEXIEX-DPlBNGmf.js → stateDiagram-FHFEXIEX-CbEtfhbx.js} +1 -1
- package/dist/assets/stateDiagram-v2-QKLJ7IA2-CyY84hEA.js +1 -0
- package/dist/assets/{timeline-definition-GMOUNBTQ-CbbyTlHk.js → timeline-definition-GMOUNBTQ-BV7JTNMI.js} +1 -1
- package/dist/assets/{vennDiagram-DHZGUBPP-Bj4GaFfj.js → vennDiagram-DHZGUBPP-DBZiT48j.js} +1 -1
- package/dist/assets/{wardley-RL74JXVD-RtNzq8KU.js → wardley-RL74JXVD-Cc8uoiL3.js} +37 -37
- package/dist/assets/{wardleyDiagram-NUSXRM2D-CDfE3zSj.js → wardleyDiagram-NUSXRM2D-DEYcWGo5.js} +1 -1
- package/dist/assets/{xychartDiagram-5P7HB3ND-CZXHHYD5.js → xychartDiagram-5P7HB3ND-bFhLXv2b.js} +1 -1
- package/dist/index.html +1 -1
- package/lib/build.js +193 -19
- package/lib/completion-writer.js +7 -4
- package/lib/deps.js +17 -6
- package/lib/discover-workspaces.js +109 -0
- package/lib/feature-events.js +3 -0
- package/lib/feature-writer.js +34 -22
- package/lib/followup-writer.js +556 -0
- package/lib/mcp-enforcement.js +173 -0
- package/lib/migrate-roadmap.js +4 -1
- package/lib/project-paths.js +36 -0
- package/lib/resolve-workspace.js +166 -0
- package/lib/review-lenses.js +23 -8
- package/lib/review-normalize.js +42 -3
- package/lib/roadmap-drift.js +54 -0
- package/lib/roadmap-gen.js +297 -27
- package/lib/roadmap-preservers.js +353 -0
- package/lib/step-prompt.js +15 -0
- package/lib/triage.js +2 -1
- package/lib/version-check.js +110 -0
- package/package.json +1 -2
- package/server/compose-mcp-tools.js +44 -8
- package/server/compose-mcp.js +66 -1
- package/server/project-root.js +4 -0
- package/server/vision-routes.js +51 -2
- package/templates/ROADMAP.md +6 -0
- package/dist/assets/channel-LRG9kHqJ.js +0 -1
- package/dist/assets/classDiagram-6PBFFD2Q-BC9a6pDE.js +0 -1
- package/dist/assets/classDiagram-v2-HSJHXN6E-BC9a6pDE.js +0 -1
- package/dist/assets/clone-dRxgFrBv.js +0 -1
- package/dist/assets/stateDiagram-v2-QKLJ7IA2-BW0ezXb4.js +0 -1
package/dist/assets/{wardleyDiagram-NUSXRM2D-CDfE3zSj.js → wardleyDiagram-NUSXRM2D-DEYcWGo5.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{s as zt,g as Lt,q as Xt,p as At,a as Ct,b as Et,_ as y,l as et,I as Tt,e as Yt,z as Bt,c as H,i as Rt}from"./index-
|
|
1
|
+
import{s as zt,g as Lt,q as Xt,p as At,a as Ct,b as Et,_ as y,l as et,I as Tt,e as Yt,z as Bt,c as H,i as Rt}from"./index-Ceywghsu.js";import{p as Ft}from"./chunk-4BX2VUAB-wijkFgZY.js";import{p as It}from"./wardley-RL74JXVD-Cc8uoiL3.js";import"./min-DtJISjld.js";import"./_baseUniq-3jW4HAOf.js";var q=y((a,r)=>{const e=a<=1?a*100:a;if(e<0||e>100)throw new Error(`${r} must be between 0-1 (decimal) or 0-100 (percentage). Received: ${a}`);return e},"toPercent"),Y=y((a,r,e)=>({x:q(r,`${e} evolution`),y:q(a,`${e} visibility`)}),"toCoordinates"),tt=y(a=>{if(a){if(a==="+<>")return"bidirectional";if(a==="+<")return"backward";if(a==="+>")return"forward"}},"getFlowFromPort"),Ot=y(a=>{if(!(a!=null&&a.startsWith("+")))return{};const r=/^\+'([^']*)'/.exec(a),e=r==null?void 0:r[1];return a.includes("<>")?{flow:"bidirectional",label:e}:a.includes("<")?{flow:"backward",label:e}:a.includes(">")?{flow:"forward",label:e}:{label:e}},"extractFlowFromArrow"),Wt=y((a,r)=>{if(Ft(a,r),a.size&&r.setSize(a.size.width,a.size.height),a.evolution){const e=a.evolution.stages.map(o=>o.secondName?`${o.name.trim()} / ${o.secondName.trim()}`:o.name.trim()),h=a.evolution.stages.filter(o=>o.boundary!==void 0).map(o=>o.boundary);r.updateAxes({stages:e,stageBoundaries:h})}if(a.anchors.forEach(e=>{const h=Y(e.visibility,e.evolution,`Anchor "${e.name}"`);r.addNode(e.name,e.name,h.x,h.y,"anchor")}),a.components.forEach(e=>{var v;const h=Y(e.visibility,e.evolution,`Component "${e.name}"`),o=e.label?(e.label.negX?-1:1)*e.label.offsetX:void 0,d=e.label?(e.label.negY?-1:1)*e.label.offsetY:void 0,m=(v=e.decorator)==null?void 0:v.strategy;r.addNode(e.name,e.name,h.x,h.y,"component",o,d,e.inertia,m)}),a.notes.forEach(e=>{const h=Y(e.visibility,e.evolution,`Note "${e.text}"`);r.addNote(e.text,h.x,h.y)}),a.pipelines.forEach(e=>{const h=r.getNode(e.parent);if(!h||typeof h.y!="number")throw new Error(`Pipeline "${e.parent}" must reference an existing component with coordinates.`);const o=h.y;r.startPipeline(e.parent),e.components.forEach(d=>{const m=`${e.parent}_${d.name}`,v=d.label?(d.label.negX?-1:1)*d.label.offsetX:void 0,g=d.label?(d.label.negY?-1:1)*d.label.offsetY:void 0,A=q(d.evolution,`Pipeline component "${d.name}" evolution`);r.addNode(m,d.name,A,o,"pipeline-component",v,g),r.addPipelineComponent(e.parent,m)})}),a.links.forEach(e=>{const h=!!e.arrow&&(e.arrow.includes("-.->")||e.arrow.includes(".-."));let o=tt(e.fromPort)??tt(e.toPort);const{flow:d,label:m}=Ot(e.arrow);!o&&d&&(o=d);const v=e.linkLabel,g=m??v;r.addLink(e.from,e.to,h,g,o)}),a.evolves.forEach(e=>{const h=r.getNode(e.component);if((h==null?void 0:h.y)!==void 0){const o=q(e.target,`Evolve target for "${e.component}"`);r.addTrend(e.component,o,h.y)}}),a.annotations.length>0){const e=a.annotations[0],h=Y(e.x,e.y,"Annotations box");r.setAnnotationsBox(h.x,h.y)}a.annotation.forEach(e=>{const h=Y(e.x,e.y,`Annotation ${e.number}`);r.addAnnotation(e.number,[{x:h.x,y:h.y}],e.text)}),a.accelerators.forEach(e=>{const h=Y(e.x,e.y,`Accelerator "${e.name}"`);r.addAccelerator(e.name,h.x,h.y)}),a.deaccelerators.forEach(e=>{const h=Y(e.x,e.y,`Deaccelerator "${e.name}"`);r.addDeaccelerator(e.name,h.x,h.y)})},"populateDb"),at={parser:{yy:void 0},parse:y(async a=>{var h;const r=await It("wardley",a);et.debug(r);const e=(h=at.parser)==null?void 0:h.yy;if(!e||typeof e.addNode!="function")throw new Error("parser.parser?.yy was not a WardleyDB. This is due to a bug within Mermaid, please report this issue at https://github.com/mermaid-js/mermaid/issues.");Wt(r,e)},"parse")},R,Dt=(R=class{constructor(){this.nodes=new Map,this.links=[],this.trends=new Map,this.pipelines=new Map,this.annotations=[],this.notes=[],this.accelerators=[],this.deaccelerators=[],this.axes={}}addNode(r){const e=this.nodes.get(r.id)??{id:r.id,label:r.label},h={...e,...r,className:r.className??e.className,labelOffsetX:r.labelOffsetX??e.labelOffsetX,labelOffsetY:r.labelOffsetY??e.labelOffsetY};this.nodes.set(r.id,h)}addLink(r){this.links.push(r)}addTrend(r){this.trends.set(r.nodeId,r)}startPipeline(r){this.pipelines.set(r,{nodeId:r,componentIds:[]});const e=this.nodes.get(r);e&&(e.isPipelineParent=!0)}addPipelineComponent(r,e){const h=this.pipelines.get(r);h&&h.componentIds.push(e);const o=this.nodes.get(e);o&&(o.inPipeline=!0)}addAnnotation(r){this.annotations.push(r)}addNote(r){this.notes.push(r)}addAccelerator(r){this.accelerators.push(r)}addDeaccelerator(r){this.deaccelerators.push(r)}setAnnotationsBox(r,e){this.annotationsBox={x:r,y:e}}setAxes(r){this.axes={...this.axes,...r}}setSize(r,e){this.size={width:r,height:e}}getNode(r){return this.nodes.get(r)}build(){const r=[];for(const e of this.nodes.values()){if(typeof e.x!="number"||typeof e.y!="number")throw new Error(`Node "${e.label}" is missing coordinates`);r.push(e)}return{nodes:r,links:[...this.links],trends:[...this.trends.values()],pipelines:[...this.pipelines.values()],annotations:[...this.annotations],notes:[...this.notes],accelerators:[...this.accelerators],deaccelerators:[...this.deaccelerators],annotationsBox:this.annotationsBox,axes:{...this.axes},size:this.size}}clear(){this.nodes.clear(),this.links=[],this.trends.clear(),this.pipelines.clear(),this.annotations=[],this.notes=[],this.accelerators=[],this.deaccelerators=[],this.annotationsBox=void 0,this.axes={},this.size=void 0}},y(R,"WardleyBuilder"),R),P=new Dt;function N(a){const r=H();return Rt(a.trim(),r)}y(N,"textSanitizer");function rt(){return H()["wardley-beta"]}y(rt,"getConfig");function ot(a,r,e,h,o,d,m,v,g){P.addNode({id:a,label:N(r),x:e,y:h,className:o,labelOffsetX:d,labelOffsetY:m,inertia:v,sourceStrategy:g})}y(ot,"addNode");function st(a,r,e=!1,h,o){P.addLink({source:a,target:r,dashed:e,label:h,flow:o})}y(st,"addLink");function nt(a,r,e){P.addTrend({nodeId:a,targetX:r,targetY:e})}y(nt,"addTrend");function it(a,r,e){P.addAnnotation({number:a,coordinates:r,text:e?N(e):void 0})}y(it,"addAnnotation");function dt(a,r,e){P.addNote({text:N(a),x:r,y:e})}y(dt,"addNote");function lt(a,r,e){P.addAccelerator({name:N(a),x:r,y:e})}y(lt,"addAccelerator");function ct(a,r,e){P.addDeaccelerator({name:N(a),x:r,y:e})}y(ct,"addDeaccelerator");function pt(a,r){P.setAnnotationsBox(a,r)}y(pt,"setAnnotationsBox");function ht(a,r){P.setSize(a,r)}y(ht,"setSize");function xt(a){P.startPipeline(a)}y(xt,"startPipeline");function ft(a,r){P.addPipelineComponent(a,r)}y(ft,"addPipelineComponent");function gt(a){const r={};a.xLabel&&(r.xLabel=N(a.xLabel)),a.yLabel&&(r.yLabel=N(a.yLabel)),a.stages&&(r.stages=a.stages.map(e=>N(e))),a.stageBoundaries&&(r.stageBoundaries=a.stageBoundaries),P.setAxes(r)}y(gt,"updateAxes");function ut(a){return P.getNode(a)}y(ut,"getNode");function yt(){return P.build()}y(yt,"getWardleyData");function mt(){P.clear(),Bt()}y(mt,"clear");var Gt={getConfig:rt,addNode:ot,addLink:st,addTrend:nt,addAnnotation:it,addNote:dt,addAccelerator:lt,addDeaccelerator:ct,setAnnotationsBox:pt,setSize:ht,startPipeline:xt,addPipelineComponent:ft,updateAxes:gt,getNode:ut,getWardleyData:yt,clear:mt,setAccTitle:Et,getAccTitle:Ct,setDiagramTitle:At,getDiagramTitle:Xt,getAccDescription:Lt,setAccDescription:zt},qt=["Genesis","Custom Built","Product","Commodity"],Ht=y(()=>{var r,e,h,o,d,m,v,g,A,M,b,z;const{themeVariables:a}=H();return{backgroundColor:((r=a.wardley)==null?void 0:r.backgroundColor)??a.background??"#fff",axisColor:((e=a.wardley)==null?void 0:e.axisColor)??"#000",axisTextColor:((h=a.wardley)==null?void 0:h.axisTextColor)??a.primaryTextColor??"#222",gridColor:((o=a.wardley)==null?void 0:o.gridColor)??"rgba(100, 100, 100, 0.2)",componentFill:((d=a.wardley)==null?void 0:d.componentFill)??"#fff",componentStroke:((m=a.wardley)==null?void 0:m.componentStroke)??"#000",componentLabelColor:((v=a.wardley)==null?void 0:v.componentLabelColor)??a.primaryTextColor??"#222",linkStroke:((g=a.wardley)==null?void 0:g.linkStroke)??"#000",evolutionStroke:((A=a.wardley)==null?void 0:A.evolutionStroke)??"#dc3545",annotationStroke:((M=a.wardley)==null?void 0:M.annotationStroke)??"#000",annotationTextColor:((b=a.wardley)==null?void 0:b.annotationTextColor)??a.primaryTextColor??"#222",annotationFill:((z=a.wardley)==null?void 0:z.annotationFill)??a.background??"#fff"}},"getTheme"),jt=y(()=>{const a=H()["wardley-beta"];return{width:(a==null?void 0:a.width)??900,height:(a==null?void 0:a.height)??600,padding:(a==null?void 0:a.padding)??48,nodeRadius:(a==null?void 0:a.nodeRadius)??6,nodeLabelOffset:(a==null?void 0:a.nodeLabelOffset)??8,axisFontSize:(a==null?void 0:a.axisFontSize)??12,labelFontSize:(a==null?void 0:a.labelFontSize)??10,showGrid:(a==null?void 0:a.showGrid)??!1,useMaxWidth:(a==null?void 0:a.useMaxWidth)??!0}},"getConfigValues"),_t=y((a,r,e,h)=>{var K,Q;et.debug(`Rendering Wardley map
|
|
2
2
|
`+a);const o=jt(),d=Ht(),m=o.nodeRadius*1.6,v=h.db,g=v.getWardleyData(),A=v.getDiagramTitle(),M=((K=g.size)==null?void 0:K.width)??o.width,b=((Q=g.size)==null?void 0:Q.height)??o.height,z=Tt(r);z.selectAll("*").remove(),Yt(z,b,M,o.useMaxWidth),z.attr("viewBox",`0 0 ${M} ${b}`);const S=z.append("g").attr("class","wardley-map"),j=z.append("defs");j.append("marker").attr("id",`arrow-${r}`).attr("viewBox","0 0 10 10").attr("refX",9).attr("refY",5).attr("markerWidth",6).attr("markerHeight",6).attr("orient","auto-start-reverse").append("path").attr("d","M 0 0 L 10 5 L 0 10 z").attr("fill",d.evolutionStroke).attr("stroke","none"),j.append("marker").attr("id",`link-arrow-end-${r}`).attr("viewBox","0 0 10 10").attr("refX",9).attr("refY",5).attr("markerWidth",5).attr("markerHeight",5).attr("orient","auto").append("path").attr("d","M 0 0 L 10 5 L 0 10 z").attr("fill",d.linkStroke).attr("stroke","none"),j.append("marker").attr("id",`link-arrow-start-${r}`).attr("viewBox","0 0 10 10").attr("refX",1).attr("refY",5).attr("markerWidth",5).attr("markerHeight",5).attr("orient","auto").append("path").attr("d","M 10 0 L 0 5 L 10 10 z").attr("fill",d.linkStroke).attr("stroke","none"),S.append("rect").attr("class","wardley-background").attr("width",M).attr("height",b).attr("fill",d.backgroundColor);const F=M-o.padding*2,I=b-o.padding*2;A&&S.append("text").attr("class","wardley-title").attr("x",M/2).attr("y",o.padding/2).attr("fill",d.axisTextColor).attr("font-size",o.axisFontSize*1.05).attr("font-weight","bold").attr("text-anchor","middle").attr("dominant-baseline","middle").text(A);const C=y(t=>o.padding+t/100*F,"projectX"),E=y(t=>b-o.padding-t/100*I,"projectY"),W=S.append("g").attr("class","wardley-axes");W.append("line").attr("x1",o.padding).attr("x2",M-o.padding).attr("y1",b-o.padding).attr("y2",b-o.padding).attr("stroke",d.axisColor).attr("stroke-width",1),W.append("line").attr("x1",o.padding).attr("x2",o.padding).attr("y1",o.padding).attr("y2",b-o.padding).attr("stroke",d.axisColor).attr("stroke-width",1);const wt=g.axes.xLabel??"Evolution",bt=g.axes.yLabel??"Visibility";W.append("text").attr("class","wardley-axis-label wardley-axis-label-x").attr("x",o.padding+F/2).attr("y",b-o.padding/4).attr("fill",d.axisTextColor).attr("font-size",o.axisFontSize).attr("font-weight","bold").attr("text-anchor","middle").text(wt),W.append("text").attr("class","wardley-axis-label wardley-axis-label-y").attr("x",o.padding/3).attr("y",o.padding+I/2).attr("fill",d.axisTextColor).attr("font-size",o.axisFontSize).attr("font-weight","bold").attr("text-anchor","middle").attr("transform",`rotate(-90 ${o.padding/3} ${o.padding+I/2})`).text(bt);const O=g.axes.stages&&g.axes.stages.length>0?g.axes.stages:qt;if(O.length>0){const t=S.append("g").attr("class","wardley-stages"),n=g.axes.stageBoundaries,s=[];if(n&&n.length===O.length){let i=0;n.forEach(p=>{s.push({start:i,end:p}),i=p})}else{const i=1/O.length;O.forEach((p,l)=>{s.push({start:l*i,end:(l+1)*i})})}O.forEach((i,p)=>{const l=s[p],x=o.padding+l.start*F,f=o.padding+l.end*F,u=(x+f)/2;p>0&&t.append("line").attr("x1",x).attr("x2",x).attr("y1",o.padding).attr("y2",b-o.padding).attr("stroke","#000").attr("stroke-width",1).attr("stroke-dasharray","5 5").attr("opacity",.8),t.append("text").attr("class","wardley-stage-label").attr("x",u).attr("y",b-o.padding/1.5).attr("fill",d.axisTextColor).attr("font-size",o.axisFontSize-2).attr("text-anchor","middle").text(i)})}if(o.showGrid){const t=S.append("g").attr("class","wardley-grid");for(let n=1;n<4;n++){const s=n/4,i=o.padding+F*s;t.append("line").attr("x1",i).attr("x2",i).attr("y1",o.padding).attr("y2",b-o.padding).attr("stroke",d.gridColor).attr("stroke-dasharray","2 6"),t.append("line").attr("x1",o.padding).attr("x2",M-o.padding).attr("y1",b-o.padding-I*s).attr("y2",b-o.padding-I*s).attr("stroke",d.gridColor).attr("stroke-dasharray","2 6")}}const c=new Map;if(g.nodes.forEach(t=>{c.set(t.id,{x:C(t.x),y:E(t.y),node:t})}),g.pipelines.length>0){const t=S.append("g").attr("class","wardley-pipelines"),n=S.append("g").attr("class","wardley-pipeline-links");g.pipelines.forEach(s=>{if(s.componentIds.length===0)return;const i=s.componentIds.map(f=>({id:f,pos:c.get(f),node:g.nodes.find(u=>u.id===f)})).filter(f=>f.pos&&f.node).sort((f,u)=>f.node.x-u.node.x);for(let f=0;f<i.length-1;f++){const u=i[f],w=i[f+1];n.append("line").attr("class","wardley-pipeline-evolution-link").attr("x1",u.pos.x).attr("y1",u.pos.y).attr("x2",w.pos.x).attr("y2",w.pos.y).attr("stroke",d.linkStroke).attr("stroke-width",1).attr("stroke-dasharray","4 4")}let p=1/0,l=-1/0,x=0;if(s.componentIds.forEach(f=>{const u=c.get(f);u&&(p=Math.min(p,u.x),l=Math.max(l,u.x),x=u.y)}),p!==1/0&&l!==-1/0){const u=o.nodeRadius*4,w=x-u/2,$=c.get(s.nodeId);if($){const X=(p+l)/2;$.x=X,$.y=w-m/6}t.append("rect").attr("class","wardley-pipeline-box").attr("x",p-15).attr("y",w).attr("width",l-p+30).attr("height",u).attr("fill","none").attr("stroke",d.axisColor).attr("stroke-width",1.5).attr("rx",4).attr("ry",4)}})}const Z=S.append("g").attr("class","wardley-links"),U=new Map;g.pipelines.forEach(t=>{U.set(t.nodeId,new Set(t.componentIds))});const J=g.links.filter(t=>{if(!c.has(t.source)||!c.has(t.target))return!1;const n=U.get(t.target);return!(n!=null&&n.has(t.source))});Z.selectAll("line").data(J).enter().append("line").attr("class",t=>`wardley-link${t.dashed?" wardley-link--dashed":""}`).attr("x1",t=>{const n=c.get(t.source),s=c.get(t.target),p=g.nodes.find(u=>u.id===t.source).isPipelineParent?m/Math.sqrt(2):o.nodeRadius,l=s.x-n.x,x=s.y-n.y,f=Math.sqrt(l*l+x*x);return n.x+l/f*p}).attr("y1",t=>{const n=c.get(t.source),s=c.get(t.target),p=g.nodes.find(u=>u.id===t.source).isPipelineParent?m/Math.sqrt(2):o.nodeRadius,l=s.x-n.x,x=s.y-n.y,f=Math.sqrt(l*l+x*x);return n.y+x/f*p}).attr("x2",t=>{const n=c.get(t.source),s=c.get(t.target),p=g.nodes.find(u=>u.id===t.target).isPipelineParent?m/Math.sqrt(2):o.nodeRadius,l=n.x-s.x,x=n.y-s.y,f=Math.sqrt(l*l+x*x);return s.x+l/f*p}).attr("y2",t=>{const n=c.get(t.source),s=c.get(t.target),p=g.nodes.find(u=>u.id===t.target).isPipelineParent?m/Math.sqrt(2):o.nodeRadius,l=n.x-s.x,x=n.y-s.y,f=Math.sqrt(l*l+x*x);return s.y+x/f*p}).attr("stroke",d.linkStroke).attr("stroke-width",1).attr("stroke-dasharray",t=>t.dashed?"6 6":null).attr("marker-end",t=>t.flow==="forward"||t.flow==="bidirectional"?`url(#link-arrow-end-${r})`:null).attr("marker-start",t=>t.flow==="backward"||t.flow==="bidirectional"?`url(#link-arrow-start-${r})`:null),Z.selectAll("text").data(J.filter(t=>t.label)).enter().append("text").attr("class","wardley-link-label").attr("x",t=>{const n=c.get(t.source),s=c.get(t.target),i=(n.x+s.x)/2,p=s.y-n.y,l=s.x-n.x,x=Math.sqrt(l*l+p*p),f=8,u=p/x;return i+u*f}).attr("y",t=>{const n=c.get(t.source),s=c.get(t.target),i=(n.y+s.y)/2,p=s.x-n.x,l=s.y-n.y,x=Math.sqrt(p*p+l*l),f=8,u=-p/x;return i+u*f}).attr("fill",d.axisTextColor).attr("font-size",o.labelFontSize).attr("text-anchor","middle").attr("dominant-baseline","middle").attr("transform",t=>{const n=c.get(t.source),s=c.get(t.target),i=(n.x+s.x)/2,p=(n.y+s.y)/2,l=s.x-n.x,x=s.y-n.y,f=Math.sqrt(l*l+x*x),u=8,w=x/f,$=-l/f,X=i+w*u,D=p+$*u;let B=Math.atan2(x,l)*180/Math.PI;return(B>90||B<-90)&&(B+=180),`rotate(${B} ${X} ${D})`}).text(t=>t.label);const kt=S.append("g").attr("class","wardley-trends"),Pt=g.trends.map(t=>{const n=c.get(t.nodeId);if(!n)return null;const s=C(t.targetX),i=E(t.targetY),p=s-n.x,l=i-n.y,x=Math.sqrt(p*p+l*l),f=o.nodeRadius+2,u=x>f?s-p/x*f:s,w=x>f?i-l/x*f:i;return{origin:n,targetX:s,targetY:i,adjustedX2:u,adjustedY2:w}}).filter(t=>t!==null);kt.selectAll("line").data(Pt).enter().append("line").attr("class","wardley-trend").attr("x1",t=>t.origin.x).attr("y1",t=>t.origin.y).attr("x2",t=>t.adjustedX2).attr("y2",t=>t.adjustedY2).attr("stroke",d.evolutionStroke).attr("stroke-width",1).attr("stroke-dasharray","4 4").attr("marker-end",`url(#arrow-${r})`);const L=S.append("g").attr("class","wardley-nodes").selectAll("g").data(g.nodes).enter().append("g").attr("class",t=>["wardley-node",t.className?`wardley-node--${t.className}`:""].filter(Boolean).join(" "));L.filter(t=>t.sourceStrategy==="outsource").append("circle").attr("class","wardley-outsource-overlay").attr("cx",t=>c.get(t.id).x).attr("cy",t=>c.get(t.id).y).attr("r",o.nodeRadius*2).attr("fill","#666").attr("stroke",d.componentStroke).attr("stroke-width",1),L.filter(t=>t.sourceStrategy==="buy").append("circle").attr("class","wardley-buy-overlay").attr("cx",t=>c.get(t.id).x).attr("cy",t=>c.get(t.id).y).attr("r",o.nodeRadius*2).attr("fill","#ccc").attr("stroke",d.componentStroke).attr("stroke-width",1),L.filter(t=>t.sourceStrategy==="build").append("circle").attr("class","wardley-build-overlay").attr("cx",t=>c.get(t.id).x).attr("cy",t=>c.get(t.id).y).attr("r",o.nodeRadius*2).attr("fill","#eee").attr("stroke","#000").attr("stroke-width",1);const T=L.filter(t=>t.sourceStrategy==="market");T.append("circle").attr("class","wardley-market-overlay").attr("cx",t=>c.get(t.id).x).attr("cy",t=>c.get(t.id).y).attr("r",o.nodeRadius*2).attr("fill","white").attr("stroke",d.componentStroke).attr("stroke-width",1),L.filter(t=>!t.isPipelineParent&&t.sourceStrategy!=="market"&&t.className!=="anchor").append("circle").attr("cx",t=>c.get(t.id).x).attr("cy",t=>c.get(t.id).y).attr("r",o.nodeRadius).attr("fill",d.componentFill).attr("stroke",d.componentStroke).attr("stroke-width",1);const _=o.nodeRadius*.7,k=o.nodeRadius*1.2;if(T.append("line").attr("class","wardley-market-line").attr("x1",t=>c.get(t.id).x).attr("y1",t=>c.get(t.id).y-k).attr("x2",t=>c.get(t.id).x-k*Math.cos(Math.PI/6)).attr("y2",t=>c.get(t.id).y+k*Math.sin(Math.PI/6)).attr("stroke",d.componentStroke).attr("stroke-width",1),T.append("line").attr("class","wardley-market-line").attr("x1",t=>c.get(t.id).x-k*Math.cos(Math.PI/6)).attr("y1",t=>c.get(t.id).y+k*Math.sin(Math.PI/6)).attr("x2",t=>c.get(t.id).x+k*Math.cos(Math.PI/6)).attr("y2",t=>c.get(t.id).y+k*Math.sin(Math.PI/6)).attr("stroke",d.componentStroke).attr("stroke-width",1),T.append("line").attr("class","wardley-market-line").attr("x1",t=>c.get(t.id).x+k*Math.cos(Math.PI/6)).attr("y1",t=>c.get(t.id).y+k*Math.sin(Math.PI/6)).attr("x2",t=>c.get(t.id).x).attr("y2",t=>c.get(t.id).y-k).attr("stroke",d.componentStroke).attr("stroke-width",1),T.append("circle").attr("class","wardley-market-dot").attr("cx",t=>c.get(t.id).x).attr("cy",t=>c.get(t.id).y-k).attr("r",_).attr("fill","white").attr("stroke",d.componentStroke).attr("stroke-width",2),T.append("circle").attr("class","wardley-market-dot").attr("cx",t=>c.get(t.id).x-k*Math.cos(Math.PI/6)).attr("cy",t=>c.get(t.id).y+k*Math.sin(Math.PI/6)).attr("r",_).attr("fill","white").attr("stroke",d.componentStroke).attr("stroke-width",2),T.append("circle").attr("class","wardley-market-dot").attr("cx",t=>c.get(t.id).x+k*Math.cos(Math.PI/6)).attr("cy",t=>c.get(t.id).y+k*Math.sin(Math.PI/6)).attr("r",_).attr("fill","white").attr("stroke",d.componentStroke).attr("stroke-width",2),L.filter(t=>t.isPipelineParent===!0).append("rect").attr("x",t=>c.get(t.id).x-m/2).attr("y",t=>c.get(t.id).y-m/2).attr("width",m).attr("height",m).attr("fill",d.componentFill).attr("stroke",d.componentStroke).attr("stroke-width",1),L.filter(t=>t.inertia===!0).append("line").attr("class","wardley-inertia").attr("x1",t=>{const n=c.get(t.id);let s=t.isPipelineParent?m/2+15:o.nodeRadius+15;return t.sourceStrategy&&(s+=o.nodeRadius+10),n.x+s}).attr("y1",t=>{const n=c.get(t.id),s=t.isPipelineParent?m:o.nodeRadius*2;return n.y-s/2}).attr("x2",t=>{const n=c.get(t.id);let s=t.isPipelineParent?m/2+15:o.nodeRadius+15;return t.sourceStrategy&&(s+=o.nodeRadius+10),n.x+s}).attr("y2",t=>{const n=c.get(t.id),s=t.isPipelineParent?m:o.nodeRadius*2;return n.y+s/2}).attr("stroke",d.componentStroke).attr("stroke-width",6),L.append("text").attr("x",t=>{const n=c.get(t.id);if(t.className==="anchor")return t.labelOffsetX!==void 0?n.x+t.labelOffsetX:n.x;let s=o.nodeLabelOffset;t.sourceStrategy&&t.labelOffsetX===void 0&&(s+=10);const i=t.labelOffsetX??s;return n.x+i}).attr("y",t=>{const n=c.get(t.id);if(t.className==="anchor")return t.labelOffsetY!==void 0?n.y+t.labelOffsetY:n.y-3;let s=-o.nodeLabelOffset;t.sourceStrategy&&t.labelOffsetY===void 0&&(s-=10);const i=t.labelOffsetY??s;return n.y+i}).attr("class","wardley-node-label").attr("fill",t=>t.className==="evolved"?d.evolutionStroke:t.className==="anchor"?"#000":d.componentLabelColor).attr("font-size",o.labelFontSize).attr("font-weight",t=>t.className==="anchor"?"bold":"normal").attr("text-anchor",t=>t.className==="anchor"?"middle":"start").attr("dominant-baseline",t=>t.className==="anchor"?"middle":"auto").text(t=>t.label),g.annotations.length>0){const t=S.append("g").attr("class","wardley-annotations");if(g.annotations.forEach(n=>{const s=n.coordinates.map(i=>({x:C(i.x),y:E(i.y)}));if(s.length>1)for(let i=0;i<s.length-1;i++)t.append("line").attr("class","wardley-annotation-line").attr("x1",s[i].x).attr("y1",s[i].y).attr("x2",s[i+1].x).attr("y2",s[i+1].y).attr("stroke",d.axisColor).attr("stroke-width",1.5).attr("stroke-dasharray","4 4");s.forEach(i=>{const p=t.append("g").attr("class","wardley-annotation");p.append("circle").attr("cx",i.x).attr("cy",i.y).attr("r",10).attr("fill","white").attr("stroke",d.axisColor).attr("stroke-width",1.5),p.append("text").attr("x",i.x).attr("y",i.y).attr("text-anchor","middle").attr("dominant-baseline","central").attr("font-size",10).attr("fill",d.axisTextColor).attr("font-weight","bold").text(n.number)})}),g.annotationsBox){let n=C(g.annotationsBox.x),s=E(g.annotationsBox.y);const i=10,p=16,l=11,x=t.append("g").attr("class","wardley-annotations-box"),f=[...g.annotations].filter(w=>w.text).sort((w,$)=>w.number-$.number),u=[];if(f.forEach((w,$)=>{const X=x.append("text").attr("x",n+i).attr("y",s+i+($+1)*p).attr("font-size",l).attr("fill",d.axisTextColor).attr("text-anchor","start").attr("dominant-baseline","middle").text(`${w.number}. ${w.text}`);u.push(X)}),u.length>0){let w=0,$=0;u.forEach(V=>{const G=V.node(),Mt=G.getComputedTextLength();w=Math.max(w,Mt);const Nt=G.getBBox();$=Math.max($,Nt.height)});const X=w+i*2+105,D=f.length*p+i*2+$/2,B=o.padding,vt=M-o.padding-X,St=o.padding,$t=b-o.padding-D;n=Math.max(B,Math.min(n,vt)),s=Math.max(St,Math.min(s,$t)),u.forEach((V,G)=>{V.attr("x",n+i).attr("y",s+i+(G+1)*p)}),x.insert("rect","text").attr("x",n).attr("y",s).attr("width",X).attr("height",D).attr("fill","white").attr("stroke",d.axisColor).attr("stroke-width",1.5).attr("rx",4).attr("ry",4)}}}if(g.notes.length>0){const t=S.append("g").attr("class","wardley-notes");g.notes.forEach(n=>{const s=C(n.x),i=E(n.y);t.append("text").attr("x",s).attr("y",i).attr("text-anchor","start").attr("font-size",11).attr("fill",d.axisTextColor).attr("font-weight","bold").text(n.text)})}if(g.accelerators.length>0){const t=S.append("g").attr("class","wardley-accelerators");g.accelerators.forEach(n=>{const s=C(n.x),i=E(n.y),p=60,l=30,x=20,f=`
|
|
3
3
|
M ${s} ${i-l/2}
|
|
4
4
|
L ${s+p-x} ${i-l/2}
|
package/dist/assets/{xychartDiagram-5P7HB3ND-CZXHHYD5.js → xychartDiagram-5P7HB3ND-bFhLXv2b.js}
RENAMED
|
@@ -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-
|
|
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&>.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-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-Ceywghsu.js"></script>
|
|
8
8
|
<link rel="stylesheet" crossorigin href="/assets/index-DKBsEUJ-.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
package/lib/build.js
CHANGED
|
@@ -13,7 +13,7 @@ import { join, resolve, dirname } from 'node:path';
|
|
|
13
13
|
import { fileURLToPath } from 'node:url';
|
|
14
14
|
import { homedir, tmpdir } from 'node:os';
|
|
15
15
|
import { execSync, execFileSync } from 'node:child_process';
|
|
16
|
-
import { createHash } from 'node:crypto';
|
|
16
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
17
17
|
|
|
18
18
|
import { StratumMcpClient, StratumError } from './stratum-mcp-client.js';
|
|
19
19
|
import { runAndNormalize, AgentTimeoutError, UserInterruptError, AgentError } from './result-normalizer.js';
|
|
@@ -32,6 +32,7 @@ import { SECTIONS_DIR } from './constants.js';
|
|
|
32
32
|
|
|
33
33
|
import YAML from 'yaml';
|
|
34
34
|
import { updateFeature, readFeature, writeFeature } from './feature-json.js';
|
|
35
|
+
import { loadFeaturesDir } from './project-paths.js';
|
|
35
36
|
import { evaluatePolicy } from '../server/policy-evaluator.js';
|
|
36
37
|
import { runTriage, isTriageStale } from './triage.js';
|
|
37
38
|
import { shouldRunCrossModel, LENS_DEFINITIONS } from './review-lenses.js';
|
|
@@ -553,9 +554,34 @@ export async function runBuild(featureCode, opts = {}) {
|
|
|
553
554
|
// Single resolver — used at every site that previously hardcoded
|
|
554
555
|
// `docs/features/<featureCode>/`. Callers must use this (not inline
|
|
555
556
|
// string concatenation) so the bug-mode path stays in sync.
|
|
557
|
+
// COMP-MCP-MIGRATION-2: feature-mode honors paths.features override.
|
|
558
|
+
const featuresDir = loadFeaturesDir(cwd);
|
|
556
559
|
const resolveItemDir = (code) => isBugMode
|
|
557
560
|
? join(cwd, 'docs', 'bugs', code)
|
|
558
|
-
: join(cwd,
|
|
561
|
+
: join(cwd, featuresDir, code);
|
|
562
|
+
|
|
563
|
+
// COMP-MCP-MIGRATION-1: per-build correlation ID stamped onto every audit
|
|
564
|
+
// row written during this run, so `executeShipStep`'s pre-stage scan can
|
|
565
|
+
// tell agent-emitted typed-tool events apart from anything else.
|
|
566
|
+
const build_id = randomUUID();
|
|
567
|
+
const buildStartedAt = new Date().toISOString();
|
|
568
|
+
const _priorBuildIdEnv = process.env.COMPOSE_BUILD_ID;
|
|
569
|
+
if (_priorBuildIdEnv !== undefined) {
|
|
570
|
+
// Concurrent in-process builds are not currently supported — env-based
|
|
571
|
+
// stamping cannot tell them apart. Warn loudly so the surrounding
|
|
572
|
+
// process tree picks it up; do not throw, since the prior env value
|
|
573
|
+
// may simply be inherited from the parent (CI, harness) and harmless.
|
|
574
|
+
// eslint-disable-next-line no-console
|
|
575
|
+
console.warn(
|
|
576
|
+
`[build] COMPOSE_BUILD_ID was already set ("${_priorBuildIdEnv}") when runBuild started. ` +
|
|
577
|
+
`Overriding for this build; concurrent in-process builds will mis-stamp events.`
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
process.env.COMPOSE_BUILD_ID = build_id;
|
|
581
|
+
const _restoreBuildIdEnv = () => {
|
|
582
|
+
if (_priorBuildIdEnv === undefined) delete process.env.COMPOSE_BUILD_ID;
|
|
583
|
+
else process.env.COMPOSE_BUILD_ID = _priorBuildIdEnv;
|
|
584
|
+
};
|
|
559
585
|
|
|
560
586
|
const featureDir = resolveItemDir(featureCode);
|
|
561
587
|
|
|
@@ -599,14 +625,14 @@ export async function runBuild(featureCode, opts = {}) {
|
|
|
599
625
|
// Bug mode skips pre-build triage entirely — triage is feature-shaped
|
|
600
626
|
// (writes feature.json, profile selection per feature complexity tiers).
|
|
601
627
|
if (!isBugMode && !opts.skipTriage && !opts.template) {
|
|
602
|
-
let cachedFeature = readFeature(cwd, featureCode);
|
|
603
|
-
if (cachedFeature?.profile && !isTriageStale(cwd, featureCode)) {
|
|
628
|
+
let cachedFeature = readFeature(cwd, featureCode, featuresDir);
|
|
629
|
+
if (cachedFeature?.profile && !isTriageStale(cwd, featureCode, featuresDir)) {
|
|
604
630
|
// Reuse cached profile
|
|
605
631
|
buildProfile = cachedFeature.profile;
|
|
606
632
|
console.log(`[triage] Using cached profile (tier ${cachedFeature.complexity ?? '?'}): ${JSON.stringify(buildProfile)}`);
|
|
607
633
|
} else {
|
|
608
634
|
// Run fresh triage
|
|
609
|
-
const triageResult = await runTriage(featureCode, { cwd });
|
|
635
|
+
const triageResult = await runTriage(featureCode, { cwd, featuresDir });
|
|
610
636
|
buildProfile = triageResult.profile;
|
|
611
637
|
console.log(`[triage] Tier ${triageResult.tier}: ${triageResult.rationale}`);
|
|
612
638
|
console.log(`[triage] Profile: ${JSON.stringify(buildProfile)}`);
|
|
@@ -622,13 +648,13 @@ export async function runBuild(featureCode, opts = {}) {
|
|
|
622
648
|
complexity: String(triageResult.tier),
|
|
623
649
|
profile: buildProfile,
|
|
624
650
|
triageTimestamp,
|
|
625
|
-
});
|
|
651
|
+
}, featuresDir);
|
|
626
652
|
} else {
|
|
627
653
|
updateFeature(cwd, featureCode, {
|
|
628
654
|
complexity: String(triageResult.tier),
|
|
629
655
|
profile: buildProfile,
|
|
630
656
|
triageTimestamp,
|
|
631
|
-
});
|
|
657
|
+
}, featuresDir);
|
|
632
658
|
}
|
|
633
659
|
}
|
|
634
660
|
}
|
|
@@ -663,7 +689,7 @@ export async function runBuild(featureCode, opts = {}) {
|
|
|
663
689
|
delete step.skip_reason;
|
|
664
690
|
} else if (buildProfile[needsKey] === false) {
|
|
665
691
|
// Disable step — mark as unconditionally skipped
|
|
666
|
-
const tier = readFeature(cwd, featureCode)?.complexity ?? '?';
|
|
692
|
+
const tier = readFeature(cwd, featureCode, featuresDir)?.complexity ?? '?';
|
|
667
693
|
step.skip_if = 'true';
|
|
668
694
|
step.skip_reason = `Skipped by triage (tier ${tier})`;
|
|
669
695
|
}
|
|
@@ -726,7 +752,7 @@ export async function runBuild(featureCode, opts = {}) {
|
|
|
726
752
|
// Update feature.json status to IN_PROGRESS (feature mode only;
|
|
727
753
|
// bug mode does not use feature.json).
|
|
728
754
|
if (!isBugMode) {
|
|
729
|
-
updateFeature(cwd, featureCode, { status: 'IN_PROGRESS' });
|
|
755
|
+
updateFeature(cwd, featureCode, { status: 'IN_PROGRESS' }, featuresDir);
|
|
730
756
|
}
|
|
731
757
|
|
|
732
758
|
// Hoisted for finally-block visibility
|
|
@@ -880,6 +906,20 @@ export async function runBuild(featureCode, opts = {}) {
|
|
|
880
906
|
// Dispatch loop — agents operate in agentCwd (which may differ from cwd for cross-repo builds)
|
|
881
907
|
// stepHistory accumulates context across steps so downstream steps don't re-explore
|
|
882
908
|
const stepHistory = [];
|
|
909
|
+
// COMP-MCP-MIGRATION: read enforcement.mcpForFeatureMgmt from settings.
|
|
910
|
+
// When true, step-prompt.js injects a hard instruction telling the agent
|
|
911
|
+
// to use typed MCP tools instead of free-text Edit/Write for ROADMAP /
|
|
912
|
+
// CHANGELOG / feature.json.
|
|
913
|
+
const enforceMcpForFeatureMgmt = (() => {
|
|
914
|
+
try {
|
|
915
|
+
if (existsSync(settingsPath)) {
|
|
916
|
+
const s = JSON.parse(readFileSync(settingsPath, 'utf-8'));
|
|
917
|
+
return Boolean(s?.enforcement?.mcpForFeatureMgmt);
|
|
918
|
+
}
|
|
919
|
+
} catch { /* default false */ }
|
|
920
|
+
return false;
|
|
921
|
+
})();
|
|
922
|
+
|
|
883
923
|
const context = {
|
|
884
924
|
cwd: agentCwd,
|
|
885
925
|
featureCode,
|
|
@@ -887,6 +927,10 @@ export async function runBuild(featureCode, opts = {}) {
|
|
|
887
927
|
contextDir: contextDirPath,
|
|
888
928
|
stepHistory,
|
|
889
929
|
mode,
|
|
930
|
+
enforceMcpForFeatureMgmt,
|
|
931
|
+
build_id,
|
|
932
|
+
buildStartedAt,
|
|
933
|
+
featuresDir,
|
|
890
934
|
...(isBugMode ? { bug_code: featureCode } : {}),
|
|
891
935
|
};
|
|
892
936
|
|
|
@@ -1787,7 +1831,7 @@ export async function runBuild(featureCode, opts = {}) {
|
|
|
1787
1831
|
// COMP-QA: persist filesChanged so `compose qa-scope` can read them post-build.
|
|
1788
1832
|
// Bug mode skips feature-json — bugs don't have feature.json (COMP-FIX-HARD T4).
|
|
1789
1833
|
if (!isBugMode) {
|
|
1790
|
-
updateFeature(cwd, featureCode, { status: 'COMPLETE', filesChanged: context.filesChanged ?? [] });
|
|
1834
|
+
updateFeature(cwd, featureCode, { status: 'COMPLETE', filesChanged: context.filesChanged ?? [] }, featuresDir);
|
|
1791
1835
|
}
|
|
1792
1836
|
const termState = readActiveBuild(dataDir);
|
|
1793
1837
|
if (termState) {
|
|
@@ -1798,7 +1842,7 @@ export async function runBuild(featureCode, opts = {}) {
|
|
|
1798
1842
|
buildStatus = 'killed';
|
|
1799
1843
|
console.log('\nBuild killed.');
|
|
1800
1844
|
await visionWriter.updateItemStatus(itemId, 'killed');
|
|
1801
|
-
if (!isBugMode) updateFeature(cwd, featureCode, { status: 'PLANNED' });
|
|
1845
|
+
if (!isBugMode) updateFeature(cwd, featureCode, { status: 'PLANNED' }, featuresDir);
|
|
1802
1846
|
const termState = readActiveBuild(dataDir);
|
|
1803
1847
|
if (termState) {
|
|
1804
1848
|
writeActiveBuild(dataDir, { ...termState, status: 'aborted', completedAt: new Date().toISOString() });
|
|
@@ -1807,7 +1851,7 @@ export async function runBuild(featureCode, opts = {}) {
|
|
|
1807
1851
|
// Ship failure or other explicit failure — write terminal state
|
|
1808
1852
|
console.log('\nBuild failed.');
|
|
1809
1853
|
await visionWriter.updateItemStatus(itemId, 'failed');
|
|
1810
|
-
if (!isBugMode) updateFeature(cwd, featureCode, { status: 'PLANNED' });
|
|
1854
|
+
if (!isBugMode) updateFeature(cwd, featureCode, { status: 'PLANNED' }, featuresDir);
|
|
1811
1855
|
const termState = readActiveBuild(dataDir);
|
|
1812
1856
|
if (termState) {
|
|
1813
1857
|
writeActiveBuild(dataDir, { ...termState, status: 'failed', completedAt: new Date().toISOString() });
|
|
@@ -1832,7 +1876,7 @@ export async function runBuild(featureCode, opts = {}) {
|
|
|
1832
1876
|
const currentPhase = stepHistory.length > 0
|
|
1833
1877
|
? stepHistory[stepHistory.length - 1].stepId
|
|
1834
1878
|
: 'build';
|
|
1835
|
-
const stalenessResults = checkStaleness(
|
|
1879
|
+
const stalenessResults = checkStaleness(resolveItemDir(featureCode), currentPhase);
|
|
1836
1880
|
buildSignals.doc_freshness = stalenessResults;
|
|
1837
1881
|
} catch { /* staleness check is optional — skip on error */ }
|
|
1838
1882
|
|
|
@@ -1964,6 +2008,9 @@ export async function runBuild(featureCode, opts = {}) {
|
|
|
1964
2008
|
}
|
|
1965
2009
|
progress.finish();
|
|
1966
2010
|
await stratum.close();
|
|
2011
|
+
// COMP-MCP-MIGRATION-1: restore COMPOSE_BUILD_ID env to its prior value
|
|
2012
|
+
// (or unset) so subsequent processes / tests don't inherit a stale UUID.
|
|
2013
|
+
_restoreBuildIdEnv();
|
|
1967
2014
|
}
|
|
1968
2015
|
}
|
|
1969
2016
|
|
|
@@ -2021,10 +2068,12 @@ export function maybeEmitSectionsAfterPlanGate(stepId, featureDir, opts = {}) {
|
|
|
2021
2068
|
* Returns a PhaseResult-shaped object.
|
|
2022
2069
|
*/
|
|
2023
2070
|
export async function executeShipStep(featureCode, agentCwd, cwd, context, description, progress) {
|
|
2024
|
-
// COMP-FIX-HARD T4: bug mode stages docs/bugs/<code>/ instead of
|
|
2071
|
+
// COMP-FIX-HARD T4: bug mode stages docs/bugs/<code>/ instead of <featuresDir>/<code>/
|
|
2072
|
+
// COMP-MCP-MIGRATION-2: feature mode honors paths.features override.
|
|
2073
|
+
const featuresDir = loadFeaturesDir(cwd);
|
|
2025
2074
|
const featureDir = context?.mode === 'bug'
|
|
2026
2075
|
? `docs/bugs/${featureCode}`
|
|
2027
|
-
:
|
|
2076
|
+
: `${featuresDir}/${featureCode}`;
|
|
2028
2077
|
|
|
2029
2078
|
try {
|
|
2030
2079
|
// 0. Check if we're in a git repository — if not, skip git operations
|
|
@@ -2063,10 +2112,12 @@ export async function executeShipStep(featureCode, agentCwd, cwd, context, descr
|
|
|
2063
2112
|
for (const f of context.filesChanged) filesToStage.add(f);
|
|
2064
2113
|
}
|
|
2065
2114
|
|
|
2066
|
-
// Also catch any unstaged changes via git
|
|
2115
|
+
// Also catch any unstaged changes via git, plus already-staged files
|
|
2116
|
+
// (so MCP enforcement can scan files that an agent staged via `git add`
|
|
2117
|
+
// before reaching ship — COMP-MCP-MIGRATION-1).
|
|
2067
2118
|
try {
|
|
2068
2119
|
const dirty = execSync(
|
|
2069
|
-
'git diff --name-only HEAD 2>/dev/null; git ls-files --others --exclude-standard 2>/dev/null',
|
|
2120
|
+
'git diff --name-only HEAD 2>/dev/null; git ls-files --others --exclude-standard 2>/dev/null; git diff --cached --name-only 2>/dev/null',
|
|
2070
2121
|
{ cwd: agentCwd, encoding: 'utf-8', timeout: 5000 }
|
|
2071
2122
|
).trim();
|
|
2072
2123
|
if (dirty) {
|
|
@@ -2095,6 +2146,58 @@ export async function executeShipStep(featureCode, agentCwd, cwd, context, descr
|
|
|
2095
2146
|
};
|
|
2096
2147
|
}
|
|
2097
2148
|
|
|
2149
|
+
// COMP-MCP-MIGRATION-1: pre-stage MCP enforcement scan. When
|
|
2150
|
+
// enforcement.mcpForFeatureMgmt is 'block' or 'log', verify every
|
|
2151
|
+
// dirty guarded path (ROADMAP.md, CHANGELOG.md, feature.json) has at
|
|
2152
|
+
// least one matching typed-tool audit row stamped with this build's
|
|
2153
|
+
// build_id. Block mode rejects unauthorized edits; log mode emits
|
|
2154
|
+
// decision events but proceeds.
|
|
2155
|
+
if (context?.build_id) {
|
|
2156
|
+
try {
|
|
2157
|
+
const { readEnforcementMode, scanGuarded, enforcementError } =
|
|
2158
|
+
await import('./mcp-enforcement.js');
|
|
2159
|
+
const { readEvents } = await import('./feature-events.js');
|
|
2160
|
+
const dataDir = join(cwd, '.compose', 'data');
|
|
2161
|
+
const mode = readEnforcementMode(dataDir);
|
|
2162
|
+
if (mode !== 'off') {
|
|
2163
|
+
const events = readEvents(cwd, { since: context.buildStartedAt });
|
|
2164
|
+
const { violations } = scanGuarded({
|
|
2165
|
+
dirtyFiles: featureFiles,
|
|
2166
|
+
featuresDir: context.featuresDir ?? loadFeaturesDir(cwd),
|
|
2167
|
+
buildId: context.build_id,
|
|
2168
|
+
events,
|
|
2169
|
+
});
|
|
2170
|
+
if (violations.length > 0) {
|
|
2171
|
+
// Emit a decision event for visibility in either mode
|
|
2172
|
+
try {
|
|
2173
|
+
const { emitDecisionEvent } = await import('../server/decision-event-emit.js');
|
|
2174
|
+
emitDecisionEvent(() => {}, {
|
|
2175
|
+
type: 'mcp_enforcement_violation',
|
|
2176
|
+
featureCode: context.featureCode,
|
|
2177
|
+
build_id: context.build_id,
|
|
2178
|
+
mode,
|
|
2179
|
+
violations,
|
|
2180
|
+
timestamp: new Date().toISOString(),
|
|
2181
|
+
});
|
|
2182
|
+
} catch { /* decision event emit best-effort */ }
|
|
2183
|
+
// eslint-disable-next-line no-console
|
|
2184
|
+
console.warn(
|
|
2185
|
+
`[ship] MCP enforcement (${mode}): ${violations.length} guarded path(s) without typed-tool events:` +
|
|
2186
|
+
violations.map(v => `\n - ${v.path}`).join('')
|
|
2187
|
+
);
|
|
2188
|
+
if (mode === 'block') {
|
|
2189
|
+
throw enforcementError(violations);
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
} catch (err) {
|
|
2194
|
+
if (err && err.code === 'MCP_ENFORCEMENT_VIOLATION') throw err;
|
|
2195
|
+
// Other failures inside the scan are best-effort — log and proceed
|
|
2196
|
+
// eslint-disable-next-line no-console
|
|
2197
|
+
console.warn(`[ship] MCP enforcement scan errored (proceeding): ${err.message}`);
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2098
2201
|
// 3. Stage files
|
|
2099
2202
|
if (progress) progress.toolUse('ship', `Staging ${featureFiles.length} files...`);
|
|
2100
2203
|
for (const f of featureFiles) {
|
|
@@ -2157,6 +2260,31 @@ export async function executeShipStep(featureCode, agentCwd, cwd, context, descr
|
|
|
2157
2260
|
// If we got nothing from show, fall back to the staged list (still best-effort).
|
|
2158
2261
|
if (filesChanged.length === 0 && sha) filesChanged = stagedFiles;
|
|
2159
2262
|
|
|
2263
|
+
// COMP-MCP-MIGRATION: write a commit-bound completion record via the
|
|
2264
|
+
// typed writer. The writer flips feature.status to COMPLETE atomically
|
|
2265
|
+
// and regenerates ROADMAP.md. Best-effort: completion failures must not
|
|
2266
|
+
// downgrade the ship outcome since the commit itself succeeded.
|
|
2267
|
+
let completionWarning = null;
|
|
2268
|
+
if (sha && featureCode) {
|
|
2269
|
+
try {
|
|
2270
|
+
const { recordCompletion } = await import('./completion-writer.js');
|
|
2271
|
+
await recordCompletion(cwd, {
|
|
2272
|
+
feature_code: featureCode,
|
|
2273
|
+
commit_sha: sha,
|
|
2274
|
+
tests_pass: true,
|
|
2275
|
+
files_changed: filesChanged,
|
|
2276
|
+
notes: shortDesc,
|
|
2277
|
+
});
|
|
2278
|
+
if (progress) progress.toolUse('ship', `Recorded completion for ${featureCode}`);
|
|
2279
|
+
} catch (err) {
|
|
2280
|
+
completionWarning = err.code === 'STATUS_FLIP_AFTER_COMPLETION_RECORDED'
|
|
2281
|
+
? `completion recorded but status flip failed: ${err.message}`
|
|
2282
|
+
: `completion record failed (${err.code || 'UNKNOWN'}): ${err.message}`;
|
|
2283
|
+
// eslint-disable-next-line no-console
|
|
2284
|
+
console.warn(`[build/ship] ${featureCode}: ${completionWarning}`);
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
|
|
2160
2288
|
return {
|
|
2161
2289
|
phase: 'ship',
|
|
2162
2290
|
artifact: sha ?? '',
|
|
@@ -2166,6 +2294,7 @@ export async function executeShipStep(featureCode, agentCwd, cwd, context, descr
|
|
|
2166
2294
|
: `Committed: ${commitMsg} (${stagedFiles.length} files)`,
|
|
2167
2295
|
commit: sha,
|
|
2168
2296
|
filesChanged,
|
|
2297
|
+
...(completionWarning ? { completionWarning } : {}),
|
|
2169
2298
|
};
|
|
2170
2299
|
|
|
2171
2300
|
} catch (err) {
|
|
@@ -2200,6 +2329,46 @@ export async function executeShipStep(featureCode, agentCwd, cwd, context, descr
|
|
|
2200
2329
|
* @returns {Promise<object>} updated ReviewResult with crossModelSynthesis field,
|
|
2201
2330
|
* or original mergedResult if skipped
|
|
2202
2331
|
*/
|
|
2332
|
+
/**
|
|
2333
|
+
* STRAT-REV-FU-1: compute total changed-line count from `git diff --shortstat`.
|
|
2334
|
+
* Returns null if the command fails (no git, not a repo, etc.) so the caller
|
|
2335
|
+
* falls back to file-count-only classification.
|
|
2336
|
+
*
|
|
2337
|
+
* @param {string} cwd
|
|
2338
|
+
* @returns {Promise<number|null>}
|
|
2339
|
+
*/
|
|
2340
|
+
/**
|
|
2341
|
+
* Parse `git diff --shortstat` output to a total changed-line count.
|
|
2342
|
+
* Exported for unit testing; production use goes through computeChangedLineCount.
|
|
2343
|
+
*
|
|
2344
|
+
* Shapes:
|
|
2345
|
+
* ` 3 files changed, 10 insertions(+), 5 deletions(-)` → 15
|
|
2346
|
+
* ` 1 file changed, 250 insertions(+)` → 250
|
|
2347
|
+
* ` 1 file changed, 250 deletions(-)` → 250
|
|
2348
|
+
* `` → 0 (no changes)
|
|
2349
|
+
* `garbage` → null (unknown shape)
|
|
2350
|
+
*
|
|
2351
|
+
* @param {string} stdout
|
|
2352
|
+
* @returns {number|null}
|
|
2353
|
+
*/
|
|
2354
|
+
export function parseShortstat(stdout) {
|
|
2355
|
+
const ins = stdout.match(/(\d+)\s+insertion/);
|
|
2356
|
+
const del = stdout.match(/(\d+)\s+deletion/);
|
|
2357
|
+
if (!ins && !del) {
|
|
2358
|
+
return stdout.trim() === '' ? 0 : null;
|
|
2359
|
+
}
|
|
2360
|
+
return (ins ? parseInt(ins[1], 10) : 0) + (del ? parseInt(del[1], 10) : 0);
|
|
2361
|
+
}
|
|
2362
|
+
|
|
2363
|
+
async function computeChangedLineCount(cwd) {
|
|
2364
|
+
try {
|
|
2365
|
+
const out = execSync('git diff --no-color --shortstat HEAD', { cwd, encoding: 'utf8', timeout: 5000 });
|
|
2366
|
+
return parseShortstat(out);
|
|
2367
|
+
} catch {
|
|
2368
|
+
return null;
|
|
2369
|
+
}
|
|
2370
|
+
}
|
|
2371
|
+
|
|
2203
2372
|
async function runCrossModelReview(mergedResult, filesChanged, cwd, stratum, streamWriter, opts = {}) {
|
|
2204
2373
|
// --- Opt-out checks ---
|
|
2205
2374
|
if (opts.skipCrossModel) {
|
|
@@ -2210,12 +2379,17 @@ async function runCrossModelReview(mergedResult, filesChanged, cwd, stratum, str
|
|
|
2210
2379
|
if (streamWriter) streamWriter.write({ type: 'cross_model_review', status: 'skipped', reason: 'COMPOSE_CROSS_MODEL=0' });
|
|
2211
2380
|
return mergedResult;
|
|
2212
2381
|
}
|
|
2213
|
-
|
|
2382
|
+
// STRAT-REV-FU-1: compute line count so single-file mega-refactors also trigger cross-model.
|
|
2383
|
+
// Cheap one-shot — git diff --shortstat over the working tree at cwd. Failures fall back to null
|
|
2384
|
+
// (file-count-only gate, original behavior).
|
|
2385
|
+
const lineCount = await computeChangedLineCount(cwd);
|
|
2386
|
+
|
|
2387
|
+
if (!shouldRunCrossModel(filesChanged, lineCount)) {
|
|
2214
2388
|
return mergedResult; // small/medium diff — skip silently
|
|
2215
2389
|
}
|
|
2216
2390
|
|
|
2217
2391
|
if (streamWriter) {
|
|
2218
|
-
streamWriter.write({ type: 'cross_model_review', status: 'started', filesChanged: filesChanged.length });
|
|
2392
|
+
streamWriter.write({ type: 'cross_model_review', status: 'started', filesChanged: filesChanged.length, lineCount });
|
|
2219
2393
|
}
|
|
2220
2394
|
|
|
2221
2395
|
// --- Codex review pass ---
|
package/lib/completion-writer.js
CHANGED
|
@@ -25,6 +25,7 @@ import { mkdirSync, rmSync, statSync } from 'fs';
|
|
|
25
25
|
import { join, dirname, posix } from 'path';
|
|
26
26
|
|
|
27
27
|
import { readFeature, updateFeature, listFeatures } from './feature-json.js';
|
|
28
|
+
import { loadFeaturesDir } from './project-paths.js';
|
|
28
29
|
import { appendEvent, normalizeSince } from './feature-events.js';
|
|
29
30
|
import { checkOrInsert } from './idempotency.js';
|
|
30
31
|
import { FEATURE_CODE_RE_STRICT as FEATURE_CODE_RE } from './feature-code.js';
|
|
@@ -251,13 +252,14 @@ export async function recordCompletion(cwd, args) {
|
|
|
251
252
|
// 3. Compute completion_id
|
|
252
253
|
const completion_id = `${feature_code}:${commit_sha}`;
|
|
253
254
|
|
|
255
|
+
const featuresDir = loadFeaturesDir(cwd);
|
|
254
256
|
// 4. Wrap in maybeIdempotent for caller-key path
|
|
255
257
|
return maybeIdempotent({ ...args, cwd }, async () => {
|
|
256
258
|
// 5a. Acquire per-feature advisory lock (Decision 10)
|
|
257
259
|
const release = await acquireFeatureLock(cwd, feature_code);
|
|
258
260
|
try {
|
|
259
261
|
// 5b. Read feature
|
|
260
|
-
const feature = readFeature(cwd, feature_code);
|
|
262
|
+
const feature = readFeature(cwd, feature_code, featuresDir);
|
|
261
263
|
if (!feature) throw notFoundError(feature_code);
|
|
262
264
|
|
|
263
265
|
// 5c. Snapshot completions array
|
|
@@ -301,7 +303,7 @@ export async function recordCompletion(cwd, args) {
|
|
|
301
303
|
}
|
|
302
304
|
|
|
303
305
|
// 5h. Persist completion record BEFORE status flip (so flip failure doesn't lose the record)
|
|
304
|
-
updateFeature(cwd, feature_code, { completions });
|
|
306
|
+
updateFeature(cwd, feature_code, { completions }, featuresDir);
|
|
305
307
|
|
|
306
308
|
// 5i. Status flip (default on)
|
|
307
309
|
const set_status = args.set_status !== false;
|
|
@@ -398,12 +400,13 @@ export function getCompletions(cwd, opts = {}) {
|
|
|
398
400
|
if (limit > MAX_LIMIT) limit = MAX_LIMIT;
|
|
399
401
|
|
|
400
402
|
// 2. Gather candidate features
|
|
403
|
+
const featuresDir = loadFeaturesDir(cwd);
|
|
401
404
|
let features;
|
|
402
405
|
if (opts.feature_code) {
|
|
403
|
-
const f = readFeature(cwd, opts.feature_code);
|
|
406
|
+
const f = readFeature(cwd, opts.feature_code, featuresDir);
|
|
404
407
|
features = f ? [f] : [];
|
|
405
408
|
} else {
|
|
406
|
-
features = listFeatures(cwd);
|
|
409
|
+
features = listFeatures(cwd, featuresDir);
|
|
407
410
|
}
|
|
408
411
|
|
|
409
412
|
// 3. Flatten all completions[] arrays
|
package/lib/deps.js
CHANGED
|
@@ -153,7 +153,15 @@ export function checkExternalSkills(deps, home = homedir()) {
|
|
|
153
153
|
* json — emit JSON with full dep records (id, required_for, install, fallback, optional)
|
|
154
154
|
* verbose — also list scanned paths in human mode
|
|
155
155
|
*/
|
|
156
|
-
|
|
156
|
+
/**
|
|
157
|
+
* Build a JSON-serializable dep report. Used by both `printDepReport` and
|
|
158
|
+
* callers that need to merge the report with other top-level fields (e.g.
|
|
159
|
+
* `compose doctor --json` adding a version block) into a single JSON document.
|
|
160
|
+
*
|
|
161
|
+
* @param {object} result - from checkExternalSkills
|
|
162
|
+
* @returns {{present:object[], missing:object[], scannedPaths:string[]}}
|
|
163
|
+
*/
|
|
164
|
+
export function buildDepReport(result) {
|
|
157
165
|
const projectDep = (d) => ({
|
|
158
166
|
id: d.id,
|
|
159
167
|
required_for: d.required_for,
|
|
@@ -161,13 +169,16 @@ export function printDepReport(result, opts = {}) {
|
|
|
161
169
|
fallback: d.fallback ?? null,
|
|
162
170
|
optional: d.optional,
|
|
163
171
|
})
|
|
172
|
+
return {
|
|
173
|
+
present: result.present.map(projectDep),
|
|
174
|
+
missing: result.missing.map(projectDep),
|
|
175
|
+
scannedPaths: result.scannedPaths,
|
|
176
|
+
}
|
|
177
|
+
}
|
|
164
178
|
|
|
179
|
+
export function printDepReport(result, opts = {}) {
|
|
165
180
|
if (opts.json) {
|
|
166
|
-
console.log(JSON.stringify(
|
|
167
|
-
present: result.present.map(projectDep),
|
|
168
|
-
missing: result.missing.map(projectDep),
|
|
169
|
-
scannedPaths: result.scannedPaths,
|
|
170
|
-
}, null, 2))
|
|
181
|
+
console.log(JSON.stringify(buildDepReport(result), null, 2))
|
|
171
182
|
return result.missing.every(d => d.optional)
|
|
172
183
|
}
|
|
173
184
|
|