apex-ruby 1.0.18 → 1.0.20

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 (207) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +17 -3
  3. data/ext/apex_ext/apex_ext.c +260 -91
  4. data/ext/apex_ext/apex_src/CHANGELOG.md +440 -0
  5. data/ext/apex_ext/apex_src/CMakeLists.txt +15 -1
  6. data/ext/apex_ext/apex_src/Formula/apex.rb +7 -2
  7. data/ext/apex_ext/apex_src/Package.swift +15 -4
  8. data/ext/apex_ext/apex_src/README.md +94 -35
  9. data/ext/apex_ext/apex_src/VERSION +1 -1
  10. data/ext/apex_ext/apex_src/apex-debug.log +4 -0
  11. data/ext/apex_ext/apex_src/apex.code-workspace +38 -0
  12. data/ext/apex_ext/apex_src/apex.taskpaper +12 -0
  13. data/ext/apex_ext/apex_src/apex_deflist_debug.log +25 -0
  14. data/ext/apex_ext/apex_src/buildnotes.md +1435 -0
  15. data/ext/apex_ext/apex_src/cli/main.c +262 -33
  16. data/ext/apex_ext/apex_src/debug.log +7 -0
  17. data/ext/apex_ext/apex_src/docs/CITATIONS_EXAMPLES.md +274 -0
  18. data/ext/apex_ext/apex_src/docs/CITATIONS_IMPLEMENTATION_PLAN.md +749 -0
  19. data/ext/apex_ext/apex_src/docs/CITATIONS_PLAN.md +744 -0
  20. data/ext/apex_ext/apex_src/docs/INDEX_IMPLEMENTATION_ANALYSIS.md +382 -0
  21. data/ext/apex_ext/apex_src/docs/plans/2025-02-27-man-page-output-implementation.md +351 -0
  22. data/ext/apex_ext/apex_src/docs/superpowers/plans/2026-07-12-toc-output-format.md +745 -0
  23. data/ext/apex_ext/apex_src/docs/superpowers/plans/2026-07-18-bear-image-attributes.md +829 -0
  24. data/ext/apex_ext/apex_src/docs/superpowers/plans/2026-07-19-apex-node-npm.md +1148 -0
  25. data/ext/apex_ext/apex_src/docs/superpowers/specs/2026-07-12-toc-output-format-design.md +161 -0
  26. data/ext/apex_ext/apex_src/docs/superpowers/specs/2026-07-18-bear-image-attributes-design.md +184 -0
  27. data/ext/apex_ext/apex_src/docs/superpowers/specs/2026-07-19-apex-node-npm-design.md +211 -0
  28. data/ext/apex_ext/apex_src/documentation/app-settings-table.md +67 -0
  29. data/ext/apex_ext/apex_src/documentation/app-transformed/Citations.md +340 -0
  30. data/ext/apex_ext/apex_src/documentation/app-transformed/Credits.md +11 -0
  31. data/ext/apex_ext/apex_src/documentation/app-transformed/Header-IDs.md +234 -0
  32. data/ext/apex_ext/apex_src/documentation/app-transformed/Inline-Attribute-Lists.md +516 -0
  33. data/ext/apex_ext/apex_src/documentation/app-transformed/Metadata-Transforms.md +461 -0
  34. data/ext/apex_ext/apex_src/documentation/app-transformed/Modes.md +193 -0
  35. data/ext/apex_ext/apex_src/documentation/app-transformed/Multi-File-Documents.md +201 -0
  36. data/ext/apex_ext/apex_src/documentation/app-transformed/Plugins.md +678 -0
  37. data/ext/apex_ext/apex_src/documentation/app-transformed/Syntax.md +1367 -0
  38. data/ext/apex_ext/apex_src/documentation/docsets/Apex Command Line Options.cheatsheet.txt +6 -0
  39. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/C-API.html +774 -300
  40. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Callouts.html +1194 -0
  41. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Citations.html +416 -216
  42. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Command-Line-Options.html +3544 -1094
  43. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Configuration.html +371 -97
  44. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Credits.html +153 -23
  45. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Examples.html +358 -124
  46. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Filters.html +1656 -0
  47. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Generating-Man-Pages.html +1226 -0
  48. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Getting-Started.html +201 -56
  49. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Header-IDs.html +279 -109
  50. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Home.html +200 -53
  51. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Indices.html +2327 -0
  52. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Inline-Attribute-Lists.html +690 -271
  53. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Installation.html +201 -55
  54. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Limitations-and-Roadmap.html +238 -68
  55. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Metadata-Transforms.html +421 -221
  56. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Modes.html +340 -78
  57. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Multi-File-Documents.html +251 -86
  58. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Multi-Format-Images.html +1362 -0
  59. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Pandoc-Integration.html +277 -90
  60. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Plugins.html +673 -346
  61. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Quarto-Mode.html +1405 -0
  62. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Rendering-Markdown-In-Terminal.html +1902 -0
  63. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Syntax.html +1939 -1698
  64. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Tables.html +1781 -0
  65. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Troubleshooting.html +315 -140
  66. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Usage.html +307 -113
  67. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Using-Apex-with-Jekyll.html +1132 -0
  68. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Writing-Tests.html +1754 -0
  69. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/Documents/Xcode-Integration.html +872 -376
  70. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/docSet.dsidx +0 -0
  71. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/optimizedIndex.dsidx +0 -0
  72. data/ext/apex_ext/apex_src/documentation/html/apex-app-docs.html +8746 -0
  73. data/ext/apex_ext/apex_src/documentation/shared_styles.css +20 -15
  74. data/ext/apex_ext/apex_src/documentation/transform_for_app.md +55 -0
  75. data/ext/apex_ext/apex_src/escaping-repro.md +103 -0
  76. data/ext/apex_ext/apex_src/examples/quick-actions/Markdown to RTF.workflow/Contents/Info.plist +8 -0
  77. data/ext/apex_ext/apex_src/examples/quick-actions/Markdown to RTF.workflow/Contents/QuickLook/Preview.png +0 -0
  78. data/ext/apex_ext/apex_src/examples/quick-actions/Markdown to RTF.workflow/Contents/document.wflow +547 -0
  79. data/ext/apex_ext/apex_src/examples/quick-actions/Markdown-to-RTF.workflow.zip +0 -0
  80. data/ext/apex_ext/apex_src/examples/quick-actions/README.md +33 -0
  81. data/ext/apex_ext/apex_src/include/apex/apex.h +92 -6
  82. data/ext/apex_ext/apex_src/include/apex/ast_rtf.h +23 -0
  83. data/ext/apex_ext/apex_src/include/apex/ast_terminal.h +6 -0
  84. data/ext/apex_ext/apex_src/include/apex/module.modulemap +5 -0
  85. data/ext/apex_ext/apex_src/include/apex/plugins.h +105 -0
  86. data/ext/apex_ext/apex_src/include/apexc.h +1 -0
  87. data/ext/apex_ext/apex_src/man/apex.1 +82 -78
  88. data/ext/apex_ext/apex_src/man/apex.1.md +81 -4
  89. data/ext/apex_ext/apex_src/objc/Apex.swift +73 -0
  90. data/ext/apex_ext/apex_src/objc/ApexPlugins.swift +79 -0
  91. data/ext/apex_ext/apex_src/objc/NSString+Apex.h +83 -5
  92. data/ext/apex_ext/apex_src/objc/NSString+Apex.m +244 -1
  93. data/ext/apex_ext/apex_src/objc/NSString+ApexPlugins.h +76 -0
  94. data/ext/apex_ext/apex_src/objc/NSString+ApexPlugins.m +169 -0
  95. data/ext/apex_ext/apex_src/output.html +87 -0
  96. data/ext/apex_ext/apex_src/plans/quarto-mode.md +442 -0
  97. data/ext/apex_ext/apex_src/src/_README.md +85 -32
  98. data/ext/apex_ext/apex_src/src/apex.c +770 -200
  99. data/ext/apex_ext/apex_src/src/ast_rtf.c +848 -0
  100. data/ext/apex_ext/apex_src/src/ast_terminal.c +184 -7
  101. data/ext/apex_ext/apex_src/src/extensions/abbreviations.c +10 -3
  102. data/ext/apex_ext/apex_src/src/extensions/advanced_tables.c +7 -11
  103. data/ext/apex_ext/apex_src/src/extensions/bear_image_attrs.c +467 -0
  104. data/ext/apex_ext/apex_src/src/extensions/bear_image_attrs.h +27 -0
  105. data/ext/apex_ext/apex_src/src/extensions/callouts.c +400 -4
  106. data/ext/apex_ext/apex_src/src/extensions/callouts.h +15 -5
  107. data/ext/apex_ext/apex_src/src/extensions/citations.c +26 -5
  108. data/ext/apex_ext/apex_src/src/extensions/code_fence_attrs.c +632 -0
  109. data/ext/apex_ext/apex_src/src/extensions/code_fence_attrs.h +22 -0
  110. data/ext/apex_ext/apex_src/src/extensions/concordance.c +632 -0
  111. data/ext/apex_ext/apex_src/src/extensions/definition_list.c +129 -15
  112. data/ext/apex_ext/apex_src/src/extensions/grid_tables.c +1050 -121
  113. data/ext/apex_ext/apex_src/src/extensions/html_markdown.c +569 -16
  114. data/ext/apex_ext/apex_src/src/extensions/ial.c +502 -52
  115. data/ext/apex_ext/apex_src/src/extensions/ial.h +2 -1
  116. data/ext/apex_ext/apex_src/src/extensions/includes.c +532 -33
  117. data/ext/apex_ext/apex_src/src/extensions/includes.h +7 -1
  118. data/ext/apex_ext/apex_src/src/extensions/index.c +1427 -140
  119. data/ext/apex_ext/apex_src/src/extensions/index.h +28 -0
  120. data/ext/apex_ext/apex_src/src/extensions/metadata.c +200 -161
  121. data/ext/apex_ext/apex_src/src/extensions/quarto_diagrams.c +251 -0
  122. data/ext/apex_ext/apex_src/src/extensions/quarto_diagrams.h +22 -0
  123. data/ext/apex_ext/apex_src/src/extensions/quarto_lists.c +624 -0
  124. data/ext/apex_ext/apex_src/src/extensions/quarto_lists.h +35 -0
  125. data/ext/apex_ext/apex_src/src/extensions/quarto_polish.c +264 -0
  126. data/ext/apex_ext/apex_src/src/extensions/quarto_polish.h +23 -0
  127. data/ext/apex_ext/apex_src/src/extensions/quarto_shortcodes.c +307 -0
  128. data/ext/apex_ext/apex_src/src/extensions/quarto_shortcodes.h +21 -0
  129. data/ext/apex_ext/apex_src/src/extensions/raw_content.c +351 -0
  130. data/ext/apex_ext/apex_src/src/extensions/raw_content.h +20 -0
  131. data/ext/apex_ext/apex_src/src/extensions/relaxed_tables.c +117 -3
  132. data/ext/apex_ext/apex_src/src/extensions/special_markers.c +53 -0
  133. data/ext/apex_ext/apex_src/src/extensions/syntax_highlight.c +23 -1
  134. data/ext/apex_ext/apex_src/src/extensions/table_html_postprocess.c +83 -92
  135. data/ext/apex_ext/apex_src/src/extensions/toc.c +446 -25
  136. data/ext/apex_ext/apex_src/src/extensions/toc.h +26 -1
  137. data/ext/apex_ext/apex_src/src/extensions/wiki_links.c +28 -0
  138. data/ext/apex_ext/apex_src/src/html_renderer.c +387 -17
  139. data/ext/apex_ext/apex_src/src/plugin_catalog.c +517 -0
  140. data/ext/apex_ext/apex_src/src/plugins_remote.c +88 -14
  141. data/ext/apex_ext/apex_src/src/plugins_remote.h +34 -0
  142. data/ext/apex_ext/apex_src/src/pretty_html.c +6 -1
  143. data/ext/apex_ext/apex_src/tests/fixtures/comprehensive_test.md +21 -0
  144. data/ext/apex_ext/apex_src/tests/fixtures/images/bear_image_attributes.md +10 -0
  145. data/ext/apex_ext/apex_src/tests/fixtures/includes/sections.md +19 -0
  146. data/ext/apex_ext/apex_src/tests/fixtures/includes/sections.txt +11 -0
  147. data/ext/apex_ext/apex_src/tests/fixtures/minimal_pb.md +2 -0
  148. data/ext/apex_ext/apex_src/tests/fixtures/obsidian-callouts.md +46 -0
  149. data/ext/apex_ext/apex_src/tests/fixtures/pagebreak_html_repro.md +5 -0
  150. data/ext/apex_ext/apex_src/tests/fixtures/python-callouts.md +47 -0
  151. data/ext/apex_ext/apex_src/tests/fixtures/quarto/callouts.md +3 -0
  152. data/ext/apex_ext/apex_src/tests/fixtures/quarto/code-attrs.md +3 -0
  153. data/ext/apex_ext/apex_src/tests/fixtures/quarto/diagrams.md +8 -0
  154. data/ext/apex_ext/apex_src/tests/fixtures/quarto/lists.md +19 -0
  155. data/ext/apex_ext/apex_src/tests/fixtures/quarto/raw-content.md +5 -0
  156. data/ext/apex_ext/apex_src/tests/fixtures/quarto/shortcodes.md +7 -0
  157. data/ext/apex_ext/apex_src/tests/fixtures/quarto/smoke.md +81 -0
  158. data/ext/apex_ext/apex_src/tests/fixtures/quarto/spans.md +5 -0
  159. data/ext/apex_ext/apex_src/tests/fixtures/quarto-callouts.md +47 -0
  160. data/ext/apex_ext/apex_src/tests/fixtures/speed.md +192 -0
  161. data/ext/apex_ext/apex_src/tests/fixtures/tables/grid-tables.md +37 -0
  162. data/ext/apex_ext/apex_src/tests/paginate_cli_test.sh +16 -0
  163. data/ext/apex_ext/apex_src/tests/test_bear_image_attrs.c +301 -0
  164. data/ext/apex_ext/apex_src/tests/test_concordance.tsv +4 -0
  165. data/ext/apex_ext/apex_src/tests/test_escaping_repro.c +82 -0
  166. data/ext/apex_ext/apex_src/tests/test_extensions.c +821 -1
  167. data/ext/apex_ext/apex_src/tests/test_ial.c +7 -0
  168. data/ext/apex_ext/apex_src/tests/test_marked_integration.c +25 -0
  169. data/ext/apex_ext/apex_src/tests/test_metadata.c +129 -0
  170. data/ext/apex_ext/apex_src/tests/test_output.c +568 -2
  171. data/ext/apex_ext/apex_src/tests/test_runner.c +11 -0
  172. data/ext/apex_ext/apex_src/tests/test_tables.c +420 -0
  173. data/ext/apex_ext/apex_src/vendor/cmark-gfm/CMakeLists.txt +1 -1
  174. data/ext/apex_ext/apex_src/vendor/cmark-gfm/extensions/autolink.c +77 -1
  175. data/ext/apex_ext/apex_src/vendor/cmark-gfm/extensions/table.c +135 -5
  176. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/blocks.c +56 -1
  177. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/config.h +38 -0
  178. data/ext/apex_ext/apex_src/vendor/cmark-gfm/src/main.c +1 -1
  179. data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/__pycache__/cmark.cpython-314.pyc +0 -0
  180. data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/__pycache__/normalize.cpython-314.pyc +0 -0
  181. data/ext/apex_ext/apex_src/vendor/cmark-gfm/test/extensions.txt +69 -0
  182. data/ext/apex_ext/apex_src/xcode_build/CMakeScripts/ReRunCMake.make +74 -0
  183. data/ext/apex_ext/apex_src/xcode_build/CTestTestfile.cmake +22 -0
  184. data/ext/apex_ext/apex_src/xcode_build/Debug/Apex.framework/Versions/A/Apex +0 -0
  185. data/ext/apex_ext/apex_src/xcode_build/Debug/Apex.framework/Versions/A/Resources/Info.plist +44 -0
  186. data/ext/apex_ext/apex_src/xcode_build/Debug/Apex.framework/Versions/A/_CodeSignature/CodeResources +128 -0
  187. data/ext/apex_ext/apex_src/xcode_build/apex.xcodeproj/project.pbxproj +5118 -0
  188. data/ext/apex_ext/apex_src/xcode_build/apex.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +8 -0
  189. data/ext/apex_ext/apex_src/xcode_build/apex.xcodeproj/project.xcworkspace/xcuserdata/ttscoff.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  190. data/ext/apex_ext/apex_src/xcode_build/apex.xcodeproj/project.xcworkspace/xcuserdata/ttscoff.xcuserdatad/WorkspaceSettings.xcsettings +8 -0
  191. data/ext/apex_ext/apex_src/xcode_build/apex.xcodeproj/xcuserdata/ttscoff.xcuserdatad/xcschemes/xcschememanagement.plist +99 -0
  192. data/ext/apex_ext/apex_src/xcode_build/cmake_install.cmake +452 -0
  193. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/CMakeScripts/ReRunCMake.make +52 -0
  194. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/cmake_install.cmake +55 -0
  195. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/cmark-gfm.xcodeproj/project.pbxproj +2455 -0
  196. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/cmark-gfm.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +8 -0
  197. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/extensions/cmake_install.cmake +171 -0
  198. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/man/cmake_install.cmake +55 -0
  199. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/src/cmake_install.cmake +220 -0
  200. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/src/cmark-gfm_export.h +43 -0
  201. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/src/cmark-gfm_version.h +7 -0
  202. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/src/config.h +76 -0
  203. data/ext/apex_ext/apex_src/xcode_build/vendor/cmark-gfm/src/libcmark-gfm.pc +10 -0
  204. data/ext/apex_ext/extconf.rb +35 -0
  205. data/lib/apex/version.rb +2 -2
  206. metadata +117 -2
  207. data/ext/apex_ext/apex_src/documentation/docsets/Apex.docset/Contents/Resources/tempOptimizedIndex.dsidx +0 -0
@@ -0,0 +1,1435 @@
1
+ template: project, github
2
+ project: apex
3
+ readme: src/_README.md
4
+ changelog: CHANGELOG.md
5
+
6
+ # Apex Build Notes
7
+
8
+ Comprehensive guide for building, testing, and working with the Apex Markdown processor.
9
+
10
+ This document works with [Howzit](https://brettterpstra.com/projects/howzit).
11
+
12
+ ## Clone repository
13
+
14
+ ```run Clone from GitHub
15
+ #!/bin/bash
16
+
17
+ git clone git@github.com:ApexMarkdown/apex
18
+ cd apex
19
+ git submodule update --init --recursive
20
+ ```
21
+
22
+ ## Setup build directory
23
+
24
+ ```run Make build dir and run cmake
25
+ #!/bin/bash
26
+
27
+ mkdir -p build
28
+ cd build
29
+ cmake ..
30
+ ```
31
+
32
+ ## Build all
33
+
34
+ Builds cli, shared library, test runner, and framework.
35
+
36
+ @run(cmake --build build) Cmake build
37
+
38
+ ## Build CLI tool
39
+
40
+ Build just the CLI tool.
41
+
42
+ @run(cmake --build build --target apex_cli) Build CLI tool
43
+
44
+ ## Build shared library
45
+
46
+ Build just the shared library.
47
+
48
+ @run(cmake --build build --target apex)
49
+
50
+ ## Just static library
51
+
52
+ Build just the static library.
53
+
54
+ @run(cmake --build build --target apex_static)
55
+
56
+ ## Build macOS framework
57
+
58
+ Build the framework for Xcode.
59
+
60
+ @run(cmake --build build --target apex_framework)
61
+
62
+ ## Build test runner
63
+
64
+ Build just the test runner.
65
+
66
+ @run(cmake --build build --target apex_test_runner &>/dev/null) Build test runner
67
+
68
+ ## Build with Address Sanitizer
69
+
70
+ Build with AddressSanitizer and UndefinedBehaviorSanitizer for detecting memory errors and undefined behavior. This is useful for debugging crashes and assertion failures.
71
+
72
+ ```run Build ASan
73
+ #!/bin/bash
74
+
75
+ mkdir -p build-asan
76
+ cd build-asan
77
+ cmake .. -DCMAKE_BUILD_TYPE=ASan
78
+ cmake --build .
79
+ ```
80
+
81
+ ## Run with Address Santizer
82
+
83
+ @include(Build with Address Santizer)
84
+
85
+ After building, run tests with:
86
+
87
+ @run(./build-asan/apex_test_runner advanced_tables) Run with ASan
88
+
89
+ ## Clean build
90
+
91
+ Delete the build directory and run `cmake` clean.
92
+
93
+ ```run Clean all targets
94
+ #!/bin/bash
95
+
96
+ cd build
97
+ cmake --build . --target clean
98
+ ```
99
+
100
+ ## Rebuild from scratch
101
+
102
+ Delete the build directory and build all.
103
+
104
+ ```run Delete build directory and build fresh
105
+ #!/bin/bash
106
+
107
+ rm -rf build
108
+ mkdir build && cd build
109
+ cmake ..
110
+ cmake --build .
111
+ ```
112
+
113
+ ## List Available Tests (search:"")
114
+
115
+ List all available tests, or search for a matching test.
116
+
117
+ ```run List tests
118
+ #!/usr/bin/env ruby
119
+
120
+ $files = `./build/apex_test_runner list`.strip.split(/\n/)
121
+ search = "${search}"
122
+
123
+ def normalize_suite(suite)
124
+ if suite && !suite.empty?
125
+ suite = suite.split('').join('.*')
126
+ $files.delete_if { |f| f !~ /#{suite}/ }
127
+ end
128
+ $files
129
+ end
130
+
131
+ puts normalize_suite(search)
132
+ ```
133
+
134
+ ## Run tests (suite:all, errors_only:false)
135
+
136
+ Build and run the test runner. If suite argument is anything other than "all", run specific suite(s). If errors argument is "errors", "true", or "yes", only show errors.
137
+
138
+ @include(Build CLI)
139
+ @include(Build test runner)
140
+
141
+ ```run Run tests
142
+ #!/usr/bin/env ruby
143
+ require 'English'
144
+
145
+ errors_only = "${errors_only}" =~ /^[ety]/i ? '--errors-only ' : ''
146
+ suite = "${suite}"
147
+ out = ""
148
+ code = 0
149
+
150
+ def normalize_suite(suite)
151
+ files = `./build/apex_test_runner list`.strip.split(/\n/)
152
+
153
+ if suite && !suite.empty?
154
+ suite = suite.split('').join('.*')
155
+ files.delete_if { |f| f !~ /#{suite}/ }
156
+ end
157
+ files
158
+ end
159
+
160
+ if suite =~ /^all$/i
161
+ out = `./build/apex_test_runner #{errors_only} 2>&1`
162
+ code = $CHILD_STATUS.exitstatus
163
+ else
164
+ test_files = normalize_suite(suite)
165
+
166
+ test_files.each do |file|
167
+ puts "Running #{file} tests"
168
+ out += `./build/apex_test_runner #{file} #{errors_only} 2>&1`
169
+ if $CHILD_STATUS.exitstatus != 0
170
+ code = $CHILD_STATUS.exitstatus
171
+ end
172
+ end
173
+ end
174
+
175
+ puts out
176
+
177
+ exit code
178
+ ```
179
+
180
+ @include(Multi-file CLI tests)
181
+
182
+ @include(Metadata CLI tests)
183
+
184
+ @include(Test Swift Package)
185
+
186
+ ## Profile Execution (file:tests/fixtures/comprehensive_test.md)
187
+
188
+ @run(APEX_PROFILE=true build/apex "${file}")
189
+
190
+ ## Profile Plugins (file:tests/fixtures/comprehensive_test.md)
191
+
192
+ @run(APEX_PROFILE_PLUGINS=true build/apex --plugins "${file}")
193
+
194
+ ## Profile All (file:tests/fixtures/comprehensive_test.md)
195
+
196
+ @run(APEX_PROFILE_PLUGINS=true APEX_PROFILE=true build/apex --plugins "${file}")
197
+
198
+ ## Test Swift Package
199
+
200
+ ```run
201
+ #!/bin/bash
202
+ cmake --build build --target swift_package_check
203
+ cmake --build build --target swift_package_check_release
204
+ ```
205
+
206
+ ## Multi-file CLI tests
207
+
208
+ In addition to the C test runner, there is a small shell script that exercises the **CLI** multi-file features (`--combine` and `--mmd-merge`) against the `tests/fixtures/combine_summary` fixtures:
209
+
210
+ ```run Run multi-file CLI tests
211
+ #!/bin/bash
212
+
213
+ ./tests/multi_file_cli_test.sh
214
+ ```
215
+
216
+ This script is intentionally separate from `apex_test_runner` because it tests the command-line interface behavior (flags, piping, and index files) rather than the core library API. Run it after rebuilding `build/apex` when you change multi-file behavior.
217
+
218
+ ## Metadata CLI tests
219
+
220
+ Shell tests for **`--info`**, **`--extract-meta`**, and **`--extract-meta-value`** using `tests/fixtures/metadata` (YAML front matter, MultiMarkdown metadata, and Pandoc title lines).
221
+
222
+ ```run Run metadata CLI tests
223
+ #!/bin/bash
224
+
225
+ ./tests/metadata_cli_test.sh
226
+ ```
227
+
228
+ Run after rebuilding `build/apex` when you change metadata extraction or CLI info output.
229
+
230
+ ## Test basic conversion
231
+
232
+ Render the most basic string.
233
+
234
+ @run(echo "# Test Header" | ./build/apex)
235
+
236
+ ## Test with file (file)
237
+
238
+ Render the most basic file.
239
+
240
+ @run(./build/apex "${file:filetests/test_basic.md}") Render basic file
241
+
242
+ ## Test CommonMark mode (file)
243
+
244
+ @run(./build/apex --mode commonmark "${file:tests/test_basic.md}") Render with CommonMark
245
+
246
+ ## Test GFM mode (file)
247
+
248
+ @run(./build/apex --mode gfm "${file:tests/test_basic.md}") Render with GFM
249
+
250
+ ## Test MultiMarkdown mode (file)
251
+
252
+ @run(./build/apex --mode multimarkdown "${file:tests/test_basic.md}") Render with MMD
253
+
254
+ ## Test Kramdown mode (file)
255
+
256
+ @run(./build/apex --mode kramdown "${file:tests/test_basic.md}") Render with Kramdown
257
+
258
+ ## Test standalone output (file)
259
+
260
+ @run(./build/apex --standalone --title "Test" "${file:tests/test_basic.md}") Render standalone
261
+
262
+ ## Test pretty-print (file)
263
+
264
+ @run(./build/apex --pretty "${file:tests/test_basic.md}") Render pretty print
265
+
266
+ ## Install (prefix)
267
+
268
+ Include an argument with path to change install location.
269
+
270
+ ```run Install to ${prefix} with sudo
271
+ #!/bin/bash
272
+
273
+ cd build
274
+ cmake -DCMAKE_INSTALL_PREFIX=${prefix:/usr/local} ..
275
+ make
276
+ sudo make install
277
+ ```
278
+
279
+ ## Build with debug symbols
280
+
281
+ ```run Build debug
282
+ #!/bin/bash
283
+
284
+ mkdir -p build_debug
285
+ cd build_debug
286
+ cmake -DCMAKE_BUILD_TYPE=Debug ..
287
+ cmake --build .
288
+ ```
289
+
290
+ ## Run with debugger
291
+
292
+ @run(lldb ./build/apex) Run with lldb
293
+
294
+ **Common lldb commands**:
295
+
296
+ ```bash
297
+ # Set breakpoint by function name
298
+ (lldb) breakpoint set --name apex_markdown_to_html
299
+ (lldb) br s -n apex_markdown_to_html
300
+
301
+ # Set breakpoint at file:line
302
+ (lldb) breakpoint set --file apex.c --line 100
303
+ (lldb) br s -f apex.c -l 100
304
+
305
+ # List breakpoints
306
+ (lldb) breakpoint list
307
+ (lldb) br list
308
+
309
+ # Run the program with arguments
310
+ (lldb) run document.md
311
+ (lldb) r document.md
312
+ (lldb) r --mode multimarkdown document.md
313
+
314
+ # Run with stdin
315
+ (lldb) r < input.md
316
+
317
+ # Step over (next line)
318
+ (lldb) next
319
+ (lldb) n
320
+
321
+ # Step into (enter function)
322
+ (lldb) step
323
+ (lldb) s
324
+
325
+ # Continue execution
326
+ (lldb) continue
327
+ (lldb) c
328
+
329
+ # Print variable
330
+ (lldb) print options
331
+ (lldb) p options
332
+ (lldb) p options.enable_tables
333
+
334
+ # Print string
335
+ (lldb) p (char*)html
336
+ (lldb) p/s html
337
+
338
+ # Examine memory
339
+ (lldb) x/s markdown
340
+ (lldb) memory read --size 100 markdown
341
+
342
+ # Show backtrace (call stack)
343
+ (lldb) bt
344
+ (lldb) thread backtrace
345
+
346
+ # List source code
347
+ (lldb) list
348
+ (lldb) l
349
+
350
+ # Show current frame
351
+ (lldb) frame info
352
+
353
+ # Move up/down call stack
354
+ (lldb) up
355
+ (lldb) down
356
+
357
+ # Watch variable
358
+ (lldb) watchpoint set variable html
359
+ (lldb) watchpoint list
360
+
361
+ # Delete breakpoint
362
+ (lldb) breakpoint delete 1
363
+ (lldb) br del 1
364
+
365
+ # Delete all breakpoints
366
+ (lldb) breakpoint delete
367
+ (lldb) br del
368
+
369
+ # Exit
370
+ (lldb) quit
371
+ (lldb) q
372
+ ```
373
+
374
+ **Debugging specific issues**:
375
+
376
+ ```bash
377
+ # Debug crash
378
+ lldb ./build/apex
379
+ (lldb) r document.md
380
+ # When it crashes:
381
+ (lldb) bt
382
+ (lldb) frame variable
383
+
384
+ # Debug with specific mode
385
+ lldb ./build/apex
386
+ (lldb) r --mode kramdown tests/test_basic.md
387
+
388
+ # Debug preprocessing step
389
+ lldb ./build/apex
390
+ (lldb) br s -n apex_process_includes
391
+ (lldb) r --enable-includes document.md
392
+ (lldb) p text_ptr
393
+
394
+ # Debug extension
395
+ lldb ./build/apex
396
+ (lldb) br s -f wiki_links.c -l 50
397
+ (lldb) r document.md
398
+ ```
399
+
400
+ ## Check for memory leaks
401
+
402
+ @run(leaks --atExit -- ./build/apex tests/test_basic.md) Check for leaks
403
+
404
+ ## Build verbose
405
+
406
+ @run(cmake --build build -- VERBOSE=1) Build verbosely
407
+
408
+ ## Benchmark conversion
409
+
410
+ @run(time ./build/apex tests/test_basic.md > /dev/null) Basic benchmark
411
+
412
+ ## Detailed Benchmarks
413
+
414
+ @run(./tests/benchmark.sh) Detailed benchmark with averages
415
+
416
+ ## Comparison Benchmarks
417
+
418
+ @run(./tests/benchmark_comparison.sh) Detailed benchmark comparing processors
419
+
420
+ ## Update Benchmark Markdown
421
+
422
+ Runs both benchmark scripts, outputs to Markdown files, and commits to the repo.
423
+
424
+ ```run Run benchmark scripts and output to Markdown files
425
+ #!/bin/bash
426
+ cd $HOME/Desktop/Code/apex
427
+
428
+ ./tests/benchmark.sh >BENCHMARK.md
429
+ ./tests/benchmark_comparison.sh >BENCHMARK_COMPARISON.md
430
+ ```
431
+
432
+ ```run? Commit to Apex repo
433
+ git add BENCHMARK.md BENCHMARK_COMPARISON.md
434
+ git commit -m "Update benchmarks"
435
+ ```
436
+
437
+ ## Generate Xcode project
438
+
439
+ ```run Generate the xcode project
440
+ #!/bin/bash
441
+
442
+ mkdir -p xcode_build
443
+ cd xcode_build
444
+ cmake -G Xcode ..
445
+ ```
446
+
447
+ @run?!(open Apex.xcodeproj) Open in Xcode
448
+
449
+ ## Fix cmark-gfm submodule
450
+
451
+ ```run Make the cmark-gfm submodule
452
+ #!/bin/bash
453
+
454
+ git submodule update --init --recursive
455
+ cd vendor/cmark-gfm
456
+ mkdir -p build && cd build
457
+ cmake ..
458
+ make
459
+ ```
460
+
461
+ ## Convert standalone document and paginate (file)
462
+
463
+ @run(build/apex "${file}" --standalone --wikilinks | bat) Convert and pipe to bat
464
+
465
+ ## Convert standalone document and open in browser (file)
466
+
467
+ @run(build/apex "${file}" --standalone --wikilinks | bcat) Convert and pipe to bcat
468
+
469
+ ## Convert document fragment (file)
470
+
471
+ ```run Convert file as snippet to output.html
472
+ #!/bin/bash
473
+
474
+ # Compact HTML fragment (default)
475
+ ./build/apex "${file}" > output.html
476
+ ```
477
+
478
+ ## Convert with pretty-print (file)
479
+
480
+ ```run Pretty print file to output.html
481
+ #!/bin/bash
482
+
483
+ # Pretty-formatted HTML
484
+ ./build/apex --pretty "${file}" > output.html
485
+ ```
486
+
487
+ ## Convert standalone document (file, title)
488
+
489
+ ```run Convert file to standalone output.html with title
490
+ #!/bin/bash
491
+
492
+ # Complete HTML5 document
493
+ ./build/apex --standalone --title "${title:Document}" "${file}" > output.html
494
+ ```
495
+
496
+ ## Convert with custom CSS (title,stylesheet)
497
+
498
+ ```run
499
+ #!/bin/bash
500
+
501
+ # Standalone with custom CSS
502
+ ./build/apex -s --title "[%title:Document]" --style [%stylesheet:styles.css] document.md > output.html
503
+ ```
504
+
505
+ ## Convert beautiful complete doc (title,stylesheet)
506
+
507
+ ```run
508
+ #!/bin/bash
509
+
510
+ # Standalone + pretty + custom CSS
511
+ ./build/apex -s --pretty --title "[%title:Document]" --style [%stylesheet:styles.css] document.md -o output.html
512
+ ```
513
+
514
+ ## Process Critic Markup with changes visible
515
+
516
+ ```run
517
+ #!/bin/bash
518
+
519
+ # Show markup with HTML tags (default)
520
+ ./build/apex document.md > review.html
521
+ ```
522
+
523
+ ## Process Critic Markup accept changes
524
+
525
+ ```run
526
+ #!/bin/bash
527
+
528
+ # Accept all changes, clean output
529
+ ./build/apex --accept draft.md > final.html
530
+ ```
531
+
532
+ ## Process Critic Markup reject changes
533
+
534
+ ```run
535
+ #!/bin/bash
536
+
537
+ # Reject all changes, revert to original
538
+ ./build/apex --reject draft.md > original.html
539
+ ```
540
+
541
+ ## Convert with file includes
542
+
543
+ ```run
544
+ #!/bin/bash
545
+
546
+ # Enable file transclusion
547
+ ./build/apex --enable-includes document.md > output.html
548
+ ```
549
+
550
+ ## View help
551
+
552
+ @run(./build/apex --help)
553
+
554
+ ## View version
555
+
556
+ @run(./build/apex --version)
557
+
558
+ ## Project Structure
559
+
560
+ **Source organization**:
561
+
562
+ - `src/apex.c` - Core processor
563
+ - `src/extensions/` - Feature extensions (18 modules)
564
+ - `src/pretty_html.c` - Pretty-print formatter
565
+ - `src/html_renderer.c` - Custom HTML renderer
566
+ - `include/apex/apex.h` - Public API
567
+ - `cli/main.c` - CLI interface
568
+ - `tests/test_runner.c` - Test suite (187 tests)
569
+ - `tests/fixtures/` - Test data files
570
+ - `docs/` - Documentation (16 files)
571
+ - `vendor/cmark-gfm/` - Base parser (submodule)
572
+
573
+ ## Build Targets
574
+
575
+ **Available CMake targets**:
576
+
577
+ - `apex_cli` - Command-line tool
578
+ - `apex` - Shared library
579
+ - `apex_static` - Static library
580
+ - `apex_framework` - macOS framework (macOS only)
581
+ - `apex_test_runner` - Test suite
582
+ - `cmark-gfm` - Vendor dependency (auto-built)
583
+
584
+ ## Common Options
585
+
586
+ **Processor modes** (`--mode`):
587
+
588
+ - `commonmark` - Pure CommonMark spec
589
+ - `gfm` - GitHub Flavored Markdown
590
+ - `multimarkdown` / `mmd` - MultiMarkdown compatibility
591
+ - `kramdown` - Kramdown compatibility
592
+ - `unified` - All features enabled (default)
593
+
594
+ **Output formats**:
595
+
596
+ - Default - Compact HTML fragment
597
+ - `--pretty` - Formatted HTML with indentation
598
+ - `--standalone` / `-s` - Complete HTML5 document
599
+ - `-s --pretty` - Beautiful complete document
600
+
601
+ **Critic Markup modes**:
602
+
603
+ - Default - Show markup with HTML tags
604
+ - `--accept` - Apply all changes
605
+ - `--reject` - Revert all changes
606
+
607
+ **Feature toggles**:
608
+
609
+ - `--enable-includes` - File transclusion
610
+ - `--no-tables` - Disable tables
611
+ - `--no-footnotes` - Disable footnotes
612
+ - `--no-smart` - Disable smart typography
613
+ - `--no-math` - Disable math
614
+ - `--hardbreaks` - Treat newlines as hard breaks
615
+
616
+ **Document options** (standalone mode):
617
+
618
+ - `--title TITLE` - Set document title
619
+ - `--style FILE` - Link external CSS
620
+
621
+ ## Test Coverage
622
+
623
+ **Current status**:
624
+
625
+ - Total tests: 187
626
+ - Passing: 187 (100%)
627
+ - Coverage: 95%
628
+ - Test file: `tests/test_runner.c` (1,089 lines)
629
+
630
+ **Test categories**:
631
+
632
+ - Basic Markdown (5 tests)
633
+ - GFM Features (5 tests)
634
+ - Metadata (4 tests)
635
+ - Wiki Links (3 tests)
636
+ - Math (4 tests)
637
+ - Critic Markup (13 tests)
638
+ - Processor Modes (4 tests)
639
+ - File Includes (16 tests)
640
+ - IAL (5 tests)
641
+ - Definition Lists (11 tests)
642
+ - Advanced Tables (6 tests)
643
+ - Callouts (10 tests)
644
+ - TOC Generation (14 tests)
645
+ - HTML Markdown (9 tests)
646
+ - Abbreviations (11 tests)
647
+ - Emoji (10 tests)
648
+ - Special Markers (7 tests)
649
+ - Footnotes (10 tests)
650
+ - Standalone Output (14 tests)
651
+ - Pretty-Print (11 tests)
652
+
653
+ ## Documentation Files
654
+
655
+ **Available documentation** (17 files):
656
+
657
+ - `README.md` - Project overview
658
+ - `buildnotes.md` - This file
659
+ - `docs/USER_GUIDE.md` - User documentation (693 lines)
660
+ - `docs/API_REFERENCE.md` - C API reference
661
+ - `docs/ARCHITECTURE.md` - System design
662
+ - `docs/CRITICMARKUP.md` - Critic Markup guide
663
+ - `docs/OUTPUT_MODES.md` - Output format guide
664
+ - `docs/STANDALONE_FEATURE.md` - Standalone mode guide
665
+ - `docs/CMARK_INTEGRATION.md` - cmark-gfm integration
666
+ - `docs/MARKED_INTEGRATION.md` - Marked app integration
667
+ - `docs/INTEGRATION_EXAMPLE.m` - Objective-C example
668
+ - `docs/TEST_COVERAGE.md` - Test analysis
669
+ - `docs/LIMITATIONS_RESOLVED.md` - Resolved issues
670
+ - `docs/FINAL_STATUS_UPDATE.md` - Project status
671
+ - `docs/FUTURE_FEATURES.md` - Feature roadmap
672
+ - `docs/PROGRESS.md` - Development progress
673
+ - `docs/WIKI_LINKS_ISSUE.md` - Technical note
674
+
675
+ ## Features
676
+
677
+ **40+ features across 5 Markdown flavors**:
678
+
679
+ - CommonMark compliance
680
+ - GitHub Flavored Markdown (tables, strikethrough, task lists, autolinks)
681
+ - MultiMarkdown (metadata, transclusion, abbreviations, smart typography)
682
+ - Kramdown (IAL, definition lists, attributes)
683
+ - Marked special syntax (callouts, wiki links, file includes, TOC)
684
+
685
+ **Key features**:
686
+
687
+ - 3 abbreviation syntaxes (classic MMD, MMD 6 reference, MMD 6 inline)
688
+ - 3 footnote syntaxes (reference, Kramdown inline, MMD inline)
689
+ - 3 file include syntaxes (Marked, MMD, iA Writer)
690
+ - Advanced tables (rowspan, colspan, captions)
691
+ - Critic Markup with accept/reject modes
692
+ - GitHub emoji (350+)
693
+ - Math support (LaTeX)
694
+ - Definition lists with Markdown content
695
+ - TOC generation with depth control
696
+ - Standalone HTML output with CSS
697
+ - Pretty-print formatting
698
+
699
+ ## Requirements
700
+
701
+ **Build requirements**:
702
+
703
+ - CMake 3.10+
704
+ - C compiler (GCC, Clang, MSVC)
705
+ - Git
706
+
707
+ **Runtime**:
708
+
709
+ - No dependencies (statically linked)
710
+ - macOS 10.13+ / Linux / Windows
711
+
712
+ ## Version
713
+
714
+ **Current version**: 0.1.0
715
+
716
+ Defined in `include/apex/apex.h`:
717
+
718
+ - `APEX_VERSION_MAJOR` - 0
719
+ - `APEX_VERSION_MINOR` - 1
720
+ - `APEX_VERSION_PATCH` - 0
721
+ - `APEX_VERSION_STRING` - "0.1.0"
722
+
723
+ ## Bump Version (increment)
724
+
725
+ @run(make bump-${increment:patch})
726
+
727
+ ## Update Tests Badge
728
+
729
+ Update the test badge in the src/_README.md
730
+
731
+ ```run Replace PASSING and TEST_COLOR comments in [%readme]
732
+ #!/usr/bin/env ruby
733
+
734
+ target = "[%readme:src/_README.md]"
735
+
736
+ system("cmake --build build --target apex_test_runner &>/dev/null")
737
+ pass_total = `build/apex_test_runner --badge`.strip.split("/").map(&:to_i)
738
+
739
+ badge = "![Tests passing #{pass_total[0]}/#{pass_total[1]}](https://img.shields.io/badge/Tests-#{pass_total[0]}/#{pass_total[1]}-#{pass_total[0] == pass_total[1] ? "a5da78" : "f97373"})"
740
+ File.write(target, File.read(target).gsub(/<!--TESTS_BADGE-->.*?<!--END TESTS_BADGE-->/m, "<!--TESTS_BADGE-->#{badge}<!--END TESTS_BADGE-->"))
741
+ ```
742
+
743
+ ## Deploy (increment)
744
+
745
+ To create a release, bump the version in `VERSION`, commit, tag, and push:
746
+
747
+ @before
748
+ Are you sure your changelog Git entries are up to date?
749
+ @end
750
+
751
+ @include(Bump Version[${increment:patch}])
752
+
753
+ ```run Update changelog and create tag message
754
+ #!/bin/bash
755
+
756
+ VERSION=$(cat VERSION | tr -d '[:space:]')
757
+
758
+ echo "v$VERSION" > tag_message.txt
759
+ echo >> tag_message.txt
760
+ changelog >> tag_message.txt
761
+ changelog -u
762
+ ```
763
+
764
+ @include(Update Tests Badge)
765
+ @include(Update GitHub Readme)
766
+ @include(Update Blog Project)
767
+
768
+ ```run Create and push release
769
+ VERSION=$(cat VERSION | tr -d '[:space:]')
770
+
771
+ git add VERSION
772
+ git add CHANGELOG.md
773
+ git add include/apex/apex.h
774
+ git add CMakeLists.txt
775
+ git commit -a -F tag_message.txt
776
+ git tag "v$VERSION" -F tag_message.txt
777
+ git push origin main
778
+ git push origin "v$VERSION"
779
+ rm tag_message.txt
780
+ ```
781
+
782
+ The GitHub Actions workflow will automatically:
783
+
784
+ - Build macOS universal binary (x86_64 + arm64)
785
+ - Build Linux binaries (amd64 + arm64)
786
+ - Generate SHA256 checksums
787
+ - Create GitHub release with all artifacts
788
+
789
+ ```run Wait for CI to build and upload release artifacts
790
+ #!/bin/bash
791
+ # Wait for GitHub Actions to build and upload the tarball
792
+
793
+ RED='\033[0;31m'
794
+ YELLOW='\033[0;33m'
795
+ CYAN='\033[0;36m'
796
+ MAGENTA='\033[0;35m'
797
+ GREEN='\033[0;32m'
798
+ NC='\033[0m' # No Color (reset)
799
+ B_WHITE='\033[1;37m'
800
+
801
+ VERSION=$(cat VERSION | tr -d '[:space:]')
802
+ TARBALL_URL="https://github.com/ApexMarkdown/apex/releases/download/v$VERSION/apex-$VERSION-macos-universal.tar.gz"
803
+
804
+ echo -e "${YELLOW}Waiting for CI to build and upload release artifacts..."
805
+ echo -e "${YELLOW}Checking for: ${B_WHITE}$TARBALL_URL"
806
+
807
+ ATTEMPT_INTERVAL=10
808
+ MAX_ATTEMPTS=60
809
+ ATTEMPT=0
810
+
811
+ while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do
812
+ if curl -s -f -o /dev/null "$TARBALL_URL"; then
813
+ echo -e "${GREEN}✓ Release artifact is available!"
814
+ exit 0
815
+ fi
816
+
817
+ ATTEMPT=$((ATTEMPT + 1))
818
+ echo -ne "\r\033[K${YELLOW}Attempt ${B_WHITE}$ATTEMPT/${CYAN}$MAX_ATTEMPTS: ${YELLOW}Artifact not ready yet, waiting $ATTEMPT_INTERVAL seconds...${NC}"
819
+ sleep $ATTEMPT_INTERVAL
820
+ done
821
+
822
+ echo -e "${RED}⚠ Warning: Release artifact not available after $((MAX_ATTEMPTS * $MAX_INTERVAL)) seconds"
823
+ echo -e "${B_WHITE}You may need to run 'Update Homebrew Formula' manually after CI completes"
824
+ exit 0
825
+ ```
826
+
827
+ @include(Publish the wiki)
828
+ @include(Update Homebrew)
829
+
830
+ @include?!(Release Node Module [${increment}])
831
+ @include?!(Update and Deploy Ruby Gem)
832
+
833
+ ## Update and Deploy Ruby Gem
834
+
835
+ - cd to the apex-ruby directory and run the deploy howzit task.
836
+
837
+ ```run Update Gem
838
+ #!/usr/bin/bash
839
+
840
+ cd ~/Desktop/Code/apex-ruby && howzit -r deploy
841
+ ```
842
+
843
+ ## Release Node module (increment:patch)
844
+
845
+ The Node binding is published as `@apexmarkdown/apex` from the
846
+ [`ApexMarkdown/apex-node`](https://github.com/ApexMarkdown/apex-node)
847
+ repository. Its tag-triggered GitHub Actions workflow builds the platform
848
+ packages first, uploads them as workflow artifacts, and only then runs the
849
+ dependent npm publish job.
850
+
851
+ Before the first release:
852
+
853
+ - Create or join the `apexmarkdown` organization on npm. The
854
+ `@apexmarkdown/*` package scope belongs to that npm organization; the
855
+ GitHub organization does not create it automatically.
856
+ - Enable npm 2FA for authorization and writes. An npm passkey/security key
857
+ satisfies the second-factor requirement for interactive publishing.
858
+ - Log the local npm CLI in with `npm login` and confirm it with `npm whoami`.
859
+ - Trusted publishing is configured on npm for the root and platform packages
860
+ against workflow `publish-prebuilds.yml` in `ApexMarkdown/apex-node`.
861
+
862
+ Default increment is `patch`. Use `increment=current` only to tag the version
863
+ already in `package.json` (first release / retry of an untagged bump). If that
864
+ tag already exists, the task bumps patch automatically and continues. You can
865
+ also pass `minor`, `major`, a prerelease increment, or an exact version. The
866
+ task keeps every optional platform package pinned to the same version as the
867
+ root package.
868
+
869
+ ```run Commit, tag, build, and publish Node module
870
+ #!/bin/bash
871
+ set -euo pipefail
872
+
873
+ NODE_ROOT="$HOME/Desktop/Code/apex-node"
874
+ REPO="ApexMarkdown/apex-node"
875
+ WORKFLOW="publish-prebuilds.yml"
876
+ INCREMENT="${increment:patch}"
877
+
878
+ cd "$NODE_ROOT"
879
+
880
+ if ! git diff --quiet || ! git diff --cached --quiet; then
881
+ echo "The apex-node working tree has uncommitted changes."
882
+ git status --short
883
+ exit 1
884
+ fi
885
+
886
+ git pull --ff-only origin main
887
+ git submodule update --init --recursive
888
+
889
+ sync_platform_versions() {
890
+ # Keep platform package requirements and package-lock metadata synchronized.
891
+ node <<'NODE'
892
+ const fs = require("node:fs");
893
+
894
+ const pkgPath = "package.json";
895
+ const lockPath = "package-lock.json";
896
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
897
+ const lock = JSON.parse(fs.readFileSync(lockPath, "utf8"));
898
+
899
+ for (const name of Object.keys(pkg.optionalDependencies || {})) {
900
+ if (name.startsWith("@apexmarkdown/apex-")) {
901
+ pkg.optionalDependencies[name] = pkg.version;
902
+ }
903
+ }
904
+
905
+ lock.version = pkg.version;
906
+ lock.packages[""].version = pkg.version;
907
+ lock.packages[""].optionalDependencies = pkg.optionalDependencies;
908
+
909
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
910
+ fs.writeFileSync(lockPath, JSON.stringify(lock, null, 2) + "\n");
911
+ NODE
912
+ }
913
+
914
+ case "$INCREMENT" in
915
+ current)
916
+ ;;
917
+ patch|minor|major|prepatch|preminor|premajor|prerelease)
918
+ npm version "$INCREMENT" --no-git-tag-version
919
+ ;;
920
+ [0-9]*)
921
+ npm version "$INCREMENT" --no-git-tag-version
922
+ ;;
923
+ *)
924
+ echo "Unsupported increment: $INCREMENT"
925
+ echo "Use current, patch, minor, major, a prerelease increment,"
926
+ echo "or an exact version."
927
+ exit 1
928
+ ;;
929
+ esac
930
+
931
+ sync_platform_versions
932
+
933
+ VERSION=$(node -p "require('./package.json').version")
934
+ TAG="v$VERSION"
935
+
936
+ if git rev-parse "$TAG" >/dev/null 2>&1; then
937
+ echo "Tag $TAG already exists; bumping patch before tagging again."
938
+ npm version patch --no-git-tag-version
939
+ sync_platform_versions
940
+ VERSION=$(node -p "require('./package.json').version")
941
+ TAG="v$VERSION"
942
+ if git rev-parse "$TAG" >/dev/null 2>&1; then
943
+ echo "Tag $TAG still exists after patch bump."
944
+ exit 1
945
+ fi
946
+ echo "Continuing with $TAG"
947
+ fi
948
+
949
+ echo "Building and testing @apexmarkdown/apex $VERSION"
950
+ npm install --no-audit --no-fund
951
+ npm run build
952
+ npm test
953
+ npm run package:prebuild
954
+ npm pack --dry-run
955
+
956
+ if ! git diff --quiet -- package.json package-lock.json; then
957
+ git add package.json package-lock.json
958
+ git commit -m "$(cat <<EOF
959
+ Release Node module v$VERSION.
960
+
961
+ @changed **Node module release** preparing v$VERSION and its platform packages.
962
+
963
+ EOF
964
+ )"
965
+ else
966
+ echo "Version files are unchanged; tagging the current commit."
967
+ fi
968
+
969
+ git tag -a "$TAG" -m "Release $TAG"
970
+ COMMIT_SHA=$(git rev-parse HEAD)
971
+ git push origin main
972
+ git push origin "$TAG"
973
+
974
+ echo "Waiting for the $WORKFLOW workflow to start..."
975
+ RUN_ID=""
976
+ for attempt in $(seq 1 30); do
977
+ RUN_ID=$(gh run list \
978
+ --repo "$REPO" \
979
+ --workflow "$WORKFLOW" \
980
+ --commit "$COMMIT_SHA" \
981
+ --limit 1 \
982
+ --json databaseId \
983
+ --jq '.[0].databaseId // empty')
984
+ if [ -n "$RUN_ID" ]; then
985
+ break
986
+ fi
987
+ sleep 5
988
+ done
989
+
990
+ if [ -z "$RUN_ID" ]; then
991
+ echo "Could not find the release workflow run for $TAG."
992
+ exit 1
993
+ fi
994
+
995
+ echo "Watching GitHub Actions run $RUN_ID..."
996
+ gh run watch "$RUN_ID" --repo "$REPO" --exit-status
997
+
998
+ echo "Generated artifacts:"
999
+ gh api "repos/$REPO/actions/runs/$RUN_ID/artifacts" \
1000
+ --jq '.artifacts[] | " \(.name)"'
1001
+
1002
+ PUBLISHED=$(npm view "@apexmarkdown/apex@$VERSION" version)
1003
+ if [ "$PUBLISHED" != "$VERSION" ]; then
1004
+ echo "npm did not report @apexmarkdown/apex@$VERSION after the workflow completed."
1005
+ exit 1
1006
+ fi
1007
+
1008
+ echo "Published @apexmarkdown/apex@$PUBLISHED"
1009
+ ```
1010
+
1011
+ The CI matrix publishes all four platform packages: macOS arm64, macOS x64,
1012
+ Linux x64, and Linux arm64 (`ubuntu-24.04-arm`).
1013
+
1014
+ The publish job uses **trusted publishing** (GitHub Actions OIDC) instead of
1015
+ `NPM_TOKEN`: it sets `permissions: id-token: write`, runs Node 24 with the
1016
+ latest npm, and has no `NODE_AUTH_TOKEN` settings. Each published package
1017
+ needs a trusted publisher configured on npmjs.com (Settings -> Trusted
1018
+ Publisher on the package page):
1019
+
1020
+ - Organization or user: `ApexMarkdown`
1021
+ - Repository: `apex-node`
1022
+ - Workflow filename: `publish-prebuilds.yml` (filename only, no path)
1023
+
1024
+ Configure it for `@apexmarkdown/apex` and every `@apexmarkdown/apex-*`
1025
+ platform package. `@apexmarkdown/apex-linux-arm64` was not published in
1026
+ 0.1.0; after the next release creates it, add the same trusted publisher
1027
+ on that package page (or keep `NPM_TOKEN` available for that first
1028
+ create-only publish if OIDC rejects an unpublished name). All fields are
1029
+ case-sensitive and only checked at publish time. After a successful
1030
+ trusted-publish release for every package, delete the `NPM_TOKEN` repo
1031
+ secret and revoke the token on npm:
1032
+
1033
+ ```bash
1034
+ gh secret delete NPM_TOKEN --repo ApexMarkdown/apex-node
1035
+ ```
1036
+
1037
+ ## Publish the wiki
1038
+
1039
+ ```run Commit and publish wiki changes
1040
+ #!/bin/bash
1041
+ # Update the wiki
1042
+
1043
+ VERSION=$(cat VERSION | tr -d '[:space:]')
1044
+ cd $HOME/Desktop/Code/apex.wiki
1045
+ git ar
1046
+ git commit -a -m "v$VERSION update"
1047
+ git pull
1048
+ git push
1049
+ ```
1050
+
1051
+ ## Update Homebrew
1052
+
1053
+ After a release, update the Homebrew formula with the new version and binary SHA256:
1054
+
1055
+ ```run Update Homebrew formula
1056
+ #!/bin/bash
1057
+ set -e
1058
+
1059
+ # Ensure we're in the apex directory (use $HOME instead of ~)
1060
+ APEX_DIR="$HOME/Desktop/Code/apex"
1061
+ cd "$APEX_DIR" || { echo "Error: Could not cd to $APEX_DIR"; exit 1; }
1062
+
1063
+ # Debug: Show current directory and verify VERSION file exists
1064
+ echo "Current directory: $(pwd)"
1065
+ echo "VERSION file exists: $([ -f VERSION ] && echo 'yes' || echo 'no')"
1066
+
1067
+ # Get current version - use absolute path to be safe
1068
+ VERSION_FILE="$APEX_DIR/VERSION"
1069
+ if [ ! -f "$VERSION_FILE" ]; then
1070
+ echo "Error: VERSION file not found at $VERSION_FILE"
1071
+ exit 1
1072
+ fi
1073
+
1074
+ VERSION=$(cat "$VERSION_FILE" | tr -d '[:space:]')
1075
+
1076
+ if [ -z "$VERSION" ]; then
1077
+ echo "Error: VERSION file is empty or could not be read from $VERSION_FILE"
1078
+ echo "Contents: '$(cat "$VERSION_FILE")'"
1079
+ exit 1
1080
+ fi
1081
+
1082
+ echo "Detected version: $VERSION"
1083
+
1084
+ # Download the macOS universal tarball and compute its SHA256
1085
+ # NOTE: Use $VERSION (no braces) so Howzit doesn"t try to expand ${VERSION}"
1086
+ TARBALL_URL="https://github.com/ApexMarkdown/apex/releases/download/v$VERSION/apex-$VERSION-macos-universal.tar.gz"
1087
+ TARBALL_PATH="/tmp/apex-$VERSION-macos-universal.tar.gz"
1088
+
1089
+ echo "Downloading tarball from $TARBALL_URL"
1090
+ curl -L -o "$TARBALL_PATH" "$TARBALL_URL"
1091
+
1092
+ if [ ! -f "$TARBALL_PATH" ]; then
1093
+ echo "Error: Failed to download tarball to $TARBALL_PATH"
1094
+ exit 1
1095
+ fi
1096
+
1097
+ echo "Raw shasum output:"
1098
+ SHASUM_OUTPUT=$(shasum -a 256 "$TARBALL_PATH")
1099
+ echo "$SHASUM_OUTPUT"
1100
+
1101
+ # Extract just the SHA256 (first field) without using awk/curly braces (to avoid Howzit templating issues)
1102
+ SHA256=$(printf '%s\n' "$SHASUM_OUTPUT" | cut -d ' ' -f 1)
1103
+
1104
+ if [ -z "$SHA256" ]; then
1105
+ echo "Error: Failed to compute SHA256 for $TARBALL_PATH"
1106
+ exit 1
1107
+ fi
1108
+
1109
+ echo "Computed SHA256: $SHA256"
1110
+
1111
+ # Verify Formula file exists
1112
+ FORMULA_FILE="$APEX_DIR/Formula/apex.rb"
1113
+ if [ ! -f "$FORMULA_FILE" ]; then
1114
+ echo "Error: Formula file not found at $FORMULA_FILE"
1115
+ exit 1
1116
+ fi
1117
+
1118
+ # Debug: Verify variables are still set before using them
1119
+ echo "Before update - VERSION='$VERSION', SHA256='$SHA256'"
1120
+
1121
+ # Create a temporary update script to ensure variables are available
1122
+ cat > /tmp/update_formula.rb << 'RUBY_SCRIPT'
1123
+ #!/usr/bin/env ruby
1124
+ version = ARGV[0]
1125
+ sha256 = ARGV[1]
1126
+ file = ARGV[2]
1127
+
1128
+ content = File.read(file)
1129
+ content.gsub!(/^ version "[^"]*"/, " version \"#{version}\"")
1130
+ content.gsub!(/^ sha256 "[^"]*"/, " sha256 \"#{sha256}\"")
1131
+
1132
+ File.write(file, content)
1133
+ RUBY_SCRIPT
1134
+
1135
+ chmod +x /tmp/update_formula.rb
1136
+ /tmp/update_formula.rb "$VERSION" "$SHA256" "$FORMULA_FILE"
1137
+ rm /tmp/update_formula.rb
1138
+
1139
+ # Debug: Verify variables are still set after update
1140
+ echo "After update - VERSION='$VERSION', SHA256='$SHA256'"
1141
+
1142
+ echo "Updated Formula/apex.rb to version ${VERSION} with SHA256 ${SHA256}"
1143
+ cat "$FORMULA_FILE" | grep -E '(version "|sha256 ")'
1144
+ ```
1145
+
1146
+ ```run Copy formula and push to homebrew-thelab
1147
+ #!/bin/bash
1148
+
1149
+ # Copy formula to homebrew-thelab repo
1150
+ cp Formula/apex.rb ~/Desktop/Code/homebrew-thelab/Formula/
1151
+
1152
+ cat ~/Desktop/Code/homebrew-thelab/Formula/apex.rb
1153
+ ```
1154
+
1155
+ ```run Commit and push formula
1156
+ #!/bin/bash
1157
+
1158
+ # Commit and push
1159
+ cd ~/Desktop/Code/homebrew-thelab
1160
+ git add Formula/apex.rb
1161
+ git commit -m "Update apex to $(grep 'version "' Formula/apex.rb | sed 's/.*version "\([^"]*\)".*/\1/')"
1162
+ git push origin main
1163
+
1164
+ echo "Homebrew formula committed and pushed!"
1165
+ ```
1166
+
1167
+ ## Deploy mk CLI (increment:patch, upload_site:false)
1168
+
1169
+ Recommended distribution path: publish `mk.pkg` to a GitHub Release and have Homebrew install it from your tap as a **cask**. Homebrew formulae are best for source/tarball installs, while notarized PKGs map cleanly to casks and use the PKG SHA directly.
1170
+
1171
+ If you still want `https://markedapp.com/download/mk.pkg`, keep that URL as a mirror by copying the same notarized file there after release.
1172
+
1173
+ ```run Build, sign, package, notarize, and staple mk.pkg
1174
+ #!/bin/bash
1175
+ set -euo pipefail
1176
+
1177
+ MK_ROOT="$HOME/Desktop/Code/marked/mk"
1178
+ PBXPROJ="$MK_ROOT/mk.xcodeproj/project.pbxproj"
1179
+ DERIVED="$MK_ROOT/.build-mk-release"
1180
+ PKG_STAGE="$MK_ROOT/package"
1181
+
1182
+ APP_SIGNING_IDENTITY="${APP_SIGNING_IDENTITY:-Developer ID Application: Brett Terpstra (47TRS7H4BH)}"
1183
+ INSTALLER_SIGNATURE="${INSTALLER_SIGNATURE:-Developer ID Installer: Brett Terpstra (47TRS7H4BH)}"
1184
+ NOTARY_PROFILE="${NOTARY_PROFILE:-Developer-notarytool}"
1185
+ PKG_IDENTIFIER="${PKG_IDENTIFIER:-com.brettterpstra.mk}"
1186
+
1187
+ MK_VERSION=$(ruby -e 'f=ARGV[0]; s=File.read(f); m=s.match(/MARKETING_VERSION = ([^;]+);/); abort("Could not parse MARKETING_VERSION") unless m; puts m[1].strip' "$PBXPROJ")
1188
+ PKG_PATH="$MK_ROOT/mk-$MK_VERSION.pkg"
1189
+
1190
+ echo "Building mk v$MK_VERSION"
1191
+ rm -rf "$DERIVED" "$PKG_STAGE" "$PKG_PATH"
1192
+
1193
+ xcodebuild \
1194
+ -project "$MK_ROOT/mk.xcodeproj" \
1195
+ -scheme mk \
1196
+ -configuration Release \
1197
+ -derivedDataPath "$DERIVED" \
1198
+ ARCHS="arm64 x86_64" \
1199
+ ONLY_ACTIVE_ARCH=NO \
1200
+ build
1201
+
1202
+ BIN_PATH="$DERIVED/Build/Products/Release/mk"
1203
+ if [ ! -x "$BIN_PATH" ]; then
1204
+ echo "Could not find built mk binary at $BIN_PATH"
1205
+ exit 1
1206
+ fi
1207
+
1208
+ codesign --force --sign "$APP_SIGNING_IDENTITY" --timestamp --options runtime "$BIN_PATH"
1209
+ codesign --verify -vvvv "$BIN_PATH"
1210
+
1211
+ mkdir -p "$PKG_STAGE/usr/local/bin"
1212
+ cp "$BIN_PATH" "$PKG_STAGE/usr/local/bin/mk"
1213
+
1214
+ pkgbuild \
1215
+ --root "$PKG_STAGE" \
1216
+ --identifier "$PKG_IDENTIFIER" \
1217
+ --version "$MK_VERSION" \
1218
+ --sign "$INSTALLER_SIGNATURE" \
1219
+ "$PKG_PATH"
1220
+
1221
+ echo "Submitting for notarization..."
1222
+ xcrun notarytool submit "$PKG_PATH" --keychain-profile "$NOTARY_PROFILE" --wait
1223
+ xcrun stapler staple "$PKG_PATH"
1224
+ xcrun stapler validate "$PKG_PATH"
1225
+
1226
+ SHA256=$(shasum -a 256 "$PKG_PATH" | cut -d ' ' -f 1)
1227
+ echo "$SHA256" > "$MK_ROOT/mk-$MK_VERSION.pkg.sha256"
1228
+
1229
+ echo "Built package: $PKG_PATH"
1230
+ echo "SHA256: $SHA256"
1231
+ ```
1232
+
1233
+ ```run Publish mk.pkg to GitHub Release (recommended canonical host)
1234
+ #!/bin/bash
1235
+ set -euo pipefail
1236
+
1237
+ MK_ROOT="$HOME/Desktop/Code/marked/mk"
1238
+ PBXPROJ="$MK_ROOT/mk.xcodeproj/project.pbxproj"
1239
+ MK_RELEASE_REPO="${MK_RELEASE_REPO:-ttscoff/mk}"
1240
+ MK_VERSION=$(ruby -e 'f=ARGV[0]; s=File.read(f); m=s.match(/MARKETING_VERSION = ([^;]+);/); abort("Could not parse MARKETING_VERSION") unless m; puts m[1].strip' "$PBXPROJ")
1241
+ PKG_PATH="$MK_ROOT/mk-$MK_VERSION.pkg"
1242
+
1243
+ if [ ! -f "$PKG_PATH" ]; then
1244
+ echo "Missing package: $PKG_PATH"
1245
+ echo "Run 'Build, sign, package, notarize, and staple mk.pkg' first."
1246
+ exit 1
1247
+ fi
1248
+
1249
+ if gh release view "v$MK_VERSION" --repo "$MK_RELEASE_REPO" >/dev/null 2>&1; then
1250
+ gh release upload "v$MK_VERSION" "$PKG_PATH" --repo "$MK_RELEASE_REPO" --clobber
1251
+ else
1252
+ gh release create "v$MK_VERSION" "$PKG_PATH" \
1253
+ --repo "$MK_RELEASE_REPO" \
1254
+ --title "mk v$MK_VERSION" \
1255
+ --notes "Notarized mk CLI installer package."
1256
+ fi
1257
+
1258
+ echo "Release asset URL:"
1259
+ echo "https://github.com/$MK_RELEASE_REPO/releases/download/v$MK_VERSION/mk-$MK_VERSION.pkg"
1260
+ ```
1261
+
1262
+ ```run Optionally mirror mk.pkg to markedapp.com/download
1263
+ #!/bin/bash
1264
+ set -euo pipefail
1265
+
1266
+ MK_ROOT="$HOME/Desktop/Code/marked/mk"
1267
+ PBXPROJ="$MK_ROOT/mk.xcodeproj/project.pbxproj"
1268
+ MARKEDAPP_SSH_TARGET="${MARKEDAPP_SSH_TARGET:-markedapp@your-server:/var/www/markedapp.com/download/mk.pkg}"
1269
+ MK_VERSION=$(ruby -e 'f=ARGV[0]; s=File.read(f); m=s.match(/MARKETING_VERSION = ([^;]+);/); abort("Could not parse MARKETING_VERSION") unless m; puts m[1].strip' "$PBXPROJ")
1270
+ PKG_PATH="$MK_ROOT/mk-$MK_VERSION.pkg"
1271
+
1272
+ if [ ! -f "$PKG_PATH" ]; then
1273
+ echo "Missing package: $PKG_PATH"
1274
+ exit 1
1275
+ fi
1276
+
1277
+ scp "$PKG_PATH" "$MARKEDAPP_SSH_TARGET"
1278
+ echo "Uploaded to $MARKEDAPP_SSH_TARGET"
1279
+ ```
1280
+
1281
+ ```run Update Homebrew tap entry for mk.pkg (cask)
1282
+ #!/bin/bash
1283
+ set -euo pipefail
1284
+
1285
+ MK_ROOT="$HOME/Desktop/Code/marked/mk"
1286
+ PBXPROJ="$MK_ROOT/mk.xcodeproj/project.pbxproj"
1287
+ TAP_ROOT="$HOME/Desktop/Code/homebrew-thelab"
1288
+ MK_RELEASE_REPO="${MK_RELEASE_REPO:-ttscoff/mk}"
1289
+ MK_VERSION=$(ruby -e 'f=ARGV[0]; s=File.read(f); m=s.match(/MARKETING_VERSION = ([^;]+);/); abort("Could not parse MARKETING_VERSION") unless m; puts m[1].strip' "$PBXPROJ")
1290
+ PKG_PATH="$MK_ROOT/mk-$MK_VERSION.pkg"
1291
+ SHA256=$(shasum -a 256 "$PKG_PATH" | cut -d ' ' -f 1)
1292
+
1293
+ mkdir -p "$TAP_ROOT/Casks"
1294
+ CASK_FILE="$TAP_ROOT/Casks/mk.rb"
1295
+
1296
+ cat > "$CASK_FILE" <<EOF
1297
+ cask "mk" do
1298
+ version "$MK_VERSION"
1299
+ sha256 "$SHA256"
1300
+
1301
+ url "https://github.com/$MK_RELEASE_REPO/releases/download/v#{version}/mk-#{version}.pkg"
1302
+ name "mk"
1303
+ desc "Marked command line utility"
1304
+ homepage "https://markedapp.com"
1305
+
1306
+ pkg "mk-#{version}.pkg"
1307
+ uninstall pkgutil: "com.brettterpstra.mk"
1308
+ end
1309
+ EOF
1310
+
1311
+ cd "$TAP_ROOT"
1312
+ git add "$CASK_FILE"
1313
+ git commit -m "Add/update mk cask v$MK_VERSION"
1314
+ git push origin main
1315
+
1316
+ echo "Updated $CASK_FILE with sha256 $SHA256"
1317
+ ```
1318
+
1319
+ If you decide to keep Homebrew as a **formula** instead of a cask, publish a tarball containing only the `mk` binary and use that tarball SHA in `Formula/mk.rb`. A formula that downloads a PKG is possible but not recommended.
1320
+
1321
+ ## AST filters (Pandoc-style)
1322
+
1323
+ Apex now supports **AST-level filters** similar to Pandoc's JSON filters
1324
+ ([Pandoc filters](https://pandoc.org/filters.html#summary)).
1325
+
1326
+ - Pipeline:
1327
+ - Markdown → cmark-gfm AST → **Pandoc-compatible JSON AST**
1328
+ - JSON AST → external filters → JSON AST
1329
+ - JSON AST → cmark-gfm AST → HTML
1330
+ - Filters are ordinary executables that:
1331
+ - Read a Pandoc JSON AST from **stdin**
1332
+ - Write a (possibly transformed) JSON AST to **stdout**
1333
+
1334
+ ### CLI flags
1335
+
1336
+ - `--filter NAME`:
1337
+ - Runs a single filter from `~/.config/apex/filters/NAME`
1338
+ - `NAME` should be an executable (any language) that implements the Pandoc
1339
+ JSON filter contract.
1340
+ - `--filters`:
1341
+ - Runs all executable files in `~/.config/apex/filters`, sorted by filename.
1342
+ - `--no-strict-filters`:
1343
+ - By default, Apex **aborts** if a filter exits non-zero or returns invalid JSON.
1344
+ - With this flag, Apex logs a warning and **skips** the failing filter instead.
1345
+
1346
+ ### Ruby bindings
1347
+
1348
+ `Apex::Document.markdown_to_html` accepts two new options:
1349
+
1350
+ - `ast_filters: [ "cmd1", "cmd2" ]`
1351
+ - `ast_filter_strict: false` (optional, defaults to strict)
1352
+
1353
+ Example:
1354
+
1355
+ ```ruby
1356
+ html = Apex::Document.markdown_to_html(
1357
+ markdown,
1358
+ mode: :unified,
1359
+ ast_filters: ["~/.config/apex/filters/behead"],
1360
+ ast_filter_strict: true
1361
+ )
1362
+ ```
1363
+
1364
+ ### Example filters
1365
+
1366
+ Python (using `pandocfilters`) – convert level 2+ headings to paragraphs
1367
+ with emphasized text:
1368
+
1369
+ ```python
1370
+ #!/usr/bin/env python3
1371
+
1372
+ from pandocfilters import toJSONFilter, Emph, Para
1373
+
1374
+ def behead(key, value, fmt, meta):
1375
+ if key == "Header" and value[0] >= 2:
1376
+ return Para([Emph(value[2])])
1377
+
1378
+ if __name__ == "__main__":
1379
+ toJSONFilter(behead)
1380
+ ```
1381
+
1382
+ Install as `~/.config/apex/filters/behead` (make it executable), then:
1383
+
1384
+ ```bash
1385
+ apex --filter behead input.md > output.html
1386
+ ```
1387
+
1388
+ Ruby filter – uppercase all words in plain paragraphs:
1389
+
1390
+ ```ruby
1391
+ #!/usr/bin/env ruby
1392
+
1393
+ require "json"
1394
+
1395
+ doc = JSON.parse($stdin.read)
1396
+
1397
+ doc["blocks"].each do |blk|
1398
+ next unless blk["t"] == "Para"
1399
+ blk["c"].each do |inline|
1400
+ next unless inline["t"] == "Str"
1401
+ inline["c"] = inline["c"].upcase
1402
+ end
1403
+ end
1404
+
1405
+ puts JSON.dump(doc)
1406
+ ```
1407
+
1408
+ Save as `~/.config/apex/filters/upper_para` (and `chmod +x`), then:
1409
+
1410
+ ```bash
1411
+ apex --filter upper_para input.md > output.html
1412
+ ```
1413
+
1414
+ ## Cursor Commands (command)
1415
+
1416
+
1417
+ /buildnotes
1418
+ : Add sections to this file
1419
+
1420
+ /changelog
1421
+ : Generate a commit_message.txt with @tags and notes about staged and unstaged changes.
1422
+
1423
+ /commit
1424
+ : Run `git commit -a -F commit_message.txt`
1425
+
1426
+ /docs
1427
+ : Update the Wiki documentation (../apex.wiki)
1428
+
1429
+ /prefix_commands
1430
+ : Instruct Cursor to run appropriate commands with `mise x`, and prefix built-ins with `command`.
1431
+
1432
+ /readme
1433
+ : Update src/_README.md
1434
+
1435
+ @run(cursor-agent --output-format text --print .cursor/commands/${command:changelog}.md && afplay /System/Library/Sounds/Hero.aiff)