@crewhaus/ir-passes 0.1.4 → 0.1.5

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.
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Section 28 — `ir-passes`. Idempotent IR optimization passes; each pass
3
+ * is a pure `(IrNode) → IrNode` function. The pipeline is composable and
4
+ * deterministic: same input → same output regardless of pass order
5
+ * (within the published "safe order" — see `applyPasses`).
6
+ *
7
+ * Built-in passes:
8
+ * - `deadToolElimination` — drop entries from `tools` that no sub-agent
9
+ * or permission rule references. Catches the common case where a spec
10
+ * declares `tools: [Read, Write, Bash]` but only ever references some of
11
+ * them — e.g. an `alwaysDeny` rule naming `Write` actually counts as a
12
+ * reference and KEEPS `Write` in the list; only a tool that no rule and
13
+ * no sub-agent mentions at all (say `Bash` here) gets dropped.
14
+ * - `redundantMcpServerCollapse` — dedup `mcp_servers` map entries by
15
+ * `(transport, command, args)` signature so two specs that import the
16
+ * same server under different keys collapse into one boot per process.
17
+ * - `permissionRuleCanonicalize` — sort + dedup `permissions.rules` by
18
+ * canonical (type, pattern) tuples; preserves source priority order
19
+ * (alwaysDeny > alwaysAsk > alwaysAllow) but de-dupes identical entries.
20
+ * - `transactionPolicyEnforcement` — §47 validating pass: checks the
21
+ * blockchain blocks (wallets/contracts/chains/transaction_policy) for
22
+ * referential integrity and throws `IrPassError` on a mismatch.
23
+ * - `wellFormednessCheck` — Track F validating pass: checks typed
24
+ * multi-agent graphs/crews (edges connect declared nodes, reachability
25
+ * from entry, schema/role references resolve) and throws on a violation.
26
+ * - `promptCachePrefixSort` — TODO: re-orders system-block segments so
27
+ * the cache prefix is maximised. v0 stub returns IR unchanged so the
28
+ * pipeline contract holds; v1 follow-up wires this once we land
29
+ * multi-block system prompts in IR.
30
+ *
31
+ * Pipeline order in `applyPasses` (the safe default):
32
+ * deadToolElimination → redundantMcpServerCollapse →
33
+ * permissionRuleCanonicalize → transactionPolicyEnforcement →
34
+ * wellFormednessCheck → promptCachePrefixSort
35
+ */
36
+ import { CrewhausError } from "@crewhaus/errors";
37
+ import type { IrNode } from "@crewhaus/ir";
38
+ export declare class IrPassError extends CrewhausError {
39
+ readonly name = "IrPassError";
40
+ constructor(message: string, cause?: unknown);
41
+ }
42
+ export type IrPass = (ir: IrNode) => IrNode;
43
+ export type ApplyPassesOptions = {
44
+ /** Override the pass order. Default: safe order. */
45
+ readonly passes?: ReadonlyArray<IrPass>;
46
+ };
47
+ /** Apply every pass in order; returns the final IR. */
48
+ export declare function applyPasses(ir: IrNode, opts?: ApplyPassesOptions): IrNode;
49
+ /**
50
+ * Pass 1 — drop tools that no permission rule and no sub-agent references.
51
+ * v0 only inspects `IrV0` (the cli target) since that's where tools[] +
52
+ * sub_agents + permissions live in the same shape. Other targets either
53
+ * don't carry `tools[]` (workflow uses per-step tools) or aren't v0-IR
54
+ * subjects of dead-tool elimination yet (graph nodes carry their own
55
+ * tool sets — handled by a follow-up pass).
56
+ */
57
+ export declare function deadToolElimination(ir: IrNode): IrNode;
58
+ /**
59
+ * Pass 2 — collapse `mcp_servers` entries that share `(transport, command,
60
+ * args)` (stdio) or `(transport, url)` (sse). The first key wins on
61
+ * collision; later duplicates are dropped. Order is preserved for entries
62
+ * that survive.
63
+ */
64
+ export declare function redundantMcpServerCollapse(ir: IrNode): IrNode;
65
+ /**
66
+ * Pass 3 — sort + dedup permission rules. Within each precedence tier
67
+ * (alwaysDeny > alwaysAsk > alwaysAllow), rules are sorted alphabetically
68
+ * by pattern and exact duplicates dropped.
69
+ */
70
+ export declare function permissionRuleCanonicalize(ir: IrNode): IrNode;
71
+ /**
72
+ * Pass 4 — re-order system-block segments to maximise the prompt-cache
73
+ * prefix. v0 stub: IR carries the system prompt as a single string, so
74
+ * there's no segmentation to reorder yet. Returns input unchanged.
75
+ * The placeholder keeps the pipeline contract stable; once IR carries
76
+ * multi-block system prompts (Section 31's Studio v1 paths), the real
77
+ * impl lands here.
78
+ */
79
+ export declare function promptCachePrefixSort(ir: IrNode): IrNode;
80
+ export declare function transactionPolicyEnforcement(ir: IrNode): IrNode;
81
+ /**
82
+ * Track F (Section 57) — well-formedness check for typed multi-agent
83
+ * graphs. Source: AgentFlow (arxiv 2604.20801). Before any candidate
84
+ * harness is sent for expensive LLM evaluation, this pass:
85
+ *
86
+ * 1. Verifies every graph edge connects declared nodes.
87
+ * 2. Verifies the graph is connected (every node reachable from entry).
88
+ * 3. Verifies every edge's `schema` either is `untyped` or resolves
89
+ * to a declared `messageSchemas` entry.
90
+ * 4. For crews: verifies routing.match targets all reference declared
91
+ * roles (a subset of what `parseSpec` does; we re-check here so
92
+ * `applyPasses(ir)` is safe to call standalone).
93
+ *
94
+ * Failing this check is fast and cheap, which means the search budget
95
+ * for upstream optimizers (Tracks D, E) goes to well-formed harnesses
96
+ * only. Cited paper: AgentFlow (arxiv 2604.20801).
97
+ */
98
+ export declare function wellFormednessCheck(ir: IrNode): IrNode;
99
+ export declare const DEFAULT_PIPELINE: ReadonlyArray<IrPass>;
package/dist/index.js ADDED
@@ -0,0 +1,375 @@
1
+ /**
2
+ * Section 28 — `ir-passes`. Idempotent IR optimization passes; each pass
3
+ * is a pure `(IrNode) → IrNode` function. The pipeline is composable and
4
+ * deterministic: same input → same output regardless of pass order
5
+ * (within the published "safe order" — see `applyPasses`).
6
+ *
7
+ * Built-in passes:
8
+ * - `deadToolElimination` — drop entries from `tools` that no sub-agent
9
+ * or permission rule references. Catches the common case where a spec
10
+ * declares `tools: [Read, Write, Bash]` but only ever references some of
11
+ * them — e.g. an `alwaysDeny` rule naming `Write` actually counts as a
12
+ * reference and KEEPS `Write` in the list; only a tool that no rule and
13
+ * no sub-agent mentions at all (say `Bash` here) gets dropped.
14
+ * - `redundantMcpServerCollapse` — dedup `mcp_servers` map entries by
15
+ * `(transport, command, args)` signature so two specs that import the
16
+ * same server under different keys collapse into one boot per process.
17
+ * - `permissionRuleCanonicalize` — sort + dedup `permissions.rules` by
18
+ * canonical (type, pattern) tuples; preserves source priority order
19
+ * (alwaysDeny > alwaysAsk > alwaysAllow) but de-dupes identical entries.
20
+ * - `transactionPolicyEnforcement` — §47 validating pass: checks the
21
+ * blockchain blocks (wallets/contracts/chains/transaction_policy) for
22
+ * referential integrity and throws `IrPassError` on a mismatch.
23
+ * - `wellFormednessCheck` — Track F validating pass: checks typed
24
+ * multi-agent graphs/crews (edges connect declared nodes, reachability
25
+ * from entry, schema/role references resolve) and throws on a violation.
26
+ * - `promptCachePrefixSort` — TODO: re-orders system-block segments so
27
+ * the cache prefix is maximised. v0 stub returns IR unchanged so the
28
+ * pipeline contract holds; v1 follow-up wires this once we land
29
+ * multi-block system prompts in IR.
30
+ *
31
+ * Pipeline order in `applyPasses` (the safe default):
32
+ * deadToolElimination → redundantMcpServerCollapse →
33
+ * permissionRuleCanonicalize → transactionPolicyEnforcement →
34
+ * wellFormednessCheck → promptCachePrefixSort
35
+ */
36
+ import { CrewhausError } from "@crewhaus/errors";
37
+ export class IrPassError extends CrewhausError {
38
+ name = "IrPassError";
39
+ constructor(message, cause) {
40
+ super("compiler", message, cause);
41
+ }
42
+ }
43
+ /** Apply every pass in order; returns the final IR. */
44
+ export function applyPasses(ir, opts = {}) {
45
+ const pipeline = opts.passes ?? DEFAULT_PIPELINE;
46
+ let current = ir;
47
+ for (const pass of pipeline) {
48
+ current = pass(current);
49
+ }
50
+ return current;
51
+ }
52
+ /**
53
+ * Pass 1 — drop tools that no permission rule and no sub-agent references.
54
+ * v0 only inspects `IrV0` (the cli target) since that's where tools[] +
55
+ * sub_agents + permissions live in the same shape. Other targets either
56
+ * don't carry `tools[]` (workflow uses per-step tools) or aren't v0-IR
57
+ * subjects of dead-tool elimination yet (graph nodes carry their own
58
+ * tool sets — handled by a follow-up pass).
59
+ */
60
+ export function deadToolElimination(ir) {
61
+ if (ir.target !== "cli")
62
+ return ir;
63
+ const cli = ir;
64
+ const tools = cli.tools ?? [];
65
+ if (tools.length === 0)
66
+ return ir;
67
+ // Track which tool names have any reachable use site.
68
+ const used = new Set();
69
+ // Permission rules can reference a specific tool name (e.g. "Bash" or
70
+ // "Bash(rm *)"). The matcher's compilePattern parses this as the tool
71
+ // followed by an optional invocation pattern in parens. We look at the
72
+ // characters before the first `(` or `:` to extract the tool name.
73
+ const ruleNames = (cli.permissions?.rules ?? []).map((r) => extractToolFromPattern(r.pattern));
74
+ for (const n of ruleNames)
75
+ if (n)
76
+ used.add(n);
77
+ // Sub-agents inherit a subset of the parent's tools — surface them.
78
+ const subAgentRefs = cli.subAgents ?? [];
79
+ for (const sa of subAgentRefs) {
80
+ for (const t of sa.tools)
81
+ used.add(t);
82
+ }
83
+ // Always-allow defaults: if any rule references a tool by exact name we
84
+ // count it; otherwise the original tool list serves as the
85
+ // "implicitly used" baseline. We use case-insensitive comparison since
86
+ // tool registration uses the lowercase variant.
87
+ const filtered = tools.filter((t) => used.has(t) ||
88
+ used.has(t.toLowerCase()) ||
89
+ [...used].some((u) => u.toLowerCase() === t.toLowerCase()));
90
+ // If no reference exists at all (rules + sub-agents both empty), return
91
+ // input unchanged — eliminating every tool would be wrong.
92
+ if (used.size === 0)
93
+ return ir;
94
+ if (filtered.length === tools.length)
95
+ return ir;
96
+ return { ...cli, tools: Object.freeze(filtered) };
97
+ }
98
+ function extractToolFromPattern(pattern) {
99
+ if (!pattern)
100
+ return undefined;
101
+ // Strip a `(...)` invocation suffix: "Bash(rm *)" → "Bash".
102
+ const parenIdx = pattern.indexOf("(");
103
+ const head = (parenIdx === -1 ? pattern : pattern.slice(0, parenIdx)).trim();
104
+ if (!head)
105
+ return undefined;
106
+ return head;
107
+ }
108
+ /**
109
+ * Pass 2 — collapse `mcp_servers` entries that share `(transport, command,
110
+ * args)` (stdio) or `(transport, url)` (sse). The first key wins on
111
+ * collision; later duplicates are dropped. Order is preserved for entries
112
+ * that survive.
113
+ */
114
+ export function redundantMcpServerCollapse(ir) {
115
+ // mcp_servers lives on cli, channel, managed
116
+ const carriesMcp = (n) => n.target === "cli" || n.target === "channel" || n.target === "managed";
117
+ if (!carriesMcp(ir))
118
+ return ir;
119
+ const ms = ir.mcp_servers;
120
+ if (!ms || Object.keys(ms).length < 2)
121
+ return ir;
122
+ const sigToFirstKey = new Map();
123
+ const keptOrdered = [];
124
+ for (const [k, v] of Object.entries(ms)) {
125
+ const sig = mcpSignature(v);
126
+ if (sigToFirstKey.has(sig))
127
+ continue;
128
+ sigToFirstKey.set(sig, k);
129
+ keptOrdered.push([k, v]);
130
+ }
131
+ if (keptOrdered.length === Object.keys(ms).length)
132
+ return ir;
133
+ const next = {};
134
+ for (const [k, v] of keptOrdered)
135
+ next[k] = v;
136
+ return { ...ir, mcp_servers: Object.freeze(next) };
137
+ }
138
+ function mcpSignature(c) {
139
+ if (c.transport === "stdio") {
140
+ return `stdio|${c.command}|${(c.args ?? []).join(" ")}`;
141
+ }
142
+ return `sse|${c.url}`;
143
+ }
144
+ /**
145
+ * Pass 3 — sort + dedup permission rules. Within each precedence tier
146
+ * (alwaysDeny > alwaysAsk > alwaysAllow), rules are sorted alphabetically
147
+ * by pattern and exact duplicates dropped.
148
+ */
149
+ export function permissionRuleCanonicalize(ir) {
150
+ const carriesPerms = (n) => n.target === "cli" || n.target === "channel" || n.target === "managed";
151
+ if (!carriesPerms(ir))
152
+ return ir;
153
+ const perms = ir.permissions;
154
+ if (!perms)
155
+ return ir;
156
+ const tier = (t) => t === "alwaysDeny" ? 0 : t === "alwaysAsk" ? 1 : 2;
157
+ const seen = new Set();
158
+ const sorted = [...perms.rules]
159
+ .map((r) => ({ r, key: `${r.type}:${r.pattern}` }))
160
+ .filter(({ key }) => {
161
+ if (seen.has(key))
162
+ return false;
163
+ seen.add(key);
164
+ return true;
165
+ })
166
+ .sort((a, b) => {
167
+ const ta = tier(a.r.type);
168
+ const tb = tier(b.r.type);
169
+ if (ta !== tb)
170
+ return ta - tb;
171
+ return a.r.pattern.localeCompare(b.r.pattern);
172
+ })
173
+ .map(({ r }) => r);
174
+ if (sorted.length === perms.rules.length) {
175
+ let identical = true;
176
+ for (let i = 0; i < sorted.length; i++) {
177
+ if (sorted[i] !== perms.rules[i]) {
178
+ identical = false;
179
+ break;
180
+ }
181
+ }
182
+ if (identical)
183
+ return ir;
184
+ }
185
+ const newPerms = {
186
+ ...perms,
187
+ rules: Object.freeze(sorted),
188
+ };
189
+ return { ...ir, permissions: newPerms };
190
+ }
191
+ /**
192
+ * Pass 4 — re-order system-block segments to maximise the prompt-cache
193
+ * prefix. v0 stub: IR carries the system prompt as a single string, so
194
+ * there's no segmentation to reorder yet. Returns input unchanged.
195
+ * The placeholder keeps the pipeline contract stable; once IR carries
196
+ * multi-block system prompts (Section 31's Studio v1 paths), the real
197
+ * impl lands here.
198
+ */
199
+ export function promptCachePrefixSort(ir) {
200
+ return ir;
201
+ }
202
+ function carriesChainSubsystem(ir) {
203
+ const t = ir.target;
204
+ return (t === "cli" ||
205
+ t === "workflow" ||
206
+ t === "channel" ||
207
+ t === "graph" ||
208
+ t === "crew" ||
209
+ t === "research" ||
210
+ t === "batch");
211
+ }
212
+ export function transactionPolicyEnforcement(ir) {
213
+ if (!carriesChainSubsystem(ir))
214
+ return ir;
215
+ const chains = ir.chains;
216
+ const wallets = ir.wallets;
217
+ const contracts = ir.contracts;
218
+ const policy = ir.transactionPolicy;
219
+ // Empty subsystem is a no-op (existing specs untouched).
220
+ if ((chains === undefined || chains.length === 0) &&
221
+ (wallets === undefined || wallets.length === 0) &&
222
+ (contracts === undefined || contracts.length === 0) &&
223
+ policy === undefined) {
224
+ return ir;
225
+ }
226
+ const chainIds = new Set((chains ?? []).map((c) => c.id));
227
+ const contractIds = new Set((contracts ?? []).map((c) => c.id));
228
+ // chains[] uniqueness
229
+ if (chains !== undefined) {
230
+ const seen = new Set();
231
+ for (const c of chains) {
232
+ if (seen.has(c.id)) {
233
+ throw new IrPassError(`duplicate chains[].id "${c.id}"`);
234
+ }
235
+ seen.add(c.id);
236
+ }
237
+ }
238
+ // wallets[*].chainId ⊆ chains[].id; keyRef required for non-user-controlled
239
+ if (wallets !== undefined) {
240
+ const seen = new Set();
241
+ for (const w of wallets) {
242
+ if (seen.has(w.id)) {
243
+ throw new IrPassError(`duplicate wallets[].id "${w.id}"`);
244
+ }
245
+ seen.add(w.id);
246
+ if (!chainIds.has(w.chainId)) {
247
+ throw new IrPassError(`wallets[].id "${w.id}" references chainId "${w.chainId}" which is not declared in chains[]`);
248
+ }
249
+ if (w.custody !== "user-controlled" && w.keyRef === undefined) {
250
+ throw new IrPassError(`wallets[].id "${w.id}" has custody="${w.custody}" but no keyRef; kms/hsm/local custody requires keyRef`);
251
+ }
252
+ }
253
+ }
254
+ // contracts[*].chainId ⊆ chains[].id
255
+ if (contracts !== undefined) {
256
+ const seen = new Set();
257
+ for (const c of contracts) {
258
+ if (seen.has(c.id)) {
259
+ throw new IrPassError(`duplicate contracts[].id "${c.id}"`);
260
+ }
261
+ seen.add(c.id);
262
+ if (!chainIds.has(c.chainId)) {
263
+ throw new IrPassError(`contracts[].id "${c.id}" references chainId "${c.chainId}" which is not declared in chains[]`);
264
+ }
265
+ }
266
+ }
267
+ // transaction_policy.allowed_contracts ⊆ contracts[].id
268
+ if (policy !== undefined) {
269
+ for (const cid of policy.allowedContracts) {
270
+ if (!contractIds.has(cid)) {
271
+ throw new IrPassError(`transaction_policy.allowedContracts entry "${cid}" is not a declared contracts[].id`);
272
+ }
273
+ }
274
+ if (policy.defaultWriteApproval === "none") {
275
+ const allAutomated = (wallets ?? []).every((w) => w.signingPolicy === "automated");
276
+ if (!allAutomated) {
277
+ throw new IrPassError('transaction_policy.defaultWriteApproval="none" requires every wallet to have signingPolicy="automated"');
278
+ }
279
+ }
280
+ }
281
+ // No structural rewrite — the pass validates and passes through.
282
+ return ir;
283
+ }
284
+ /**
285
+ * Track F (Section 57) — well-formedness check for typed multi-agent
286
+ * graphs. Source: AgentFlow (arxiv 2604.20801). Before any candidate
287
+ * harness is sent for expensive LLM evaluation, this pass:
288
+ *
289
+ * 1. Verifies every graph edge connects declared nodes.
290
+ * 2. Verifies the graph is connected (every node reachable from entry).
291
+ * 3. Verifies every edge's `schema` either is `untyped` or resolves
292
+ * to a declared `messageSchemas` entry.
293
+ * 4. For crews: verifies routing.match targets all reference declared
294
+ * roles (a subset of what `parseSpec` does; we re-check here so
295
+ * `applyPasses(ir)` is safe to call standalone).
296
+ *
297
+ * Failing this check is fast and cheap, which means the search budget
298
+ * for upstream optimizers (Tracks D, E) goes to well-formed harnesses
299
+ * only. Cited paper: AgentFlow (arxiv 2604.20801).
300
+ */
301
+ export function wellFormednessCheck(ir) {
302
+ if (ir.target === "graph") {
303
+ const g = ir;
304
+ const nodeNames = new Set(g.nodes.map((n) => n.name));
305
+ if (!nodeNames.has(g.entry)) {
306
+ throw new IrPassError(`graph entry "${g.entry}" is not a declared node (nodes: ${[...nodeNames].join(", ")})`);
307
+ }
308
+ const schemaNames = new Set((g.messageSchemas ?? []).map((s) => s.name));
309
+ for (const e of g.edges) {
310
+ if (!nodeNames.has(e.from)) {
311
+ throw new IrPassError(`graph edge from "${e.from}" references an undeclared node`);
312
+ }
313
+ if (!nodeNames.has(e.to)) {
314
+ throw new IrPassError(`graph edge to "${e.to}" references an undeclared node`);
315
+ }
316
+ if (e.schema !== undefined && e.schema.kind === "named") {
317
+ if (!schemaNames.has(e.schema.name)) {
318
+ throw new IrPassError(`graph edge ${e.from}→${e.to} references undeclared message schema "${e.schema.name}"`);
319
+ }
320
+ }
321
+ }
322
+ // Reachability from entry.
323
+ const reachable = new Set([g.entry]);
324
+ let added = true;
325
+ while (added) {
326
+ added = false;
327
+ for (const e of g.edges) {
328
+ if (reachable.has(e.from) && !reachable.has(e.to)) {
329
+ reachable.add(e.to);
330
+ added = true;
331
+ }
332
+ }
333
+ }
334
+ for (const n of g.nodes) {
335
+ if (!reachable.has(n.name)) {
336
+ throw new IrPassError(`graph node "${n.name}" is unreachable from entry "${g.entry}"`);
337
+ }
338
+ }
339
+ }
340
+ else if (ir.target === "crew") {
341
+ const c = ir;
342
+ const roleNames = new Set(c.roles.map((r) => r.name));
343
+ if (!roleNames.has(c.entry)) {
344
+ throw new IrPassError(`crew entry "${c.entry}" is not a declared role (roles: ${[...roleNames].join(", ")})`);
345
+ }
346
+ const schemaNames = new Set((c.messageSchemas ?? []).map((s) => s.name));
347
+ if (c.routing?.kind === "match" && c.routing.match !== undefined) {
348
+ for (const [from, rules] of Object.entries(c.routing.match)) {
349
+ if (!roleNames.has(from)) {
350
+ throw new IrPassError(`crew routing.match["${from}"]: source role not declared`);
351
+ }
352
+ for (const rule of rules) {
353
+ if (!roleNames.has(rule.to)) {
354
+ throw new IrPassError(`crew routing.match["${from}"].to "${rule.to}": target role not declared`);
355
+ }
356
+ }
357
+ }
358
+ }
359
+ // Schema declarations exist (they're optional but if present they must
360
+ // be uniquely named). This catches the easy authoring bug of declaring
361
+ // two schemas with the same name.
362
+ if (schemaNames.size !== (c.messageSchemas ?? []).length) {
363
+ throw new IrPassError("crew messageSchemas contains duplicate names");
364
+ }
365
+ }
366
+ return ir;
367
+ }
368
+ export const DEFAULT_PIPELINE = Object.freeze([
369
+ deadToolElimination,
370
+ redundantMcpServerCollapse,
371
+ permissionRuleCanonicalize,
372
+ transactionPolicyEnforcement,
373
+ wellFormednessCheck,
374
+ promptCachePrefixSort,
375
+ ]);
package/package.json CHANGED
@@ -1,19 +1,22 @@
1
1
  {
2
2
  "name": "@crewhaus/ir-passes",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "type": "module",
5
5
  "description": "Idempotent IR optimization passes (dead-tool-elimination, prompt-cache-prefix-sort, redundant-mcp-server-collapse, permission-rule-canonicalize)",
6
- "main": "src/index.ts",
7
- "types": "src/index.ts",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
8
  "exports": {
9
- ".": "./src/index.ts"
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
10
13
  },
11
14
  "scripts": {
12
15
  "test": "bun test src"
13
16
  },
14
17
  "dependencies": {
15
- "@crewhaus/errors": "0.1.4",
16
- "@crewhaus/ir": "0.1.4"
18
+ "@crewhaus/errors": "0.1.5",
19
+ "@crewhaus/ir": "0.1.5"
17
20
  },
18
21
  "license": "Apache-2.0",
19
22
  "author": {
@@ -33,5 +36,5 @@
33
36
  "publishConfig": {
34
37
  "access": "public"
35
38
  },
36
- "files": ["src", "README.md", "LICENSE", "NOTICE"]
39
+ "files": ["dist", "README.md", "LICENSE", "NOTICE"]
37
40
  }