fix: address code review findings on the bundle lifecycle

The user-origin marker on replaced mcp.json entries is now sticky:
re-records and cross-bundle transfers only upgrade replaced to
transferred when the prior record proves bundle origin, so updating a
bundle can no longer make uninstall delete a key the user had before the
bundle replaced it. Canonical source URLs lowercase only the host, since
self-hosted forges treat repository paths as case-sensitive and
collapsing distinct repos misdirects updates and uninstalls. git clone
invocations pass '--' before the URL so a crafted source cannot be
parsed as a git flag. Lifecycle flags (--install, --install-builtins,
--update-bundle, --uninstall) and their companions now conflict
explicitly instead of first-match dispatch silently dropping actions.
--install-from returns as a hidden tombstone that errors with the
replacement instead of feeding the flag to the LLM as prompt text.
--list-bundles dispatches before config load so a pure read no longer
boots MCP servers. write_file_atomic fsyncs before the rename so a crash
cannot persist a truncated store. REPL: .uninstall accepts --yes,
.install rejects trailing tokens after a category, and .install remote
gets a migration hint. Plus polish: host validation rejects '#' and '?',
renamed_to no longer serializes null, derived names get a debug assert
against the validator, completions share DEFAULT_GIT_HOST, README
mentions skills.
This commit is contained in:
2026-08-24 11:15:13 -06:00
parent 4324d551d6
commit 80b082423c
8 changed files with 239 additions and 44 deletions
+10 -1
View File
@@ -165,7 +165,16 @@ pub(crate) fn write_file_atomic(
std::process::id(),
TMP_COUNTER.fetch_add(1, Ordering::Relaxed)
));
fs::write(&tmp, content)?;
let write_synced = || -> std::io::Result<()> {
use std::io::Write;
let mut file = fs::File::create(&tmp)?;
file.write_all(content.as_bytes())?;
file.sync_all()
};
if let Err(err) = write_synced() {
let _ = fs::remove_file(&tmp);
return Err(err.into());
}
#[cfg(unix)]
if let Some(mode) = mode {