sx-tools: WASM kernel updates, TW/CSSX rework, content refresh, new debugging tools

Build tooling: updated OCaml bootstrapper, compile-modules, bundle.sh, sx-build-all.
WASM browser: rebuilt sx_browser.bc.js/wasm, sx-platform-2.js, .sxbc bytecode files.
CSSX/Tailwind: reworked cssx.sx templates and tw-layout, added tw-type support.
Content: refreshed essays, plans, geography, reactive islands, docs, demos, handlers.
New tools: bisect_sxbc.sh, test-spa.js, render-trace.sx, morph playwright spec.
Tests: added test-match.sx, test-examples.sx, updated test-tw.sx and web tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-02 11:31:57 +00:00
parent 9ed1100ef6
commit d40a9c6796
178 changed files with 13591 additions and 9110 deletions

View File

@@ -3,7 +3,7 @@
:id "debugging"
(p
"Three tools for understanding evaluation behavior, dependency chains, and build composition.")
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "Trace evaluation")
(h4 (~tw :tokens "font-semibold text-stone-700 mt-6 mb-2") "Trace evaluation")
(p
(code "sx_trace")
" steps through the CEK machine showing each symbol lookup, function call, and return value. Invaluable for debugging undefined symbol errors and unexpected evaluation paths.")
@@ -14,7 +14,7 @@
"The "
(code "file")
" parameter loads definitions before tracing, so you can trace expressions that reference project-specific functions.")
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "Dependency analysis")
(h4 (~tw :tokens "font-semibold text-stone-700 mt-6 mb-2") "Dependency analysis")
(p
(code "sx_deps")
" walks a component or file and collects every free symbol -- names that are referenced but not locally bound. For each symbol it reports where the definition lives: same file, another "
@@ -29,7 +29,7 @@
" entries are HTML tags or symbols defined at runtime. This tool catches missing imports and build pipeline gaps -- it would have instantly revealed that "
(code "resource")
" was missing from the browser island environment.")
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "Build manifest")
(h4 (~tw :tokens "font-semibold text-stone-700 mt-6 mb-2") "Build manifest")
(p
(code "sx_build_manifest")
" shows what a JS or OCaml build contains: which adapters, spec modules, primitive modules, and extensions are included. For JS builds it also reports the current bundle size and primitive count.")
@@ -43,7 +43,7 @@
(~docs/page
:title title
(p
:class "text-stone-500 text-sm italic mb-8"
(~tw :tokens "text-stone-500 text-sm italic mb-8")
"A structural tree editor for s-expression files -- because the thing that reads and edits code should understand the code as a tree, not as a sequence of characters.")
(~docs/section
:title "Tool catalogue"
@@ -51,274 +51,274 @@
(p
"The SX tree MCP server provides 40+ tools across 9 categories. Every tool operates on the parsed tree, not raw text.")
(h4
:class "font-semibold text-stone-700 mt-6 mb-2"
(~tw :tokens "font-semibold text-stone-700 mt-6 mb-2")
"Comprehension (7 tools)")
(p "Read-only tools for understanding structure before editing.")
(table
:class "min-w-full text-sm mb-6"
(~tw :tokens "min-w-full text-sm mb-6")
(thead
(tr
(th
:class "text-left pr-4 pb-2 font-semibold text-stone-700"
(~tw :tokens "text-left pr-4 pb-2 font-semibold text-stone-700")
"Tool")
(th :class "text-left pb-2 font-semibold text-stone-700" "Purpose")))
(th (~tw :tokens "text-left pb-2 font-semibold text-stone-700") "Purpose")))
(tbody
:class "text-stone-600"
(~tw :tokens "text-stone-600")
(tr
(td :class "pr-4 py-1 font-mono text-xs" "sx_read_tree")
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "sx_read_tree")
(td
:class "py-1"
(~tw :tokens "py-1")
"Annotated tree with path labels. Auto-summarises large files. Params: focus (expand matching subtrees), max_depth, max_lines/offset."))
(tr
(td :class "pr-4 py-1 font-mono text-xs" "sx_summarise")
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "sx_summarise")
(td
:class "py-1"
(~tw :tokens "py-1")
"Folded overview at configurable depth -- shape without detail."))
(tr
(td :class "pr-4 py-1 font-mono text-xs" "sx_read_subtree")
(td :class "py-1" "Expand a specific subtree by path."))
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "sx_read_subtree")
(td (~tw :tokens "py-1") "Expand a specific subtree by path."))
(tr
(td :class "pr-4 py-1 font-mono text-xs" "sx_get_context")
(td :class "py-1" "Enclosing chain from root to a target node."))
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "sx_get_context")
(td (~tw :tokens "py-1") "Enclosing chain from root to a target node."))
(tr
(td :class "pr-4 py-1 font-mono text-xs" "sx_find_all")
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "sx_find_all")
(td
:class "py-1"
(~tw :tokens "py-1")
"Search by pattern in one file, returns matching paths."))
(tr
(td :class "pr-4 py-1 font-mono text-xs" "sx_get_siblings")
(td :class "py-1" "Siblings of a node with the target marked."))
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "sx_get_siblings")
(td (~tw :tokens "py-1") "Siblings of a node with the target marked."))
(tr
(td :class "pr-4 py-1 font-mono text-xs" "sx_validate")
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "sx_validate")
(td
:class "py-1"
(~tw :tokens "py-1")
"Structural integrity: balanced parens, valid paths."))))
(~docs/code
:src ";; Focus mode -- expand only matching subtrees\nsx_read_tree file=\"home.sx\" focus=\"defisland\"\n\n;; Paginated -- first 30 lines\nsx_read_tree file=\"home.sx\" max_lines=30\n\n;; Context chain for a deep node\nsx_get_context file=\"home.sx\" path=\"(0 2 2 1 12)\"")
(h4
:class "font-semibold text-stone-700 mt-6 mb-2"
(~tw :tokens "font-semibold text-stone-700 mt-6 mb-2")
"Path-based editing (4 tools)")
(p
"Structural edits by tree path. Fragment is parsed before the file is touched.")
(table
:class "min-w-full text-sm mb-6"
(~tw :tokens "min-w-full text-sm mb-6")
(thead
(tr
(th
:class "text-left pr-4 pb-2 font-semibold text-stone-700"
(~tw :tokens "text-left pr-4 pb-2 font-semibold text-stone-700")
"Tool")
(th :class "text-left pb-2 font-semibold text-stone-700" "Purpose")))
(th (~tw :tokens "text-left pb-2 font-semibold text-stone-700") "Purpose")))
(tbody
:class "text-stone-600"
(~tw :tokens "text-stone-600")
(tr
(td :class "pr-4 py-1 font-mono text-xs" "sx_replace_node")
(td :class "py-1" "Replace node at path with new parsed source."))
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "sx_replace_node")
(td (~tw :tokens "py-1") "Replace node at path with new parsed source."))
(tr
(td :class "pr-4 py-1 font-mono text-xs" "sx_insert_child")
(td :class "py-1" "Insert child at index within a list."))
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "sx_insert_child")
(td (~tw :tokens "py-1") "Insert child at index within a list."))
(tr
(td :class "pr-4 py-1 font-mono text-xs" "sx_delete_node")
(td :class "py-1" "Remove node -- siblings shift to fill gap."))
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "sx_delete_node")
(td (~tw :tokens "py-1") "Remove node -- siblings shift to fill gap."))
(tr
(td :class "pr-4 py-1 font-mono text-xs" "sx_wrap_node")
(td :class "py-1" "Wrap node in template with _ placeholder."))))
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "sx_wrap_node")
(td (~tw :tokens "py-1") "Wrap node in template with _ placeholder."))))
(~docs/code
:src ";; Replace a node\nsx_replace_node file=\"home.sx\" path=\"(0 2 1)\"\n new_source=\"(div :class \\\"updated\\\")\"\n\n;; Wrap in a when guard\nsx_wrap_node file=\"home.sx\" path=\"(0 3)\"\n template=\"(when active _)\"")
(h4
:class "font-semibold text-stone-700 mt-6 mb-2"
(~tw :tokens "font-semibold text-stone-700 mt-6 mb-2")
"Smart editing (4 tools)")
(p "Pattern-based: find by name, then edit. No path arithmetic needed.")
(table
:class "min-w-full text-sm mb-6"
(~tw :tokens "min-w-full text-sm mb-6")
(thead
(tr
(th
:class "text-left pr-4 pb-2 font-semibold text-stone-700"
(~tw :tokens "text-left pr-4 pb-2 font-semibold text-stone-700")
"Tool")
(th :class "text-left pb-2 font-semibold text-stone-700" "Purpose")))
(th (~tw :tokens "text-left pb-2 font-semibold text-stone-700") "Purpose")))
(tbody
:class "text-stone-600"
(~tw :tokens "text-stone-600")
(tr
(td :class "pr-4 py-1 font-mono text-xs" "sx_rename_symbol")
(td :class "py-1" "Rename all occurrences of a symbol in a file."))
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "sx_rename_symbol")
(td (~tw :tokens "py-1") "Rename all occurrences of a symbol in a file."))
(tr
(td :class "pr-4 py-1 font-mono text-xs" "sx_replace_by_pattern")
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "sx_replace_by_pattern")
(td
:class "py-1"
(~tw :tokens "py-1")
"Find + replace first/all nodes matching a pattern."))
(tr
(td :class "pr-4 py-1 font-mono text-xs" "sx_insert_near")
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "sx_insert_near")
(td
:class "py-1"
(~tw :tokens "py-1")
"Insert before/after a pattern match (top-level)."))
(tr
(td :class "pr-4 py-1 font-mono text-xs" "sx_rename_across")
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "sx_rename_across")
(td
:class "py-1"
(~tw :tokens "py-1")
"Rename symbol across all .sx files in a directory. dry_run=true to preview."))))
(~docs/code
:src ";; Rename a component everywhere\nsx_rename_across dir=\"sx/\" old_name=\"~card\"\n new_name=\"~ui/card\" dry_run=true\n;; → home.sx: 3 occurrences (dry run)\n;; → layout.sx: 1 occurrence (dry run)")
(h4
:class "font-semibold text-stone-700 mt-6 mb-2"
(~tw :tokens "font-semibold text-stone-700 mt-6 mb-2")
"Project-wide search (3 tools)")
(p "Search across all .sx files in a directory tree.")
(table
:class "min-w-full text-sm mb-6"
(~tw :tokens "min-w-full text-sm mb-6")
(thead
(tr
(th
:class "text-left pr-4 pb-2 font-semibold text-stone-700"
(~tw :tokens "text-left pr-4 pb-2 font-semibold text-stone-700")
"Tool")
(th :class "text-left pb-2 font-semibold text-stone-700" "Purpose")))
(th (~tw :tokens "text-left pb-2 font-semibold text-stone-700") "Purpose")))
(tbody
:class "text-stone-600"
(~tw :tokens "text-stone-600")
(tr
(td :class "pr-4 py-1 font-mono text-xs" "sx_find_across")
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "sx_find_across")
(td
:class "py-1"
(~tw :tokens "py-1")
"Search pattern across all .sx files. Returns file + path + summary."))
(tr
(td :class "pr-4 py-1 font-mono text-xs" "sx_comp_list")
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "sx_comp_list")
(td
:class "py-1"
(~tw :tokens "py-1")
"List all definitions (defcomp/defisland/defmacro/defpage/define) across files."))
(tr
(td :class "pr-4 py-1 font-mono text-xs" "sx_comp_usage")
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "sx_comp_usage")
(td
:class "py-1"
(~tw :tokens "py-1")
"Find all uses of a component/symbol across files."))))
(~docs/code
:src ";; Who uses ~docs/section?\nsx_comp_usage dir=\"sx/\" name=\"~docs/section\"\n;; → sx-tools.sx [0,3,4] (~docs/section :title ...)\n;; → sx-tools.sx [0,3,5] (~docs/section :title ...)")
(h4
:class "font-semibold text-stone-700 mt-6 mb-2"
(~tw :tokens "font-semibold text-stone-700 mt-6 mb-2")
"Development (7 tools)")
(p "Build, test, lint, format, evaluate.")
(table
:class "min-w-full text-sm mb-6"
(~tw :tokens "min-w-full text-sm mb-6")
(thead
(tr
(th
:class "text-left pr-4 pb-2 font-semibold text-stone-700"
(~tw :tokens "text-left pr-4 pb-2 font-semibold text-stone-700")
"Tool")
(th :class "text-left pb-2 font-semibold text-stone-700" "Purpose")))
(th (~tw :tokens "text-left pb-2 font-semibold text-stone-700") "Purpose")))
(tbody
:class "text-stone-600"
(~tw :tokens "text-stone-600")
(tr
(td :class "pr-4 py-1 font-mono text-xs" "sx_pretty_print")
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "sx_pretty_print")
(td
:class "py-1"
(~tw :tokens "py-1")
"Reformat .sx file with indentation. All edit tools auto-format."))
(tr
(td :class "pr-4 py-1 font-mono text-xs" "sx_write_file")
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "sx_write_file")
(td
:class "py-1"
(~tw :tokens "py-1")
"Create/overwrite .sx file with parse validation."))
(tr
(td :class "pr-4 py-1 font-mono text-xs" "sx_build")
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "sx_build")
(td
:class "py-1"
(~tw :tokens "py-1")
"Build JS bundle (target=js) or OCaml binary (target=ocaml)."))
(tr
(td :class "pr-4 py-1 font-mono text-xs" "sx_test")
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "sx_test")
(td
:class "py-1"
(~tw :tokens "py-1")
"Run test suite -- host=js or ocaml, full=true for extensions."))
(tr
(td :class "pr-4 py-1 font-mono text-xs" "sx_format_check")
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "sx_format_check")
(td
:class "py-1"
(~tw :tokens "py-1")
"Lint: empty bindings, missing bodies, duplicate params."))
(tr
(td :class "pr-4 py-1 font-mono text-xs" "sx_macroexpand")
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "sx_macroexpand")
(td
:class "py-1"
(~tw :tokens "py-1")
"Evaluate expression with a file's macro definitions loaded."))
(tr
(td :class "pr-4 py-1 font-mono text-xs" "sx_eval")
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "sx_eval")
(td
:class "py-1"
(~tw :tokens "py-1")
"REPL -- evaluate SX expressions in the MCP server env."))))
(~docs/code
:src ";; Build and test in one go\nsx_build target=\"ocaml\"\nsx_test host=\"ocaml\"\n;; → Results: 1116 passed, 0 failed\n\n;; Quick REPL check\nsx_eval \"(+ (* 3 4) 5)\" ;; → 17")
(h4
:class "font-semibold text-stone-700 mt-6 mb-2"
(~tw :tokens "font-semibold text-stone-700 mt-6 mb-2")
"Git integration (3 tools)")
(p "Structural awareness of version control.")
(table
:class "min-w-full text-sm mb-6"
(~tw :tokens "min-w-full text-sm mb-6")
(thead
(tr
(th
:class "text-left pr-4 pb-2 font-semibold text-stone-700"
(~tw :tokens "text-left pr-4 pb-2 font-semibold text-stone-700")
"Tool")
(th :class "text-left pb-2 font-semibold text-stone-700" "Purpose")))
(th (~tw :tokens "text-left pb-2 font-semibold text-stone-700") "Purpose")))
(tbody
:class "text-stone-600"
(~tw :tokens "text-stone-600")
(tr
(td :class "pr-4 py-1 font-mono text-xs" "sx_changed")
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "sx_changed")
(td
:class "py-1"
(~tw :tokens "py-1")
"List .sx files changed since a ref with structural summaries."))
(tr
(td :class "pr-4 py-1 font-mono text-xs" "sx_diff_branch")
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "sx_diff_branch")
(td
:class "py-1"
(~tw :tokens "py-1")
"Structural diff of all .sx changes on branch vs base ref."))
(tr
(td :class "pr-4 py-1 font-mono text-xs" "sx_blame")
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "sx_blame")
(td
:class "py-1"
(~tw :tokens "py-1")
"Git blame for .sx file, optionally focused on a tree path."))))
(~docs/code
:src ";; What changed on this branch?\nsx_diff_branch\n;; → adapter-dom.sx: ADDED [3] (define *memo-cache* ...)\n;; → adapter-dom.sx: ADDED [4] (define contains-deref? ...)\n;; → boot.sx: CHANGED [12,4,2,2,2,2,3,3,1,0] cek-try wrapper")
(h4
:class "font-semibold text-stone-700 mt-6 mb-2"
(~tw :tokens "font-semibold text-stone-700 mt-6 mb-2")
"Test harness (1 tool, multi-file)")
(p
"Sandboxed evaluation with mock IO. Supports loading multiple files and setup expressions.")
(table
:class "min-w-full text-sm mb-6"
(~tw :tokens "min-w-full text-sm mb-6")
(thead
(tr
(th
:class "text-left pr-4 pb-2 font-semibold text-stone-700"
(~tw :tokens "text-left pr-4 pb-2 font-semibold text-stone-700")
"Tool")
(th :class "text-left pb-2 font-semibold text-stone-700" "Purpose")))
(th (~tw :tokens "text-left pb-2 font-semibold text-stone-700") "Purpose")))
(tbody
:class "text-stone-600"
(~tw :tokens "text-stone-600")
(tr
(td :class "pr-4 py-1 font-mono text-xs" "sx_harness_eval")
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "sx_harness_eval")
(td
:class "py-1"
(~tw :tokens "py-1")
"Evaluate SX with mock IO. Load multiple files via files param. Setup expressions run before eval."))))
(~docs/code
:src ";; Test a component's IO behavior\nsx_harness_eval\n expr=\"(fetch-data \\\"users\\\" \\\"all\\\")\"\n mock=\"{:fetch (fn (url opts) {:status 200 :body \\\"[]\\\"})}\"")
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "Analysis (3 tools)")
(h4 (~tw :tokens "font-semibold text-stone-700 mt-6 mb-2") "Analysis (3 tools)")
(p "Structural diff, documentation generation, browser testing.")
(table
:class "min-w-full text-sm mb-6"
(~tw :tokens "min-w-full text-sm mb-6")
(thead
(tr
(th
:class "text-left pr-4 pb-2 font-semibold text-stone-700"
(~tw :tokens "text-left pr-4 pb-2 font-semibold text-stone-700")
"Tool")
(th :class "text-left pb-2 font-semibold text-stone-700" "Purpose")))
(th (~tw :tokens "text-left pb-2 font-semibold text-stone-700") "Purpose")))
(tbody
:class "text-stone-600"
(~tw :tokens "text-stone-600")
(tr
(td :class "pr-4 py-1 font-mono text-xs" "sx_diff")
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "sx_diff")
(td
:class "py-1"
(~tw :tokens "py-1")
"Structural diff between two .sx files (ADDED/REMOVED/CHANGED)."))
(tr
(td :class "pr-4 py-1 font-mono text-xs" "sx_doc_gen")
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "sx_doc_gen")
(td
:class "py-1"
(~tw :tokens "py-1")
"Generate component docs from signatures across a directory."))
(tr
(td :class "pr-4 py-1 font-mono text-xs" "sx_playwright")
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "sx_playwright")
(td
:class "py-1"
(~tw :tokens "py-1")
"Run Playwright browser tests for the SX docs site."))))
(~docs/code
:src ";; Run all browser tests\nsx_playwright\n;; → 23 passed, 0 failed\n\n;; Generate docs for all components\nsx_doc_gen dir=\"sx/sx/\"\n;; → TYPE NAME FILE PARAMS\n;; → defcomp ~docs/page docs.sx &key title\n;; → defisland ~home/stepper home.sx ()"))
@@ -480,7 +480,7 @@
(em "understand")
" structure before touching anything. They are read-only and have no side effects. They are not a convenience layer -- they are as important as the editing tools.")
(h4
:class "font-semibold text-stone-700 mt-6 mb-2"
(~tw :tokens "font-semibold text-stone-700 mt-6 mb-2")
"Annotated tree view")
(p
"The primary comprehension tool. Every node gets its path label inline, making tree structure explicit. The structural correspondence that is invisible in raw text is readable by inspection, with no need to count brackets:")
@@ -494,7 +494,7 @@
" [0,2,3] (h2 title)\n"
" [0,2,4] (when subtitle (p subtitle))\n"
" [0,2,5] children))"))
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "Structural summary")
(h4 (~tw :tokens "font-semibold text-stone-700 mt-6 mb-2") "Structural summary")
(p
"A folded view for large files, showing shape without detail. Claude orients itself in a 300-line island, identifies the region it needs, then calls "
(code "read-subtree")
@@ -510,7 +510,7 @@
" (do-back ...)) [0,2,2,1,13]\n"
" (freeze-scope ...) [0,2,2,2]\n"
" (let ((_eff ...)) (div ...)))) [0,2,2,5]"))
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "Context view")
(h4 (~tw :tokens "font-semibold text-stone-700 mt-6 mb-2") "Context view")
(p
"Given a deep path, shows the enclosing chain back to the root. Essential for working deep in a tree without loading the entire file:")
(~docs/code
@@ -522,7 +522,7 @@
" [0,2,2,1] bindings list (14 pairs)\n"
" → [0,2,2,1,12] (rebuild-preview (fn (target) ...))"))
(h4
:class "font-semibold text-stone-700 mt-6 mb-2"
(~tw :tokens "font-semibold text-stone-700 mt-6 mb-2")
"Bracket-paired view")
(p
"The raw source annotated with matched pair labels, for cases where Claude needs to see the actual syntax and verify bracket correspondence:")
@@ -540,7 +540,7 @@
"For large files, "
(code "sx_read_tree")
" automatically manages context size. With no extra parameters, it detects when a file exceeds 200 lines and auto-summarises at depth 2. Four modes give Claude control over how much context to pull in:")
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "Focus mode")
(h4 (~tw :tokens "font-semibold text-stone-700 mt-6 mb-2") "Focus mode")
(p
"The "
(code "focus")
@@ -548,7 +548,7 @@
(~docs/code
:src (str
";; Show only the parts of the tree that mention \"defisland\"\nsx_read_tree file=\"home.sx\" focus=\"defisland\"\n\n;; The ~docs/section containing defisland expands fully.\n;; Every other section collapses to one line."))
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "Depth limit")
(h4 (~tw :tokens "font-semibold text-stone-700 mt-6 mb-2") "Depth limit")
(p
"The "
(code "max_depth")
@@ -558,7 +558,7 @@
(~docs/code
:src (str
";; Just the top-level structure\nsx_read_tree file=\"home.sx\" max_depth=1\n\n;; Two levels deep\nsx_read_tree file=\"home.sx\" max_depth=3"))
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "Pagination")
(h4 (~tw :tokens "font-semibold text-stone-700 mt-6 mb-2") "Pagination")
(p
"The "
(code "max_lines")
@@ -568,7 +568,7 @@
(~docs/code
:src (str
";; First 30 lines\nsx_read_tree file=\"home.sx\" max_lines=30\n\n;; Next 30 lines\nsx_read_tree file=\"home.sx\" max_lines=30 offset=30\n\n;; Output header:\n;; ;; Lines 0-30 of 590 (more available)"))
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "Auto mode")
(h4 (~tw :tokens "font-semibold text-stone-700 mt-6 mb-2") "Auto mode")
(p
"With no parameters, files under 200 lines return the full annotated tree. Larger files auto-summarise at depth 2 with a note showing the total line count and how to get more detail."))
(~docs/section
@@ -577,42 +577,42 @@
(p
"All operations take a tree, perform the operation, and return either a new tree or a structured error. Nothing is mutated in place. File writing is a separate step that only happens after the edit succeeds.")
(table
:class "min-w-full text-sm mb-6"
(~tw :tokens "min-w-full text-sm mb-6")
(thead
(tr
(th
:class "text-left pr-4 pb-2 font-semibold text-stone-700"
(~tw :tokens "text-left pr-4 pb-2 font-semibold text-stone-700")
"Operation")
(th
:class "text-left pb-2 font-semibold text-stone-700"
(~tw :tokens "text-left pb-2 font-semibold text-stone-700")
"Description")))
(tbody
:class "text-stone-600"
(~tw :tokens "text-stone-600")
(tr
(td :class "pr-4 py-1 font-mono text-xs" "replace-node")
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "replace-node")
(td
:class "py-1"
(~tw :tokens "py-1")
"Replace the node at a path with new parsed source"))
(tr
(td :class "pr-4 py-1 font-mono text-xs" "insert-child")
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "insert-child")
(td
:class "py-1"
(~tw :tokens "py-1")
"Insert a new child at a specific index within a list"))
(tr
(td :class "pr-4 py-1 font-mono text-xs" "delete-node")
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "delete-node")
(td
:class "py-1"
(~tw :tokens "py-1")
"Remove a node -- siblings shift to fill the gap"))
(tr
(td :class "pr-4 py-1 font-mono text-xs" "wrap-node")
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "wrap-node")
(td
:class "py-1"
(~tw :tokens "py-1")
"Wrap a node in a new list -- e.g. wrap expression in "
(code "(when cond ...)")))
(tr
(td :class "pr-4 py-1 font-mono text-xs" "validate")
(td (~tw :tokens "pr-4 py-1 font-mono text-xs") "validate")
(td
:class "py-1"
(~tw :tokens "py-1")
"Check structural integrity -- balanced parens, valid paths"))))
(p
(strong "Fragment-first validation.")
@@ -629,14 +629,14 @@
:id "smart-editing"
(p
"The path-based edit tools require knowing the exact path to a node. The smart edit tools combine search with editing -- find a node by pattern, then edit it in one call.")
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "Rename symbol")
(h4 (~tw :tokens "font-semibold text-stone-700 mt-6 mb-2") "Rename symbol")
(p
(code "sx_rename_symbol")
" renames every occurrence of a symbol throughout a file. Structural -- only renames symbol nodes, never touches strings or keywords.")
(~docs/code
:src (str
";; Rename a component\nsx_rename_symbol file=\"home.sx\" old_name=\"~card\" new_name=\"~ui/card\"\n;; → Renamed 3 occurrences of '~card' → '~ui/card' in home.sx"))
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "Replace by pattern")
(h4 (~tw :tokens "font-semibold text-stone-700 mt-6 mb-2") "Replace by pattern")
(p
(code "sx_replace_by_pattern")
" finds the first node matching a pattern and replaces it with new source. Set "
@@ -646,7 +646,7 @@
:src (str
";; Replace first match\nsx_replace_by_pattern file=\"home.sx\" pattern=\"~old-card\" new_source=\"(~new-card :title t)\"\n\n;; Replace all matches\nsx_replace_by_pattern file=\"home.sx\" pattern=\"~old-card\" new_source=\"(~new-card :title t)\" all=true"))
(h4
:class "font-semibold text-stone-700 mt-6 mb-2"
(~tw :tokens "font-semibold text-stone-700 mt-6 mb-2")
"Insert near pattern")
(p
(code "sx_insert_near")
@@ -655,7 +655,7 @@
:src (str
";; Insert a new component before the footer definition\nsx_insert_near file=\"page.sx\" pattern=\"~footer\" position=\"before\"\n new_source=\"(defcomp ~sidebar () (div :class sidebar))\""))
(h4
:class "font-semibold text-stone-700 mt-6 mb-2"
(~tw :tokens "font-semibold text-stone-700 mt-6 mb-2")
"Rename across files")
(p
(code "sx_rename_across")
@@ -689,7 +689,7 @@
"These tools search across all "
(code ".sx")
" files in a directory tree. They answer the questions Claude needs most often: where is something defined, what definitions exist, and who uses a given component.")
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "Find across files")
(h4 (~tw :tokens "font-semibold text-stone-700 mt-6 mb-2") "Find across files")
(p
(code "sx_find_across")
" runs "
@@ -700,7 +700,7 @@
(~docs/code
:src (str
";; Find all islands in the project\nsx_find_across dir=\"/root/rose-ash/sx\" pattern=\"defisland\"\n\n;; Output:\n;; sx/home.sx [0] (defisland ~home/stepper ...)\n;; sx/sx-tools-editor.sx [0] (defisland ~sx-tools/tree-editor ...)"))
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "Component list")
(h4 (~tw :tokens "font-semibold text-stone-700 mt-6 mb-2") "Component list")
(p
(code "sx_comp_list")
" scans a directory for all top-level definitions: "
@@ -717,7 +717,7 @@
(~docs/code
:src (str
";; List everything defined in sx/\nsx_comp_list dir=\"/root/rose-ash/sx\"\n\n;; Output:\n;; TYPE NAME FILE PARAMS\n;; defcomp ~docs/page sx/docs.sx &key title ..."))
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "Component usage")
(h4 (~tw :tokens "font-semibold text-stone-700 mt-6 mb-2") "Component usage")
(p
(code "sx_comp_usage")
" finds every reference to a component or symbol across all "
@@ -754,19 +754,19 @@
:id "dev-tools"
(p
"Tools for the SX development workflow: building, testing, formatting, linting, and macro expansion.")
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "Pretty print")
(h4 (~tw :tokens "font-semibold text-stone-700 mt-6 mb-2") "Pretty print")
(p
(code "sx_pretty_print")
" reformats an "
(code ".sx")
" file with indentation. Short forms stay on one line, longer forms break across lines with keyword args kept paired. All edit tools use the pretty printer automatically when writing files.")
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "Write file")
(h4 (~tw :tokens "font-semibold text-stone-700 mt-6 mb-2") "Write file")
(p
(code "sx_write_file")
" creates or overwrites an "
(code ".sx")
" file. Source is parsed before writing -- malformed SX is rejected and the file is not touched. Output is pretty-printed.")
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "Build")
(h4 (~tw :tokens "font-semibold text-stone-700 mt-6 mb-2") "Build")
(p
(code "sx_build")
" builds the SX runtime. Target "
@@ -780,7 +780,7 @@
". Set "
(code "full=true")
" for extensions and type system.")
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "Test")
(h4 (~tw :tokens "font-semibold text-stone-700 mt-6 mb-2") "Test")
(p
(code "sx_test")
" runs the SX test suite and returns a summary with pass/fail counts. Any failures are listed with details. Host is "
@@ -790,7 +790,7 @@
". Set "
(code "full=true")
" for the extended suite.")
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "Format check")
(h4 (~tw :tokens "font-semibold text-stone-700 mt-6 mb-2") "Format check")
(p
(code "sx_format_check")
" lints an "
@@ -804,7 +804,7 @@
" with too few args, "
(code "define")
" with no body, duplicate parameters.")
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "Macro expand")
(h4 (~tw :tokens "font-semibold text-stone-700 mt-6 mb-2") "Macro expand")
(p
(code "sx_macroexpand")
" evaluates an expression with a file's definitions loaded. Use to test macros -- the file's "
@@ -817,7 +817,7 @@
"Tools that combine git awareness with structural understanding of "
(code ".sx")
" files.")
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "Changed files")
(h4 (~tw :tokens "font-semibold text-stone-700 mt-6 mb-2") "Changed files")
(p
(code "sx_changed")
" lists all "
@@ -825,7 +825,7 @@
" files changed since a git ref (default: "
(code "main")
"), with a depth-1 structural summary of each. Use to understand the scope of changes on a branch.")
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "Branch diff")
(h4 (~tw :tokens "font-semibold text-stone-700 mt-6 mb-2") "Branch diff")
(p
(code "sx_diff_branch")
" runs "
@@ -833,14 +833,14 @@
" on every changed "
(code ".sx")
" file, comparing the current version against the base ref. Shows structural ADDED/REMOVED/CHANGED per file -- like a PR review that understands trees.")
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "Blame")
(h4 (~tw :tokens "font-semibold text-stone-700 mt-6 mb-2") "Blame")
(p
(code "sx_blame")
" shows git blame for an "
(code ".sx")
" file. Optionally takes a tree path to focus on a specific node.")
(h4
:class "font-semibold text-stone-700 mt-6 mb-2"
(~tw :tokens "font-semibold text-stone-700 mt-6 mb-2")
"Documentation generator")
(p
(code "sx_doc_gen")
@@ -851,7 +851,7 @@
", and "
(code "defmacro")
" signatures -- names, types, keyword parameters, and whether the component accepts children.")
(h4 :class "font-semibold text-stone-700 mt-6 mb-2" "Playwright tests")
(h4 (~tw :tokens "font-semibold text-stone-700 mt-6 mb-2") "Playwright tests")
(p
(code "sx_playwright")
" runs the Playwright browser test suite for the SX docs site. Optionally specify a single spec file. Returns pass/fail summary with failure details."))
@@ -889,7 +889,7 @@
(code "angstrom")
" parser combinators and a Wadler-Lindig pretty-printer. This is unnecessary. The SX ecosystem already has everything:")
(ul
:class "space-y-2 text-stone-600"
(~tw :tokens "space-y-2 text-stone-600")
(li
(strong "Parser: ")
(code "sx-parse")
@@ -921,7 +921,7 @@
:title "Build plan"
:id "build-plan"
(h4
:class "font-semibold text-stone-700 mt-6 mb-2"
(~tw :tokens "font-semibold text-stone-700 mt-6 mb-2")
"Phase 1 -- Tree comprehension functions")
(p
"Implement "
@@ -942,7 +942,7 @@
(code ".sx")
" files. Iterate on output formats until the output is genuinely easy for a language model to read -- the format is load-bearing.")
(h4
:class "font-semibold text-stone-700 mt-6 mb-2"
(~tw :tokens "font-semibold text-stone-700 mt-6 mb-2")
"Phase 2 -- Edit operations")
(p
"Implement "
@@ -957,18 +957,18 @@
(code "validate")
" as pure SX functions. Fragment-first validation on all write operations. Test error paths exhaustively -- error messages are part of the interface.")
(h4
:class "font-semibold text-stone-700 mt-6 mb-2"
(~tw :tokens "font-semibold text-stone-700 mt-6 mb-2")
"Phase 3 -- MCP server")
(p
"Thin OCaml binary: stdio JSON-RPC, calls SX functions via the bridge, reads/writes files. Wire all comprehension and edit tools to MCP handlers. Manual testing with raw JSON.")
(h4
:class "font-semibold text-stone-700 mt-6 mb-2"
(~tw :tokens "font-semibold text-stone-700 mt-6 mb-2")
"Phase 4 -- Web editor")
(p
(code "defisland ~sx-tools/tree-editor")
" -- interactive tree visualization on this page. Click a node to see its path, context, and siblings. Edit nodes through a structural interface. Islands and signals for reactivity. A tool and a demonstration.")
(h4
:class "font-semibold text-stone-700 mt-6 mb-2"
(~tw :tokens "font-semibold text-stone-700 mt-6 mb-2")
"Phase 5 -- Integration and iteration")
(p
"Write the "
@@ -986,7 +986,7 @@
(p
"With SX Tools, the debugging session that found the home-stepper bug would not have happened. The workflow would have been:")
(ul
:class "space-y-2 text-stone-600"
(~tw :tokens "space-y-2 text-stone-600")
(li
(code "summarise home-stepper.sx")
" → see that "