diff --git a/bin/install.js b/bin/install.js
index dcaeeca89..376976115 100755
--- a/bin/install.js
+++ b/bin/install.js
@@ -123,8 +123,13 @@ function writeSettings(settingsPath, settings) {
/**
* Recursively copy directory, replacing paths in .md files
+ * Deletes existing destDir first to remove orphaned files from previous versions
*/
function copyWithPathReplacement(srcDir, destDir, pathPrefix) {
+ // Clean install: remove existing destination to prevent orphaned files
+ if (fs.existsSync(destDir)) {
+ fs.rmSync(destDir, { recursive: true });
+ }
fs.mkdirSync(destDir, { recursive: true });
const entries = fs.readdirSync(srcDir, { withFileTypes: true });
@@ -187,10 +192,30 @@ function install(isGlobal) {
console.log(` ${green}✓${reset} Installed get-shit-done`);
// Copy agents to ~/.claude/agents (subagents must be at root level)
+ // Only delete gsd-*.md files to preserve user's custom agents
const agentsSrc = path.join(src, 'agents');
if (fs.existsSync(agentsSrc)) {
const agentsDest = path.join(claudeDir, 'agents');
- copyWithPathReplacement(agentsSrc, agentsDest, pathPrefix);
+ fs.mkdirSync(agentsDest, { recursive: true });
+
+ // Remove old GSD agents (gsd-*.md) before copying new ones
+ if (fs.existsSync(agentsDest)) {
+ for (const file of fs.readdirSync(agentsDest)) {
+ if (file.startsWith('gsd-') && file.endsWith('.md')) {
+ fs.unlinkSync(path.join(agentsDest, file));
+ }
+ }
+ }
+
+ // Copy new agents (don't use copyWithPathReplacement which would wipe the folder)
+ const agentEntries = fs.readdirSync(agentsSrc, { withFileTypes: true });
+ for (const entry of agentEntries) {
+ if (entry.isFile() && entry.name.endsWith('.md')) {
+ let content = fs.readFileSync(path.join(agentsSrc, entry.name), 'utf8');
+ content = content.replace(/~\/\.claude\//g, pathPrefix);
+ fs.writeFileSync(path.join(agentsDest, entry.name), content);
+ }
+ }
console.log(` ${green}✓${reset} Installed agents`);
}
diff --git a/commands/gsd/update.md b/commands/gsd/update.md
index ad878296b..bfe930b14 100644
--- a/commands/gsd/update.md
+++ b/commands/gsd/update.md
@@ -77,6 +77,57 @@ You're ahead of the latest release (development version?).
STOP here if ahead.
+
+**If update available**, fetch and show what's new BEFORE updating:
+
+1. Fetch changelog (same as fetch_changelog step)
+2. Extract entries between installed and latest versions
+3. Display preview and ask for confirmation:
+
+```
+## GSD Update Available
+
+**Installed:** 1.5.10
+**Latest:** 1.5.15
+
+### What's New
+────────────────────────────────────────────────────────────
+
+## [1.5.15] - 2026-01-20
+
+### Added
+- Feature X
+
+## [1.5.14] - 2026-01-18
+
+### Fixed
+- Bug fix Y
+
+────────────────────────────────────────────────────────────
+
+⚠️ **Note:** The installer performs a clean install of GSD folders:
+- `~/.claude/commands/gsd/` will be wiped and replaced
+- `~/.claude/get-shit-done/` will be wiped and replaced
+- `~/.claude/agents/gsd-*` files will be replaced
+
+Your custom files in other locations are preserved:
+- Custom commands in `~/.claude/commands/your-stuff/` ✓
+- Custom agents not prefixed with `gsd-` ✓
+- Custom hooks ✓
+- Your CLAUDE.md files ✓
+
+If you've modified any GSD files directly, back them up first.
+```
+
+Use AskUserQuestion:
+- Question: "Proceed with update?"
+- Options:
+ - "Yes, update now"
+ - "No, cancel"
+
+**If user cancels:** STOP here.
+
+
Run the update:
@@ -93,63 +144,18 @@ rm -f ~/.claude/cache/gsd-update-check.json
```
-
-Fetch changelog from GitHub:
-
-Use WebFetch tool with:
-- URL: `https://raw.githubusercontent.com/glittercowboy/get-shit-done/main/CHANGELOG.md`
-- Prompt: "Extract all version entries with their dates and changes. Return the raw markdown for each version section."
-
-**If fetch fails:**
-Fall back to local:
-```bash
-cat ~/.claude/get-shit-done/CHANGELOG.md 2>/dev/null
-```
-
-
-
-From the changelog, extract entries between:
-- **From:** installed version (exclusive)
-- **To:** latest version (inclusive)
-
-Parse each `## [X.Y.Z]` section and collect all versions in the range.
-
-
-Format beautiful output:
+Format completion message (changelog was already shown in confirmation step):
```
╔═══════════════════════════════════════════════════════════╗
║ GSD Updated: v1.5.10 → v1.5.15 ║
╚═══════════════════════════════════════════════════════════╝
-✨ What's New
-────────────────────────────────────────────────────────────
-
-## [1.5.15] - 2026-01-20
-
-### Added
-- Feature X
-- Feature Y
-
-## [1.5.14] - 2026-01-18
-
-### Fixed
-- Bug in feature A
-
-────────────────────────────────────────────────────────────
-
⚠️ Restart Claude Code to pick up the new commands.
[View full changelog](https://github.com/glittercowboy/get-shit-done/blob/main/CHANGELOG.md)
```
-
-**Key elements:**
-- Box header with version transition
-- All changelog entries in the range
-- **BREAKING:** changes surfaced prominently
-- Restart reminder (critical for picking up new commands)
-- Link to full changelog
@@ -158,8 +164,9 @@ Format beautiful output:
- [ ] Installed version read correctly
- [ ] Latest version checked via npm
- [ ] Update skipped if already current
+- [ ] Changelog fetched and displayed BEFORE update
+- [ ] Clean install warning shown
+- [ ] User confirmation obtained
- [ ] Update executed successfully
-- [ ] Changelog fetched (remote or local fallback)
-- [ ] Changes between versions displayed
- [ ] Restart reminder shown