@jaguilar87/gaia-ops 2.5.3 → 2.5.5
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/bin/gaia-cleanup.js +156 -0
- package/bin/gaia-update.js +85 -13
- package/package.json +3 -2
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @jaguilar87/gaia-ops - Cleanup script
|
|
5
|
+
*
|
|
6
|
+
* Runs automatically on npm uninstall (preuninstall hook)
|
|
7
|
+
*
|
|
8
|
+
* Purpose:
|
|
9
|
+
* - Remove CLAUDE.md
|
|
10
|
+
* - Remove settings.json
|
|
11
|
+
* - Remove all symlinks (agents, tools, hooks, commands, config, templates, speckit, CHANGELOG.md)
|
|
12
|
+
* - Preserve project-specific data (logs, tests, project-context, session, metrics)
|
|
13
|
+
*
|
|
14
|
+
* Usage: Automatic (npm preuninstall hook)
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { fileURLToPath } from 'url';
|
|
18
|
+
import { dirname, join } from 'path';
|
|
19
|
+
import fs from 'fs/promises';
|
|
20
|
+
import { existsSync } from 'fs';
|
|
21
|
+
import chalk from 'chalk';
|
|
22
|
+
import ora from 'ora';
|
|
23
|
+
|
|
24
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
25
|
+
const __dirname = dirname(__filename);
|
|
26
|
+
const CWD = process.env.INIT_CWD || process.cwd();
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Remove CLAUDE.md if it exists
|
|
30
|
+
*/
|
|
31
|
+
async function removeClaudeMd() {
|
|
32
|
+
const spinner = ora('Removing CLAUDE.md...').start();
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const claudeMdPath = join(CWD, 'CLAUDE.md');
|
|
36
|
+
|
|
37
|
+
if (!existsSync(claudeMdPath)) {
|
|
38
|
+
spinner.info('CLAUDE.md not found, skipping');
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
await fs.unlink(claudeMdPath);
|
|
43
|
+
spinner.succeed('CLAUDE.md removed');
|
|
44
|
+
return true;
|
|
45
|
+
} catch (error) {
|
|
46
|
+
spinner.fail(`Failed to remove CLAUDE.md: ${error.message}`);
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Remove settings.json if it exists
|
|
53
|
+
*/
|
|
54
|
+
async function removeSettingsJson() {
|
|
55
|
+
const spinner = ora('Removing settings.json...').start();
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const settingsPath = join(CWD, '.claude', 'settings.json');
|
|
59
|
+
|
|
60
|
+
if (!existsSync(settingsPath)) {
|
|
61
|
+
spinner.info('settings.json not found, skipping');
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
await fs.unlink(settingsPath);
|
|
66
|
+
spinner.succeed('settings.json removed');
|
|
67
|
+
return true;
|
|
68
|
+
} catch (error) {
|
|
69
|
+
spinner.fail(`Failed to remove settings.json: ${error.message}`);
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Remove all symlinks in .claude/ directory
|
|
76
|
+
*/
|
|
77
|
+
async function removeSymlinks() {
|
|
78
|
+
const spinner = ora('Removing symlinks...').start();
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const claudeDir = join(CWD, '.claude');
|
|
82
|
+
|
|
83
|
+
if (!existsSync(claudeDir)) {
|
|
84
|
+
spinner.info('.claude/ directory not found, skipping');
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Define all symlinks that should be removed
|
|
89
|
+
const symlinks = [
|
|
90
|
+
join(claudeDir, 'agents'),
|
|
91
|
+
join(claudeDir, 'tools'),
|
|
92
|
+
join(claudeDir, 'hooks'),
|
|
93
|
+
join(claudeDir, 'commands'),
|
|
94
|
+
join(claudeDir, 'templates'),
|
|
95
|
+
join(claudeDir, 'config'),
|
|
96
|
+
join(claudeDir, 'speckit'),
|
|
97
|
+
join(claudeDir, 'CHANGELOG.md'),
|
|
98
|
+
join(claudeDir, 'README.en.md'),
|
|
99
|
+
join(claudeDir, 'README.md')
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
let removed = 0;
|
|
103
|
+
for (const symlinkPath of symlinks) {
|
|
104
|
+
if (existsSync(symlinkPath)) {
|
|
105
|
+
try {
|
|
106
|
+
await fs.unlink(symlinkPath);
|
|
107
|
+
removed++;
|
|
108
|
+
} catch (error) {
|
|
109
|
+
// Ignore errors
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (removed > 0) {
|
|
115
|
+
spinner.succeed(`Removed ${removed} symlink(s)`);
|
|
116
|
+
return true;
|
|
117
|
+
} else {
|
|
118
|
+
spinner.info('No symlinks found to remove');
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
} catch (error) {
|
|
122
|
+
spinner.fail(`Failed to remove symlinks: ${error.message}`);
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Main function
|
|
129
|
+
*/
|
|
130
|
+
async function main() {
|
|
131
|
+
console.log(chalk.cyan('\n🧹 @jaguilar87/gaia-ops cleanup\n'));
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
const claudeRemoved = await removeClaudeMd();
|
|
135
|
+
const settingsRemoved = await removeSettingsJson();
|
|
136
|
+
const symlinksRemoved = await removeSymlinks();
|
|
137
|
+
|
|
138
|
+
if (claudeRemoved || settingsRemoved || symlinksRemoved) {
|
|
139
|
+
console.log(chalk.green('\n✅ Cleanup completed\n'));
|
|
140
|
+
console.log(chalk.gray('Preserved data:'));
|
|
141
|
+
console.log(chalk.gray(' • .claude/logs/'));
|
|
142
|
+
console.log(chalk.gray(' • .claude/tests/'));
|
|
143
|
+
console.log(chalk.gray(' • .claude/project-context/'));
|
|
144
|
+
console.log(chalk.gray(' • .claude/session/'));
|
|
145
|
+
console.log(chalk.gray(' • .claude/metrics/\n'));
|
|
146
|
+
} else {
|
|
147
|
+
console.log(chalk.gray('\n✓ Nothing to clean up\n'));
|
|
148
|
+
}
|
|
149
|
+
} catch (error) {
|
|
150
|
+
console.error(chalk.red(`\n❌ Cleanup failed: ${error.message}\n`));
|
|
151
|
+
// Don't fail npm uninstall, just warn
|
|
152
|
+
process.exit(0);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
main();
|
package/bin/gaia-update.js
CHANGED
|
@@ -29,11 +29,11 @@ const __dirname = dirname(__filename);
|
|
|
29
29
|
const CWD = process.env.INIT_CWD || process.cwd();
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
* Check if
|
|
32
|
+
* Check if .claude/ directory exists (to determine if this is first-time install)
|
|
33
33
|
*/
|
|
34
|
-
async function
|
|
35
|
-
const
|
|
36
|
-
return existsSync(
|
|
34
|
+
async function isExistingInstallation() {
|
|
35
|
+
const claudeDir = join(CWD, '.claude');
|
|
36
|
+
return existsSync(claudeDir);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/**
|
|
@@ -52,22 +52,25 @@ async function updateClaudeMd() {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
const claudeMdPath = join(CWD, 'CLAUDE.md');
|
|
55
|
+
const claudeDir = join(CWD, '.claude');
|
|
55
56
|
|
|
56
|
-
// Check if this is first-time install
|
|
57
|
-
if (!existsSync(
|
|
58
|
-
//
|
|
57
|
+
// Check if .claude/ exists (indicates this is NOT first-time install)
|
|
58
|
+
if (!existsSync(claudeDir)) {
|
|
59
|
+
// True first-time install - skip, let gaia-init handle it
|
|
59
60
|
spinner.info('First-time installation detected - skipping auto-update');
|
|
60
61
|
return false;
|
|
61
62
|
}
|
|
62
63
|
|
|
63
|
-
//
|
|
64
|
-
//
|
|
64
|
+
// .claude/ exists, so this is an existing installation
|
|
65
|
+
// Regenerate CLAUDE.md (whether it exists or not)
|
|
66
|
+
const fileExistedBefore = existsSync(claudeMdPath);
|
|
65
67
|
const template = await fs.readFile(templatePath, 'utf-8');
|
|
66
68
|
|
|
67
|
-
// Write
|
|
69
|
+
// Write/overwrite CLAUDE.md
|
|
68
70
|
await fs.writeFile(claudeMdPath, template, 'utf-8');
|
|
69
71
|
|
|
70
|
-
|
|
72
|
+
const action = fileExistedBefore ? 'updated successfully (existing file overwritten)' : 'created successfully';
|
|
73
|
+
spinner.succeed(`CLAUDE.md ${action}`);
|
|
71
74
|
return true;
|
|
72
75
|
} catch (error) {
|
|
73
76
|
spinner.fail(`Failed to update CLAUDE.md: ${error.message}`);
|
|
@@ -110,6 +113,74 @@ async function updateSettingsJson() {
|
|
|
110
113
|
}
|
|
111
114
|
}
|
|
112
115
|
|
|
116
|
+
/**
|
|
117
|
+
* Recreate missing symlinks in .claude/ directory
|
|
118
|
+
*/
|
|
119
|
+
async function recreateSymlinks() {
|
|
120
|
+
const spinner = ora('Checking symlinks...').start();
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
const claudeDir = join(CWD, '.claude');
|
|
124
|
+
|
|
125
|
+
// Skip if .claude/ doesn't exist (first-time install)
|
|
126
|
+
if (!existsSync(claudeDir)) {
|
|
127
|
+
spinner.info('First-time installation detected - skipping symlink check');
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Calculate relative path from .claude/ to node_modules/@jaguilar87/gaia-ops
|
|
132
|
+
const packagePath = join(CWD, 'node_modules', '@jaguilar87', 'gaia-ops');
|
|
133
|
+
|
|
134
|
+
// Verify package exists
|
|
135
|
+
if (!existsSync(packagePath)) {
|
|
136
|
+
spinner.fail('Package not found in node_modules');
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const { relative } = await import('path');
|
|
141
|
+
const relativePath = relative(claudeDir, packagePath);
|
|
142
|
+
|
|
143
|
+
// Define all symlinks that should exist
|
|
144
|
+
const symlinks = [
|
|
145
|
+
{ target: join(relativePath, 'agents'), link: join(claudeDir, 'agents'), name: 'agents' },
|
|
146
|
+
{ target: join(relativePath, 'tools'), link: join(claudeDir, 'tools'), name: 'tools' },
|
|
147
|
+
{ target: join(relativePath, 'hooks'), link: join(claudeDir, 'hooks'), name: 'hooks' },
|
|
148
|
+
{ target: join(relativePath, 'commands'), link: join(claudeDir, 'commands'), name: 'commands' },
|
|
149
|
+
{ target: join(relativePath, 'templates'), link: join(claudeDir, 'templates'), name: 'templates' },
|
|
150
|
+
{ target: join(relativePath, 'config'), link: join(claudeDir, 'config'), name: 'config' },
|
|
151
|
+
{ target: join(relativePath, 'speckit'), link: join(claudeDir, 'speckit'), name: 'speckit' },
|
|
152
|
+
{ target: join(relativePath, 'CHANGELOG.md'), link: join(claudeDir, 'CHANGELOG.md'), name: 'CHANGELOG.md' }
|
|
153
|
+
];
|
|
154
|
+
|
|
155
|
+
let recreated = 0;
|
|
156
|
+
for (const { target, link, name } of symlinks) {
|
|
157
|
+
// Check if symlink exists and is valid
|
|
158
|
+
if (existsSync(link)) {
|
|
159
|
+
continue; // Symlink exists, skip
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Symlink missing, recreate it
|
|
163
|
+
try {
|
|
164
|
+
await fs.symlink(target, link);
|
|
165
|
+
recreated++;
|
|
166
|
+
} catch (error) {
|
|
167
|
+
// Ignore errors, might be permissions or already exists
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (recreated > 0) {
|
|
172
|
+
spinner.succeed(`Recreated ${recreated} missing symlink(s)`);
|
|
173
|
+
return true;
|
|
174
|
+
} else {
|
|
175
|
+
spinner.succeed('All symlinks are valid');
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
} catch (error) {
|
|
179
|
+
spinner.fail(`Failed to check symlinks: ${error.message}`);
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
113
184
|
/**
|
|
114
185
|
* Main function
|
|
115
186
|
*/
|
|
@@ -118,7 +189,7 @@ async function main() {
|
|
|
118
189
|
|
|
119
190
|
try {
|
|
120
191
|
// Check if this is an update (not first-time install)
|
|
121
|
-
const isUpdate = await
|
|
192
|
+
const isUpdate = await isExistingInstallation();
|
|
122
193
|
|
|
123
194
|
if (isUpdate) {
|
|
124
195
|
// Show warning before overwriting files
|
|
@@ -130,8 +201,9 @@ async function main() {
|
|
|
130
201
|
|
|
131
202
|
const claudeUpdated = await updateClaudeMd();
|
|
132
203
|
const settingsUpdated = await updateSettingsJson();
|
|
204
|
+
const symlinksRecreated = await recreateSymlinks();
|
|
133
205
|
|
|
134
|
-
if (claudeUpdated || settingsUpdated) {
|
|
206
|
+
if (claudeUpdated || settingsUpdated || symlinksRecreated) {
|
|
135
207
|
console.log(chalk.green('\n✅ Auto-update completed\n'));
|
|
136
208
|
console.log(chalk.yellow('⚠️ IMPORTANT: Files have been overwritten from templates'));
|
|
137
209
|
console.log(chalk.gray('\nNext steps:'));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jaguilar87/gaia-ops",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.5",
|
|
4
4
|
"description": "Multi-agent orchestration system for Claude Code - DevOps automation toolkit",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -56,7 +56,8 @@
|
|
|
56
56
|
"pre-publish:dry": "node bin/pre-publish-validate.js --dry-run",
|
|
57
57
|
"pre-publish:validate": "node bin/pre-publish-validate.js --validate-only",
|
|
58
58
|
"prepublishOnly": "npm run clean",
|
|
59
|
-
"postinstall": "node bin/gaia-update.js"
|
|
59
|
+
"postinstall": "node bin/gaia-update.js",
|
|
60
|
+
"preuninstall": "node bin/gaia-cleanup.js"
|
|
60
61
|
},
|
|
61
62
|
"_postinstall_note": "⚠️ postinstall hook overwrites CLAUDE.md and settings.json from templates on every npm install/update",
|
|
62
63
|
"dependencies": {
|