feat: add --update-bundle with provenance-aware conflict handling

Updates re-clone a bundle's recorded source (honoring a recorded commit
pin unless a #<ref> override moves it), silently refresh files the bundle
owns that the user never modified, and fall back to the normal conflict
prompts for modified or unowned files. Files the remote no longer ships
are offered for deletion (kept by default non-interactively, staying
owned). The record is refreshed with the new commit, version, and
metadata, and stamped with an updated_at timestamp on success.
This commit is contained in:
Dark-Alex-17 committed 2026-08-24 11:15:12 -06:00
1 parent 2790a823b0
commit 0a806da8d2
5 files changed
+762 -12

No files matched your search

+12 -1
View File
@@ -53,7 +53,7 @@ pub enum McpScopeArg {
"install_from", "sync_models", "list_models", "list_roles",
"list_sessions", "list_agents", "list_rags", "list_macros",
"list_skills", "list_bundles", "skill", "tail_logs", "completions",
"update",
"update", "update_bundle",
])
),
group(
@@ -207,6 +207,9 @@ pub struct Cli {
help_heading = "Installation & Updates"
)]
pub install_force: bool,
/// Update an installed bundle from its recorded source (NAME may be suffixed with #<ref> to move a pin)
#[arg(long, value_name = "NAME", help_heading = "Installation & Updates")]
pub update_bundle: Option<String>,
/// Sync models updates
#[arg(long, help_heading = "Installation & Updates")]
pub sync_models: bool,
@@ -508,6 +511,14 @@ mod tests {
assert!(parse(&[]).skill.is_empty());
}
#[test]
fn parse_update_bundle_flag_takes_name() {
assert_eq!(
parse(&["--update-bundle", "foo"]).update_bundle.as_deref(),
Some("foo")
);
}
#[test]
fn parse_multiple_skill_flags_preserves_order() {
assert_eq!(