diff --git a/hosts/ocaml/bin/mcp_tree.ml b/hosts/ocaml/bin/mcp_tree.ml index bb8be8f4..cf6bbcff 100644 --- a/hosts/ocaml/bin/mcp_tree.ml +++ b/hosts/ocaml/bin/mcp_tree.ml @@ -719,14 +719,15 @@ let rec handle_tool name args = let project_dir = try Sys.getenv "SX_PROJECT_DIR" with Not_found -> try Sys.getenv "SX_ROOT" with Not_found -> Sys.getcwd () in let sx_dir = project_dir ^ "/sx/sx" in - (* Extract all nav items from nav-data.sx by parsing and walking the AST *) + (* Extract all nav items from nav-data.sx AND nav-tree.sx *) let scan_nav () = - let src = try In_channel.with_open_text (sx_dir ^ "/nav-data.sx") In_channel.input_all with _ -> "" in let items = ref [] in + let seen = Hashtbl.create 64 in let rec walk = function | Dict d -> (match Hashtbl.find_opt d "href", Hashtbl.find_opt d "label" with - | Some (String href), Some (String label) -> + | Some (String href), Some (String label) when not (Hashtbl.mem seen href) -> + Hashtbl.replace seen href (); let summary = match Hashtbl.find_opt d "summary" with Some (String s) -> s | _ -> "" in items := (href, label, summary) :: !items | _ -> ()); @@ -734,7 +735,16 @@ let rec handle_tool name args = | List l | ListRef { contents = l } -> List.iter walk l | _ -> () in - List.iter walk (try Sx_parser.parse_all src with _ -> []); + (* Scan both files — nav-data has the groups, nav-tree has the sidebar structure *) + List.iter (fun file -> + let src = try In_channel.with_open_text (sx_dir ^ "/" ^ file) In_channel.input_all with _ -> "" in + (* Evaluate defines so (dict :key val) calls produce Dict values *) + let exprs = try Sx_parser.parse_all src with _ -> [] in + List.iter (fun expr -> + try walk (Sx_ref.eval_expr expr (Env !env)) + with _ -> walk expr (* fallback: walk unevaluated AST *) + ) exprs + ) ["nav-data.sx"; "nav-tree.sx"]; List.rev !items in let href_section href =