feat(#607): rebuild get-shit-done-cc → gsd-core migration (per-package cache + installer auto-cleanup + --dry-run) (#611)

* feat(#607): rebuild get-shit-done-cc → gsd-core migration

Leftover get-shit-done-cc installs poisoned the shared update cache,
causing a permanent false "update available". Rebuild the migration so a
stale old install is both harmless and actively removed.

- Per-package update cache filename (gsd-update-check-<slug>.json) in the
  shared ~/.cache/gsd dir, single-sourced via package-identity; writers
  stamp package_name and readers reject foreign/absent lineage. Multi-
  runtime visibility preserved (same shared dir + filename across runtimes).
- New get-shit-done/bin/lib/legacy-cleanup.cjs seam: detects code-file
  references to the old package + the legacy fixed-name cache across home
  runtime dirs; installer auto-cleans on every install; --dry-run previews
  and mutates nothing. User hooks and dev-preferences are never touched.
- update.md cache-clear globs gsd-update-check*.json across ALL supported
  runtimes (adds cursor/windsurf/augment/trae/qwen/hermes/codebuddy/cline).
- Fix worker MODULE_NOT_FOUND post-install (ship managed-hooks-registry.cjs
  + degrade gracefully) so the per-package cache is always written.
- Diataxis how-to: docs/cleanup-get-shit-done-cc.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore(#607): add changeset for PR #611

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(#607): set USERPROFILE alongside HOME in dry-run install test for Windows

os.homedir() reads USERPROFILE on win32, so HOME-only isolation let the
spawned installer scan the real runner home on windows-latest. Set both.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-06-02 12:19:57 -04:00
committed by GitHub
parent df04aae5e4
commit 91f5bd7cb1
26 changed files with 1546 additions and 75 deletions

View File

@@ -17,8 +17,8 @@ const {
renderGithubReleaseNotes,
} = require(path.join(ROOT, 'scripts', 'changeset', 'github-release-notes.cjs'));
function run(command, args, cwd) {
const result = cp.spawnSync(command, args, { cwd, encoding: 'utf8' });
function run(command, args, cwd, env) {
const result = cp.spawnSync(command, args, { cwd, encoding: 'utf8', env: env || process.env });
assert.equal(result.status, 0, `${command} ${args.join(' ')}\nstdout=${result.stdout}\nstderr=${result.stderr}`);
return result.stdout;
}
@@ -31,19 +31,34 @@ function writeFragment(repo, name, type, pr, body) {
function createTaggedRepo() {
const repo = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-release-notes-'));
run('git', ['init', '-q'], repo);
run('git', ['config', 'user.email', 'test@example.com'], repo);
run('git', ['config', 'user.name', 'Test User'], repo);
// Isolate from the developer's global/system git config (e.g. gpgSign settings)
// by pointing GIT_CONFIG_GLOBAL and GIT_CONFIG_SYSTEM at an empty file inside
// the temp dir. This prevents tag.gpgSign / commit.gpgSign / tag.forceSignAnnotated
// from leaking in and breaking lightweight tags or unsigned commits.
const emptyGitConfig = path.join(repo, '.git-config-empty');
fs.writeFileSync(emptyGitConfig, '');
const gitEnv = {
...process.env,
GIT_CONFIG_GLOBAL: emptyGitConfig,
GIT_CONFIG_SYSTEM: emptyGitConfig,
};
run('git', ['init', '-q'], repo, gitEnv);
// Belt-and-suspenders: also set local repo config to disable signing
run('git', ['config', 'user.email', 'test@example.com'], repo, gitEnv);
run('git', ['config', 'user.name', 'Test User'], repo, gitEnv);
run('git', ['config', 'commit.gpgSign', 'false'], repo, gitEnv);
run('git', ['config', 'tag.gpgSign', 'false'], repo, gitEnv);
run('git', ['config', 'tag.forceSignAnnotated', 'false'], repo, gitEnv);
fs.writeFileSync(path.join(repo, 'README.md'), 'fixture\n');
run('git', ['add', 'README.md'], repo);
run('git', ['commit', '-q', '-m', 'initial'], repo);
run('git', ['tag', 'v1.0.0'], repo);
run('git', ['add', 'README.md'], repo, gitEnv);
run('git', ['commit', '-q', '-m', 'initial'], repo, gitEnv);
run('git', ['tag', 'v1.0.0'], repo, gitEnv);
writeFragment(repo, 'fix-install-sdk', 'Fixed', 101, '**`gsd-sdk` now installs reliably** — persistent PATH is checked.');
writeFragment(repo, 'remove-intel-noise', 'Removed', 102, '**`gsd-intel-updater` no longer emits layout detection noise** — ordinary projects stay quiet.');
run('git', ['add', '.changeset'], repo);
run('git', ['commit', '-q', '-m', 'add changesets'], repo);
run('git', ['tag', 'v1.0.1'], repo);
run('git', ['add', '.changeset'], repo, gitEnv);
run('git', ['commit', '-q', '-m', 'add changesets'], repo, gitEnv);
run('git', ['tag', 'v1.0.1'], repo, gitEnv);
return repo;
}