@malloydata/cli 0.0.54-next.2026.5.1.591f62d → 0.0.54-next.2026.5.2.65fe17b
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/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/dist/skills/malloy-language-reference.md +2 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4478,7 +4478,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4478
4478
|
`)):`##! experimental.persistence
|
|
4479
4479
|
`+t}async function fOe(t){if(t.uri&&t.source||!t.uri&&t.source===void 0)return{ok:!1,problems:[{severity:"error",code:"bad-input",message:"Exactly one of {uri, source} must be provided."}]};let e=t.source!==void 0?Sxa(t.source):void 0,r=t.uri?Egi(t.uri):Oxa(t.baseUri),{urlReader:n,readSource:i}=Rxa(r,e),s=new S1t.Runtime({config:Gc,urlReader:n});try{let o=await ZE(()=>s.loadModel(r).getModel());return{ok:!0,loaded:{model:o,rootUrl:r,runtime:s,readSource:i},problems:dOe(o.problems)}}catch(o){return o instanceof S1t.MalloyError?{ok:!1,problems:dOe(o.problems)}:{ok:!1,problems:[pOe(o,r.href)]}}}var s2r,ggi,mgi,S1t,Mxa,o2r=ye(()=>{"use strict";s2r=ut(require("node:url")),ggi=ut(require("node:fs")),mgi=ut(require("node:path")),S1t=ut(Ic());MD();IS();i2r();Mxa="memory://mcp/"});function r0e(t){if(t)return[t.range.start.line,t.range.start.character]}function w1t(t,e){return!!t&&t.url===e}function Cgi(t,e){if(!t||!e)return;let r=t.split(`
|
|
4480
4480
|
`),{start:n,end:i}=e.range;if(n.line<0||n.line>=r.length||i.line<0||i.line>=r.length)return;if(n.line===i.line)return r[n.line].slice(n.character,i.character);let s=[r[n.line].slice(n.character)];for(let o=n.line+1;o<i.line;o++)s.push(r[o]);return s.push(r[i.line].slice(0,i.character)),s.join(`
|
|
4481
|
-
`)}function Txa(t){let e=t.structDef.join;return e==="one"||e==="many"||e==="cross"?e:"one"}function Igi(t){let e=t.structDef;return e.type==="array"&&e.elementTypeDef?.type!=="record_element"}function Dxa(t){let e=t.structDef;return e.type==="array"&&e.elementTypeDef?.type==="record_element"}function vxa(t){return t.structDef.type==="record"}function Nxa(t,e){return Igi(t)?e.filter(r=>r.name!=="value"):e}function Qxa(t,e){let n=e.find(i=>i.name===t.name)?.expressionType;return n&&((0,D1t.expressionIsAggregate)(n)||(0,D1t.expressionIsAnalytic)(n))?"measure":"dimension"}function zxa(t,e){if(Igi(t)||Dxa(t)||vxa(t))return;let r=t.structDef;if(r.sourceID){let n=r.sourceID.split("@")[0];if(n&&e.has(n))return n}if(r.name&&e.has(r.name))return r.name;if(e.has(t.name))return t.name}function ygi(t,e,r,n){let i=t.getTaglines?.()??[],s=t.location,o=w1t(s,n.rootUri),a={name:t.name,kind:"dimension"};if(i.length>0&&(a.annotations=i),o){let l=r0e(s);l&&(a.location=l)}if(t.isExploreField()){let l=t,A=zxa(l,n.knownSources);if(a.kind="join",a.relationship=Txa(l),A&&(a.source_ref=A),(n.opts.expand==="inline"||!A)&&r<wxa){let f=l.structDef.fields??[],h=[];for(let g of l.allFields)h.push(ygi(g,f,r+1,n));a.fields=Nxa(l,h)}return a}if(t.isQueryField()){let l=t;a.kind="view";let A=l.expression||void 0;if(A&&A!==t.name&&(a.expression=A),o&&s){let p=n.readSource(s.url),f=Cgi(p,s);f&&(a.body=f)}return a}let c=t;a.kind=Qxa(c,e),a.type=c.type;let u=c.expression||void 0;return u&&u!==t.name&&(a.expression=u),a}function Lxa(t,e){let r=[],n=[],i=[],s=[],o=t.structDef.fields??[];for(let l of t.allFields){let A=ygi(l,o,0,e);switch(A.kind){case"dimension":r.push(A);break;case"measure":n.push(A);break;case"view":i.push(A);break;case"join":s.push(A);break}}let a=t.getTaglines?.()??[],c={name:t.name,dimensions:r,measures:n,views:i,joins:s};a.length>0&&(c.annotations=a);let u=r0e(t.location);return u&&(c.location=u),t.primaryKey&&(c.primaryKey=t.primaryKey),c}function T1t(t){if(!t)return[];let e=[];for(let r of t.blockNotes??[])e.push(r.text);for(let r of t.notes??[])e.push(r.text);return e}async function Pxa(t,e,r,n){let i=new Set([...t.explores.map(p=>p.name),...t.namedQueries.map(p=>p.as??p.name??"")]),s={rootUri:e,knownSources:i,opts:n,readSource:r},o=[];for(let p of t.explores)w1t(p.location,e)&&o.push(Lxa(p,s));let a=[];for(let p of t.namedQueries){let f=p.location;if(!w1t(f,e))continue;let h={name:p.as??p.name??""},g=T1t(p.annotation);g.length>0&&(h.annotations=g);let E=r0e(f);E&&(h.location=E);let C=Cgi(r(e),f);C&&(h.body=C),a.push(h)}let c=[],u=t._modelDef.queryList??[];for(let p=0;p<u.length;p++){let f=u[p],h={index:p};f.name&&(h.name=f.name);let g=T1t(f.annotation);g.length>0&&(h.annotations=g);let E=r0e(f.location);if(E&&(h.location=E),n.emitRunSql)try{let C=t.getPreparedQueryByIndex(p);h.sql=C.preparedResult.sql.trim()}catch(C){h.error=C instanceof Error?C.message:String(C)}c.push(h)}let l=t.getTaglines?.()??[],A={rootUri:e,sources:o,queries:a,runs:c};return l.length>0&&(A.annotations=l),A}async function a2r(t,e={}){let r=await fOe(t);if(!r.ok||!r.loaded)return{ok:!1,problems:r.problems};let{model:n,rootUrl:i,readSource:s}=r.loaded;try{return{ok:!0,model:await Pxa(n,i.href,s,{expand:e.expand??"ref",emitRunSql:e.emitRunSql??!1}),problems:r.problems}}catch(o){return{ok:!1,problems:[...r.problems,pOe(o,i.href)]}}}async function bgi(t){let e=await fOe(t);if(!e.ok||!e.loaded)return{ok:!1,runs:[],queries:[],problems:e.problems};let{model:r,rootUrl:n}=e.loaded,s=(r._modelDef.queryList??[]).map((a,c)=>{let u={index:c};a.name&&(u.name=a.name);let l=r0e(a.location);l&&(u.location=l);let A=T1t(a.annotation);return A.length>0&&(u.annotations=A),u}),o=r.namedQueries.filter(a=>w1t(a.location,n.href)).map(a=>{let c={name:a.as??a.name??""},u=r0e(a.location);u&&(c.location=u);let l=T1t(a.annotation);return l.length>0&&(c.annotations=l),c});return{ok:!0,rootUri:n.href,runs:s,queries:o,problems:e.problems}}var D1t,wxa,_gi=ye(()=>{"use strict";D1t=ut(Ic());o2r();wxa=4});async function c2r(t,e={}){let r=await fOe(t);if(!r.ok||!r.loaded)return{ok:!1,problems:r.problems.map(a=>xgi(a,t))};let{model:n,rootUrl:i,runtime:s}=r.loaded,o=n._modelDef.queryList??[];try{let a=s.loadModel(i),c;if(e.name){if(!n.namedQueries.find(b=>(b.as??b.name)===e.name)){let b={queries:n.namedQueries.map(O=>O.as??O.name??""),runs:o.map((O,M)=>({index:M,name:O.name??null}))};return{ok:!1,problems:[{severity:"error",code:"selector-not-found",message:`No query named '${e.name}'. Available: ${JSON.stringify(b)}`,uri:i.href}]}}c=a.loadQueryByName(e.name)}else if(typeof e.index=="number"){if(e.index<0||e.index>=o.length)return{ok:!1,problems:[{severity:"error",code:"selector-out-of-range",message:`Index ${e.index} out of range; file has ${o.length} run: statement(s).`,uri:i.href}]};c=a.loadQueryByIndex(e.index)}else{if(o.length===0)return{ok:!1,problems:[{severity:"error",code:"no-run",message:"Source has no run: statement. Specify a named query via `name`, or add a run: to the source.",uri:i.href}]};c=a.loadFinalQuery()}let u=e.rowLimit??Y8,l=Date.now(),A=(await c.getSQL()).trim(),p=Date.now(),f=await ZE(()=>c.run({rowLimit:u})),h=Date.now(),E=f.toJSON().queryResult.result,C=E.length===u;return{ok:!0,sql:A,rowCount:E.length,truncated:C,rows:E,compileTimeMS:p-l,totalTimeMS:h-l,problems:r.problems}}catch(a){return a instanceof Bgi.MalloyError?{ok:!1,problems:[...r.problems,...dOe(a.problems).map(c=>xgi(c,t))]}:{ok:!1,problems:[...r.problems,pOe(a,i.href)]}}}function xgi(t,e){if(t.code!=="field-not-found")return t;let r=e.uri?`Call compile_file with uri="${e.uri}" to see what fields, measures, views, and joins exist on this source.`:"Call compile on the same source (or compile_file on any imported file) to see what fields, measures, views, and joins exist.";return{...t,message:`${t.message} \u2014 ${r}`,help_topic:t.help_topic??"fields"}}var Bgi,Mgi=ye(()=>{"use strict";Bgi=ut(Ic());o2r();K$();IS()});function hOe(t){let{result:e,errors:r}=(0,Ogi.prettify)(t);return{formatted:e,errors:r}}var Ogi,u2r=ye(()=>{"use strict";Ogi=ut(NJ())});var Rgi=m((M30,kxa)=>{kxa.exports={name:"@malloydata/cli",version:"0.0.54-next.2026.5.1.591f62d",description:"Malloy CLI",bin:{"malloy-cli":"dist/index.js","malloy-packages":"scripts/malloy-packages.ts"},files:["dist/**","scripts/malloy-packages.ts"],engines:{node:">=18"},scripts:{lint:"tsc --noEmit && gts lint",test:"NODE_ENV=test jest --projects jest.config.js --runInBand","test-silent":"NODE_ENV=test JEST_SILENT_REPORTER_SHOW_PATHS=true jest --projects jest.config.js --runInBand --reporters jest-silent-reporter","test-e2e":"npm run package-npm test > /dev/null 2>&1 && NODE_ENV=test jest --projects=jest-e2e.config.js --runInBand","test-silent-e2e":"npm run package-npm test > /dev/null 2>&1 && NODE_ENV=test JEST_SILENT_REPORTER_SHOW_PATHS=true jest --projects=jest-e2e.config.js --runInBand --reporters jest-silent-reporter",build:"ts-node scripts/build","build-action":"esbuild --platform=node --bundle ./.github/actions/permission-check/lib/index.ts --outfile=./.github/actions/permission-check/dist/index.js","build-action-dev":"npm run build-action -- --watch",cli:"npx ts-node ./dist/cli.js",watch:"ts-node scripts/build watch",package:"ts-node scripts/package","package-npm":"ts-node scripts/package-npm","npm-publish":"ts-node scripts/npm-publish",postpack:"npm run build","malloy-update-next":"npm install --no-fund --no-audit --save-exact $(./scripts/malloy-packages.ts next) && ts-node scripts/sync-duckdb.ts && npm install --no-fund --no-audit","malloy-update":"npm install --no-fund --no-audit --save-exact $(./scripts/malloy-packages.ts latest) && ts-node scripts/sync-duckdb.ts && npm install --no-fund --no-audit","malloy-link":"npm --no-fund --no-audit link $(./scripts/malloy-packages.ts)","malloy-unlink":"npm --no-fund --no-save --no-audit unlink $(./scripts/malloy-packages.ts) && npm --no-fund --no-audit install --force"},repository:{type:"git",url:"git+https://github.com/malloydata/malloy-cli.git"},author:"Malloy",license:"MIT",bugs:{url:"https://github.com/malloydata/malloy-cli/issues"},homepage:"https://github.com/malloydata/malloy-cli#readme",builtIntoPackageDependencies:["@malloydata/db-bigquery","@malloydata/db-duckdb","@malloydata/db-mysql","@malloydata/db-postgres","@malloydata/db-snowflake","@malloydata/db-presto","@malloydata/db-trino","@malloydata/malloy","@malloydata/malloy-connections","@malloydata/malloy-sql","chalk","commander","logform","winston"],optionalDependencies:{"@duckdb/node-bindings-darwin-arm64":"1.4.4-r.1","@duckdb/node-bindings-darwin-x64":"1.4.4-r.1","@duckdb/node-bindings-linux-arm64":"1.4.4-r.1","@duckdb/node-bindings-linux-x64":"1.4.4-r.1","@duckdb/node-bindings-win32-x64":"1.4.4-r.1"},devDependencies:{"@actions/core":"^1.11.1","@actions/github":"^6.0.0","@commander-js/extra-typings":"^12.1.0","@duckdb/node-api":"1.4.4-r.1","@duckdb/node-bindings":"1.4.4-r.1","@malloydata/db-bigquery":"0.0.388","@malloydata/db-duckdb":"0.0.388","@malloydata/db-postgres":"0.0.388","@malloydata/db-snowflake":"0.0.388","@malloydata/db-trino":"0.0.388","@malloydata/malloy":"0.0.388","@malloydata/malloy-connections":"0.0.388","@malloydata/malloy-sql":"0.0.388","@types/jest":"^29.2.5","@types/node":"^14.11.2","@typescript-eslint/eslint-plugin":"^5.48.2","@typescript-eslint/parser":"^5.48.2",ajv:"^8.12.0",axios:"^1.4.0",chalk:"^4.0.0",commander:"^12.1.0","csv-stringify":"^6.4.0",esbuild:"0.25.0",eslint:"^8.32.0","eslint-config-prettier":"^8.3.0","eslint-plugin-prettier":"^3.4.0",gts:"^3.1.1",jest:"^29.3.1","jest-silent-reporter":"^0.5.0",logform:"^2.5.1",pkg:"^5.8.1","ts-jest":"^29.1.0","ts-node":"^10.9.1",typescript:"^5.9.3",winston:"^3.9.0"},dependencies:{"@modelcontextprotocol/sdk":"^1.29.0",zod:"^4.3.6"}}});function L4(t){let e={...t};return{content:[{type:"text",text:JSON.stringify(t,null,2)}],structuredContent:e}}async function Dgi(){async function t(){await Gc.shutdown("idle")}let e=new B1t({name:"malloy-cli",version:Fxa.version??"development"},{instructions:qxa});e.registerTool("compile_file",{title:"Inspect a Malloy file (compile)",description:"PREFER THIS OVER READING A .malloy FILE AS TEXT. Compile a .malloy file at the given URI and return the full structured model: sources (with dimensions, measures, views, joins), named queries, top-level run: statements with their SQL, and annotations. This is the primary way to understand what is in a .malloy file \u2014 what data sources it defines, what can be queried, what the schema looks like \u2014 whether you are about to edit it, answer a question with it, or just explore it. Reading the file as text shows you syntax; compiling it shows you semantics. Joins to named sources render by reference via `source_ref` (look up the referenced source by name in the same response); pass `expand: 'inline'` to get recursive inlined fields instead.",inputSchema:{uri:l2r,expand:Sgi,emit_run_sql:wgi}},async({uri:i,expand:s,emit_run_sql:o})=>{let a=await a2r({uri:i},{expand:s,emitRunSql:o});return await t(),L4(a)}),e.registerTool("compile",{title:"Compile an inline Malloy source string",description:'Compile a Malloy source string (not backed by a file). Same output shape as compile_file. Use this when drafting new Malloy \u2014 the common pattern is `import "file.malloy"` + a new `run: ...`, which you iterate on against the `problems[]` feedback until clean. The compiler is ground truth; do not guess syntax. When in doubt about what fields / measures / views exist in the imported file, call compile_file on it first.',inputSchema:{source:A2r,base_uri:Tgi,expand:Sgi,emit_run_sql:wgi}},async({source:i,base_uri:s,expand:o,emit_run_sql:a})=>{let c=await a2r({source:i,baseUri:s},{expand:o,emitRunSql:a});return await t(),L4(c)}),e.registerTool("run_file",{title:"Execute a run: from a Malloy file",description:"Execute one run: or named query from a .malloy file against the user's configured connections. Selection: `name` wins if provided, else `index` (0-based into run: statements), else the final run:. Returns the generated SQL and rows (default 10000, set row_limit to override).",inputSchema:{uri:l2r,name:mC.string().optional().describe("Name of a `query:` definition to execute. Wins over `index`."),index:mC.number().int().optional().describe("0-based index into run: statements."),row_limit:mC.number().int().optional().describe("Maximum number of rows to return. Defaults to 10000.")}},async({uri:i,name:s,index:o,row_limit:a})=>{let c=await c2r({uri:i},{name:s,index:o,rowLimit:a});return await t(),L4(c)}),e.registerTool("run",{title:"Execute inline Malloy source",description:"Compile an inline Malloy source and execute its final run: statement against the user's configured connections. Returns the generated SQL and rows (default 10000, set row_limit to override). Use this after compile is clean to see the answer.",inputSchema:{source:A2r,base_uri:Tgi,row_limit:mC.number().int().optional().describe("Maximum number of rows to return. Defaults to 10000.")}},async({source:i,base_uri:s,row_limit:o})=>{let a=await c2r({source:i,baseUri:s},{rowLimit:o});return await t(),L4(a)}),e.registerTool("prettify",{title:"Pretty-print Malloy source",description:"Reformat a Malloy source string. Returns the formatted source and any parse errors (lexer + parser only \u2014 semantic errors are not checked here; use `compile` for that). When `errors` is non-empty the formatted output is best-effort and may not round-trip; fix the parse errors first. Useful for cleaning up freshly-authored or machine-generated Malloy before saving.",inputSchema:{source:A2r}},async({source:i})=>L4(hOe(i))),e.registerTool("language_help",{title:"Malloy language reference (by topic)",description:"Look up a section of the Malloy language reference by topic slug or title. Call this before guessing syntax \u2014 especially for pipelines, reduction vs projection, aggregate locality, calculations/window functions, filtered aggregates, pick expressions, time ranges, tags/annotations. Also call this after any compile error whose fix is not obvious from the message \u2014 problems[] entries carry a `help_topic` field when there's a natural match. Call with no topic to list the available topics.",inputSchema:{topic:mC.string().optional().describe("Topic slug or title to look up (case-insensitive; substrings match). Omit to list all available topics.")}},async({topic:i})=>{if(!i)return L4({topics:n2r()});let s=fgi(i);return L4(s?{slug:s.slug,title:s.title,body:s.body}:{error:`No topic matches '${i}'.`,topics:n2r()})}),e.registerTool("list_runs",{title:"List runnable queries in a Malloy file",description:"Lightweight discovery: return the runnable run: statements (with optional names and indexes) and named `query:` definitions in a file, without serializing the full model. Use this when you only need to know what can be executed; if you need to understand the schema (sources, measures, views, joins), use compile_file instead.",inputSchema:{uri:l2r}},async({uri:i})=>{let s=await bgi({uri:i});return await t(),L4(s)});let r=Agi(R1t());for(let i of r)e.registerPrompt(i.name,{title:i.name,description:i.description},()=>({messages:[{role:"user",content:{type:"text",text:i.body}}]})),e.registerResource(i.name,`malloy-skill://${i.name}`,{title:i.name,description:i.description,mimeType:"text/markdown"},async s=>({contents:[{uri:s.href,mimeType:"text/markdown",text:i.body}]}));let n=new O1t;await e.connect(n)}var Fxa,Sgi,wgi,l2r,A2r,Tgi,qxa,vgi=ye(()=>{"use strict";agi();lgi();JEr();_gi();Mgi();i2r();e2r();MD();u2r();Fxa=Rgi(),Sgi=mC.enum(["ref","inline"]).optional().describe("How to render joins in the model output. 'ref' (default) references joined sources by name via `source_ref`. 'inline' recursively inlines the joined source schema (larger output, useful when the client cannot cross-reference)."),wgi=mC.boolean().optional().describe("If true, compile each run: statement to SQL and include it as `model.runs[].sql`. Default false because run SQL is typically large and is not needed while inspecting the schema. Use run_file to execute a run: and get both SQL and rows."),l2r=mC.string().describe("File URI (file://...) or bare filesystem path. Relative paths resolve against the server's working directory."),A2r=mC.string().describe("Malloy source code."),Tgi=mC.string().optional().describe("Optional URI used to resolve relative imports in the inline source. Typically the file:// URI of the file the snippet will live next to. If omitted, imports must be absolute.");qxa=`
|
|
4481
|
+
`)}function Txa(t){let e=t.structDef.join;return e==="one"||e==="many"||e==="cross"?e:"one"}function Igi(t){let e=t.structDef;return e.type==="array"&&e.elementTypeDef?.type!=="record_element"}function Dxa(t){let e=t.structDef;return e.type==="array"&&e.elementTypeDef?.type==="record_element"}function vxa(t){return t.structDef.type==="record"}function Nxa(t,e){return Igi(t)?e.filter(r=>r.name!=="value"):e}function Qxa(t,e){let n=e.find(i=>i.name===t.name)?.expressionType;return n&&((0,D1t.expressionIsAggregate)(n)||(0,D1t.expressionIsAnalytic)(n))?"measure":"dimension"}function zxa(t,e){if(Igi(t)||Dxa(t)||vxa(t))return;let r=t.structDef;if(r.sourceID){let n=r.sourceID.split("@")[0];if(n&&e.has(n))return n}if(r.name&&e.has(r.name))return r.name;if(e.has(t.name))return t.name}function ygi(t,e,r,n){let i=t.getTaglines?.()??[],s=t.location,o=w1t(s,n.rootUri),a={name:t.name,kind:"dimension"};if(i.length>0&&(a.annotations=i),o){let l=r0e(s);l&&(a.location=l)}if(t.isExploreField()){let l=t,A=zxa(l,n.knownSources);if(a.kind="join",a.relationship=Txa(l),A&&(a.source_ref=A),(n.opts.expand==="inline"||!A)&&r<wxa){let f=l.structDef.fields??[],h=[];for(let g of l.allFields)h.push(ygi(g,f,r+1,n));a.fields=Nxa(l,h)}return a}if(t.isQueryField()){let l=t;a.kind="view";let A=l.expression||void 0;if(A&&A!==t.name&&(a.expression=A),o&&s){let p=n.readSource(s.url),f=Cgi(p,s);f&&(a.body=f)}return a}let c=t;a.kind=Qxa(c,e),a.type=c.type;let u=c.expression||void 0;return u&&u!==t.name&&(a.expression=u),a}function Lxa(t,e){let r=[],n=[],i=[],s=[],o=t.structDef.fields??[];for(let l of t.allFields){let A=ygi(l,o,0,e);switch(A.kind){case"dimension":r.push(A);break;case"measure":n.push(A);break;case"view":i.push(A);break;case"join":s.push(A);break}}let a=t.getTaglines?.()??[],c={name:t.name,dimensions:r,measures:n,views:i,joins:s};a.length>0&&(c.annotations=a);let u=r0e(t.location);return u&&(c.location=u),t.primaryKey&&(c.primaryKey=t.primaryKey),c}function T1t(t){if(!t)return[];let e=[];for(let r of t.blockNotes??[])e.push(r.text);for(let r of t.notes??[])e.push(r.text);return e}async function Pxa(t,e,r,n){let i=new Set([...t.explores.map(p=>p.name),...t.namedQueries.map(p=>p.as??p.name??"")]),s={rootUri:e,knownSources:i,opts:n,readSource:r},o=[];for(let p of t.explores)w1t(p.location,e)&&o.push(Lxa(p,s));let a=[];for(let p of t.namedQueries){let f=p.location;if(!w1t(f,e))continue;let h={name:p.as??p.name??""},g=T1t(p.annotation);g.length>0&&(h.annotations=g);let E=r0e(f);E&&(h.location=E);let C=Cgi(r(e),f);C&&(h.body=C),a.push(h)}let c=[],u=t._modelDef.queryList??[];for(let p=0;p<u.length;p++){let f=u[p],h={index:p};f.name&&(h.name=f.name);let g=T1t(f.annotation);g.length>0&&(h.annotations=g);let E=r0e(f.location);if(E&&(h.location=E),n.emitRunSql)try{let C=t.getPreparedQueryByIndex(p);h.sql=C.preparedResult.sql.trim()}catch(C){h.error=C instanceof Error?C.message:String(C)}c.push(h)}let l=t.getTaglines?.()??[],A={rootUri:e,sources:o,queries:a,runs:c};return l.length>0&&(A.annotations=l),A}async function a2r(t,e={}){let r=await fOe(t);if(!r.ok||!r.loaded)return{ok:!1,problems:r.problems};let{model:n,rootUrl:i,readSource:s}=r.loaded;try{return{ok:!0,model:await Pxa(n,i.href,s,{expand:e.expand??"ref",emitRunSql:e.emitRunSql??!1}),problems:r.problems}}catch(o){return{ok:!1,problems:[...r.problems,pOe(o,i.href)]}}}async function bgi(t){let e=await fOe(t);if(!e.ok||!e.loaded)return{ok:!1,runs:[],queries:[],problems:e.problems};let{model:r,rootUrl:n}=e.loaded,s=(r._modelDef.queryList??[]).map((a,c)=>{let u={index:c};a.name&&(u.name=a.name);let l=r0e(a.location);l&&(u.location=l);let A=T1t(a.annotation);return A.length>0&&(u.annotations=A),u}),o=r.namedQueries.filter(a=>w1t(a.location,n.href)).map(a=>{let c={name:a.as??a.name??""},u=r0e(a.location);u&&(c.location=u);let l=T1t(a.annotation);return l.length>0&&(c.annotations=l),c});return{ok:!0,rootUri:n.href,runs:s,queries:o,problems:e.problems}}var D1t,wxa,_gi=ye(()=>{"use strict";D1t=ut(Ic());o2r();wxa=4});async function c2r(t,e={}){let r=await fOe(t);if(!r.ok||!r.loaded)return{ok:!1,problems:r.problems.map(a=>xgi(a,t))};let{model:n,rootUrl:i,runtime:s}=r.loaded,o=n._modelDef.queryList??[];try{let a=s.loadModel(i),c;if(e.name){if(!n.namedQueries.find(b=>(b.as??b.name)===e.name)){let b={queries:n.namedQueries.map(O=>O.as??O.name??""),runs:o.map((O,M)=>({index:M,name:O.name??null}))};return{ok:!1,problems:[{severity:"error",code:"selector-not-found",message:`No query named '${e.name}'. Available: ${JSON.stringify(b)}`,uri:i.href}]}}c=a.loadQueryByName(e.name)}else if(typeof e.index=="number"){if(e.index<0||e.index>=o.length)return{ok:!1,problems:[{severity:"error",code:"selector-out-of-range",message:`Index ${e.index} out of range; file has ${o.length} run: statement(s).`,uri:i.href}]};c=a.loadQueryByIndex(e.index)}else{if(o.length===0)return{ok:!1,problems:[{severity:"error",code:"no-run",message:"Source has no run: statement. Specify a named query via `name`, or add a run: to the source.",uri:i.href}]};c=a.loadFinalQuery()}let u=e.rowLimit??Y8,l=Date.now(),A=(await c.getSQL()).trim(),p=Date.now(),f=await ZE(()=>c.run({rowLimit:u})),h=Date.now(),E=f.toJSON().queryResult.result,C=E.length===u;return{ok:!0,sql:A,rowCount:E.length,truncated:C,rows:E,compileTimeMS:p-l,totalTimeMS:h-l,problems:r.problems}}catch(a){return a instanceof Bgi.MalloyError?{ok:!1,problems:[...r.problems,...dOe(a.problems).map(c=>xgi(c,t))]}:{ok:!1,problems:[...r.problems,pOe(a,i.href)]}}}function xgi(t,e){if(t.code!=="field-not-found")return t;let r=e.uri?`Call compile_file with uri="${e.uri}" to see what fields, measures, views, and joins exist on this source.`:"Call compile on the same source (or compile_file on any imported file) to see what fields, measures, views, and joins exist.";return{...t,message:`${t.message} \u2014 ${r}`,help_topic:t.help_topic??"fields"}}var Bgi,Mgi=ye(()=>{"use strict";Bgi=ut(Ic());o2r();K$();IS()});function hOe(t){let{result:e,errors:r}=(0,Ogi.prettify)(t);return{formatted:e,errors:r}}var Ogi,u2r=ye(()=>{"use strict";Ogi=ut(NJ())});var Rgi=m((M30,kxa)=>{kxa.exports={name:"@malloydata/cli",version:"0.0.54-next.2026.5.2.65fe17b",description:"Malloy CLI",bin:{"malloy-cli":"dist/index.js","malloy-packages":"scripts/malloy-packages.ts"},files:["dist/**","scripts/malloy-packages.ts"],engines:{node:">=18"},scripts:{lint:"tsc --noEmit && gts lint",test:"NODE_ENV=test jest --projects jest.config.js --runInBand","test-silent":"NODE_ENV=test JEST_SILENT_REPORTER_SHOW_PATHS=true jest --projects jest.config.js --runInBand --reporters jest-silent-reporter","test-e2e":"npm run package-npm test > /dev/null 2>&1 && NODE_ENV=test jest --projects=jest-e2e.config.js --runInBand","test-silent-e2e":"npm run package-npm test > /dev/null 2>&1 && NODE_ENV=test JEST_SILENT_REPORTER_SHOW_PATHS=true jest --projects=jest-e2e.config.js --runInBand --reporters jest-silent-reporter",build:"ts-node scripts/build","build-action":"esbuild --platform=node --bundle ./.github/actions/permission-check/lib/index.ts --outfile=./.github/actions/permission-check/dist/index.js","build-action-dev":"npm run build-action -- --watch",cli:"npx ts-node ./dist/cli.js",watch:"ts-node scripts/build watch",package:"ts-node scripts/package","package-npm":"ts-node scripts/package-npm","npm-publish":"ts-node scripts/npm-publish",postpack:"npm run build","malloy-update-next":"npm install --no-fund --no-audit --save-exact $(./scripts/malloy-packages.ts next) && ts-node scripts/sync-duckdb.ts && npm install --no-fund --no-audit","malloy-update":"npm install --no-fund --no-audit --save-exact $(./scripts/malloy-packages.ts latest) && ts-node scripts/sync-duckdb.ts && npm install --no-fund --no-audit","malloy-link":"npm --no-fund --no-audit link $(./scripts/malloy-packages.ts)","malloy-unlink":"npm --no-fund --no-save --no-audit unlink $(./scripts/malloy-packages.ts) && npm --no-fund --no-audit install --force"},repository:{type:"git",url:"git+https://github.com/malloydata/malloy-cli.git"},author:"Malloy",license:"MIT",bugs:{url:"https://github.com/malloydata/malloy-cli/issues"},homepage:"https://github.com/malloydata/malloy-cli#readme",builtIntoPackageDependencies:["@malloydata/db-bigquery","@malloydata/db-duckdb","@malloydata/db-mysql","@malloydata/db-postgres","@malloydata/db-snowflake","@malloydata/db-presto","@malloydata/db-trino","@malloydata/malloy","@malloydata/malloy-connections","@malloydata/malloy-sql","chalk","commander","logform","winston"],optionalDependencies:{"@duckdb/node-bindings-darwin-arm64":"1.4.4-r.1","@duckdb/node-bindings-darwin-x64":"1.4.4-r.1","@duckdb/node-bindings-linux-arm64":"1.4.4-r.1","@duckdb/node-bindings-linux-x64":"1.4.4-r.1","@duckdb/node-bindings-win32-x64":"1.4.4-r.1"},devDependencies:{"@actions/core":"^1.11.1","@actions/github":"^6.0.0","@commander-js/extra-typings":"^12.1.0","@duckdb/node-api":"1.4.4-r.1","@duckdb/node-bindings":"1.4.4-r.1","@malloydata/db-bigquery":"0.0.388","@malloydata/db-duckdb":"0.0.388","@malloydata/db-postgres":"0.0.388","@malloydata/db-snowflake":"0.0.388","@malloydata/db-trino":"0.0.388","@malloydata/malloy":"0.0.388","@malloydata/malloy-connections":"0.0.388","@malloydata/malloy-sql":"0.0.388","@types/jest":"^29.2.5","@types/node":"^14.11.2","@typescript-eslint/eslint-plugin":"^5.48.2","@typescript-eslint/parser":"^5.48.2",ajv:"^8.12.0",axios:"^1.4.0",chalk:"^4.0.0",commander:"^12.1.0","csv-stringify":"^6.4.0",esbuild:"0.25.0",eslint:"^8.32.0","eslint-config-prettier":"^8.3.0","eslint-plugin-prettier":"^3.4.0",gts:"^3.1.1",jest:"^29.3.1","jest-silent-reporter":"^0.5.0",logform:"^2.5.1",pkg:"^5.8.1","ts-jest":"^29.1.0","ts-node":"^10.9.1",typescript:"^5.9.3",winston:"^3.9.0"},dependencies:{"@modelcontextprotocol/sdk":"^1.29.0",zod:"^4.3.6"}}});function L4(t){let e={...t};return{content:[{type:"text",text:JSON.stringify(t,null,2)}],structuredContent:e}}async function Dgi(){async function t(){await Gc.shutdown("idle")}let e=new B1t({name:"malloy-cli",version:Fxa.version??"development"},{instructions:qxa});e.registerTool("compile_file",{title:"Inspect a Malloy file (compile)",description:"PREFER THIS OVER READING A .malloy FILE AS TEXT. Compile a .malloy file at the given URI and return the full structured model: sources (with dimensions, measures, views, joins), named queries, top-level run: statements with their SQL, and annotations. This is the primary way to understand what is in a .malloy file \u2014 what data sources it defines, what can be queried, what the schema looks like \u2014 whether you are about to edit it, answer a question with it, or just explore it. Reading the file as text shows you syntax; compiling it shows you semantics. Joins to named sources render by reference via `source_ref` (look up the referenced source by name in the same response); pass `expand: 'inline'` to get recursive inlined fields instead.",inputSchema:{uri:l2r,expand:Sgi,emit_run_sql:wgi}},async({uri:i,expand:s,emit_run_sql:o})=>{let a=await a2r({uri:i},{expand:s,emitRunSql:o});return await t(),L4(a)}),e.registerTool("compile",{title:"Compile an inline Malloy source string",description:'Compile a Malloy source string (not backed by a file). Same output shape as compile_file. Use this when drafting new Malloy \u2014 the common pattern is `import "file.malloy"` + a new `run: ...`, which you iterate on against the `problems[]` feedback until clean. The compiler is ground truth; do not guess syntax. When in doubt about what fields / measures / views exist in the imported file, call compile_file on it first.',inputSchema:{source:A2r,base_uri:Tgi,expand:Sgi,emit_run_sql:wgi}},async({source:i,base_uri:s,expand:o,emit_run_sql:a})=>{let c=await a2r({source:i,baseUri:s},{expand:o,emitRunSql:a});return await t(),L4(c)}),e.registerTool("run_file",{title:"Execute a run: from a Malloy file",description:"Execute one run: or named query from a .malloy file against the user's configured connections. Selection: `name` wins if provided, else `index` (0-based into run: statements), else the final run:. Returns the generated SQL and rows (default 10000, set row_limit to override).",inputSchema:{uri:l2r,name:mC.string().optional().describe("Name of a `query:` definition to execute. Wins over `index`."),index:mC.number().int().optional().describe("0-based index into run: statements."),row_limit:mC.number().int().optional().describe("Maximum number of rows to return. Defaults to 10000.")}},async({uri:i,name:s,index:o,row_limit:a})=>{let c=await c2r({uri:i},{name:s,index:o,rowLimit:a});return await t(),L4(c)}),e.registerTool("run",{title:"Execute inline Malloy source",description:"Compile an inline Malloy source and execute its final run: statement against the user's configured connections. Returns the generated SQL and rows (default 10000, set row_limit to override). Use this after compile is clean to see the answer.",inputSchema:{source:A2r,base_uri:Tgi,row_limit:mC.number().int().optional().describe("Maximum number of rows to return. Defaults to 10000.")}},async({source:i,base_uri:s,row_limit:o})=>{let a=await c2r({source:i,baseUri:s},{rowLimit:o});return await t(),L4(a)}),e.registerTool("prettify",{title:"Pretty-print Malloy source",description:"Reformat a Malloy source string. Returns the formatted source and any parse errors (lexer + parser only \u2014 semantic errors are not checked here; use `compile` for that). When `errors` is non-empty the formatted output is best-effort and may not round-trip; fix the parse errors first. Useful for cleaning up freshly-authored or machine-generated Malloy before saving.",inputSchema:{source:A2r}},async({source:i})=>L4(hOe(i))),e.registerTool("language_help",{title:"Malloy language reference (by topic)",description:"Look up a section of the Malloy language reference by topic slug or title. Call this before guessing syntax \u2014 especially for pipelines, reduction vs projection, aggregate locality, calculations/window functions, filtered aggregates, pick expressions, time ranges, tags/annotations. Also call this after any compile error whose fix is not obvious from the message \u2014 problems[] entries carry a `help_topic` field when there's a natural match. Call with no topic to list the available topics.",inputSchema:{topic:mC.string().optional().describe("Topic slug or title to look up (case-insensitive; substrings match). Omit to list all available topics.")}},async({topic:i})=>{if(!i)return L4({topics:n2r()});let s=fgi(i);return L4(s?{slug:s.slug,title:s.title,body:s.body}:{error:`No topic matches '${i}'.`,topics:n2r()})}),e.registerTool("list_runs",{title:"List runnable queries in a Malloy file",description:"Lightweight discovery: return the runnable run: statements (with optional names and indexes) and named `query:` definitions in a file, without serializing the full model. Use this when you only need to know what can be executed; if you need to understand the schema (sources, measures, views, joins), use compile_file instead.",inputSchema:{uri:l2r}},async({uri:i})=>{let s=await bgi({uri:i});return await t(),L4(s)});let r=Agi(R1t());for(let i of r)e.registerPrompt(i.name,{title:i.name,description:i.description},()=>({messages:[{role:"user",content:{type:"text",text:i.body}}]})),e.registerResource(i.name,`malloy-skill://${i.name}`,{title:i.name,description:i.description,mimeType:"text/markdown"},async s=>({contents:[{uri:s.href,mimeType:"text/markdown",text:i.body}]}));let n=new O1t;await e.connect(n)}var Fxa,Sgi,wgi,l2r,A2r,Tgi,qxa,vgi=ye(()=>{"use strict";agi();lgi();JEr();_gi();Mgi();i2r();e2r();MD();u2r();Fxa=Rgi(),Sgi=mC.enum(["ref","inline"]).optional().describe("How to render joins in the model output. 'ref' (default) references joined sources by name via `source_ref`. 'inline' recursively inlines the joined source schema (larger output, useful when the client cannot cross-reference)."),wgi=mC.boolean().optional().describe("If true, compile each run: statement to SQL and include it as `model.runs[].sql`. Default false because run SQL is typically large and is not needed while inspecting the schema. Use run_file to execute a run: and get both SQL and rows."),l2r=mC.string().describe("File URI (file://...) or bare filesystem path. Relative paths resolve against the server's working directory."),A2r=mC.string().describe("Malloy source code."),Tgi=mC.string().optional().describe("Optional URI used to resolve relative imports in the inline source. Typically the file:// URI of the file the snippet will live next to. If omitted, imports must be absolute.");qxa=`
|
|
4482
4482
|
This server lets an agent inspect, author, and execute Malloy semantic models.
|
|
4483
4483
|
|
|
4484
4484
|
Malloy (.malloy) files are NOT plain text you should read for comprehension.
|
package/dist/index.js
CHANGED
|
@@ -96,6 +96,8 @@ join_cross: other_table on other_table.key = key -- cross join
|
|
|
96
96
|
|
|
97
97
|
The `with` shorthand requires the joined source to have a declared `primary_key`. All joins are left outer by default. There is no right join — Malloy's graph model doesn't need one.
|
|
98
98
|
|
|
99
|
+
**Choosing `join_one` vs `join_many`:** Ask "for a single row in the base source, can the joined source match more than one row?" If yes → `join_many`. If no (or at most one) → `join_one`. The common mistake is reaching for `join_many` when joining a *lookup or summary table* (e.g., joining an inventory snapshot to a purchase history on a wine key). Even though the joined table may have many rows overall, if each base row resolves to *at most one* joined row, use `join_one`. Use `join_many` only when the join genuinely fans out the base rows — e.g., joining line items to orders, or notes to a wine catalog.
|
|
100
|
+
|
|
99
101
|
When you reference a joined source's fields, you use dot notation: `carriers.nickname`, `origin_airport.state`. This is one of Malloy's most important abstractions: **the access path to nested data is identical regardless of how the nesting is physically stored.** An array of records embedded in a column, a `join_many` to a separate table, a record-typed column — all are navigated with the same dot notation. The SQL required to traverse these different physical arrangements varies wildly (unnesting arrays, LEFT JOINs, correlated subqueries, ARRAY_AGG), but Malloy hides all of that. You think about the logical shape of your data — "flights have carriers, carriers have a nickname" — and write `carriers.nickname`. The compiler figures out what SQL is needed to get there. This means you can restructure your physical schema (normalize a nested array into a separate table, or denormalize a joined table into a record column) without changing any of the Malloy that references that data.
|
|
100
102
|
|
|
101
103
|
## Fields
|