magmascript 3.2.0__tar.gz → 3.2.2__tar.gz

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 (100) hide show
  1. {magmascript-3.2.0 → magmascript-3.2.2}/PKG-INFO +53 -25
  2. {magmascript-3.2.0 → magmascript-3.2.2}/README.md +48 -22
  3. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/__init__.py +1 -1
  4. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/cli.py +248 -138
  5. magmascript-3.2.2/magmascript/codegen/__init__.py +23 -0
  6. magmascript-3.2.2/magmascript/codegen/c.py +284 -0
  7. magmascript-3.2.2/magmascript/core/command_registry.py +55 -0
  8. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/core/github.py +1 -1
  9. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/mc1/client.py +22 -0
  10. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/mcp/client.py +3 -3
  11. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/lang/hypnagogia.py +92 -67
  12. magmascript-3.2.2/magmascript/lang/pretty.py +262 -0
  13. magmascript-3.2.2/magmascript/lang/visitor.py +100 -0
  14. magmascript-3.2.2/magmascript/scaffold/__init__.py +70 -0
  15. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript.egg-info/PKG-INFO +53 -25
  16. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript.egg-info/SOURCES.txt +6 -0
  17. {magmascript-3.2.0 → magmascript-3.2.2}/pyproject.toml +5 -3
  18. {magmascript-3.2.0 → magmascript-3.2.2}/LICENSE +0 -0
  19. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/core/__init__.py +0 -0
  20. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/core/cache.py +0 -0
  21. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/core/commands.py +0 -0
  22. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/core/config.py +0 -0
  23. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/core/exceptions.py +0 -0
  24. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/core/output.py +0 -0
  25. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/core/registry.py +0 -0
  26. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/core/rpc.py +0 -0
  27. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/core/runner.py +0 -0
  28. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/__init__.py +0 -0
  29. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/archive/__init__.py +0 -0
  30. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/archive/client.py +0 -0
  31. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/gh/__init__.py +0 -0
  32. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/gh/client.py +0 -0
  33. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/gh/tools.py +0 -0
  34. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/lastfm/__init__.py +0 -0
  35. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/lastfm/client.py +0 -0
  36. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/mac/__init__.py +0 -0
  37. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/mac/client.py +0 -0
  38. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/mac/tools.py +0 -0
  39. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/mb/__init__.py +0 -0
  40. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/mb/client.py +0 -0
  41. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/mc1/__init__.py +0 -0
  42. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/mc1/tools.py +0 -0
  43. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/mcp/__init__.py +0 -0
  44. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/mcp/tools.py +0 -0
  45. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/media/__init__.py +0 -0
  46. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/media/client.py +0 -0
  47. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/media/providers/__init__.py +0 -0
  48. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/media/providers/archive.py +0 -0
  49. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/media/providers/met_museum.py +0 -0
  50. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/media/providers/openverse.py +0 -0
  51. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/media/providers/pexels.py +0 -0
  52. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/media/providers/pixabay.py +0 -0
  53. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/media/providers/smithsonian.py +0 -0
  54. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/media/tools.py +0 -0
  55. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/pi/__init__.py +0 -0
  56. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/pi/client.py +0 -0
  57. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/pi/tools.py +0 -0
  58. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/rights/__init__.py +0 -0
  59. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/rights/client.py +0 -0
  60. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/rights/tools.py +0 -0
  61. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/scores/__init__.py +0 -0
  62. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/scores/client.py +0 -0
  63. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/scores/tools.py +0 -0
  64. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/search/__init__.py +0 -0
  65. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/domains/search/client.py +0 -0
  66. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/lang/__init__.py +0 -0
  67. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/lang/ast_nodes.py +0 -0
  68. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/lang/astheno/__init__.py +0 -0
  69. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/lang/astheno/arena.py +0 -0
  70. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/lang/astheno/dump.py +0 -0
  71. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/lang/astheno/floorplan.py +0 -0
  72. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/lang/astheno/numeric.py +0 -0
  73. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/lang/builtins.py +0 -0
  74. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/lang/domain_bridge.py +0 -0
  75. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/lang/environment.py +0 -0
  76. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/lang/interpreter.py +0 -0
  77. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/lang/lexer.py +0 -0
  78. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/lang/parser.py +0 -0
  79. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/lang/pygments_mgs.py +0 -0
  80. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/lang/tokens.py +0 -0
  81. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/lang/util.py +0 -0
  82. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript/repl.py +0 -0
  83. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript.egg-info/dependency_links.txt +0 -0
  84. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript.egg-info/entry_points.txt +0 -0
  85. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript.egg-info/requires.txt +0 -0
  86. {magmascript-3.2.0 → magmascript-3.2.2}/magmascript.egg-info/top_level.txt +0 -0
  87. {magmascript-3.2.0 → magmascript-3.2.2}/setup.cfg +0 -0
  88. {magmascript-3.2.0 → magmascript-3.2.2}/tests/test_astheno.py +0 -0
  89. {magmascript-3.2.0 → magmascript-3.2.2}/tests/test_cache.py +0 -0
  90. {magmascript-3.2.0 → magmascript-3.2.2}/tests/test_classes.py +0 -0
  91. {magmascript-3.2.0 → magmascript-3.2.2}/tests/test_cli.py +0 -0
  92. {magmascript-3.2.0 → magmascript-3.2.2}/tests/test_commands.py +0 -0
  93. {magmascript-3.2.0 → magmascript-3.2.2}/tests/test_domain_bridge.py +0 -0
  94. {magmascript-3.2.0 → magmascript-3.2.2}/tests/test_import.py +0 -0
  95. {magmascript-3.2.0 → magmascript-3.2.2}/tests/test_lang.py +0 -0
  96. {magmascript-3.2.0 → magmascript-3.2.2}/tests/test_mac.py +0 -0
  97. {magmascript-3.2.0 → magmascript-3.2.2}/tests/test_mc1.py +0 -0
  98. {magmascript-3.2.0 → magmascript-3.2.2}/tests/test_mcp_client.py +0 -0
  99. {magmascript-3.2.0 → magmascript-3.2.2}/tests/test_rpc.py +0 -0
  100. {magmascript-3.2.0 → magmascript-3.2.2}/tests/test_try_catch.py +0 -0
@@ -1,11 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: magmascript
3
- Version: 3.2.0
3
+ Version: 3.2.2
4
4
  Summary: Scripting toolkit with domain-first subcommands
5
5
  Author: Jake McCoy
6
6
  License-Expression: MIT
7
- Project-URL: Homepage, https://github.com/magmacrunchmedia/magmascript
8
- Project-URL: Repository, https://github.com/magmacrunchmedia/magmascript
7
+ Project-URL: Homepage, https://magmacrunch.com/ware/magmascript/
8
+ Project-URL: Documentation, https://github.com/magmacrunch-media/magmascript/wiki
9
+ Project-URL: Repository, https://github.com/magmacrunch-media/magmascript
10
+ Project-URL: Changelog, https://github.com/magmacrunch-media/magmascript/blob/main/CHANGELOG.md
9
11
  Keywords: mcp,scripting,cli,magmacrunch
10
12
  Classifier: Development Status :: 3 - Alpha
11
13
  Classifier: Environment :: Console
@@ -39,8 +41,30 @@ Scripting toolkit with domain-first subcommands for managing magmacrunch.com inf
39
41
 
40
42
  ## Install
41
43
 
44
+ Requires Python >= 3.11.
45
+
46
+ ### pip (recommended)
47
+
48
+ ```bash
49
+ pip install magmascript
50
+ ```
51
+
52
+ ### pipx (best for CLI-only usage)
53
+
42
54
  ```bash
43
- git clone https://github.com/magmacrunchmedia/magmascript.git
55
+ pipx install magmascript
56
+ ```
57
+
58
+ ### Homebrew (macOS / Linux)
59
+
60
+ ```bash
61
+ brew install magmacrunch-media/tap/magmascript
62
+ ```
63
+
64
+ ### Development
65
+
66
+ ```bash
67
+ git clone https://github.com/magmacrunch-media/magmascript.git
44
68
  cd magmascript
45
69
  python3 -m venv .venv
46
70
  .venv/bin/pip install -e ".[all]"
@@ -71,7 +95,7 @@ user = "jake"
71
95
 
72
96
  [gh]
73
97
  token = "ghp_..."
74
- owner = "magmacrunchmedia"
98
+ owner = "magmacrunch-media"
75
99
  repo = "magmacrunch.com"
76
100
 
77
101
  [project]
@@ -396,7 +420,7 @@ this ground was scorched at line 3 (garrisoned at line 1)
396
420
 
397
421
  `floorplan` is the only new reserved word; everything else is a shadowable
398
422
  builtin, so existing scripts are unaffected. Full reference on the
399
- [Asthenosphere wiki page](https://github.com/magmacrunchmedia/magmascript/wiki/Asthenosphere).
423
+ [Asthenosphere wiki page](https://github.com/magmacrunch-media/magmascript/wiki/Asthenosphere).
400
424
 
401
425
  ### REPL
402
426
 
@@ -468,6 +492,10 @@ Also on the CLI: `magmascript mc1 status`, `mc1 info`, `mc1 processes`,
468
492
  `mc1 restart <service>`, `mc1 power`, `mc1 set-power-mode <always-on|sleep>`,
469
493
  `mc1 wake <mac>`, `mc1 reboot`.
470
494
 
495
+ > **Note:** MC1 commands connect over SSH. Don't run them on MC1 itself — they
496
+ > will try to SSH into the configured host, which on MC1 means connecting to
497
+ > itself and hanging. Use these from your Mac, Pi, or any other machine.
498
+
471
499
  ### Mac Domain — Mac management (via SSH)
472
500
  ```magmascript
473
501
  // In a .mgs script, or: magmascript mac info
@@ -600,7 +628,7 @@ first use, not at startup, so a domain may hold real resources. Built-in domains
600
628
  win a name clash, and an entry point that fails to import is skipped rather than
601
629
  breaking the interpreter.
602
630
 
603
- [texastoast](https://github.com/magmacrunchmedia/texastoast) is the first:
631
+ [texastoast](https://github.com/magmacrunch-media/texastoast) is the first:
604
632
  `pip install texastoast` and `.mgs` scripts can drive a game engine. It
605
633
  registers under two names, `texastoast` and the shorter `tt`.
606
634
 
@@ -649,24 +677,24 @@ gh_workflows # GitHub commands
649
677
 
650
678
  ## Documentation
651
679
 
652
- Full documentation on the [Wiki](https://github.com/magmacrunchmedia/magmascript/wiki):
653
- - [MagmaScript Language](https://github.com/magmacrunchmedia/magmascript/wiki/MagmaScript-Language)
654
- - [Configuration](https://github.com/magmacrunchmedia/magmascript/wiki/Configuration)
655
- - [MCP Domain](https://github.com/magmacrunchmedia/magmascript/wiki/MCP-Domain)
656
- - [Pi Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Pi-Domain)
657
- - [MC1 Domain](https://github.com/magmacrunchmedia/magmascript/wiki/MC1-Domain)
658
- - [Mac Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Mac-Domain)
659
- - [GitHub Domain](https://github.com/magmacrunchmedia/magmascript/wiki/GitHub-Domain)
660
- - [Scores Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Scores-Domain)
661
- - [Rights Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Rights-Domain)
662
- - [Archive Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Archive-Domain)
663
- - [Last.fm Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Last.fm-Domain)
664
- - [MusicBrainz Domain](https://github.com/magmacrunchmedia/magmascript/wiki/MusicBrainz-Domain)
665
- - [Search Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Search-Domain)
666
- - [Media Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Media-Domain)
667
- - [Example Scripts](https://github.com/magmacrunchmedia/magmascript/wiki/Example-Scripts)
668
- - [Asthenosphere](https://github.com/magmacrunchmedia/magmascript/wiki/Asthenosphere)
669
- - [Architecture](https://github.com/magmacrunchmedia/magmascript/wiki/Architecture)
680
+ Full documentation on the [Wiki](https://github.com/magmacrunch-media/magmascript/wiki):
681
+ - [MagmaScript Language](https://github.com/magmacrunch-media/magmascript/wiki/MagmaScript-Language)
682
+ - [Configuration](https://github.com/magmacrunch-media/magmascript/wiki/Configuration)
683
+ - [MCP Domain](https://github.com/magmacrunch-media/magmascript/wiki/MCP-Domain)
684
+ - [Pi Domain](https://github.com/magmacrunch-media/magmascript/wiki/Pi-Domain)
685
+ - [MC1 Domain](https://github.com/magmacrunch-media/magmascript/wiki/MC1-Domain)
686
+ - [Mac Domain](https://github.com/magmacrunch-media/magmascript/wiki/Mac-Domain)
687
+ - [GitHub Domain](https://github.com/magmacrunch-media/magmascript/wiki/GitHub-Domain)
688
+ - [Scores Domain](https://github.com/magmacrunch-media/magmascript/wiki/Scores-Domain)
689
+ - [Rights Domain](https://github.com/magmacrunch-media/magmascript/wiki/Rights-Domain)
690
+ - [Archive Domain](https://github.com/magmacrunch-media/magmascript/wiki/Archive-Domain)
691
+ - [Last.fm Domain](https://github.com/magmacrunch-media/magmascript/wiki/Last.fm-Domain)
692
+ - [MusicBrainz Domain](https://github.com/magmacrunch-media/magmascript/wiki/MusicBrainz-Domain)
693
+ - [Search Domain](https://github.com/magmacrunch-media/magmascript/wiki/Search-Domain)
694
+ - [Media Domain](https://github.com/magmacrunch-media/magmascript/wiki/Media-Domain)
695
+ - [Example Scripts](https://github.com/magmacrunch-media/magmascript/wiki/Example-Scripts)
696
+ - [Asthenosphere](https://github.com/magmacrunch-media/magmascript/wiki/Asthenosphere)
697
+ - [Architecture](https://github.com/magmacrunch-media/magmascript/wiki/Architecture)
670
698
 
671
699
  ## License
672
700
 
@@ -4,8 +4,30 @@ Scripting toolkit with domain-first subcommands for managing magmacrunch.com inf
4
4
 
5
5
  ## Install
6
6
 
7
+ Requires Python >= 3.11.
8
+
9
+ ### pip (recommended)
10
+
11
+ ```bash
12
+ pip install magmascript
13
+ ```
14
+
15
+ ### pipx (best for CLI-only usage)
16
+
7
17
  ```bash
8
- git clone https://github.com/magmacrunchmedia/magmascript.git
18
+ pipx install magmascript
19
+ ```
20
+
21
+ ### Homebrew (macOS / Linux)
22
+
23
+ ```bash
24
+ brew install magmacrunch-media/tap/magmascript
25
+ ```
26
+
27
+ ### Development
28
+
29
+ ```bash
30
+ git clone https://github.com/magmacrunch-media/magmascript.git
9
31
  cd magmascript
10
32
  python3 -m venv .venv
11
33
  .venv/bin/pip install -e ".[all]"
@@ -36,7 +58,7 @@ user = "jake"
36
58
 
37
59
  [gh]
38
60
  token = "ghp_..."
39
- owner = "magmacrunchmedia"
61
+ owner = "magmacrunch-media"
40
62
  repo = "magmacrunch.com"
41
63
 
42
64
  [project]
@@ -361,7 +383,7 @@ this ground was scorched at line 3 (garrisoned at line 1)
361
383
 
362
384
  `floorplan` is the only new reserved word; everything else is a shadowable
363
385
  builtin, so existing scripts are unaffected. Full reference on the
364
- [Asthenosphere wiki page](https://github.com/magmacrunchmedia/magmascript/wiki/Asthenosphere).
386
+ [Asthenosphere wiki page](https://github.com/magmacrunch-media/magmascript/wiki/Asthenosphere).
365
387
 
366
388
  ### REPL
367
389
 
@@ -433,6 +455,10 @@ Also on the CLI: `magmascript mc1 status`, `mc1 info`, `mc1 processes`,
433
455
  `mc1 restart <service>`, `mc1 power`, `mc1 set-power-mode <always-on|sleep>`,
434
456
  `mc1 wake <mac>`, `mc1 reboot`.
435
457
 
458
+ > **Note:** MC1 commands connect over SSH. Don't run them on MC1 itself — they
459
+ > will try to SSH into the configured host, which on MC1 means connecting to
460
+ > itself and hanging. Use these from your Mac, Pi, or any other machine.
461
+
436
462
  ### Mac Domain — Mac management (via SSH)
437
463
  ```magmascript
438
464
  // In a .mgs script, or: magmascript mac info
@@ -565,7 +591,7 @@ first use, not at startup, so a domain may hold real resources. Built-in domains
565
591
  win a name clash, and an entry point that fails to import is skipped rather than
566
592
  breaking the interpreter.
567
593
 
568
- [texastoast](https://github.com/magmacrunchmedia/texastoast) is the first:
594
+ [texastoast](https://github.com/magmacrunch-media/texastoast) is the first:
569
595
  `pip install texastoast` and `.mgs` scripts can drive a game engine. It
570
596
  registers under two names, `texastoast` and the shorter `tt`.
571
597
 
@@ -614,24 +640,24 @@ gh_workflows # GitHub commands
614
640
 
615
641
  ## Documentation
616
642
 
617
- Full documentation on the [Wiki](https://github.com/magmacrunchmedia/magmascript/wiki):
618
- - [MagmaScript Language](https://github.com/magmacrunchmedia/magmascript/wiki/MagmaScript-Language)
619
- - [Configuration](https://github.com/magmacrunchmedia/magmascript/wiki/Configuration)
620
- - [MCP Domain](https://github.com/magmacrunchmedia/magmascript/wiki/MCP-Domain)
621
- - [Pi Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Pi-Domain)
622
- - [MC1 Domain](https://github.com/magmacrunchmedia/magmascript/wiki/MC1-Domain)
623
- - [Mac Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Mac-Domain)
624
- - [GitHub Domain](https://github.com/magmacrunchmedia/magmascript/wiki/GitHub-Domain)
625
- - [Scores Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Scores-Domain)
626
- - [Rights Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Rights-Domain)
627
- - [Archive Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Archive-Domain)
628
- - [Last.fm Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Last.fm-Domain)
629
- - [MusicBrainz Domain](https://github.com/magmacrunchmedia/magmascript/wiki/MusicBrainz-Domain)
630
- - [Search Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Search-Domain)
631
- - [Media Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Media-Domain)
632
- - [Example Scripts](https://github.com/magmacrunchmedia/magmascript/wiki/Example-Scripts)
633
- - [Asthenosphere](https://github.com/magmacrunchmedia/magmascript/wiki/Asthenosphere)
634
- - [Architecture](https://github.com/magmacrunchmedia/magmascript/wiki/Architecture)
643
+ Full documentation on the [Wiki](https://github.com/magmacrunch-media/magmascript/wiki):
644
+ - [MagmaScript Language](https://github.com/magmacrunch-media/magmascript/wiki/MagmaScript-Language)
645
+ - [Configuration](https://github.com/magmacrunch-media/magmascript/wiki/Configuration)
646
+ - [MCP Domain](https://github.com/magmacrunch-media/magmascript/wiki/MCP-Domain)
647
+ - [Pi Domain](https://github.com/magmacrunch-media/magmascript/wiki/Pi-Domain)
648
+ - [MC1 Domain](https://github.com/magmacrunch-media/magmascript/wiki/MC1-Domain)
649
+ - [Mac Domain](https://github.com/magmacrunch-media/magmascript/wiki/Mac-Domain)
650
+ - [GitHub Domain](https://github.com/magmacrunch-media/magmascript/wiki/GitHub-Domain)
651
+ - [Scores Domain](https://github.com/magmacrunch-media/magmascript/wiki/Scores-Domain)
652
+ - [Rights Domain](https://github.com/magmacrunch-media/magmascript/wiki/Rights-Domain)
653
+ - [Archive Domain](https://github.com/magmacrunch-media/magmascript/wiki/Archive-Domain)
654
+ - [Last.fm Domain](https://github.com/magmacrunch-media/magmascript/wiki/Last.fm-Domain)
655
+ - [MusicBrainz Domain](https://github.com/magmacrunch-media/magmascript/wiki/MusicBrainz-Domain)
656
+ - [Search Domain](https://github.com/magmacrunch-media/magmascript/wiki/Search-Domain)
657
+ - [Media Domain](https://github.com/magmacrunch-media/magmascript/wiki/Media-Domain)
658
+ - [Example Scripts](https://github.com/magmacrunch-media/magmascript/wiki/Example-Scripts)
659
+ - [Asthenosphere](https://github.com/magmacrunch-media/magmascript/wiki/Asthenosphere)
660
+ - [Architecture](https://github.com/magmacrunch-media/magmascript/wiki/Architecture)
635
661
 
636
662
  ## License
637
663
 
@@ -1,6 +1,6 @@
1
1
  """magmascript — a scripting toolkit with domain-first subcommands."""
2
2
 
3
- __version__ = "3.2.0"
3
+ __version__ = "3.2.2"
4
4
 
5
5
  from magmascript.core.config import Config, GHConfig, MediaConfig, MC1Config, PIConfig, get_config, load_config, set_config
6
6
  from magmascript.core.registry import get_domain, list_domains, register_domain
@@ -11,12 +11,46 @@ from __future__ import annotations
11
11
 
12
12
  import json
13
13
  import sys
14
+ from functools import wraps
15
+ from typing import Callable
14
16
 
15
17
  from magmascript.core.config import get_config
16
18
  from magmascript.core.exceptions import MagmascriptError
17
19
  from magmascript.core.output import format_output
18
20
 
19
21
 
22
+ # -- command registry ----------------------------------------------------
23
+
24
+ COMMANDS: dict[str, Callable] = {}
25
+
26
+
27
+ def register(name: str, help_text: str = "") -> Callable:
28
+ """Decorator to register a CLI command handler.
29
+
30
+ The handler receives (action, args, fmt).
31
+ """
32
+ def decorator(fn: Callable) -> Callable:
33
+ COMMANDS[name] = fn
34
+ fn._help_text = help_text
35
+ return fn
36
+ return decorator
37
+
38
+
39
+ def _domain_command(client_cls: type, dispatch_fn: Callable, help_text: str = "") -> Callable:
40
+ """Create a command handler that manages config, client lifecycle, and dispatch."""
41
+ def handler(action: str, args: list[str], fmt: str) -> None:
42
+ config = get_config()
43
+ client = client_cls(config)
44
+ try:
45
+ dispatch_fn(action, args, client, fmt)
46
+ finally:
47
+ close = getattr(client, "close", None)
48
+ if close:
49
+ close()
50
+ handler._help_text = help_text
51
+ return handler
52
+
53
+
20
54
  def _generate_channels_js(channels: list[dict]) -> str:
21
55
  """Generate visual/tv/channels.js from TV channel data."""
22
56
  lines = []
@@ -30,6 +64,212 @@ def _generate_channels_js(channels: list[dict]) -> str:
30
64
  return 'window.TV_CHANNELS = [\n' + ',\n'.join(lines) + '\n];\n'
31
65
 
32
66
 
67
+ # -- register domain commands --------------------------------------------
68
+
69
+ def _register_domains():
70
+ from magmascript.domains.mcp import MCPClient
71
+ from magmascript.domains.pi import PIClient
72
+ from magmascript.domains.mac import MacClient
73
+ from magmascript.domains.mc1 import MC1Client
74
+ from magmascript.domains.gh import GHClient
75
+ from magmascript.domains.media import MediaClient
76
+ from magmascript.domains.scores import ScoresClient
77
+ from magmascript.domains.rights import RightsClient
78
+ from magmascript.domains.archive import ArchiveClient
79
+ from magmascript.domains.mb import MusicBrainzClient
80
+ from magmascript.domains.lastfm import LastFmClient
81
+ from magmascript.domains.search import SearchClient
82
+
83
+ register("mcp")(_domain_command(MCPClient, _dispatch_mcp))
84
+ register("pi")(_domain_command(PIClient, _dispatch_pi))
85
+ register("mac")(_domain_command(MacClient, _dispatch_mac))
86
+ register("mc1")(_domain_command(MC1Client, _dispatch_mc1))
87
+ register("gh")(_domain_command(GHClient, _dispatch_gh))
88
+ register("media")(_domain_command(MediaClient, _dispatch_media))
89
+ register("scores")(_domain_command(ScoresClient, _dispatch_scores))
90
+ register("rights")(_domain_command(RightsClient, _dispatch_rights))
91
+
92
+ def _archive_handler(action, args, fmt):
93
+ from pathlib import Path
94
+ config = get_config()
95
+ project_root = Path(config.project.root) if config.project.root else None
96
+ client = ArchiveClient(project_root=project_root)
97
+ _dispatch_archive(action, args, client, fmt)
98
+
99
+ register("archive", "Archive page operations")(_archive_handler)
100
+
101
+ def _mb_handler(action, args, fmt):
102
+ from pathlib import Path
103
+ config = get_config()
104
+ project_root = Path(config.project.root) if config.project.root else None
105
+ client = MusicBrainzClient(project_root=project_root)
106
+ try:
107
+ _dispatch_mb(action, args, client, fmt)
108
+ finally:
109
+ client.close()
110
+
111
+ register("mb", "MusicBrainz API client")(_mb_handler)
112
+
113
+ def _lastfm_handler(action, args, fmt):
114
+ from pathlib import Path
115
+ config = get_config()
116
+ project_root = Path(config.project.root) if config.project.root else None
117
+ try:
118
+ client = LastFmClient(project_root=project_root)
119
+ except MagmascriptError as e:
120
+ if action and action != "--help":
121
+ print(f"Error: {e}", file=sys.stderr)
122
+ sys.exit(1)
123
+ client = None
124
+ try:
125
+ _dispatch_lastfm(action, args, client, fmt)
126
+ finally:
127
+ if client:
128
+ client.close()
129
+
130
+ register("lastfm", "Last.fm API client")(_lastfm_handler)
131
+
132
+ def _search_handler(action, args, fmt):
133
+ from pathlib import Path
134
+ config = get_config()
135
+ project_root = Path(config.project.root) if config.project.root else None
136
+ client = SearchClient(project_root=project_root)
137
+ _dispatch_search(action, args, client, fmt)
138
+
139
+ register("search", "Site search index builder")(_search_handler)
140
+
141
+
142
+ # -- register built-in meta commands ------------------------------------
143
+
144
+ @register("run", "Run a MagmaScript (.mgs) file")
145
+ def _cmd_run(action: str, args: list[str], fmt: str) -> None:
146
+ _dispatch_run(action, args)
147
+
148
+
149
+ @register("repl", "Start interactive MagmaScript REPL")
150
+ def _cmd_repl(action: str, args: list[str], fmt: str) -> None:
151
+ from magmascript.repl import repl
152
+ repl()
153
+
154
+
155
+ @register("configure", "Fetch API key from MCP server")
156
+ def _cmd_configure(action: str, args: list[str], fmt: str) -> None:
157
+ _dispatch_configure(action, args)
158
+
159
+
160
+ @register("magma", "Show system status dashboard")
161
+ def _cmd_magma(action: str, args: list[str], fmt: str) -> None:
162
+ _dispatch_magma(action, fmt)
163
+
164
+
165
+ @register("crunch", "Run a batch pipeline")
166
+ def _cmd_crunch(action: str, args: list[str], fmt: str) -> None:
167
+ _dispatch_crunch(action, args, fmt)
168
+
169
+
170
+ @register("texas", "Full/heavy operation")
171
+ def _cmd_texas(action: str, args: list[str], fmt: str) -> None:
172
+ _dispatch_texas(action, args, fmt)
173
+
174
+
175
+ @register("toast", "Burn/clear caches")
176
+ def _cmd_toast(action: str, args: list[str], fmt: str) -> None:
177
+ _dispatch_toast(action, args, fmt)
178
+
179
+
180
+ @register("cache", "Cache management")
181
+ def _cmd_cache(action: str, args: list[str], fmt: str) -> None:
182
+ _dispatch_cache(action, args, fmt)
183
+
184
+
185
+ @register("codegen", "Generate C code from .mgs files")
186
+ def _cmd_codegen(action: str, args: list[str], fmt: str) -> None:
187
+ """Dispatch codegen subcommands."""
188
+ if not action or action == "--help":
189
+ print("""Generate C code from MagmaScript (.mgs) files.
190
+
191
+ Usage:
192
+ magmascript codegen <file.mgs> Generate C to stdout
193
+ magmascript codegen <file.mgs> -o out.c Generate C to file
194
+
195
+ Options:
196
+ --help Show this help
197
+ """)
198
+ sys.exit(0)
199
+
200
+ from pathlib import Path
201
+ script_path = Path(action)
202
+ if not script_path.exists():
203
+ print(f"Error: File not found: {script_path}", file=sys.stderr)
204
+ sys.exit(1)
205
+
206
+ if not script_path.suffix == ".mgs":
207
+ print(f"Warning: File does not have .mgs extension: {script_path}", file=sys.stderr)
208
+
209
+ try:
210
+ source = script_path.read_text(encoding="utf-8")
211
+ from magmascript.lang.lexer import Lexer
212
+ from magmascript.lang.parser import Parser
213
+ from magmascript.codegen import generate_c
214
+
215
+ filename = str(script_path)
216
+ tokens = Lexer(source, filename=filename).tokenize()
217
+ program = Parser(tokens, source=source, filename=filename).parse()
218
+
219
+ c_code = generate_c(program)
220
+
221
+ # Check for -o flag
222
+ output_file = None
223
+ if "-o" in args:
224
+ idx = args.index("-o")
225
+ if idx + 1 < len(args):
226
+ output_file = args[idx + 1]
227
+
228
+ if output_file:
229
+ Path(output_file).write_text(c_code)
230
+ print(f"Generated {output_file}")
231
+ else:
232
+ print(c_code)
233
+
234
+ except Exception as e:
235
+ if hasattr(e, "format"):
236
+ print(e.format(), file=sys.stderr)
237
+ else:
238
+ print(f"Error: {e}", file=sys.stderr)
239
+ sys.exit(1)
240
+
241
+
242
+ @register("scaffold", "Generate project structures and boilerplate")
243
+ def _cmd_scaffold(action: str, args: list[str], fmt: str) -> None:
244
+ """Dispatch scaffold subcommands."""
245
+ if not action or action == "--help":
246
+ print("""Generate MagmaScript project structures and boilerplate.
247
+
248
+ Usage:
249
+ magmascript scaffold module <name> Generate a new module with tests
250
+
251
+ Options:
252
+ --help Show this help
253
+ """)
254
+ sys.exit(0)
255
+
256
+ if action == "module":
257
+ if not args:
258
+ print("Error: Module name required. Usage: magmascript scaffold module <name>", file=sys.stderr)
259
+ sys.exit(1)
260
+
261
+ name = args[0]
262
+ from magmascript.scaffold import scaffold_module
263
+ path = scaffold_module(name)
264
+ print(f"Created module: {path}")
265
+ print(f" {path / name}.mgs")
266
+ print(f" {path / 'tests' / f'test_{name}.mgs'}")
267
+
268
+ else:
269
+ print(f"Unknown scaffold type: {action!r}. Available: module", file=sys.stderr)
270
+ sys.exit(1)
271
+
272
+
33
273
  def usage():
34
274
  print("""magmascript — scripting toolkit with domain-first subcommands
35
275
 
@@ -197,147 +437,17 @@ def main():
197
437
  no_cache = True
198
438
  rest.remove("--no-cache")
199
439
 
200
- config = get_config()
440
+ # Register domain commands (lazy, on first use)
441
+ if not COMMANDS:
442
+ _register_domains()
201
443
 
202
444
  try:
203
- if domain == "mcp":
204
- from magmascript.domains.mcp import MCPClient
205
- client = MCPClient(config)
206
- try:
207
- _dispatch_mcp(action, rest, client, fmt)
208
- finally:
209
- client.close()
210
-
211
- elif domain == "pi":
212
- from magmascript.domains.pi import PIClient
213
- client = PIClient(config)
214
- try:
215
- _dispatch_pi(action, rest, client, fmt)
216
- finally:
217
- client.close()
218
-
219
- elif domain == "mac":
220
- from magmascript.domains.mac import MacClient
221
- client = MacClient(config)
222
- try:
223
- _dispatch_mac(action, rest, client, fmt)
224
- finally:
225
- client.close()
226
-
227
- elif domain == "mc1":
228
- from magmascript.domains.mc1 import MC1Client
229
- client = MC1Client(config)
230
- try:
231
- _dispatch_mc1(action, rest, client, fmt)
232
- finally:
233
- client.close()
234
-
235
- elif domain == "gh":
236
- from magmascript.domains.gh import GHClient
237
- client = GHClient(config)
238
- try:
239
- _dispatch_gh(action, rest, client, fmt)
240
- finally:
241
- client.close()
242
-
243
- elif domain == "media":
244
- from magmascript.domains.media import MediaClient
245
- client = MediaClient(config)
246
- try:
247
- _dispatch_media(action, rest, client, fmt)
248
- finally:
249
- client.close()
250
-
251
- elif domain == "scores":
252
- from magmascript.domains.scores import ScoresClient
253
- client = ScoresClient(config)
254
- try:
255
- _dispatch_scores(action, rest, client, fmt)
256
- finally:
257
- client.close()
258
-
259
- elif domain == "rights":
260
- from magmascript.domains.rights import RightsClient
261
- client = RightsClient(config)
262
- try:
263
- _dispatch_rights(action, rest, client, fmt)
264
- finally:
265
- client.close()
266
-
267
- elif domain == "archive":
268
- from pathlib import Path
269
- from magmascript.domains.archive import ArchiveClient
270
- project_root = Path(config.project.root) if config.project.root else None
271
- client = ArchiveClient(project_root=project_root)
272
- try:
273
- _dispatch_archive(action, rest, client, fmt)
274
- finally:
275
- pass # ArchiveClient doesn't have a close method
276
-
277
- elif domain == "mb":
278
- from pathlib import Path
279
- from magmascript.domains.mb import MusicBrainzClient
280
- project_root = Path(config.project.root) if config.project.root else None
281
- client = MusicBrainzClient(project_root=project_root)
282
- try:
283
- _dispatch_mb(action, rest, client, fmt)
284
- finally:
285
- client.close()
286
-
287
- elif domain == "lastfm":
288
- from pathlib import Path
289
- from magmascript.domains.lastfm import LastFmClient
290
- project_root = Path(config.project.root) if config.project.root else None
291
- try:
292
- client = LastFmClient(project_root=project_root)
293
- except MagmascriptError as e:
294
- if action and action != "--help":
295
- print(f"Error: {e}", file=sys.stderr)
296
- sys.exit(1)
297
- client = None
298
- try:
299
- _dispatch_lastfm(action, rest, client, fmt)
300
- finally:
301
- if client:
302
- client.close()
303
-
304
- elif domain == "search":
305
- from pathlib import Path
306
- from magmascript.domains.search import SearchClient
307
- project_root = Path(config.project.root) if config.project.root else None
308
- client = SearchClient(project_root=project_root)
309
- try:
310
- _dispatch_search(action, rest, client, fmt)
311
- finally:
312
- pass # SearchClient doesn't have a close method
313
-
314
- elif domain == "cache":
315
- _dispatch_cache(action, rest, fmt)
316
-
317
- elif domain == "run":
318
- _dispatch_run(action, rest)
319
-
320
- elif domain == "repl":
321
- from magmascript.repl import repl
322
- repl()
323
-
324
- elif domain == "configure":
325
- _dispatch_configure(action, rest)
326
-
327
- elif domain == "magma":
328
- _dispatch_magma(action, fmt)
329
-
330
- elif domain == "crunch":
331
- _dispatch_crunch(action, rest, fmt)
332
-
333
- elif domain == "texas":
334
- _dispatch_texas(action, rest, fmt)
335
-
336
- elif domain == "toast":
337
- _dispatch_toast(action, rest, fmt)
338
-
445
+ cmd = COMMANDS.get(domain)
446
+ if cmd:
447
+ cmd(action, rest, fmt)
339
448
  else:
340
- print(f"Unknown domain: {domain!r}. Available: mcp, pi, gh, media, scores, rights, archive, mb, lastfm, search, cache, run, repl, configure, magma, crunch, texas, toast", file=sys.stderr)
449
+ available = ", ".join(sorted(COMMANDS.keys()))
450
+ print(f"Unknown domain: {domain!r}. Available: {available}", file=sys.stderr)
341
451
  sys.exit(1)
342
452
 
343
453
  except KeyboardInterrupt: