Monorepo management for opam overlays
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

doctor: run sync before analysis and provide status in prompt

- Add --no-sync flag to skip the pre-analysis sync
- Run monopam sync before doctor analysis by default
- Include formatted monopam status output in Claude prompt
- Tell Claude it doesn't need to run status/sync itself

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

+79 -19
+27 -9
bin/main.ml
··· 730 730 them by type, priority, and risk level."; 731 731 `S "WHAT IT DOES"; 732 732 `P "The doctor command:"; 733 - `I ("1.", "Checks local sync status (monorepo vs checkouts)"); 734 - `I ("2.", "Checks remote sync status (checkouts vs upstream)"); 735 - `I ("3.", "Analyzes fork relationships with verse members"); 736 - `I ("4.", "Uses Claude to categorize and prioritize their commits"); 737 - `I ("5.", "Generates actionable recommendations"); 733 + `I ("1.", "Syncs the workspace (unless $(b,--no-sync) is specified)"); 734 + `I ("2.", "Checks local sync status (monorepo vs checkouts)"); 735 + `I ("3.", "Checks remote sync status (checkouts vs upstream)"); 736 + `I ("4.", "Analyzes fork relationships with verse members"); 737 + `I ("5.", "Uses Claude to categorize and prioritize their commits"); 738 + `I ("6.", "Generates actionable recommendations"); 739 + `P "The status output from $(b,monopam status) is automatically included \ 740 + in the prompt sent to Claude, so Claude doesn't need to run it separately."; 738 741 `S "OUTPUT FORMATS"; 739 742 `P "By default, outputs human-readable text with colors."; 740 743 `P "Use $(b,--json) for JSON output suitable for tooling."; 741 744 `S Manpage.s_examples; 742 - `P "Run full analysis:"; 745 + `P "Run full analysis (syncs first):"; 743 746 `Pre "monopam doctor"; 747 + `P "Run analysis without syncing first:"; 748 + `Pre "monopam doctor --no-sync"; 744 749 `P "Analyze a specific repo:"; 745 750 `Pre "monopam doctor eio"; 746 751 `P "Output as JSON:"; ··· 752 757 let doc = "Output as JSON instead of formatted text." in 753 758 Arg.(value & flag & info [ "json" ] ~doc) 754 759 in 755 - let run package json () = 760 + let no_sync_arg = 761 + let doc = "Skip running sync before analysis." in 762 + Arg.(value & flag & info [ "no-sync" ] ~doc) 763 + in 764 + let run package json no_sync () = 756 765 Eio_main.run @@ fun env -> 757 766 with_config env @@ fun config -> 758 767 with_verse_config env @@ fun verse_config -> 759 768 let fs = Eio.Stdenv.fs env in 760 769 let proc = Eio.Stdenv.process_mgr env in 761 770 let clock = Eio.Stdenv.clock env in 762 - let report = Monopam.Doctor.analyze ~proc ~fs ~config ~verse_config ~clock ?package () in 771 + (* Run sync before analysis unless --no-sync is specified *) 772 + if not no_sync then begin 773 + Fmt.pr "Syncing workspace before analysis...@."; 774 + match Monopam.sync ~proc ~fs ~config ?package () with 775 + | Ok _summary -> () 776 + | Error e -> 777 + Fmt.pr "Warning: sync failed: %a@." Monopam.pp_error_with_hint e; 778 + Fmt.pr "Continuing with analysis...@." 779 + end; 780 + let report = Monopam.Doctor.analyze ~proc ~fs ~config ~verse_config ~clock ?package ~no_sync () in 763 781 if json then 764 782 print_endline (Monopam.Doctor.to_json report) 765 783 else 766 784 Fmt.pr "%a@." Monopam.Doctor.pp_report report; 767 785 `Ok () 768 786 in 769 - Cmd.v info Term.(ret (const run $ package_arg $ json_arg $ logging_term)) 787 + Cmd.v info Term.(ret (const run $ package_arg $ json_arg $ no_sync_arg $ logging_term)) 770 788 771 789 (* Main command group *) 772 790
+35 -2
lib/doctor.ml
··· 380 380 analyze_remote ~proc ~fs ~checkout_dir ~remote_name) 381 381 remotes 382 382 383 - (** Build status summary for prompt *) 383 + (** Strip ANSI escape codes from a string *) 384 + let strip_ansi s = 385 + let buf = Buffer.create (String.length s) in 386 + let rec loop i = 387 + if i >= String.length s then Buffer.contents buf 388 + else if s.[i] = '\027' && i + 1 < String.length s && s.[i + 1] = '[' then 389 + (* Skip escape sequence until 'm' *) 390 + let rec skip j = 391 + if j >= String.length s then j 392 + else if s.[j] = 'm' then j + 1 393 + else skip (j + 1) 394 + in 395 + loop (skip (i + 2)) 396 + else begin 397 + Buffer.add_char buf s.[i]; 398 + loop (i + 1) 399 + end 400 + in 401 + loop 0 402 + 403 + (** Build status summary for prompt - includes formatted monopam status output *) 384 404 let build_status_summary statuses = 385 405 let buf = Buffer.create 4096 in 386 406 Buffer.add_string buf "## Current Monorepo Status\n\n"; 407 + Buffer.add_string buf "Output of `monopam status`:\n```\n"; 408 + (* Capture formatted pp_summary output (strip ANSI codes for prompt) *) 409 + let fmt_output = Fmt.str "%a" Status.pp_summary statuses in 410 + Buffer.add_string buf (strip_ansi fmt_output); 411 + Buffer.add_string buf "```\n\n"; 412 + Buffer.add_string buf "Detailed status per repository:\n"; 387 413 List.iter (fun (status : Status.t) -> 388 414 let name = Package.repo_name status.package in 389 415 let local_str = match status.subtree_sync with ··· 439 465 let prompt = Buffer.create 16384 in 440 466 Buffer.add_string prompt {|You are analyzing a monorepo workspace to provide actionable recommendations. 441 467 468 + IMPORTANT: The workspace has already been synced and the status output is provided below. 469 + You do NOT need to run `monopam status` or `monopam sync` - this has already been done. 470 + Use the status information provided to inform your analysis. 471 + 442 472 |}; 443 473 Buffer.add_string prompt status_summary; 444 474 Buffer.add_string prompt incoming_summary; ··· 448 478 449 479 Analyze the workspace state and incoming commits. For each repository with incoming commits, 450 480 categorize what the changes represent and provide recommendations. 481 + 482 + The status output above shows the current state after syncing. Use this information directly. 451 483 452 484 Respond with JSON containing: 453 485 - repos: array of repo analyses, each with: ··· 684 716 (** Run the doctor analysis *) 685 717 let analyze 686 718 ~proc ~fs ~config ~verse_config ~clock 687 - ?package () = 719 + ?package ?(no_sync=false) () = 720 + let _ = no_sync in (* Sync is run at CLI level before calling analyze *) 688 721 let now = Eio.Time.now clock in 689 722 let now_ptime = match Ptime.of_float_s now with 690 723 | Some t -> t
+17 -8
lib/doctor.mli
··· 157 157 verse_config:Verse_config.t -> 158 158 clock:float Eio.Time.clock_ty Eio.Resource.t -> 159 159 ?package:string -> 160 + ?no_sync:bool -> 160 161 unit -> 161 162 report 162 - (** [analyze ~proc ~fs ~config ~verse_config ~clock ?package ()] runs the 163 - doctor analysis and returns a report. 163 + (** [analyze ~proc ~fs ~config ~verse_config ~clock ?package ?no_sync ()] runs 164 + the doctor analysis and returns a report. 165 + 166 + By default, runs [monopam sync] first to ensure the workspace is up-to-date 167 + before analysis. Use [~no_sync:true] to skip the initial sync. 164 168 165 169 Performs the following analysis: 166 - 1. Computes status for all packages (or the specified package) 167 - 2. Checks for dirty state in opam-repo and monorepo 168 - 3. Analyzes fork relationships with verse members 169 - 4. Uses Claude AI to categorize and prioritize verse commits 170 - 5. Generates actionable recommendations 170 + 1. Runs sync to update workspace (unless [~no_sync:true]) 171 + 2. Computes status for all packages (or the specified package) 172 + 3. Checks for dirty state in opam-repo and monorepo 173 + 4. Analyzes fork relationships with verse members 174 + 5. Uses Claude AI to categorize and prioritize verse commits 175 + 6. Generates actionable recommendations 176 + 177 + The status output from [monopam status] is provided directly to Claude 178 + in the prompt, so Claude doesn't need to run it separately. 171 179 172 180 @param proc Eio process manager 173 181 @param fs Eio filesystem 174 182 @param config Monopam configuration 175 183 @param verse_config Verse/opamverse configuration 176 184 @param clock Eio clock for time operations 177 - @param package Optional specific package to analyze *) 185 + @param package Optional specific package to analyze 186 + @param no_sync If true, skip the initial sync (default: false) *)