@@ -36,6 +36,8 @@ inline constexpr std::string_view kXlingsPinnedVersion = mcpp::xlings::pinned::k
3636struct IndexRepo {
3737 std::string name;
3838 std::string url;
39+ std::string artifact; // optional artifact source base (xlings >= 0.4.68, #269)
40+ std::string source; // optional "auto" | "artifact" | "git" ("" = xlings default auto)
3941};
4042
4143// Canonical mcpplibs index coordinates. The index repository moved from the
@@ -375,7 +377,9 @@ home = ""
375377default = "mcpplibs"
376378
377379[index.repos."mcpplibs"]
378- url = "https://github.com/mcpplibs/mcpp-index.git"
380+ url = "https://github.com/mcpplibs/mcpp-index.git"
381+ artifact = "https://github.com/xlings-res/mcpp-index"
382+ # source = "auto" # default: artifact first, git fallback; set "git" to force git
379383# xlings auto-adds xim / awesome / scode / d2x as defaults.
380384
381385[cache]
@@ -393,10 +397,11 @@ bool write_default_xlings_json(const std::filesystem::path& path,
393397 const std::vector<IndexRepo>& repos,
394398 std::string_view mirror_override = {})
395399{
396- // Delegate to xlings module. Convert IndexRepo vec to pair span.
397- std::vector<std::pair<std::string,std::string> > pairs;
400+ // Delegate to xlings module. Convert IndexRepo vec to SeedRepo span.
401+ std::vector<mcpp::xlings::SeedRepo > pairs;
398402 pairs.reserve (repos.size ());
399- for (auto & r : repos) pairs.emplace_back (r.name , r.url );
403+ for (auto & r : repos)
404+ pairs.push_back ({ r.name , r.url , r.artifact , r.source });
400405 // seed_xlings_json writes to env.home / ".xlings.json", so we
401406 // construct a temporary Env with home = path.parent_path().
402407 mcpp::xlings::Env env;
@@ -469,6 +474,12 @@ void canonicalize_legacy_index_names(GlobalConfig& cfg) {
469474 // repo that happens to be named "mcpp-index" untouched.
470475 if (r.name == " mcpp-index" && r.url == kMcpplibsIndexUrl )
471476 r.name = " mcpplibs" ;
477+ // ③ In-memory artifact default for the official index (#269): heals
478+ // configs written by pre-artifact templates on every load without
479+ // text surgery on user-editable config.toml. `source = "git"` is the
480+ // explicit opt-out; a user-set artifact base always wins.
481+ if (r.url == kMcpplibsIndexUrl && r.artifact .empty () && r.source != " git" )
482+ r.artifact = std::string (kMcpplibsIndexArtifact );
472483 bool duplicate = std::any_of (normalized.begin (), normalized.end (),
473484 [&](const IndexRepo& existing) {
474485 return existing.name == r.name && existing.url == r.url ;
@@ -562,7 +573,12 @@ std::expected<GlobalConfig, ConfigError> load_or_init(
562573 auto & tt = val.as_table ();
563574 auto it = tt.find (" url" );
564575 if (it == tt.end () || !it->second .is_string ()) continue ;
565- cfg.indexRepos .push_back ({ name, it->second .as_string () });
576+ IndexRepo r{ name, it->second .as_string () };
577+ if (auto a = tt.find (" artifact" ); a != tt.end () && a->second .is_string ())
578+ r.artifact = a->second .as_string ();
579+ if (auto s = tt.find (" source" ); s != tt.end () && s->second .is_string ())
580+ r.source = s->second .as_string ();
581+ cfg.indexRepos .push_back (std::move (r));
566582 }
567583 }
568584 // [indices] — new-schema custom index repositories.
@@ -592,11 +608,13 @@ std::expected<GlobalConfig, ConfigError> load_or_init(
592608 // them ourselves can cause cross-index name conflicts during
593609 // dependency resolution (e.g. linux-headers existing in both
594610 // scode and xim). See docs/21 §VII.
595- auto add_default = [&](std::string_view name, std::string_view url) {
611+ auto add_default = [&](std::string_view name, std::string_view url,
612+ std::string_view artifact = {}) {
596613 for (auto & r : cfg.indexRepos ) if (r.name == name) return ;
597- cfg.indexRepos .push_back ({ std::string (name), std::string (url) });
614+ cfg.indexRepos .push_back ({ std::string (name), std::string (url),
615+ std::string (artifact), std::string () });
598616 };
599- add_default (" mcpplibs" , kMcpplibsIndexUrl );
617+ add_default (" mcpplibs" , kMcpplibsIndexUrl , kMcpplibsIndexArtifact );
600618 canonicalize_legacy_index_names (cfg);
601619
602620 // 5. Seed registry/.xlings.json if missing; migrate legacy cached
@@ -687,8 +705,10 @@ void print_env(const GlobalConfig& cfg) {
687705 std::println (" Index repos:" );
688706 for (auto & r : cfg.indexRepos ) {
689707 bool isDefault = (r.name == cfg.defaultIndex );
690- std::println (" {} {}{}" ,
691- r.name , r.url , isDefault ? " (default)" : " " );
708+ std::println (" {} {}{}{}" ,
709+ r.name , r.url ,
710+ r.artifact .empty () ? " " : " [artifact]" ,
711+ isDefault ? " (default)" : " " );
692712 }
693713}
694714
@@ -701,17 +721,17 @@ bool ensure_project_index_dir(
701721 // Collect custom non-builtin indices that need xlings project-scope data.
702722 // Local path indices are also seeded so xlings can create its own
703723 // project-local repo link and install packages from that index.
704- std::vector<std::pair<std::string,std::string> > customRepos;
724+ std::vector<mcpp::xlings::SeedRepo > customRepos;
705725 for (auto & [name, spec] : indices) {
706726 if (spec.is_builtin ()) continue ;
707727 if (spec.is_local ()) {
708728 auto source = resolve_project_index_path (projectDir, spec);
709729 std::error_code ec;
710730 std::filesystem::remove (source / " .xlings-index-cache.json" , ec);
711- customRepos.emplace_back ( name, source.generic_string ());
731+ customRepos.push_back ({ name, source.generic_string (), " " , " " } );
712732 continue ;
713733 }
714- customRepos.emplace_back ( name, spec.url );
734+ customRepos.push_back ({ name, spec.url , " " , " " } );
715735 }
716736
717737 // NOTE: the official global `xim` index is deliberately NOT injected into
0 commit comments