fix(upgrade): refresh installer-written agent surfaces after a binary upgrade (#1238) (#1239)

* 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:
XU Pengfei
2026-07-10 10:58:55 -05:00
committed by GitHub
co-authored by Claude Fable 5 xuing Colby McHenry
parent 63eb488ed4
commit 386bff0f84
7 changed files with 334 additions and 5 deletions
+33
View File
@@ -2193,12 +2193,14 @@ program
.option('-y, --yes', 'Non-interactive: defaults to --location=global --target=auto, auto-allow on')
.option('--no-permissions', 'Skip writing the auto-allow permissions list (Claude Code only)')
.option('--print-config <id>', 'Print MCP config snippet for the named agent and exit (no file writes)')
.option('--refresh', 'Rewrite what previous installs configured, for already-configured agents only (never adds new ones). Run automatically by `codegraph upgrade`')
.action(async (opts: {
target?: string;
location?: string;
yes?: boolean;
permissions?: boolean;
printConfig?: string;
refresh?: boolean;
}) => {
if (opts.printConfig) {
const { getTarget, listTargetIds } = await import('../installer/targets/registry');
@@ -2213,6 +2215,37 @@ program
return;
}
// --refresh: non-interactive sweep that re-writes what previous
// installs configured (instructions section, MCP entry, legacy-hook
// cleanups) for already-configured agents, so those surfaces match
// THIS binary's templates. Skips everything else — never a first
// install, never touches permissions or the prompt hook. Sweeps both
// locations unless --location narrows it.
if (opts.refresh) {
const { refreshTargets } = await import('../installer');
const { ALL_TARGETS } = await import('../installer/targets/registry');
if (opts.location && opts.location !== 'global' && opts.location !== 'local') {
error(`--location must be "global" or "local" (got "${opts.location}").`);
process.exit(1);
}
const locs: Array<'global' | 'local'> = opts.location
? [opts.location as 'global' | 'local']
: ['global', 'local'];
let changed = 0;
for (const loc of locs) {
for (const report of refreshTargets(ALL_TARGETS, loc)) {
for (const p of report.changedPaths) {
changed += 1;
console.log(` ${report.displayName}: refreshed ${p}`);
}
}
}
if (changed === 0) {
console.log('All configured agent surfaces are already current.');
}
return;
}
const { runInstallerWithOptions } = await import('../installer');
if (opts.location && opts.location !== 'global' && opts.location !== 'local') {
error(`--location must be "global" or "local" (got "${opts.location}").`);