claudeup 4.29.0 → 4.31.0

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.
@@ -126,7 +126,16 @@ async function runCheck(
126
126
  // ── install steps ───────────────────────────────────────────────────────────
127
127
 
128
128
  async function ensureToolchains(closure: ResolvedClosure, yes: boolean): Promise<void> {
129
- const toolchains = await detectToolchains(closure.bins);
129
+ // Only the binaries actually missing can need a toolchain. Checking all of
130
+ // them warns "pip isn't installed" on a machine whose pip-provided binaries
131
+ // are already on PATH — an alarming message about work that will not happen.
132
+ const missing: ResolvedBin[] = [];
133
+ for (const bin of closure.bins) {
134
+ if (!(await resolveExecutable(bin.name))) missing.push(bin);
135
+ }
136
+ if (missing.length === 0) return;
137
+
138
+ const toolchains = await detectToolchains(missing);
130
139
  for (const tc of toolchains) {
131
140
  if (tc.present) continue;
132
141
  const bootstrap = TOOLCHAIN_BOOTSTRAP[tc.name];
@@ -160,11 +169,25 @@ async function registerMarketplaces(closure: ResolvedClosure): Promise<void> {
160
169
  }
161
170
  }
162
171
 
172
+ /**
173
+ * Install the closure's plugins at PROJECT scope.
174
+ *
175
+ * A profile is a property of one repo, so its plugins must be too. At user
176
+ * scope, adopting profiles in a single repo would enable that repo's plugins in
177
+ * every other project on the machine — and since Claude Code resolves
178
+ * enabledPlugins per id with fall-through, those would stay on everywhere.
179
+ * Project scope keeps the blast radius to this repo. The plugin *cache* is
180
+ * global either way, so `profile switch` stays offline.
181
+ *
182
+ * Whatever the CLI writes into .claude/settings.json here is transient:
183
+ * materialization rewrites that file from the manifest, and activation replaces
184
+ * it with a symlink. The manifest stays authoritative.
185
+ */
163
186
  async function installPlugins(closure: ResolvedClosure): Promise<void> {
164
187
  for (const pluginId of Object.keys(closure.plugins)) {
165
188
  try {
166
189
  console.log(`+ plugin ${pluginId}`);
167
- await installPlugin(pluginId, "user");
190
+ await installPlugin(pluginId, "project");
168
191
  } catch (e) {
169
192
  console.warn(`⚠ plugin ${pluginId}: ${(e as Error).message}`);
170
193
  }
@@ -317,10 +340,18 @@ export async function runInstallCommand(
317
340
 
318
341
  // Materialize every profile (so `profile switch` needs no reinstall), then
319
342
  // activate the target (or the sole/first profile).
320
- const toMaterialize = flags.profile ? [flags.profile] : profileIds;
321
- for (const id of toMaterialize) {
343
+ //
344
+ // Every profile is materialized even when one was named: a profile arg scopes
345
+ // what gets *installed* and which becomes active, not which settings files
346
+ // exist. It also keeps the union below consistent across all of them — a
347
+ // profile left holding an older union would stop disabling plugins added
348
+ // since, silently making `switch` additive again.
349
+ const unionPluginIds = Object.keys(
350
+ (flags.profile ? await resolveAllProfiles(manifest) : closure).plugins,
351
+ );
352
+ for (const id of profileIds) {
322
353
  const profileClosure = await resolveProfile(manifest, id);
323
- await materializeProfile(id, profileClosure, projectPath);
354
+ await materializeProfile(id, profileClosure, projectPath, unionPluginIds);
324
355
  }
325
356
 
326
357
  // Keep the generated dir + active-profile symlinks out of git before
package/src/cli/router.ts CHANGED
@@ -73,25 +73,33 @@ function printHelp(version: string): void {
73
73
 
74
74
  TUI tool for managing Claude Code plugins, MCPs, and configuration.
75
75
 
76
- Usage: claudeup [options]
77
- claudeup claude [args...] Run claude with auto-update prerunner
78
- claudeup update Update claudeup to latest version
76
+ Usage: claudeup Open the interactive TUI
77
+ claudeup <command> [args]
79
78
 
80
79
  Options:
81
80
  -v, --version Show version and check for updates
82
81
  -h, --help Show this help message
83
82
  --no-refresh Skip auto-refresh of marketplaces on startup
84
83
 
85
- Commands:
84
+ Team profiles — reproduce this repo's Claude Code setup on any machine.
85
+ The manifest is .claude/profiles.json, committed. See docs/team-configuration.md.
86
+ install [profile] Install the profile's plugins, binaries, skills and env,
87
+ then activate it. No argument installs every profile.
88
+ --check Report drift only, write nothing (exits 1 in strict mode)
89
+ --yes, -y Skip the confirmation prompt
90
+ --force Discard unsynced local edits instead of refusing
91
+ profile list Show every profile; ● marks the active one
92
+ profile show <n> Print a profile's fully resolved closure
93
+ profile switch <n> Repoint the active profile — offline, no reinstall
94
+ profile sync Promote local edits back into .claude/profiles.json
95
+ doctor Check binary deps, profile symlinks, and conventions
96
+ --fix Apply the repairs it can make itself
97
+
98
+ Other commands:
86
99
  claude [args...] Check for plugin updates (1h cache), then run claude
87
100
  -f, --force Force update check (bypass 1h cache)
88
101
  update Update claudeup itself to latest version
89
102
 
90
- Experimental (in development):
91
- install [profile] Install a team profile from .claude/profiles.json
92
- profile <cmd> list | show | switch | sync team profiles
93
- doctor Diagnose & repair plugin/marketplace/profile state
94
-
95
103
  Navigation (TUI):
96
104
  [1] Plugins [4] Settings [7] Git State
97
105
  [2] Skills [5] Profiles [8] Alias