stone-lang 0.1.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.
Files changed (68) hide show
  1. package/README.md +52 -0
  2. package/StoneEngine.js +879 -0
  3. package/StoneEngineService.js +1727 -0
  4. package/adapters/FileSystemAdapter.js +230 -0
  5. package/adapters/OutputAdapter.js +208 -0
  6. package/adapters/index.js +6 -0
  7. package/cli/CLIOutputAdapter.js +196 -0
  8. package/cli/DaemonClient.js +349 -0
  9. package/cli/JSONOutputAdapter.js +135 -0
  10. package/cli/ReplSession.js +567 -0
  11. package/cli/ViewerServer.js +590 -0
  12. package/cli/commands/check.js +84 -0
  13. package/cli/commands/daemon.js +189 -0
  14. package/cli/commands/kill.js +66 -0
  15. package/cli/commands/package.js +713 -0
  16. package/cli/commands/ps.js +65 -0
  17. package/cli/commands/run.js +537 -0
  18. package/cli/entry.js +169 -0
  19. package/cli/index.js +14 -0
  20. package/cli/stonec.js +358 -0
  21. package/cli/test-compiler.js +181 -0
  22. package/cli/viewer/index.html +495 -0
  23. package/daemon/IPCServer.js +455 -0
  24. package/daemon/ProcessManager.js +327 -0
  25. package/daemon/ProcessRunner.js +307 -0
  26. package/daemon/daemon.js +398 -0
  27. package/daemon/index.js +16 -0
  28. package/frontend/analysis/index.js +5 -0
  29. package/frontend/analysis/livenessAnalyzer.js +568 -0
  30. package/frontend/analysis/treeShaker.js +265 -0
  31. package/frontend/index.js +20 -0
  32. package/frontend/parsing/astBuilder.js +2196 -0
  33. package/frontend/parsing/index.js +7 -0
  34. package/frontend/parsing/sonParser.js +592 -0
  35. package/frontend/parsing/stoneAstTypes.js +703 -0
  36. package/frontend/parsing/terminal-registry.js +435 -0
  37. package/frontend/parsing/tokenizer.js +692 -0
  38. package/frontend/type-checker/OverloadedFunctionType.js +43 -0
  39. package/frontend/type-checker/TypeEnvironment.js +165 -0
  40. package/frontend/type-checker/bidirectionalInference.js +149 -0
  41. package/frontend/type-checker/index.js +10 -0
  42. package/frontend/type-checker/moduleAnalysis.js +248 -0
  43. package/frontend/type-checker/operatorMappings.js +35 -0
  44. package/frontend/type-checker/overloadResolution.js +605 -0
  45. package/frontend/type-checker/typeChecker.js +452 -0
  46. package/frontend/type-checker/typeCompatibility.js +389 -0
  47. package/frontend/type-checker/visitors/controlFlow.js +483 -0
  48. package/frontend/type-checker/visitors/functions.js +604 -0
  49. package/frontend/type-checker/visitors/index.js +38 -0
  50. package/frontend/type-checker/visitors/literals.js +341 -0
  51. package/frontend/type-checker/visitors/modules.js +159 -0
  52. package/frontend/type-checker/visitors/operators.js +109 -0
  53. package/frontend/type-checker/visitors/statements.js +768 -0
  54. package/frontend/types/index.js +5 -0
  55. package/frontend/types/operatorMap.js +134 -0
  56. package/frontend/types/types.js +2046 -0
  57. package/frontend/utils/errorCollector.js +244 -0
  58. package/frontend/utils/index.js +5 -0
  59. package/frontend/utils/moduleResolver.js +479 -0
  60. package/package.json +50 -0
  61. package/packages/browserCache.js +359 -0
  62. package/packages/fetcher.js +236 -0
  63. package/packages/index.js +130 -0
  64. package/packages/lockfile.js +271 -0
  65. package/packages/manifest.js +291 -0
  66. package/packages/packageResolver.js +356 -0
  67. package/packages/resolver.js +310 -0
  68. package/packages/semver.js +635 -0
@@ -0,0 +1,713 @@
1
+ /**
2
+ * Package commands - Manage Stone packages
3
+ *
4
+ * Usage:
5
+ * stone install Install dependencies from package.son
6
+ * stone install <pkg> --source <url> Add and install from URL
7
+ * stone install <pkg> --source <path> Add and install from local directory
8
+ * stone install -g <pkg> Install package globally
9
+ * stone remove <pkg> Remove a dependency
10
+ * stone update Update all dependencies
11
+ * stone update <pkg> Update specific package
12
+ * stone list List installed packages
13
+ * stone init Create new package.son
14
+ */
15
+
16
+ import fs from 'fs';
17
+ import path from 'path';
18
+ import { parseManifest, createManifest, stringifyManifest } from '../../packages/manifest.js';
19
+ import { parseLockFile, LockFile, stringifyLockFile } from '../../packages/lockfile.js';
20
+ import { DependencyResolver } from '../../packages/resolver.js';
21
+ import { createFetcher } from '../../packages/fetcher.js';
22
+ import { parseRange, maxSatisfying } from '../../packages/semver.js';
23
+
24
+ /**
25
+ * Check if a source is a URL or a local path
26
+ */
27
+ function isUrl(source) {
28
+ return source.startsWith('http://') || source.startsWith('https://');
29
+ }
30
+
31
+ /**
32
+ * Check if a source is a local path
33
+ */
34
+ function isLocalPath(source) {
35
+ // Absolute paths or relative paths starting with . or /
36
+ return !isUrl(source) && (
37
+ path.isAbsolute(source) ||
38
+ source.startsWith('./') ||
39
+ source.startsWith('../') ||
40
+ source.startsWith('.\\') ||
41
+ source.startsWith('..\\') ||
42
+ // Windows drive letters
43
+ /^[a-zA-Z]:/.test(source)
44
+ );
45
+ }
46
+
47
+ /**
48
+ * Find project root by walking up to find package.son
49
+ */
50
+ function findProjectRoot(startDir = process.cwd()) {
51
+ let currentDir = startDir;
52
+
53
+ while (currentDir) {
54
+ const manifestPath = path.join(currentDir, 'package.son');
55
+ if (fs.existsSync(manifestPath)) {
56
+ return currentDir;
57
+ }
58
+
59
+ const parent = path.dirname(currentDir);
60
+ if (parent === currentDir) break; // Hit root
61
+ currentDir = parent;
62
+ }
63
+
64
+ return null;
65
+ }
66
+
67
+ /**
68
+ * Get Stone home directory
69
+ */
70
+ function getStoneHome() {
71
+ if (process.env.STONE_HOME) {
72
+ return process.env.STONE_HOME;
73
+ }
74
+ const home = process.env.HOME || process.env.USERPROFILE;
75
+ if (home) {
76
+ return path.join(home, '.stone');
77
+ }
78
+ return null;
79
+ }
80
+
81
+ /**
82
+ * Initialize a new package.son
83
+ */
84
+ export async function initCommand(options = {}) {
85
+ const { json = false, force = false } = options;
86
+ const cwd = process.cwd();
87
+ const manifestPath = path.join(cwd, 'package.son');
88
+
89
+ if (fs.existsSync(manifestPath) && !force) {
90
+ if (json) {
91
+ console.log(JSON.stringify({ error: 'package.son already exists. Use --force to overwrite.' }));
92
+ } else {
93
+ console.error('package.son already exists. Use --force to overwrite.');
94
+ }
95
+ process.exit(1);
96
+ }
97
+
98
+ // Get project name from directory
99
+ const projectName = path.basename(cwd).toLowerCase().replace(/[^a-z0-9_-]/g, '-');
100
+
101
+ const manifest = createManifest(projectName, '1.0.0');
102
+ const content = stringifyManifest(manifest);
103
+
104
+ fs.writeFileSync(manifestPath, content);
105
+
106
+ if (json) {
107
+ console.log(JSON.stringify({ success: true, file: manifestPath }));
108
+ } else {
109
+ console.log(`Created package.son`);
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Install dependencies
115
+ */
116
+ export async function installCommand(pkg = null, options = {}) {
117
+ const { json = false, global: isGlobal = false, source = null, save = true } = options;
118
+
119
+ // Determine target directory
120
+ let targetDir;
121
+ let manifestPath;
122
+
123
+ if (isGlobal) {
124
+ targetDir = getStoneHome();
125
+ if (!targetDir) {
126
+ const msg = 'Could not determine Stone home directory';
127
+ if (json) {
128
+ console.log(JSON.stringify({ error: msg }));
129
+ } else {
130
+ console.error(msg);
131
+ }
132
+ process.exit(1);
133
+ }
134
+ manifestPath = path.join(targetDir, 'package.son');
135
+
136
+ // Create global dirs if needed
137
+ if (!fs.existsSync(targetDir)) {
138
+ fs.mkdirSync(targetDir, { recursive: true });
139
+ }
140
+ const globalModules = path.join(targetDir, 'stone_modules');
141
+ if (!fs.existsSync(globalModules)) {
142
+ fs.mkdirSync(globalModules, { recursive: true });
143
+ }
144
+
145
+ // Create global package.son if needed
146
+ if (!fs.existsSync(manifestPath)) {
147
+ const manifest = createManifest('global', '0.0.0');
148
+ fs.writeFileSync(manifestPath, stringifyManifest(manifest));
149
+ }
150
+ } else {
151
+ targetDir = findProjectRoot();
152
+ if (!targetDir) {
153
+ const msg = 'No package.son found. Run `stone init` first.';
154
+ if (json) {
155
+ console.log(JSON.stringify({ error: msg }));
156
+ } else {
157
+ console.error(msg);
158
+ }
159
+ process.exit(1);
160
+ }
161
+ manifestPath = path.join(targetDir, 'package.son');
162
+ }
163
+
164
+ // Read manifest
165
+ const manifestContent = fs.readFileSync(manifestPath, 'utf8');
166
+ const manifest = parseManifest(manifestContent, manifestPath);
167
+
168
+ if (pkg) {
169
+ // Install specific package
170
+ await installPackage(pkg, source, manifest, manifestPath, targetDir, options);
171
+ } else {
172
+ // Install all dependencies from manifest
173
+ await installAllDependencies(manifest, targetDir, options);
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Install a package from a local directory
179
+ */
180
+ async function installFromLocalPath(pkg, sourcePath, manifest, manifestPath, targetDir, options) {
181
+ const { json = false, save = true } = options;
182
+
183
+ // Resolve the source path
184
+ const resolvedSource = path.resolve(sourcePath);
185
+
186
+ if (!fs.existsSync(resolvedSource)) {
187
+ throw new Error(`Source directory not found: ${resolvedSource}`);
188
+ }
189
+
190
+ const stat = fs.statSync(resolvedSource);
191
+ if (!stat.isDirectory()) {
192
+ throw new Error(`Source is not a directory: ${resolvedSource}`);
193
+ }
194
+
195
+ // Read the source package.son if it exists
196
+ const sourceManifestPath = path.join(resolvedSource, 'package.son');
197
+ let pkgManifest = null;
198
+ let version = '*';
199
+
200
+ if (fs.existsSync(sourceManifestPath)) {
201
+ try {
202
+ const content = fs.readFileSync(sourceManifestPath, 'utf8');
203
+ pkgManifest = parseManifest(content, sourceManifestPath);
204
+ version = pkgManifest.version || '*';
205
+ } catch (e) {
206
+ // Ignore manifest parse errors, proceed without version info
207
+ }
208
+ }
209
+
210
+ // Install to stone_modules
211
+ const modulesDir = path.join(targetDir, 'stone_modules', pkg);
212
+
213
+ // Remove existing if present
214
+ if (fs.existsSync(modulesDir)) {
215
+ fs.rmSync(modulesDir, { recursive: true, force: true });
216
+ }
217
+
218
+ // Copy all files from source to target
219
+ copyDirectorySync(resolvedSource, modulesDir);
220
+
221
+ // Update project manifest
222
+ if (save) {
223
+ // Store the absolute path as source for local packages
224
+ manifest.addDependency(pkg, version === '*' ? '*' : `^${version}`, resolvedSource);
225
+ fs.writeFileSync(manifestPath, stringifyManifest(manifest));
226
+ }
227
+
228
+ return { version };
229
+ }
230
+
231
+ /**
232
+ * Recursively copy a directory
233
+ */
234
+ function copyDirectorySync(src, dest) {
235
+ fs.mkdirSync(dest, { recursive: true });
236
+
237
+ const entries = fs.readdirSync(src, { withFileTypes: true });
238
+
239
+ for (const entry of entries) {
240
+ const srcPath = path.join(src, entry.name);
241
+ const destPath = path.join(dest, entry.name);
242
+
243
+ if (entry.isDirectory()) {
244
+ copyDirectorySync(srcPath, destPath);
245
+ } else {
246
+ fs.copyFileSync(srcPath, destPath);
247
+ }
248
+ }
249
+ }
250
+
251
+ /**
252
+ * Install a specific package
253
+ */
254
+ async function installPackage(pkg, source, manifest, manifestPath, targetDir, options) {
255
+ const { json = false, save = true } = options;
256
+
257
+ if (!source) {
258
+ const msg = `Source required. Use: stone install ${pkg} --source <url-or-path>`;
259
+ if (json) {
260
+ console.log(JSON.stringify({ error: msg }));
261
+ } else {
262
+ console.error(msg);
263
+ }
264
+ process.exit(1);
265
+ }
266
+
267
+ // Check if source is a local path or URL
268
+ if (isLocalPath(source)) {
269
+ // Install from local directory
270
+ if (!json) {
271
+ console.log(`Installing ${pkg} from local path ${source}...`);
272
+ }
273
+
274
+ try {
275
+ const result = await installFromLocalPath(pkg, source, manifest, manifestPath, targetDir, options);
276
+
277
+ if (json) {
278
+ console.log(JSON.stringify({ success: true, package: pkg, version: result.version, source: 'local' }));
279
+ } else {
280
+ console.log(` Installed ${pkg}@${result.version} (from local)`);
281
+ }
282
+ } catch (err) {
283
+ if (json) {
284
+ console.log(JSON.stringify({ error: err.message }));
285
+ } else {
286
+ console.error(`Failed to install ${pkg}: ${err.message}`);
287
+ }
288
+ process.exit(1);
289
+ }
290
+ return;
291
+ }
292
+
293
+ // Install from URL
294
+ if (!json) {
295
+ console.log(`Installing ${pkg} from ${source}...`);
296
+ }
297
+
298
+ const fetcher = createFetcher();
299
+
300
+ try {
301
+ // Fetch available versions
302
+ const versionInfo = await fetcher.fetchVersions(pkg, source);
303
+
304
+ if (!versionInfo.versions || versionInfo.versions.length === 0) {
305
+ // No versions.son - try fetching latest directly
306
+ if (!json) {
307
+ console.log(` Fetching package...`);
308
+ }
309
+
310
+ const pkgData = await fetcher.fetchPackage(pkg, 'latest', source);
311
+
312
+ // Install to stone_modules
313
+ const modulesDir = path.join(targetDir, 'stone_modules', pkg);
314
+ fs.mkdirSync(modulesDir, { recursive: true });
315
+
316
+ // Write files
317
+ for (const [filename, content] of pkgData.files) {
318
+ const filePath = path.join(modulesDir, filename);
319
+ const fileDir = path.dirname(filePath);
320
+ if (!fs.existsSync(fileDir)) {
321
+ fs.mkdirSync(fileDir, { recursive: true });
322
+ }
323
+ fs.writeFileSync(filePath, content);
324
+ }
325
+
326
+ // Write package.son if available
327
+ if (pkgData.manifest) {
328
+ fs.writeFileSync(
329
+ path.join(modulesDir, 'package.son'),
330
+ JSON.stringify(pkgData.manifest, null, 2) // TODO: use stringifySON
331
+ );
332
+ }
333
+
334
+ // Update project manifest
335
+ if (save) {
336
+ manifest.addDependency(pkg, pkgData.manifest?.version || '*', source);
337
+ fs.writeFileSync(manifestPath, stringifyManifest(manifest));
338
+ }
339
+
340
+ if (json) {
341
+ console.log(JSON.stringify({ success: true, package: pkg, version: pkgData.manifest?.version }));
342
+ } else {
343
+ console.log(` Installed ${pkg}@${pkgData.manifest?.version || 'latest'}`);
344
+ }
345
+ } else {
346
+ // Pick highest version
347
+ const version = versionInfo.versions[versionInfo.versions.length - 1];
348
+
349
+ if (!json) {
350
+ console.log(` Found version ${version}`);
351
+ }
352
+
353
+ const pkgData = await fetcher.fetchPackage(pkg, version, source);
354
+
355
+ // Install to stone_modules
356
+ const modulesDir = path.join(targetDir, 'stone_modules', pkg);
357
+ fs.mkdirSync(modulesDir, { recursive: true });
358
+
359
+ for (const [filename, content] of pkgData.files) {
360
+ const filePath = path.join(modulesDir, filename);
361
+ const fileDir = path.dirname(filePath);
362
+ if (!fs.existsSync(fileDir)) {
363
+ fs.mkdirSync(fileDir, { recursive: true });
364
+ }
365
+ fs.writeFileSync(filePath, content);
366
+ }
367
+
368
+ // Update project manifest
369
+ if (save) {
370
+ manifest.addDependency(pkg, `^${version}`, source);
371
+ fs.writeFileSync(manifestPath, stringifyManifest(manifest));
372
+ }
373
+
374
+ if (json) {
375
+ console.log(JSON.stringify({ success: true, package: pkg, version }));
376
+ } else {
377
+ console.log(` Installed ${pkg}@${version}`);
378
+ }
379
+ }
380
+ } catch (err) {
381
+ if (json) {
382
+ console.log(JSON.stringify({ error: err.message }));
383
+ } else {
384
+ console.error(`Failed to install ${pkg}: ${err.message}`);
385
+ }
386
+ process.exit(1);
387
+ }
388
+ }
389
+
390
+ /**
391
+ * Install all dependencies from manifest
392
+ */
393
+ async function installAllDependencies(manifest, targetDir, options) {
394
+ const { json = false } = options;
395
+
396
+ const deps = manifest.getNormalizedDependencies();
397
+ const depNames = Object.keys(deps);
398
+
399
+ if (depNames.length === 0) {
400
+ if (json) {
401
+ console.log(JSON.stringify({ success: true, installed: 0 }));
402
+ } else {
403
+ console.log('No dependencies to install.');
404
+ }
405
+ return;
406
+ }
407
+
408
+ if (!json) {
409
+ console.log(`Installing ${depNames.length} dependencies...`);
410
+ }
411
+
412
+ const fetcher = createFetcher();
413
+ const installed = [];
414
+ const errors = [];
415
+
416
+ for (const name of depNames) {
417
+ const spec = deps[name];
418
+
419
+ try {
420
+ if (!json) {
421
+ console.log(` ${name}...`);
422
+ }
423
+
424
+ // Check if source is a local path
425
+ if (spec.source && isLocalPath(spec.source)) {
426
+ // Install from local directory
427
+ const resolvedSource = path.resolve(spec.source);
428
+
429
+ if (!fs.existsSync(resolvedSource)) {
430
+ throw new Error(`Source directory not found: ${resolvedSource}`);
431
+ }
432
+
433
+ // Read the source package.son if it exists
434
+ const sourceManifestPath = path.join(resolvedSource, 'package.son');
435
+ let version = spec.version || '*';
436
+
437
+ if (fs.existsSync(sourceManifestPath)) {
438
+ try {
439
+ const content = fs.readFileSync(sourceManifestPath, 'utf8');
440
+ const pkgManifest = parseManifest(content, sourceManifestPath);
441
+ version = pkgManifest.version || version;
442
+ } catch (e) {
443
+ // Ignore manifest parse errors
444
+ }
445
+ }
446
+
447
+ // Install to stone_modules
448
+ const modulesDir = path.join(targetDir, 'stone_modules', name);
449
+
450
+ // Remove existing if present
451
+ if (fs.existsSync(modulesDir)) {
452
+ fs.rmSync(modulesDir, { recursive: true, force: true });
453
+ }
454
+
455
+ // Copy all files
456
+ copyDirectorySync(resolvedSource, modulesDir);
457
+
458
+ installed.push({ name, version });
459
+
460
+ if (!json) {
461
+ console.log(` ✓ ${name}@${version} (local)`);
462
+ }
463
+ } else {
464
+ // Install from URL
465
+ const versionInfo = await fetcher.fetchVersions(name, spec.source);
466
+
467
+ let version;
468
+ if (versionInfo.versions && versionInfo.versions.length > 0) {
469
+ version = maxSatisfying(versionInfo.versions, spec.version);
470
+ if (!version) {
471
+ throw new Error(`No version satisfies ${spec.version}`);
472
+ }
473
+ } else {
474
+ version = 'latest';
475
+ }
476
+
477
+ const pkgData = await fetcher.fetchPackage(name, version, spec.source);
478
+
479
+ // Install to stone_modules
480
+ const modulesDir = path.join(targetDir, 'stone_modules', name);
481
+ fs.mkdirSync(modulesDir, { recursive: true });
482
+
483
+ for (const [filename, content] of pkgData.files) {
484
+ const filePath = path.join(modulesDir, filename);
485
+ const fileDir = path.dirname(filePath);
486
+ if (!fs.existsSync(fileDir)) {
487
+ fs.mkdirSync(fileDir, { recursive: true });
488
+ }
489
+ fs.writeFileSync(filePath, content);
490
+ }
491
+
492
+ installed.push({ name, version: pkgData.manifest?.version || version });
493
+
494
+ if (!json) {
495
+ console.log(` ✓ ${name}@${pkgData.manifest?.version || version}`);
496
+ }
497
+ }
498
+ } catch (err) {
499
+ errors.push({ name, error: err.message });
500
+ if (!json) {
501
+ console.error(` ✗ ${name}: ${err.message}`);
502
+ }
503
+ }
504
+ }
505
+
506
+ // Write lock file
507
+ const lockFile = new LockFile();
508
+ for (const { name, version } of installed) {
509
+ const spec = deps[name];
510
+ lockFile.setPackage(name, version, spec.source);
511
+ }
512
+ const lockPath = path.join(targetDir, 'package.lock.son');
513
+ fs.writeFileSync(lockPath, stringifyLockFile(lockFile));
514
+
515
+ if (json) {
516
+ console.log(JSON.stringify({
517
+ success: errors.length === 0,
518
+ installed: installed.length,
519
+ errors: errors.length > 0 ? errors : undefined,
520
+ }));
521
+ } else {
522
+ console.log(`\nInstalled ${installed.length}/${depNames.length} packages.`);
523
+ if (errors.length > 0) {
524
+ console.log(`${errors.length} errors occurred.`);
525
+ }
526
+ }
527
+
528
+ if (errors.length > 0) {
529
+ process.exit(1);
530
+ }
531
+ }
532
+
533
+ /**
534
+ * Remove a package
535
+ */
536
+ export async function removeCommand(pkg, options = {}) {
537
+ const { json = false, global: isGlobal = false } = options;
538
+
539
+ let targetDir;
540
+ let manifestPath;
541
+
542
+ if (isGlobal) {
543
+ targetDir = getStoneHome();
544
+ if (!targetDir) {
545
+ const msg = 'Could not determine Stone home directory';
546
+ if (json) console.log(JSON.stringify({ error: msg }));
547
+ else console.error(msg);
548
+ process.exit(1);
549
+ }
550
+ manifestPath = path.join(targetDir, 'package.son');
551
+ } else {
552
+ targetDir = findProjectRoot();
553
+ if (!targetDir) {
554
+ const msg = 'No package.son found.';
555
+ if (json) console.log(JSON.stringify({ error: msg }));
556
+ else console.error(msg);
557
+ process.exit(1);
558
+ }
559
+ manifestPath = path.join(targetDir, 'package.son');
560
+ }
561
+
562
+ // Read manifest
563
+ const manifestContent = fs.readFileSync(manifestPath, 'utf8');
564
+ const manifest = parseManifest(manifestContent, manifestPath);
565
+
566
+ if (!manifest.hasDependency(pkg)) {
567
+ const msg = `Package ${pkg} is not a dependency`;
568
+ if (json) console.log(JSON.stringify({ error: msg }));
569
+ else console.error(msg);
570
+ process.exit(1);
571
+ }
572
+
573
+ // Remove from manifest
574
+ manifest.removeDependency(pkg);
575
+ fs.writeFileSync(manifestPath, stringifyManifest(manifest));
576
+
577
+ // Remove from stone_modules
578
+ const moduleDir = path.join(targetDir, 'stone_modules', pkg);
579
+ if (fs.existsSync(moduleDir)) {
580
+ fs.rmSync(moduleDir, { recursive: true, force: true });
581
+ }
582
+
583
+ if (json) {
584
+ console.log(JSON.stringify({ success: true, removed: pkg }));
585
+ } else {
586
+ console.log(`Removed ${pkg}`);
587
+ }
588
+ }
589
+
590
+ /**
591
+ * Update dependencies
592
+ */
593
+ export async function updateCommand(pkg = null, options = {}) {
594
+ const { json = false } = options;
595
+
596
+ const targetDir = findProjectRoot();
597
+ if (!targetDir) {
598
+ const msg = 'No package.son found.';
599
+ if (json) console.log(JSON.stringify({ error: msg }));
600
+ else console.error(msg);
601
+ process.exit(1);
602
+ }
603
+
604
+ // For now, just re-install
605
+ // TODO: Implement proper update logic with version bumping
606
+ if (!json) {
607
+ console.log('Updating dependencies...');
608
+ }
609
+
610
+ const manifestPath = path.join(targetDir, 'package.son');
611
+ const manifestContent = fs.readFileSync(manifestPath, 'utf8');
612
+ const manifest = parseManifest(manifestContent, manifestPath);
613
+
614
+ await installAllDependencies(manifest, targetDir, options);
615
+ }
616
+
617
+ /**
618
+ * List installed packages
619
+ */
620
+ export async function listCommand(options = {}) {
621
+ const { json = false, global: isGlobal = false } = options;
622
+
623
+ let targetDir;
624
+ let manifestPath;
625
+
626
+ if (isGlobal) {
627
+ targetDir = getStoneHome();
628
+ if (!targetDir) {
629
+ const msg = 'Could not determine Stone home directory';
630
+ if (json) console.log(JSON.stringify({ error: msg }));
631
+ else console.error(msg);
632
+ process.exit(1);
633
+ }
634
+ manifestPath = path.join(targetDir, 'package.son');
635
+ } else {
636
+ targetDir = findProjectRoot();
637
+ if (!targetDir) {
638
+ const msg = 'No package.son found.';
639
+ if (json) console.log(JSON.stringify({ error: msg }));
640
+ else console.error(msg);
641
+ process.exit(1);
642
+ }
643
+ manifestPath = path.join(targetDir, 'package.son');
644
+ }
645
+
646
+ if (!fs.existsSync(manifestPath)) {
647
+ if (json) {
648
+ console.log(JSON.stringify({ packages: [] }));
649
+ } else {
650
+ console.log('No packages installed.');
651
+ }
652
+ return;
653
+ }
654
+
655
+ const manifestContent = fs.readFileSync(manifestPath, 'utf8');
656
+ const manifest = parseManifest(manifestContent, manifestPath);
657
+
658
+ const deps = manifest.getNormalizedDependencies();
659
+ const packages = [];
660
+
661
+ // Check what's actually installed
662
+ const modulesDir = path.join(targetDir, 'stone_modules');
663
+ for (const [name, spec] of Object.entries(deps)) {
664
+ const pkgDir = path.join(modulesDir, name);
665
+ const installed = fs.existsSync(pkgDir);
666
+
667
+ let installedVersion = null;
668
+ if (installed) {
669
+ const pkgManifest = path.join(pkgDir, 'package.son');
670
+ if (fs.existsSync(pkgManifest)) {
671
+ try {
672
+ const content = fs.readFileSync(pkgManifest, 'utf8');
673
+ const pkg = parseManifest(content, pkgManifest);
674
+ installedVersion = pkg.version;
675
+ } catch (e) {
676
+ // Ignore parse errors
677
+ }
678
+ }
679
+ }
680
+
681
+ packages.push({
682
+ name,
683
+ wanted: spec.version,
684
+ installed: installedVersion,
685
+ source: spec.source,
686
+ });
687
+ }
688
+
689
+ if (json) {
690
+ console.log(JSON.stringify({ packages }));
691
+ } else {
692
+ if (packages.length === 0) {
693
+ console.log('No packages.');
694
+ } else {
695
+ console.log(`${isGlobal ? 'Global' : 'Project'} packages:\n`);
696
+ for (const pkg of packages) {
697
+ const status = pkg.installed ? `@${pkg.installed}` : '(not installed)';
698
+ console.log(` ${pkg.name}${status}`);
699
+ if (pkg.source) {
700
+ console.log(` source: ${pkg.source}`);
701
+ }
702
+ }
703
+ }
704
+ }
705
+ }
706
+
707
+ export default {
708
+ initCommand,
709
+ installCommand,
710
+ removeCommand,
711
+ updateCommand,
712
+ listCommand,
713
+ };