magmascript 3.2.1__tar.gz → 3.2.3__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.1 → magmascript-3.2.3}/PKG-INFO +59 -26
  2. {magmascript-3.2.1 → magmascript-3.2.3}/README.md +54 -22
  3. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/__init__.py +1 -1
  4. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/cli.py +248 -138
  5. magmascript-3.2.3/magmascript/codegen/__init__.py +23 -0
  6. magmascript-3.2.3/magmascript/codegen/c.py +284 -0
  7. magmascript-3.2.3/magmascript/core/command_registry.py +55 -0
  8. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/core/github.py +1 -1
  9. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/mc1/client.py +22 -0
  10. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/mcp/client.py +3 -3
  11. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/lang/hypnagogia.py +92 -67
  12. magmascript-3.2.3/magmascript/lang/pretty.py +262 -0
  13. magmascript-3.2.3/magmascript/lang/visitor.py +100 -0
  14. magmascript-3.2.3/magmascript/scaffold/__init__.py +70 -0
  15. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript.egg-info/PKG-INFO +59 -26
  16. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript.egg-info/SOURCES.txt +6 -0
  17. {magmascript-3.2.1 → magmascript-3.2.3}/pyproject.toml +5 -4
  18. {magmascript-3.2.1 → magmascript-3.2.3}/LICENSE +0 -0
  19. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/core/__init__.py +0 -0
  20. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/core/cache.py +0 -0
  21. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/core/commands.py +0 -0
  22. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/core/config.py +0 -0
  23. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/core/exceptions.py +0 -0
  24. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/core/output.py +0 -0
  25. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/core/registry.py +0 -0
  26. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/core/rpc.py +0 -0
  27. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/core/runner.py +0 -0
  28. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/__init__.py +0 -0
  29. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/archive/__init__.py +0 -0
  30. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/archive/client.py +0 -0
  31. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/gh/__init__.py +0 -0
  32. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/gh/client.py +0 -0
  33. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/gh/tools.py +0 -0
  34. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/lastfm/__init__.py +0 -0
  35. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/lastfm/client.py +0 -0
  36. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/mac/__init__.py +0 -0
  37. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/mac/client.py +0 -0
  38. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/mac/tools.py +0 -0
  39. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/mb/__init__.py +0 -0
  40. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/mb/client.py +0 -0
  41. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/mc1/__init__.py +0 -0
  42. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/mc1/tools.py +0 -0
  43. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/mcp/__init__.py +0 -0
  44. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/mcp/tools.py +0 -0
  45. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/media/__init__.py +0 -0
  46. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/media/client.py +0 -0
  47. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/media/providers/__init__.py +0 -0
  48. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/media/providers/archive.py +0 -0
  49. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/media/providers/met_museum.py +0 -0
  50. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/media/providers/openverse.py +0 -0
  51. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/media/providers/pexels.py +0 -0
  52. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/media/providers/pixabay.py +0 -0
  53. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/media/providers/smithsonian.py +0 -0
  54. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/media/tools.py +0 -0
  55. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/pi/__init__.py +0 -0
  56. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/pi/client.py +0 -0
  57. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/pi/tools.py +0 -0
  58. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/rights/__init__.py +0 -0
  59. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/rights/client.py +0 -0
  60. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/rights/tools.py +0 -0
  61. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/scores/__init__.py +0 -0
  62. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/scores/client.py +0 -0
  63. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/scores/tools.py +0 -0
  64. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/search/__init__.py +0 -0
  65. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/domains/search/client.py +0 -0
  66. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/lang/__init__.py +0 -0
  67. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/lang/ast_nodes.py +0 -0
  68. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/lang/astheno/__init__.py +0 -0
  69. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/lang/astheno/arena.py +0 -0
  70. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/lang/astheno/dump.py +0 -0
  71. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/lang/astheno/floorplan.py +0 -0
  72. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/lang/astheno/numeric.py +0 -0
  73. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/lang/builtins.py +0 -0
  74. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/lang/domain_bridge.py +0 -0
  75. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/lang/environment.py +0 -0
  76. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/lang/interpreter.py +0 -0
  77. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/lang/lexer.py +0 -0
  78. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/lang/parser.py +0 -0
  79. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/lang/pygments_mgs.py +0 -0
  80. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/lang/tokens.py +0 -0
  81. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/lang/util.py +0 -0
  82. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript/repl.py +0 -0
  83. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript.egg-info/dependency_links.txt +0 -0
  84. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript.egg-info/entry_points.txt +0 -0
  85. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript.egg-info/requires.txt +0 -0
  86. {magmascript-3.2.1 → magmascript-3.2.3}/magmascript.egg-info/top_level.txt +0 -0
  87. {magmascript-3.2.1 → magmascript-3.2.3}/setup.cfg +0 -0
  88. {magmascript-3.2.1 → magmascript-3.2.3}/tests/test_astheno.py +0 -0
  89. {magmascript-3.2.1 → magmascript-3.2.3}/tests/test_cache.py +0 -0
  90. {magmascript-3.2.1 → magmascript-3.2.3}/tests/test_classes.py +0 -0
  91. {magmascript-3.2.1 → magmascript-3.2.3}/tests/test_cli.py +0 -0
  92. {magmascript-3.2.1 → magmascript-3.2.3}/tests/test_commands.py +0 -0
  93. {magmascript-3.2.1 → magmascript-3.2.3}/tests/test_domain_bridge.py +0 -0
  94. {magmascript-3.2.1 → magmascript-3.2.3}/tests/test_import.py +0 -0
  95. {magmascript-3.2.1 → magmascript-3.2.3}/tests/test_lang.py +0 -0
  96. {magmascript-3.2.1 → magmascript-3.2.3}/tests/test_mac.py +0 -0
  97. {magmascript-3.2.1 → magmascript-3.2.3}/tests/test_mc1.py +0 -0
  98. {magmascript-3.2.1 → magmascript-3.2.3}/tests/test_mcp_client.py +0 -0
  99. {magmascript-3.2.1 → magmascript-3.2.3}/tests/test_rpc.py +0 -0
  100. {magmascript-3.2.1 → magmascript-3.2.3}/tests/test_try_catch.py +0 -0
@@ -1,13 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: magmascript
3
- Version: 3.2.1
3
+ Version: 3.2.3
4
4
  Summary: Scripting toolkit with domain-first subcommands
5
5
  Author: Jake McCoy
6
6
  License-Expression: MIT
7
7
  Project-URL: Homepage, https://magmacrunch.com/ware/magmascript/
8
- Project-URL: Documentation, https://github.com/magmacrunchmedia/magmascript/wiki
9
- Project-URL: Repository, https://github.com/magmacrunchmedia/magmascript
10
- Project-URL: Changelog, https://github.com/magmacrunchmedia/magmascript/blob/main/CHANGELOG.md
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
11
+ Project-URL: Funding, https://github.com/sponsors/magmacrunch-media
11
12
  Keywords: mcp,scripting,cli,magmacrunch
12
13
  Classifier: Development Status :: 3 - Alpha
13
14
  Classifier: Environment :: Console
@@ -41,8 +42,30 @@ Scripting toolkit with domain-first subcommands for managing magmacrunch.com inf
41
42
 
42
43
  ## Install
43
44
 
45
+ Requires Python >= 3.11.
46
+
47
+ ### pip (recommended)
48
+
49
+ ```bash
50
+ pip install magmascript
51
+ ```
52
+
53
+ ### pipx (best for CLI-only usage)
54
+
44
55
  ```bash
45
- git clone https://github.com/magmacrunchmedia/magmascript.git
56
+ pipx install magmascript
57
+ ```
58
+
59
+ ### Homebrew (macOS / Linux)
60
+
61
+ ```bash
62
+ brew install magmacrunch-media/tap/magmascript
63
+ ```
64
+
65
+ ### Development
66
+
67
+ ```bash
68
+ git clone https://github.com/magmacrunch-media/magmascript.git
46
69
  cd magmascript
47
70
  python3 -m venv .venv
48
71
  .venv/bin/pip install -e ".[all]"
@@ -73,7 +96,7 @@ user = "jake"
73
96
 
74
97
  [gh]
75
98
  token = "ghp_..."
76
- owner = "magmacrunchmedia"
99
+ owner = "magmacrunch-media"
77
100
  repo = "magmacrunch.com"
78
101
 
79
102
  [project]
@@ -398,7 +421,7 @@ this ground was scorched at line 3 (garrisoned at line 1)
398
421
 
399
422
  `floorplan` is the only new reserved word; everything else is a shadowable
400
423
  builtin, so existing scripts are unaffected. Full reference on the
401
- [Asthenosphere wiki page](https://github.com/magmacrunchmedia/magmascript/wiki/Asthenosphere).
424
+ [Asthenosphere wiki page](https://github.com/magmacrunch-media/magmascript/wiki/Asthenosphere).
402
425
 
403
426
  ### REPL
404
427
 
@@ -470,6 +493,10 @@ Also on the CLI: `magmascript mc1 status`, `mc1 info`, `mc1 processes`,
470
493
  `mc1 restart <service>`, `mc1 power`, `mc1 set-power-mode <always-on|sleep>`,
471
494
  `mc1 wake <mac>`, `mc1 reboot`.
472
495
 
496
+ > **Note:** MC1 commands connect over SSH. Don't run them on MC1 itself — they
497
+ > will try to SSH into the configured host, which on MC1 means connecting to
498
+ > itself and hanging. Use these from your Mac, Pi, or any other machine.
499
+
473
500
  ### Mac Domain — Mac management (via SSH)
474
501
  ```magmascript
475
502
  // In a .mgs script, or: magmascript mac info
@@ -602,7 +629,7 @@ first use, not at startup, so a domain may hold real resources. Built-in domains
602
629
  win a name clash, and an entry point that fails to import is skipped rather than
603
630
  breaking the interpreter.
604
631
 
605
- [texastoast](https://github.com/magmacrunchmedia/texastoast) is the first:
632
+ [texastoast](https://github.com/magmacrunch-media/texastoast) is the first:
606
633
  `pip install texastoast` and `.mgs` scripts can drive a game engine. It
607
634
  registers under two names, `texastoast` and the shorter `tt`.
608
635
 
@@ -651,24 +678,30 @@ gh_workflows # GitHub commands
651
678
 
652
679
  ## Documentation
653
680
 
654
- Full documentation on the [Wiki](https://github.com/magmacrunchmedia/magmascript/wiki):
655
- - [MagmaScript Language](https://github.com/magmacrunchmedia/magmascript/wiki/MagmaScript-Language)
656
- - [Configuration](https://github.com/magmacrunchmedia/magmascript/wiki/Configuration)
657
- - [MCP Domain](https://github.com/magmacrunchmedia/magmascript/wiki/MCP-Domain)
658
- - [Pi Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Pi-Domain)
659
- - [MC1 Domain](https://github.com/magmacrunchmedia/magmascript/wiki/MC1-Domain)
660
- - [Mac Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Mac-Domain)
661
- - [GitHub Domain](https://github.com/magmacrunchmedia/magmascript/wiki/GitHub-Domain)
662
- - [Scores Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Scores-Domain)
663
- - [Rights Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Rights-Domain)
664
- - [Archive Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Archive-Domain)
665
- - [Last.fm Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Last.fm-Domain)
666
- - [MusicBrainz Domain](https://github.com/magmacrunchmedia/magmascript/wiki/MusicBrainz-Domain)
667
- - [Search Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Search-Domain)
668
- - [Media Domain](https://github.com/magmacrunchmedia/magmascript/wiki/Media-Domain)
669
- - [Example Scripts](https://github.com/magmacrunchmedia/magmascript/wiki/Example-Scripts)
670
- - [Asthenosphere](https://github.com/magmacrunchmedia/magmascript/wiki/Asthenosphere)
671
- - [Architecture](https://github.com/magmacrunchmedia/magmascript/wiki/Architecture)
681
+ Full documentation on the [Wiki](https://github.com/magmacrunch-media/magmascript/wiki):
682
+ - [MagmaScript Language](https://github.com/magmacrunch-media/magmascript/wiki/MagmaScript-Language)
683
+ - [Configuration](https://github.com/magmacrunch-media/magmascript/wiki/Configuration)
684
+ - [MCP Domain](https://github.com/magmacrunch-media/magmascript/wiki/MCP-Domain)
685
+ - [Pi Domain](https://github.com/magmacrunch-media/magmascript/wiki/Pi-Domain)
686
+ - [MC1 Domain](https://github.com/magmacrunch-media/magmascript/wiki/MC1-Domain)
687
+ - [Mac Domain](https://github.com/magmacrunch-media/magmascript/wiki/Mac-Domain)
688
+ - [GitHub Domain](https://github.com/magmacrunch-media/magmascript/wiki/GitHub-Domain)
689
+ - [Scores Domain](https://github.com/magmacrunch-media/magmascript/wiki/Scores-Domain)
690
+ - [Rights Domain](https://github.com/magmacrunch-media/magmascript/wiki/Rights-Domain)
691
+ - [Archive Domain](https://github.com/magmacrunch-media/magmascript/wiki/Archive-Domain)
692
+ - [Last.fm Domain](https://github.com/magmacrunch-media/magmascript/wiki/Last.fm-Domain)
693
+ - [MusicBrainz Domain](https://github.com/magmacrunch-media/magmascript/wiki/MusicBrainz-Domain)
694
+ - [Search Domain](https://github.com/magmacrunch-media/magmascript/wiki/Search-Domain)
695
+ - [Media Domain](https://github.com/magmacrunch-media/magmascript/wiki/Media-Domain)
696
+ - [Example Scripts](https://github.com/magmacrunch-media/magmascript/wiki/Example-Scripts)
697
+ - [Asthenosphere](https://github.com/magmacrunch-media/magmascript/wiki/Asthenosphere)
698
+ - [Architecture](https://github.com/magmacrunch-media/magmascript/wiki/Architecture)
699
+
700
+ ## Support This Project
701
+
702
+ If you find magmascript useful, consider supporting its development:
703
+
704
+ [![Sponsor](https://img.shields.io/badge/%E2%9D%A4_Sponsor-pink)](https://github.com/sponsors/magmacrunch-media)
672
705
 
673
706
  ## License
674
707
 
@@ -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,30 @@ 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)
661
+
662
+ ## Support This Project
663
+
664
+ If you find magmascript useful, consider supporting its development:
665
+
666
+ [![Sponsor](https://img.shields.io/badge/%E2%9D%A4_Sponsor-pink)](https://github.com/sponsors/magmacrunch-media)
635
667
 
636
668
  ## License
637
669
 
@@ -1,6 +1,6 @@
1
1
  """magmascript — a scripting toolkit with domain-first subcommands."""
2
2
 
3
- __version__ = "3.2.1"
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: