@@ -319,12 +319,22 @@ std::expected<std::string, std::string> run_capture(const std::string& cmd) {
319319std::string shq (std::string_view s) {
320320 std::string out;
321321 out.reserve (s.size () + 2 );
322+ #if defined(_WIN32)
323+ // cmd.exe uses double quotes; escape inner double quotes with backslash
324+ out.push_back (' "' );
325+ for (char c : s) {
326+ if (c == ' "' ) out += " \\\" " ;
327+ else out.push_back (c);
328+ }
329+ out.push_back (' "' );
330+ #else
322331 out.push_back (' \' ' );
323332 for (char c : s) {
324333 if (c == ' \' ' ) out += " '\\ ''" ;
325334 else out.push_back (c);
326335 }
327336 out.push_back (' \' ' );
337+ #endif
328338 return out;
329339}
330340
@@ -413,6 +423,24 @@ std::filesystem::path sandbox_init_marker(const Env& env) {
413423
414424std::string build_command_prefix (const Env& env) {
415425 auto xvmBin = paths::sandbox_bin (env).string ();
426+ #if defined(_WIN32)
427+ // Windows: use cmd.exe set + call semantics.
428+ if (env.projectDir .empty ()) {
429+ return std::format (
430+ " cd /d {} && set \" PATH={};%PATH%\" && set \" XLINGS_HOME={}\" && set \" XLINGS_PROJECT_DIR=\" && {}" ,
431+ shq (env.home .string ()),
432+ xvmBin,
433+ env.home .string (),
434+ shq (env.binary .string ()));
435+ }
436+ return std::format (
437+ " cd /d {} && set \" PATH={};%PATH%\" && set \" XLINGS_HOME={}\" && set \" XLINGS_PROJECT_DIR={}\" && {}" ,
438+ shq (env.home .string ()),
439+ xvmBin,
440+ env.home .string (),
441+ env.projectDir .string (),
442+ shq (env.binary .string ()));
443+ #else
416444 if (env.projectDir .empty ()) {
417445 // Global mode: unset XLINGS_PROJECT_DIR (existing behavior).
418446 return std::format (
@@ -431,13 +459,19 @@ std::string build_command_prefix(const Env& env) {
431459 shq (env.home .string ()),
432460 shq (env.projectDir .string ()),
433461 shq (env.binary .string ()));
462+ #endif
434463}
435464
436465std::string build_interface_command (const Env& env,
437466 std::string_view capability,
438467 std::string_view argsJson) {
468+ #if defined(_WIN32)
469+ return std::format (" {} interface {} --args {} 2>nul" ,
470+ build_command_prefix (env), capability, shq (argsJson));
471+ #else
439472 return std::format (" {} interface {} --args {} 2>/dev/null" ,
440473 build_command_prefix (env), capability, shq (argsJson));
474+ #endif
441475}
442476
443477// ─── JSON extraction helpers ────────────────────────────────────────
@@ -628,12 +662,21 @@ int install_with_progress(const Env& env, std::string_view target,
628662 auto argsJson = std::format (
629663 R"( {{"targets":["{}"],"yes":true}})" , target);
630664
665+ #if defined(_WIN32)
666+ auto cmd = std::format (
667+ " cd /d {} && set \" XLINGS_PROJECT_DIR=\" && set \" XLINGS_HOME={}\" && {} interface install_packages --args {} 2>nul" ,
668+ shq (env.home .string ()),
669+ env.home .string (),
670+ shq (env.binary .string ()),
671+ shq (argsJson));
672+ #else
631673 auto cmd = std::format (
632674 " cd {} && env -u XLINGS_PROJECT_DIR XLINGS_HOME={} {} interface install_packages --args {} 2>/dev/null" ,
633675 shq (env.home .string ()),
634676 shq (env.home .string ()),
635677 shq (env.binary .string ()),
636678 shq (argsJson));
679+ #endif
637680
638681 std::FILE* fp = ::popen (cmd.c_str (), " r" );
639682 if (!fp) return -1 ;
@@ -746,11 +789,19 @@ void ensure_init(const Env& env, bool quiet) {
746789
747790 if (!quiet)
748791 print_status (" Initialize" , " mcpp sandbox layout (one-time)" );
792+ #if defined(_WIN32)
793+ auto cmd = std::format (
794+ " cd /d {} && set \" XLINGS_PROJECT_DIR=\" && set \" XLINGS_HOME={}\" && {} self init >nul 2>&1" ,
795+ shq (env.home .string ()),
796+ env.home .string (),
797+ shq (env.binary .string ()));
798+ #else
749799 auto cmd = std::format (
750800 " cd {} && env -u XLINGS_PROJECT_DIR XLINGS_HOME={} {} self init >/dev/null 2>&1" ,
751801 shq (env.home .string ()),
752802 shq (env.home .string ()),
753803 shq (env.binary .string ()));
804+ #endif
754805 int rc = std::system (cmd.c_str ());
755806 if (rc != 0 && !quiet) {
756807 std::println (stderr,
0 commit comments