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:
@@ -26,6 +26,9 @@ const STAGE_DIR = path.join(HOOKS_DIR, `.dist-staging-${process.pid}`);
|
||||
const HOOKS_TO_COPY = [
|
||||
'gsd-check-update-worker.js',
|
||||
'gsd-check-update.js',
|
||||
// Required by gsd-check-update-worker.js at runtime — must ship alongside it
|
||||
// so require('./managed-hooks-registry.cjs') resolves in the installed hooks/ dir.
|
||||
'managed-hooks-registry.cjs',
|
||||
'gsd-context-monitor.js',
|
||||
'gsd-prompt-guard.js',
|
||||
'gsd-read-guard.js',
|
||||
|
||||
@@ -24,6 +24,21 @@ function parseRepoSlug(repository) {
|
||||
return m ? m[1] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Pure: turn an npm package name into a filesystem-safe slug for cache filenames.
|
||||
* Strips a leading `@`, replaces `/` with `-`, then collapses any run of
|
||||
* characters that are NOT `[a-z0-9]` to a single `-`, and trims leading/trailing `-`.
|
||||
*/
|
||||
function slugifyPackageName(name) {
|
||||
if (!name) return '';
|
||||
return name
|
||||
.replace(/^@/, '')
|
||||
.replace(/\//g, '-')
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, '-')
|
||||
.replace(/^-+|-+$/g, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Pure: package.json object -> the package identity coordinates.
|
||||
*/
|
||||
@@ -35,7 +50,9 @@ function deriveIdentity(pkg = {}) {
|
||||
const changelogRawUrl = repoSlug
|
||||
? `https://raw.githubusercontent.com/${repoSlug}/main/CHANGELOG.md`
|
||||
: '';
|
||||
return { packageName, binName, repoSlug, repoUrl, changelogRawUrl };
|
||||
const cacheSlug = slugifyPackageName(packageName);
|
||||
const updateCacheFileName = cacheSlug ? `gsd-update-check-${cacheSlug}.json` : 'gsd-update-check.json';
|
||||
return { packageName, binName, repoSlug, repoUrl, changelogRawUrl, cacheSlug, updateCacheFileName };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,7 +79,7 @@ const GENERATED_HEADER =
|
||||
* runtime command builder is byte-identical to the tested source above.
|
||||
*/
|
||||
function render(identity) {
|
||||
const { packageName, binName, repoSlug, repoUrl, changelogRawUrl } = identity;
|
||||
const { packageName, binName, repoSlug, repoUrl, changelogRawUrl, cacheSlug, updateCacheFileName } = identity;
|
||||
const j = (v) => JSON.stringify(v);
|
||||
return (
|
||||
GENERATED_HEADER +
|
||||
@@ -71,7 +88,9 @@ function render(identity) {
|
||||
`const binName = ${j(binName)};\n` +
|
||||
`const repoSlug = ${j(repoSlug)};\n` +
|
||||
`const repoUrl = ${j(repoUrl)};\n` +
|
||||
`const changelogRawUrl = ${j(changelogRawUrl)};\n\n` +
|
||||
`const changelogRawUrl = ${j(changelogRawUrl)};\n` +
|
||||
`const cacheSlug = ${j(cacheSlug)};\n` +
|
||||
`const updateCacheFileName = ${j(updateCacheFileName)};\n\n` +
|
||||
`${formatManualInstall.toString()}\n\n` +
|
||||
'function manualInstallCommand(opts = {}) {\n' +
|
||||
' return formatManualInstall({ packageName, binName, scope: opts.scope, runtime: opts.runtime });\n' +
|
||||
@@ -79,12 +98,14 @@ function render(identity) {
|
||||
'module.exports = Object.freeze({\n' +
|
||||
' packageName,\n' +
|
||||
' // PACKAGE_NAME: back-compat alias for #516-era consumers. Baked here, so it\n' +
|
||||
' // survives the installed tree’s synthetic package.json (fixes the #378 undefined).\n' +
|
||||
" // survives the installed tree's synthetic package.json (fixes the #378 undefined).\n" +
|
||||
' PACKAGE_NAME: packageName,\n' +
|
||||
' binName,\n' +
|
||||
' repoSlug,\n' +
|
||||
' repoUrl,\n' +
|
||||
' changelogRawUrl,\n' +
|
||||
' cacheSlug,\n' +
|
||||
' updateCacheFileName,\n' +
|
||||
' manualInstallCommand,\n' +
|
||||
'});\n'
|
||||
);
|
||||
@@ -101,4 +122,4 @@ function main() {
|
||||
|
||||
if (require.main === module) main();
|
||||
|
||||
module.exports = { deriveIdentity, parseRepoSlug, formatManualInstall, render, main };
|
||||
module.exports = { deriveIdentity, parseRepoSlug, slugifyPackageName, formatManualInstall, render, main };
|
||||
|
||||
Reference in New Issue
Block a user