claudeup 4.19.0 → 4.23.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.
Files changed (182) hide show
  1. package/bin/claudeup.js +43 -19
  2. package/package.json +14 -19
  3. package/scripts/build-binaries.ts +77 -0
  4. package/scripts/check-optional-deps.ts +49 -0
  5. package/scripts/test-isolated.ts +43 -0
  6. package/src/__tests__/cli-router.test.ts +65 -0
  7. package/src/__tests__/conventions-integration.test.ts +745 -0
  8. package/src/__tests__/conventions-manager.test.ts +1172 -0
  9. package/src/__tests__/doctor-bins.test.ts +35 -0
  10. package/src/__tests__/doctor.test.ts +450 -0
  11. package/src/__tests__/dual-write-prevention.test.ts +14 -4
  12. package/src/__tests__/file-locking.test.ts +208 -0
  13. package/src/__tests__/gitignore-prerun.test.ts +1 -0
  14. package/src/__tests__/install-command.test.ts +81 -0
  15. package/src/__tests__/install-plan.test.ts +98 -0
  16. package/src/__tests__/manifest.test.ts +136 -0
  17. package/src/__tests__/marketplace-badge.test.ts +91 -0
  18. package/src/__tests__/marketplace-refresh.test.ts +267 -0
  19. package/src/__tests__/plugin-requires.test.ts +133 -0
  20. package/src/__tests__/plugin-setup.test.ts +7 -0
  21. package/src/__tests__/profile-command.test.ts +116 -0
  22. package/src/__tests__/profile-materializer.test.ts +82 -0
  23. package/src/__tests__/profile-sync.test.ts +136 -0
  24. package/src/__tests__/registry-version-resolution.test.ts +74 -0
  25. package/src/__tests__/resolve-executable.test.ts +42 -0
  26. package/src/__tests__/resolver.test.ts +218 -0
  27. package/src/__tests__/symlink-manager.test.ts +99 -0
  28. package/src/__tests__/toolchain.test.ts +56 -0
  29. package/src/cli/claude.ts +21 -0
  30. package/src/cli/doctor.ts +132 -0
  31. package/src/cli/install.ts +360 -0
  32. package/src/cli/profile.ts +166 -0
  33. package/src/cli/router.ts +107 -0
  34. package/src/cli/update.ts +85 -0
  35. package/src/data/cli-tools.ts +7 -7
  36. package/src/data/gitignore-defaults.ts +4 -0
  37. package/src/data/marketplaces.ts +12 -0
  38. package/src/data/predefined-profiles.ts +3 -4
  39. package/src/main.tsx +11 -154
  40. package/src/prerunner/index.ts +62 -27
  41. package/src/services/claude-cli.ts +19 -22
  42. package/src/services/claude-settings.ts +99 -26
  43. package/src/services/conventions-manager.ts +865 -0
  44. package/src/services/doctor-bins.ts +49 -0
  45. package/src/services/doctor.ts +509 -0
  46. package/src/services/install-plan.ts +107 -0
  47. package/src/services/manifest.ts +166 -0
  48. package/src/services/marketplace-refresh.ts +173 -0
  49. package/src/services/plugin-manager.ts +7 -1
  50. package/src/services/plugin-requires.ts +215 -0
  51. package/src/services/plugin-version-check.ts +9 -4
  52. package/src/services/profile-materializer.ts +61 -0
  53. package/src/services/profile-sync.ts +150 -0
  54. package/src/services/resolver.ts +293 -0
  55. package/src/services/symlink-manager.ts +146 -0
  56. package/src/services/toolchain.ts +97 -0
  57. package/src/services/version-check.ts +10 -11
  58. package/src/types/index.ts +138 -31
  59. package/src/ui/App.tsx +0 -4
  60. package/src/ui/adapters/pluginsAdapter.ts +13 -0
  61. package/src/ui/screens/CliToolsScreen.tsx +11 -3
  62. package/src/ui/screens/index.ts +0 -1
  63. package/src/ui/state/reducer.ts +0 -101
  64. package/src/ui/state/types.ts +0 -35
  65. package/src/utils/command-utils.ts +23 -0
  66. package/src/utils/file-locking.ts +144 -0
  67. package/src/data/alias-flags.js +0 -205
  68. package/src/data/cli-tools.js +0 -123
  69. package/src/data/gitignore-defaults.js +0 -24
  70. package/src/data/gitignore-reasons.js +0 -97
  71. package/src/data/gitignore-templates.js +0 -21
  72. package/src/data/marketplaces.js +0 -138
  73. package/src/data/mcp-servers.js +0 -509
  74. package/src/data/predefined-profiles.js +0 -248
  75. package/src/data/settings-catalog.js +0 -625
  76. package/src/data/skill-repos.js +0 -160
  77. package/src/data/statuslines.js +0 -159
  78. package/src/data/statuslines.ts +0 -188
  79. package/src/index.js +0 -4
  80. package/src/index.ts +0 -5
  81. package/src/main.js +0 -170
  82. package/src/prerunner/index.js +0 -252
  83. package/src/services/alias-settings.js +0 -51
  84. package/src/services/alias-shell-writer.js +0 -1018
  85. package/src/services/alias-store.js +0 -129
  86. package/src/services/claude-cli.js +0 -189
  87. package/src/services/claude-runner.js +0 -28
  88. package/src/services/claude-settings.js +0 -1223
  89. package/src/services/gitignore-detector.js +0 -155
  90. package/src/services/gitignore-fixer.js +0 -231
  91. package/src/services/gitignore-prerun.js +0 -46
  92. package/src/services/gitignore-resolver.js +0 -143
  93. package/src/services/gitignore-service.js +0 -117
  94. package/src/services/local-marketplace.js +0 -339
  95. package/src/services/marketplace-fetcher.js +0 -96
  96. package/src/services/marketplace-sync.js +0 -94
  97. package/src/services/mcp-registry.js +0 -87
  98. package/src/services/plugin-manager.js +0 -473
  99. package/src/services/plugin-mcp-config.js +0 -177
  100. package/src/services/plugin-setup.js +0 -499
  101. package/src/services/plugin-version-check.js +0 -262
  102. package/src/services/profiles.js +0 -161
  103. package/src/services/settings-manager.js +0 -243
  104. package/src/services/skills-manager.js +0 -393
  105. package/src/services/skillsmp-client.js +0 -87
  106. package/src/services/update-cache.js +0 -52
  107. package/src/services/version-check.js +0 -99
  108. package/src/types/gitignore.js +0 -6
  109. package/src/types/index.js +0 -1
  110. package/src/ui/App.js +0 -355
  111. package/src/ui/adapters/pluginsAdapter.js +0 -139
  112. package/src/ui/adapters/settingsAdapter.js +0 -111
  113. package/src/ui/adapters/skillsAdapter.js +0 -154
  114. package/src/ui/components/CategoryHeader.js +0 -9
  115. package/src/ui/components/EmptyFilterState.js +0 -4
  116. package/src/ui/components/FlagDetailEditor.js +0 -0
  117. package/src/ui/components/ScopeIndicator.js +0 -30
  118. package/src/ui/components/ScrollableList.js +0 -36
  119. package/src/ui/components/SearchInput.js +0 -19
  120. package/src/ui/components/StyledText.js +0 -39
  121. package/src/ui/components/TabBar.js +0 -19
  122. package/src/ui/components/layout/FooterHints.js +0 -29
  123. package/src/ui/components/layout/Panel.js +0 -6
  124. package/src/ui/components/layout/ProgressBar.js +0 -14
  125. package/src/ui/components/layout/ScopeTabs.js +0 -6
  126. package/src/ui/components/layout/ScreenLayout.js +0 -16
  127. package/src/ui/components/layout/index.js +0 -5
  128. package/src/ui/components/modals/ConfirmModal.js +0 -14
  129. package/src/ui/components/modals/InputModal.js +0 -5
  130. package/src/ui/components/modals/LoadingModal.js +0 -17
  131. package/src/ui/components/modals/MessageModal.js +0 -16
  132. package/src/ui/components/modals/ModalContainer.js +0 -137
  133. package/src/ui/components/modals/SelectModal.js +0 -18
  134. package/src/ui/components/modals/VersionMismatchModal.js +0 -36
  135. package/src/ui/components/modals/index.js +0 -6
  136. package/src/ui/components/primitives/ActionHints.js +0 -13
  137. package/src/ui/components/primitives/DetailSection.js +0 -7
  138. package/src/ui/components/primitives/KeyValueLine.js +0 -8
  139. package/src/ui/components/primitives/ListCategoryRow.js +0 -8
  140. package/src/ui/components/primitives/MetaText.js +0 -8
  141. package/src/ui/components/primitives/ScopeDetail.js +0 -32
  142. package/src/ui/components/primitives/ScopeSquares.js +0 -11
  143. package/src/ui/components/primitives/SelectableRow.js +0 -5
  144. package/src/ui/components/primitives/index.js +0 -8
  145. package/src/ui/hooks/index.js +0 -4
  146. package/src/ui/hooks/useAsyncData.js +0 -77
  147. package/src/ui/hooks/useGitignoreModal.js +0 -74
  148. package/src/ui/hooks/useKeyboard.js +0 -13
  149. package/src/ui/hooks/useKeyboardHandler.js +0 -39
  150. package/src/ui/hooks/useMismatchModal.js +0 -77
  151. package/src/ui/registry.js +0 -1
  152. package/src/ui/renderers/cliToolRenderers.js +0 -54
  153. package/src/ui/renderers/gitignoreRenderers.js +0 -46
  154. package/src/ui/renderers/mcpRenderers.js +0 -26
  155. package/src/ui/renderers/pluginRenderers.js +0 -124
  156. package/src/ui/renderers/profileRenderers.js +0 -177
  157. package/src/ui/renderers/settingsRenderers.js +0 -73
  158. package/src/ui/renderers/skillRenderers.js +0 -138
  159. package/src/ui/screens/AliasScreen.js +0 -1111
  160. package/src/ui/screens/CliToolsScreen.js +0 -338
  161. package/src/ui/screens/EnvVarsScreen.js +0 -152
  162. package/src/ui/screens/GitignoreScreen.js +0 -328
  163. package/src/ui/screens/McpRegistryScreen.js +0 -238
  164. package/src/ui/screens/McpScreen.js +0 -176
  165. package/src/ui/screens/ModelSelectorScreen.js +0 -296
  166. package/src/ui/screens/ModelSelectorScreen.tsx +0 -444
  167. package/src/ui/screens/PluginsScreen.js +0 -769
  168. package/src/ui/screens/ProfilesScreen.js +0 -298
  169. package/src/ui/screens/SkillsScreen.js +0 -549
  170. package/src/ui/screens/StatusLineScreen.js +0 -206
  171. package/src/ui/screens/StatusLineScreen.tsx +0 -416
  172. package/src/ui/screens/index.js +0 -10
  173. package/src/ui/state/AnimationContext.js +0 -34
  174. package/src/ui/state/AppContext.js +0 -162
  175. package/src/ui/state/DimensionsContext.js +0 -71
  176. package/src/ui/state/reducer.js +0 -547
  177. package/src/ui/state/types.js +0 -1
  178. package/src/ui/theme.js +0 -47
  179. package/src/utils/clipboard.js +0 -56
  180. package/src/utils/command-utils.js +0 -19
  181. package/src/utils/fuzzy-search.js +0 -101
  182. package/src/utils/string-utils.js +0 -62
@@ -1,252 +0,0 @@
1
- import fs from "fs-extra";
2
- import path from "node:path";
3
- import os from "node:os";
4
- import { UpdateCache } from "../services/update-cache.js";
5
- import { getAvailablePlugins, clearMarketplaceCache, } from "../services/plugin-manager.js";
6
- import { runClaude } from "../services/claude-runner.js";
7
- import { recoverMarketplaceSettings, migrateMarketplaceRename, cleanupExtraKnownMarketplaces, readGlobalSettings, writeGlobalSettings, saveGlobalInstalledPluginVersion, gapFillInstalledPluginVersions, } from "../services/claude-settings.js";
8
- import { checkPluginVersionMismatches, formatMismatchWarning, } from "../services/plugin-version-check.js";
9
- import { updatePlugin, isClaudeAvailable } from "../services/claude-cli.js";
10
- import { autoAddMissingMarketplaces } from "../services/marketplace-sync.js";
11
- import { checkGitignore, formatPrerunWarning as formatGitignoreWarning, } from "../services/gitignore-prerun.js";
12
- const CONTINUITY_PLUGIN_SENTINEL = "tmux-claude-continuity";
13
- const CONTINUITY_PLUGIN_SCRIPT = path.join(os.homedir(), ".tmux", "plugins", "tmux-claude-continuity", "scripts", "on_session_start.sh");
14
- /**
15
- * Ensure tmux-claude-continuity Claude Code hooks are present in global settings.
16
- * If the tmux plugin is installed but the hooks are missing, they are appended.
17
- * Returns a description of what was added, or null if nothing changed.
18
- */
19
- async function ensureTmuxContinuityHooks() {
20
- // Plugin not installed — nothing to do
21
- if (!(await fs.pathExists(CONTINUITY_PLUGIN_SCRIPT))) {
22
- return null;
23
- }
24
- const settings = await readGlobalSettings();
25
- // Check if hooks are already configured by looking for the sentinel string
26
- const existingHooks = settings.hooks ?? {};
27
- for (const groups of Object.values(existingHooks)) {
28
- for (const group of groups) {
29
- for (const hook of group.hooks) {
30
- if (hook.command.includes(CONTINUITY_PLUGIN_SENTINEL)) {
31
- return null; // Already configured
32
- }
33
- }
34
- }
35
- }
36
- // Append hooks, preserving any existing entries in SessionStart and Stop
37
- const sessionStartGroups = existingHooks["SessionStart"] ?? [];
38
- const stopGroups = existingHooks["Stop"] ?? [];
39
- sessionStartGroups.push({
40
- hooks: [
41
- {
42
- type: "command",
43
- command: "bash ~/.tmux/plugins/tmux-claude-continuity/scripts/on_session_start.sh",
44
- },
45
- ],
46
- });
47
- stopGroups.push({
48
- hooks: [
49
- {
50
- type: "command",
51
- command: "bash ~/.tmux/plugins/tmux-claude-continuity/scripts/on_stop.sh",
52
- },
53
- ],
54
- });
55
- settings.hooks = {
56
- ...existingHooks,
57
- SessionStart: sessionStartGroups,
58
- Stop: stopGroups,
59
- };
60
- await writeGlobalSettings(settings);
61
- return "SessionStart + Stop hooks";
62
- }
63
- /**
64
- * Prerun orchestration: Check for updates, apply them, then run claude
65
- * @param claudeArgs - Arguments to pass to claude CLI
66
- * @param options - Prerun options (force, etc.)
67
- * @returns Exit code from claude process
68
- */
69
- export async function prerunClaude(claudeArgs, options = {}) {
70
- const cache = new UpdateCache();
71
- try {
72
- // STEP 0: Migrate old marketplace names → magus (idempotent, no-ops if already migrated)
73
- const migration = await migrateMarketplaceRename();
74
- const migTotal = migration.projectMigrated +
75
- migration.globalMigrated +
76
- migration.localMigrated +
77
- migration.registryMigrated +
78
- (migration.knownMarketplacesMigrated ? 1 : 0);
79
- if (migTotal > 0) {
80
- console.log(`✓ Migrated ${migTotal} plugin reference(s) → magus`);
81
- }
82
- // STEP 0.5: Auto-add missing marketplaces
83
- // When plugins reference a marketplace that's not installed locally
84
- // (e.g., settings synced from another machine or committed by a
85
- // teammate in .claude/settings.json), add it automatically. Pass
86
- // process.cwd() so project and local enabledPlugins are scanned —
87
- // otherwise a freshly cloned repo whose only @magus references live
88
- // in project settings looks "clean" from the global-scope view.
89
- if (await isClaudeAvailable()) {
90
- const addedMarketplaces = await autoAddMissingMarketplaces(process.cwd());
91
- if (addedMarketplaces.length > 0) {
92
- console.log(`✓ Auto-added marketplace(s): ${addedMarketplaces.join(", ")}`);
93
- }
94
- }
95
- // STEP 0.55: Clean up extraKnownMarketplaces entries that are now in known_marketplaces.json
96
- // extraKnownMarketplaces was the old install mechanism; the official way is known_marketplaces.json
97
- try {
98
- const cleaned = await cleanupExtraKnownMarketplaces();
99
- if (cleaned.length > 0) {
100
- console.log(`✓ Cleaned up legacy extraKnownMarketplaces: ${cleaned.join(", ")}`);
101
- }
102
- }
103
- catch {
104
- // Non-fatal: cleanup is best-effort
105
- }
106
- // STEP 0.6: Ensure tmux-claude-continuity hooks are configured
107
- const addedHooks = await ensureTmuxContinuityHooks();
108
- if (addedHooks) {
109
- console.log(`✓ Added tmux-claude-continuity hooks to ~/.claude/settings.json`);
110
- }
111
- // STEP 0.7: Gap-fill installedPluginVersions across scopes
112
- // When a plugin is updated at project/local scope, global may lag behind.
113
- // Sync the highest version from any scope up to global so Claude Code
114
- // resolves the latest cached plugin paths at session startup.
115
- try {
116
- const gapFilled = await gapFillInstalledPluginVersions(process.cwd());
117
- if (gapFilled.length > 0) {
118
- console.log(`✓ Synced ${gapFilled.length} plugin version(s) to global:`);
119
- for (const { pluginId, oldVersion, newVersion } of gapFilled) {
120
- console.log(` - ${pluginId}: ${oldVersion} → ${newVersion}`);
121
- }
122
- }
123
- }
124
- catch {
125
- // Non-fatal: gap-fill is best-effort
126
- }
127
- // STEP 0.8: Detect plugin version mismatches caused by the [0] index bug
128
- // Claude Code's pluginLoader.ts takes entries[0] regardless of project,
129
- // so if another project installed a different version first, this project
130
- // will silently load the wrong version.
131
- try {
132
- const cwd = process.cwd();
133
- const mismatches = await checkPluginVersionMismatches(cwd);
134
- if (mismatches.length > 0) {
135
- console.warn("");
136
- console.warn(formatMismatchWarning(mismatches));
137
- console.warn("");
138
- }
139
- }
140
- catch {
141
- // Non-fatal: mismatch detection is best-effort
142
- }
143
- // STEP 0.9: Detect gitignore manifest violations. Warn-only here — the
144
- // fix dialog lives in the TUI Gitignore tab, mirroring the plugin
145
- // version-mismatch flow (since destructive fixes need user confirmation).
146
- try {
147
- const cwd = process.cwd();
148
- const result = await checkGitignore(cwd);
149
- const warning = formatGitignoreWarning(result);
150
- if (warning) {
151
- console.warn("");
152
- console.warn(warning);
153
- console.warn("");
154
- }
155
- }
156
- catch {
157
- // Non-fatal: gitignore detection is best-effort
158
- }
159
- // STEP 1: Check if we should update (time-based cache, or forced)
160
- const shouldUpdate = options.force || (await cache.shouldCheckForUpdates());
161
- if (options.force) {
162
- console.log("⟳ Forcing plugin update check...");
163
- }
164
- if (shouldUpdate) {
165
- // STEP 1.5: Recover marketplace settings (enable autoUpdate, remove stale entries)
166
- const recovery = await recoverMarketplaceSettings();
167
- if (recovery.enabledAutoUpdate.length > 0) {
168
- console.log(`✓ Enabled auto-update for: ${recovery.enabledAutoUpdate.join(", ")}`);
169
- }
170
- if (recovery.removed.length > 0) {
171
- console.log(`✓ Removed stale marketplaces: ${recovery.removed.join(", ")}`);
172
- }
173
- if (recovery.reregistered.length > 0) {
174
- console.log(`✓ Re-registered as GitHub source: ${recovery.reregistered.join(", ")}`);
175
- // Trigger marketplace update for re-registered entries so the local
176
- // clone gets refreshed with the latest plugins.
177
- const cliAvailable = await isClaudeAvailable();
178
- if (cliAvailable) {
179
- const { updateMarketplace } = await import("../services/claude-cli.js");
180
- for (const mpName of recovery.reregistered) {
181
- try {
182
- await updateMarketplace(mpName);
183
- console.log(`✓ Updated marketplace: ${mpName}`);
184
- }
185
- catch (error) {
186
- console.warn(`⚠ Failed to update marketplace ${mpName}:`, error instanceof Error ? error.message : "Unknown error");
187
- }
188
- }
189
- }
190
- }
191
- // STEP 2: Clear cache to force fresh plugin info
192
- clearMarketplaceCache();
193
- // STEP 3: Get updated plugin info (to detect versions)
194
- const plugins = await getAvailablePlugins();
195
- // STEP 4: Auto-update enabled plugins via claude CLI
196
- const autoUpdatedPlugins = [];
197
- const cliAvailable = await isClaudeAvailable();
198
- for (const plugin of plugins) {
199
- // Only update if:
200
- // 1. Plugin is enabled
201
- // 2. Plugin has an update available
202
- // 3. Plugin has both installedVersion and version (newVersion)
203
- if (plugin.enabled &&
204
- plugin.hasUpdate &&
205
- plugin.installedVersion &&
206
- plugin.version) {
207
- try {
208
- if (!cliAvailable) {
209
- // CLI unavailable — skip update entirely, don't write phantom state
210
- continue;
211
- }
212
- await updatePlugin(plugin.id, "user");
213
- // CLI does NOT update installedPluginVersions — save it ourselves
214
- await saveGlobalInstalledPluginVersion(plugin.id, plugin.version);
215
- autoUpdatedPlugins.push({
216
- pluginId: plugin.id,
217
- oldVersion: plugin.installedVersion,
218
- newVersion: plugin.version,
219
- });
220
- }
221
- catch (error) {
222
- // Non-fatal: Log warning and continue
223
- console.warn(`⚠ Failed to auto-update ${plugin.id}:`, error instanceof Error ? error.message : "Unknown error");
224
- }
225
- }
226
- }
227
- // STEP 5: Save cache
228
- await cache.saveCheck({
229
- lastUpdateCheck: new Date().toISOString(),
230
- lastUpdateResult: {
231
- updated: [],
232
- failed: [],
233
- autoUpdated: autoUpdatedPlugins,
234
- },
235
- });
236
- // STEP 6: Display auto-update summary
237
- if (autoUpdatedPlugins.length > 0) {
238
- console.log(`✓ Auto-updated ${autoUpdatedPlugins.length} plugin(s):`);
239
- for (const { pluginId, oldVersion, newVersion } of autoUpdatedPlugins) {
240
- console.log(` - ${pluginId}: ${oldVersion} → ${newVersion}`);
241
- }
242
- }
243
- }
244
- // If !shouldUpdate → silent (no output)
245
- }
246
- catch (error) {
247
- // Non-fatal errors - warn and continue
248
- console.warn("Warning: Plugin update check failed:", error instanceof Error ? error.message : "Unknown error");
249
- }
250
- // STEP 8: Always run claude (even if update failed)
251
- return runClaude(claudeArgs);
252
- }
@@ -1,51 +0,0 @@
1
- /**
2
- * Read/write the managed-alias name in Claude Code's `settings.json`.
3
- *
4
- * The alias is computer-wide (one name per user), so it lives in
5
- * `~/.claude/settings.json` only — no project scope. We store it under a
6
- * `claudeup` namespace to avoid colliding with Claude Code's own keys:
7
- *
8
- * {
9
- * "model": "sonnet",
10
- * "claudeup": {
11
- * "aliasName": "c"
12
- * }
13
- * }
14
- *
15
- * Flag values are NOT stored here. They live in the shell rc file's managed
16
- * block and are read on screen mount via `parseAliasFromRc`.
17
- */
18
- import { readGlobalSettings, writeGlobalSettings, } from "./claude-settings.js";
19
- import { DEFAULT_ALIAS_NAME, validateAliasName } from "./alias-store.js";
20
- /**
21
- * Load the user's preferred alias name from `~/.claude/settings.json`.
22
- * Returns the default `"c"` when the key is absent or invalid.
23
- */
24
- export async function loadAliasName() {
25
- const settings = (await readGlobalSettings());
26
- const stored = settings.claudeup?.aliasName;
27
- if (typeof stored !== "string")
28
- return DEFAULT_ALIAS_NAME;
29
- if (validateAliasName(stored) !== null)
30
- return DEFAULT_ALIAS_NAME;
31
- return stored;
32
- }
33
- /**
34
- * Save the alias name to `~/.claude/settings.json`, preserving all other
35
- * keys (including unrelated Claude Code settings). No-op without throwing
36
- * if the name fails validation — caller should validate first.
37
- */
38
- export async function saveAliasName(name) {
39
- if (validateAliasName(name) !== null)
40
- return;
41
- const settings = (await readGlobalSettings());
42
- const next = {
43
- ...settings,
44
- claudeup: {
45
- ...(settings.claudeup ?? {}),
46
- aliasName: name,
47
- },
48
- };
49
- // claude-settings.writeGlobalSettings accepts the same shape we received.
50
- await writeGlobalSettings(next);
51
- }