@ghl-ai/aw 0.1.12 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/commands/nuke.mjs +112 -64
- package/package.json +1 -1
package/commands/nuke.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// commands/nuke.mjs — Safe cleanup:
|
|
1
|
+
// commands/nuke.mjs — Safe cleanup: removes only what AW created
|
|
2
2
|
//
|
|
3
3
|
// Safety guarantee: NEVER deletes files that AW didn't create.
|
|
4
4
|
|
|
@@ -13,31 +13,28 @@ const HOME = homedir();
|
|
|
13
13
|
const GLOBAL_AW_DIR = join(HOME, '.aw_registry');
|
|
14
14
|
const MANIFEST_PATH = join(GLOBAL_AW_DIR, '.aw-manifest.json');
|
|
15
15
|
|
|
16
|
-
// IDE dirs where AW creates symlinks
|
|
17
16
|
const IDE_DIRS = ['.claude', '.cursor', '.codex'];
|
|
18
17
|
const CONTENT_TYPES = ['agents', 'skills', 'commands', 'blueprints', 'evals'];
|
|
19
18
|
|
|
20
19
|
function loadManifest() {
|
|
21
20
|
if (!existsSync(MANIFEST_PATH)) return null;
|
|
22
|
-
try {
|
|
23
|
-
|
|
24
|
-
} catch { return null; }
|
|
21
|
+
try { return JSON.parse(readFileSync(MANIFEST_PATH, 'utf8')); }
|
|
22
|
+
catch { return null; }
|
|
25
23
|
}
|
|
26
24
|
|
|
27
|
-
// Remove AW-created files listed in manifest
|
|
25
|
+
// 1. Remove AW-created files listed in manifest
|
|
28
26
|
function removeCreatedFiles(manifest) {
|
|
29
27
|
if (!manifest?.createdFiles?.length) return;
|
|
30
28
|
let removed = 0;
|
|
31
29
|
for (const rel of manifest.createdFiles) {
|
|
32
30
|
const p = join(HOME, rel);
|
|
33
|
-
try {
|
|
34
|
-
|
|
35
|
-
} catch { /* best effort */ }
|
|
31
|
+
try { if (existsSync(p)) { rmSync(p); removed++; } }
|
|
32
|
+
catch { /* best effort */ }
|
|
36
33
|
}
|
|
37
34
|
if (removed > 0) fmt.logStep(`Removed ${removed} generated file${removed > 1 ? 's' : ''}`);
|
|
38
35
|
}
|
|
39
36
|
|
|
40
|
-
// Remove symlinks from IDE dirs that point into .aw_registry
|
|
37
|
+
// 2. Remove symlinks from IDE dirs that point into .aw_registry
|
|
41
38
|
function removeIdeSymlinks() {
|
|
42
39
|
let removed = 0;
|
|
43
40
|
|
|
@@ -60,42 +57,118 @@ function removeIdeSymlinks() {
|
|
|
60
57
|
// Remove dir if now empty
|
|
61
58
|
try { if (readdirSync(dir).length === 0) rmSync(dir); } catch {}
|
|
62
59
|
}
|
|
60
|
+
|
|
61
|
+
// Also clean CLAUDE.md / AGENTS.md if they're AW-generated
|
|
62
|
+
for (const file of ['CLAUDE.md', 'AGENTS.md']) {
|
|
63
|
+
const p = join(HOME, ide, file);
|
|
64
|
+
try {
|
|
65
|
+
if (lstatSync(p).isSymbolicLink()) {
|
|
66
|
+
const target = readlinkSync(p);
|
|
67
|
+
if (target.includes('.aw_registry') || target.includes('aw_registry')) {
|
|
68
|
+
unlinkSync(p); removed++;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
} catch { /* not a symlink or doesn't exist */ }
|
|
72
|
+
}
|
|
63
73
|
}
|
|
64
74
|
|
|
65
75
|
if (removed > 0) fmt.logStep(`Removed ${removed} IDE symlink${removed > 1 ? 's' : ''}`);
|
|
66
76
|
}
|
|
67
77
|
|
|
68
|
-
// Find and remove .aw_registry symlinks from project directories
|
|
78
|
+
// 3. Find and remove ALL .aw_registry symlinks from project directories
|
|
69
79
|
function removeProjectSymlinks() {
|
|
70
80
|
let removed = 0;
|
|
71
|
-
const dirsToCheck = new Set([process.cwd()]);
|
|
72
81
|
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
82
|
+
// Use find command for thorough search — much more reliable than manual scanning
|
|
83
|
+
try {
|
|
84
|
+
const result = execSync(
|
|
85
|
+
`find "${HOME}" -maxdepth 4 -name ".aw_registry" -type l 2>/dev/null`,
|
|
86
|
+
{ encoding: 'utf8', timeout: 10000 }
|
|
87
|
+
).trim();
|
|
88
|
+
|
|
89
|
+
if (result) {
|
|
90
|
+
for (const linkPath of result.split('\n').filter(Boolean)) {
|
|
91
|
+
try {
|
|
92
|
+
unlinkSync(linkPath);
|
|
93
|
+
removed++;
|
|
94
|
+
} catch { /* best effort */ }
|
|
80
95
|
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
96
|
+
}
|
|
97
|
+
} catch {
|
|
98
|
+
// Fallback: manual scan of common dirs
|
|
99
|
+
const dirsToCheck = new Set([process.cwd()]);
|
|
100
|
+
for (const parent of ['Documents', 'Projects', 'Desktop', 'dev', 'repos', 'src', 'code', 'work']) {
|
|
101
|
+
const p = join(HOME, parent);
|
|
102
|
+
if (!existsSync(p)) continue;
|
|
103
|
+
try {
|
|
104
|
+
for (const sub of readdirSync(p)) {
|
|
105
|
+
const sp = join(p, sub);
|
|
106
|
+
dirsToCheck.add(sp);
|
|
107
|
+
// Also check one level deeper
|
|
108
|
+
try {
|
|
109
|
+
for (const sub2 of readdirSync(sp)) {
|
|
110
|
+
dirsToCheck.add(join(sp, sub2));
|
|
111
|
+
}
|
|
112
|
+
} catch {}
|
|
113
|
+
}
|
|
114
|
+
} catch {}
|
|
115
|
+
}
|
|
83
116
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
117
|
+
for (const dir of dirsToCheck) {
|
|
118
|
+
const link = join(dir, '.aw_registry');
|
|
119
|
+
try {
|
|
120
|
+
if (lstatSync(link).isSymbolicLink()) { unlinkSync(link); removed++; }
|
|
121
|
+
} catch {}
|
|
122
|
+
}
|
|
89
123
|
}
|
|
90
124
|
|
|
91
125
|
if (removed > 0) fmt.logStep(`Removed ${removed} project .aw_registry symlink${removed > 1 ? 's' : ''}`);
|
|
92
126
|
}
|
|
93
127
|
|
|
128
|
+
// 4. Remove git template hook
|
|
129
|
+
function removeGitTemplate(manifest) {
|
|
130
|
+
const gitTemplateDir = manifest?.gitTemplate || join(HOME, '.git-templates', 'aw');
|
|
131
|
+
if (!existsSync(gitTemplateDir)) return;
|
|
132
|
+
|
|
133
|
+
rmSync(gitTemplateDir, { recursive: true, force: true });
|
|
134
|
+
try {
|
|
135
|
+
const current = execSync('git config --global init.templateDir', {
|
|
136
|
+
encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe']
|
|
137
|
+
}).trim();
|
|
138
|
+
if (current === gitTemplateDir) {
|
|
139
|
+
execSync('git config --global --unset init.templateDir', { stdio: 'pipe' });
|
|
140
|
+
}
|
|
141
|
+
} catch { /* not set */ }
|
|
142
|
+
fmt.logStep('Removed git template hook');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// 5. Remove IDE auto-init tasks
|
|
146
|
+
function removeIdeTasks() {
|
|
147
|
+
for (const ide of ['Code', 'Cursor']) {
|
|
148
|
+
const tasksPath = join(HOME, 'Library', 'Application Support', ide, 'User', 'tasks.json');
|
|
149
|
+
if (!existsSync(tasksPath)) continue;
|
|
150
|
+
try {
|
|
151
|
+
const data = JSON.parse(readFileSync(tasksPath, 'utf8'));
|
|
152
|
+
const before = data.tasks?.length || 0;
|
|
153
|
+
data.tasks = (data.tasks || []).filter(t =>
|
|
154
|
+
t.label !== 'aw: sync registry' && t.label !== 'aw: pull registry'
|
|
155
|
+
);
|
|
156
|
+
if (data.tasks.length < before) {
|
|
157
|
+
if (data.tasks.length === 0) {
|
|
158
|
+
unlinkSync(tasksPath);
|
|
159
|
+
} else {
|
|
160
|
+
writeFileSync(tasksPath, JSON.stringify(data, null, 2) + '\n');
|
|
161
|
+
}
|
|
162
|
+
fmt.logStep(`Removed auto-sync task from ${ide}`);
|
|
163
|
+
}
|
|
164
|
+
} catch { /* best effort */ }
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
94
168
|
export function nukeCommand(args) {
|
|
95
169
|
fmt.intro('aw nuke');
|
|
96
170
|
|
|
97
171
|
if (!existsSync(GLOBAL_AW_DIR)) {
|
|
98
|
-
// Check cwd for local symlink
|
|
99
172
|
const local = join(process.cwd(), '.aw_registry');
|
|
100
173
|
if (existsSync(local) && lstatSync(local).isSymbolicLink()) {
|
|
101
174
|
unlinkSync(local);
|
|
@@ -114,71 +187,46 @@ export function nukeCommand(args) {
|
|
|
114
187
|
manifest ? `${chalk.dim('installed:')} ${manifest.installedAt}` : null,
|
|
115
188
|
].filter(Boolean).join('\n'), 'Cleaning up');
|
|
116
189
|
|
|
190
|
+
// Order matters: remove symlinks BEFORE removing the source
|
|
191
|
+
|
|
117
192
|
// 1. Remove AW-created files (instruction files, MCP configs)
|
|
118
193
|
removeCreatedFiles(manifest);
|
|
119
194
|
|
|
120
195
|
// 2. Remove IDE symlinks (only those pointing to .aw_registry)
|
|
121
196
|
removeIdeSymlinks();
|
|
122
197
|
|
|
123
|
-
// 3. Remove .aw_registry symlinks from project directories
|
|
198
|
+
// 3. Remove .aw_registry symlinks from ALL project directories
|
|
124
199
|
removeProjectSymlinks();
|
|
125
200
|
|
|
126
201
|
// 4. Remove git template hook
|
|
127
|
-
|
|
128
|
-
if (existsSync(gitTemplateDir)) {
|
|
129
|
-
rmSync(gitTemplateDir, { recursive: true, force: true });
|
|
130
|
-
// Unset git config if it pointed to our template
|
|
131
|
-
try {
|
|
132
|
-
const current = execSync('git config --global init.templateDir', {
|
|
133
|
-
encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe']
|
|
134
|
-
}).trim();
|
|
135
|
-
if (current === gitTemplateDir) {
|
|
136
|
-
execSync('git config --global --unset init.templateDir', { stdio: 'pipe' });
|
|
137
|
-
}
|
|
138
|
-
} catch { /* not set */ }
|
|
139
|
-
fmt.logStep('Removed git template hook');
|
|
140
|
-
}
|
|
202
|
+
removeGitTemplate(manifest);
|
|
141
203
|
|
|
142
|
-
// 5. Remove IDE auto-
|
|
143
|
-
|
|
144
|
-
const tasksPath = join(HOME, 'Library', 'Application Support', ide, 'User', 'tasks.json');
|
|
145
|
-
if (!existsSync(tasksPath)) continue;
|
|
146
|
-
try {
|
|
147
|
-
const data = JSON.parse(readFileSync(tasksPath, 'utf8'));
|
|
148
|
-
const before = data.tasks?.length || 0;
|
|
149
|
-
data.tasks = (data.tasks || []).filter(t => t.label !== 'aw: sync registry' && t.label !== 'aw: pull registry');
|
|
150
|
-
if (data.tasks.length < before) {
|
|
151
|
-
if (data.tasks.length === 0) {
|
|
152
|
-
unlinkSync(tasksPath);
|
|
153
|
-
} else {
|
|
154
|
-
writeFileSync(tasksPath, JSON.stringify(data, null, 2) + '\n');
|
|
155
|
-
}
|
|
156
|
-
fmt.logStep(`Removed auto-pull task from ${ide}`);
|
|
157
|
-
}
|
|
158
|
-
} catch { /* best effort */ }
|
|
159
|
-
}
|
|
204
|
+
// 5. Remove IDE auto-init tasks
|
|
205
|
+
removeIdeTasks();
|
|
160
206
|
|
|
161
|
-
//
|
|
207
|
+
// 6. Remove ~/.aw_docs/
|
|
162
208
|
const awDocs = join(HOME, '.aw_docs');
|
|
163
209
|
if (existsSync(awDocs)) {
|
|
164
210
|
rmSync(awDocs, { recursive: true, force: true });
|
|
165
211
|
fmt.logStep('Removed ~/.aw_docs/');
|
|
166
212
|
}
|
|
167
213
|
|
|
168
|
-
//
|
|
214
|
+
// 7. Remove ~/.aw_registry/ itself (source of truth — last!)
|
|
169
215
|
rmSync(GLOBAL_AW_DIR, { recursive: true, force: true });
|
|
170
216
|
fmt.logStep('Removed ~/.aw_registry/');
|
|
171
217
|
|
|
172
218
|
fmt.outro([
|
|
173
219
|
'Fully removed',
|
|
174
220
|
'',
|
|
221
|
+
` ${chalk.green('✓')} Generated files cleaned`,
|
|
175
222
|
` ${chalk.green('✓')} IDE symlinks cleaned`,
|
|
176
223
|
` ${chalk.green('✓')} Project symlinks cleaned`,
|
|
177
224
|
` ${chalk.green('✓')} Git template hook removed`,
|
|
178
|
-
` ${chalk.green('✓')} IDE auto-
|
|
225
|
+
` ${chalk.green('✓')} IDE auto-sync tasks removed`,
|
|
179
226
|
` ${chalk.green('✓')} Source of truth deleted`,
|
|
180
227
|
'',
|
|
181
228
|
` ${chalk.dim('No existing files were touched.')}`,
|
|
182
|
-
` ${chalk.dim('
|
|
229
|
+
` ${chalk.dim('To reinstall:')} ${chalk.bold('aw init')}`,
|
|
230
|
+
` ${chalk.dim('To uninstall CLI:')} ${chalk.bold('npm uninstall -g @ghl-ai/aw')}`,
|
|
183
231
|
].join('\n'));
|
|
184
232
|
}
|