claudeup 4.18.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__/alias-adopt.test.ts +364 -0
- package/src/__tests__/alias-parser.test.ts +92 -0
- package/src/__tests__/alias-shell-writer.test.ts +7 -0
- package/src/__tests__/alias-store.test.ts +77 -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 +118 -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/alias-flags.ts +11 -1
- 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/alias-shell-writer.ts +382 -8
- package/src/services/alias-store.ts +60 -0
- 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-setup.ts +61 -4
- 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 +16 -11
- package/src/ui/components/FlagDetailEditor.tsx +0 -0
- package/src/ui/components/modals/ConfirmModal.tsx +1 -1
- package/src/ui/screens/AliasScreen.tsx +491 -359
- package/src/ui/screens/CliToolsScreen.tsx +11 -3
- package/src/ui/screens/PluginsScreen.tsx +3 -1
- package/src/ui/screens/index.ts +0 -1
- package/src/ui/state/reducer.ts +6 -102
- package/src/ui/state/types.ts +8 -35
- package/src/utils/command-utils.ts +23 -0
- package/src/utils/file-locking.ts +144 -0
- package/src/data/alias-flags.js +0 -196
- 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 -764
- package/src/services/alias-store.js +0 -77
- 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 -444
- 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 -346
- 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/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 -1008
- 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 -766
- 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 -543
- 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
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `claudeup profile <list|show|switch|sync> [name]`
|
|
3
|
+
*
|
|
4
|
+
* Inspect and switch between team profiles materialized under
|
|
5
|
+
* `.claude/_profiles/`. Switching is cheap and offline — it just repoints the
|
|
6
|
+
* shared-config symlinks. `sync` (Phase 5) promotes local drift back into the
|
|
7
|
+
* committed manifest.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import { readManifest } from "../services/manifest.js";
|
|
12
|
+
import { resolveProfile } from "../services/resolver.js";
|
|
13
|
+
import { summarizeClosure } from "../services/install-plan.js";
|
|
14
|
+
import {
|
|
15
|
+
activateProfile,
|
|
16
|
+
activeProfile,
|
|
17
|
+
profileDir,
|
|
18
|
+
PROFILE_GITIGNORE_ENTRIES,
|
|
19
|
+
} from "../services/symlink-manager.js";
|
|
20
|
+
import { ensureGitignoreEntries } from "../services/conventions-manager.js";
|
|
21
|
+
import { syncProfile } from "../services/profile-sync.js";
|
|
22
|
+
import { checkBinaries, missingBinaries } from "../services/doctor-bins.js";
|
|
23
|
+
import fs from "fs-extra";
|
|
24
|
+
|
|
25
|
+
export async function runProfileCommand(args: string[]): Promise<number> {
|
|
26
|
+
const [sub, ...rest] = args;
|
|
27
|
+
const projectPath = process.cwd();
|
|
28
|
+
|
|
29
|
+
switch (sub) {
|
|
30
|
+
case undefined:
|
|
31
|
+
case "list":
|
|
32
|
+
return listProfiles(projectPath);
|
|
33
|
+
case "show":
|
|
34
|
+
return showProfile(rest[0], projectPath);
|
|
35
|
+
case "switch":
|
|
36
|
+
return switchProfile(rest[0], projectPath);
|
|
37
|
+
case "sync":
|
|
38
|
+
return syncActiveProfile(rest[0], projectPath);
|
|
39
|
+
default:
|
|
40
|
+
console.error(
|
|
41
|
+
`Unknown profile command "${sub}". Use: list | show <name> | switch <name> | sync`,
|
|
42
|
+
);
|
|
43
|
+
return 1;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function listProfiles(projectPath: string): Promise<number> {
|
|
48
|
+
const manifest = await readManifest(projectPath);
|
|
49
|
+
const ids = Object.keys(manifest.profiles);
|
|
50
|
+
if (ids.length === 0) {
|
|
51
|
+
console.log("No profiles defined in .claude/profiles.json.");
|
|
52
|
+
return 0;
|
|
53
|
+
}
|
|
54
|
+
const active = await activeProfile(projectPath);
|
|
55
|
+
console.log("Profiles:");
|
|
56
|
+
for (const id of ids) {
|
|
57
|
+
const entry = manifest.profiles[id]!;
|
|
58
|
+
const marker = id === active ? "●" : " ";
|
|
59
|
+
const materialized = (await fs.pathExists(profileDir(id, projectPath)))
|
|
60
|
+
? ""
|
|
61
|
+
: " (not installed)";
|
|
62
|
+
console.log(` ${marker} ${id} — ${entry.name}${materialized}`);
|
|
63
|
+
}
|
|
64
|
+
if (active) console.log(`\nActive: ${active}`);
|
|
65
|
+
return 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export async function showProfile(
|
|
69
|
+
name: string | undefined,
|
|
70
|
+
projectPath: string,
|
|
71
|
+
): Promise<number> {
|
|
72
|
+
if (!name) {
|
|
73
|
+
console.error("Usage: claudeup profile show <name>");
|
|
74
|
+
return 1;
|
|
75
|
+
}
|
|
76
|
+
const manifest = await readManifest(projectPath);
|
|
77
|
+
if (!manifest.profiles[name]) {
|
|
78
|
+
console.error(
|
|
79
|
+
`Profile "${name}" not found. Available: ${Object.keys(manifest.profiles).join(", ")}`,
|
|
80
|
+
);
|
|
81
|
+
return 1;
|
|
82
|
+
}
|
|
83
|
+
const closure = await resolveProfile(manifest, name);
|
|
84
|
+
console.log(`Profile: ${name} (${manifest.profiles[name]!.name})`);
|
|
85
|
+
for (const line of summarizeClosure(closure)) console.log(` ${line}`);
|
|
86
|
+
return 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export async function switchProfile(
|
|
90
|
+
name: string | undefined,
|
|
91
|
+
projectPath: string,
|
|
92
|
+
): Promise<number> {
|
|
93
|
+
if (!name) {
|
|
94
|
+
console.error("Usage: claudeup profile switch <name>");
|
|
95
|
+
return 1;
|
|
96
|
+
}
|
|
97
|
+
const manifest = await readManifest(projectPath);
|
|
98
|
+
if (!manifest.profiles[name]) {
|
|
99
|
+
console.error(
|
|
100
|
+
`Profile "${name}" not found. Available: ${Object.keys(manifest.profiles).join(", ")}`,
|
|
101
|
+
);
|
|
102
|
+
return 1;
|
|
103
|
+
}
|
|
104
|
+
if (!(await fs.pathExists(profileDir(name, projectPath)))) {
|
|
105
|
+
console.error(
|
|
106
|
+
`Profile "${name}" isn't installed yet. Run: claudeup install ${name}`,
|
|
107
|
+
);
|
|
108
|
+
return 1;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// In profile mode the source of truth is .claude/profiles.json; the
|
|
112
|
+
// generated dir AND the per-developer symlinks must stay out of git so
|
|
113
|
+
// switching profiles causes no churn.
|
|
114
|
+
await ensureGitignoreEntries(
|
|
115
|
+
path.join(projectPath, ".gitignore"),
|
|
116
|
+
PROFILE_GITIGNORE_ENTRIES,
|
|
117
|
+
"# claudeup: generated profile build output + active-profile symlinks",
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
await activateProfile(name, projectPath);
|
|
121
|
+
console.log(`✓ Switched to profile: ${name}`);
|
|
122
|
+
|
|
123
|
+
// Verify the profile's binary deps are actually usable — the switch itself
|
|
124
|
+
// succeeds (symlinks are valid), but warn so "switched" isn't misleading.
|
|
125
|
+
try {
|
|
126
|
+
const closure = await resolveProfile(manifest, name);
|
|
127
|
+
const missing = missingBinaries(await checkBinaries(closure.bins));
|
|
128
|
+
if (missing.length > 0) {
|
|
129
|
+
console.log(
|
|
130
|
+
`\n⚠ ${missing.length} binary dependenc${missing.length === 1 ? "y is" : "ies are"} missing:`,
|
|
131
|
+
);
|
|
132
|
+
for (const m of missing) console.log(` ✗ ${m.name} — run: ${m.installCommand}`);
|
|
133
|
+
console.log(`Run \`claudeup install ${name}\` to install them.`);
|
|
134
|
+
}
|
|
135
|
+
} catch {
|
|
136
|
+
// Verification is best-effort — never fail a valid switch on it.
|
|
137
|
+
}
|
|
138
|
+
return 0;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export async function syncActiveProfile(
|
|
142
|
+
nameArg: string | undefined,
|
|
143
|
+
projectPath: string,
|
|
144
|
+
): Promise<number> {
|
|
145
|
+
const name = nameArg ?? (await activeProfile(projectPath));
|
|
146
|
+
if (!name) {
|
|
147
|
+
console.error(
|
|
148
|
+
"No active profile to sync. Pass a name: claudeup profile sync <name>",
|
|
149
|
+
);
|
|
150
|
+
return 1;
|
|
151
|
+
}
|
|
152
|
+
try {
|
|
153
|
+
const { changes, wrote } = await syncProfile(name, projectPath);
|
|
154
|
+
if (!wrote) {
|
|
155
|
+
console.log(`Profile "${name}" is already in sync with the manifest.`);
|
|
156
|
+
return 0;
|
|
157
|
+
}
|
|
158
|
+
console.log(`Synced "${name}" → .claude/profiles.json:`);
|
|
159
|
+
for (const c of changes) console.log(` ${c.kind}: ${c.detail}`);
|
|
160
|
+
console.log("\nReview the diff and commit .claude/profiles.json.");
|
|
161
|
+
return 0;
|
|
162
|
+
} catch (e) {
|
|
163
|
+
console.error((e as Error).message);
|
|
164
|
+
return 1;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* claudeup subcommand router.
|
|
3
|
+
*
|
|
4
|
+
* Parses argv and dispatches to a command handler. Commands run to completion
|
|
5
|
+
* and return an exit code; bare invocation (or an unknown token) falls through
|
|
6
|
+
* to the interactive TUI, preserving claudeup's historical behavior.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { checkForUpdates } from "../services/version-check.js";
|
|
10
|
+
import { runClaudeCommand } from "./claude.js";
|
|
11
|
+
import { runUpdateCommand } from "./update.js";
|
|
12
|
+
import { runInstallCommand } from "./install.js";
|
|
13
|
+
import { runProfileCommand } from "./profile.js";
|
|
14
|
+
import { runDoctorCommand } from "./doctor.js";
|
|
15
|
+
|
|
16
|
+
export interface RouteOutcome {
|
|
17
|
+
/** A subcommand (or top-level flag) handled the invocation. */
|
|
18
|
+
handled: boolean;
|
|
19
|
+
/** Exit code to use when `handled` is true. */
|
|
20
|
+
exitCode?: number;
|
|
21
|
+
/** No subcommand matched — the caller should launch the TUI. */
|
|
22
|
+
launchTui: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const HANDLED = (exitCode: number): RouteOutcome => ({
|
|
26
|
+
handled: true,
|
|
27
|
+
exitCode,
|
|
28
|
+
launchTui: false,
|
|
29
|
+
});
|
|
30
|
+
const LAUNCH_TUI: RouteOutcome = { handled: false, launchTui: true };
|
|
31
|
+
|
|
32
|
+
export async function route(
|
|
33
|
+
argv: string[],
|
|
34
|
+
version: string,
|
|
35
|
+
): Promise<RouteOutcome> {
|
|
36
|
+
// Top-level flags take precedence over any command token.
|
|
37
|
+
if (argv.includes("--version") || argv.includes("-v")) {
|
|
38
|
+
console.log(`claudeup v${version}`);
|
|
39
|
+
const result = await checkForUpdates();
|
|
40
|
+
if (result.updateAvailable) {
|
|
41
|
+
console.log(`\nUpdate available: v${result.latestVersion}`);
|
|
42
|
+
console.log("Run: claudeup update");
|
|
43
|
+
}
|
|
44
|
+
return HANDLED(0);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
48
|
+
printHelp(version);
|
|
49
|
+
return HANDLED(0);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const [command, ...rest] = argv;
|
|
53
|
+
|
|
54
|
+
switch (command) {
|
|
55
|
+
case "claude":
|
|
56
|
+
return HANDLED(await runClaudeCommand(rest));
|
|
57
|
+
case "update":
|
|
58
|
+
return HANDLED(await runUpdateCommand());
|
|
59
|
+
case "install":
|
|
60
|
+
return HANDLED(await runInstallCommand(rest));
|
|
61
|
+
case "profile":
|
|
62
|
+
return HANDLED(await runProfileCommand(rest));
|
|
63
|
+
case "doctor":
|
|
64
|
+
return HANDLED(await runDoctorCommand(rest));
|
|
65
|
+
default:
|
|
66
|
+
// Bare `claudeup` or an unrecognized token → interactive TUI.
|
|
67
|
+
return LAUNCH_TUI;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function printHelp(version: string): void {
|
|
72
|
+
console.log(`claudeup v${version}
|
|
73
|
+
|
|
74
|
+
TUI tool for managing Claude Code plugins, MCPs, and configuration.
|
|
75
|
+
|
|
76
|
+
Usage: claudeup [options]
|
|
77
|
+
claudeup claude [args...] Run claude with auto-update prerunner
|
|
78
|
+
claudeup update Update claudeup to latest version
|
|
79
|
+
|
|
80
|
+
Options:
|
|
81
|
+
-v, --version Show version and check for updates
|
|
82
|
+
-h, --help Show this help message
|
|
83
|
+
--no-refresh Skip auto-refresh of marketplaces on startup
|
|
84
|
+
|
|
85
|
+
Commands:
|
|
86
|
+
claude [args...] Check for plugin updates (1h cache), then run claude
|
|
87
|
+
-f, --force Force update check (bypass 1h cache)
|
|
88
|
+
update Update claudeup itself to latest version
|
|
89
|
+
|
|
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
|
+
Navigation (TUI):
|
|
96
|
+
[1] Plugins [4] Settings [7] Git State
|
|
97
|
+
[2] Skills [5] Profiles [8] Alias
|
|
98
|
+
[3] MCP [6] CLI Tools
|
|
99
|
+
|
|
100
|
+
Keys:
|
|
101
|
+
↑/↓ or j/k Navigate
|
|
102
|
+
Enter Select / Toggle
|
|
103
|
+
r Refresh current screen
|
|
104
|
+
? Show help
|
|
105
|
+
q / Escape Back / Quit
|
|
106
|
+
`);
|
|
107
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `claudeup update` — self-update.
|
|
3
|
+
*
|
|
4
|
+
* Detects claudeup installs across bun + npm global, warns on duplicates
|
|
5
|
+
* (active vs shadowed), then re-installs `claudeup@latest` with whichever
|
|
6
|
+
* package manager owns the active binary. Resolves with the child's exit code.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { spawn } from "node:child_process";
|
|
10
|
+
|
|
11
|
+
export async function runUpdateCommand(): Promise<number> {
|
|
12
|
+
const { execSync } = await import("node:child_process");
|
|
13
|
+
const { existsSync } = await import("node:fs");
|
|
14
|
+
|
|
15
|
+
// Detect all installations of claudeup across package managers
|
|
16
|
+
const installations: Array<{ manager: "bun" | "npm"; path: string }> = [];
|
|
17
|
+
|
|
18
|
+
// Check bun global
|
|
19
|
+
try {
|
|
20
|
+
const bunGlobalBin = execSync("bun pm -g bin", {
|
|
21
|
+
encoding: "utf-8",
|
|
22
|
+
timeout: 5000,
|
|
23
|
+
}).trim();
|
|
24
|
+
const bunPath = `${bunGlobalBin}/claudeup`;
|
|
25
|
+
if (existsSync(bunPath)) installations.push({ manager: "bun", path: bunPath });
|
|
26
|
+
} catch {
|
|
27
|
+
// bun not installed or no global claudeup
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Check npm global
|
|
31
|
+
try {
|
|
32
|
+
const npmPrefix = execSync("npm prefix -g", {
|
|
33
|
+
encoding: "utf-8",
|
|
34
|
+
timeout: 5000,
|
|
35
|
+
}).trim();
|
|
36
|
+
const npmPath = `${npmPrefix}/bin/claudeup`;
|
|
37
|
+
if (existsSync(npmPath)) installations.push({ manager: "npm", path: npmPath });
|
|
38
|
+
} catch {
|
|
39
|
+
// npm not installed or no global claudeup
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Warn about duplicate installations
|
|
43
|
+
if (installations.length > 1) {
|
|
44
|
+
const activePath = execSync("which claudeup", {
|
|
45
|
+
encoding: "utf-8",
|
|
46
|
+
timeout: 5000,
|
|
47
|
+
}).trim();
|
|
48
|
+
|
|
49
|
+
console.log(`⚠ claudeup is installed via multiple package managers:\n`);
|
|
50
|
+
for (const inst of installations) {
|
|
51
|
+
const tag = inst.path === activePath ? " (active)" : " (shadowed)";
|
|
52
|
+
console.log(` ${inst.manager}: ${inst.path}${tag}`);
|
|
53
|
+
}
|
|
54
|
+
console.log(`\nTo fix, keep one and remove the other:`);
|
|
55
|
+
for (const inst of installations) {
|
|
56
|
+
console.log(` ${inst.manager} uninstall -g claudeup`);
|
|
57
|
+
}
|
|
58
|
+
console.log();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Determine which package manager to use for the update
|
|
62
|
+
let usesBun = false;
|
|
63
|
+
try {
|
|
64
|
+
const claudeupPath = execSync("which claudeup", {
|
|
65
|
+
encoding: "utf-8",
|
|
66
|
+
}).trim();
|
|
67
|
+
usesBun = claudeupPath.includes(".bun") || claudeupPath.includes("bun/bin");
|
|
68
|
+
} catch {
|
|
69
|
+
// If which fails, default to npm
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const pkgManager = usesBun ? "bun" : "npm";
|
|
73
|
+
console.log(`Updating claudeup using ${pkgManager}...`);
|
|
74
|
+
|
|
75
|
+
const installArgs = ["install", "-g", "claudeup@latest"];
|
|
76
|
+
|
|
77
|
+
return new Promise<number>((resolve) => {
|
|
78
|
+
const proc = spawn(pkgManager, installArgs, {
|
|
79
|
+
stdio: "inherit",
|
|
80
|
+
shell: false, // Avoid shell for security (fixes DEP0190 warning)
|
|
81
|
+
});
|
|
82
|
+
proc.on("exit", (code) => resolve(code ?? 0));
|
|
83
|
+
proc.on("error", () => resolve(1));
|
|
84
|
+
});
|
|
85
|
+
}
|
package/src/data/alias-flags.ts
CHANGED
|
@@ -222,6 +222,16 @@ export const ALIAS_FLAGS: AliasFlag[] = [
|
|
|
222
222
|
description:
|
|
223
223
|
"Path to a file whose contents are appended to the default system prompt.",
|
|
224
224
|
},
|
|
225
|
+
{
|
|
226
|
+
id: "channels",
|
|
227
|
+
label: "Channels (approved)",
|
|
228
|
+
flag: "--channels",
|
|
229
|
+
kind: "text-list",
|
|
230
|
+
group: "channels",
|
|
231
|
+
description:
|
|
232
|
+
"Approved channel plugins/MCP servers to run this session (the allowlist). Channels derived from --dangerously-load appear in a separate section here and are unioned in on write; add your own approved channels on top.",
|
|
233
|
+
defaultValues: [],
|
|
234
|
+
},
|
|
225
235
|
{
|
|
226
236
|
id: "dangerously-load-development-channels",
|
|
227
237
|
label: "Load development channels",
|
|
@@ -229,7 +239,7 @@ export const ALIAS_FLAGS: AliasFlag[] = [
|
|
|
229
239
|
kind: "text-list",
|
|
230
240
|
group: "channels",
|
|
231
241
|
description:
|
|
232
|
-
"Sideload development channels
|
|
242
|
+
"Sideload development channels not on the approved allowlist (local dev only). Also shown under --channels and unioned into it on write. Default seed: claudish (the only magus plugin currently shipping channels).",
|
|
233
243
|
defaultValues: ["plugin:claudish@magus"],
|
|
234
244
|
},
|
|
235
245
|
{
|
package/src/data/cli-tools.ts
CHANGED
|
@@ -6,7 +6,7 @@ export interface CliTool {
|
|
|
6
6
|
checkCommand: string;
|
|
7
7
|
website: string;
|
|
8
8
|
category: "ai-coding" | "utility";
|
|
9
|
-
packageManager: "npm" | "pip";
|
|
9
|
+
packageManager: "bun" | "npm" | "pip" | "brew" | "go";
|
|
10
10
|
packageName: string;
|
|
11
11
|
}
|
|
12
12
|
|
|
@@ -16,11 +16,11 @@ export const cliTools: CliTool[] = [
|
|
|
16
16
|
displayName: "claudeup",
|
|
17
17
|
description:
|
|
18
18
|
"TUI tool for managing Claude Code plugins, MCPs, and configuration",
|
|
19
|
-
installCommand: "
|
|
19
|
+
installCommand: "bun install -g claudeup",
|
|
20
20
|
checkCommand: "claudeup --version",
|
|
21
21
|
website: "https://github.com/MadAppGang/magus/tree/main/tools/claudeup",
|
|
22
22
|
category: "ai-coding",
|
|
23
|
-
packageManager: "
|
|
23
|
+
packageManager: "bun",
|
|
24
24
|
packageName: "claudeup",
|
|
25
25
|
},
|
|
26
26
|
{
|
|
@@ -28,22 +28,22 @@ export const cliTools: CliTool[] = [
|
|
|
28
28
|
displayName: "Mnemex",
|
|
29
29
|
description:
|
|
30
30
|
"AST-aware code search with PageRank, callers/callees, and semantic embeddings",
|
|
31
|
-
installCommand: "
|
|
31
|
+
installCommand: "bun install -g mnemex",
|
|
32
32
|
checkCommand: "mnemex --version",
|
|
33
33
|
website: "https://github.com/MadAppGang/mnemex",
|
|
34
34
|
category: "ai-coding",
|
|
35
|
-
packageManager: "
|
|
35
|
+
packageManager: "bun",
|
|
36
36
|
packageName: "mnemex",
|
|
37
37
|
},
|
|
38
38
|
{
|
|
39
39
|
name: "claudish",
|
|
40
40
|
displayName: "Claudish",
|
|
41
41
|
description: "Run Claude Code with OpenRouter models (Grok, GPT-5, Gemini)",
|
|
42
|
-
installCommand: "
|
|
42
|
+
installCommand: "bun install -g claudish",
|
|
43
43
|
checkCommand: "claudish --version",
|
|
44
44
|
website: "https://github.com/MadAppGang/claudish",
|
|
45
45
|
category: "ai-coding",
|
|
46
|
-
packageManager: "
|
|
46
|
+
packageManager: "bun",
|
|
47
47
|
packageName: "claudish",
|
|
48
48
|
},
|
|
49
49
|
{
|
|
@@ -12,6 +12,8 @@ export const BUILTIN_DEFAULTS: GitignoreManifest = {
|
|
|
12
12
|
".claude/scheduled_tasks.lock",
|
|
13
13
|
".claude/cache/",
|
|
14
14
|
".claude/.statusline-worktree-*",
|
|
15
|
+
// Generated, per-developer profile build output (like node_modules).
|
|
16
|
+
".claude/_profiles/",
|
|
15
17
|
"ai-docs/sessions/",
|
|
16
18
|
".mnemex/",
|
|
17
19
|
".claudemem/",
|
|
@@ -19,6 +21,8 @@ export const BUILTIN_DEFAULTS: GitignoreManifest = {
|
|
|
19
21
|
".agents/",
|
|
20
22
|
],
|
|
21
23
|
track: [
|
|
24
|
+
// The committed team source of truth when using profiles.
|
|
25
|
+
".claude/profiles.json",
|
|
22
26
|
".claude/settings.json",
|
|
23
27
|
".mcp.json",
|
|
24
28
|
"plugin.json",
|
package/src/data/marketplaces.ts
CHANGED
|
@@ -39,6 +39,18 @@ export const defaultMarketplaces: Marketplace[] = [
|
|
|
39
39
|
official: false,
|
|
40
40
|
featured: true, // Always show plugins (expanded by default)
|
|
41
41
|
},
|
|
42
|
+
{
|
|
43
|
+
name: "magus-marketing",
|
|
44
|
+
displayName: "Magus Marketing",
|
|
45
|
+
source: {
|
|
46
|
+
source: "github",
|
|
47
|
+
repo: "MadAppGang/magus-marketing",
|
|
48
|
+
},
|
|
49
|
+
description:
|
|
50
|
+
"SEO, cold email outreach, AI image generation, and video editing plugins",
|
|
51
|
+
official: false,
|
|
52
|
+
// Not featured: a niche channel, so keep it collapsed by default.
|
|
53
|
+
},
|
|
42
54
|
{
|
|
43
55
|
name: "claude-plugins-official",
|
|
44
56
|
displayName: "Anthropic Official",
|
|
@@ -127,7 +127,6 @@ export const PREDEFINED_PROFILES: PredefinedProfile[] = [
|
|
|
127
127
|
"code-analysis",
|
|
128
128
|
"terminal",
|
|
129
129
|
"statusline",
|
|
130
|
-
"conductor",
|
|
131
130
|
"multimodel",
|
|
132
131
|
"gtd",
|
|
133
132
|
],
|
|
@@ -200,10 +199,10 @@ export const PREDEFINED_PROFILES: PredefinedProfile[] = [
|
|
|
200
199
|
"code-analysis",
|
|
201
200
|
"terminal",
|
|
202
201
|
"statusline",
|
|
203
|
-
"seo",
|
|
202
|
+
"seo@magus-marketing",
|
|
204
203
|
"browser-use",
|
|
205
|
-
"nanobanana",
|
|
206
|
-
"video-editing",
|
|
204
|
+
"nanobanana@magus-marketing",
|
|
205
|
+
"video-editing@magus-marketing",
|
|
207
206
|
],
|
|
208
207
|
anthropicPlugins: [
|
|
209
208
|
"explanatory-output-style",
|
package/src/main.tsx
CHANGED
|
@@ -2,15 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
import { createCliRenderer } from "@opentui/core";
|
|
4
4
|
import { createRoot } from "@opentui/react";
|
|
5
|
-
import { spawn } from "node:child_process";
|
|
6
|
-
import { createRequire } from "node:module";
|
|
7
5
|
import { App } from "./ui/App.js";
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
6
|
+
import { route } from "./cli/router.js";
|
|
7
|
+
// Static import so `bun build --compile` embeds package.json into the binary;
|
|
8
|
+
// a dynamic require("../package.json") is not resolvable inside the bunfs root.
|
|
9
|
+
import pkg from "../package.json";
|
|
10
10
|
|
|
11
|
-
const
|
|
12
|
-
const pkg = require("../package.json") as { version: string };
|
|
13
|
-
export const VERSION = pkg.version;
|
|
11
|
+
export const VERSION = (pkg as { version: string }).version;
|
|
14
12
|
|
|
15
13
|
// Note: OpenTUI renderer handles alternate screen buffer automatically
|
|
16
14
|
// No need for manual ANSI escape codes
|
|
@@ -18,153 +16,12 @@ export const VERSION = pkg.version;
|
|
|
18
16
|
async function main(): Promise<void> {
|
|
19
17
|
const args = process.argv.slice(2);
|
|
20
18
|
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
claudeArgs.includes("--force") || claudeArgs.includes("-f");
|
|
28
|
-
|
|
29
|
-
// Remove prerunner flags before passing to claude
|
|
30
|
-
const filteredArgs = claudeArgs.filter(
|
|
31
|
-
(arg) => arg !== "--force" && arg !== "-f",
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
const exitCode = await prerunClaude(filteredArgs, { force: forceUpdate });
|
|
35
|
-
process.exit(exitCode);
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Handle "claudeup update" - self-update command
|
|
40
|
-
if (args[0] === "update") {
|
|
41
|
-
const { execSync } = await import("node:child_process");
|
|
42
|
-
const { existsSync } = await import("node:fs");
|
|
43
|
-
|
|
44
|
-
// Detect all installations of claudeup across package managers
|
|
45
|
-
const installations: Array<{
|
|
46
|
-
manager: "bun" | "npm";
|
|
47
|
-
path: string;
|
|
48
|
-
}> = [];
|
|
49
|
-
|
|
50
|
-
// Check bun global
|
|
51
|
-
try {
|
|
52
|
-
const bunGlobalBin = execSync("bun pm -g bin", {
|
|
53
|
-
encoding: "utf-8",
|
|
54
|
-
timeout: 5000,
|
|
55
|
-
}).trim();
|
|
56
|
-
const bunPath = `${bunGlobalBin}/claudeup`;
|
|
57
|
-
if (existsSync(bunPath)) installations.push({ manager: "bun", path: bunPath });
|
|
58
|
-
} catch {
|
|
59
|
-
// bun not installed or no global claudeup
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Check npm global
|
|
63
|
-
try {
|
|
64
|
-
const npmPrefix = execSync("npm prefix -g", {
|
|
65
|
-
encoding: "utf-8",
|
|
66
|
-
timeout: 5000,
|
|
67
|
-
}).trim();
|
|
68
|
-
const npmPath = `${npmPrefix}/bin/claudeup`;
|
|
69
|
-
if (existsSync(npmPath)) installations.push({ manager: "npm", path: npmPath });
|
|
70
|
-
} catch {
|
|
71
|
-
// npm not installed or no global claudeup
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Warn about duplicate installations
|
|
75
|
-
if (installations.length > 1) {
|
|
76
|
-
const activePath = execSync("which claudeup", {
|
|
77
|
-
encoding: "utf-8",
|
|
78
|
-
timeout: 5000,
|
|
79
|
-
}).trim();
|
|
80
|
-
|
|
81
|
-
console.log(
|
|
82
|
-
`⚠ claudeup is installed via multiple package managers:\n`,
|
|
83
|
-
);
|
|
84
|
-
for (const inst of installations) {
|
|
85
|
-
const tag =
|
|
86
|
-
inst.path === activePath ? " (active)" : " (shadowed)";
|
|
87
|
-
console.log(` ${inst.manager}: ${inst.path}${tag}`);
|
|
88
|
-
}
|
|
89
|
-
console.log(`\nTo fix, keep one and remove the other:`);
|
|
90
|
-
for (const inst of installations) {
|
|
91
|
-
console.log(` ${inst.manager} uninstall -g claudeup`);
|
|
92
|
-
}
|
|
93
|
-
console.log();
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Determine which package manager to use for the update
|
|
97
|
-
let usesBun = false;
|
|
98
|
-
try {
|
|
99
|
-
const claudeupPath = execSync("which claudeup", {
|
|
100
|
-
encoding: "utf-8",
|
|
101
|
-
}).trim();
|
|
102
|
-
usesBun =
|
|
103
|
-
claudeupPath.includes(".bun") || claudeupPath.includes("bun/bin");
|
|
104
|
-
} catch {
|
|
105
|
-
// If which fails, default to npm
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const pkgManager = usesBun ? "bun" : "npm";
|
|
109
|
-
console.log(`Updating claudeup using ${pkgManager}...`);
|
|
110
|
-
|
|
111
|
-
const installArgs = ["install", "-g", "claudeup@latest"];
|
|
112
|
-
|
|
113
|
-
const proc = spawn(pkgManager, installArgs, {
|
|
114
|
-
stdio: "inherit",
|
|
115
|
-
shell: false, // Avoid shell for security (fixes DEP0190 warning)
|
|
116
|
-
});
|
|
117
|
-
proc.on("exit", (code) => process.exit(code || 0));
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Handle --version flag - show version and check for updates
|
|
122
|
-
if (args.includes("--version") || args.includes("-v")) {
|
|
123
|
-
console.log(`claudeup v${VERSION}`);
|
|
124
|
-
const result = await checkForUpdates();
|
|
125
|
-
if (result.updateAvailable) {
|
|
126
|
-
console.log(`\nUpdate available: v${result.latestVersion}`);
|
|
127
|
-
console.log("Run: claudeup update");
|
|
128
|
-
}
|
|
129
|
-
process.exit(0);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Handle --help flag
|
|
133
|
-
if (args.includes("--help") || args.includes("-h")) {
|
|
134
|
-
console.log(`claudeup v${VERSION}
|
|
135
|
-
|
|
136
|
-
TUI tool for managing Claude Code plugins, MCPs, and configuration.
|
|
137
|
-
|
|
138
|
-
Usage: claudeup [options]
|
|
139
|
-
claudeup claude [args...] Run claude with auto-update prerunner
|
|
140
|
-
claudeup update Update claudeup to latest version
|
|
141
|
-
|
|
142
|
-
Options:
|
|
143
|
-
-v, --version Show version and check for updates
|
|
144
|
-
-h, --help Show this help message
|
|
145
|
-
--no-refresh Skip auto-refresh of marketplaces on startup
|
|
146
|
-
|
|
147
|
-
Commands:
|
|
148
|
-
claude [args...] Check for plugin updates (1h cache), then run claude
|
|
149
|
-
-f, --force Force update check (bypass 1h cache)
|
|
150
|
-
update Update claudeup itself to latest version
|
|
151
|
-
|
|
152
|
-
Navigation:
|
|
153
|
-
[1] Plugins Manage plugin marketplaces and installed plugins
|
|
154
|
-
[2] MCP Setup and manage MCP servers
|
|
155
|
-
[3] Status Configure status line display
|
|
156
|
-
[4] Env Environment variables for MCP servers
|
|
157
|
-
[5] Tools Install and update AI coding CLI tools
|
|
158
|
-
|
|
159
|
-
Keys:
|
|
160
|
-
↑/↓ or j/k Navigate
|
|
161
|
-
Enter Select / Toggle
|
|
162
|
-
g Toggle global/project scope (in Plugins)
|
|
163
|
-
r Refresh current screen
|
|
164
|
-
? Show help
|
|
165
|
-
q / Escape Back / Quit
|
|
166
|
-
`);
|
|
167
|
-
process.exit(0);
|
|
19
|
+
// Dispatch non-interactive subcommands (claude, update, install, …) and
|
|
20
|
+
// top-level flags (--version/--help). A bare invocation falls through to
|
|
21
|
+
// the interactive TUI below.
|
|
22
|
+
const outcome = await route(args, VERSION);
|
|
23
|
+
if (outcome.handled) {
|
|
24
|
+
process.exit(outcome.exitCode ?? 0);
|
|
168
25
|
}
|
|
169
26
|
|
|
170
27
|
// Create OpenTUI renderer (handles alternate screen buffer automatically)
|