arc-lang 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,115 @@
1
+ // Arc Module Resolver and Loader
2
+ import { readFileSync, existsSync } from "fs";
3
+ import { resolve, dirname } from "path";
4
+ import { fileURLToPath } from "url";
5
+ import { lex } from "./lexer.js";
6
+ import { parse } from "./parser.js";
7
+ import { createEnv, runStmt } from "./interpreter.js";
8
+ const __filename2 = fileURLToPath(import.meta.url);
9
+ const __dirname2 = dirname(__filename2);
10
+ const moduleCache = new Map();
11
+ export function clearModuleCache() {
12
+ moduleCache.clear();
13
+ }
14
+ /**
15
+ * Resolve a module path to a file path.
16
+ * Search order: stdlib/ first (searching upward), then relative to basePath.
17
+ * This prevents test files from shadowing stdlib modules.
18
+ */
19
+ export function resolveModule(path, basePath) {
20
+ const modulePath = path.join("/") + ".arc";
21
+ // 1. Search up from basePath for a stdlib/ directory (stdlib takes priority)
22
+ let dir = dirname(basePath);
23
+ for (let i = 0; i < 10; i++) {
24
+ const stdlibPath = resolve(dir, "stdlib", modulePath);
25
+ if (existsSync(stdlibPath))
26
+ return stdlibPath;
27
+ const parent = dirname(dir);
28
+ if (parent === dir)
29
+ break;
30
+ dir = parent;
31
+ }
32
+ // 2. Relative to current file's directory
33
+ const relPath = resolve(dirname(basePath), modulePath);
34
+ if (existsSync(relPath))
35
+ return relPath;
36
+ // 3. Check compiler's sibling stdlib/
37
+ const compilerStdlib = resolve(__dirname2, "..", "..", "stdlib", modulePath);
38
+ if (existsSync(compilerStdlib))
39
+ return compilerStdlib;
40
+ throw new Error(`Module not found: ${path.join("/")} (searched from ${basePath})`);
41
+ }
42
+ /**
43
+ * Load a module, parse it, execute it, and return its pub exports.
44
+ */
45
+ export function loadModule(filePath) {
46
+ const absPath = resolve(filePath);
47
+ if (moduleCache.has(absPath)) {
48
+ return moduleCache.get(absPath);
49
+ }
50
+ // Prevent circular imports — set empty first
51
+ moduleCache.set(absPath, {});
52
+ const source = readFileSync(absPath, "utf-8");
53
+ const tokens = lex(source);
54
+ const ast = parse(tokens);
55
+ const env = createEnv();
56
+ // Execute the module, handling nested use statements
57
+ for (const stmt of ast.stmts) {
58
+ if (stmt.kind === "UseStmt") {
59
+ handleUse(stmt, env, absPath);
60
+ }
61
+ else {
62
+ runStmt(stmt, env);
63
+ }
64
+ }
65
+ // Collect pub exports
66
+ const exports = {};
67
+ for (const stmt of ast.stmts) {
68
+ if (stmt.kind === "LetStmt") {
69
+ const ls = stmt;
70
+ if (ls.pub && typeof ls.name === "string") {
71
+ exports[ls.name] = env.get(ls.name);
72
+ }
73
+ }
74
+ else if (stmt.kind === "FnStmt") {
75
+ const fs = stmt;
76
+ if (fs.pub) {
77
+ exports[fs.name] = env.get(fs.name);
78
+ }
79
+ }
80
+ }
81
+ moduleCache.set(absPath, exports);
82
+ return exports;
83
+ }
84
+ /**
85
+ * Handle a use statement: resolve, load, and bind imports into env.
86
+ */
87
+ export function handleUse(stmt, env, currentFile) {
88
+ const modulePath = resolveModule(stmt.path, currentFile);
89
+ const exports = loadModule(modulePath);
90
+ if (stmt.wildcard) {
91
+ for (const [name, value] of Object.entries(exports)) {
92
+ env.set(name, value);
93
+ }
94
+ }
95
+ else if (stmt.imports && stmt.imports.length > 0) {
96
+ for (const name of stmt.imports) {
97
+ if (!(name in exports)) {
98
+ throw new Error(`Module ${stmt.path.join("/")} does not export '${name}'`);
99
+ }
100
+ env.set(name, exports[name]);
101
+ }
102
+ }
103
+ else {
104
+ // No selective imports: bind all exports
105
+ for (const [name, value] of Object.entries(exports)) {
106
+ env.set(name, value);
107
+ }
108
+ }
109
+ }
110
+ /**
111
+ * Create a UseHandler bound to a specific file path.
112
+ */
113
+ export function createUseHandler(currentFile) {
114
+ return (stmt, env) => handleUse(stmt, env, currentFile);
115
+ }
@@ -0,0 +1,17 @@
1
+ import { IRInstr, IRModule } from "./ir.js";
2
+ export type OptIRInstr = IRInstr | {
3
+ op: "parallel_toolcall";
4
+ dest: string;
5
+ calls: {
6
+ dest: string;
7
+ method: string;
8
+ url: string;
9
+ body?: string;
10
+ }[];
11
+ };
12
+ export declare function optimize(module: IRModule): IRModule;
13
+ export declare function optimizeWithBatching(module: IRModule): {
14
+ module: IRModule;
15
+ batchedMain: (IRInstr | OptIRInstr)[];
16
+ };
17
+ export declare function formatOptInstr(instr: IRInstr | OptIRInstr): string;
@@ -0,0 +1,481 @@
1
+ // Arc IR Optimizer — Multi-pass optimization for Arc's SSA IR
2
+ // ---- Pass 1: Constant Folding ----
3
+ function constantFolding(instrs) {
4
+ const constants = new Map();
5
+ const result = [];
6
+ for (const instr of instrs) {
7
+ if (instr.op === "const") {
8
+ constants.set(instr.dest, instr.value);
9
+ result.push(instr);
10
+ continue;
11
+ }
12
+ if (instr.op === "binop") {
13
+ const lv = constants.get(instr.left);
14
+ const rv = constants.get(instr.right);
15
+ if (lv !== undefined && rv !== undefined && typeof lv === "number" && typeof rv === "number") {
16
+ let folded = null;
17
+ let isBoolean = false;
18
+ switch (instr.operator) {
19
+ case "+":
20
+ folded = lv + rv;
21
+ break;
22
+ case "-":
23
+ folded = lv - rv;
24
+ break;
25
+ case "*":
26
+ folded = lv * rv;
27
+ break;
28
+ case "/":
29
+ folded = rv !== 0 ? lv / rv : null;
30
+ break;
31
+ case "%":
32
+ folded = rv !== 0 ? lv % rv : null;
33
+ break;
34
+ case "==":
35
+ folded = lv === rv;
36
+ isBoolean = true;
37
+ break;
38
+ case "!=":
39
+ folded = lv !== rv;
40
+ isBoolean = true;
41
+ break;
42
+ case "<":
43
+ folded = lv < rv;
44
+ isBoolean = true;
45
+ break;
46
+ case ">":
47
+ folded = lv > rv;
48
+ isBoolean = true;
49
+ break;
50
+ case "<=":
51
+ folded = lv <= rv;
52
+ isBoolean = true;
53
+ break;
54
+ case ">=":
55
+ folded = lv >= rv;
56
+ isBoolean = true;
57
+ break;
58
+ }
59
+ if (folded !== null) {
60
+ constants.set(instr.dest, folded);
61
+ result.push({ op: "const", dest: instr.dest, value: folded });
62
+ continue;
63
+ }
64
+ }
65
+ // String concat folding
66
+ if (instr.operator === "++" && typeof lv === "string" && typeof rv === "string") {
67
+ const folded = lv + rv;
68
+ constants.set(instr.dest, folded);
69
+ result.push({ op: "const", dest: instr.dest, value: folded });
70
+ continue;
71
+ }
72
+ // Boolean operator folding
73
+ if ((instr.operator === "and" || instr.operator === "or") && typeof lv === "boolean" && typeof rv === "boolean") {
74
+ const folded = instr.operator === "and" ? lv && rv : lv || rv;
75
+ constants.set(instr.dest, folded);
76
+ result.push({ op: "const", dest: instr.dest, value: folded });
77
+ continue;
78
+ }
79
+ }
80
+ if (instr.op === "unop") {
81
+ const v = constants.get(instr.operand);
82
+ if (v !== undefined) {
83
+ let folded = null;
84
+ if (instr.operator === "-" && typeof v === "number")
85
+ folded = -v;
86
+ if (instr.operator === "not" && typeof v === "boolean")
87
+ folded = !v;
88
+ if (folded !== null) {
89
+ constants.set(instr.dest, folded);
90
+ result.push({ op: "const", dest: instr.dest, value: folded });
91
+ continue;
92
+ }
93
+ }
94
+ }
95
+ result.push(instr);
96
+ }
97
+ return result;
98
+ }
99
+ // ---- Pass 2: Constant Propagation ----
100
+ function constantPropagation(instrs) {
101
+ // Build map of SSA temps that are assigned exactly once to a const
102
+ const constants = new Map();
103
+ for (const instr of instrs) {
104
+ if (instr.op === "const") {
105
+ constants.set(instr.dest, instr.value);
106
+ }
107
+ }
108
+ // Replace uses of constant temps
109
+ return instrs.map(instr => {
110
+ if (instr.op === "binop") {
111
+ return { ...instr };
112
+ }
113
+ return instr;
114
+ });
115
+ // Note: constant propagation mostly helps after folding converts binops to consts.
116
+ // The main benefit is enabling dead code elimination.
117
+ }
118
+ // ---- Pass 3: Dead Code Elimination ----
119
+ function deadCodeElimination(instrs) {
120
+ // Find all used temporaries
121
+ const used = new Set();
122
+ function addUses(instr) {
123
+ switch (instr.op) {
124
+ case "binop":
125
+ used.add(instr.left);
126
+ used.add(instr.right);
127
+ break;
128
+ case "unop":
129
+ used.add(instr.operand);
130
+ break;
131
+ case "call":
132
+ instr.args.forEach(a => used.add(a));
133
+ break;
134
+ case "toolcall":
135
+ used.add(instr.url);
136
+ if (instr.body)
137
+ used.add(instr.body);
138
+ break;
139
+ case "store":
140
+ used.add(instr.src);
141
+ break;
142
+ case "load": break;
143
+ case "field":
144
+ used.add(instr.obj);
145
+ break;
146
+ case "index":
147
+ used.add(instr.obj);
148
+ used.add(instr.idx);
149
+ break;
150
+ case "setfield":
151
+ used.add(instr.obj);
152
+ used.add(instr.src);
153
+ break;
154
+ case "setindex":
155
+ used.add(instr.obj);
156
+ used.add(instr.idx);
157
+ used.add(instr.src);
158
+ break;
159
+ case "branch":
160
+ used.add(instr.cond);
161
+ break;
162
+ case "ret":
163
+ if (instr.value)
164
+ used.add(instr.value);
165
+ break;
166
+ case "print":
167
+ used.add(instr.value);
168
+ break;
169
+ case "list":
170
+ instr.elements.forEach(e => used.add(e));
171
+ break;
172
+ case "map":
173
+ instr.keys.forEach(k => used.add(k));
174
+ instr.values.forEach(v => used.add(v));
175
+ break;
176
+ case "range":
177
+ used.add(instr.start);
178
+ used.add(instr.end);
179
+ break;
180
+ case "phi":
181
+ instr.sources.forEach(s => used.add(s.value));
182
+ break;
183
+ }
184
+ }
185
+ // Collect all uses
186
+ for (const instr of instrs) {
187
+ addUses(instr);
188
+ }
189
+ // Instructions with side effects are never dead
190
+ const hasSideEffect = (instr) => {
191
+ switch (instr.op) {
192
+ case "store":
193
+ case "setfield":
194
+ case "setindex":
195
+ case "call":
196
+ case "toolcall":
197
+ case "print":
198
+ case "ret":
199
+ case "jump":
200
+ case "branch":
201
+ case "label":
202
+ case "nop":
203
+ return true;
204
+ default:
205
+ return false;
206
+ }
207
+ };
208
+ // Remove instructions whose dest is never used (and have no side effects)
209
+ return instrs.filter(instr => {
210
+ if (hasSideEffect(instr))
211
+ return true;
212
+ // Check if this instr defines a dest that's used
213
+ const dest = instr.dest;
214
+ if (dest && !used.has(dest))
215
+ return false;
216
+ return true;
217
+ });
218
+ }
219
+ // ---- Pass 4: Common Subexpression Elimination ----
220
+ function commonSubexprElimination(instrs) {
221
+ const seen = new Map(); // key -> dest
222
+ const remap = new Map(); // old dest -> replacement dest
223
+ function resolve(name) {
224
+ return remap.get(name) ?? name;
225
+ }
226
+ const result = [];
227
+ for (const instr of instrs) {
228
+ if (instr.op === "binop") {
229
+ const left = resolve(instr.left);
230
+ const right = resolve(instr.right);
231
+ const key = `binop:${instr.operator}:${left}:${right}`;
232
+ const existing = seen.get(key);
233
+ if (existing) {
234
+ remap.set(instr.dest, existing);
235
+ continue; // eliminate duplicate
236
+ }
237
+ const newInstr = { ...instr, left, right };
238
+ seen.set(key, instr.dest);
239
+ result.push(newInstr);
240
+ }
241
+ else {
242
+ // Apply remapping to all operands
243
+ result.push(remapInstr(instr, remap));
244
+ }
245
+ }
246
+ return result;
247
+ }
248
+ function remapInstr(instr, remap) {
249
+ function r(name) { return remap.get(name) ?? name; }
250
+ switch (instr.op) {
251
+ case "binop": return { ...instr, left: r(instr.left), right: r(instr.right) };
252
+ case "unop": return { ...instr, operand: r(instr.operand) };
253
+ case "call": return { ...instr, args: instr.args.map(r) };
254
+ case "toolcall": return { ...instr, url: r(instr.url), body: instr.body ? r(instr.body) : undefined };
255
+ case "store": return { ...instr, src: r(instr.src) };
256
+ case "field": return { ...instr, obj: r(instr.obj) };
257
+ case "index": return { ...instr, obj: r(instr.obj), idx: r(instr.idx) };
258
+ case "setfield": return { ...instr, obj: r(instr.obj), src: r(instr.src) };
259
+ case "setindex": return { ...instr, obj: r(instr.obj), idx: r(instr.idx), src: r(instr.src) };
260
+ case "branch": return { ...instr, cond: r(instr.cond) };
261
+ case "ret": return { ...instr, value: instr.value ? r(instr.value) : undefined };
262
+ case "print": return { ...instr, value: r(instr.value) };
263
+ case "list": return { ...instr, elements: instr.elements.map(r) };
264
+ case "map": return { ...instr, keys: instr.keys.map(r), values: instr.values.map(r) };
265
+ case "range": return { ...instr, start: r(instr.start), end: r(instr.end) };
266
+ case "phi": return { ...instr, sources: instr.sources.map(s => ({ ...s, value: r(s.value) })) };
267
+ default: return instr;
268
+ }
269
+ }
270
+ // ---- Pass 5: Tool Call Batching (Arc-specific) ----
271
+ function toolCallBatching(instrs) {
272
+ // Find all toolcall instructions and check if they can be batched
273
+ // Two toolcalls are independent if neither uses the other's dest
274
+ const toolcallIndices = [];
275
+ for (let i = 0; i < instrs.length; i++) {
276
+ if (instrs[i].op === "toolcall")
277
+ toolcallIndices.push(i);
278
+ }
279
+ if (toolcallIndices.length < 2)
280
+ return [...instrs];
281
+ // Group consecutive-ish toolcalls (only separated by const/store that don't create dependencies)
282
+ const groups = [];
283
+ let currentGroup = [toolcallIndices[0]];
284
+ for (let g = 1; g < toolcallIndices.length; g++) {
285
+ const prevIdx = toolcallIndices[g - 1];
286
+ const currIdx = toolcallIndices[g];
287
+ const tc = instrs[currIdx];
288
+ // Check if any instruction between prev toolcall and this one uses a toolcall dest
289
+ const prevDests = new Set(currentGroup.map(i => instrs[i].dest));
290
+ const deps = [tc.url];
291
+ if (tc.body)
292
+ deps.push(tc.body);
293
+ // Check instructions between for side effects that would prevent reordering
294
+ let canBatch = true;
295
+ for (let j = prevIdx + 1; j < currIdx; j++) {
296
+ const between = instrs[j];
297
+ // If any instruction between uses a toolcall dest from current batch, can't batch
298
+ // Stores of toolcall results are fine - they just save the value
299
+ // Only block if a store's value is used by subsequent instructions before the next toolcall
300
+ if (between.op === "print") {
301
+ canBatch = false;
302
+ break;
303
+ }
304
+ if (between.op === "call") {
305
+ canBatch = false;
306
+ break;
307
+ }
308
+ if (between.op === "branch") {
309
+ canBatch = false;
310
+ break;
311
+ }
312
+ }
313
+ // Check if this toolcall depends on a previous one's result
314
+ if (deps.some(d => prevDests.has(d)))
315
+ canBatch = false;
316
+ if (canBatch) {
317
+ currentGroup.push(currIdx);
318
+ }
319
+ else {
320
+ groups.push(currentGroup);
321
+ currentGroup = [currIdx];
322
+ }
323
+ }
324
+ groups.push(currentGroup);
325
+ // Now emit: replace groups with parallel_toolcall
326
+ const batchedIndices = new Set();
327
+ const batchAtIndex = new Map(); // first toolcall index -> parallel
328
+ for (const group of groups) {
329
+ if (group.length < 2)
330
+ continue;
331
+ const calls = group.map(i => {
332
+ const tc = instrs[i];
333
+ batchedIndices.add(i);
334
+ return { dest: tc.dest, method: tc.method, url: tc.url, body: tc.body };
335
+ });
336
+ batchAtIndex.set(group[0], {
337
+ op: "parallel_toolcall",
338
+ dest: calls[0].dest,
339
+ calls,
340
+ });
341
+ }
342
+ const result = [];
343
+ for (let i = 0; i < instrs.length; i++) {
344
+ if (batchAtIndex.has(i)) {
345
+ result.push(batchAtIndex.get(i));
346
+ }
347
+ else if (!batchedIndices.has(i)) {
348
+ result.push(instrs[i]);
349
+ }
350
+ }
351
+ return result;
352
+ }
353
+ // ---- Pass 6: Pipeline Fusion (Arc-specific) ----
354
+ // Detect chains: call dest1 = map(x, f) ; call dest2 = filter(dest1, g) -> fused_map_filter
355
+ function pipelineFusion(instrs) {
356
+ // Build map of dest -> call instruction index for map/filter calls
357
+ const callByDest = new Map();
358
+ for (let i = 0; i < instrs.length; i++) {
359
+ const instr = instrs[i];
360
+ if (instr.op === "call" && (instr.fn === "map" || instr.fn === "filter")) {
361
+ callByDest.set(instr.dest, i);
362
+ }
363
+ }
364
+ const fused = new Set(); // indices to skip
365
+ const fusionMap = new Map(); // index -> replacement
366
+ for (let i = 0; i < instrs.length; i++) {
367
+ const instr = instrs[i];
368
+ if (instr.op === "call" && (instr.fn === "map" || instr.fn === "filter")) {
369
+ // Check if first arg is the dest of a map/filter call
370
+ const srcIdx = callByDest.get(instr.args[0]);
371
+ if (srcIdx !== undefined && !fused.has(srcIdx)) {
372
+ const src = instrs[srcIdx];
373
+ if ((src.fn === "map" && instr.fn === "filter") ||
374
+ (src.fn === "filter" && instr.fn === "map")) {
375
+ const fusedFn = src.fn === "map" ? "fused_map_filter" : "fused_filter_map";
376
+ fusionMap.set(i, {
377
+ op: "call",
378
+ dest: instr.dest,
379
+ fn: fusedFn,
380
+ args: [src.args[0], src.args[1], instr.args[1]],
381
+ });
382
+ fused.add(srcIdx);
383
+ }
384
+ }
385
+ }
386
+ }
387
+ const result = [];
388
+ for (let i = 0; i < instrs.length; i++) {
389
+ if (fused.has(i))
390
+ continue;
391
+ if (fusionMap.has(i)) {
392
+ result.push(fusionMap.get(i));
393
+ }
394
+ else {
395
+ result.push(instrs[i]);
396
+ }
397
+ }
398
+ return result;
399
+ }
400
+ // ---- Block-level: Remove unreachable blocks ----
401
+ function removeUnreachableBlocks(blocks) {
402
+ if (blocks.length === 0)
403
+ return blocks;
404
+ // Build set of reachable block labels
405
+ const reachable = new Set();
406
+ const queue = [blocks[0].label];
407
+ reachable.add(blocks[0].label);
408
+ // Build label -> block map
409
+ const blockMap = new Map();
410
+ for (const b of blocks)
411
+ blockMap.set(b.label, b);
412
+ // Also find jump/branch targets in instructions (including labels embedded in single-block IR)
413
+ function findTargets(instrs) {
414
+ const targets = [];
415
+ for (const instr of instrs) {
416
+ if (instr.op === "jump")
417
+ targets.push(instr.target);
418
+ if (instr.op === "branch") {
419
+ targets.push(instr.ifTrue);
420
+ targets.push(instr.ifFalse);
421
+ }
422
+ }
423
+ return targets;
424
+ }
425
+ while (queue.length > 0) {
426
+ const label = queue.pop();
427
+ const block = blockMap.get(label);
428
+ if (!block)
429
+ continue;
430
+ for (const target of findTargets(block.instrs)) {
431
+ if (!reachable.has(target)) {
432
+ reachable.add(target);
433
+ queue.push(target);
434
+ }
435
+ }
436
+ }
437
+ return blocks.filter(b => reachable.has(b.label));
438
+ }
439
+ // ---- Optimize a single block's instructions ----
440
+ function optimizeInstrs(instrs) {
441
+ let result = instrs;
442
+ // Run passes in order, iterating for convergence
443
+ for (let iter = 0; iter < 3; iter++) {
444
+ const prev = result.length;
445
+ result = constantFolding(result);
446
+ result = commonSubexprElimination(result);
447
+ result = pipelineFusion(result);
448
+ result = deadCodeElimination(result);
449
+ if (result.length === prev)
450
+ break;
451
+ }
452
+ return result;
453
+ }
454
+ // ---- Main optimize function ----
455
+ export function optimize(module) {
456
+ const optimizedFunctions = module.functions.map(fn => ({
457
+ ...fn,
458
+ blocks: removeUnreachableBlocks(fn.blocks.map(b => ({ ...b, instrs: optimizeInstrs(b.instrs) }))),
459
+ }));
460
+ const optimizedMain = removeUnreachableBlocks(module.main.map(b => ({ ...b, instrs: optimizeInstrs(b.instrs) })));
461
+ return {
462
+ functions: optimizedFunctions,
463
+ main: optimizedMain,
464
+ };
465
+ }
466
+ // Tool call batching runs separately since it produces extended IR
467
+ export function optimizeWithBatching(module) {
468
+ const optimized = optimize(module);
469
+ const mainInstrs = optimized.main.flatMap(b => b.instrs);
470
+ const batched = toolCallBatching(mainInstrs);
471
+ return { module: optimized, batchedMain: batched };
472
+ }
473
+ // Format optimized IR (including parallel_toolcall)
474
+ export function formatOptInstr(instr) {
475
+ if (instr.op === "parallel_toolcall") {
476
+ const pt = instr;
477
+ const calls = pt.calls.map(c => `${c.dest} = @${c.method} ${c.url}${c.body ? ` ${c.body}` : ""}`).join(", ");
478
+ return `parallel_toolcall [${calls}]`;
479
+ }
480
+ return ""; // Use printIR's formatInstr for regular instructions
481
+ }
@@ -0,0 +1,31 @@
1
+ export interface ArcToml {
2
+ package: {
3
+ name: string;
4
+ version: string;
5
+ description: string;
6
+ author: string;
7
+ license: string;
8
+ };
9
+ dependencies: Record<string, string>;
10
+ "dev-dependencies": Record<string, string>;
11
+ }
12
+ export declare function parseArcToml(content: string): ArcToml;
13
+ export declare function serializeArcToml(toml: ArcToml): string;
14
+ export declare function findArcToml(startDir?: string): string;
15
+ export declare function readToml(dir?: string): ArcToml;
16
+ export declare function writeToml(toml: ArcToml, dir?: string): void;
17
+ export interface LockEntry {
18
+ name: string;
19
+ version: string;
20
+ source: string;
21
+ integrity: string;
22
+ }
23
+ export declare function generateLockFile(toml: ArcToml): string;
24
+ export declare function pkgInit(dir?: string): void;
25
+ export declare function pkgAdd(name: string, options?: {
26
+ dev?: boolean;
27
+ dir?: string;
28
+ }): void;
29
+ export declare function pkgRemove(name: string, dir?: string): void;
30
+ export declare function pkgList(dir?: string): void;
31
+ export declare function pkgInstall(dir?: string): void;