claude-all-config 3.5.8 → 3.5.10

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.
Files changed (3) hide show
  1. package/VERSION +1 -1
  2. package/package.json +1 -1
  3. package/postinstall.js +127 -21
package/VERSION CHANGED
@@ -1 +1 @@
1
- 3.5.8
1
+ 3.5.10
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-all-config",
3
- "version": "3.5.8",
3
+ "version": "3.5.10",
4
4
  "description": "🦾 MONSTER ENGINEER v2 - Ultimate AI CLI with 63 Skills, 12 Superpowers, 14 Agents. Multi-Agent Orchestration, Cost-Aware, Security Scorecard, Parallel-First.",
5
5
  "main": "index.js",
6
6
  "bin": {
package/postinstall.js CHANGED
@@ -8,15 +8,65 @@ const fs = require('fs');
8
8
  const path = require('path');
9
9
  const { execSync } = require('child_process');
10
10
 
11
- const HOME = process.env.HOME || process.env.USERPROFILE;
11
+ // --- Sudo-aware HOME detection ----------------------------------------------
12
+ // When the package is installed via `sudo npm install -g`, npm runs the
13
+ // postinstall as root, so process.env.HOME is /root and configs would land in
14
+ // the wrong place. We honor SUDO_USER to write into the invoking user's HOME
15
+ // and chown the result back to them so a follow-up non-sudo install does not
16
+ // trip over EACCES on root-owned files.
17
+ const SUDO_USER = process.env.SUDO_USER && process.env.SUDO_USER !== 'root'
18
+ ? process.env.SUDO_USER : null;
19
+ const SUDO_UID = process.env.SUDO_UID ? Number(process.env.SUDO_UID) : null;
20
+ const SUDO_GID = process.env.SUDO_GID ? Number(process.env.SUDO_GID) : null;
21
+
22
+ function resolveHome() {
23
+ if (SUDO_USER) {
24
+ // Look up the real home from /etc/passwd
25
+ try {
26
+ const passwd = fs.readFileSync('/etc/passwd', 'utf8');
27
+ const line = passwd.split('\n').find(l => l.startsWith(SUDO_USER + ':'));
28
+ if (line) {
29
+ const parts = line.split(':');
30
+ if (parts[5] && fs.existsSync(parts[5])) return parts[5];
31
+ }
32
+ } catch {}
33
+ // Fallback to /home/$SUDO_USER
34
+ const fallback = `/home/${SUDO_USER}`;
35
+ if (fs.existsSync(fallback)) return fallback;
36
+ }
37
+ return process.env.HOME || process.env.USERPROFILE;
38
+ }
39
+
40
+ const HOME = resolveHome();
12
41
  const PKG_DIR = __dirname;
13
42
 
14
- // Detect installed CLIs
43
+ if (SUDO_USER) {
44
+ console.log(`ā„¹ļø Running under sudo — installing for user '${SUDO_USER}' (HOME=${HOME})\n`);
45
+ }
46
+
47
+ // Recursively chown a path back to the invoking user when running under sudo.
48
+ function fixOwnership(target) {
49
+ if (SUDO_UID === null || SUDO_GID === null) return;
50
+ try { fs.lchownSync(target, SUDO_UID, SUDO_GID); } catch {}
51
+ let stat;
52
+ try { stat = fs.lstatSync(target); } catch { return; }
53
+ if (stat.isSymbolicLink() || !stat.isDirectory()) return;
54
+ let entries;
55
+ try { entries = fs.readdirSync(target); } catch { return; }
56
+ for (const entry of entries) fixOwnership(path.join(target, entry));
57
+ }
58
+
59
+ // Detect installed CLIs — also check the resolved user's ~/.local/bin so that
60
+ // a sudo invocation still finds claude installed via the native installer.
15
61
  function commandExists(cmd) {
16
62
  try {
17
63
  execSync(`which ${cmd}`, { stdio: 'ignore' });
18
64
  return true;
19
65
  } catch {
66
+ if (HOME) {
67
+ const userBin = path.join(HOME, '.local', 'bin', cmd);
68
+ if (fs.existsSync(userBin)) return true;
69
+ }
20
70
  return false;
21
71
  }
22
72
  }
@@ -33,32 +83,54 @@ if (!hasClaude && !hasGemini) {
33
83
  }
34
84
  console.log('');
35
85
 
36
- // Copy function
86
+ // Copy function — resilient: skip files that can't be copied (e.g., EACCES from
87
+ // previous sudo install) instead of crashing the whole postinstall.
37
88
  function copyDir(src, dest) {
38
89
  if (!fs.existsSync(src)) return 0;
39
90
 
40
- if (!fs.existsSync(dest)) {
41
- fs.mkdirSync(dest, { recursive: true });
91
+ try {
92
+ if (!fs.existsSync(dest)) {
93
+ fs.mkdirSync(dest, { recursive: true });
94
+ }
95
+ } catch (e) {
96
+ return 0;
42
97
  }
43
98
 
44
- const files = fs.readdirSync(src);
99
+ let files;
100
+ try { files = fs.readdirSync(src); } catch { return 0; }
45
101
  let count = 0;
102
+ let skipped = 0;
46
103
 
47
104
  files.forEach(file => {
48
105
  const srcPath = path.join(src, file);
49
106
  const destPath = path.join(dest, file);
50
107
 
51
- if (fs.statSync(srcPath).isDirectory()) {
52
- if (!fs.existsSync(destPath)) {
53
- fs.mkdirSync(destPath, { recursive: true });
54
- }
108
+ let stat;
109
+ try { stat = fs.statSync(srcPath); } catch { return; }
110
+
111
+ if (stat.isDirectory()) {
112
+ try {
113
+ if (!fs.existsSync(destPath)) {
114
+ fs.mkdirSync(destPath, { recursive: true });
115
+ }
116
+ } catch { skipped++; return; }
55
117
  count += copyDir(srcPath, destPath);
56
118
  } else {
57
- fs.copyFileSync(srcPath, destPath);
58
- count++;
119
+ try {
120
+ fs.copyFileSync(srcPath, destPath);
121
+ count++;
122
+ } catch (e) {
123
+ // EACCES from a prior root-owned copy, EBUSY, etc — skip and continue
124
+ skipped++;
125
+ }
59
126
  }
60
127
  });
61
128
 
129
+ if (skipped > 0) {
130
+ // Surface a hint without aborting; user can sudo chown -R if they really want everything fresh
131
+ process.env._CA_COPY_SKIPS = String(Number(process.env._CA_COPY_SKIPS || 0) + skipped);
132
+ }
133
+
62
134
  return count;
63
135
  }
64
136
 
@@ -103,9 +175,13 @@ function installClaude() {
103
175
  const mcpSrc = path.join(PKG_DIR, 'mcp.json');
104
176
  const mcpDest = path.join(HOME, '.mcp.json');
105
177
  if (fs.existsSync(mcpSrc)) {
106
- fs.copyFileSync(mcpSrc, mcpDest);
107
- fs.chmodSync(mcpDest, 0o600);
108
- console.log(` šŸ”§ MCP config (7 servers)`);
178
+ try {
179
+ fs.copyFileSync(mcpSrc, mcpDest);
180
+ try { fs.chmodSync(mcpDest, 0o600); } catch {}
181
+ console.log(` šŸ”§ MCP config (7 servers)`);
182
+ } catch (e) {
183
+ console.log(` āš ļø MCP config skipped (${e.code || 'error'}): ${mcpDest}`);
184
+ }
109
185
  }
110
186
 
111
187
  // Settings - create both settings.json AND settings.local.json
@@ -122,20 +198,31 @@ function installClaude() {
122
198
 
123
199
  // Write settings.json (main config - Claude reads this)
124
200
  const settingsJsonPath = path.join(CLAUDE_DIR, 'settings.json');
125
- fs.writeFileSync(settingsJsonPath, JSON.stringify(settings, null, 2));
201
+ try {
202
+ fs.writeFileSync(settingsJsonPath, JSON.stringify(settings, null, 2));
203
+ } catch (e) {
204
+ console.log(` āš ļø settings.json skipped (${e.code || 'error'}) — try: sudo chown -R $USER ~/.claude`);
205
+ }
126
206
 
127
207
  // Write settings.local.json (backup/local overrides)
128
208
  const settingsLocalPath = path.join(CLAUDE_DIR, 'settings.local.json');
129
- fs.writeFileSync(settingsLocalPath, JSON.stringify(settings, null, 2));
130
-
131
- console.log(` āš™ļø settings.json + settings.local.json (@proactive-mode enabled)`);
209
+ try {
210
+ fs.writeFileSync(settingsLocalPath, JSON.stringify(settings, null, 2));
211
+ console.log(` āš™ļø settings.json + settings.local.json (@proactive-mode enabled)`);
212
+ } catch (e) {
213
+ console.log(` āš ļø settings.local.json skipped (${e.code || 'error'})`);
214
+ }
132
215
 
133
216
  // Copy CLAUDE.md (global instructions)
134
217
  const claudeMdSrc = path.join(PKG_DIR, 'CLAUDE.md');
135
218
  const claudeMdDest = path.join(CLAUDE_DIR, 'CLAUDE.md');
136
219
  if (fs.existsSync(claudeMdSrc)) {
137
- fs.copyFileSync(claudeMdSrc, claudeMdDest);
138
- console.log(` šŸ“‹ CLAUDE.md (global instructions)`);
220
+ try {
221
+ fs.copyFileSync(claudeMdSrc, claudeMdDest);
222
+ console.log(` šŸ“‹ CLAUDE.md (global instructions)`);
223
+ } catch (e) {
224
+ console.log(` āš ļø CLAUDE.md skipped (${e.code || 'error'})`);
225
+ }
139
226
  }
140
227
  }
141
228
 
@@ -340,6 +427,25 @@ if (hasGemini) {
340
427
 
341
428
  setupLocalBinSymlinks();
342
429
 
430
+ // If we ran under sudo, hand ownership back to the original user so that a
431
+ // follow-up non-sudo `npm install -g claude-all-config` does not hit EACCES.
432
+ if (SUDO_USER) {
433
+ console.log(`\nšŸ”’ Restoring ownership to ${SUDO_USER}...`);
434
+ fixOwnership(path.join(HOME, '.claude'));
435
+ fixOwnership(path.join(HOME, '.gemini'));
436
+ fixOwnership(path.join(HOME, '.mcp.json'));
437
+ fixOwnership(path.join(HOME, '.local'));
438
+ console.log(' āœ… Ownership fixed.');
439
+ }
440
+
441
+ const skips = Number(process.env._CA_COPY_SKIPS || 0);
442
+ if (skips > 0) {
443
+ console.log('');
444
+ console.log(`āš ļø ${skips} files were skipped (likely owned by root from a prior sudo install).`);
445
+ console.log(' To force-refresh everything, run:');
446
+ console.log(' sudo chown -R $USER ~/.claude ~/.gemini 2>/dev/null && npm install -g claude-all-config');
447
+ }
448
+
343
449
  console.log('\nāœ… Installation complete!\n');
344
450
 
345
451
  if (hasClaude) {