claudeup 4.19.0 → 4.22.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.
- package/bin/claudeup.js +43 -19
- package/package.json +14 -19
- package/scripts/build-binaries.ts +77 -0
- package/scripts/check-optional-deps.ts +49 -0
- package/scripts/test-isolated.ts +43 -0
- package/src/__tests__/cli-router.test.ts +65 -0
- package/src/__tests__/conventions-integration.test.ts +745 -0
- package/src/__tests__/conventions-manager.test.ts +1172 -0
- package/src/__tests__/doctor-bins.test.ts +35 -0
- package/src/__tests__/doctor.test.ts +450 -0
- package/src/__tests__/dual-write-prevention.test.ts +14 -4
- package/src/__tests__/file-locking.test.ts +208 -0
- package/src/__tests__/gitignore-prerun.test.ts +1 -0
- package/src/__tests__/install-command.test.ts +81 -0
- package/src/__tests__/install-plan.test.ts +98 -0
- package/src/__tests__/manifest.test.ts +136 -0
- package/src/__tests__/marketplace-refresh.test.ts +267 -0
- package/src/__tests__/plugin-requires.test.ts +133 -0
- package/src/__tests__/plugin-setup.test.ts +7 -0
- package/src/__tests__/profile-command.test.ts +116 -0
- package/src/__tests__/profile-materializer.test.ts +82 -0
- package/src/__tests__/profile-sync.test.ts +136 -0
- package/src/__tests__/registry-version-resolution.test.ts +74 -0
- package/src/__tests__/resolve-executable.test.ts +42 -0
- package/src/__tests__/resolver.test.ts +218 -0
- package/src/__tests__/symlink-manager.test.ts +99 -0
- package/src/__tests__/toolchain.test.ts +56 -0
- package/src/cli/claude.ts +21 -0
- package/src/cli/doctor.ts +132 -0
- package/src/cli/install.ts +360 -0
- package/src/cli/profile.ts +166 -0
- package/src/cli/router.ts +107 -0
- package/src/cli/update.ts +85 -0
- package/src/data/cli-tools.ts +7 -7
- package/src/data/gitignore-defaults.ts +4 -0
- package/src/data/marketplaces.ts +12 -0
- package/src/data/predefined-profiles.ts +3 -4
- package/src/main.tsx +11 -154
- package/src/prerunner/index.ts +62 -27
- package/src/services/claude-cli.ts +19 -22
- package/src/services/claude-settings.ts +99 -26
- package/src/services/conventions-manager.ts +865 -0
- package/src/services/doctor-bins.ts +49 -0
- package/src/services/doctor.ts +509 -0
- package/src/services/install-plan.ts +107 -0
- package/src/services/manifest.ts +166 -0
- package/src/services/marketplace-refresh.ts +173 -0
- package/src/services/plugin-manager.ts +7 -1
- package/src/services/plugin-requires.ts +215 -0
- package/src/services/plugin-version-check.ts +9 -4
- package/src/services/profile-materializer.ts +61 -0
- package/src/services/profile-sync.ts +150 -0
- package/src/services/resolver.ts +293 -0
- package/src/services/symlink-manager.ts +146 -0
- package/src/services/toolchain.ts +97 -0
- package/src/services/version-check.ts +10 -11
- package/src/types/index.ts +138 -31
- package/src/ui/App.tsx +0 -4
- package/src/ui/screens/CliToolsScreen.tsx +11 -3
- package/src/ui/screens/index.ts +0 -1
- package/src/ui/state/reducer.ts +0 -101
- package/src/ui/state/types.ts +0 -35
- package/src/utils/command-utils.ts +23 -0
- package/src/utils/file-locking.ts +144 -0
- package/src/data/alias-flags.js +0 -205
- package/src/data/cli-tools.js +0 -123
- package/src/data/gitignore-defaults.js +0 -24
- package/src/data/gitignore-reasons.js +0 -97
- package/src/data/gitignore-templates.js +0 -21
- package/src/data/marketplaces.js +0 -138
- package/src/data/mcp-servers.js +0 -509
- package/src/data/predefined-profiles.js +0 -248
- package/src/data/settings-catalog.js +0 -625
- package/src/data/skill-repos.js +0 -160
- package/src/data/statuslines.js +0 -159
- package/src/data/statuslines.ts +0 -188
- package/src/index.js +0 -4
- package/src/index.ts +0 -5
- package/src/main.js +0 -170
- package/src/prerunner/index.js +0 -252
- package/src/services/alias-settings.js +0 -51
- package/src/services/alias-shell-writer.js +0 -1018
- package/src/services/alias-store.js +0 -129
- package/src/services/claude-cli.js +0 -189
- package/src/services/claude-runner.js +0 -28
- package/src/services/claude-settings.js +0 -1223
- package/src/services/gitignore-detector.js +0 -155
- package/src/services/gitignore-fixer.js +0 -231
- package/src/services/gitignore-prerun.js +0 -46
- package/src/services/gitignore-resolver.js +0 -143
- package/src/services/gitignore-service.js +0 -117
- package/src/services/local-marketplace.js +0 -339
- package/src/services/marketplace-fetcher.js +0 -96
- package/src/services/marketplace-sync.js +0 -94
- package/src/services/mcp-registry.js +0 -87
- package/src/services/plugin-manager.js +0 -473
- package/src/services/plugin-mcp-config.js +0 -177
- package/src/services/plugin-setup.js +0 -499
- package/src/services/plugin-version-check.js +0 -262
- package/src/services/profiles.js +0 -161
- package/src/services/settings-manager.js +0 -243
- package/src/services/skills-manager.js +0 -393
- package/src/services/skillsmp-client.js +0 -87
- package/src/services/update-cache.js +0 -52
- package/src/services/version-check.js +0 -99
- package/src/types/gitignore.js +0 -6
- package/src/types/index.js +0 -1
- package/src/ui/App.js +0 -355
- package/src/ui/adapters/pluginsAdapter.js +0 -139
- package/src/ui/adapters/settingsAdapter.js +0 -111
- package/src/ui/adapters/skillsAdapter.js +0 -154
- package/src/ui/components/CategoryHeader.js +0 -9
- package/src/ui/components/EmptyFilterState.js +0 -4
- package/src/ui/components/FlagDetailEditor.js +0 -0
- package/src/ui/components/ScopeIndicator.js +0 -30
- package/src/ui/components/ScrollableList.js +0 -36
- package/src/ui/components/SearchInput.js +0 -19
- package/src/ui/components/StyledText.js +0 -39
- package/src/ui/components/TabBar.js +0 -19
- package/src/ui/components/layout/FooterHints.js +0 -29
- package/src/ui/components/layout/Panel.js +0 -6
- package/src/ui/components/layout/ProgressBar.js +0 -14
- package/src/ui/components/layout/ScopeTabs.js +0 -6
- package/src/ui/components/layout/ScreenLayout.js +0 -16
- package/src/ui/components/layout/index.js +0 -5
- package/src/ui/components/modals/ConfirmModal.js +0 -14
- package/src/ui/components/modals/InputModal.js +0 -5
- package/src/ui/components/modals/LoadingModal.js +0 -17
- package/src/ui/components/modals/MessageModal.js +0 -16
- package/src/ui/components/modals/ModalContainer.js +0 -137
- package/src/ui/components/modals/SelectModal.js +0 -18
- package/src/ui/components/modals/VersionMismatchModal.js +0 -36
- package/src/ui/components/modals/index.js +0 -6
- package/src/ui/components/primitives/ActionHints.js +0 -13
- package/src/ui/components/primitives/DetailSection.js +0 -7
- package/src/ui/components/primitives/KeyValueLine.js +0 -8
- package/src/ui/components/primitives/ListCategoryRow.js +0 -8
- package/src/ui/components/primitives/MetaText.js +0 -8
- package/src/ui/components/primitives/ScopeDetail.js +0 -32
- package/src/ui/components/primitives/ScopeSquares.js +0 -11
- package/src/ui/components/primitives/SelectableRow.js +0 -5
- package/src/ui/components/primitives/index.js +0 -8
- package/src/ui/hooks/index.js +0 -4
- package/src/ui/hooks/useAsyncData.js +0 -77
- package/src/ui/hooks/useGitignoreModal.js +0 -74
- package/src/ui/hooks/useKeyboard.js +0 -13
- package/src/ui/hooks/useKeyboardHandler.js +0 -39
- package/src/ui/hooks/useMismatchModal.js +0 -77
- package/src/ui/registry.js +0 -1
- package/src/ui/renderers/cliToolRenderers.js +0 -54
- package/src/ui/renderers/gitignoreRenderers.js +0 -46
- package/src/ui/renderers/mcpRenderers.js +0 -26
- package/src/ui/renderers/pluginRenderers.js +0 -124
- package/src/ui/renderers/profileRenderers.js +0 -177
- package/src/ui/renderers/settingsRenderers.js +0 -73
- package/src/ui/renderers/skillRenderers.js +0 -138
- package/src/ui/screens/AliasScreen.js +0 -1111
- package/src/ui/screens/CliToolsScreen.js +0 -338
- package/src/ui/screens/EnvVarsScreen.js +0 -152
- package/src/ui/screens/GitignoreScreen.js +0 -328
- package/src/ui/screens/McpRegistryScreen.js +0 -238
- package/src/ui/screens/McpScreen.js +0 -176
- package/src/ui/screens/ModelSelectorScreen.js +0 -296
- package/src/ui/screens/ModelSelectorScreen.tsx +0 -444
- package/src/ui/screens/PluginsScreen.js +0 -769
- package/src/ui/screens/ProfilesScreen.js +0 -298
- package/src/ui/screens/SkillsScreen.js +0 -549
- package/src/ui/screens/StatusLineScreen.js +0 -206
- package/src/ui/screens/StatusLineScreen.tsx +0 -416
- package/src/ui/screens/index.js +0 -10
- package/src/ui/state/AnimationContext.js +0 -34
- package/src/ui/state/AppContext.js +0 -162
- package/src/ui/state/DimensionsContext.js +0 -71
- package/src/ui/state/reducer.js +0 -547
- package/src/ui/state/types.js +0 -1
- package/src/ui/theme.js +0 -47
- package/src/utils/clipboard.js +0 -56
- package/src/utils/command-utils.js +0 -19
- package/src/utils/fuzzy-search.js +0 -101
- package/src/utils/string-utils.js +0 -62
|
@@ -1,298 +0,0 @@
|
|
|
1
|
-
import { jsxs as _jsxs, jsx as _jsx } from "@opentui/react/jsx-runtime";
|
|
2
|
-
import { useEffect, useCallback } from "react";
|
|
3
|
-
import { useApp, useModal } from "../state/AppContext.js";
|
|
4
|
-
import { useDimensions } from "../state/DimensionsContext.js";
|
|
5
|
-
import { useKeyboard } from "../hooks/useKeyboard.js";
|
|
6
|
-
import { ScreenLayout } from "../components/layout/index.js";
|
|
7
|
-
import { listProfiles, applyProfile, renameProfile, deleteProfile, exportProfileToJson, importProfileFromJson, } from "../../services/profiles.js";
|
|
8
|
-
import { readSettings, writeSettings, } from "../../services/claude-settings.js";
|
|
9
|
-
import { writeClipboard, readClipboard, ClipboardUnavailableError, } from "../../utils/clipboard.js";
|
|
10
|
-
import { PREDEFINED_PROFILES, } from "../../data/predefined-profiles.js";
|
|
11
|
-
import { buildProfileListItems, renderProfileRow, renderProfileDetail, } from "../renderers/profileRenderers.js";
|
|
12
|
-
import { ScrollableList } from "../components/ScrollableList.js";
|
|
13
|
-
export function ProfilesScreen() {
|
|
14
|
-
const { state, dispatch } = useApp();
|
|
15
|
-
const { profiles: profilesState } = state;
|
|
16
|
-
const modal = useModal();
|
|
17
|
-
const dimensions = useDimensions();
|
|
18
|
-
const fetchData = useCallback(async () => {
|
|
19
|
-
dispatch({ type: "PROFILES_DATA_LOADING" });
|
|
20
|
-
try {
|
|
21
|
-
const entries = await listProfiles(state.projectPath);
|
|
22
|
-
dispatch({ type: "PROFILES_DATA_SUCCESS", profiles: entries });
|
|
23
|
-
}
|
|
24
|
-
catch (error) {
|
|
25
|
-
dispatch({
|
|
26
|
-
type: "PROFILES_DATA_ERROR",
|
|
27
|
-
error: error instanceof Error ? error : new Error(String(error)),
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
}, [dispatch, state.projectPath]);
|
|
31
|
-
useEffect(() => {
|
|
32
|
-
fetchData();
|
|
33
|
-
}, [fetchData, state.dataRefreshVersion]);
|
|
34
|
-
const profileList = profilesState.profiles.status === "success"
|
|
35
|
-
? profilesState.profiles.data
|
|
36
|
-
: [];
|
|
37
|
-
const allItems = buildProfileListItems(profileList, PREDEFINED_PROFILES);
|
|
38
|
-
const selectedItem = allItems[profilesState.selectedIndex];
|
|
39
|
-
const isNavigable = (item) => item.kind !== "header";
|
|
40
|
-
useKeyboard((event) => {
|
|
41
|
-
if (state.isSearching || state.modal)
|
|
42
|
-
return;
|
|
43
|
-
if (event.name === "up" || event.name === "k") {
|
|
44
|
-
let newIndex = profilesState.selectedIndex - 1;
|
|
45
|
-
while (newIndex > 0 && !isNavigable(allItems[newIndex])) {
|
|
46
|
-
newIndex--;
|
|
47
|
-
}
|
|
48
|
-
if (newIndex >= 0 && isNavigable(allItems[newIndex])) {
|
|
49
|
-
dispatch({ type: "PROFILES_SELECT", index: newIndex });
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
else if (event.name === "down" || event.name === "j") {
|
|
53
|
-
let newIndex = profilesState.selectedIndex + 1;
|
|
54
|
-
while (newIndex < allItems.length - 1 &&
|
|
55
|
-
!isNavigable(allItems[newIndex])) {
|
|
56
|
-
newIndex++;
|
|
57
|
-
}
|
|
58
|
-
if (newIndex < allItems.length && isNavigable(allItems[newIndex])) {
|
|
59
|
-
dispatch({ type: "PROFILES_SELECT", index: newIndex });
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
else if (event.name === "enter" || event.name === "a") {
|
|
63
|
-
if (selectedItem?.kind === "predefined") {
|
|
64
|
-
void handleApplyPredefined(selectedItem.profile);
|
|
65
|
-
}
|
|
66
|
-
else if (selectedItem?.kind === "saved") {
|
|
67
|
-
void handleApply();
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
else if (event.name === "r") {
|
|
71
|
-
if (selectedItem?.kind === "saved")
|
|
72
|
-
void handleRename();
|
|
73
|
-
}
|
|
74
|
-
else if (event.name === "d") {
|
|
75
|
-
if (selectedItem?.kind === "saved")
|
|
76
|
-
void handleDelete();
|
|
77
|
-
}
|
|
78
|
-
else if (event.name === "c") {
|
|
79
|
-
if (selectedItem?.kind === "saved")
|
|
80
|
-
void handleCopy();
|
|
81
|
-
}
|
|
82
|
-
else if (event.name === "i") {
|
|
83
|
-
void handleImport();
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
// ─── Actions ──────────────────────────────────────────────────────────────
|
|
87
|
-
const handleApplyPredefined = async (profile) => {
|
|
88
|
-
const allPlugins = [
|
|
89
|
-
...profile.magusPlugins.map((p) => `${p}@magus`),
|
|
90
|
-
...profile.anthropicPlugins.map((p) => `${p}@claude-plugins-official`),
|
|
91
|
-
];
|
|
92
|
-
const settingsCount = Object.keys(profile.settings).length;
|
|
93
|
-
const confirmed = await modal.confirm(`Apply ${profile.name}?`, `This will add ${allPlugins.length} plugins, ${profile.skills.length} skills, and update ${settingsCount} settings.\n\nSettings are merged additively — existing values are kept.`);
|
|
94
|
-
if (!confirmed)
|
|
95
|
-
return;
|
|
96
|
-
modal.loading(`Applying "${profile.name}"...`);
|
|
97
|
-
try {
|
|
98
|
-
const settings = await readSettings(state.projectPath);
|
|
99
|
-
settings.enabledPlugins = settings.enabledPlugins ?? {};
|
|
100
|
-
for (const plugin of allPlugins) {
|
|
101
|
-
if (!settings.enabledPlugins[plugin]) {
|
|
102
|
-
settings.enabledPlugins[plugin] = true;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
for (const [key, value] of Object.entries(profile.settings)) {
|
|
106
|
-
if (key === "env") {
|
|
107
|
-
const envMap = value;
|
|
108
|
-
const existing = settings;
|
|
109
|
-
const existingEnv = existing["env"] ?? {};
|
|
110
|
-
for (const [envKey, envVal] of Object.entries(envMap)) {
|
|
111
|
-
if (!existingEnv[envKey]) {
|
|
112
|
-
existingEnv[envKey] = envVal;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
settings["env"] = existingEnv;
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
const settingsMap = settings;
|
|
119
|
-
if (settingsMap[key] === undefined) {
|
|
120
|
-
settingsMap[key] = value;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
await writeSettings(settings, state.projectPath);
|
|
125
|
-
modal.hideModal();
|
|
126
|
-
dispatch({ type: "DATA_REFRESH_COMPLETE" });
|
|
127
|
-
await modal.message("Applied", `Profile "${profile.name}" merged into project settings.`, "success");
|
|
128
|
-
}
|
|
129
|
-
catch (error) {
|
|
130
|
-
modal.hideModal();
|
|
131
|
-
await modal.message("Error", `Failed to apply profile: ${error}`, "error");
|
|
132
|
-
}
|
|
133
|
-
};
|
|
134
|
-
const handleApply = async () => {
|
|
135
|
-
if (selectedItem?.kind !== "saved")
|
|
136
|
-
return;
|
|
137
|
-
const selectedProfile = selectedItem.entry;
|
|
138
|
-
const scopeChoice = await modal.select("Apply Profile", `Apply "${selectedProfile.name}" to which scope?`, [
|
|
139
|
-
{ label: "User — ~/.claude/settings.json (global)", value: "user" },
|
|
140
|
-
{
|
|
141
|
-
label: "Project — .claude/settings.json (this project)",
|
|
142
|
-
value: "project",
|
|
143
|
-
},
|
|
144
|
-
]);
|
|
145
|
-
if (scopeChoice === null)
|
|
146
|
-
return;
|
|
147
|
-
const targetScope = scopeChoice;
|
|
148
|
-
const scopeLabel = targetScope === "user"
|
|
149
|
-
? "~/.claude/settings.json"
|
|
150
|
-
: ".claude/settings.json";
|
|
151
|
-
const pluginCount = Object.keys(selectedProfile.plugins).length;
|
|
152
|
-
const emptyWarning = pluginCount === 0
|
|
153
|
-
? "\n\nWARNING: This profile has no plugins — applying will disable all plugins."
|
|
154
|
-
: "";
|
|
155
|
-
const confirmed = await modal.confirm("Confirm Apply", `This will REPLACE all enabledPlugins in ${scopeLabel}.${emptyWarning}\n\nContinue?`);
|
|
156
|
-
if (!confirmed)
|
|
157
|
-
return;
|
|
158
|
-
modal.loading(`Applying "${selectedProfile.name}"...`);
|
|
159
|
-
try {
|
|
160
|
-
await applyProfile(selectedProfile.id, selectedProfile.scope, targetScope, state.projectPath);
|
|
161
|
-
modal.hideModal();
|
|
162
|
-
dispatch({ type: "DATA_REFRESH_COMPLETE" });
|
|
163
|
-
await modal.message("Applied", `Profile "${selectedProfile.name}" applied to ${scopeLabel}.`, "success");
|
|
164
|
-
}
|
|
165
|
-
catch (error) {
|
|
166
|
-
modal.hideModal();
|
|
167
|
-
await modal.message("Error", `Failed to apply profile: ${error}`, "error");
|
|
168
|
-
}
|
|
169
|
-
};
|
|
170
|
-
const handleRename = async () => {
|
|
171
|
-
if (selectedItem?.kind !== "saved")
|
|
172
|
-
return;
|
|
173
|
-
const selectedProfile = selectedItem.entry;
|
|
174
|
-
const newName = await modal.input("Rename Profile", "New name:", selectedProfile.name);
|
|
175
|
-
if (newName === null || !newName.trim())
|
|
176
|
-
return;
|
|
177
|
-
modal.loading("Renaming...");
|
|
178
|
-
try {
|
|
179
|
-
await renameProfile(selectedProfile.id, newName.trim(), selectedProfile.scope, state.projectPath);
|
|
180
|
-
modal.hideModal();
|
|
181
|
-
await fetchData();
|
|
182
|
-
await modal.message("Renamed", `Profile renamed to "${newName.trim()}".`, "success");
|
|
183
|
-
}
|
|
184
|
-
catch (error) {
|
|
185
|
-
modal.hideModal();
|
|
186
|
-
await modal.message("Error", `Failed to rename: ${error}`, "error");
|
|
187
|
-
}
|
|
188
|
-
};
|
|
189
|
-
const handleDelete = async () => {
|
|
190
|
-
if (selectedItem?.kind !== "saved")
|
|
191
|
-
return;
|
|
192
|
-
const selectedProfile = selectedItem.entry;
|
|
193
|
-
const confirmed = await modal.confirm(`Delete "${selectedProfile.name}"?`, "This will permanently remove the profile.");
|
|
194
|
-
if (!confirmed)
|
|
195
|
-
return;
|
|
196
|
-
modal.loading("Deleting...");
|
|
197
|
-
try {
|
|
198
|
-
await deleteProfile(selectedProfile.id, selectedProfile.scope, state.projectPath);
|
|
199
|
-
modal.hideModal();
|
|
200
|
-
const newIndex = Math.max(0, Math.min(profilesState.selectedIndex, allItems.length - 2));
|
|
201
|
-
dispatch({ type: "PROFILES_SELECT", index: newIndex });
|
|
202
|
-
await fetchData();
|
|
203
|
-
await modal.message("Deleted", "Profile deleted.", "success");
|
|
204
|
-
}
|
|
205
|
-
catch (error) {
|
|
206
|
-
modal.hideModal();
|
|
207
|
-
await modal.message("Error", `Failed to delete: ${error}`, "error");
|
|
208
|
-
}
|
|
209
|
-
};
|
|
210
|
-
const handleCopy = async () => {
|
|
211
|
-
if (selectedItem?.kind !== "saved")
|
|
212
|
-
return;
|
|
213
|
-
const selectedProfile = selectedItem.entry;
|
|
214
|
-
modal.loading("Exporting...");
|
|
215
|
-
try {
|
|
216
|
-
const json = await exportProfileToJson(selectedProfile.id, selectedProfile.scope, state.projectPath);
|
|
217
|
-
modal.hideModal();
|
|
218
|
-
try {
|
|
219
|
-
await writeClipboard(json);
|
|
220
|
-
await modal.message("Copied", `Profile JSON copied to clipboard.\nShare it with teammates to import.`, "success");
|
|
221
|
-
}
|
|
222
|
-
catch (err) {
|
|
223
|
-
if (err instanceof ClipboardUnavailableError) {
|
|
224
|
-
await modal.message("Profile JSON (copy manually)", json, "info");
|
|
225
|
-
}
|
|
226
|
-
else {
|
|
227
|
-
throw err;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
catch (error) {
|
|
232
|
-
modal.hideModal();
|
|
233
|
-
await modal.message("Error", `Failed to export: ${error}`, "error");
|
|
234
|
-
}
|
|
235
|
-
};
|
|
236
|
-
const handleImport = async () => {
|
|
237
|
-
let json = null;
|
|
238
|
-
try {
|
|
239
|
-
json = await readClipboard();
|
|
240
|
-
}
|
|
241
|
-
catch (err) {
|
|
242
|
-
if (err instanceof ClipboardUnavailableError) {
|
|
243
|
-
json = await modal.input("Import Profile", "Paste profile JSON:");
|
|
244
|
-
}
|
|
245
|
-
else {
|
|
246
|
-
await modal.message("Error", `Clipboard error: ${err}`, "error");
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
if (json === null || !json.trim())
|
|
251
|
-
return;
|
|
252
|
-
const scopeChoice = await modal.select("Import Profile", "Save imported profile to which scope?", [
|
|
253
|
-
{ label: "User — ~/.claude/profiles.json (global)", value: "user" },
|
|
254
|
-
{
|
|
255
|
-
label: "Project — .claude/profiles.json (this project)",
|
|
256
|
-
value: "project",
|
|
257
|
-
},
|
|
258
|
-
]);
|
|
259
|
-
if (scopeChoice === null)
|
|
260
|
-
return;
|
|
261
|
-
const targetScope = scopeChoice;
|
|
262
|
-
modal.loading("Importing...");
|
|
263
|
-
try {
|
|
264
|
-
const id = await importProfileFromJson(json, targetScope, state.projectPath);
|
|
265
|
-
modal.hideModal();
|
|
266
|
-
await fetchData();
|
|
267
|
-
await modal.message("Imported", `Profile imported successfully (id: ${id}).`, "success");
|
|
268
|
-
}
|
|
269
|
-
catch (error) {
|
|
270
|
-
modal.hideModal();
|
|
271
|
-
await modal.message("Error", `Failed to import: ${error}`, "error");
|
|
272
|
-
}
|
|
273
|
-
};
|
|
274
|
-
// ─── Render ───────────────────────────────────────────────────────────────
|
|
275
|
-
const profileCount = profileList.length;
|
|
276
|
-
const userCount = profileList.filter((p) => p.scope === "user").length;
|
|
277
|
-
const projCount = profileList.filter((p) => p.scope === "project").length;
|
|
278
|
-
const statusContent = (_jsxs("text", { children: [_jsxs("span", { fg: "blue", children: [PREDEFINED_PROFILES.length, " presets"] }), _jsx("span", { fg: "gray", children: " + " }), _jsxs("span", { fg: "cyan", children: [userCount, " user"] }), _jsx("span", { fg: "gray", children: " + " }), _jsxs("span", { fg: "green", children: [projCount, " project"] }), _jsx("span", { fg: "gray", children: " = " }), _jsxs("span", { fg: "white", children: [PREDEFINED_PROFILES.length + profileCount, " total"] })] }));
|
|
279
|
-
const firstNavigableIndex = allItems.findIndex(isNavigable);
|
|
280
|
-
const effectiveIndex = profilesState.selectedIndex === 0 && selectedItem?.kind === "header"
|
|
281
|
-
? firstNavigableIndex
|
|
282
|
-
: profilesState.selectedIndex;
|
|
283
|
-
const loadingStatus = profilesState.profiles.status === "loading"
|
|
284
|
-
? true
|
|
285
|
-
: false;
|
|
286
|
-
const errorMessage = profilesState.profiles.status === "error"
|
|
287
|
-
? profilesState.profiles.error.message
|
|
288
|
-
: undefined;
|
|
289
|
-
return (_jsx(ScreenLayout, { title: "claudeup Plugin Profiles", currentScreen: "profiles", statusLine: statusContent, footerHints: [
|
|
290
|
-
{ keys: ["↑", "↓"], label: "nav" },
|
|
291
|
-
{ keys: ["Enter", "/", "a"], label: "apply" },
|
|
292
|
-
{ keys: ["r"], label: "rename" },
|
|
293
|
-
{ keys: ["d"], label: "delete" },
|
|
294
|
-
{ keys: ["c"], label: "copy" },
|
|
295
|
-
{ keys: ["i"], label: "import" },
|
|
296
|
-
], listPanel: _jsx(ScrollableList, { items: allItems, selectedIndex: effectiveIndex, renderItem: renderProfileRow, maxHeight: dimensions.listPanelHeight }), detailPanel: renderProfileDetail(allItems[effectiveIndex], loadingStatus, errorMessage) }));
|
|
297
|
-
}
|
|
298
|
-
export default ProfilesScreen;
|