* fix(upgrade): refresh installer-written agent surfaces after a binary upgrade codegraph upgrade swapped the binary but never revisited what earlier installs wrote into CLAUDE.md / AGENTS.md / GEMINI.md and the agent configs, so sections written by a pre-1.0 installer kept teaching agents a multi-tool surface (including tools that no longer exist) months of releases later. The install path already self-heals everything it owns, but nothing ever called it on upgrade. - codegraph install --refresh: non-interactive sweep that re-runs install() for already-configured targets only — never a first install; permissions and prompt-hook choices are preserved. - codegraph upgrade spawns it via the freshly-installed binary after a successful swap (the still-running old process would only rewrite its own stale template). Gated on PATH resolution and the CODEGRAPH_NO_INSTALL_REFRESH=1 kill-switch; never fatal to the upgrade. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(installer): clarify refresh change reporting --------- Co-authored-by: xuing <np2v9bvbbs@privaterelay.appleid.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Colby McHenry <me@colbymchenry.com>
This commit is contained in:
co-authored by
Claude Fable 5
xuing
Colby McHenry
parent
63eb488ed4
commit
386bff0f84
@@ -359,6 +359,66 @@ export function uninstallTargets(
|
||||
});
|
||||
}
|
||||
|
||||
export type RefreshStatus = 'refreshed' | 'unchanged' | 'not-configured' | 'unsupported';
|
||||
|
||||
/**
|
||||
* Per-target outcome of a refresh sweep. `refreshed` means at least one
|
||||
* filesystem entry was created, updated, or removed; `unchanged` means the target was
|
||||
* already current (every write reported byte-identical); the other two
|
||||
* mirror `UninstallStatus`.
|
||||
*/
|
||||
export interface RefreshReport {
|
||||
id: TargetId;
|
||||
displayName: string;
|
||||
location: Location;
|
||||
status: RefreshStatus;
|
||||
/** Absolute paths created, updated, or removed by the refresh. */
|
||||
changedPaths: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Pure refresh sweep — re-runs `install()` for every target that is
|
||||
* ALREADY configured at `location`, so the surfaces a previous version
|
||||
* wrote (the marker-fenced instructions section, the MCP server entry,
|
||||
* the legacy-hook cleanups) match the binary that will serve them.
|
||||
* Without this, those files keep the wording — and the tool names — of
|
||||
* whatever version first wrote them, no matter how many upgrades later.
|
||||
*
|
||||
* Strictly a refresh, never a first install:
|
||||
* - targets that aren't `alreadyConfigured` are skipped untouched;
|
||||
* - permissions are not written (`autoAllow: false`) and the prompt
|
||||
* hook is left as-is (`promptHook: undefined`), so choices the user
|
||||
* made at install time — or by hand since — are preserved.
|
||||
*
|
||||
* Every write underneath is the targets' own idempotent upsert, so a
|
||||
* re-run on an already-current machine reports `unchanged` everywhere.
|
||||
* Exposed (and unit-tested) separately from the CLI wiring, same as
|
||||
* `uninstallTargets`.
|
||||
*/
|
||||
export function refreshTargets(
|
||||
targets: readonly AgentTarget[],
|
||||
location: Location,
|
||||
): RefreshReport[] {
|
||||
return targets.map((target) => {
|
||||
const base = { id: target.id, displayName: target.displayName, location };
|
||||
if (!target.supportsLocation(location)) {
|
||||
return { ...base, status: 'unsupported' as const, changedPaths: [] };
|
||||
}
|
||||
if (!target.detect(location).alreadyConfigured) {
|
||||
return { ...base, status: 'not-configured' as const, changedPaths: [] };
|
||||
}
|
||||
const result = target.install(location, { autoAllow: false, promptHook: undefined });
|
||||
const changedPaths = result.files
|
||||
.filter((f) => f.action === 'created' || f.action === 'updated' || f.action === 'removed')
|
||||
.map((f) => f.path);
|
||||
return {
|
||||
...base,
|
||||
status: changedPaths.length > 0 ? ('refreshed' as const) : ('unchanged' as const),
|
||||
changedPaths,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Interactive uninstaller — the inverse of `runInstallerWithOptions`.
|
||||
* Asks global-vs-local first (unless `--location`/`--yes` is given),
|
||||
|
||||
Reference in New Issue
Block a user